PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/engine/lib/languages.php

https://github.com/fragilbert/Elgg
PHP | 354 lines | 202 code | 58 blank | 94 comment | 51 complexity | 3bcc7b52896db7349765c7f739cbbe28 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * Elgg language module
  4. * Functions to manage language and translations.
  5. *
  6. * @package Elgg.Core
  7. * @subpackage Languages
  8. */
  9. /**
  10. * Given a message key, returns an appropriately translated full-text string
  11. *
  12. * @param string $message_key The short message code
  13. * @param array $args An array of arguments to pass through vsprintf().
  14. * @param string $language Optionally, the standard language code
  15. * (defaults to site/user default, then English)
  16. *
  17. * @return string Either the translated string, the English string,
  18. * or the original language string.
  19. */
  20. function elgg_echo($message_key, $args = array(), $language = "") {
  21. global $CONFIG;
  22. static $CURRENT_LANGUAGE;
  23. // old param order is deprecated
  24. if (!is_array($args)) {
  25. elgg_deprecated_notice(
  26. 'As of Elgg 1.8, the 2nd arg to elgg_echo() is an array of string replacements and the 3rd arg is the language.',
  27. 1.8
  28. );
  29. $language = $args;
  30. $args = array();
  31. }
  32. if (!isset($CONFIG->translations)) {
  33. // this means we probably had an exception before translations were initialized
  34. register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
  35. }
  36. if (!$CURRENT_LANGUAGE) {
  37. $CURRENT_LANGUAGE = get_language();
  38. }
  39. if (!$language) {
  40. $language = $CURRENT_LANGUAGE;
  41. }
  42. if (isset($CONFIG->translations[$language][$message_key])) {
  43. $string = $CONFIG->translations[$language][$message_key];
  44. } else if (isset($CONFIG->translations["en"][$message_key])) {
  45. $string = $CONFIG->translations["en"][$message_key];
  46. $lang = $CONFIG->translations["en"][$language];
  47. elgg_log(sprintf('Missing %s translation for "%s" language key', $lang, $message_key), 'NOTICE');
  48. } else {
  49. $string = $message_key;
  50. elgg_log(sprintf('Missing English translation for "%s" language key', $message_key), 'NOTICE');
  51. }
  52. // only pass through if we have arguments to allow backward compatibility
  53. // with manual sprintf() calls.
  54. if ($args) {
  55. $string = vsprintf($string, $args);
  56. }
  57. return $string;
  58. }
  59. /**
  60. * Add a translation.
  61. *
  62. * Translations are arrays in the Zend Translation array format, eg:
  63. *
  64. * $english = array('message1' => 'message1', 'message2' => 'message2');
  65. * $german = array('message1' => 'Nachricht1','message2' => 'Nachricht2');
  66. *
  67. * @param string $country_code Standard country code (eg 'en', 'nl', 'es')
  68. * @param array $language_array Formatted array of strings
  69. *
  70. * @return bool Depending on success
  71. */
  72. function add_translation($country_code, $language_array) {
  73. global $CONFIG;
  74. if (!isset($CONFIG->translations)) {
  75. $CONFIG->translations = array();
  76. }
  77. $country_code = strtolower($country_code);
  78. $country_code = trim($country_code);
  79. if (is_array($language_array) && sizeof($language_array) > 0 && $country_code != "") {
  80. if (!isset($CONFIG->translations[$country_code])) {
  81. $CONFIG->translations[$country_code] = $language_array;
  82. } else {
  83. $CONFIG->translations[$country_code] = $language_array + $CONFIG->translations[$country_code];
  84. }
  85. return true;
  86. }
  87. return false;
  88. }
  89. /**
  90. * Detect the current language being used by the current site or logged in user.
  91. *
  92. * @return string The language code for the site/user or "en" if not set
  93. */
  94. function get_current_language() {
  95. $language = get_language();
  96. if (!$language) {
  97. $language = 'en';
  98. }
  99. return $language;
  100. }
  101. /**
  102. * Gets the current language in use by the system or user.
  103. *
  104. * @return string The language code (eg "en") or false if not set
  105. */
  106. function get_language() {
  107. global $CONFIG;
  108. $user = elgg_get_logged_in_user_entity();
  109. $language = false;
  110. if (($user) && ($user->language)) {
  111. $language = $user->language;
  112. }
  113. if ((!$language) && (isset($CONFIG->language)) && ($CONFIG->language)) {
  114. $language = $CONFIG->language;
  115. }
  116. if ($language) {
  117. return $language;
  118. }
  119. return false;
  120. }
  121. function _elgg_load_translations() {
  122. global $CONFIG;
  123. if ($CONFIG->system_cache_enabled) {
  124. $loaded = true;
  125. $languages = array_unique(array('en', get_current_language()));
  126. foreach ($languages as $language) {
  127. $data = elgg_load_system_cache("$language.lang");
  128. if ($data) {
  129. add_translation($language, unserialize($data));
  130. } else {
  131. $loaded = false;
  132. }
  133. }
  134. if ($loaded) {
  135. $CONFIG->i18n_loaded_from_cache = true;
  136. // this is here to force
  137. $CONFIG->language_paths[dirname(dirname(dirname(__FILE__))) . "/languages/"] = true;
  138. return;
  139. }
  140. }
  141. // load core translations from languages directory
  142. register_translations(dirname(dirname(dirname(__FILE__))) . "/languages/");
  143. }
  144. /**
  145. * When given a full path, finds translation files and loads them
  146. *
  147. * @param string $path Full path
  148. * @param bool $load_all If true all languages are loaded, if
  149. * false only the current language + en are loaded
  150. *
  151. * @return bool success
  152. */
  153. function register_translations($path, $load_all = false) {
  154. global $CONFIG;
  155. $path = sanitise_filepath($path);
  156. // Make a note of this path just incase we need to register this language later
  157. if (!isset($CONFIG->language_paths)) {
  158. $CONFIG->language_paths = array();
  159. }
  160. $CONFIG->language_paths[$path] = true;
  161. // Get the current language based on site defaults and user preference
  162. $current_language = get_current_language();
  163. elgg_log("Translations loaded from: $path");
  164. // only load these files unless $load_all is true.
  165. $load_language_files = array(
  166. 'en.php',
  167. "$current_language.php"
  168. );
  169. $load_language_files = array_unique($load_language_files);
  170. $handle = opendir($path);
  171. if (!$handle) {
  172. elgg_log("Could not open language path: $path", 'ERROR');
  173. return false;
  174. }
  175. $return = true;
  176. while (false !== ($language = readdir($handle))) {
  177. // ignore bad files
  178. if (substr($language, 0, 1) == '.' || substr($language, -4) !== '.php') {
  179. continue;
  180. }
  181. if (in_array($language, $load_language_files) || $load_all) {
  182. $result = include_once($path . $language);
  183. if (!$result) {
  184. $return = false;
  185. continue;
  186. } elseif (is_array($result)) {
  187. add_translation(basename($language, '.php'), $result);
  188. }
  189. }
  190. }
  191. return $return;
  192. }
  193. /**
  194. * Reload all translations from all registered paths.
  195. *
  196. * This is only called by functions which need to know all possible translations.
  197. *
  198. * @todo Better on demand loading based on language_paths array
  199. *
  200. * @return void
  201. */
  202. function reload_all_translations() {
  203. global $CONFIG;
  204. static $LANG_RELOAD_ALL_RUN;
  205. if ($LANG_RELOAD_ALL_RUN) {
  206. return;
  207. }
  208. if ($CONFIG->i18n_loaded_from_cache) {
  209. $cache = elgg_get_system_cache();
  210. $cache_dir = $cache->getVariable("cache_path");
  211. $filenames = elgg_get_file_list($cache_dir, array(), array(), array(".lang"));
  212. foreach ($filenames as $filename) {
  213. if (preg_match('/([a-z]+)\.[^.]+$/', $filename, $matches)) {
  214. $language = $matches[1];
  215. $data = elgg_load_system_cache("$language.lang");
  216. if ($data) {
  217. add_translation($language, unserialize($data));
  218. }
  219. }
  220. }
  221. } else {
  222. foreach ($CONFIG->language_paths as $path => $dummy) {
  223. register_translations($path, true);
  224. }
  225. }
  226. $LANG_RELOAD_ALL_RUN = true;
  227. }
  228. /**
  229. * Return an array of installed translations as an associative
  230. * array "two letter code" => "native language name".
  231. *
  232. * @return array
  233. */
  234. function get_installed_translations() {
  235. global $CONFIG;
  236. // Ensure that all possible translations are loaded
  237. reload_all_translations();
  238. $installed = array();
  239. foreach ($CONFIG->translations as $k => $v) {
  240. $installed[$k] = elgg_echo($k, array(), $k);
  241. if (elgg_is_admin_logged_in()) {
  242. $completeness = get_language_completeness($k);
  243. if (($completeness < 100) && ($k != 'en')) {
  244. $installed[$k] .= " (" . $completeness . "% " . elgg_echo('complete') . ")";
  245. }
  246. }
  247. }
  248. return $installed;
  249. }
  250. /**
  251. * Return the level of completeness for a given language code (compared to english)
  252. *
  253. * @param string $language Language
  254. *
  255. * @return int
  256. */
  257. function get_language_completeness($language) {
  258. global $CONFIG;
  259. // Ensure that all possible translations are loaded
  260. reload_all_translations();
  261. $language = sanitise_string($language);
  262. $en = count($CONFIG->translations['en']);
  263. $missing = get_missing_language_keys($language);
  264. if ($missing) {
  265. $missing = count($missing);
  266. } else {
  267. $missing = 0;
  268. }
  269. //$lang = count($CONFIG->translations[$language]);
  270. $lang = $en - $missing;
  271. return round(($lang / $en) * 100, 2);
  272. }
  273. /**
  274. * Return the translation keys missing from a given language,
  275. * or those that are identical to the english version.
  276. *
  277. * @param string $language The language
  278. *
  279. * @return mixed
  280. */
  281. function get_missing_language_keys($language) {
  282. global $CONFIG;
  283. // Ensure that all possible translations are loaded
  284. reload_all_translations();
  285. $missing = array();
  286. foreach ($CONFIG->translations['en'] as $k => $v) {
  287. if ((!isset($CONFIG->translations[$language][$k]))
  288. || ($CONFIG->translations[$language][$k] == $CONFIG->translations['en'][$k])) {
  289. $missing[] = $k;
  290. }
  291. }
  292. if (count($missing)) {
  293. return $missing;
  294. }
  295. return false;
  296. }