Gettext.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. namespace App\Gettext;
  3. use Exception;
  4. use RecursiveDirectoryIterator;
  5. use RecursiveIteratorIterator;
  6. use Gettext\GettextTranslator;
  7. use Gettext\Extractors;
  8. use Gettext\Generators;
  9. use Gettext\Translations;
  10. use Gettext\Translator;
  11. class Gettext
  12. {
  13. private $locale;
  14. private $config = array();
  15. private $formats = array('php', 'mo', 'po');
  16. private $translator;
  17. public function __construct(array $config)
  18. {
  19. $this->setConfig($config);
  20. }
  21. public function setConfig(array $config)
  22. {
  23. if (!isset($config['native'])) {
  24. $config['native'] = false;
  25. }
  26. if (!isset($config['functions'])) {
  27. $config['functions'] = true;
  28. }
  29. if (!isset($config['formats'])) {
  30. $config['formats'] = $this->formats;
  31. }
  32. $this->config = $config;
  33. }
  34. private function getFile($locale)
  35. {
  36. return sprintf('%s/%s/LC_MESSAGES/%s.', $this->config['storage'], $locale, $this->config['domain']);
  37. }
  38. private function getCache($locale)
  39. {
  40. if (is_file($file = $this->getFile($locale).'po')) {
  41. return Extractors\Po::fromFile($file);
  42. }
  43. return false;
  44. }
  45. private function store($locale, $entries)
  46. {
  47. $file = $this->getFile($locale);
  48. $dir = dirname($file);
  49. if (!is_dir($dir)) {
  50. mkdir($dir, 0755, true);
  51. }
  52. Generators\Mo::toFile($entries, $file.'mo');
  53. Generators\Po::toFile($entries, $file.'po');
  54. Generators\PhpArray::toFile($entries, $file.'php');
  55. return $entries;
  56. }
  57. private function scan()
  58. {
  59. Extractors\PhpCode::$functions += [
  60. '__' => '__',
  61. '_' => '__',
  62. ];
  63. $entries = new Translations();
  64. foreach ($this->config['directories'] as $dir) {
  65. if (!is_dir($dir)) {
  66. throw new Exception(__('Folder %s not exists. Gettext scan aborted.', $dir));
  67. }
  68. foreach ($this->scanDir($dir) as $file) {
  69. if (strstr($file, '.blade.php')) {
  70. $entries->mergeWith(Extractors\Blade::fromFile($file));
  71. } elseif (strstr($file, '.php')) {
  72. $entries->mergeWith(Extractors\PhpCode::fromFile($file));
  73. }
  74. }
  75. }
  76. return $entries;
  77. }
  78. private function scanDir($dir)
  79. {
  80. $directory = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
  81. $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::LEAVES_ONLY);
  82. $files = array();
  83. foreach ($iterator as $fileinfo) {
  84. $name = $fileinfo->getPathname();
  85. if (!strpos($name, '/.')) {
  86. $files[] = $name;
  87. }
  88. }
  89. return $files;
  90. }
  91. public function getEntries($locale, $refresh = true)
  92. {
  93. if (empty($refresh) && ($cache = $this->getCache($locale))) {
  94. return $cache;
  95. }
  96. $entries = clone $this->scan();
  97. if (is_file($file = $this->getFile($locale).'mo')) {
  98. $entries->mergeWith(Extractors\Mo::fromFile($file));
  99. }
  100. return $entries;
  101. }
  102. public function setEntries($locale, $translations)
  103. {
  104. if (empty($translations)) {
  105. return true;
  106. }
  107. $entries = $this->getCache($locale) ?: (new Translations());
  108. foreach ($translations as $msgid => $msgstr) {
  109. $msgid = urldecode($msgid);
  110. if (!($entry = $entries->find(null, $msgid))) {
  111. $entry = $entries->insert(null, $msgid);
  112. }
  113. $entry->setTranslation($msgstr);
  114. }
  115. $this->store($locale, $entries);
  116. return $entries;
  117. }
  118. public function load()
  119. {
  120. $locale = $this->locale.'.UTF-8';
  121. # IMPORTANT: locale must be installed in server!
  122. # sudo locale-gen es_ES.UTF-8
  123. # sudo update-locale
  124. putenv('LANG='.$locale);
  125. putenv('LANGUAGE='.$locale);
  126. putenv('LC_MESSAGES='.$locale);
  127. putenv('LC_PAPER='.$locale);
  128. putenv('LC_TIME='.$locale);
  129. putenv('LC_MONETARY='.$locale);
  130. if (defined('LC_MESSAGES')) {
  131. setlocale(LC_MESSAGES, $locale);
  132. }
  133. if (defined('LC_COLLATE')) {
  134. setlocale(LC_COLLATE, $locale);
  135. }
  136. if (defined('LC_TIME')) {
  137. setlocale(LC_TIME, $locale);
  138. }
  139. if (defined('LC_MONETARY')) {
  140. setlocale(LC_MONETARY, $locale);
  141. }
  142. if (!defined('LC_MESSAGES') && !defined('LC_COLLATE') && !defined('LC_TIME') && !defined('LC_MONETARY')) {
  143. setlocale(LC_ALL, $locale);
  144. }
  145. if ($this->config['native']) {
  146. $this->loadNative($locale);
  147. } else {
  148. $this->loadParsed($locale);
  149. }
  150. }
  151. private function loadNative($locale)
  152. {
  153. $translator = new GettextTranslator();
  154. $translator->setLanguage($locale);
  155. $translator->loadDomain($this->config['domain'], $this->config['storage']);
  156. bind_textdomain_codeset($this->config['domain'], 'UTF-8');
  157. if ($this->config['functions']) {
  158. $translator->register();
  159. }
  160. $this->translator = $translator;
  161. }
  162. private function loadParsed($locale)
  163. {
  164. $file = dirname($this->getFile($this->locale)).'/'.$this->config['domain'];
  165. $translations = null;
  166. foreach ($this->config['formats'] as $format) {
  167. if ($translations = $this->loadFormat($format, $file)) {
  168. break;
  169. }
  170. }
  171. if ($translations === null) {
  172. $translations = new Translations();
  173. }
  174. $this->translator = (new Translator())->loadTranslations($translations);
  175. if ($this->config['functions']) {
  176. Translator::initGettextFunctions($this->translator);
  177. }
  178. }
  179. private function loadFormat($format, $file)
  180. {
  181. switch ($format) {
  182. case 'mo':
  183. return $this->loadFormatMo($file);
  184. case 'po':
  185. return $this->loadFormatPo($file);
  186. case 'php':
  187. return $this->loadFormatPHP($file);
  188. }
  189. throw new Exception(sprintf('Format %s is not available', $format));
  190. }
  191. private function loadFormatMo($file)
  192. {
  193. return is_file($file.'.mo') ? Translations::fromMoFile($file.'.mo') : null;
  194. }
  195. private function loadFormatPo($file)
  196. {
  197. return is_file($file.'.po') ? Translations::fromPoFile($file.'.po') : null;
  198. }
  199. private function loadFormatPHP($file)
  200. {
  201. return is_file($file.'.php') ? ($file.'.php') : null;
  202. }
  203. public function setLocale($current, $new)
  204. {
  205. if (empty($current) || !in_array($current, $this->config['locales'])) {
  206. $current = $this->config['locales'][0];
  207. }
  208. if ($new && ($new !== $current) && in_array($new, $this->config['locales'])) {
  209. $current = $new;
  210. }
  211. $this->locale = $current;
  212. }
  213. public function getLocale()
  214. {
  215. return $this->locale;
  216. }
  217. public function getTranslator()
  218. {
  219. return $this->translator;
  220. }
  221. }