diff --git a/includes/locale.inc b/includes/locale.inc index 6154cf3c355ce9e9c2e53b2761089ed86c846746..d0b11f83bf7d1dbc866aa517a39a8fa71f385afc 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -1688,129 +1688,6 @@ function _locale_export_remove_plural($entry) { * @} End of "locale-api-import-export" */ -/** - * @defgroup locale-api-seek Translation search API - * @{ - * Functions to search in translation files. - * - * These functions provide the functionality to search for specific - * translations. - */ - -/** - * Perform a string search and display results in a table - */ -function _locale_translate_seek() { - $output = ''; - - // We have at least one criterion to match - if (!($query = _locale_translate_seek_query())) { - $query = array( - 'translation' => 'all', - 'group' => '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', 'textgroup')); - $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'] != 'en') { - // Only search in translations if the language is not forced to English. - $condition->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); - } - $sql_query->condition($condition); - break; - } - - $limit_language = NULL; - if ($query['language'] != 'en' && $query['language'] != 'all') { - $sql_query->condition('language', $query['language']); - $limit_language = $query['language']; - } - - // Add a condition on the text group. - if (!empty($query['group']) && $query['group'] != 'all') { - $sql_query->condition('s.textgroup', $query['group']); - } - - $sql_query = $sql_query->extend('PagerDefault')->limit(50); - $locales = $sql_query->execute(); - - $groups = module_invoke_all('locale', 'groups'); - $header = array(t('Text group'), 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( - 'group' => $locale->textgroup, - '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( - $groups[$string['group']], - array('data' => check_plain(truncate_utf8($string['source'], 150, FALSE, TRUE)) . '<br /><small>' . $string['location'] . '</small>'), - $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; -} - -/** - * 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', 'group'); - foreach ($fields as $field) { - if (isset($_SESSION['locale_translation_filter'][$field])) { - $query[$field] = $_SESSION['locale_translation_filter'][$field]; - } - } - } - return $query; -} - /** * Force the JavaScript translation file(s) to be refreshed. * @@ -1974,28 +1851,6 @@ function _locale_rebuild_js($langcode = NULL) { } } -/** - * 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(); - unset($languages['en']); - $output = ''; - foreach ($languages as $langcode => $language) { - if (!$limit_language || $limit_language == $langcode) { - $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "<em class=\"locale-untranslated\">$langcode</em> "; - } - } - - return $output; -} -/** - * @} End of "locale-api-seek" - */ - /** * @defgroup locale-api-predefined List of predefined languages * @{ diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index b0a77c5b6ed1d619e239288e1c66831e993565d1..ad7a583a74a7d2a5102a9a21cda40a2569c71b9d 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -743,186 +743,6 @@ function locale_language_providers_session_form($form, &$form_state) { * @} End of "locale-language-administration" */ -/** - * @defgroup locale-translate-administration-screens Translation administration screens - * @{ - * Screens for translation administration. - * - * These functions provide various screens as administration interface - * to import, export and view translations. - */ - -/** - * Overview screen for translations. - */ -function locale_translate_overview_screen() { - drupal_static_reset('language_list'); - $languages = language_list('language'); - $groups = module_invoke_all('locale', 'groups'); - - // Build headers with all groups in order. - $headers = array_merge(array(t('Language')), array_values($groups)); - - // Collect summaries of all source strings in all groups. - $sums = db_query("SELECT COUNT(*) AS strings, textgroup FROM {locales_source} GROUP BY textgroup"); - $groupsums = array(); - foreach ($sums as $group) { - $groupsums[$group->textgroup] = $group->strings; - } - - // Set up overview table with default values, ensuring common order for values. - $rows = array(); - foreach ($languages as $langcode => $language) { - $rows[$langcode] = array('name' => ($langcode == 'en' ? t('English (built-in)') : t($language->name))); - foreach ($groups as $group => $name) { - $rows[$langcode][$group] = ($langcode == 'en' ? t('n/a') : '0/' . (isset($groupsums[$group]) ? $groupsums[$group] : 0) . ' (0%)'); - } - } - - // Languages with at least one record in the locale table. - $translations = db_query("SELECT COUNT(*) AS translation, t.language, s.textgroup FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid GROUP BY textgroup, language"); - foreach ($translations as $data) { - $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation > 0) ? round(($data->translation/$groupsums[$data->textgroup]) * 100.0, 2) : 0; - $rows[$data->language][$data->textgroup] = $data->translation . '/' . $groupsums[$data->textgroup] . " ($ratio%)"; - } - - return theme('table', array('header' => $headers, 'rows' => $rows)); -} - -/** - * String search screen. - */ -function locale_translate_seek_screen() { - // Add CSS. - drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); - - $elements = drupal_get_form('locale_translation_filter_form'); - $output = drupal_render($elements); - $output .= _locale_translate_seek(); - return $output; -} - -/** - * 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 = locale_language_list('name'); - unset($languages['en']); - - $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'), 'en' => t('English (provided by Drupal)')), $languages), - ); - - $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')), - ); - - $groups = module_invoke_all('locale', 'groups'); - $filters['group'] = array( - 'title' => t('Limit search to'), - 'options' => array_merge(array('all' => t('All text groups')), $groups), - ); - - 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']) && empty($form_state['values']['group'])) { - 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 the translation import screen. @@ -1091,191 +911,6 @@ function locale_translate_export_po_form_submit($form, &$form_state) { } _locale_export_po($language, _locale_export_po_generate($language, _locale_export_get_strings($language, $form_state['values']['group']))); } -/** - * @} End of "locale-translate-administration-screens" - */ - -/** - * @defgroup locale-translate-edit-delete Translation editing/deletion interface - * @{ - * Edit and delete translation strings. - * - * These functions provide the user interface to edit and delete - * translation strings. - */ - -/** - * User interface for string editing. - */ -function locale_translate_edit_form($form, &$form_state, $lid) { - // Fetch source string, if possible. - $source = db_query('SELECT source, context, textgroup, 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['textgroup'] = array( - '#type' => 'value', - '#value' => $source->textgroup, - ); - $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(); - $default = language_default(); - // We don't need the default language value, that value is in $source. - $omit = $source->textgroup == 'default' ? 'en' : $default->language; - unset($languages[($omit)]); - $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' => t($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 AND language <> :omit", array(':lid' => $lid, ':omit' => $omit)); - 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) { - // Locale string check is needed for default textgroup only. - $safe_check_needed = $form_state['values']['textgroup'] == 'default'; - foreach ($form_state['values']['translations'] as $key => $value) { - if ($safe_check_needed && !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_clear_all('locale:', 'cache', TRUE); - - $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. - */ -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_clear_all('locale:', 'cache', TRUE); - drupal_set_message(t('The string has been removed.')); - $form_state['redirect'] = 'admin/config/regional/translate/translate'; -} -/** - * @} End of "locale-translate-edit-delete" - */ /** * Returns HTML for a locale date format form. diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 07884614a51e6b2d84ae1c91b842bbb83df46200..871048be0d6d70338893ce1a5fed775b2dc7f25f 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -140,7 +140,7 @@ function locale_menu() { 'description' => 'Translate the built in interface and optionally other text.', 'page callback' => 'locale_translate_overview_screen', 'access arguments' => array('translate interface'), - 'file' => 'locale.admin.inc', + 'file' => 'locale.pages.inc', 'weight' => -5, ); $items['admin/config/regional/translate/overview'] = array( @@ -154,7 +154,7 @@ function locale_menu() { 'type' => MENU_LOCAL_TASK, 'page callback' => 'locale_translate_seek_screen', // search results and form concatenated 'access arguments' => array('translate interface'), - 'file' => 'locale.admin.inc', + 'file' => 'locale.pages.inc', ); $items['admin/config/regional/translate/import'] = array( 'title' => 'Import', @@ -178,14 +178,14 @@ function locale_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('locale_translate_edit_form', 5), 'access arguments' => array('translate interface'), - 'file' => 'locale.admin.inc', + 'file' => 'locale.pages.inc', ); $items['admin/config/regional/translate/delete/%'] = array( 'title' => 'Delete string', 'page callback' => 'locale_translate_delete_page', 'page arguments' => array(5), 'access arguments' => array('translate interface'), - 'file' => 'locale.admin.inc', + 'file' => 'locale.pages.inc', ); // Localize date formats. diff --git a/modules/locale/locale.pages.inc b/modules/locale/locale.pages.inc new file mode 100644 index 0000000000000000000000000000000000000000..b4ccb6b8116ea44d50d0bbd69a90570eccbec64a --- /dev/null +++ b/modules/locale/locale.pages.inc @@ -0,0 +1,500 @@ +<?php + +/** + * @defgroup locale-translate-administration-screens Translation administration screens + * @{ + * Screens for translation administration. + * + * These functions provide various screens to view, edit and delete translations. + */ + +/** + * Overview screen for translations. + */ +function locale_translate_overview_screen() { + drupal_static_reset('language_list'); + $languages = language_list('language'); + $groups = module_invoke_all('locale', 'groups'); + + // Build headers with all groups in order. + $headers = array_merge(array(t('Language')), array_values($groups)); + + // Collect summaries of all source strings in all groups. + $sums = db_query("SELECT COUNT(*) AS strings, textgroup FROM {locales_source} GROUP BY textgroup"); + $groupsums = array(); + foreach ($sums as $group) { + $groupsums[$group->textgroup] = $group->strings; + } + + // Set up overview table with default values, ensuring common order for values. + $rows = array(); + foreach ($languages as $langcode => $language) { + $rows[$langcode] = array('name' => ($langcode == 'en' ? t('English (built-in)') : t($language->name))); + foreach ($groups as $group => $name) { + $rows[$langcode][$group] = ($langcode == 'en' ? t('n/a') : '0/' . (isset($groupsums[$group]) ? $groupsums[$group] : 0) . ' (0%)'); + } + } + + // Languages with at least one record in the locale table. + $translations = db_query("SELECT COUNT(*) AS translation, t.language, s.textgroup FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid GROUP BY textgroup, language"); + foreach ($translations as $data) { + $ratio = (!empty($groupsums[$data->textgroup]) && $data->translation > 0) ? round(($data->translation/$groupsums[$data->textgroup]) * 100.0, 2) : 0; + $rows[$data->language][$data->textgroup] = $data->translation . '/' . $groupsums[$data->textgroup] . " ($ratio%)"; + } + + return theme('table', array('header' => $headers, 'rows' => $rows)); +} + +/** + * String search screen. + */ +function locale_translate_seek_screen() { + // Add CSS. + drupal_add_css(drupal_get_path('module', 'locale') . '/locale.css'); + + $elements = drupal_get_form('locale_translation_filter_form'); + $output = drupal_render($elements); + $output .= _locale_translate_seek(); + return $output; +} + +/** + * Perform a string search and display results in a table + */ +function _locale_translate_seek() { + $output = ''; + + // We have at least one criterion to match + if (!($query = _locale_translate_seek_query())) { + $query = array( + 'translation' => 'all', + 'group' => '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', 'textgroup')); + $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'] != 'en') { + // Only search in translations if the language is not forced to English. + $condition->condition('t.translation', '%' . db_like($query['string']) . '%', 'LIKE'); + } + $sql_query->condition($condition); + break; + } + + $limit_language = NULL; + if ($query['language'] != 'en' && $query['language'] != 'all') { + $sql_query->condition('language', $query['language']); + $limit_language = $query['language']; + } + + // Add a condition on the text group. + if (!empty($query['group']) && $query['group'] != 'all') { + $sql_query->condition('s.textgroup', $query['group']); + } + + $sql_query = $sql_query->extend('PagerDefault')->limit(50); + $locales = $sql_query->execute(); + + $groups = module_invoke_all('locale', 'groups'); + $header = array(t('Text group'), 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( + 'group' => $locale->textgroup, + '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( + $groups[$string['group']], + array('data' => check_plain(truncate_utf8($string['source'], 150, FALSE, TRUE)) . '<br /><small>' . $string['location'] . '</small>'), + $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(); + unset($languages['en']); + $output = ''; + foreach ($languages as $langcode => $language) { + if (!$limit_language || $limit_language == $langcode) { + $output .= (!empty($translation[$langcode])) ? $langcode . ' ' : "<em class=\"locale-untranslated\">$langcode</em> "; + } + } + + 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', 'group'); + 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 = locale_language_list('name'); + unset($languages['en']); + + $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'), 'en' => t('English (provided by Drupal)')), $languages), + ); + + $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')), + ); + + $groups = module_invoke_all('locale', 'groups'); + $filters['group'] = array( + 'title' => t('Limit search to'), + 'options' => array_merge(array('all' => t('All text groups')), $groups), + ); + + 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']) && empty($form_state['values']['group'])) { + 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'; +} + +/** + * @} End of "locale-translate-administration-screens" + */ + +/** + * @defgroup locale-translate-edit-delete Translation editing/deletion interface + * @{ + * Edit and delete translation strings. + * + * These functions provide the user interface to edit and delete + * translation strings. + */ + +/** + * User interface for string editing. + */ +function locale_translate_edit_form($form, &$form_state, $lid) { + // Fetch source string, if possible. + $source = db_query('SELECT source, context, textgroup, 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['textgroup'] = array( + '#type' => 'value', + '#value' => $source->textgroup, + ); + $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(); + $default = language_default(); + // We don't need the default language value, that value is in $source. + $omit = $source->textgroup == 'default' ? 'en' : $default->language; + unset($languages[($omit)]); + $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' => t($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 AND language <> :omit", array(':lid' => $lid, ':omit' => $omit)); + 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) { + // Locale string check is needed for default textgroup only. + $safe_check_needed = $form_state['values']['textgroup'] == 'default'; + foreach ($form_state['values']['translations'] as $key => $value) { + if ($safe_check_needed && !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_clear_all('locale:', 'cache', TRUE); + + $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. + */ +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_clear_all('locale:', 'cache', TRUE); + drupal_set_message(t('The string has been removed.')); + $form_state['redirect'] = 'admin/config/regional/translate/translate'; +} +/** + * @} End of "locale-translate-edit-delete" + */