4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule
;
14 use Symfony\Component\Intl\Exception\RuntimeException
;
15 use Symfony\Component\Intl\Intl
;
16 use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface
;
17 use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface
;
18 use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter
;
21 * The rule for compiling the locale bundle.
23 * @author Bernhard Schussek <bschussek@gmail.com>
25 class LocaleBundleTransformationRule
implements TransformationRuleInterface
28 * @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface
30 private $languageBundle;
33 * @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface
35 private $regionBundle;
37 public function __construct()
39 $this->languageBundle
= Intl
::getLanguageBundle();
40 $this->regionBundle
= Intl
::getRegionBundle();
46 public function getBundleName()
54 public function beforeCompile(CompilationContextInterface
$context)
56 $tempDir = sys_get_temp_dir() . '/icu-data-locales';
58 $context->getFilesystem()->remove($tempDir);
59 $context->getFilesystem()->mkdir($tempDir);
61 $this->generateTextFiles($tempDir, $this->scanLocales($context));
69 public function afterCompile(CompilationContextInterface
$context)
71 $context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales');
77 public function beforeCreateStub(StubbingContextInterface
$context)
80 'Locales' => Intl
::getLocaleBundle()->getLocaleNames('en'),
87 public function afterCreateStub(StubbingContextInterface
$context)
91 private function scanLocales(CompilationContextInterface
$context)
93 $tempDir = sys_get_temp_dir() . '/icu-data-locales-source';
95 $context->getFilesystem()->remove($tempDir);
96 $context->getFilesystem()->mkdir($tempDir);
98 // Temporarily generate the resource bundles
99 $context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir);
101 // Discover the list of supported locales, which are the names of the resource
102 // bundles in the "locales" directory
103 $locales = glob($tempDir . '/*.res');
105 // Remove file extension and sort
106 array_walk($locales, function (&$locale) { $locale
= basename($locale
, '.res'); });
109 // Delete unneeded locales
110 foreach ($locales as $key => $locale) {
111 // Delete all aliases from the list
112 // i.e., "az_AZ" is an alias for "az_Latn_AZ"
113 $content = file_get_contents($context->getSourceDir() . '/locales/' . $locale . '.txt');
115 // The key "%%ALIAS" is not accessible through the \ResourceBundle class,
116 // so look in the original .txt file instead
117 if (strpos($content, '%%ALIAS') !== false) {
118 unset($locales[$key]);
121 // Delete locales that have no content (i.e. only "Version" key)
122 $bundle = new \
ResourceBundle($locale, $tempDir);
124 if (null === $bundle) {
125 throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir);
128 // There seems to be no other way for identifying all keys in this specific
130 if (array_keys(iterator_to_array($bundle)) === array('Version')) {
131 unset($locales[$key]);
135 $context->getFilesystem()->remove($tempDir);
140 private function generateTextFiles($targetDirectory, array $locales)
142 $displayLocales = array_unique(array_merge(
143 $this->languageBundle
->getLocales(),
144 $this->regionBundle
->getLocales()
147 $txtWriter = new TextBundleWriter();
149 // Generate a list of locale names in the language of each display locale
150 // Each locale name has the form: "Language (Script, Region, Variant1, ...)
151 // Script, Region and Variants are optional. If none of them is available,
152 // the braces are not printed.
153 foreach ($displayLocales as $displayLocale) {
154 // Don't include ICU's root resource bundle
155 if ('root' === $displayLocale) {
161 foreach ($locales as $locale) {
162 // Don't include ICU's root resource bundle
163 if ($locale === 'root') {
167 if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) {
168 $names[$locale] = $name;
172 // If no names could be generated for the current locale, skip it
173 if (0 === count($names)) {
177 $txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names));
181 private function generateLocaleName($locale, $displayLocale)
185 $lang = \Locale
::getPrimaryLanguage($locale);
186 $script = \Locale
::getScript($locale);
187 $region = \Locale
::getRegion($locale);
188 $variants = \Locale
::getAllVariants($locale);
190 // Currently the only available variant is POSIX, which we don't want
191 // to include in the list
192 if (count($variants) > 0) {
196 // Some languages are translated together with their region,
197 // i.e. "en_GB" is translated as "British English"
198 // we don't include these languages though because they mess up
200 // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region);
202 // Some languages are simply not translated
203 // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans)
204 if (null === ($name = $this->languageBundle
->getLanguageName($lang, null, $displayLocale))) {
208 // "as" (Assamese) has no "Variants" block
209 //if (!$langBundle->get('Variants')) {
215 // Discover the name of the script part of the locale
216 // i.e. in zh_Hans_MO, "Hans" is the script
218 // Some scripts are not translated into every language
219 if (null === ($scriptName = $this->languageBundle
->getScriptName($script, $lang, $displayLocale))) {
223 $extras[] = $scriptName;
226 // Discover the name of the region part of the locale
227 // i.e. in de_AT, "AT" is the region
229 // Some regions are not translated into every language
230 if (null === ($regionName = $this->regionBundle
->getCountryName($region, $displayLocale))) {
234 $extras[] = $regionName;
237 if (count($extras) > 0) {
238 // Remove any existing extras
239 // For example, in German, zh_Hans is "Chinesisch (vereinfacht)".
240 // The latter is the script part which is already included in the
241 // extras and will be appended again with the other extras.
242 if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) {
246 $name .= ' ('.implode(', ', $extras).')';