]>
Commit | Line | Data |
---|---|---|
4f5b44bd NL |
1 | <?php |
2 | ||
3 | /* | |
4 | * This file is part of the Symfony package. | |
5 | * | |
6 | * (c) Fabien Potencier <fabien@symfony.com> | |
7 | * | |
8 | * For the full copyright and license information, please view the LICENSE | |
9 | * file that was distributed with this source code. | |
10 | */ | |
11 | ||
12 | namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule; | |
13 | ||
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; | |
19 | ||
20 | /** | |
21 | * The rule for compiling the locale bundle. | |
22 | * | |
23 | * @author Bernhard Schussek <bschussek@gmail.com> | |
24 | */ | |
25 | class LocaleBundleTransformationRule implements TransformationRuleInterface | |
26 | { | |
27 | /** | |
28 | * @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface | |
29 | */ | |
30 | private $languageBundle; | |
31 | ||
32 | /** | |
33 | * @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface | |
34 | */ | |
35 | private $regionBundle; | |
36 | ||
37 | public function __construct() | |
38 | { | |
39 | $this->languageBundle = Intl::getLanguageBundle(); | |
40 | $this->regionBundle = Intl::getRegionBundle(); | |
41 | } | |
42 | ||
43 | /** | |
44 | * {@inheritdoc} | |
45 | */ | |
46 | public function getBundleName() | |
47 | { | |
48 | return 'locales'; | |
49 | } | |
50 | ||
51 | /** | |
52 | * {@inheritdoc} | |
53 | */ | |
54 | public function beforeCompile(CompilationContextInterface $context) | |
55 | { | |
56 | $tempDir = sys_get_temp_dir() . '/icu-data-locales'; | |
57 | ||
58 | $context->getFilesystem()->remove($tempDir); | |
59 | $context->getFilesystem()->mkdir($tempDir); | |
60 | ||
61 | $this->generateTextFiles($tempDir, $this->scanLocales($context)); | |
62 | ||
63 | return $tempDir; | |
64 | } | |
65 | ||
66 | /** | |
67 | * {@inheritdoc} | |
68 | */ | |
69 | public function afterCompile(CompilationContextInterface $context) | |
70 | { | |
71 | $context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales'); | |
72 | } | |
73 | ||
74 | /** | |
75 | * {@inheritdoc} | |
76 | */ | |
77 | public function beforeCreateStub(StubbingContextInterface $context) | |
78 | { | |
79 | return array( | |
80 | 'Locales' => Intl::getLocaleBundle()->getLocaleNames('en'), | |
81 | ); | |
82 | } | |
83 | ||
84 | /** | |
85 | * {@inheritdoc} | |
86 | */ | |
87 | public function afterCreateStub(StubbingContextInterface $context) | |
88 | { | |
89 | } | |
90 | ||
91 | private function scanLocales(CompilationContextInterface $context) | |
92 | { | |
93 | $tempDir = sys_get_temp_dir() . '/icu-data-locales-source'; | |
94 | ||
95 | $context->getFilesystem()->remove($tempDir); | |
96 | $context->getFilesystem()->mkdir($tempDir); | |
97 | ||
98 | // Temporarily generate the resource bundles | |
99 | $context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir); | |
100 | ||
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'); | |
104 | ||
105 | // Remove file extension and sort | |
106 | array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); }); | |
107 | sort($locales); | |
108 | ||
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'); | |
114 | ||
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]); | |
119 | } | |
120 | ||
121 | // Delete locales that have no content (i.e. only "Version" key) | |
122 | $bundle = new \ResourceBundle($locale, $tempDir); | |
123 | ||
124 | if (null === $bundle) { | |
125 | throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir); | |
126 | } | |
127 | ||
128 | // There seems to be no other way for identifying all keys in this specific | |
129 | // resource bundle | |
130 | if (array_keys(iterator_to_array($bundle)) === array('Version')) { | |
131 | unset($locales[$key]); | |
132 | } | |
133 | } | |
134 | ||
135 | $context->getFilesystem()->remove($tempDir); | |
136 | ||
137 | return $locales; | |
138 | } | |
139 | ||
140 | private function generateTextFiles($targetDirectory, array $locales) | |
141 | { | |
142 | $displayLocales = array_unique(array_merge( | |
143 | $this->languageBundle->getLocales(), | |
144 | $this->regionBundle->getLocales() | |
145 | )); | |
146 | ||
147 | $txtWriter = new TextBundleWriter(); | |
148 | ||
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) { | |
156 | continue; | |
157 | } | |
158 | ||
159 | $names = array(); | |
160 | ||
161 | foreach ($locales as $locale) { | |
162 | // Don't include ICU's root resource bundle | |
163 | if ($locale === 'root') { | |
164 | continue; | |
165 | } | |
166 | ||
167 | if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) { | |
168 | $names[$locale] = $name; | |
169 | } | |
170 | } | |
171 | ||
172 | // If no names could be generated for the current locale, skip it | |
173 | if (0 === count($names)) { | |
174 | continue; | |
175 | } | |
176 | ||
177 | $txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names)); | |
178 | } | |
179 | } | |
180 | ||
181 | private function generateLocaleName($locale, $displayLocale) | |
182 | { | |
183 | $name = null; | |
184 | ||
185 | $lang = \Locale::getPrimaryLanguage($locale); | |
186 | $script = \Locale::getScript($locale); | |
187 | $region = \Locale::getRegion($locale); | |
188 | $variants = \Locale::getAllVariants($locale); | |
189 | ||
190 | // Currently the only available variant is POSIX, which we don't want | |
191 | // to include in the list | |
192 | if (count($variants) > 0) { | |
193 | return null; | |
194 | } | |
195 | ||
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 | |
199 | // the name sorting | |
200 | // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region); | |
201 | ||
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))) { | |
205 | return null; | |
206 | } | |
207 | ||
208 | // "as" (Assamese) has no "Variants" block | |
209 | //if (!$langBundle->get('Variants')) { | |
210 | // continue; | |
211 | //} | |
212 | ||
213 | $extras = array(); | |
214 | ||
215 | // Discover the name of the script part of the locale | |
216 | // i.e. in zh_Hans_MO, "Hans" is the script | |
217 | if ($script) { | |
218 | // Some scripts are not translated into every language | |
219 | if (null === ($scriptName = $this->languageBundle->getScriptName($script, $lang, $displayLocale))) { | |
220 | return null; | |
221 | } | |
222 | ||
223 | $extras[] = $scriptName; | |
224 | } | |
225 | ||
226 | // Discover the name of the region part of the locale | |
227 | // i.e. in de_AT, "AT" is the region | |
228 | if ($region) { | |
229 | // Some regions are not translated into every language | |
230 | if (null === ($regionName = $this->regionBundle->getCountryName($region, $displayLocale))) { | |
231 | return null; | |
232 | } | |
233 | ||
234 | $extras[] = $regionName; | |
235 | } | |
236 | ||
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)) { | |
243 | $name = $matches[1]; | |
244 | } | |
245 | ||
246 | $name .= ' ('.implode(', ', $extras).')'; | |
247 | } | |
248 | ||
249 | return $name; | |
250 | } | |
251 | } |