i18nstrings.module in Internationalization 5.3
Same filename and directory in other branches
Internationalization (i18n) package - translattable strings
This (rewritten) module uses locale's tables to store source strings in the default language. The storage aims to be forward compatible with Drupal 6 locale (with textgroups) and i18nstrings 6.x
The 'string id' is some context information about the string, has several parts separated by ':'
- textgroup, generic string group name (profile, content_type, etc..)
- object type, like 'field', 'type''
- object id
- property
About the storage, as Drupal 5 doesn't have textgroups, it will use an aditional table (i18n_locale) that links together 'strid' and 'lid'. While there may be multiple objects using the same default string, locale tables will only have one instance.
@author Jose A. Reyero, 2007
File
i18nstrings/i18nstrings.moduleView source
<?php
/**
* @file
* Internationalization (i18n) package - translattable strings
*
* This (rewritten) module uses locale's tables to store source strings in the default language. The storage aims
* to be forward compatible with Drupal 6 locale (with textgroups) and i18nstrings 6.x
*
* The 'string id' is some context information about the string, has several parts separated by ':'
* - textgroup, generic string group name (profile, content_type, etc..)
* - object type, like 'field', 'type''
* - object id
* - property
*
* About the storage, as Drupal 5 doesn't have textgroups, it will use an aditional table (i18n_locale)
* that links together 'strid' and 'lid'. While there may be multiple objects using the same default
* string, locale tables will only have one instance.
*
* @author Jose A. Reyero, 2007
*
*/
// This is a special char that shouldn't appear in paths and will help us store two strids on the same location
define('I18NSTRINGS_SPLIT', "\n");
/**
* Implementation of hook_menu().
*/
function i18nstrings_menu($may_cache) {
$items[] = array(
'path' => 'admin/settings/i18n/strings',
'type' => MENU_LOCAL_TASK,
'title' => t('Strings'),
'description' => t('Translatable strings.'),
'callback' => 'i18nstrings_admin',
'access' => user_access('administer site configuration'),
);
return $items;
}
/**
* Menu callback. Administration page
*/
function i18nstrings_admin($op = NULL, $strid = NULL) {
switch ($op) {
case 'edit':
return drupal_get_form('i18nstrings_admin_form', $strid);
default:
return i18nstrings_admin_overview();
}
}
/**
* List of strings
*/
function i18nstrings_admin_overview() {
$output = '';
$header = array(
t('String Id'),
t('Default'),
'',
);
$result = db_query("SELECT DISTINCT(strid) FROM {i18n_locale} ORDER BY strid", i18n_default_language());
$rows = array();
while ($str = db_fetch_object($result)) {
$rows[] = array(
$str->strid,
tt($str->strid),
l(t('edit'), 'admin/settings/i18n/strings/edit/' . $str->strid),
);
}
$output .= theme('table', $header, $rows);
return $output;
}
/**
* Form callback: i18nstrings_admin_form
*/
function i18nstrings_admin_form($strid) {
$strings = i18nstrings_load($strid);
$form['strid'] = array(
'#type' => 'value',
'#value' => $strid,
);
$form['languages'] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => t('Translations'),
);
// Approximate the number of rows in a textfield with a maximum of 10.
$trans = i18nstrings_get_string($strid, i18n_default_language());
$default = !empty($trans->translation) ? $trans->translation : '';
$rows = min(ceil(str_word_count($default) / 12), 10);
foreach (i18n_supported_languages() as $language => $name) {
$form['languages'][$language] = array(
'#type' => 'textarea',
'#rows' => $rows,
'#title' => $name,
'#default_value' => i18nstrings_get_string($strid, $language),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Form submit callback
*/
function i18nstrings_admin_form_submit($form_id, $form_values) {
$strid = $form_values['strid'];
foreach (i18n_supported_languages() as $language => $name) {
i18nstrings_save_string($strid, $language, $form_values['languages'][$language]);
}
drupal_set_message(t('The strings have been updated'));
return 'admin/settings/i18n/strings';
}
/**
* Load string translations
*/
function i18nstrings_load($strid) {
$strings = array();
$result = db_query("SELECT * FROM {i18n_locale} WHERE strid = '%s'", $strid);
while ($str = db_fetch_object($result)) {
$strings[$str->locale] = $str->text;
}
}
/**
* Get string for a language.
*
* @return object The translation object if found with lid and translation properties
*/
function i18nstrings_get_string($strid, $language = FALSE, $default = NULL, $refresh = FALSE) {
static $strings = array();
if ($refresh) {
$language ? $strings[$language] = array() : ($strings = array());
}
if (!isset($strings[$language][$strid])) {
$translation = db_fetch_object(db_query("SELECT s.lid, t.translation FROM {i18n_locale} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.locale = '%s' WHERE s.strid = '%s'", $language, $strid));
if ($translation) {
$strings[$language][$strid] = $translation->translation ? $translation : FALSE;
}
else {
// Looks like this string is not in the collection, add it
$source = i18nstrings_save_string($strid, $default);
$strings[$language][$strid] = $source;
}
}
return $strings[$language][$strid];
}
/**
* Save string for a language
*
* Locale's cache needs refreshing after calling this one. We don't do it here as
* locale_refresh_cache() is a quite expensive operation.
*/
function i18nstrings_save_string($strid, $string) {
// This will store the string sources to update or create at the end
if ($source = _i18nstrings_get_source($string, $strid)) {
if (!$source->strid) {
// We have the string, just need to create i18n row
$source->strid = $strid;
$source->paths[] = $strid;
_i18nstrings_save_source($source);
}
elseif ($source->source != $string) {
if (count($source->paths) == 1) {
// This is a unique i18n row, just update locale source
unset($source->strid);
// So i18n row is not updated we add it back 2 lines below
$source->source = $string;
_i18nstrings_save_source($source);
$source->strid = $strid;
}
else {
// Fuck, there are more i18n rows, we need to update this one and create a new one
$update = $source;
$update->paths = array_diff($source->paths, array(
$strid,
));
unset($source->strid);
_i18nstrings_save_source($update);
// Now we save the new one but search before for this other string
if ($search = _i18nstrings_get_source($string)) {
$source = $search;
$source->paths[] = $strid;
}
else {
// String not found so we create a full new one at the end
unset($source->lid);
$source->paths = array(
$strid,
);
}
$source->strid = $strid;
_i18nstrings_save_source($source);
}
}
else {
// Just check in case the strid is not in locale table, this shouldn't happen thoug
if (!in_array($strid, $source->paths)) {
$source->paths[] = $strid;
_i18nstrings_save_source($source);
}
}
}
else {
// Full create
$source = new Stdclass();
$source->strid = $strid;
$source->source = $string;
$source->paths[] = $strid;
_i18nstrings_save_source($source);
}
return $source;
}
/**
* Get source string from locale system
*
* @param $string
* String in the default language
* @param $strid
* String id
*/
function _i18nstrings_get_source($string, $strid = NULL) {
$source = NULL;
// First try with string id using i18n_locale
if ($strid) {
$source = db_fetch_object(db_query("SELECT i.*, s.source, s.location FROM {i18n_locale} i LEFT JOIN {locales_source} s ON i.lid = s.lid WHERE strid = '%s'", $strid));
}
if (!$source && $string) {
// Retry with the string itself using the locales_source table
$source = db_fetch_object(db_query("SELECT s.* FROM {locales_source} s WHERE s.source = '%s'", $string));
}
if ($source) {
// This will let us know how many object elements use this source string
// If location is empty (may happen when imported strings, the array should countain one empty string)
$source->paths = explode(I18NSTRINGS_SPLIT, $source->location);
}
return $source;
}
/**
* Update locale source and i18n table
*/
function _i18nstrings_save_source(&$source, $update = TRUE) {
// Build location with all the paths this string belongs to
$source->paths = array_unique($source->paths);
$source->location = implode(I18NSTRINGS_SPLIT, $source->paths);
if (!$source->lid) {
// Create locales_source and get new lid
db_query("INSERT INTO {locales_source} (location, source) VALUES ('%s', '%s')", $source->location, $source->source);
$source->lid = db_result(db_query("SELECT lid FROM {locales_source} WHERE location = '%s' AND source = '%s'", $source->location, $source->source));
}
elseif ($update) {
// Update locales source
db_query("UPDATE {locales_source} SET location = '%s', source = '%s' WHERE lid = %d", $source->location, $source->source, $source->lid);
}
// Now update/insert into i18n table
if ($source->strid) {
db_query("DELETE FROM {i18n_locale} WHERE strid = '%s'", $source->strid);
db_query("INSERT INTO {i18n_locale}(strid, lid) VALUES('%s', %d)", $source->strid, $source->lid);
}
}
/**
* Translate configurable string, and store for l10n client
*
* @param $strid
* Textgroup and location glued with ':'
* I.e. profile:
* @param $default
* String in default language. Default language may or may not be English
* @param $langcode
* Optional language code if different from current request language
* @param $update
* Whether to update/create the string
*/
function i18nstrings_tt($strid, $default, $language = NULL, $update = FALSE) {
global $locale, $l10n_client_strings;
//dsm("i18nstrings translating $strid $default");
$language = $language ? $language : $locale;
$string = NULL;
if ($update) {
i18nstrings_save_string($strid, $default);
}
if ($language == i18n_default_language()) {
// We just translate from default language, not to default language
return $default;
}
else {
// We get the full source object to add it to l10n list, if not existing it will be created, so we get an lid
$source = i18nstrings_get_string($strid, $language, $default);
if (!empty($source->translation)) {
$l10n_client_strings[$default] = $source;
return $source->translation;
}
else {
$l10n_client_strings[$default] = !empty($source) ? $source : TRUE;
return $default;
}
}
}
Functions
Name | Description |
---|---|
i18nstrings_admin | Menu callback. Administration page |
i18nstrings_admin_form | Form callback: i18nstrings_admin_form |
i18nstrings_admin_form_submit | Form submit callback |
i18nstrings_admin_overview | List of strings |
i18nstrings_get_string | Get string for a language. |
i18nstrings_load | Load string translations |
i18nstrings_menu | Implementation of hook_menu(). |
i18nstrings_save_string | Save string for a language |
i18nstrings_tt | Translate configurable string, and store for l10n client |
_i18nstrings_get_source | Get source string from locale system |
_i18nstrings_save_source | Update locale source and i18n table |
Constants
Name | Description |
---|---|
I18NSTRINGS_SPLIT |