8000 [Intl] Revise timezone name generation by ro0NL · Pull Request #31434 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Intl] Revise timezone name generation #31434

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

Merged
merged 1 commit into from
May 9, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 111 additions & 96 deletions src/Symfony/Component/Intl/Data/Generator/TimezoneDataGenerator.php

Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@
*/
class TimezoneDataGenerator extends AbstractDataGenerator
{
use FallbackTrait;

/**
* Collects all available zone IDs.
*
* @var string[]
*/
private $zoneIds = [];
private $zoneToCountryMapping = [];
private $localeAliases = [];

/**
* {@inheritdoc}
*/
protected function scanLocales(LocaleScanner $scanner, $sourceDir)
{
$this->localeAliases = $scanner->scanAliases($sourceDir.'/locales');

return $scanner->scanLocales($sourceDir.'/zone');
}

Expand All @@ -63,50 +69,71 @@ protected function compileTemporaryBundles(BundleCompilerInterface $compiler, $s
protected function preGenerate()
{
$this->zoneIds = [];
$this->zoneToCountryMapping = [];
}

/**
* {@inheritdoc}
*/
protected function generateDataForLocale(BundleEntryReaderInterface $reader, $tempDir, $displayLocale)
{
$localeBundle = $reader->read($tempDir, $displayLocale);
if (!$this->zoneToCountryMapping) {
$this->zoneToCountryMapping = self::generateZoneToCountryMapping($reader->read($tempDir, 'windowsZones'));
}

if (isset($localeBundle['zoneStrings']) && null !== $localeBundle['zoneStrings']) {
$localeBundles = [$localeBundle];
$fallback = $displayLocale;
while (null !== ($fallback = Locale::getFallback($fallback))) {
$localeBundles[] = $reader->read($tempDir, $fallback);
}
$metadata = [];
$data = [
'Version' => $localeBundle['Version'],
'Names' => $this->generateZones(
$reader,
$tempDir,
$displayLocale,
$localeBundles,
$metadata
),
];

if (!$data['Names'] && !$metadata) {
return;
}
// Don't generate aliases, as they are resolved during runtime
// Unless an alias is needed as fallback for de-duplication purposes
if (isset($this->localeAliases[$displayLocale]) && !$this->generatingFallback) {
return;
}

$data['Meta'] = $metadata;
$localeBundle = $reader->read($tempDir, $displayLocale);

if (!isset($localeBundle['zoneStrings']) || null === $localeBundle['zoneStrings']) {
return;
}

$this->zoneIds = array_merge($this->zoneIds, array_keys($data['Names']));
$data = [
'Version' => $localeBundle['Version'],
'Names' => $this->generateZones($reader, $tempDir, $displayLocale),
'Meta' => self::generateZoneMetadata($localeBundle),
];

// Don't de-duplicate a fallback locale
// Ensures the display locale can be de-duplicated on itself
if ($this->generatingFallback) {
return $data;
}

// Process again to de-duplicate locales and their fallback locales
// Only keep the differences
$fallback = $this->generateFallbackData($reader, $tempDir, $displayLocale);
if (isset($fallback['Names'])) {
$data['Names'] = array_diff($data['Names'], $fallback['Names']);
}
if (isset($fallback['Meta'])) {
$data['Meta'] = array_diff($data['Meta'], $fallback['Meta']);
}
if (!$data['Names'] && !$data['Meta']) {
return;
}

$this->zoneIds = array_merge($this->zoneIds, array_keys($data['Names']));

return $data;
}

/**
* {@inheritdoc}
*/
protected function generateDataForRoot(BundleEntryReaderInterface $reader, $tempDir)
{
$rootBundle = $reader->read($tempDir, 'root');

return [
'Version' => $rootBundle['Version'],
'Meta' => self::generateZoneMetadata($rootBundle),
];
}

/**
Expand All @@ -119,66 +146,21 @@ protected function generateDataForMeta(BundleEntryReaderInterface $reader, $temp
$this->zoneIds = array_unique($this->zoneIds);

sort($this->zoneIds);
ksort($this->zoneToCountryMapping);

$data = [
'Version' => $rootBundle['Version'],
'Zones' => $this->zoneIds,
'ZoneToCountry' => self::generateZoneToCountryMapping($reader->read($tempDir, 'windowsZones')),
'ZoneToCountry' => $this->zoneToCountryMapping,
'CountryToZone' => self::generateCountryToZoneMapping($this->zoneToCountryMapping),
];

$data['CountryToZone'] = self::generateCountryToZoneMapping($data['ZoneToCountry']);

return $data;
}

/**
* @param ArrayAccessibleResourceBundle[] $localeBundles
*/
private function generateZones(BundleEntryReaderInterface $reader, string $tempDir, string $locale, array $localeBundles, array &$metadata = []): array
private function generateZones(BundleEntryReaderInterface $reader, string $tempDir, string $locale): array
{
$typeBundle = $reader->read($tempDir, 'timezoneTypes');
$metaBundle = $reader->read($tempDir, 'metaZones');
$windowsZonesBundle = $reader->read($tempDir, 'windowsZones');
$accessor = static function (ArrayAccessibleResourceBundle $resourceBundle, array $indices) {
$result = $resourceBundle;
foreach ($indices as $indice) {
$result = $result[$indice] ?? null;
}

return $result;
};
$accessor = static function (array $indices, &$inherited = false) use ($localeBundles, $accessor) {
$inherited = false;
foreach ($localeBundles as $i => $localeBundle) {
$nextLocaleBundle = $localeBundles[$i + 1] ?? null;
$result = $accessor($localeBundle, $indices);
if (null !== $result && (null === $nextLocaleBundle || $result !== $accessor($nextLocaleBundle, $indices))) {
$inherited = 0 !== $i;

return $result;
}
}

return null;
};
$regionFormat = $reader->readEntry($tempDir, $locale, ['zoneStrings', 'regionFormat']);
$fallbackFormat = $reader->readEntry($tempDir, $locale, ['zoneStrings', 'fallbackFormat']);
$zoneToCountry = self::generateZoneToCountryMapping($windowsZonesBundle);
$resolveName = function (string $id, string $city = null) use ($reader, $tempDir, $locale, $regionFormat, $fallbackFormat, $zoneToCountry): ?string {
if (isset($zoneToCountry[$id])) {
try {
$country = $reader->readEntry($tempDir.'/region', $locale, ['Countries', $zoneToCountry[$id]]);
} catch (MissingResourceException $e) {
return null;
}
return null === $city ? str_replace('{0}', $country, $regionFormat) : str_replace(['{0}', '{1}'], [$city, $country], $fallbackFormat);
} elseif (null !== $city) {
return str_replace('{0}', $city, $regionFormat);
} else {
return str_replace(['/', '_'], ' ', 0 === strrpos($id, 'Etc/') ? substr($id, 4) : $id);
}
};
$available = [];
foreach ($typeBundle['typeMap']['timezone'] as $zone => $_) {
if ('Etc:Unknown' === $zone || preg_match('~^Etc:GMT[-+]\d+$~', $zone)) {
Expand All @@ -188,64 +170,97 @@ private function generateZones(BundleEntryReaderInterface $reader, string $tempD
$available[$zone] = true;
}

$metaBundle = $reader->read($tempDir, 'metaZones');
$metazones = [];
foreach ($metaBundle['metazoneInfo'] as $zone => $info) {
foreach ($info as $metazone) {
$metazones[$zone] = $metazone->get(0);
}
}

$isBase = false === strpos($locale, '_');
$regionFormat = $reader->readEntry($tempDir, $locale, ['zoneStrings', 'regionFormat']);
$fallbackFormat = $reader->readEntry($tempDir, $locale, ['zoneStrings', 'fallbackFormat']);
$resolveName = function (string $id, string $city = null) use ($reader, $tempDir, $locale, $regionFormat, $fallbackFormat): ?string {
// Resolve default name as described per http://cldr.unicode.org/translation/timezones
if (isset($this->zoneToCountryMapping[$id])) {
try {
$country = $reader->readEntry($tempDir.'/region', $locale, ['Countries', $this->zoneToCountryMapping[$id]]);
} catch (MissingResourceException $e) {
return null;
}

$name = str_replace('{0}', $country, $regionFormat);

return null === $city ? $name : str_replace(['{0}', '{1}'], [$city, $name], $fallbackFormat);
}
if (null !== $city) {
return str_replace('{0}', $city, $regionFormat);
}

return null;
};
$accessor = static function (array $indices, array ...$fallbackIndices) use ($locale, $reader, $tempDir) {
foreach (\func_get_args() as $indices) {
try {
return $reader->readEntry($tempDir, $locale, $indices);
} catch (MissingResourceException $e) {
}
}

return null;
};
$zones = [];
foreach (array_keys($available) as $zone) {
// lg: long generic, e.g. "Central European Time"
// ls: long specific (not DST), e.g. "Central European Standard Time"
// ld: long DST, e.g. "Central European Summer Time"
// ec: example city, e.g. "Amsterdam"
$name = $accessor(['zoneStrings', $zone, 'lg'], $nameInherited) ?? $accessor(['zoneStrings', $zone, 'ls'], $nameInherited);
$city = $accessor(['zoneStrings', $zone, 'ec'], $cityInherited);
$name = $accessor(['zoneStrings', $zone, 'lg'], ['zoneStrings', $zone, 'ls']);
$city = $accessor(['zoneStrings', $zone, 'ec']);
$id = str_replace(':', '/', $zone);

if (null === $name && isset($metazones[$zone])) {
$meta = 'meta:'.$metazones[$zone];
$name = $accessor(['zoneStrings', $meta, 'lg'], $nameInherited) ?? $accessor(['zoneStrings', $meta, 'ls'], $nameInherited);
$name = $accessor(['zoneStrings', $meta, 'lg'], ['zoneStrings', $meta, 'ls']);
}

// Infer a default English named city for all locales
// Ensures each timezone ID has a distinctive name
if (null === $city && 0 !== strrpos($zone, 'Etc:') && false !== $i = strrpos($zone, ':')) {
$city = str_replace('_', ' ', substr($zone, $i + 1));
$cityInherited = !$isBase;
}
if ($isBase && null === $name) {
if (null === $name) {
$name = $resolveName($id, $city);
$city = null;
}
if (
($nameInherited && $cityInherited)
|| (null === $name && null === $city)
|| ($nameInherited && null === $city)
|| ($cityInherited && null === $name)
) {
if (null === $name) {
continue;
}
if (null === $name) {
$name = $resolveName($id, $city);
} elseif (null !== $city && false === mb_stripos(str_replace('-', ' ', $name), str_replace('-', ' ', $city))) {

// Ensure no duplicated content is generated
if (null !== $city && false === mb_stripos(str_replace('-', ' ', $name), str_replace('-', ' ', $city))) {
$name = str_replace(['{0}', '{1}'], [$city, $name], $fallbackFormat);
}

$zones[$id] = $name;
}

$gmtFormat = $ac 10000 cessor(['zoneStrings', 'gmtFormat'], $gmtFormatInherited) ?? 'GMT{0}';
if (!$gmtFormatInherited || $isBase) {
$metadata['GmtFormat'] = str_replace('{0}', '%s', $gmtFormat);
}
return $zones;
}

$hourFormat = $accessor(['zoneStrings', 'hourFormat'], $hourFormatInherited) ?? '+HH:mm;-HH:mm';
if (!$hourFormatInherited || $isBase) {
$metadata['HourFormat'] = explode(';', str_replace(['HH', 'mm', 'H', 'm'], ['%02d', '%02d', '%d', '%d'], $hourFormat), 2);
private static function generateZoneMetadata(ArrayAccessibleResourceBundle $localeBundle): array
{
$metadata = [];
if (isset($localeBundle['zoneStrings']['gmtFormat'])) {
$metadata['GmtFormat'] = str_replace('{0}', '%s', $localeBundle['zoneStrings']['gmtFormat']);
}
if (isset($localeBundle['zoneStrings']['hourFormat'])) {
$hourFormat = explode(';', str_replace(['HH', 'mm', 'H', 'm'], ['%02d', '%02d', '%d', '%d'], $localeBundle['zoneStrings']['hourFormat']), 2);
$metadata['HourFormatPos'] = $hourFormat[0];
$metadata['HourFormatNeg'] = $hourFormat[1];
}

return $zones;
return $metadata;
}

private static function generateZoneToCountryMapping(ArrayAccessibleResourceBundle $windowsZoneBundle): array
Expand Down
24 changes: 9 additions & 15 deletions src/Symfony/Component/Intl/Resources/data/timezones/af.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
"America\/Detroit": "Noord-Amerikaanse oostelike tyd (Detroit)",
"America\/Dominica": "Atlantiese tyd (Dominica)",
"America\/Edmonton": "Noord-Amerikaanse bergtyd (Edmonton)",
"America\/Eirunepe": "Brasilië (Eirunepe)",
"America\/Eirunepe": "Brasilië-tyd (Eirunepe)",
"America\/El_Salvador": "Noord-Amerikaanse sentrale tyd (El Salvador)",
"America\/Fort_Nelson": "Noord-Amerikaanse bergtyd (Fort Nelson)",
"America\/Fortaleza": "Brasilia-tyd (Fortaleza)",
Expand Down Expand Up @@ -151,7 +151,7 @@
"America\/Moncton": "Atlantiese tyd (Moncton)",
"America\/Monterrey": "Noord-Amerikaanse sentrale tyd (Monterrey)",
"America\/Montevideo": "Uruguay-tyd (Montevideo)",
"America\/Montreal": "Kanada (Montreal)",
"America\/Montreal": "Kanada-tyd (Montreal)",
"America\/Montserrat": "Atlantiese tyd (Montserrat)",
"America\/Nassau": "Noord-Amerikaanse oostelike tyd (Nassau)",
"America\/New_York": "Noord-Amerikaanse oostelike tyd (New York)",
Expand All @@ -176,7 +176,7 @@
"America\/Recife": "Brasilia-tyd (Recife)",
"America\/Regina": "Noord-Amerikaanse sentrale tyd (Regina)",
"America\/Resolute": "Noord-Amerikaanse sentrale tyd (Resolute)",
"America\/Rio_Branco": "Brasilië (Rio Branco)",
"America\/Rio_Branco": "Brasilië-tyd (Rio Branco)",
"America\/Santa_Isabel": "Noordwes-Meksiko-tyd (Santa Isabel)",
"America\/Santarem": "Brasilia-tyd (Santarem)",
"America\/Santiago": "Chili-tyd (Santiago)",
Expand Down Expand Up @@ -226,7 +226,7 @@
"Asia\/Bahrain": "Arabiese tyd (Bahrein)",
"Asia\/Baku": "Aserbeidjan-tyd (Bakoe)",
"Asia\/Bangkok": "Indosjina-tyd (Bangkok)",
"Asia\/Barnaul": "Rusland (Barnaul)",
"Asia\/Barnaul": "Rusland-tyd (Barnaul)",
"Asia\/Beirut": "Oos-Europese tyd (Beiroet)",
"Asia\/Bishkek": "Kirgistan-tyd (Bisjkek)",
"Asia\/Brunei": "Broenei Darussalam-tyd",
Expand Down Expand Up @@ -288,9 +288,9 @@
"Asia\/Tehran": "Iran-tyd (Tehran)",
"Asia\/Thimphu": "Bhoetan-tyd (Thimphu)",
"Asia\/Tokyo": "Japan-tyd (Tokio)",
"Asia\/Tomsk": "Rusland (Tomsk)",
"Asia\/Tomsk": "Rusland-tyd (Tomsk)",
"Asia\/Ulaanbaatar": "Ulaanbaatar-tyd",
"Asia\/Urumqi": "Sjina (Urumqi)",
"Asia\/Urumqi": "Sjina-tyd (Urumqi)",
"Asia\/Ust-Nera": "Wladiwostok-tyd (Ust-Nera)",
"Asia\/Vientiane": "Indosjina-tyd (Vientiane)",
"Asia\/Vladivostok": "Wladiwostok-tyd",
Expand Down Expand Up @@ -341,11 +341,11 @@
"Europe\/Guernsey": "Greenwich-tyd (Guernsey)",
"Europe\/Helsinki": "Oos-Europese tyd (Helsinki)",
"Europe\/Isle_of_Man": "Greenwich-tyd (Eiland Man)",
"Europe\/Istanbul": "Turkye (Istanbul)",
"Europe\/Istanbul": "Turkye-tyd (Istanbul)",
"Europe\/Jersey": "Greenwich-tyd (Jersey)",
"Europe\/Kaliningrad": "Oos-Europese tyd (Kaliningrad)",
"Europe\/Kiev": "Oos-Europese tyd (Kiëf)",
"Europe\/Kirov": "Rusland (Kirov)",
"Europe\/Kirov": "Rusland-tyd (Kirov)",
"Europe\/Lisbon": "Wes-Europese tyd (Lissabon)",
"Europe\/Ljubljana": "Sentraal-Europese tyd (Ljubljana)",
"Europe\/London": "Greenwich-tyd (Londen)",
Expand Down Expand Up @@ -436,11 +436,5 @@
"Pacific\/Wake": "Wake-eiland-tyd",
"Pacific\/Wallis": "Wallis en Futuna-tyd (Mata-Utu)"
},
"Meta": {
"GmtFormat": "GMT%s",
"HourFormat": [
"+%02d:%02d",
"-%02d:%02d"
]
}
"Meta": []
}
Loading
0