PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/campsite/src/admin-files/localizer/LocalizerLanguage.php

https://github.com/joechrysler/Campsite
PHP | 694 lines | 391 code | 68 blank | 235 comment | 92 complexity | 7844b0407e49a1285ec5bbf3d8cf6670 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.1, Apache-2.0
  1. <?PHP
  2. /**
  3. * @package Campware
  4. */
  5. /**
  6. * Includes
  7. */
  8. require_once('PEAR.php');
  9. require_once('LocalizerConfig.php');
  10. require_once('LocalizerFileFormat.php');
  11. require_once($GLOBALS['g_campsiteDir'].'/db_connect.php');
  12. /**
  13. * @package Campware
  14. */
  15. class LocalizerLanguage {
  16. var $m_translationTable = array();
  17. var $m_languageCode = '';
  18. var $m_countryCode = '';
  19. var $m_languageId = '';
  20. var $m_mode = '';
  21. var $m_prefix = '';
  22. var $m_filePath = '';
  23. /**
  24. * A LocalizerLanguage is basically a translation table.
  25. *
  26. * You can use this class to manipulate the translation table:
  27. * such as add, delete, and move strings.
  28. *
  29. * @param string $p_prefix
  30. * The beginning of the file name, up to the first dot ('.').
  31. *
  32. * @param string $p_directory
  33. * The location of the language file, relative to LOCALIZER_BASE_DIR.
  34. *
  35. * @param string $p_languageId
  36. * The language ID for this language, which can be in one of two forms:
  37. * 1) The two-letter language code (e.g. "en").
  38. * 2) The two-letter language code, underscore, two-letter country code
  39. * (e.g. "en_US")
  40. */
  41. function LocalizerLanguage($p_prefix, $p_languageId = null)
  42. {
  43. if (!is_null($p_languageId)) {
  44. $this->setLanguageId($p_languageId);
  45. }
  46. $this->m_prefix = $p_prefix;
  47. } // constructor
  48. /**
  49. * Return the filename prefix.
  50. * @return string
  51. */
  52. function getPrefix()
  53. {
  54. return $this->m_prefix;
  55. } // fn getPrefix
  56. /**
  57. * This will return 'gs' or 'xml'
  58. * @return string
  59. */
  60. function getMode()
  61. {
  62. return $this->m_mode;
  63. } // fn getMode
  64. /**
  65. * Set the mode to be 'xml' or 'gs'.
  66. * @param string $p_value
  67. * @return void
  68. */
  69. function setMode($p_value)
  70. {
  71. $p_value = strtolower($p_value);
  72. if (($p_value == 'xml') || ($p_value == 'gs')) {
  73. $this->m_mode = $p_value;
  74. }
  75. } // fn setMode
  76. /**
  77. * Set the language code - this can take either the two-letter language code
  78. * or the LL_CC extended version , where LL is the language code and CC
  79. * is the country code.
  80. *
  81. * @param string $p_languageId
  82. * @return void
  83. */
  84. function setLanguageId($p_languageId)
  85. {
  86. if (strlen($p_languageId) > 2) {
  87. $this->m_languageCode = substr($p_languageId, 0, 2);
  88. $this->m_countryCode = substr($p_languageId, 3, 2);
  89. $this->m_languageId = $p_languageId;
  90. }
  91. else {
  92. $this->m_languageCode = $p_languageId;
  93. $this->m_languageId = $p_languageId;
  94. }
  95. } // fn setLanguageId
  96. /**
  97. * Register a string in the translation table.
  98. * @param string $p_key
  99. * @param string $p_value
  100. * @return void
  101. */
  102. function registerString($p_key, $p_value)
  103. {
  104. if (substr($p_value, strlen($p_value)-3) == ":en"){
  105. $p_value = substr($p_value, 0, strlen($p_value)-3);
  106. }
  107. $this->m_translationTable[$p_key] = $p_value;
  108. } // fn registerString
  109. /**
  110. * Return the total number of strings in the translation table.
  111. * @return int
  112. */
  113. function getNumStrings()
  114. {
  115. return count($this->m_translationTable);
  116. } // fn getNumStrings
  117. /**
  118. * Get the language code that is in the form <two_letter_language_code>_<english_name_of_language>.
  119. *
  120. * @return string
  121. */
  122. function getLanguageId()
  123. {
  124. return $this->m_languageId;
  125. } // fn getLanguageId
  126. /**
  127. * Get the two-letter language code for the translation table.
  128. * @return string
  129. */
  130. function getLanguageCode()
  131. {
  132. return $this->m_languageCode;
  133. } // fn getLanguageCode
  134. /**
  135. * Get the two-letter country code.
  136. * @return string
  137. */
  138. function getCountryCode()
  139. {
  140. return $this->m_countryCode;
  141. } // fn getCountryCode
  142. /**
  143. * Return the file path for the last file loaded.
  144. * @return string
  145. */
  146. function getSourceFile()
  147. {
  148. return $this->m_filePath;
  149. } // fn getSourceFile
  150. /**
  151. * This is only for use by the LocalizerFileFormat functions.
  152. * @access private
  153. */
  154. function _setSourceFile($p_value)
  155. {
  156. $this->m_filePath = $p_value;
  157. } // fn _setSourceFile
  158. /**
  159. * Return true if this LocalizerLanguage has the exact same
  160. * translation strings as the given LocalizerLanguage.
  161. *
  162. * @param LocalizerLanguage $p_localizerLanguage
  163. * @return boolean
  164. */
  165. function equal($p_localizerLanguage)
  166. {
  167. if (count($this->m_translationTable) != count($p_localizerLanguage->m_translationTable)) {
  168. return false;
  169. }
  170. foreach ($this->m_translationTable as $key => $value) {
  171. if (!array_key_exists($key, $p_localizerLanguage->m_translationTable)) {
  172. //echo "missing translation string: '$key'<br>";
  173. return false;
  174. }
  175. if ($p_localizerLanguage->m_translationTable[$key] != $value) {
  176. //echo "Non-matching values: '".$p_localizerLanguage->m_translationTable[$key]."' != '".$value."'<br>";
  177. return false;
  178. }
  179. }
  180. return true;
  181. } // fn equal
  182. /**
  183. * Return a table indexed by the english language name, with the value being the
  184. * target language equivalent.
  185. *
  186. * @return array
  187. */
  188. function getTranslationTable()
  189. {
  190. return $this->m_translationTable;
  191. }
  192. /**
  193. * Return the value for the given key.
  194. *
  195. * @param unknown_type $p_key
  196. */
  197. function getValue($p_key)
  198. {
  199. if (isset($this->m_translationTable[$p_key])) {
  200. return $this->m_translationTable[$p_key];
  201. } else {
  202. return null;
  203. }
  204. } // fn getValue
  205. /**
  206. * Get the full path to the translation file.
  207. *
  208. * @param string $p_mode
  209. * Either 'gs' or 'xml'.
  210. * @return string
  211. */
  212. function getFilePath($p_mode = null)
  213. {
  214. global $g_localizerConfig;
  215. if (is_null($p_mode)) {
  216. $p_mode = $this->m_mode;
  217. }
  218. if ($p_mode == 'xml') {
  219. $relativePath = '/'.$this->m_languageId.'/'.$this->m_prefix.'.xml';
  220. }
  221. else {
  222. $relativePath = '/'.$this->m_languageCode.'/'.$this->m_prefix.'.php';
  223. }
  224. return $g_localizerConfig['TRANSLATION_DIR'].$relativePath;
  225. } // fn getFilePath
  226. /**
  227. * Return TRUE if the given string exists in the translation table.
  228. *
  229. * @param string $p_string
  230. *
  231. * @return boolean
  232. */
  233. function keyExists($p_string)
  234. {
  235. return (isset($this->m_translationTable[$p_string]));
  236. } // fn stringExists
  237. /**
  238. * Add a string to the translation table.
  239. *
  240. * @param string $p_key
  241. * The english translation of the string.
  242. *
  243. * @param string $p_value
  244. * Optional. If not specified, the value will be set to the same
  245. * value as the key.
  246. *
  247. * @param int $p_position
  248. * Optional. By default the string will be added to the end of the
  249. * translation table.
  250. *
  251. * @return boolean
  252. */
  253. function addString($p_key, $p_value = null, $p_position = null)
  254. {
  255. if (!is_null($p_position)
  256. && (!is_numeric($p_position) || ($p_position < 0)
  257. || ($p_position > count($this->m_translationTable)))) {
  258. return false;
  259. }
  260. if (!is_string($p_key) || !is_string($p_value)) {
  261. return false;
  262. }
  263. if (is_null($p_position)) {
  264. // Position is not specified - add the string at the end
  265. if (is_null($p_value)) {
  266. $this->m_translationTable[$p_key] = $p_key;
  267. }
  268. else {
  269. $this->m_translationTable[$p_key] = $p_value;
  270. }
  271. return true;
  272. }
  273. else {
  274. // The position is specified
  275. $begin = array_slice($this->m_translationTable, 0, $p_position);
  276. $end = array_slice($this->m_translationTable, $p_position);
  277. if (is_null($p_value)) {
  278. $newStr = array($p_key => $p_key);
  279. }
  280. else {
  281. $newStr = array($p_key => $p_value);
  282. }
  283. $this->m_translationTable = array_merge($begin, $newStr, $end);
  284. return true;
  285. }
  286. } // fn addString
  287. /**
  288. * Get the position of a key or a value.
  289. * @param string $p_key
  290. * @param string $p_value
  291. * @return mixed
  292. * The position of the key/value in the array, FALSE if not found.
  293. */
  294. function getPosition($p_key = null, $p_value = null)
  295. {
  296. $position = 0;
  297. if (!is_null($p_key)) {
  298. foreach ($this->m_translationTable as $key => $value) {
  299. if ($p_key == $key) {
  300. return $position;
  301. }
  302. $position++;
  303. }
  304. }
  305. elseif (!is_null($p_value)) {
  306. foreach ($this->m_translationTable as $value) {
  307. if ($p_value == $value) {
  308. return $position;
  309. }
  310. $position++;
  311. }
  312. }
  313. return false;
  314. } // fn getPosition
  315. /**
  316. * Get the string at the given position.
  317. *
  318. * @return array
  319. * An array of two elements, the first is the key, the second is the value.
  320. * They are indexed by 'key' and 'value'.
  321. */
  322. function getStringAtPosition($p_position)
  323. {
  324. if (is_null($p_position) || !is_numeric($p_position) || ($p_position < 0)
  325. || ($p_position > count($this->m_translationTable))) {
  326. return false;
  327. }
  328. $returnValue = array_splice($this->m_translationTable, $p_position, 0);
  329. $keys = array_keys($returnValue);
  330. $key = array_pop($keys);
  331. $value = array_pop($returnValue);
  332. return array('key' => $key, 'value' => $value);
  333. } // fn getStringAtPosition
  334. /**
  335. * Change the key and optionally the value of the
  336. * translation string. If the value isnt specified,
  337. * it is not changed. If the key does not exist,
  338. * it will be added. In this case, you can use p_position
  339. * to specify where to add the string.
  340. *
  341. * @param string $p_oldKey
  342. * @param string $p_newKey
  343. * @param string $p_value
  344. * @param int $p_position
  345. * @return boolean
  346. */
  347. function updateString($p_oldKey, $p_newKey, $p_value = null, $p_position = null)
  348. {
  349. if (!is_string($p_oldKey) || !is_string($p_newKey)) {
  350. return false;
  351. }
  352. // Does the old string exist?
  353. if (!isset($this->m_translationTable[$p_oldKey])) {
  354. return $this->addString($p_newKey, $p_value, $p_position);
  355. }
  356. if ($p_oldKey == $p_newKey) {
  357. // Just updating the value
  358. if (!is_null($p_value) && ($p_value != $this->m_translationTable[$p_oldKey])) {
  359. $this->m_translationTable[$p_oldKey] = $p_value;
  360. return true;
  361. }
  362. // No changes
  363. else {
  364. return true;
  365. }
  366. }
  367. // Updating the key (and possibly the value)
  368. if (is_null($p_value)) {
  369. $p_value = $this->m_translationTable[$p_oldKey];
  370. }
  371. $position = $this->getPosition($p_oldKey);
  372. $success = $this->deleteString($p_oldKey);
  373. $success &= $this->addString($p_newKey, $p_value, $position);
  374. return $success;
  375. } // fn updateString
  376. /**
  377. * Move a string to a different position in the translation array.
  378. * This allows similiar strings to be grouped together.
  379. *
  380. * @param int $p_startPositionOrKey
  381. * @param int $p_endPosition
  382. *
  383. * @return boolean
  384. * TRUE on success, FALSE on failure.
  385. */
  386. function moveString($p_startPositionOrKey, $p_endPosition)
  387. {
  388. // Check parameters
  389. if (is_numeric($p_startPositionOrKey) && (($p_startPositionOrKey < 0)
  390. || ($p_startPositionOrKey > count($this->m_translationTable)))) {
  391. return false;
  392. }
  393. if (!is_numeric($p_endPosition) || ($p_endPosition < 0)
  394. || ($p_endPosition > count($this->m_translationTable))) {
  395. return false;
  396. }
  397. $startPosition = null;
  398. if (is_numeric($p_startPositionOrKey)) {
  399. $startPosition = $p_startPositionOrKey;
  400. }
  401. elseif (is_string($p_startPositionOrKey)) {
  402. if (!isset($this->m_translationTable[$p_startPositionOrKey])) {
  403. return false;
  404. }
  405. $startPosition = $this->getPosition($p_startPositionOrKey);
  406. }
  407. else {
  408. return false;
  409. }
  410. // Success if we dont have to move the string anywhere
  411. if ($startPosition == $p_endPosition) {
  412. return true;
  413. }
  414. // Delete the string in the old position
  415. $result = $this->deleteStringAtPosition($startPosition);
  416. if (!$result) {
  417. return false;
  418. }
  419. $key = $result['key'];
  420. $value = $result['value'];
  421. // Add the string in the new position
  422. $result = $this->addString($key, $value, $p_endPosition);
  423. if (!$result) {
  424. return false;
  425. }
  426. return true;
  427. } // fn moveString
  428. /**
  429. * Delete the string given by $p_key.
  430. * @param string $p_key
  431. * @return mixed
  432. * The deleted string as array('key' => $key, 'value' => $value) on success,
  433. * FALSE if it didnt exist.
  434. */
  435. function deleteString($p_key)
  436. {
  437. if (isset($this->m_translationTable[$p_key])) {
  438. $value = $this->m_translationTable[$p_key];
  439. unset($this->m_translationTable[$p_key]);
  440. return array('key'=>$p_key, 'value'=>$value);
  441. }
  442. return false;
  443. } // fn deleteString
  444. /**
  445. * Delete a string at a specific position in the array.
  446. * @param int $p_position
  447. * @return mixed
  448. * The deleted string as array($key, $value) on success, FALSE on failure.
  449. */
  450. function deleteStringAtPosition($p_position)
  451. {
  452. if (!is_numeric($p_position) || ($p_position < 0)
  453. || ($p_position > count($this->m_translationTable))) {
  454. return false;
  455. }
  456. $returnValue = array_splice($this->m_translationTable, $p_position, 1);
  457. $keys = array_keys($returnValue);
  458. $key = array_pop($keys);
  459. $value = array_pop($returnValue);
  460. return array('key' => $key, 'value' => $value);
  461. } // fn deleteStringAtPosition
  462. /**
  463. * Synchronize the positions of the strings in the translation table
  464. * with the positions of the string in the default language translation table.
  465. */
  466. function fixPositions()
  467. {
  468. global $g_localizerConfig;
  469. $defaultLanguage = new LocalizerLanguage($this->m_prefix,
  470. $g_localizerConfig['DEFAULT_LANGUAGE']);
  471. $defaultLanguage->loadFile(Localizer::GetMode());
  472. $defaultTranslationTable = $defaultLanguage->getTranslationTable();
  473. $count = 0;
  474. $modified = false;
  475. foreach ($defaultTranslationTable as $key => $value) {
  476. if ($this->getPosition($key) != $count) {
  477. $this->moveString($key, $count);
  478. $modified = true;
  479. }
  480. $count++;
  481. }
  482. return $modified;
  483. } // fn fixPositions
  484. /**
  485. * Sync with the default language file. This means
  486. * adding any missing strings and fixing the positions of the strings to
  487. * be the same as the default language file.
  488. */
  489. function syncToDefault()
  490. {
  491. global $g_localizerConfig;
  492. $defaultLanguage = new LocalizerLanguage($this->m_prefix,
  493. $g_localizerConfig['DEFAULT_LANGUAGE']);
  494. $defaultLanguage->loadFile(Localizer::GetMode());
  495. $defaultTranslationTable = $defaultLanguage->getTranslationTable();
  496. $count = 0;
  497. $modified = false;
  498. foreach ($defaultTranslationTable as $key => $value) {
  499. if (!isset($this->m_translationTable[$key])) {
  500. $this->addString($key, '', $count);
  501. $modified = true;
  502. }
  503. $count++;
  504. }
  505. return ($this->fixPositions() || $modified);
  506. } // fn syncToDefault
  507. /**
  508. * Find the keys/values that match the given keyword.
  509. *
  510. * @param string $p_keyword
  511. *
  512. * @return array
  513. */
  514. function search($p_keyword)
  515. {
  516. $matches = array();
  517. foreach ($this->m_translationTable as $key => $value) {
  518. if (empty($p_keyword) || stristr($key, $p_keyword) || stristr($value, $p_keyword)) {
  519. $matches[$key] = $value;
  520. }
  521. }
  522. return $matches;
  523. } // fn search
  524. /**
  525. * Load a language file of the given type.
  526. *
  527. * @param string $p_type
  528. * If not specified, it will use the current mode.
  529. *
  530. * @return mixed
  531. * Return TRUE on success, PEAR_Error on failure.
  532. */
  533. function loadFile($p_type = null)
  534. {
  535. if (is_null($p_type)) {
  536. if (!empty($this->m_mode)) {
  537. $p_type = $this->m_mode;
  538. }
  539. else {
  540. $p_type = Localizer::GetMode();
  541. if (is_null($p_type)) {
  542. return false;
  543. }
  544. }
  545. }
  546. $className = 'LocalizerFileFormat_'.strtoupper($p_type);
  547. if (class_exists($className)) {
  548. $object = new $className();
  549. if (method_exists($object, 'load')) {
  550. $result = $object->load($this);
  551. return $result;
  552. } else {
  553. return new PEAR_Error("LocalizerLanguage::loadFile(): Class $className is missing the 'load' function.");
  554. }
  555. } else {
  556. return new PEAR_Error("LocalizerLanguage::loadFile(): Class $className does not exist.");
  557. }
  558. } // fn loadFile
  559. /**
  560. * Save the translation table as the given type.
  561. *
  562. * @param string $p_type
  563. * If not specified, it will use the current mode.
  564. *
  565. * @return mixed
  566. * true
  567. * PEAR_Error
  568. */
  569. function saveFile($p_type = null)
  570. {
  571. // Figure out the current mode.
  572. if (is_null($p_type)) {
  573. if (!empty($this->m_mode)) {
  574. $p_type = $this->m_mode;
  575. }
  576. else {
  577. $p_type = Localizer::GetMode();
  578. if (is_null($p_type)) {
  579. return new PEAR_Error("Localizer: unknown mode");
  580. }
  581. }
  582. }
  583. // Save in the requested mode.
  584. $className = 'LocalizerFileFormat_'.strtoupper($p_type);
  585. if (class_exists($className)) {
  586. $object = new $className();
  587. if (method_exists($object, 'save')) {
  588. return $object->save($this);
  589. } else {
  590. return new PEAR_Error("Class $className does not have a 'save' function");
  591. }
  592. } else {
  593. return new PEAR_Error("Class $className does not exist.");
  594. }
  595. } // fn saveFile
  596. /**
  597. * Erase all the values in the translation table, but
  598. * keep the keys.
  599. * @return void
  600. */
  601. function clearValues()
  602. {
  603. foreach ($this->m_translationTable as $key => $value) {
  604. $this->m_translationTable[$key] = '';
  605. }
  606. } // fn clearValues
  607. /**
  608. * For debugging purposes, displays the the translation table
  609. * in an HTML table.
  610. */
  611. function dumpToHtml()
  612. {
  613. echo "<pre>";
  614. if (!empty($this->m_filePath)) {
  615. echo "<b>File: ".$this->m_filePath."</b><br>";
  616. }
  617. echo "<b>Language Code: ".$this->m_languageId."</b><br>";
  618. echo "<table>";
  619. foreach ($this->m_translationTable as $key => $value) {
  620. echo "<tr><td>'$key'</td><td>'$value'</td></tr>";
  621. }
  622. echo "</table>";
  623. echo "</pre>";
  624. } // fn dumpToHtml
  625. } // class LocalizerLanguage
  626. ?>