PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/system/src/Grav/Common/Language/Language.php

https://gitlab.com/asun89/socianovation-web
PHP | 475 lines | 271 code | 53 blank | 151 comment | 62 complexity | ea1a01e0ff2359156e324348f22f1364 MD5 | raw file
  1. <?php
  2. namespace Grav\Common\Language;
  3. use Grav\Common\Grav;
  4. /**
  5. * Language and translation functionality for Grav
  6. */
  7. class Language
  8. {
  9. protected $grav;
  10. protected $enabled = true;
  11. protected $languages = [];
  12. protected $page_extensions = [];
  13. protected $fallback_languages = [];
  14. protected $default;
  15. protected $active = null;
  16. protected $config;
  17. protected $http_accept_language;
  18. protected $lang_in_url = false;
  19. /**
  20. * Constructor
  21. *
  22. * @param \Grav\Common\Grav $grav
  23. */
  24. public function __construct(Grav $grav)
  25. {
  26. $this->grav = $grav;
  27. $this->config = $grav['config'];
  28. $this->languages = $this->config->get('system.languages.supported', []);
  29. $this->init();
  30. }
  31. /**
  32. * Initialize the default and enabled languages
  33. */
  34. public function init()
  35. {
  36. $this->default = reset($this->languages);
  37. if (empty($this->languages)) {
  38. $this->enabled = false;
  39. }
  40. }
  41. /**
  42. * Ensure that languages are enabled
  43. *
  44. * @return bool
  45. */
  46. public function enabled()
  47. {
  48. return $this->enabled;
  49. }
  50. /**
  51. * Gets the array of supported languages
  52. *
  53. * @return array
  54. */
  55. public function getLanguages()
  56. {
  57. return $this->languages;
  58. }
  59. /**
  60. * Sets the current supported languages manually
  61. *
  62. * @param $langs
  63. */
  64. public function setLanguages($langs)
  65. {
  66. $this->languages = $langs;
  67. $this->init();
  68. }
  69. /**
  70. * Gets a pipe-separated string of available languages
  71. *
  72. * @return string
  73. */
  74. public function getAvailable()
  75. {
  76. $languagesArray = $this->languages; //Make local copy
  77. sort($languagesArray);
  78. return implode('|', array_reverse($languagesArray));
  79. }
  80. /**
  81. * Gets language, active if set, else default
  82. *
  83. * @return mixed
  84. */
  85. public function getLanguage()
  86. {
  87. return $this->active ? $this->active : $this->default;
  88. }
  89. /**
  90. * Gets current default language
  91. *
  92. * @return mixed
  93. */
  94. public function getDefault()
  95. {
  96. return $this->default;
  97. }
  98. /**
  99. * Sets default language manually
  100. *
  101. * @param $lang
  102. *
  103. * @return bool
  104. */
  105. public function setDefault($lang)
  106. {
  107. if ($this->validate($lang)) {
  108. $this->default = $lang;
  109. return $lang;
  110. }
  111. return false;
  112. }
  113. /**
  114. * Gets current active language
  115. *
  116. * @return mixed
  117. */
  118. public function getActive()
  119. {
  120. return $this->active;
  121. }
  122. /**
  123. * Sets active language manually
  124. *
  125. * @param $lang
  126. *
  127. * @return bool
  128. */
  129. public function setActive($lang)
  130. {
  131. if ($this->validate($lang)) {
  132. $this->active = $lang;
  133. return $lang;
  134. }
  135. return false;
  136. }
  137. /**
  138. * Sets the active language based on the first part of the URL
  139. *
  140. * @param $uri
  141. *
  142. * @return mixed
  143. */
  144. public function setActiveFromUri($uri)
  145. {
  146. $regex = '/(^\/(' . $this->getAvailable() . '))(?:\/.*|$)/i';
  147. // if languages set
  148. if ($this->enabled()) {
  149. // try setting from prefix of URL (/en/blah/blah)
  150. if (preg_match($regex, $uri, $matches)) {
  151. $this->lang_in_url = true;
  152. $this->active = $matches[2];
  153. $uri = preg_replace("/\\" . $matches[1] . "/", '', $matches[0], 1);
  154. // store in session if different
  155. if ($this->config->get('system.session.enabled', false)
  156. && $this->config->get('system.languages.session_store_active', true)
  157. && $this->grav['session']->active_language != $this->active
  158. ) {
  159. $this->grav['session']->active_language = $this->active;
  160. }
  161. } else {
  162. // try getting from session, else no active
  163. if ($this->config->get('system.session.enabled', false) &&
  164. $this->config->get('system.languages.session_store_active', true)) {
  165. $this->active = $this->grav['session']->active_language ?: null;
  166. }
  167. // if still null, try from http_accept_language header
  168. if ($this->active === null && $this->config->get('system.languages.http_accept_language')) {
  169. $preferred = $this->getBrowserLanguages();
  170. foreach ($preferred as $lang) {
  171. if ($this->validate($lang)) {
  172. $this->active = $lang;
  173. break;
  174. }
  175. }
  176. }
  177. }
  178. }
  179. return $uri;
  180. }
  181. /**
  182. * Get's a URL prefix based on configuration
  183. *
  184. * @param null $lang
  185. * @return string
  186. */
  187. public function getLanguageURLPrefix($lang = null)
  188. {
  189. // if active lang is not passed in, use current active
  190. if (!$lang) {
  191. $lang = $this->getLanguage();
  192. }
  193. return $this->isIncludeDefaultLanguage($lang) ? '/' . $lang : '';
  194. }
  195. /**
  196. * Test to see if language is default and language should be included in the URL
  197. *
  198. * @param null $lang
  199. * @return bool
  200. */
  201. public function isIncludeDefaultLanguage($lang = null)
  202. {
  203. // if active lang is not passed in, use current active
  204. if (!$lang) {
  205. $lang = $this->getLanguage();
  206. }
  207. if ($this->default == $lang && $this->config->get('system.languages.include_default_lang') === false) {
  208. return false;
  209. } else {
  210. return true;
  211. }
  212. }
  213. /**
  214. * Simple getter to tell if a language was found in the URL
  215. *
  216. * @return bool
  217. */
  218. public function isLanguageInUrl()
  219. {
  220. return (bool) $this->lang_in_url;
  221. }
  222. /**
  223. * Gets an array of valid extensions with active first, then fallback extensions
  224. *
  225. * @param string|null $file_ext
  226. *
  227. * @return array
  228. */
  229. public function getFallbackPageExtensions($file_ext = null)
  230. {
  231. if (empty($this->page_extensions)) {
  232. if (empty($file_ext)) {
  233. $file_ext = CONTENT_EXT;
  234. }
  235. if ($this->enabled()) {
  236. $valid_lang_extensions = [];
  237. foreach ($this->languages as $lang) {
  238. $valid_lang_extensions[] = '.' . $lang . $file_ext;
  239. }
  240. if ($this->active) {
  241. $active_extension = '.' . $this->active . $file_ext;
  242. $key = array_search($active_extension, $valid_lang_extensions);
  243. unset($valid_lang_extensions[$key]);
  244. array_unshift($valid_lang_extensions, $active_extension);
  245. }
  246. $this->page_extensions = array_merge($valid_lang_extensions, (array)$file_ext);
  247. } else {
  248. $this->page_extensions = (array)$file_ext;
  249. }
  250. }
  251. return $this->page_extensions;
  252. }
  253. /**
  254. * Gets an array of languages with active first, then fallback languages
  255. *
  256. * @return array
  257. */
  258. public function getFallbackLanguages()
  259. {
  260. if (empty($this->fallback_languages)) {
  261. if ($this->enabled()) {
  262. $fallback_languages = $this->languages;
  263. if ($this->active) {
  264. $active_extension = $this->active;
  265. $key = array_search($active_extension, $fallback_languages);
  266. unset($fallback_languages[$key]);
  267. array_unshift($fallback_languages, $active_extension);
  268. }
  269. $this->fallback_languages = $fallback_languages;
  270. }
  271. // always add english in case a translation doesn't exist
  272. $this->fallback_languages[] = 'en';
  273. }
  274. return $this->fallback_languages;
  275. }
  276. /**
  277. * Ensures the language is valid and supported
  278. *
  279. * @param $lang
  280. *
  281. * @return bool
  282. */
  283. public function validate($lang)
  284. {
  285. if (in_array($lang, $this->languages)) {
  286. return true;
  287. }
  288. return false;
  289. }
  290. /**
  291. * Translate a key and possibly arguments into a string using current lang and fallbacks
  292. *
  293. * @param mixed $args The first argument is the lookup key value
  294. * Other arguments can be passed and replaced in the translation with sprintf syntax
  295. * @param array $languages
  296. * @param bool $array_support
  297. * @param bool $html_out
  298. *
  299. * @return string
  300. */
  301. public function translate($args, array $languages = null, $array_support = false, $html_out = false)
  302. {
  303. if (is_array($args)) {
  304. $lookup = array_shift($args);
  305. } else {
  306. $lookup = $args;
  307. $args = [];
  308. }
  309. if ($this->config->get('system.languages.translations', true)) {
  310. if ($this->enabled() && $lookup) {
  311. if (empty($languages)) {
  312. if ($this->config->get('system.languages.translations_fallback', true)) {
  313. $languages = $this->getFallbackLanguages();
  314. } else {
  315. $languages = (array)$this->getLanguage();
  316. }
  317. }
  318. } else {
  319. $languages = ['en'];
  320. }
  321. foreach ((array)$languages as $lang) {
  322. $translation = $this->getTranslation($lang, $lookup, $array_support);
  323. if ($translation) {
  324. if (count($args) >= 1) {
  325. return vsprintf($translation, $args);
  326. } else {
  327. return $translation;
  328. }
  329. }
  330. }
  331. }
  332. if ($html_out) {
  333. return '<span class="untranslated">' . $lookup . '</span>';
  334. } else {
  335. return $lookup;
  336. }
  337. }
  338. /**
  339. * Translate Array
  340. *
  341. * @param $key
  342. * @param $index
  343. * @param null $languages
  344. * @param bool $html_out
  345. *
  346. * @return string
  347. */
  348. public function translateArray($key, $index, $languages = null, $html_out = false)
  349. {
  350. if ($this->config->get('system.languages.translations', true)) {
  351. if ($this->enabled() && $key) {
  352. if (empty($languages)) {
  353. if ($this->config->get('system.languages.translations_fallback', true)) {
  354. $languages = $this->getFallbackLanguages();
  355. } else {
  356. $languages = (array)$this->getDefault();
  357. }
  358. }
  359. } else {
  360. $languages = ['en'];
  361. }
  362. foreach ((array)$languages as $lang) {
  363. $translation_array = (array)$this->config->getLanguages()->get($lang . '.' . $key, null);
  364. if ($translation_array && array_key_exists($index, $translation_array)) {
  365. return $translation_array[$index];
  366. }
  367. }
  368. }
  369. if ($html_out) {
  370. return '<span class="untranslated">' . $key . '[' . $index . ']</span>';
  371. } else {
  372. return $key . '[' . $index . ']';
  373. }
  374. }
  375. /**
  376. * Lookup the translation text for a given lang and key
  377. *
  378. * @param string $lang lang code
  379. * @param string $key key to lookup with
  380. * @param bool $array_support
  381. *
  382. * @return string
  383. */
  384. public function getTranslation($lang, $key, $array_support = false)
  385. {
  386. $translation = $this->config->getLanguages()->get($lang . '.' . $key, null);
  387. if (!$array_support && is_array($translation)) {
  388. return (string)array_shift($translation);
  389. }
  390. return $translation;
  391. }
  392. /**
  393. * Get the browser accepted languages
  394. *
  395. * @param array $accept_langs
  396. *
  397. * @return array
  398. */
  399. public function getBrowserLanguages($accept_langs = [])
  400. {
  401. if (empty($this->http_accept_language)) {
  402. if (empty($accept_langs) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  403. $accept_langs = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  404. } else {
  405. return $accept_langs;
  406. }
  407. foreach (explode(',', $accept_langs) as $k => $pref) {
  408. // split $pref again by ';q='
  409. // and decorate the language entries by inverted position
  410. if (false !== ($i = strpos($pref, ';q='))) {
  411. $langs[substr($pref, 0, $i)] = [(float)substr($pref, $i + 3), -$k];
  412. } else {
  413. $langs[$pref] = [1, -$k];
  414. }
  415. }
  416. arsort($langs);
  417. // no need to undecorate, because we're only interested in the keys
  418. $this->http_accept_language = array_keys($langs);
  419. }
  420. return $this->http_accept_language;
  421. }
  422. }