Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Intl] Fix root fallback locale #31411

Merged
merged 1 commit into from May 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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']);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaulting to root during compile makes things like this more straightforward (the timezone generator in master also benefits)

i've verified it causes no side-effects in 4.2 / master 👍

$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)]));
Copy link
Contributor Author

@ro0NL ro0NL May 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now uses IntlBundleReader instead of JsonBundleReader ... total compilation time;

3.4
real    1m40.170s

PR
real    0m22.073s

moreover, this allows to keep the generators decoupled

$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
ro0NL marked this conversation as resolved.
Show resolved Hide resolved
*
* @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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is done in each generator already


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.