Skip to content

Commit

Permalink
bug #31411 [Intl] Fix root fallback locale (ro0NL)
Browse files Browse the repository at this point in the history
This PR was merged into the 3.4 branch.

Discussion
----------

[Intl] Fix root fallback locale

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass?   | yes (including intl-data group)
| Fixed tickets | #...   <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

We should never return "root" as a fallback locale for the "root" locale itself.

While at it, i realized the alias meta files are pointless :)

4.2) https://github.com/ro0NL/symfony/commit/b9fc8b785773cc38cef86dd0136046234fd77331
4.3) https://github.com/ro0NL/symfony/commit/922a1eb

Commits
-------

11ff24a [Intl] Fix root fallback locale
  • Loading branch information
fabpot committed May 8, 2019
2 parents 6c6f76f + 11ff24a commit 75d1dd4
Show file tree
Hide file tree
Showing 46 changed files with 68 additions and 199 deletions.
46 changes: 17 additions & 29 deletions src/Symfony/Component/Intl/Data/Generator/LocaleDataGenerator.php
Expand Up @@ -14,9 +14,6 @@
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
use Symfony\Component\Intl\Data\Provider\LanguageDataProvider;
use Symfony\Component\Intl\Data\Provider\RegionDataProvider;
use Symfony\Component\Intl\Data\Provider\ScriptDataProvider;
use Symfony\Component\Intl\Data\Util\LocaleScanner;
use Symfony\Component\Intl\Exception\MissingResourceException;
use Symfony\Component\Intl\Locale;
Expand All @@ -31,23 +28,11 @@
*/
class LocaleDataGenerator extends AbstractDataGenerator
{
private $languageDataProvider;
private $scriptDataProvider;
private $regionDataProvider;
private $locales;
private $localeAliases;
private $fallbackMapping;
private $fallbackCache = [];

public function __construct(BundleCompilerInterface $compiler, $dirName, LanguageDataProvider $languageDataProvider, ScriptDataProvider $scriptDataProvider, RegionDataProvider $regionDataProvider)
{
parent::__construct($compiler, $dirName);

$this->languageDataProvider = $languageDataProvider;
$this->scriptDataProvider = $scriptDataProvider;
$this->regionDataProvider = $regionDataProvider;
}

/**
* {@inheritdoc}
*/
Expand All @@ -66,8 +51,12 @@ protected function scanLocales(LocaleScanner $scanner, $sourceDir)
protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $sourceDir, $tempDir)
{
$filesystem = new Filesystem();
$filesystem->mkdir($tempDir.'/lang');
$filesystem->mkdir([
$tempDir.'/lang',
$tempDir.'/region',
]);
$compiler->compile($sourceDir.'/lang', $tempDir.'/lang');
$compiler->compile($sourceDir.'/region', $tempDir.'/region');
}

/**
Expand All @@ -83,19 +72,14 @@ protected function preGenerate()
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
// Generate aliases, needed to enable proper fallback from alias to its
// target
// Don't generate aliases, as they are resolved during runtime
if (isset($this->localeAliases[$displayLocale])) {
return ['%%ALIAS' => $this->localeAliases[$displayLocale]];
return;
}

// Generate locale names for all locales that have translations in
// at least the language or the region bundle
try {
$displayFormat = $reader->readEntry($tempDir.'/lang', $displayLocale, ['localeDisplayPattern']);
} catch (MissingResourceException $e) {
$displayFormat = $reader->readEntry($tempDir.'/lang', 'root', ['localeDisplayPattern']);
}
$displayFormat = $reader->readEntry($tempDir.'/lang', $displayLocale, ['localeDisplayPattern']);
$pattern = $displayFormat['pattern'] ?? '{0} ({1})';
$separator = $displayFormat['separator'] ?? '{0}, {1}';
$localeNames = [];
Expand All @@ -110,7 +94,7 @@ protected function generateDataForLocale(BundleEntryReaderInterface $reader, $te
// Each locale name has the form: "Language (Script, Region, Variant1, ...)
// Script, Region and Variants are optional. If none of them is
// available, the braces are not printed.
$localeNames[$locale] = $this->generateLocaleName($locale, $displayLocale, $pattern, $separator);
$localeNames[$locale] = $this->generateLocaleName($reader, $tempDir, $locale, $displayLocale, $pattern, $separator);
} catch (MissingResourceException $e) {
// Silently ignore incomplete locale names
// In this case one should configure at least one fallback locale that is complete (e.g. English) during
Expand Down Expand Up @@ -158,22 +142,26 @@ protected function generateDataForMeta(BundleEntryReaderInterface $reader, $temp
/**
* @return string
*/
private function generateLocaleName($locale, $displayLocale, $pattern, $separator)
private function generateLocaleName(BundleEntryReaderInterface $reader, $tempDir, $locale, $displayLocale, $pattern, $separator)
{
// Apply generic notation using square brackets as described per http://cldr.unicode.org/translation/language-names
$name = str_replace(['(', ')'], ['[', ']'], $this->languageDataProvider->getName(\Locale::getPrimaryLanguage($locale), $displayLocale));
$name = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Languages', \Locale::getPrimaryLanguage($locale)]));
$extras = [];

// Discover the name of the script part of the locale
// i.e. in zh_Hans_MO, "Hans" is the script
if ($script = \Locale::getScript($locale)) {
$extras[] = str_replace(['(', ')'], ['[', ']'], $this->scriptDataProvider->getName($script, $displayLocale));
$extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/lang', $displayLocale, ['Scripts', $script]));
}

// Discover the name of the region part of the locale
// i.e. in de_AT, "AT" is the region
if ($region = \Locale::getRegion($locale)) {
$extras[] = str_replace(['(', ')'], ['[', ']'], $this->regionDataProvider->getName($region, $displayLocale));
if (!RegionDataGenerator::isValidCountryCode($region)) {
throw new MissingResourceException('Skipping "'.$locale.'" due an invalid country.');
}

$extras[] = str_replace(['(', ')'], ['[', ']'], $reader->readEntry($tempDir.'/region', $displayLocale, ['Countries', $region]));
}

if ($extras) {
Expand Down
21 changes: 15 additions & 6 deletions src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php
Expand Up @@ -48,6 +48,20 @@ class RegionDataGenerator extends AbstractDataGenerator
*/
private $regionCodes = [];

public static function isValidCountryCode($region)
{
if (isset(self::$blacklist[$region])) {
return false;
}

// WORLD/CONTINENT/SUBCONTINENT/GROUPING
if (ctype_digit($region) || \is_int($region)) {
return false;
}

return true;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -125,12 +139,7 @@ protected function generateRegionNames(ArrayAccessibleResourceBundle $localeBund
$regionNames = [];

foreach ($unfilteredRegionNames as $region => $regionName) {
if (isset(self::$blacklist[$region])) {
continue;
}

// WORLD/CONTINENT/SUBCONTINENT/GROUPING
if (ctype_digit($region) || \is_int($region)) {
if (!self::isValidCountryCode($region)) {
continue;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Symfony/Component/Intl/Locale.php
Expand Up @@ -31,7 +31,7 @@ final class Locale extends \Locale
* The default fallback locale is used as fallback for locales that have no
* fallback otherwise.
*
* @param string $locale The default fallback locale
* @param string|null $locale The default fallback locale
*
* @see getFallback()
*/
Expand All @@ -43,7 +43,7 @@ public static function setDefaultFallback($locale)
/**
* Returns the default fallback locale.
*
* @return string The default fallback locale
* @return string|null The default fallback locale
*
* @see setDefaultFallback()
* @see getFallback()
Expand All @@ -70,7 +70,7 @@ public static function getFallback($locale)
if (\function_exists('locale_parse')) {
$localeSubTags = locale_parse($locale);
if (1 === \count($localeSubTags)) {
if (self::$defaultFallback === $localeSubTags['language']) {
if ('root' !== self::$defaultFallback && self::$defaultFallback === $localeSubTags['language']) {
return 'root';
}

Expand Down Expand Up @@ -98,7 +98,7 @@ public static function getFallback($locale)
return substr($locale, 0, $pos);
}

if (self::$defaultFallback === $locale) {
if ('root' !== self::$defaultFallback && self::$defaultFallback === $locale) {
return 'root';
}

Expand Down
45 changes: 8 additions & 37 deletions src/Symfony/Component/Intl/Resources/bin/update-data.php
Expand Up @@ -11,18 +11,13 @@

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Intl\Data\Bundle\Compiler\GenrbCompiler;
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader;
use Symfony\Component\Intl\Data\Bundle\Reader\JsonBundleReader;
use Symfony\Component\Intl\Data\Bundle\Writer\JsonBundleWriter;
use Symfony\Component\Intl\Data\Generator\CurrencyDataGenerator;
use Symfony\Component\Intl\Data\Generator\GeneratorConfig;
use Symfony\Component\Intl\Data\Generator\LanguageDataGenerator;
use Symfony\Component\Intl\Data\Generator\LocaleDataGenerator;
use Symfony\Component\Intl\Data\Generator\RegionDataGenerator;
use Symfony\Component\Intl\Data\Generator\ScriptDataGenerator;
use Symfony\Component\Intl\Data\Provider\LanguageDataProvider;
use Symfony\Component\Intl\Data\Provider\RegionDataProvider;
use Symfony\Component\Intl\Data\Provider\ScriptDataProvider;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Intl\Locale;
use Symfony\Component\Intl\Util\GitRepository;
Expand Down Expand Up @@ -171,27 +166,13 @@
$compiler = new GenrbCompiler($genrb, $genrbEnv);
$config = new GeneratorConfig($sourceDir.'/data', $icuVersionInDownload);
$jsonDir = dirname(__DIR__).'/data';
$targetDirs = [$jsonDir];
$workingDirs = [$jsonDir];

$config->addBundleWriter($jsonDir, new JsonBundleWriter());

echo "Starting resource bundle compilation. This may take a while...\n";

$filesystem->remove($workingDirs);

foreach ($workingDirs as $targetDir) {
$filesystem->mkdir([
$targetDir.'/'.Intl::CURRENCY_DIR,
$targetDir.'/'.Intl::LANGUAGE_DIR,
$targetDir.'/'.Intl::LOCALE_DIR,
$targetDir.'/'.Intl::REGION_DIR,
$targetDir.'/'.Intl::SCRIPT_DIR,
]);
}

// We don't want to use fallback to English during generation
Locale::setDefaultFallback(null);
Locale::setDefaultFallback('root');

echo "Generating language data...\n";

Expand All @@ -215,14 +196,7 @@

echo "Generating locale data...\n";

$reader = new BundleEntryReader(new JsonBundleReader());
$generator = new LocaleDataGenerator(
$compiler,
Intl::LOCALE_DIR,
new LanguageDataProvider($jsonDir.'/'.Intl::LANGUAGE_DIR, $reader),
new ScriptDataProvider($jsonDir.'/'.Intl::SCRIPT_DIR, $reader),
new RegionDataProvider($jsonDir.'/'.Intl::REGION_DIR, $reader)
);
$generator = new LocaleDataGenerator($compiler, Intl::LOCALE_DIR);
$generator->generateData($config);

echo "Resource bundle compilation complete.\n";
Expand All @@ -238,18 +212,15 @@
GIT_INFO;

foreach ($targetDirs as $targetDir) {
$gitInfoFile = $targetDir.'/git-info.txt';
$gitInfoFile = $jsonDir.'/git-info.txt';

file_put_contents($gitInfoFile, $gitInfo);
file_put_contents($gitInfoFile, $gitInfo);

echo "Wrote $gitInfoFile.\n";
echo "Wrote $gitInfoFile.\n";

$versionFile = $targetDir.'/version.txt';
$versionFile = $jsonDir.'/version.txt';

file_put_contents($versionFile, "$icuVersionInDownload\n");

echo "Wrote $versionFile.\n";
}
file_put_contents($versionFile, "$icuVersionInDownload\n");

echo "Wrote $versionFile.\n";
echo "Done.\n";
3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/az_AZ.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/bs_BA.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/en_NH.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/en_RH.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/ff_CM.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/ff_GN.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/ff_MR.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/ff_SN.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/in.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/in_ID.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/iw.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/iw_IL.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/mo.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/no.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/no_NO.json

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/pa_IN.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/pa_PK.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sh.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sh_BA.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sh_CS.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sh_YU.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_BA.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_CS.json

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_ME.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_RS.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_XK.json

This file was deleted.

3 changes: 0 additions & 3 deletions src/Symfony/Component/Intl/Resources/data/locales/sr_YU.json

This file was deleted.

0 comments on commit 75d1dd4

Please sign in to comment.