PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/Intl/ResourceBundle/Transformer/Rule/LocaleBundleTransformationRule.php

https://bitbucket.org/gencer/symfony
PHP | 256 lines | 131 code | 48 blank | 77 comment | 15 complexity | aebbe904bed55868f87369e24baae69f MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Intl\ResourceBundle\Transformer\Rule;
  11. use Symfony\Component\Intl\Exception\RuntimeException;
  12. use Symfony\Component\Intl\Intl;
  13. use Symfony\Component\Intl\ResourceBundle\Transformer\CompilationContextInterface;
  14. use Symfony\Component\Intl\ResourceBundle\Transformer\StubbingContextInterface;
  15. use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter;
  16. /**
  17. * The rule for compiling the locale bundle.
  18. *
  19. * @author Bernhard Schussek <bschussek@gmail.com>
  20. */
  21. class LocaleBundleTransformationRule implements TransformationRuleInterface
  22. {
  23. /**
  24. * @var \Symfony\Component\Intl\ResourceBundle\LanguageBundleInterface
  25. */
  26. private $languageBundle;
  27. /**
  28. * @var \Symfony\Component\Intl\ResourceBundle\RegionBundleInterface
  29. */
  30. private $regionBundle;
  31. public function __construct()
  32. {
  33. $this->languageBundle = Intl::getLanguageBundle();
  34. $this->regionBundle = Intl::getRegionBundle();
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function getBundleName()
  40. {
  41. return 'locales';
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function beforeCompile(CompilationContextInterface $context)
  47. {
  48. $tempDir = sys_get_temp_dir() . '/icu-data-locales';
  49. $context->getFilesystem()->remove($tempDir);
  50. $context->getFilesystem()->mkdir($tempDir);
  51. $this->generateTextFiles($tempDir, $this->scanLocales($context));
  52. return $tempDir;
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function afterCompile(CompilationContextInterface $context)
  58. {
  59. $context->getFilesystem()->remove(sys_get_temp_dir() . '/icu-data-locales');
  60. }
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function beforeCreateStub(StubbingContextInterface $context)
  65. {
  66. return array(
  67. 'Locales' => Intl::getLocaleBundle()->getLocaleNames('en'),
  68. );
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function afterCreateStub(StubbingContextInterface $context)
  74. {
  75. }
  76. private function scanLocales(CompilationContextInterface $context)
  77. {
  78. $tempDir = sys_get_temp_dir() . '/icu-data-locales-source';
  79. $context->getFilesystem()->remove($tempDir);
  80. $context->getFilesystem()->mkdir($tempDir);
  81. // Temporarily generate the resource bundles
  82. $context->getCompiler()->compile($context->getSourceDir() . '/locales', $tempDir);
  83. // Discover the list of supported locales, which are the names of the resource
  84. // bundles in the "locales" directory
  85. $locales = glob($tempDir . '/*.res');
  86. // Remove file extension and sort
  87. array_walk($locales, function (&$locale) { $locale = basename($locale, '.res'); });
  88. sort($locales);
  89. // Delete unneeded locales
  90. foreach ($locales as $key => $locale) {
  91. // Delete all aliases from the list
  92. // i.e., "az_AZ" is an alias for "az_Latn_AZ"
  93. $content = file_get_contents($context->getSourceDir() . '/locales/' . $locale . '.txt');
  94. // The key "%%ALIAS" is not accessible through the \ResourceBundle class,
  95. // so look in the original .txt file instead
  96. if (strpos($content, '%%ALIAS') !== false) {
  97. unset($locales[$key]);
  98. }
  99. // Delete locales that have no content (i.e. only "Version" key)
  100. try {
  101. $bundle = new \ResourceBundle($locale, $tempDir);
  102. } catch (\Exception $e) {
  103. // HHVM compatibility: constructor throws on invalid resource
  104. $bundle = null;
  105. }
  106. if (null === $bundle) {
  107. throw new RuntimeException('The resource bundle for locale ' . $locale . ' could not be loaded from directory ' . $tempDir);
  108. }
  109. // There seems to be no other way for identifying all keys in this specific
  110. // resource bundle
  111. if (array_keys(iterator_to_array($bundle)) === array('Version')) {
  112. unset($locales[$key]);
  113. }
  114. }
  115. $context->getFilesystem()->remove($tempDir);
  116. return $locales;
  117. }
  118. private function generateTextFiles($targetDirectory, array $locales)
  119. {
  120. $displayLocales = array_unique(array_merge(
  121. $this->languageBundle->getLocales(),
  122. $this->regionBundle->getLocales()
  123. ));
  124. $txtWriter = new TextBundleWriter();
  125. // Generate a list of locale names in the language of each display locale
  126. // Each locale name has the form: "Language (Script, Region, Variant1, ...)
  127. // Script, Region and Variants are optional. If none of them is available,
  128. // the braces are not printed.
  129. foreach ($displayLocales as $displayLocale) {
  130. // Don't include ICU's root resource bundle
  131. if ('root' === $displayLocale) {
  132. continue;
  133. }
  134. $names = array();
  135. foreach ($locales as $locale) {
  136. // Don't include ICU's root resource bundle
  137. if ($locale === 'root') {
  138. continue;
  139. }
  140. if (null !== ($name = $this->generateLocaleName($locale, $displayLocale))) {
  141. $names[$locale] = $name;
  142. }
  143. }
  144. // If no names could be generated for the current locale, skip it
  145. if (0 === count($names)) {
  146. continue;
  147. }
  148. $txtWriter->write($targetDirectory, $displayLocale, array('Locales' => $names));
  149. }
  150. }
  151. private function generateLocaleName($locale, $displayLocale)
  152. {
  153. $name = null;
  154. $lang = \Locale::getPrimaryLanguage($locale);
  155. $script = \Locale::getScript($locale);
  156. $region = \Locale::getRegion($locale);
  157. $variants = \Locale::getAllVariants($locale);
  158. // Currently the only available variant is POSIX, which we don't want
  159. // to include in the list
  160. if (count($variants) > 0) {
  161. return;
  162. }
  163. // Some languages are translated together with their region,
  164. // i.e. "en_GB" is translated as "British English"
  165. // we don't include these languages though because they mess up
  166. // the name sorting
  167. // $name = $this->langBundle->getLanguageName($displayLocale, $lang, $region);
  168. // Some languages are simply not translated
  169. // Example: "az" (Azerbaijani) has no translation in "af" (Afrikaans)
  170. if (null === ($name = $this->languageBundle->getLanguageName($lang, null, $displayLocale))) {
  171. return;
  172. }
  173. // "as" (Assamese) has no "Variants" block
  174. //if (!$langBundle->get('Variants')) {
  175. // continue;
  176. //}
  177. $extras = array();
  178. // Discover the name of the script part of the locale
  179. // i.e. in zh_Hans_MO, "Hans" is the script
  180. if ($script) {
  181. // Some scripts are not translated into every language
  182. if (null === ($scriptName = $this->languageBundle->getScriptName($script, $lang, $displayLocale))) {
  183. return;
  184. }
  185. $extras[] = $scriptName;
  186. }
  187. // Discover the name of the region part of the locale
  188. // i.e. in de_AT, "AT" is the region
  189. if ($region) {
  190. // Some regions are not translated into every language
  191. if (null === ($regionName = $this->regionBundle->getCountryName($region, $displayLocale))) {
  192. return;
  193. }
  194. $extras[] = $regionName;
  195. }
  196. if (count($extras) > 0) {
  197. // Remove any existing extras
  198. // For example, in German, zh_Hans is "Chinesisch (vereinfacht)".
  199. // The latter is the script part which is already included in the
  200. // extras and will be appended again with the other extras.
  201. if (preg_match('/^(.+)\s+\([^\)]+\)$/', $name, $matches)) {
  202. $name = $matches[1];
  203. }
  204. $name .= ' ('.implode(', ', $extras).')';
  205. }
  206. return $name;
  207. }
  208. }