'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; } } }