PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/gallery/helpers/l10n_client.php

https://github.com/zulu/MyGallery3
PHP | 284 lines | 195 code | 34 blank | 55 comment | 19 complexity | 51ebbd60f3fef87ae832c8d33708a637 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php defined("SYSPATH") or die("No direct script access.");
  2. /**
  3. * Gallery - a web based photo album viewer and editor
  4. * Copyright (C) 2000-2009 Bharat Mediratta
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or (at
  9. * your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. class l10n_client_Core {
  21. private static function _server_url() {
  22. return "http://gallery.menalto.com/index.php";
  23. }
  24. static function server_api_key_url() {
  25. return self::_server_url() . "?q=translations/userkey/" .
  26. self::client_token();
  27. }
  28. static function client_token() {
  29. return md5("l10n_client_client_token" . access::private_key());
  30. }
  31. static function api_key($api_key=null) {
  32. if ($api_key !== null) {
  33. module::set_var("gallery", "l10n_client_key", $api_key);
  34. }
  35. return module::get_var("gallery", "l10n_client_key", "");
  36. }
  37. static function server_uid($api_key=null) {
  38. $api_key = $api_key == null ? self::api_key() : $api_key;
  39. $parts = explode(":", $api_key);
  40. return empty($parts) ? 0 : $parts[0];
  41. }
  42. private static function _sign($payload, $api_key=null) {
  43. $api_key = $api_key == null ? self::api_key() : $api_key;
  44. return md5($api_key . $payload . self::client_token());
  45. }
  46. static function validate_api_key($api_key) {
  47. $version = "1.0";
  48. $url = self::_server_url() . "?q=translations/status";
  49. $signature = self::_sign($version, $api_key);
  50. list ($response_data, $response_status) = remote::post(
  51. $url, array("version" => $version,
  52. "client_token" => self::client_token(),
  53. "signature" => $signature,
  54. "uid" => self::server_uid($api_key)));
  55. if (!remote::success($response_status)) {
  56. return false;
  57. }
  58. return true;
  59. }
  60. static function fetch_updates() {
  61. $request->locales = array();
  62. $request->messages = new stdClass();
  63. $locales = locale::installed();
  64. foreach ($locales as $locale => $locale_data) {
  65. $request->locales[] = $locale;
  66. }
  67. // @todo Batch requests (max request size)
  68. foreach (Database::instance()
  69. ->select("key", "locale", "revision", "translation")
  70. ->from("incoming_translations")
  71. ->get()
  72. ->as_array() as $row) {
  73. if (!isset($request->messages->{$row->key})) {
  74. $request->messages->{$row->key} = 1;
  75. }
  76. if (!empty($row->revision) && !empty($row->translation)) {
  77. if (!is_object($request->messages->{$row->key})) {
  78. $request->messages->{$row->key} = new stdClass();
  79. }
  80. $request->messages->{$row->key}->{$row->locale} = $row->revision;
  81. }
  82. }
  83. // @todo Include messages from outgoing_translations?
  84. $request_data = json_encode($request);
  85. $url = self::_server_url() . "?q=translations/fetch";
  86. list ($response_data, $response_status) = remote::post($url, array("data" => $request_data));
  87. if (!remote::success($response_status)) {
  88. throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED " . $response_status);
  89. }
  90. if (empty($response_data)) {
  91. log::info("translations", "Translations fetch request resulted in an empty response");
  92. return;
  93. }
  94. $response = json_decode($response_data);
  95. // Response format (JSON payload):
  96. // [{key:<key_1>, translation: <JSON encoded translation>, rev:<rev>, locale:<locale>},
  97. // {key:<key_2>, ...}
  98. // ]
  99. $count = count($response);
  100. log::info("translations", "Installed $count new / updated translation messages");
  101. foreach ($response as $message_data) {
  102. // @todo Better input validation
  103. if (empty($message_data->key) || empty($message_data->translation) ||
  104. empty($message_data->locale) || empty($message_data->rev)) {
  105. throw new Exception("@todo TRANSLATIONS_FETCH_REQUEST_FAILED: Invalid response data");
  106. }
  107. $key = $message_data->key;
  108. $locale = $message_data->locale;
  109. $revision = $message_data->rev;
  110. $translation = json_decode($message_data->translation);
  111. if (!is_string($translation)) {
  112. // Normalize stdclass to array
  113. $translation = (array) $translation;
  114. }
  115. $translation = serialize($translation);
  116. // @todo Should we normalize the incoming_translations table into messages(id, key, message)
  117. // and incoming_translations(id, translation, locale, revision)? Or just allow
  118. // incoming_translations.message to be NULL?
  119. $locale = $message_data->locale;
  120. $entry = ORM::factory("incoming_translation")
  121. ->where(array("key" => $key, "locale" => $locale))
  122. ->find();
  123. if (!$entry->loaded) {
  124. // @todo Load a message key -> message (text) dict into memory outside of this loop
  125. $root_entry = ORM::factory("incoming_translation")
  126. ->where(array("key" => $key, "locale" => "root"))
  127. ->find();
  128. $entry->key = $key;
  129. $entry->message = $root_entry->message;
  130. $entry->locale = $locale;
  131. }
  132. $entry->revision = $revision;
  133. $entry->translation = $translation;
  134. $entry->save();
  135. }
  136. }
  137. static function submit_translations() {
  138. // Request format (HTTP POST):
  139. // client_token = <client_token>
  140. // uid = <l10n server user id>
  141. // signature = md5(user_api_key($uid, $client_token) . $data . $client_token))
  142. // data = // JSON payload
  143. //
  144. // {<key_1>: {message: <JSON encoded message>
  145. // translations: {<locale_1>: <JSON encoded translation>,
  146. // <locale_2>: ...}},
  147. // <key_2>: {...}
  148. // }
  149. // @todo Batch requests (max request size)
  150. // @todo include base_revision in submission / how to handle resubmissions / edit fights?
  151. foreach (Database::instance()
  152. ->select("key", "message", "locale", "base_revision", "translation")
  153. ->from("outgoing_translations")
  154. ->get() as $row) {
  155. $key = $row->key;
  156. if (!isset($request->{$key})) {
  157. $request->{$key}->message = json_encode(unserialize($row->message));
  158. }
  159. $request->{$key}->translations->{$row->locale} = json_encode(unserialize($row->translation));
  160. }
  161. // @todo reduce memory consumption, e.g. free $request
  162. $request_data = json_encode($request);
  163. $url = self::_server_url() . "?q=translations/submit";
  164. $signature = self::_sign($request_data);
  165. list ($response_data, $response_status) = remote::post(
  166. $url, array("data" => $request_data,
  167. "client_token" => self::client_token(),
  168. "signature" => $signature,
  169. "uid" => self::server_uid()));
  170. if (!remote::success($response_status)) {
  171. throw new Exception("@todo TRANSLATIONS_SUBMISSION_FAILED " . $response_status);
  172. }
  173. if (empty($response_data)) {
  174. return;
  175. }
  176. $response = json_decode($response_data);
  177. // Response format (JSON payload):
  178. // [{key:<key_1>, locale:<locale_1>, rev:<rev_1>, status:<rejected|accepted|pending>},
  179. // {key:<key_2>, ...}
  180. // ]
  181. // @todo Move messages out of outgoing into incoming, using new rev?
  182. // @todo show which messages have been rejected / are pending?
  183. }
  184. /**
  185. * Plural forms.
  186. */
  187. static function plural_forms($locale) {
  188. $parts = explode('_', $locale);
  189. $language = $parts[0];
  190. // Data from CLDR 1.6 (http://unicode.org/cldr/data/common/supplemental/plurals.xml).
  191. // Docs: http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html
  192. switch ($language) {
  193. case 'az':
  194. case 'fa':
  195. case 'hu':
  196. case 'ja':
  197. case 'ko':
  198. case 'my':
  199. case 'to':
  200. case 'tr':
  201. case 'vi':
  202. case 'yo':
  203. case 'zh':
  204. case 'bo':
  205. case 'dz':
  206. case 'id':
  207. case 'jv':
  208. case 'ka':
  209. case 'km':
  210. case 'kn':
  211. case 'ms':
  212. case 'th':
  213. return array('other');
  214. case 'ar':
  215. return array('zero', 'one', 'two', 'few', 'many', 'other');
  216. case 'lv':
  217. return array('zero', 'one', 'other');
  218. case 'ga':
  219. case 'se':
  220. case 'sma':
  221. case 'smi':
  222. case 'smj':
  223. case 'smn':
  224. case 'sms':
  225. return array('one', 'two', 'other');
  226. case 'ro':
  227. case 'mo':
  228. case 'lt':
  229. case 'cs':
  230. case 'sk':
  231. case 'pl':
  232. return array('one', 'few', 'other');
  233. case 'hr':
  234. case 'ru':
  235. case 'sr':
  236. case 'uk':
  237. case 'be':
  238. case 'bs':
  239. case 'sh':
  240. case 'mt':
  241. return array('one', 'few', 'many', 'other');
  242. case 'sl':
  243. return array('one', 'two', 'few', 'other');
  244. case 'cy':
  245. return array('one', 'two', 'many', 'other');
  246. default: // en, de, etc.
  247. return array('one', 'other');
  248. }
  249. }
  250. }