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