Description
At the moment, we have issues with regard to CLDR/ICU data (i.e. information about languages, currencies etc. that is collected by the CLDR project and exposed in a C API by the ICU project):
- Method calls are long:
Intl::getCurrencyBundle()->getCurrencyName(...)
- We don't expose all information that is actually available in the CLDR data (e.g. see Flag deprecated currencies in the bundled ICU data, filter them out by default #11886)
- Since the bundles implement interfaces, we can't add methods to expose that data without breaking BC
To fix these problems, I suggest:
- Moving to static method calls for data access (e.g.
Currencies::getLocalizedName('EUR', 'en')
). This has been discussed before in Use classes and interfaces to represent the shipped ICU data #11887.- The data is always available on the file system since we bundle it with the Intl component, hence I don't see a need to mock this information in tests and static access should be fine.
- Also, I don't see a need to provide value objects (
Currency
), since we expose data only. Projects that need value objects need specialized value objects most of the time and are better off if they implement their own. We didn't find a good solution in Use classes and interfaces to represent the shipped ICU data #11887, so I suggest to KISS and move forward. - To avoid confusion with value object classes and to underline that the class contains static methods only, I suggest to use plural names.
- Adding new methods for accessing CLDR data that is currently not being exposed.
This work was started once in #9206, but abandoned. I suggest to look at the PR for inspiration.
To Do
- Migrate
CurrencyBundle
toCurrencies
- Migrate
LanguageBundle
toLanguages
- Migrate
LocaleBundle
toLocales
- Migrate
RegionBundle
toCountries
- Add
Scripts
- Add
Timezones
(started in [Intl] Provide translated timezone names #17636) - Analyze CLDR data and add accessors for available data
✨ Who wants to work on these PRs? ✨
Canonicalization
We should add canonicalize()
methods to all static classes in order to canonicalize (normalize) language/currency/etc. codes. For example, fr-FR and fr_FR are both valid language codes. Within our code, we want to use one specific format, however (namely fr_FR). Language codes received from other sources (browsers, request headers, ...) can be passed to Languages::canonicalize()
in order to convert them to Symfony's preferred format.
Currencies API
Here is a proposed API for the Currencies
class. I added the doc comments from #9206 where needed for clarity. See #9206 for implementation inspiration.
class Currencies
{
/**
* Returns all available currencies.
*
* @return string[] An array of ISO 4217 three-letter currency codes
*/
public static function getCurrencyCodes();
public static function exists($currencyCode);
public static function canonicalize($currencyCode);
public static function getSymbol($currencyCode);
public static function getLocalizedName($currencyCode, $displayLocale = null);
public static function getLocalizedNames($displayLocale = null);
public static function getFractionDigits($currencyCode);
public static function getRoundingIncrement($currencyCode);
/**
* Returns the ISO 4217 numeric code of a currency.
*
* For example, the numeric code of the Canadian Dollar ("CAD") is 124. If
* no numeric code is available for a currency, 0 is returned.
*
* @param string $currency A canonical ISO 4217 three-letter currency code
* (e.g. "EUR")
*
* @return integer The numeric code
*
* @throws InvalidArgumentException If the currency code does not exist
*/
public static function getNumericCode($currencyCode);
/**
* Returns the matching ISO 4217 three-letter codes for a numeric code.
*
* For example, the numeric code 124 belongs to the Canadian Dollar ("CAD").
* Some numeric codes belong to multiple currencies. For example, the
* number 428 is assigned to both the Latvian Ruble ("LVR") and the Latvian
* Lats ("LVL"). For this reason, this method always returns an array.
*
* @param integer $numericCode An ISO 4217 numeric currency code (e.g. 124)
*
* @return string[] The matching ISO 4217 three-letter currency codes
*
* @throws InvalidArgumentException If the numeric code does not exist
*/
public static function forNumericCode($numericCode);
}
Languages API
class Languages
{
/**
* Returns all available languages.
*
* Languages are returned as lowercase ISO 639-1 two-letter language codes.
* For languages that don't have a two-letter code, the ISO 639-2
* three-letter code is used instead.
*
* A full table of ISO 639 language codes can be found here:
* http://www-01.sil.org/iso639-3/codes.asp
*
* @return string[] An array of canonical ISO 639 language codes
*/
public static function getLanguageCodes();
public static function exists($languageCode);
public static function canonicalize($languageCode);
public static function getLocalizedName($languageCode, $displayLocale = null);
public static function getLocalizedNames($displayLocale = null);
/**
* Returns the ISO 639-2 three-letter code of a language.
*
* @param string $language A canonical ISO 639 language code (e.g. "en")
*
* @return string The ISO 639-2 three-letter code of the language
*
* @throws InvalidArgumentException If the language is invalid
* @throws MissingResourceException If the language has no
* corresponding three-letter code
*/
public static function getAlpha3Code($languageCode)
}
Locales API
class Locales
{
/**
* Returns all available locales.
*
* @return string[] A list of ICU locale codes
*/
public static function getLocales();
public static function exists($locale);
public static function canonicalize($locale);
public static function getLocalizedName($locale, $displayLocale = null);
public static function getLocalizedNames($displayLocale = null);
/**
* Returns a list of locale aliases.
*
* @return string[] An array with locale aliases as keys and ICU locale
* codes as values
*/
public static function getAliases();
/**
* Returns the fallback locale for a given locale, if any
*
* @param string $locale The ICU locale code to find the fallback for.
*
* @return string|null The ICU locale code of the fallback locale, or null
* if no fallback exists
*/
public static function getFallbackLocale($locale);
}
Countries API
class Countries
{
public static function getCountryCodes();
public static function exists($countryCode);
public static function canonicalize($countryCode);
public static function getLocalizedName($countryCode, $displayLocale = null);
public static function getLocalizedNames($displayLocale = null);
public static function getAlpha3Code($countryCode);
public static function forAlpha3Code($alpha3Code);
}
Scripts API
class Scripts
{
public static function getScriptCodes();
public static function exists($scriptCode);
public static function canonicalize($scriptCode);
public static function getLocalizedName($scriptCode, $displayLocale = null);
public static function getLocalizedNames($displayLocale = null);
}