| Commit | Line | Data |
|---|---|---|
| 30ffe47f JR |
1 | <?php |
| 2 | // $Id$ | |
| 3 | ||
| 4 | /** | |
| 5 | * @file | |
| c6531aa5 | 6 | * Internationalization (i18n) package - translatable strings |
| 30ffe47f | 7 | * |
| 2ed66aaa | 8 | * Object oriented string translation using locale and textgroups |
| c2dca9ef JR |
9 | * |
| 10 | * Some concepts | |
| 11 | * - Textgroup. Group the string belongs to, defined by locale hook | |
| 12 | * - Location. Unique id of the string for this textgroup | |
| 13 | * - Name. Unique absolute id of the string: textgroup + location | |
| 14 | * - Context. Object with textgroup, type, oid, property | |
| c6531aa5 JR |
15 | * - Default language may be English or not. It will be the language set as default |
| 16 | * Source strings will be stored in default language | |
| 17 | * | |
| 18 | * @ TO DO: Handle default language changes. | |
| 19 | * | |
| 20 | * @author Jose A. Reyero, 2007 | |
| 30ffe47f JR |
21 | */ |
| 22 | ||
| 23 | /** | |
| 24 | * Translate configurable string | |
| c6531aa5 JR |
25 | * |
| 26 | * @param $name | |
| 27 | * Textgroup and location glued with ':' | |
| 28 | * @param $string | |
| 29 | * String in default language. Default language may or may not be English | |
| 30 | * @param $langcode | |
| 31 | * Optional language code if different from current request language | |
| 30ffe47f | 32 | */ |
| c2dca9ef | 33 | function tt($name, $string, $langcode = NULL, $update = FALSE) { |
| a9c3be2f JR |
34 | global $language; |
| 35 | $langcode = $langcode ? $langcode : $language->language; | |
| 36 | ||
| 37 | // If language is default, just return | |
| dc76d5ac | 38 | if (language_default('language') == $langcode) { |
| c2dca9ef JR |
39 | if ($update) { |
| 40 | i18nstrings_update_string($name, $string); | |
| 41 | } | |
| a9c3be2f JR |
42 | return $string; |
| 43 | } else { | |
| c2dca9ef | 44 | return i18nstrings_tt($name, $string, $langcode, $update); |
| a9c3be2f JR |
45 | } |
| 46 | } | |
| 47 | ||
| 48 | /** | |
| 49 | * Translate configurable string | |
| c6531aa5 JR |
50 | * @param $name |
| 51 | * Textgroup and location glued with ':' | |
| a9c3be2f | 52 | */ |
| c2dca9ef | 53 | function i18nstrings_tt($name, $string, $langcode, $update = FALSE, $create = TRUE) { |
| c6531aa5 | 54 | $context = i18nstrings_context($name, $string); |
| a9c3be2f JR |
55 | |
| 56 | if ($update) { | |
| dc76d5ac | 57 | i18nstrings_update_string($context, $string); |
| a9c3be2f JR |
58 | } |
| 59 | ||
| 60 | $translation = i18nstrings_get_string($context, $langcode); | |
| 61 | ||
| 62 | if ($translation) { | |
| 63 | return ($translation === TRUE) ? $string : $translation; | |
| 64 | } elseif ($translation === FALSE && $create) { | |
| 65 | i18nstrings_update_string($context, $string); | |
| 66 | i18nstrings_cache($context, $string, $langcode, TRUE); | |
| 67 | } | |
| 68 | // Default case, just return untranslated string | |
| 69 | return $string; | |
| 70 | } | |
| 71 | ||
| 72 | /** | |
| c2dca9ef JR |
73 | * Translate object |
| 74 | */ | |
| 75 | function to($context, &$object, $properties = array(), $langcode = NULL, $update = FALSE) { | |
| 76 | global $language; | |
| 77 | ||
| 78 | $langcode = $langcode ? $langcode : $language->language; | |
| 79 | ||
| 80 | // If language is default, just return | |
| 81 | if (language_default('language') == $langcode && !$update) { | |
| 82 | return $object; | |
| 83 | } else { | |
| 84 | i18nstrings_to($context, $object, $properties, $langcode, $update); | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | /** | |
| a9c3be2f JR |
89 | * Translate object properties |
| 90 | */ | |
| c2dca9ef | 91 | function i18nstrings_to($context, &$object, $properties = array(), $langcode = NULL, $update = FALSE, $create = TRUE) { |
| a9c3be2f | 92 | $context = i18nstrings_context($context); |
| c2dca9ef | 93 | // @ TODO Object prefetch |
| a9c3be2f JR |
94 | foreach ($properties as $property) { |
| 95 | $context->property = $property; | |
| c2dca9ef JR |
96 | if (!empty($object->$property)) { |
| 97 | $object->property = i18nstrings_tt($context, $object->$property, $langcode, $update, $create); | |
| a9c3be2f JR |
98 | } |
| 99 | } | |
| 30ffe47f JR |
100 | } |
| 101 | ||
| 102 | /** | |
| a9c3be2f | 103 | * Update / create / remove string |
| c6531aa5 JR |
104 | * |
| 105 | * @param $context | |
| 106 | * String context | |
| 107 | * @pram $string | |
| 108 | * New value of string for update/create. May be empty for removing. | |
| 30ffe47f | 109 | */ |
| a9c3be2f | 110 | function i18nstrings_update_string($context, $string) { |
| c2dca9ef | 111 | $context = i18nstrings_context($context); |
| a9c3be2f | 112 | if ($string) { |
| c2dca9ef | 113 | $status = i18nstrings_add_string($context, $string); |
| a9c3be2f | 114 | } else { |
| c2dca9ef | 115 | $status = i18nstrings_remove_string($context); |
| a9c3be2f | 116 | } |
| c2dca9ef JR |
117 | $params = array( |
| 118 | '%location' => i18nstrings_location($context), | |
| 119 | '%textgroup' => $context->textgroup, | |
| 120 | '%string' => $string, | |
| 121 | ); | |
| 122 | switch ($status) { | |
| 123 | case SAVED_UPDATED: | |
| 124 | drupal_set_message(t('Updated string %location for textgroup %textgroup: %string', $params)); | |
| 125 | break; | |
| 126 | case SAVED_NEW: | |
| 127 | drupal_set_message(t('Created string %location for text group %textgroup: %string', $params)); | |
| 128 | break; | |
| 129 | } | |
| 130 | return $status; | |
| a9c3be2f JR |
131 | } |
| 132 | ||
| 133 | /** | |
| 2ed66aaa JR |
134 | * Update string translation |
| 135 | */ | |
| 136 | function i18nstrings_update_translation($context, $langcode, $translation) { | |
| 137 | if ($source = i18nstrings_get_source($context, $translation)) { | |
| 138 | db_query("INSERT INTO {locales_target}(lid, language, translation) VALUES(%d, '%s', '%s')", $source->lid, $langcode, $translation); | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | /** | |
| a9c3be2f JR |
143 | * Add string |
| 144 | * | |
| 145 | * This function checks for already existing string without context for this textgroup | |
| 146 | */ | |
| c2dca9ef JR |
147 | function i18nstrings_add_string($name, $string) { |
| 148 | $context = i18nstrings_context($name); | |
| 149 | $location = i18nstrings_location($context); | |
| a9c3be2f | 150 | |
| a9c3be2f JR |
151 | // Check if we have a source string |
| 152 | $source = i18nstrings_get_source($context, $string); | |
| 153 | ||
| 154 | $status = SAVED_UPDATED; | |
| 30ffe47f | 155 | |
| c2dca9ef JR |
156 | if ($source) { |
| 157 | if ($source->source != $string || $source->location != $location) { | |
| 158 | // String has changed or didnt have location | |
| a9c3be2f JR |
159 | db_query("UPDATE {locales_source} SET source = '%s' WHERE lid = %d", $string, $source->lid); |
| 160 | } else { | |
| 161 | $status = TRUE; | |
| 162 | } | |
| 163 | } | |
| 164 | else { | |
| c2dca9ef JR |
165 | db_query("INSERT INTO {locales_source} (location, source, textgroup, version) VALUES ('%s', '%s', '%s', '%s')", $location, $string, $context->textgroup, 0); |
| 166 | // Clear locale cache so this string can be added in a later request. | |
| 167 | cache_clear_all('locale:'.$context->textgroup.':', 'cache', TRUE); | |
| 168 | // Create string | |
| 169 | $source->lid = db_last_insert_id('locales_source', 'lid'); | |
| 170 | $status = SAVED_NEW; | |
| a9c3be2f | 171 | } |
| c2dca9ef JR |
172 | // Update metadata |
| 173 | db_query("DELETE FROM {i18n_strings} WHERE lid = %d", $source->lid); | |
| 174 | db_query("INSERT INTO {i18n_strings} (lid, type, oid, property) VALUES(%d, '%s', %d, '%s')", $source->lid, $context->type, $context->oid, $context->property); | |
| 175 | ||
| a9c3be2f JR |
176 | return $status; |
| 177 | } | |
| 178 | ||
| 179 | /** | |
| c6531aa5 JR |
180 | * Get source string provided a string context |
| 181 | * | |
| 182 | * This will search first with the full context parameters and, if not found, | |
| 183 | * it will search again only with textgroup and source string | |
| 184 | * | |
| 185 | * @param $context | |
| 186 | * Context string or object | |
| 187 | * @return | |
| 188 | * Context object if it exists | |
| a9c3be2f | 189 | */ |
| a9c3be2f JR |
190 | function i18nstrings_get_source($context, $string = NULL) { |
| 191 | $context = i18nstrings_context($context); | |
| c2dca9ef JR |
192 | |
| 193 | // Check if we have the string for this location | |
| c6531aa5 JR |
194 | list($where, $args) = i18nstrings_context_query($context); |
| 195 | if ($source = db_fetch_object(db_query("SELECT s.*, i.type, i.oid, i.property FROM {locales_source} s LEFT JOIN {i18n_strings} i ON s.lid = i.lid WHERE ". implode(' AND ', $where), $args))) { | |
| a9c3be2f JR |
196 | $source->context = $context; |
| 197 | return $source; | |
| 198 | } | |
| 199 | // Search for the same string for this textgroup without object data | |
| 200 | if ($string && $source = db_fetch_object(db_query("SELECT s.*, i.type, i.oid, i.property FROM {locales_source} s LEFT JOIN {i18n_strings} i ON s.lid = i.lid WHERE s.textgroup = '%s' AND s.source = '%s' AND i.lid IS NULL", $context->textgroup, $string))) { | |
| 201 | $source->context = NULL; | |
| 202 | return $source; | |
| 203 | } | |
| a9c3be2f | 204 | } |
| c2dca9ef | 205 | |
| a9c3be2f JR |
206 | /** |
| 207 | * Get string for a language | |
| 208 | * | |
| c6531aa5 JR |
209 | * @param $context |
| 210 | * Context string or object | |
| 211 | * @param $langcode | |
| 212 | * Language code to retrieve string for | |
| 213 | * | |
| a9c3be2f JR |
214 | * @return |
| 215 | * - translation if found | |
| 216 | * - TRUE if not found and cached | |
| 217 | * - FALSE if not even cached | |
| 218 | * | |
| 219 | */ | |
| 220 | function i18nstrings_get_string($context, $langcode) { | |
| 221 | $context = i18nstrings_context($context); | |
| 222 | if ($translation = i18nstrings_cache($context, $langcode)) { | |
| 223 | return $translation; | |
| 224 | } else { | |
| 225 | // Search translation and add it to the cache | |
| c6531aa5 JR |
226 | list($where, $args) = i18nstrings_context_query($context); |
| 227 | $where[] = "t.language = '%s'"; | |
| 228 | $args[] = $langcode; | |
| 229 | $text = db_fetch_object(db_query("SELECT s.*, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE ". implode(' AND ', $where), $args)); | |
| a9c3be2f JR |
230 | if ($text && $text->translation) { |
| 231 | i18nstrings_cache($context, $langcode, NULL, $text->translation); | |
| 232 | return $text->translation; | |
| 233 | } else { | |
| 234 | i18nstrings_cache($context, $langcode, NULL, TRUE); | |
| 235 | return $text ? NULL : FALSE ; | |
| 236 | } | |
| 237 | } | |
| 238 | } | |
| 239 | ||
| 240 | /** | |
| c2dca9ef | 241 | * Remove string for a given context |
| a9c3be2f JR |
242 | */ |
| 243 | function i18nstrings_remove_string($context, $string = NULL) { | |
| 244 | if ($source = i18nstrings_get_source($context, $string)) { | |
| 245 | db_query("DELETE FROM {locales_target} WHERE lid = %d", $source->lid); | |
| 246 | db_query("DELETE FROM {i18n_strings} WHERE lid = %d", $source->lid); | |
| 247 | db_query("DELETE FROM {locales_source} WHERE lid = %d", $source->lid); | |
| 248 | cache_clear_all('locale:'.$context->textgroup.':', 'cache', TRUE); | |
| dc76d5ac | 249 | return SAVED_DELETED; |
| a9c3be2f | 250 | } |
| 30ffe47f JR |
251 | } |
| 252 | ||
| 253 | /** | |
| c6531aa5 JR |
254 | * Update context for strings. |
| 255 | * | |
| 256 | * As some string locations depend on configurable values, the field needs sometimes to be updated | |
| 257 | * without losing existing translations. I.e: | |
| 258 | * - profile fields indexed by field name | |
| 259 | * - content types indexted by low level content type name | |
| c2dca9ef JR |
260 | * |
| 261 | * Example | |
| 262 | * 'profile:field:oldfield:*' -> 'profile:field:newfield:*' | |
| 263 | */ | |
| 264 | function i18nstrings_update_context($oldname, $newname) { | |
| 265 | // Get context replacing '*' with empty string | |
| 266 | $oldcontext = i18nstrings_context(str_replace('*', '', $oldname)); | |
| 267 | $newcontext = i18nstrings_context(str_replace('*', '', $newname)); | |
| 268 | // Get location with placeholders | |
| 269 | $location = i18nstrings_location(str_replace('*', '%', $oldname)); | |
| 270 | foreach (array('textgroup', 'type', 'oid', 'property') as $field) { | |
| 271 | if ((!empty($oldcontext->$field) || !empty($newcontext->$field)) && $oldcontext->$field != $newcontext->$field) { | |
| 272 | $replace[$field] = $newcontext->$field; | |
| 273 | } | |
| 274 | } | |
| 275 | // Query and replace | |
| 276 | $result = db_query("SELECT s.*, i.type, i.oid, i.property FROM {locales_source} s LEFT JOIN {i18n_strings} i ON s.lid = i.lid WHERE s.textgroup = '%s' AND s.location LIKE '%s'", $oldcontext->textgroup, $location); | |
| 277 | while ($source = db_fetch_object($result)) { | |
| 278 | // Make sure we have string and context | |
| 279 | $context = i18nstrings_context($oldcontext->textgroup.':'.$source->location); | |
| 280 | foreach ($replace as $field => $value) { | |
| 281 | $context->$field = $value; | |
| 282 | } | |
| 283 | // Update source string | |
| 284 | db_query("UPDATE {locales_source} SET textgroup = '%s', location = '%s' WHERE lid = %d", $context->textgroup, i18nstrings_location($context), $source->lid); | |
| 285 | // Update object data | |
| 286 | db_query("UPDATE {i18n_strings} SET type = '%s', oid = '%s', property = '%s' WHERE lid = %d", $context->type, $context->oid, $context->property, $source->lid); | |
| 287 | } | |
| 288 | drupal_set_message(t('Updating string names from %oldname to %newname', array('%oldname' => $oldname, '%newname' => $newname))); | |
| 289 | } | |
| 290 | ||
| 291 | /** | |
| 30ffe47f JR |
292 | * Provides interface translation services. |
| 293 | * | |
| c6531aa5 | 294 | * This function is called from tt() to translate a string if needed. |
| 30ffe47f JR |
295 | * |
| 296 | * @param $textgroup | |
| 297 | * | |
| 298 | * @param $string | |
| 299 | * A string to look up translation for. If omitted, all the | |
| 300 | * cached strings will be returned in all languages already | |
| 301 | * used on the page. | |
| 302 | * @param $langcode | |
| 303 | * Language code to use for the lookup. | |
| 304 | */ | |
| a9c3be2f | 305 | function i18nstrings_textgroup($textgroup, $string = NULL, $langcode = NULL) { |
| 30ffe47f JR |
306 | global $language; |
| 307 | static $locale_t; | |
| 308 | ||
| 309 | // Return all cached strings if no string was specified | |
| 310 | if (!isset($string)) { | |
| 311 | return isset($locale_t[$textgroup]) ? $locale_t[$textgroup] : array(); | |
| 312 | } | |
| 313 | ||
| 314 | $langcode = isset($langcode) ? $langcode : $language->language; | |
| 315 | ||
| 316 | // Store database cached translations in a static var. | |
| 317 | if (!isset($locale_t[$langcode])) { | |
| 318 | $locale_t[$langcode] = array(); | |
| 319 | // Disabling the usage of string caching allows a module to watch for | |
| 320 | // the exact list of strings used on a page. From a performance | |
| 321 | // perspective that is a really bad idea, so we have no user | |
| 322 | // interface for this. Be careful when turning this option off! | |
| 323 | if (variable_get('locale_cache_strings', 1) == 1) { | |
| 324 | if ($cache = cache_get('locale:'.$textgroup.':'.$langcode, 'cache')) { | |
| 325 | $locale_t[$textgroup][$langcode] = $cache->data; | |
| 326 | } | |
| 327 | else { | |
| 328 | // Refresh database stored cache of translations for given language. | |
| 329 | // We only store short strings used in current version, to improve | |
| 330 | // performance and consume less memory. | |
| 331 | $result = db_query("SELECT s.source, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.textgroup = '%s' AND s.version = '%s' AND LENGTH(s.source) < 75", $langcode, $textgroup, VERSION); | |
| 332 | while ($data = db_fetch_object($result)) { | |
| 333 | $locale_t[$textgroup][$langcode][$data->source] = (empty($data->translation) ? TRUE : $data->translation); | |
| 334 | } | |
| 335 | cache_set('locale:'.$textgroup.':'. $langcode, $locale_t[$textgroup][$langcode]); | |
| 336 | } | |
| 337 | } | |
| 338 | } | |
| 339 | ||
| 340 | // If we have the translation cached, skip checking the database | |
| 341 | if (!isset($locale_t[$textgroup][$langcode][$string])) { | |
| 342 | ||
| 343 | // We do not have this translation cached, so get it from the DB. | |
| 344 | $translation = db_fetch_object(db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = '%s' WHERE s.source = '%s' AND s.textgroup = '%s'", $langcode, $string, $textgroup)); | |
| 345 | if ($translation) { | |
| 346 | // We have the source string at least. | |
| 347 | // Cache translation string or TRUE if no translation exists. | |
| 348 | $locale_t[$textgroup][$langcode][$string] = (empty($translation->translation) ? TRUE : $translation->translation); | |
| 349 | ||
| 350 | if ($translation->version != VERSION) { | |
| 351 | // This is the first use of this string under current Drupal version. Save version | |
| 352 | // and clear cache, to include the string into caching next time. Saved version is | |
| 353 | // also a string-history information for later pruning of the tables. | |
| 354 | db_query("UPDATE {locales_source} SET version = '%s' WHERE lid = %d LIMIT 1", VERSION, $translation->lid); | |
| 355 | cache_clear_all('locale:'.$textgroup.':', 'cache', TRUE); | |
| 356 | } | |
| 357 | } | |
| 358 | else { | |
| 359 | // We don't have the source string, cache this as untranslated. | |
| 360 | db_query("INSERT INTO {locales_source} (location, source, textgroup, version) VALUES ('%s', '%s', 'default', '%s')", request_uri(), $string, VERSION); | |
| 361 | $locale_t[$langcode][$string] = TRUE; | |
| 362 | // Clear locale cache so this string can be added in a later request. | |
| 363 | cache_clear_all('locale:'.$textgroup.':', 'cache', TRUE); | |
| 364 | } | |
| 365 | } | |
| 366 | ||
| 367 | return ($locale_t[$textgroup][$langcode][$string] === TRUE ? $string : $locale_t[$textgroup][$langcode][$string]); | |
| 368 | } | |
| 369 | ||
| 370 | /** | |
| c6531aa5 | 371 | * Convert context string in a context object |
| 30ffe47f JR |
372 | * |
| 373 | * I.e. | |
| 374 | * 'taxonomy:term:1:name' | |
| 375 | * will become a $context object where | |
| 376 | * $context->textgroup = 'taxonomy'; | |
| 377 | * $context->type = 'term'; | |
| 378 | * $context->oid = 1; | |
| 379 | * $context->property = 'name'; | |
| a9c3be2f | 380 | * Examples: |
| c2dca9ef | 381 | * 'taxonomy:title' -> (taxonomy, title, 0, 0) |
| c6531aa5 JR |
382 | * 'contenttypes:type:[type]:name' |
| 383 | * 'contenttypes:type:[type]:description' | |
| 384 | * 'profile:category' | |
| 385 | * 'profile:field:[fid]:title' | |
| 386 | * | |
| 387 | * @param $context | |
| 388 | * Context string or object | |
| 389 | * @return | |
| 390 | * Context object with textgroup, type, oid, property and location names | |
| 30ffe47f | 391 | */ |
| c6531aa5 | 392 | function i18nstrings_context($context, $string = NULL) { |
| 30ffe47f JR |
393 | // Context may be already an object |
| 394 | if (is_object($context)) { | |
| 395 | return $context; | |
| 396 | } else { | |
| c2dca9ef JR |
397 | // We add empty fields at the end before splitting |
| 398 | list($textgroup, $type, $oid, $property) = split(':', $context.':::'); | |
| 399 | $context = (object)array( | |
| 30ffe47f | 400 | 'textgroup' => $textgroup, |
| c2dca9ef | 401 | 'type' => $type, |
| 30ffe47f | 402 | 'oid' => $oid ? $oid : 0, |
| c2dca9ef | 403 | 'property' => $property ? $property : 0, |
| 30ffe47f | 404 | ); |
| c2dca9ef | 405 | $context->location = i18nstrings_location($context); |
| c6531aa5 JR |
406 | if (!$context->oid && !$context->property && $string) { |
| 407 | $context->source = $string; | |
| 408 | } | |
| c2dca9ef | 409 | return $context; |
| 30ffe47f JR |
410 | } |
| 411 | } | |
| c6531aa5 JR |
412 | |
| 413 | /** | |
| 414 | * Get query conditions for this context | |
| 415 | */ | |
| 416 | function i18nstrings_context_query($context, $alias = 's') { | |
| 417 | $where = array("$alias.textgroup = '%s'", "$alias.location = '%s'"); | |
| 418 | $args = array($context->textgroup, $context->location); | |
| 419 | if (!empty($context->source)) { | |
| 420 | $where[] = "s.source = '%s'"; | |
| 421 | $args[] = $context->source; | |
| 422 | } | |
| 423 | return array($where, $args); | |
| 424 | } | |
| 425 | ||
| c2dca9ef JR |
426 | /** |
| 427 | * Get location string from context | |
| c6531aa5 JR |
428 | * |
| 429 | * Returns the location for the locale table for a string context | |
| c2dca9ef JR |
430 | */ |
| 431 | function i18nstrings_location($context) { | |
| 432 | if (is_string($context)){ | |
| 433 | $context = i18nstrings_context($context); | |
| 434 | } | |
| 435 | $location[] = $context->type; | |
| 436 | if ($context->oid) { | |
| 437 | $location[] = $context->oid; | |
| 438 | if ($context->property) { | |
| 439 | $location[] = $context->property; | |
| 440 | } | |
| 441 | } | |
| 442 | return implode(':', $location); | |
| 443 | } | |
| 30ffe47f JR |
444 | |
| 445 | /** | |
| 446 | * Prefetch a number of object strings | |
| 447 | */ | |
| 448 | function i18nstrings_prefetch($context, $langcode = NULL, $join = array(), $conditions = array()) { | |
| 449 | global $language; | |
| 450 | ||
| 451 | $langcode = $langcode ? $langcode : $language->language; | |
| 452 | // Add language condition | |
| 453 | $conditions['t.language'] = $langcode; | |
| 454 | // Get context conditions | |
| 455 | $context = (array)i18nstrings_context($context); | |
| 456 | foreach ($context as $key => $value) { | |
| 457 | if ($value) { | |
| 458 | if ($key == 'textgroup') { | |
| 459 | $conditions['s.textgroup'] = $value; | |
| 460 | } else { | |
| 461 | $conditions['i.'. $key] = $value; | |
| 462 | } | |
| 463 | } | |
| 464 | } | |
| 465 | // Prepare where clause | |
| 466 | $where = $params = array(); | |
| 467 | foreach ($conditions as $key => $value) { | |
| 468 | if (is_array($value)) { | |
| 469 | $where[] = $key . ' IN ('.db_placeholders($value, is_int($value[0]) ? 'int' : 'string').')'; | |
| 470 | $params = array_merge($params, $value); | |
| 471 | } else { | |
| 472 | $where[] = $key . ' = ' . is_int($value) ? '%d' : "'%s'"; | |
| 473 | $params[] = $value; | |
| 474 | } | |
| 475 | } | |
| 476 | $sql = "SELECT s.textgroup, s.source, i.type, i.oid, i.property, t.translation FROM {locales_source} s INNER JOIN {i18n_strings} i ON s.lid = i.lid INNER JOIN {locales_target} t ON s.lid = t.lid "; | |
| 477 | $sql .= implode(' ', $join) . ' ' . implode (' AND ', $where); | |
| 478 | $result = db_query($sql, $params); | |
| 479 | ||
| 480 | // Fetch all rows and store in cache | |
| 481 | while ($t = db_fetch_object($result)) { | |
| 482 | i18nstrings_cache($t, $langcode, $t->source, $t->translation); | |
| 483 | } | |
| 484 | ||
| 485 | } | |
| 486 | ||
| 487 | /** | |
| c6531aa5 | 488 | * Retrieves and stores translations in page (static variable) cache. |
| 30ffe47f JR |
489 | */ |
| 490 | function i18nstrings_cache($context, $langcode, $string = NULL, $translation = NULL) { | |
| 491 | static $strings; | |
| 492 | ||
| 493 | $context = i18nstrings_context($context); | |
| 494 | ||
| c2dca9ef JR |
495 | if (!$context->oid && $string) { |
| 496 | // This is a type indexed by string | |
| 497 | $context->oid = $string; | |
| 498 | } | |
| 499 | // At this point context must have at least textgroup and type | |
| 30ffe47f | 500 | if ($translation) { |
| c2dca9ef | 501 | if ($context->property) { |
| 30ffe47f | 502 | $strings[$langcode][$context->textgroup][$context->type][$context->oid][$context->property] = $translation; |
| c2dca9ef JR |
503 | } elseif ($context->oid) { |
| 504 | $strings[$langcode][$context->textgroup][$context->type][$context->oid] = $translation; | |
| 30ffe47f | 505 | } else { |
| c2dca9ef | 506 | $strings[$langcode][$context->textgroup][$context->type] = $translation; |
| 30ffe47f JR |
507 | } |
| 508 | } else { | |
| c2dca9ef JR |
509 | // Search up the tree for the object or a default |
| 510 | $search = &$strings[$langcode]; | |
| 511 | $default = NULL; | |
| 512 | $list = array('textgroup', 'type', 'oid', 'property'); | |
| 513 | while (($field = array_shift($list)) && !empty($context->$field)) { | |
| 514 | if (isset($search[$context->$field])) { | |
| 515 | $search = &$search[$context->$field]; | |
| 516 | if (isset($search['#default'])) { | |
| 517 | $default = $search['#default']; | |
| 518 | } | |
| 519 | } else { | |
| 520 | // We dont have cached this tree so we return the default | |
| 521 | return $default; | |
| 522 | } | |
| 30ffe47f | 523 | } |
| c2dca9ef JR |
524 | // Returns the part of the array we got to |
| 525 | return $search; | |
| 526 | } | |
| 527 | ||
| 30ffe47f JR |
528 | } |
| 529 | ||
| 530 | /** | |
| 30ffe47f JR |
531 | * Implementation of hook_menu(). |
| 532 | * | |
| 533 | function i18nstrings_menu($may_cache) { | |
| 534 | $items = array(); | |
| 535 | if ($may_cache) { | |
| 536 | $items[] = array( | |
| 537 | 'path' => 'admin/settings/i18n/strings', | |
| 538 | 'type' => MENU_LOCAL_TASK, | |
| 539 | 'title' => t('Strings'), | |
| 540 | 'description' => t('Translatable strings.'), | |
| 541 | 'callback' => 'i18nstrings_admin', | |
| 542 | 'access' => user_access('administer site configuration'), | |
| 543 | ); | |
| 544 | } else { | |
| 545 | ||
| 546 | } | |
| 547 | ||
| 548 | return $items; | |
| 549 | } | |
| 550 | ||
| 551 | /** | |
| 552 | * Menu callback. Administration page | |
| 553 | * | |
| 554 | function i18nstrings_admin($op = NULL, $strid = NULL) { | |
| 555 | switch($op) { | |
| 556 | case 'edit': | |
| 557 | return drupal_get_form('i18nstrings_admin_form', $strid); | |
| 558 | default: | |
| 559 | return i18nstrings_admin_overview(); | |
| 560 | } | |
| 561 | } | |
| 562 | ||
| 563 | /** | |
| 564 | * List of strings | |
| 565 | * | |
| 566 | function i18nstrings_admin_overview() { | |
| 567 | $output = ''; | |
| 568 | $header = array(t('String Id'), t('Default'), ''); | |
| 569 | $result = db_query("SELECT DISTINCT(strid) FROM {i18n_strings} ORDER BY strid", i18n_default_language()); | |
| 570 | $rows = array(); | |
| 571 | while($str = db_fetch_object($result)) { | |
| 572 | $rows[] = array( | |
| 573 | $str->strid, | |
| 574 | tt($str->strid), | |
| 575 | l(t('edit'), 'admin/settings/i18n/strings/edit/'.$str->strid) | |
| 576 | ); | |
| 577 | } | |
| 578 | $output .= theme('table', $header, $rows); | |
| 579 | return $output; | |
| 580 | } | |
| 581 | ||
| 582 | /** | |
| 583 | * Form callback: i18nstrings_admin_form | |
| 584 | * | |
| 585 | function i18nstrings_admin_form($strid) { | |
| 586 | $strings = i18nstrings_load($strid); | |
| 587 | $form['strid'] = array('#type' => 'value', '#value' => $strid); | |
| 588 | $form['languages'] = array('#type' => 'fieldset', '#tree' => TRUE, '#title' => t('Translations')); | |
| 589 | ||
| 590 | // Approximate the number of rows in a textfield with a maximum of 10. | |
| 591 | $default = i18nstrings_get_string($strid, i18n_default_language()); | |
| 592 | $rows = min(ceil(str_word_count($default) / 12), 10); | |
| 593 | ||
| 594 | foreach (i18n_supported_languages() as $language => $name) { | |
| 595 | $form['languages'][$language] = array( | |
| 596 | '#type' => 'textarea', | |
| 597 | '#rows' => $rows, | |
| 598 | '#title' => $name, | |
| 599 | '#default_value' => i18nstrings_get_string($strid, $language) | |
| 600 | ); | |
| 601 | } | |
| 602 | $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); | |
| 603 | return $form; | |
| 604 | } | |
| 605 | ||
| 606 | /** | |
| 607 | * Form submit callback | |
| 608 | * | |
| 609 | function i18nstrings_admin_form_submit($form_id, $form_values) { | |
| 610 | $strid = $form_values['strid']; | |
| 611 | foreach (i18n_supported_languages() as $language => $name) { | |
| 612 | i18nstrings_save_string($strid, $language, $form_values['languages'][$language]); | |
| 613 | } | |
| 614 | drupal_set_message(t('The strings have been updated')); | |
| 615 | return 'admin/settings/i18n/strings'; | |
| 616 | } | |
| 617 | ||
| 618 | /** | |
| 619 | * Load string translations | |
| 620 | * | |
| 621 | function i18nstrings_load($strid) { | |
| 622 | $strings = array(); | |
| 623 | $result = db_query("SELECT * FROM {i18n_strings} WHERE strid = '%s'", $strid); | |
| 624 | while ($str = db_fetch_object($result)) { | |
| 625 | $strings[$str->locale] = $str->text; | |
| 626 | } | |
| 627 | } | |
| 628 | ||
| 629 | /** | |
| 630 | * Save string for a language | |
| 631 | * | |
| 632 | function i18nstrings_save_string($strid, $language, $value) { | |
| 633 | drupal_set_message("DEBUG: i18n_strings_save: $strid($language)= $value"); | |
| 634 | db_query("DELETE FROM {i18n_strings} WHERE strid = '%s' AND locale = '%s'", $strid, $language); | |
| 635 | db_query("INSERT INTO {i18n_strings}(strid, locale, text) VALUES('%s', '%s', '%s')", $strid, $language, $value); | |
| 636 | } | |
| 637 | /**/ |