[Intl] Add FallbackTrait for data generation

This commit is contained in:
Roland Franssen 2019-05-08 20:13:28 +02:00
parent 75d1dd45e5
commit 36ddfd58b9
2 changed files with 102 additions and 53 deletions

View 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 ?: [];
}
}

View File

@ -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,12 +135,10 @@ 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, ];
];
}
} }
/** /**
@ -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;
}
} }