|
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 function bailout($message) |
|
13 { |
|
14 exit($message."\n"); |
|
15 } |
|
16 |
|
17 function check_dir($source) |
|
18 { |
|
19 if (!file_exists($source)) { |
|
20 bailout('The directory '.$source.' does not exist'); |
|
21 } |
|
22 |
|
23 if (!is_dir($source)) { |
|
24 bailout('The file '.$source.' is not a directory'); |
|
25 } |
|
26 } |
|
27 |
|
28 function check_command($command) |
|
29 { |
|
30 exec('which '.$command, $output, $result); |
|
31 |
|
32 if ($result !== 0) { |
|
33 bailout('The command "'.$command.'" is not installed'); |
|
34 } |
|
35 } |
|
36 |
|
37 function clear_directory($directory) |
|
38 { |
|
39 $iterator = new \DirectoryIterator($directory); |
|
40 |
|
41 foreach ($iterator as $file) { |
|
42 if (!$file->isDot()) { |
|
43 if ($file->isDir()) { |
|
44 clear_directory($file->getPathname()); |
|
45 } else { |
|
46 unlink($file->getPathname()); |
|
47 } |
|
48 } |
|
49 } |
|
50 } |
|
51 |
|
52 function make_directory($directory) |
|
53 { |
|
54 if (!file_exists($directory)) { |
|
55 mkdir($directory); |
|
56 } |
|
57 |
|
58 if (!is_dir($directory)) { |
|
59 bailout('The file '.$directory.' already exists but is no directory'); |
|
60 } |
|
61 } |
|
62 |
|
63 function list_files($directory, $extension) |
|
64 { |
|
65 $files = array(); |
|
66 $iterator = new \DirectoryIterator($directory); |
|
67 |
|
68 foreach ($iterator as $file) { |
|
69 if (!$file->isDot() && substr($file->getFilename(), -strlen($extension)) === $extension) { |
|
70 $files[] = substr($file->getFilename(), 0, -strlen($extension)); |
|
71 } |
|
72 } |
|
73 |
|
74 return $files; |
|
75 } |
|
76 |
|
77 function genrb($source, $target) |
|
78 { |
|
79 exec('genrb -d '.$target.' '.$source.DIRECTORY_SEPARATOR.'*.txt', $output, $result); |
|
80 |
|
81 if ($result !== 0) { |
|
82 bailout('genrb failed'); |
|
83 } |
|
84 } |
|
85 |
|
86 function genrb_file($target, $source, $locale) |
|
87 { |
|
88 exec('genrb -v -d '.$target.' '.$source.DIRECTORY_SEPARATOR.$locale.'.txt', $output, $result); |
|
89 |
|
90 if ($result !== 0) { |
|
91 bailout('genrb failed'); |
|
92 } |
|
93 } |
|
94 |
|
95 function load_resource_bundle($locale, $directory) |
|
96 { |
|
97 $bundle = \ResourceBundle::create($locale, $directory); |
|
98 |
|
99 if (null === $bundle) { |
|
100 bailout('The resource bundle for locale '.$locale.' could not be loaded from directory '.$directory); |
|
101 } |
|
102 |
|
103 return $bundle; |
|
104 } |
|
105 |
|
106 function get_data($index, $dataDir, $locale = 'en', $constraint = null) |
|
107 { |
|
108 $data = array(); |
|
109 $bundle = load_resource_bundle($locale, __DIR__.DIRECTORY_SEPARATOR.$dataDir); |
|
110 |
|
111 foreach ($bundle->get($index) as $code => $name) { |
|
112 if (null !== $constraint) { |
|
113 if ($constraint($code)) { |
|
114 $data[$code] = $name; |
|
115 } |
|
116 continue; |
|
117 } |
|
118 |
|
119 $data[$code] = $name; |
|
120 } |
|
121 |
|
122 $collator = new \Collator($locale); |
|
123 $collator->asort($data); |
|
124 |
|
125 return $data; |
|
126 } |
|
127 |
|
128 function create_stub_datafile($locale, $target, $data) { |
|
129 $template = <<<TEMPLATE |
|
130 <?php |
|
131 |
|
132 /* |
|
133 * This file is part of the Symfony package. |
|
134 * |
|
135 * (c) Fabien Potencier <fabien@symfony.com> |
|
136 * |
|
137 * For the full copyright and license information, please view the LICENSE |
|
138 * file that was distributed with this source code. |
|
139 */ |
|
140 |
|
141 return %s; |
|
142 |
|
143 TEMPLATE; |
|
144 |
|
145 $data = var_export($data, true); |
|
146 $data = preg_replace('/array \(/', 'array(', $data); |
|
147 $data = preg_replace('/\n {1,10}array\(/', 'array(', $data); |
|
148 $data = preg_replace('/ /', ' ', $data); |
|
149 $data = sprintf($template, $data); |
|
150 |
|
151 file_put_contents($target.DIRECTORY_SEPARATOR.$locale.'.php', $data); |
|
152 } |
|
153 |
|
154 if ($GLOBALS['argc'] !== 2) { |
|
155 bailout(<<<MESSAGE |
|
156 Usage: php update-data.php [icu-data-directory] |
|
157 |
|
158 Updates the ICU resources in Symfony2 from the given ICU data directory. You |
|
159 can checkout the ICU data directory via SVN: |
|
160 |
|
161 $ svn co http://source.icu-project.org/repos/icu/icu/trunk/source/data icu-data |
|
162 |
|
163 MESSAGE |
|
164 ); |
|
165 } |
|
166 |
|
167 // Verify that all required directories exist |
|
168 $source = $GLOBALS['argv'][1]; |
|
169 |
|
170 check_dir($source); |
|
171 |
|
172 $source = realpath($source); |
|
173 |
|
174 check_dir($source.DIRECTORY_SEPARATOR.'curr'); |
|
175 check_dir($source.DIRECTORY_SEPARATOR.'lang'); |
|
176 check_dir($source.DIRECTORY_SEPARATOR.'locales'); |
|
177 check_dir($source.DIRECTORY_SEPARATOR.'region'); |
|
178 |
|
179 check_command('genrb'); |
|
180 |
|
181 // Convert the *.txt resource bundles to *.res files |
|
182 $target = __DIR__; |
|
183 $currDir = $target.DIRECTORY_SEPARATOR.'curr'; |
|
184 $langDir = $target.DIRECTORY_SEPARATOR.'lang'; |
|
185 $localesDir = $target.DIRECTORY_SEPARATOR.'locales'; |
|
186 $namesDir = $target.DIRECTORY_SEPARATOR.'names'; |
|
187 $namesGeneratedDir = $namesDir.DIRECTORY_SEPARATOR.'generated'; |
|
188 $regionDir = $target.DIRECTORY_SEPARATOR.'region'; |
|
189 |
|
190 make_directory($currDir); |
|
191 clear_directory($currDir); |
|
192 |
|
193 genrb_file($currDir, $source.DIRECTORY_SEPARATOR.'curr', 'en'); |
|
194 genrb_file($currDir, $source.DIRECTORY_SEPARATOR.'curr', 'supplementalData'); |
|
195 |
|
196 // It seems \ResourceBundle does not like locale names with uppercase chars then we rename the binary file |
|
197 // See: http://bugs.php.net/bug.php?id=54025 |
|
198 $filename_from = $currDir.DIRECTORY_SEPARATOR.'supplementalData.res'; |
|
199 $filename_to = $currDir.DIRECTORY_SEPARATOR.'supplementaldata.res'; |
|
200 |
|
201 if (!rename($filename_from, $filename_to)) { |
|
202 bailout('The file '.$filename_from.' could not be renamed'); |
|
203 } |
|
204 |
|
205 make_directory($langDir); |
|
206 clear_directory($langDir); |
|
207 genrb($source.DIRECTORY_SEPARATOR.'lang', $langDir); |
|
208 |
|
209 make_directory($localesDir); |
|
210 clear_directory($localesDir); |
|
211 genrb($source.DIRECTORY_SEPARATOR.'locales', $localesDir); |
|
212 |
|
213 make_directory($regionDir); |
|
214 clear_directory($regionDir); |
|
215 genrb($source.DIRECTORY_SEPARATOR.'region', $regionDir); |
|
216 |
|
217 make_directory($namesDir); |
|
218 clear_directory($namesDir); |
|
219 |
|
220 make_directory($namesGeneratedDir); |
|
221 clear_directory($namesGeneratedDir); |
|
222 |
|
223 // Discover the list of supported locales, which are the names of the resource |
|
224 // bundles in the "locales" directory |
|
225 $supportedLocales = list_files($localesDir, '.res'); |
|
226 |
|
227 sort($supportedLocales); |
|
228 |
|
229 // Delete unneeded locales |
|
230 foreach ($supportedLocales as $key => $supportedLocale) { |
|
231 // Delete all aliases from the list |
|
232 // i.e., "az_AZ" is an alias for "az_Latn_AZ" |
|
233 $localeBundleOrig = file_get_contents($source.DIRECTORY_SEPARATOR.'locales'.DIRECTORY_SEPARATOR.$supportedLocale.'.txt'); |
|
234 |
|
235 // The key "%%ALIAS" is not accessible through the \ResourceBundle class |
|
236 if (strpos($localeBundleOrig, '%%ALIAS') !== false) { |
|
237 unset($supportedLocales[$key]); |
|
238 } |
|
239 |
|
240 // Delete locales that have no content (i.e. only "Version" key) |
|
241 $localeBundle = load_resource_bundle($supportedLocale, $localesDir); |
|
242 |
|
243 // There seems to be no other way for identifying all keys in this specific |
|
244 // resource bundle |
|
245 $bundleKeys = array(); |
|
246 |
|
247 foreach ($localeBundle as $bundleKey => $_) { |
|
248 $bundleKeys[] = $bundleKey; |
|
249 } |
|
250 |
|
251 if ($bundleKeys === array('Version')) { |
|
252 unset($supportedLocales[$key]); |
|
253 } |
|
254 } |
|
255 |
|
256 // Discover the list of locales for which individual language/region names |
|
257 // exist. This list contains for example "de" and "de_CH", but not "de_DE" which |
|
258 // is equal to "de" |
|
259 $translatedLocales = array_unique(array_merge( |
|
260 list_files($langDir, '.res'), |
|
261 list_files($regionDir, '.res') |
|
262 )); |
|
263 |
|
264 sort($translatedLocales); |
|
265 |
|
266 // For each translated locale, generate a list of locale names |
|
267 // Each locale name has the form: "Language (Script, Region, Variant1, ...) |
|
268 // Script, Region and Variants are optional. If none of them is available, |
|
269 // the braces are not printed. |
|
270 foreach ($translatedLocales as $translatedLocale) { |
|
271 // Don't include ICU's root resource bundle |
|
272 if ($translatedLocale === 'root') { |
|
273 continue; |
|
274 } |
|
275 |
|
276 $langBundle = load_resource_bundle($translatedLocale, $langDir); |
|
277 $regionBundle = load_resource_bundle($translatedLocale, $regionDir); |
|
278 $localeNames = array(); |
|
279 |
|
280 foreach ($supportedLocales as $supportedLocale) { |
|
281 // Don't include ICU's root resource bundle |
|
282 if ($supportedLocale === 'root') { |
|
283 continue; |
|
284 } |
|
285 |
|
286 $lang = \Locale::getPrimaryLanguage($supportedLocale); |
|
287 $script = \Locale::getScript($supportedLocale); |
|
288 $region = \Locale::getRegion($supportedLocale); |
|
289 $variants = \Locale::getAllVariants($supportedLocale); |
|
290 |
|
291 // Currently the only available variant is POSIX, which we don't want |
|
292 // to include in the list |
|
293 if (count($variants) > 0) { |
|
294 continue; |
|
295 } |
|
296 |
|
297 $langName = $langBundle->get('Languages')->get($lang); |
|
298 $extras = array(); |
|
299 |
|
300 // Some languages are simply not translated |
|
301 // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans) |
|
302 if (!$langName) { |
|
303 continue; |
|
304 } |
|
305 |
|
306 // "af" (Afrikaans) has no "Scripts" block |
|
307 if (!$langBundle->get('Scripts')) { |
|
308 continue; |
|
309 } |
|
310 |
|
311 // "as" (Assamese) has no "Variants" block |
|
312 if (!$langBundle->get('Variants')) { |
|
313 continue; |
|
314 } |
|
315 |
|
316 // Discover the name of the script part of the locale |
|
317 // i.e. in zh_Hans_MO, "Hans" is the script |
|
318 if ($script) { |
|
319 // Some languages are translated together with their script, |
|
320 // i.e. "zh_Hans" is translated as "Simplified Chinese" |
|
321 if ($langBundle->get('Languages')->get($lang.'_'.$script)) { |
|
322 $langName = $langBundle->get('Languages')->get($lang.'_'.$script); |
|
323 |
|
324 // If the script is appended in braces, extract it |
|
325 // i.e. "zh_Hans" is translated as "Chinesisch (vereinfacht)" |
|
326 // in "de" |
|
327 if (strpos($langName, '(') !== false) { |
|
328 list($langName, $scriptName) = preg_split('/[\s()]/', $langName, null, PREG_SPLIT_NO_EMPTY); |
|
329 |
|
330 $extras[] = $scriptName; |
|
331 } |
|
332 } else { |
|
333 $scriptName = $langBundle->get('Scripts')->get($script); |
|
334 |
|
335 // Some scripts are not translated into every language |
|
336 if (!$scriptName) { |
|
337 continue; |
|
338 } |
|
339 |
|
340 $extras[] = $scriptName; |
|
341 } |
|
342 } |
|
343 |
|
344 // Discover the name of the region part of the locale |
|
345 // i.e. in de_AT, "AT" is the region |
|
346 if ($region) { |
|
347 // Some languages are translated together with their region, |
|
348 // i.e. "en_GB" is translated as "British English" |
|
349 // we don't include these languages though because they mess up |
|
350 // the locale sorting |
|
351 // if ($langBundle->get('Languages')->get($lang.'_'.$region)) { |
|
352 // $langName = $langBundle->get('Languages')->get($lang.'_'.$region); |
|
353 // } else { |
|
354 $regionName = $regionBundle->get('Countries')->get($region); |
|
355 |
|
356 // Some regions are not translated into every language |
|
357 if (!$regionName) { |
|
358 continue; |
|
359 } |
|
360 |
|
361 $extras[] = $regionName; |
|
362 // } |
|
363 } |
|
364 |
|
365 if (count($extras) > 0) { |
|
366 $langName .= ' ('.implode(', ', $extras).')'; |
|
367 } |
|
368 |
|
369 $localeNames[$supportedLocale] = $langName; |
|
370 } |
|
371 |
|
372 // If no names could be generated for the current locale, skip it |
|
373 if (count($localeNames) === 0) { |
|
374 continue; |
|
375 } |
|
376 |
|
377 echo "Generating $translatedLocale...\n"; |
|
378 |
|
379 $file = fopen($namesGeneratedDir.DIRECTORY_SEPARATOR.$translatedLocale.'.txt', 'w'); |
|
380 |
|
381 fwrite($file, "$translatedLocale{\n"); |
|
382 fwrite($file, " Locales{\n"); |
|
383 |
|
384 foreach ($localeNames as $supportedLocale => $langName) { |
|
385 fwrite($file, " $supportedLocale{\"$langName\"}\n"); |
|
386 } |
|
387 |
|
388 fwrite($file, " }\n"); |
|
389 fwrite($file, "}\n"); |
|
390 fclose($file); |
|
391 } |
|
392 |
|
393 // Convert generated files to binary format |
|
394 genrb($namesGeneratedDir, $namesDir); |
|
395 |
|
396 // Clean up |
|
397 clear_directory($namesGeneratedDir); |
|
398 rmdir($namesGeneratedDir); |
|
399 |
|
400 |
|
401 // Generate the data to the stubbed intl classes We only extract data for the 'en' locale |
|
402 // The extracted data is used only by the stub classes |
|
403 $defaultLocale = 'en'; |
|
404 |
|
405 $currencies = array(); |
|
406 $currenciesMeta = array(); |
|
407 $defaultMeta = array(); |
|
408 |
|
409 $bundle = load_resource_bundle('supplementaldata', __DIR__.'/curr'); |
|
410 |
|
411 foreach ($bundle->get('CurrencyMeta') as $code => $data) { |
|
412 // The 'DEFAULT' key contains the fraction digits and the rounding increment that are common for a lot of currencies |
|
413 // Only currencies with different values are added to the icu-data (e.g: CHF and JPY) |
|
414 if ('DEFAULT' == $code) { |
|
415 $defaultMeta = array( |
|
416 'fractionDigits' => $data[0], |
|
417 'roundingIncrement' => $data[1], |
|
418 ); |
|
419 |
|
420 continue; |
|
421 } |
|
422 |
|
423 $currenciesMeta[$code]['fractionDigits'] = $data[0]; |
|
424 $currenciesMeta[$code]['roundingIncrement'] = $data[1]; |
|
425 } |
|
426 |
|
427 $bundle = load_resource_bundle('en', __DIR__.'/curr'); |
|
428 |
|
429 foreach ($bundle->get('Currencies') as $code => $data) { |
|
430 $currencies[$code]['symbol'] = $data[0]; |
|
431 $currencies[$code]['name'] = $data[1]; |
|
432 |
|
433 if (!isset($currenciesMeta[$code])) { |
|
434 $currencies[$code]['fractionDigits'] = $defaultMeta['fractionDigits']; |
|
435 $currencies[$code]['roundingIncrement'] = $defaultMeta['roundingIncrement']; |
|
436 continue; |
|
437 } |
|
438 |
|
439 $currencies[$code]['fractionDigits'] = $currenciesMeta[$code]['fractionDigits']; |
|
440 $currencies[$code]['roundingIncrement'] = $currenciesMeta[$code]['roundingIncrement']; |
|
441 } |
|
442 |
|
443 // Countries. |
|
444 $countriesConstraint = function($code) |
|
445 { |
|
446 // Global countries (f.i. "America") have numeric codes |
|
447 // Countries have alphabetic codes |
|
448 // "ZZ" is the code for unknown country |
|
449 if (ctype_alpha($code) && 'ZZ' !== $code) { |
|
450 return true; |
|
451 } |
|
452 |
|
453 return false; |
|
454 }; |
|
455 |
|
456 $countries = get_data('Countries', 'region', $defaultLocale, $countriesConstraint); |
|
457 |
|
458 // Languages |
|
459 $languagesConstraint = function($code) |
|
460 { |
|
461 // "mul" is the code for multiple languages |
|
462 if ('mul' !== $code) { |
|
463 return true; |
|
464 } |
|
465 |
|
466 return false; |
|
467 }; |
|
468 |
|
469 $languages = get_data('Languages', 'lang', $defaultLocale, $languagesConstraint); |
|
470 |
|
471 // Display locales |
|
472 $displayLocales = get_data('Locales', 'names', $defaultLocale); |
|
473 |
|
474 // Create the stubs datafiles |
|
475 $stubDir = $target.DIRECTORY_SEPARATOR.'stub'; |
|
476 $stubCurrDir = $stubDir.DIRECTORY_SEPARATOR.'curr'; |
|
477 $stubLangDir = $stubDir.DIRECTORY_SEPARATOR.'lang'; |
|
478 $stubNamesDir = $stubDir.DIRECTORY_SEPARATOR.'names'; |
|
479 $stubRegionDir = $stubDir.DIRECTORY_SEPARATOR.'region'; |
|
480 |
|
481 // Create the directories |
|
482 make_directory($stubDir); |
|
483 make_directory($stubCurrDir); |
|
484 make_directory($stubLangDir); |
|
485 make_directory($stubNamesDir); |
|
486 make_directory($stubRegionDir); |
|
487 |
|
488 clear_directory($stubCurrDir); |
|
489 clear_directory($stubLangDir); |
|
490 clear_directory($stubNamesDir); |
|
491 clear_directory($stubRegionDir); |
|
492 |
|
493 create_stub_datafile($defaultLocale, $stubCurrDir, $currencies); |
|
494 create_stub_datafile($defaultLocale, $stubLangDir, $languages); |
|
495 create_stub_datafile($defaultLocale, $stubNamesDir, $displayLocales); |
|
496 create_stub_datafile($defaultLocale, $stubRegionDir, $countries); |
|
497 |
|
498 // Clean up |
|
499 clear_directory($currDir); |
|
500 rmdir($currDir); |