minor #31432 [Intl] Add FallbackTrait for data generation (ro0NL)
This PR was merged into the 3.4 branch.
Discussion
----------
[Intl] Add FallbackTrait for data generation
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License | MIT
| Doc PR | symfony/symfony-docs#... <!-- required for new features -->
This is the last architectural change for the Intl data compilation. Promised.
It fixes de-duplicating a locale from its fallback locale. The problem is it uses a while-loop, comparing the locale to each fallback locale.
Given
- `root` (val=A)
- `ur` (val=B)
- `ur_IN` (val=A)
We have an edge case where a locale (ur_IN) override its fallback locale (ur), setting/restoring the value back to the root locale. This happens for the GMT format in the timezone bundle i know of ... in this case the `ur_IN` locale needs to write its own value.
The current approach is a while-loop comparing each fallback locale (ur, root) to the current locale (ur_IN). Eventually comparing `ur_IN <> root`, which causes a wrong diff, as such `ur_IN` falls back to `ur` providing the wrong value (val=B, where val=A is expected).
The new approach uses recursion so we only compare `ur <> ur_IN`, where `ur_IN` on itself is compared to `root`.
4.2) https://github.com/ro0NL/symfony/commit/e24d8e6
4.3) https://github.com/ro0NL/symfony/commit/31591d0
Commits
-------
36ddfd58b9
[Intl] Add FallbackTrait for data generation
This commit is contained in:
commit
9ce27da407
73
src/Symfony/Component/Intl/Data/Generator/FallbackTrait.php
Normal file
73
src/Symfony/Component/Intl/Data/Generator/FallbackTrait.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Intl\Data\Generator;
|
||||||
|
|
||||||
|
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
|
||||||
|
use Symfony\Component\Intl\Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
trait FallbackTrait
|
||||||
|
{
|
||||||
|
private $fallbackCache = [];
|
||||||
|
private $generatingFallback = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $tempDir
|
||||||
|
* @param string $displayLocale
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*
|
||||||
|
* @see AbstractDataGenerator::generateDataForLocale()
|
||||||
|
*/
|
||||||
|
abstract protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $tempDir
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*
|
||||||
|
* @see AbstractDataGenerator::generateDataForRoot()
|
||||||
|
*/
|
||||||
|
abstract protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $tempDir
|
||||||
|
* @param string $displayLocale
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function generateFallbackData(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
|
||||||
|
{
|
||||||
|
if (null === $fallback = Locale::getFallback($displayLocale)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->fallbackCache[$fallback])) {
|
||||||
|
return $this->fallbackCache[$fallback];
|
||||||
|
}
|
||||||
|
|
||||||
|
$prevGeneratingFallback = $this->generatingFallback;
|
||||||
|
$this->generatingFallback = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$data = 'root' === $fallback ? $this->generateDataForRoot($reader, $tempDir) : $this->generateDataForLocale($reader, $tempDir, $fallback);
|
||||||
|
} finally {
|
||||||
|
$this->generatingFallback = $prevGeneratingFallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fallbackCache[$fallback] = $data ?: [];
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@ use Symfony\Component\Intl\Data\Bundle\Compiler\BundleCompilerInterface;
|
|||||||
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
|
use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface;
|
||||||
use Symfony\Component\Intl\Data\Util\LocaleScanner;
|
use Symfony\Component\Intl\Data\Util\LocaleScanner;
|
||||||
use Symfony\Component\Intl\Exception\MissingResourceException;
|
use Symfony\Component\Intl\Exception\MissingResourceException;
|
||||||
use Symfony\Component\Intl\Locale;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The rule for compiling the locale bundle.
|
* The rule for compiling the locale bundle.
|
||||||
@ -28,10 +27,10 @@ use Symfony\Component\Intl\Locale;
|
|||||||
*/
|
*/
|
||||||
class LocaleDataGenerator extends AbstractDataGenerator
|
class LocaleDataGenerator extends AbstractDataGenerator
|
||||||
{
|
{
|
||||||
private $locales;
|
use FallbackTrait;
|
||||||
private $localeAliases;
|
|
||||||
private $fallbackMapping;
|
private $locales = [];
|
||||||
private $fallbackCache = [];
|
private $localeAliases = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
@ -40,7 +39,6 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
{
|
{
|
||||||
$this->locales = $scanner->scanLocales($sourceDir.'/locales');
|
$this->locales = $scanner->scanLocales($sourceDir.'/locales');
|
||||||
$this->localeAliases = $scanner->scanAliases($sourceDir.'/locales');
|
$this->localeAliases = $scanner->scanAliases($sourceDir.'/locales');
|
||||||
$this->fallbackMapping = $this->generateFallbackMapping(array_diff($this->locales, array_keys($this->localeAliases)), $this->localeAliases);
|
|
||||||
|
|
||||||
return $this->locales;
|
return $this->locales;
|
||||||
}
|
}
|
||||||
@ -64,7 +62,6 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
*/
|
*/
|
||||||
protected function preGenerate()
|
protected function preGenerate()
|
||||||
{
|
{
|
||||||
$this->fallbackCache = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,7 +70,8 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
|
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
|
||||||
{
|
{
|
||||||
// Don't generate aliases, as they are resolved during runtime
|
// Don't generate aliases, as they are resolved during runtime
|
||||||
if (isset($this->localeAliases[$displayLocale])) {
|
// Unless an alias is needed as fallback for de-duplication purposes
|
||||||
|
if (isset($this->localeAliases[$displayLocale]) && !$this->generatingFallback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +83,7 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
$localeNames = [];
|
$localeNames = [];
|
||||||
foreach ($this->locales as $locale) {
|
foreach ($this->locales as $locale) {
|
||||||
// Ensure a normalized list of pure locales
|
// Ensure a normalized list of pure locales
|
||||||
if (isset($this->localeAliases[$displayLocale]) || \Locale::getAllVariants($locale)) {
|
if (\Locale::getAllVariants($locale)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,21 +100,27 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process again to de-duplicate locales and their fallback locales
|
$data = [
|
||||||
// Only keep the differences
|
'Names' => $localeNames,
|
||||||
$fallback = $displayLocale;
|
];
|
||||||
while (isset($this->fallbackMapping[$fallback])) {
|
|
||||||
if (!isset($this->fallbackCache[$fallback = $this->fallbackMapping[$fallback]])) {
|
// Don't de-duplicate a fallback locale
|
||||||
$this->fallbackCache[$fallback] = $this->generateDataForLocale($reader, $tempDir, $fallback) ?: [];
|
// Ensures the display locale can be de-duplicated on itself
|
||||||
}
|
if ($this->generatingFallback) {
|
||||||
if (isset($this->fallbackCache[$fallback]['Names'])) {
|
return $data;
|
||||||
$localeNames = array_diff($localeNames, $this->fallbackCache[$fallback]['Names']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($localeNames) {
|
// Process again to de-duplicate locale and its fallback locales
|
||||||
return ['Names' => $localeNames];
|
// Only keep the differences
|
||||||
|
$fallbackData = $this->generateFallbackData($reader, $tempDir, $displayLocale);
|
||||||
|
if (isset($fallbackData['Names'])) {
|
||||||
|
$data['Names'] = array_diff($data['Names'], $fallbackData['Names']);
|
||||||
}
|
}
|
||||||
|
if (!$data['Names']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,13 +135,11 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
*/
|
*/
|
||||||
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
|
protected function generateDataForMeta(BundleEntryReaderInterface $reader, $tempDir)
|
||||||
{
|
{
|
||||||
if ($this->locales || $this->localeAliases) {
|
|
||||||
return [
|
return [
|
||||||
'Locales' => $this->locales,
|
'Locales' => $this->locales,
|
||||||
'Aliases' => $this->localeAliases,
|
'Aliases' => $this->localeAliases,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
@ -175,30 +177,4 @@ class LocaleDataGenerator extends AbstractDataGenerator
|
|||||||
|
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateFallbackMapping(array $displayLocales, array $aliases)
|
|
||||||
{
|
|
||||||
$displayLocales = array_flip($displayLocales);
|
|
||||||
$mapping = [];
|
|
||||||
|
|
||||||
foreach ($displayLocales as $displayLocale => $_) {
|
|
||||||
$mapping[$displayLocale] = null;
|
|
||||||
$fallback = $displayLocale;
|
|
||||||
|
|
||||||
// Recursively search for a fallback locale until one is found
|
|
||||||
while (null !== ($fallback = Locale::getFallback($fallback))) {
|
|
||||||
// Currently, no locale has an alias as fallback locale.
|
|
||||||
// If this starts to be the case, we need to add code here.
|
|
||||||
\assert(!isset($aliases[$fallback]));
|
|
||||||
|
|
||||||
// Check whether the fallback exists
|
|
||||||
if (isset($displayLocales[$fallback])) {
|
|
||||||
$mapping[$displayLocale] = $fallback;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $mapping;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user