'all', 'language' => 'all', 'string' => '', ); } $sql_query = db_select('locales_source', 's'); $sql_query->leftJoin('locales_target', 't', 't.lid = s.lid'); $sql_query->fields('s', array('source', 'location', 'context', 'lid')); $sql_query->fields('t', array('translation', 'language')); // Compute LIKE section. switch ($query['translation']) { case 'translated': $sql_query->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); $sql_query->orderBy('t.translation', 'DESC'); break; case 'untranslated': $sql_query->condition(db_and() ->condition('s.source', '%' . db_like($query['string']) . '%', 'LIKE') ->isNull('t.translation') ); $sql_query->orderBy('s.source'); break; case 'all' : default: $condition = db_or() ->condition('s.source', '%' . db_like($query['string']) . '%', 'LIKE'); if ($query['language'] != LANGUAGE_SYSTEM) { // Only search in translations if the language is not forced to system language. $condition->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); } $sql_query->condition($condition); break; } $limit_language = NULL; if ($query['language'] != LANGUAGE_SYSTEM && $query['language'] != 'all') { $sql_query->condition('language', $query['language']); $limit_language = $query['language']; } $sql_query = $sql_query->extend('PagerDefault')->limit(50); $locales = $sql_query->execute(); $header = array(t('String'), t('Context'), ($limit_language) ? t('Language') : t('Languages'), array('data' => t('Operations'), 'colspan' => '2')); $strings = array(); foreach ($locales as $locale) { if (!isset($strings[$locale->lid])) { $strings[$locale->lid] = array( 'languages' => array(), 'location' => $locale->location, 'source' => $locale->source, 'context' => $locale->context, ); } if (isset($locale->language)) { $strings[$locale->lid]['languages'][$locale->language] = $locale->translation; } } $rows = array(); foreach ($strings as $lid => $string) { $rows[] = array( array('data' => check_plain(truncate_utf8($string['source'], 150, FALSE, TRUE)) . '
' . $string['location'] . ''), $string['context'], array('data' => _locale_translate_language_list($string['languages'], $limit_language), 'align' => 'center'), array('data' => l(t('edit'), "admin/config/regional/translate/edit/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')), array('data' => l(t('delete'), "admin/config/regional/translate/delete/$lid", array('query' => drupal_get_destination())), 'class' => array('nowrap')), ); } $output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No strings available.'))); $output .= theme('pager'); return $output; } /** * List languages in search result table */ function _locale_translate_language_list($translation, $limit_language) { // Add CSS. drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); $languages = language_list(); if (!locale_translate_english()) { unset($languages['en']); } $output = ''; foreach ($languages as $langcode => $language) { if (!$limit_language || $limit_language == $langcode) { $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "$langcode "; } } return $output; } /** * Build array out of search criteria specified in request variables */ function _locale_translate_seek_query() { $query = &drupal_static(__FUNCTION__); if (!isset($query)) { $query = array(); $fields = array('string', 'language', 'translation'); foreach ($fields as $field) { if (isset($_SESSION['locale_translation_filter'][$field])) { $query[$field] = $_SESSION['locale_translation_filter'][$field]; } } } return $query; } /** * List locale translation filters that can be applied. */ function locale_translation_filters() { $filters = array(); // Get all languages, except English drupal_static_reset('language_list'); $languages = language_list(TRUE); $language_options = array(); foreach ($languages as $langcode => $language) { if ($langcode != 'en' || locale_translate_english()) { $language_options[$langcode] = $language->name; } } $filters['string'] = array( 'title' => t('String contains'), 'description' => t('Leave blank to show all strings. The search is case sensitive.'), ); $filters['language'] = array( 'title' => t('Language'), 'options' => array_merge(array('all' => t('All languages'), LANGUAGE_SYSTEM => t('System (English)')), $language_options), ); $filters['translation'] = array( 'title' => t('Search in'), 'options' => array('all' => t('Both translated and untranslated strings'), 'translated' => t('Only translated strings'), 'untranslated' => t('Only untranslated strings')), ); return $filters; } /** * Return form for locale translation filters. * * @ingroup forms */ function locale_translation_filter_form() { $filters = locale_translation_filters(); $form['filters'] = array( '#type' => 'fieldset', '#title' => t('Filter translatable strings'), '#collapsible' => TRUE, '#collapsed' => FALSE, ); foreach ($filters as $key => $filter) { // Special case for 'string' filter. if ($key == 'string') { $form['filters']['status']['string'] = array( '#type' => 'textfield', '#title' => $filter['title'], '#description' => $filter['description'], ); } else { $form['filters']['status'][$key] = array( '#title' => $filter['title'], '#type' => 'select', '#empty_value' => 'all', '#empty_option' => $filter['options']['all'], '#size' => 0, '#options' => $filter['options'], ); } if (!empty($_SESSION['locale_translation_filter'][$key])) { $form['filters']['status'][$key]['#default_value'] = $_SESSION['locale_translation_filter'][$key]; } } $form['filters']['actions'] = array( '#type' => 'actions', '#attributes' => array('class' => array('container-inline')), ); $form['filters']['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Filter'), ); if (!empty($_SESSION['locale_translation_filter'])) { $form['filters']['actions']['reset'] = array( '#type' => 'submit', '#value' => t('Reset') ); } return $form; } /** * Validate result from locale translation filter form. */ function locale_translation_filter_form_validate($form, &$form_state) { if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['language'])) { form_set_error('type', t('You must select something to filter by.')); } } /** * Process result from locale translation filter form. */ function locale_translation_filter_form_submit($form, &$form_state) { $op = $form_state['values']['op']; $filters = locale_translation_filters(); switch ($op) { case t('Filter'): foreach ($filters as $name => $filter) { if (isset($form_state['values'][$name])) { $_SESSION['locale_translation_filter'][$name] = $form_state['values'][$name]; } } break; case t('Reset'): $_SESSION['locale_translation_filter'] = array(); break; } $form_state['redirect'] = 'admin/config/regional/translate/translate'; } /** * User interface for string editing. * * @ingroup forms */ function locale_translate_edit_form($form, &$form_state, $lid) { // Fetch source string, if possible. $source = db_query('SELECT source, context, location FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject(); if (!$source) { drupal_set_message(t('String not found.'), 'error'); drupal_goto('admin/config/regional/translate/translate'); } // Add original text to the top and some values for form altering. $form['original'] = array( '#type' => 'item', '#title' => t('Original text'), '#markup' => check_plain(wordwrap($source->source, 0)), ); if (!empty($source->context)) { $form['context'] = array( '#type' => 'item', '#title' => t('Context'), '#markup' => check_plain($source->context), ); } $form['lid'] = array( '#type' => 'value', '#value' => $lid ); $form['location'] = array( '#type' => 'value', '#value' => $source->location ); // Include default form controls with empty values for all languages. // This ensures that the languages are always in the same order in forms. $languages = language_list(); if (!locale_translate_english()) { unset($languages['en']); } $form['translations'] = array('#tree' => TRUE); // Approximate the number of rows to use in the default textarea. $rows = min(ceil(str_word_count($source->source) / 12), 10); foreach ($languages as $langcode => $language) { $form['translations'][$langcode] = array( '#type' => 'textarea', '#title' => $language->name, '#rows' => $rows, '#default_value' => '', ); } // Fetch translations and fill in default values in the form. $result = db_query("SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = :lid", array(':lid' => $lid)); foreach ($result as $translation) { $form['translations'][$translation->language]['#default_value'] = $translation->translation; } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save translations')); return $form; } /** * Validate string editing form submissions. */ function locale_translate_edit_form_validate($form, &$form_state) { foreach ($form_state['values']['translations'] as $key => $value) { if (!locale_string_is_safe($value)) { form_set_error('translations', t('The submitted string contains disallowed HTML: %string', array('%string' => $value))); watchdog('locale', 'Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value), WATCHDOG_WARNING); } } } /** * Process string editing form submissions. * * Saves all translations of one string submitted from a form. */ function locale_translate_edit_form_submit($form, &$form_state) { $lid = $form_state['values']['lid']; foreach ($form_state['values']['translations'] as $key => $value) { $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $key))->fetchField(); if (!empty($value)) { // Only update or insert if we have a value to use. if (!empty($translation)) { db_update('locales_target') ->fields(array( 'translation' => $value, )) ->condition('lid', $lid) ->condition('language', $key) ->execute(); } else { db_insert('locales_target') ->fields(array( 'lid' => $lid, 'translation' => $value, 'language' => $key, )) ->execute(); } } elseif (!empty($translation)) { // Empty translation entered: remove existing entry from database. db_delete('locales_target') ->condition('lid', $lid) ->condition('language', $key) ->execute(); } // Force JavaScript translation file recreation for this language. _locale_invalidate_js($key); } drupal_set_message(t('The string has been saved.')); // Clear locale cache. _locale_invalidate_js(); cache()->deletePrefix('locale:'); $form_state['redirect'] = 'admin/config/regional/translate/translate'; return; } /** * String deletion confirmation page. */ function locale_translate_delete_page($lid) { if ($source = db_query('SELECT lid, source FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject()) { return drupal_get_form('locale_translate_delete_form', $source); } else { return drupal_not_found(); } } /** * User interface for the string deletion confirmation screen. * * @ingroup forms */ function locale_translate_delete_form($form, &$form_state, $source) { $form['lid'] = array('#type' => 'value', '#value' => $source->lid); return confirm_form($form, t('Are you sure you want to delete the string "%source"?', array('%source' => $source->source)), 'admin/config/regional/translate/translate', t('Deleting the string will remove all translations of this string in all languages. This action cannot be undone.'), t('Delete'), t('Cancel')); } /** * Process string deletion submissions. */ function locale_translate_delete_form_submit($form, &$form_state) { db_delete('locales_source') ->condition('lid', $form_state['values']['lid']) ->execute(); db_delete('locales_target') ->condition('lid', $form_state['values']['lid']) ->execute(); // Force JavaScript translation file recreation for all languages. _locale_invalidate_js(); cache()->deletePrefix('locale:'); drupal_set_message(t('The string has been removed.')); $form_state['redirect'] = 'admin/config/regional/translate/translate'; }