PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/gui/library/i18n.php

https://bitbucket.org/droidzone/i-mscp
PHP | 378 lines | 197 code | 51 blank | 130 comment | 45 complexity | 140c0ca1520a3303553516e06d9d0422 MD5 | raw file
  1. <?php
  2. /**
  3. * i-MSCP - internet Multi Server Control Panel
  4. *
  5. * The contents of this file are subject to the Mozilla Public License
  6. * Version 1.1 (the "License"); you may not use this file except in
  7. * compliance with the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS"
  11. * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  12. * License for the specific language governing rights and limitations
  13. * under the License.
  14. *
  15. * The Original Code is "VHCS - Virtual Hosting Control System".
  16. *
  17. * The Initial Developer of the Original Code is moleSoftware GmbH.
  18. * Portions created by Initial Developer are Copyright (C) 2001-2006
  19. * by moleSoftware GmbH. All Rights Reserved.
  20. *
  21. * Portions created by the ispCP Team are Copyright (C) 2006-2010 by
  22. * isp Control Panel. All Rights Reserved.
  23. *
  24. * Portions created by the i-MSCP Team are Copyright (C) 2010-2013 by
  25. * i-MSCP - internet Multi Server Control Panel. All Rights Reserved.
  26. *
  27. * @copyright 2001-2006 by moleSoftware GmbH
  28. * @copyright 2006-2010 by ispCP | http://isp-control.net
  29. * @copyright 2010-2013 by i-MSCP | http://i-mscp.net
  30. * @link http://i-mscp.net
  31. * @author ispCP Team
  32. * @author i-MSCP Team
  33. */
  34. /**
  35. * Translates a given message into the selected language, if exists.
  36. *
  37. * @author Laurent Declercq (nuxwin) <l.declercq@nuxwin.com>
  38. * @author Raphael Geissert (2007)
  39. * @param string $msgid string to translate
  40. * @param mixed $substitution,... If second parameter is bool (true), prevent the
  41. * returned string from being replaced with html
  42. * entities. If not considere the parameter(s) as
  43. * simple substitution value(s)
  44. * @return string Translated or original message
  45. */
  46. function tr($msgid, $substitution = false)
  47. {
  48. $msgstr = T_($msgid);
  49. // Detect whether $substitution is really a substitution or just a value to
  50. // be replaced in $msgstr
  51. if (!is_bool($substitution)) {
  52. $substitution = false;
  53. }
  54. // Detect comments and strip them if $msgid == $msgstr
  55. // e.g. tr('_: This is just a comment\nReal message to translate here')
  56. if (substr($msgid, 0, 3) == '_: ' && $msgid == $msgstr &&
  57. count($l = explode("\n", $msgid)) > 1
  58. ) {
  59. unset($l[0]);
  60. $msgstr = implode("\n", $l);
  61. }
  62. // Replace values
  63. if (func_num_args() > 1) {
  64. $argv = func_get_args();
  65. unset($argv[0]);
  66. if (is_bool($argv[1])) {
  67. unset($argv[1]);
  68. }
  69. $msgstr = vsprintf($msgstr, $argv);
  70. }
  71. if (!$substitution) {
  72. $msgstr = replace_html(htmlentities($msgstr, ENT_COMPAT, 'UTF-8'));
  73. }
  74. return $msgstr;
  75. }
  76. /**
  77. * Replaces special encoded strings back to their original signs
  78. *
  79. * @author Benedikt Heintel <benedikt.heintel@ispcp.net>
  80. * @param string $string String to replace chars
  81. * @return String with replaced chars
  82. */
  83. function replace_html($string)
  84. {
  85. $pattern = array(
  86. '#&lt;[ ]*b[ ]*&gt;#i', '#&lt;[ ]*/[ ]*b[ ]*&gt;#i',
  87. '#&lt;[ ]*strong[ ]*&gt;#i', '#&lt;[ ]*/[ ]*strong[ ]*&gt;#i',
  88. '#&lt;[ ]*em[ ]*&gt;#i', '#&lt;[ ]*/[ ]*em[ ]*&gt;#i',
  89. '#&lt;[ ]*i[ ]*&gt;#i', '#&lt;[ ]*/[ ]*i[ ]*&gt;#i',
  90. '#&lt;[ ]*small[ ]*&gt;#i', '#&lt;[ ]*/[ ]*small[ ]*&gt;#i',
  91. '#&lt;[ ]*br[ ]*(/|)[ ]*&gt;#i');
  92. $replacement = array(
  93. '<b>', '</b>', '<strong>', '</strong>', '<em>', '</em>', '<i>', '</i>',
  94. '<small>', '</small>', '<br />');
  95. $string = preg_replace($pattern, $replacement, $string);
  96. return $string;
  97. }
  98. // Dirty hack to make gettext add this entry to the .pot file
  99. if (false) {
  100. tr('_: Localised language');
  101. }
  102. /**
  103. * Build languages index from machine object files.
  104. *
  105. * Note: Only the files that are readable will be processed.
  106. *
  107. * @author Laurent Declercq <l.declercq@nuxwin.com>
  108. * @since i-MSCP 1.0.1.4
  109. * @return void
  110. */
  111. function i18n_buildLanguageIndex()
  112. {
  113. /** @var $cfg iMSCP_Config_Handler_File */
  114. $cfg = iMSCP_Registry::get('config');
  115. $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
  116. $cfg->GUI_ROOT_DIR . '/i18n/locales/', FilesystemIterator::SKIP_DOTS));
  117. $availableLanguages = array();
  118. /** @var $item SplFileInfo */
  119. foreach ($iterator as $item) {
  120. if(strlen($basename = $item->getBasename()) > 8) {
  121. continue;
  122. }
  123. if ($item->isReadable()) {
  124. $parser = new iMSCP_I18n_Parser_Mo($item->getPathname());
  125. $poRevisionDate = DateTime::createFromFormat(
  126. 'Y-m-d H:i O', $parser->getPotCreationDate());
  127. $availableLanguages[$basename] = array(
  128. 'locale' => $parser->getLanguage(),
  129. 'revision' => $poRevisionDate->format('Y-m-d H:i'),
  130. 'translatedStrings' => $parser->getNumberOfTranslatedStrings(),
  131. 'lastTranslator' => $parser->getLastTranslator());
  132. // Getting localized language name
  133. $translationTable = $parser->getTranslationTable();
  134. $availableLanguages[$basename]['language'] =
  135. $translationTable['_: Localised language'];
  136. }
  137. }
  138. /** @var $dbConfig iMSCP_Config_Handler_Db */
  139. $dbConfig = iMSCP_Registry::get('dbConfig');
  140. sort($availableLanguages);
  141. $serializedData = serialize($availableLanguages);
  142. $dbConfig->AVAILABLE_LANGUAGES = $serializedData;
  143. $cfg->AVAILABLE_LANGUAGES = $serializedData;
  144. }
  145. /**
  146. * Returns list of available languages with some informations.
  147. *
  148. * Note: For safe reasons, only the files that are readable will be indexed.
  149. *
  150. * @author Laurent Declercq <l.declercq@nuxwin.com>
  151. * @since i-MSCP 1.0.1.4
  152. * @return array Array that contains information about available languages
  153. */
  154. function i18n_getAvailableLanguages()
  155. {
  156. /** @var $cfg iMSCP_Config_Handler_File */
  157. $cfg = iMSCP_Registry::get('config');
  158. if (!isset($cfg->AVAILABLE_LANGUAGES) || !is_serialized($cfg->AVAILABLE_LANGUAGES)
  159. ) {
  160. i18n_buildLanguageIndex();
  161. }
  162. return unserialize(($cfg->AVAILABLE_LANGUAGES));
  163. }
  164. /**
  165. * Returns name of domain being used.
  166. *
  167. * Note: See #130 for further explaination.
  168. *
  169. * @author Laurent Declercq <l.declercq@nuxwin.com>
  170. * @since i-MSCP 1.0.1.4
  171. * @throws iMSCP_Exception
  172. * @param string $upstreamDomain Upstream domain name
  173. * @return string Domain being used
  174. */
  175. function i18n_getDomain($upstreamDomain)
  176. {
  177. /** @var $cfg iMSCP_Config_Handler_File */
  178. $cfg = iMSCP_Registry::get('config');
  179. $domainDirectory = $cfg->GUI_ROOT_DIR . "/i18n/locales/$upstreamDomain/LC_MESSAGES";
  180. if(file_exists($domainDirectory . "/$upstreamDomain.mo")) {
  181. $upstreamFileModificationTime = filemtime($domainDirectory . "/$upstreamDomain.mo");
  182. $domain = $upstreamDomain . '_' . $upstreamFileModificationTime;
  183. } else {
  184. return $upstreamDomain;
  185. }
  186. if(!file_exists($domainDirectory . "/$domain.mo")) {
  187. if(!@copy($domainDirectory . "/$upstreamDomain.mo", $domainDirectory . "/$domain.mo")) {
  188. write_log("i18n: Unable to create $domainDirectory/$domain.mo domain file for production", E_USER_ERROR);
  189. } else {
  190. write_log("i18n: Created new machine object file $domainDirectory/$domain.mo for production.", E_USER_NOTICE);
  191. i18n_domainsGarbageCollector($domainDirectory, $domain . '.mo');
  192. }
  193. }
  194. return $domain;
  195. }
  196. /**
  197. * Garbage collector for domains translation files.
  198. *
  199. * Note: See #130 for further explanations.
  200. *
  201. * @author Laurent Declercq <l.declercq@nuxwin.com>
  202. * @since i-MSCP 1.0.1.4
  203. * @param string $domainDirectory Current domain directory path
  204. * @param string $skipDomain Domain that must not be removed
  205. * @return void
  206. */
  207. function i18n_domainsGarbageCollector($domainDirectory, $skipDomain)
  208. {
  209. $currentDomainFilepath = $domainDirectory . '/' . $skipDomain;
  210. $domainsFiles = glob($domainDirectory . '/*_*_*.mo');
  211. foreach($domainsFiles as $file) {
  212. if($file != $currentDomainFilepath) {
  213. if(@unlink($file)) {
  214. write_log("i18n: Removed $file machine object production file.", E_USER_NOTICE);
  215. } else {
  216. write_log("i18n: Unable to remove $file machine object production file.", E_USER_ERROR);
  217. }
  218. }
  219. }
  220. }
  221. /**
  222. * Import Machine object file in languages directory
  223. *
  224. * @author Laurent Declercq <l.declercq@nuxwin.com>
  225. * @since i-MSCP 1.0.1.4
  226. * @return bool TRUE on success, FALSE otherwise
  227. */
  228. function i18n_importMachineObjectFile()
  229. {
  230. // closure that is run before move_uploaded_file() function - See the
  231. // Utils_UploadFile() function for further information about implementation
  232. // details
  233. $beforeMove = function()
  234. {
  235. /** @var $cfg iMSCP_Config_Handler_File */
  236. $cfg = iMSCP_Registry::get('config');
  237. $localesDirectory = $cfg->GUI_ROOT_DIR . '/i18n/locales';
  238. $filePath = $_FILES['languageFile']['tmp_name'];
  239. if (!is_readable($filePath)) {
  240. set_page_message(tr('File is not readable.'), 'error');
  241. return false;
  242. }
  243. try {
  244. $parser = new iMSCP_I18n_Parser_Mo($filePath);
  245. $encoding = $parser->getContentType();
  246. $locale = $parser->getLanguage();
  247. $revision = $parser->getPoRevisionDate();
  248. $lastTranslator = $parser->getLastTranslator();
  249. $translationTable = $parser->getTranslationTable();
  250. } catch (iMSCP_Exception $e) {
  251. set_page_message(tr('Only gettext Machine Object files (MO files) are accepted.'), 'error');
  252. return false;
  253. }
  254. if (isset($translationTable['_: Localised language'])) {
  255. $language = $translationTable['_: Localised language'];
  256. } else {
  257. $language = '';
  258. }
  259. if (empty($encoding) || empty($locale) || empty($revision) ||
  260. empty($lastTranslator) || empty($language)
  261. ) {
  262. set_page_message(
  263. tr("%s is not a valid i-MSCP language file.", tohtml($_FILES['languageFile']['name'])), 'error'
  264. );
  265. return false;
  266. }
  267. if (!is_dir("$localesDirectory/$locale")) {
  268. if (!@mkdir("$localesDirectory/$locale", 0700)) {
  269. set_page_message(tr("Unable to create '%s' directory for language file.", tohtml($locale)), 'error');
  270. return false;
  271. }
  272. }
  273. if (!is_dir("$localesDirectory/$locale/LC_MESSAGES")) {
  274. if (!@mkdir("$localesDirectory/$locale/LC_MESSAGES", 0700)) {
  275. set_page_message(tr("Unable to create 'LC_MESSAGES' directory for language file."), 'error');
  276. return false;
  277. }
  278. }
  279. // Return destination file path
  280. return "$localesDirectory/$locale/LC_MESSAGES/$locale.mo";
  281. };
  282. if (utils_uploadFile('languageFile', array($beforeMove)) === false) {
  283. return false;
  284. }
  285. // Rebuild language index
  286. i18n_buildLanguageIndex();
  287. return true;
  288. }
  289. /**
  290. * Change panel default language.
  291. *
  292. * @author Laurent Declercq <l.declercq@nuxwin.com>
  293. * @since i-MSCP 1.0.1.3
  294. * @return bool TRUE if language name is valid, FALSE otherwise
  295. */
  296. function i18n_changeDefaultLanguage()
  297. {
  298. if (isset($_POST['defaultLanguage'])) {
  299. /** @var $cfg iMSCP_Config_Handler_File */
  300. $cfg = iMSCP_Registry::get('config');
  301. /** @var $dbConfig iMSCP_Config_Handler_Db */
  302. $defaultLanguage = clean_input($_POST['defaultLanguage']);
  303. $availableLanguages = i18n_getAvailableLanguages();
  304. // Check for language availability
  305. $isValidLanguage = false;
  306. foreach($availableLanguages as $languageDefinition) {
  307. if($languageDefinition['locale'] == $defaultLanguage) {
  308. $isValidLanguage = true;
  309. }
  310. }
  311. if(!$isValidLanguage) return false;
  312. /** @var $dbConfig iMSCP_Config_Handler_Db */
  313. $dbConfig = iMSCP_Registry::get('dbConfig');
  314. $dbConfig->USER_INITIAL_LANG = $defaultLanguage;
  315. $cfg->USER_INITIAL_LANG = $defaultLanguage;
  316. // Ensures language change on next load for current user in case he has not yet
  317. // his gui properties explicitly set (eg. for the first admin user when i-MSCP
  318. // was just installed
  319. $stmt = exec_query('SELECT `lang` FROM `user_gui_props` WHERE `user_id` = ?', $_SESSION['user_id']);
  320. if ($stmt->fields['lang'] == null) {
  321. unset($_SESSION['user_def_lang']);
  322. }
  323. } else {
  324. return false;
  325. }
  326. return true;
  327. }