Commit 2a8ec5ea authored by Dries's avatar Dries
Browse files

- Patch #1222072 by Gábor Hojtsy: Split translation editing functionality out...

- Patch #1222072 by Gábor Hojtsy: Split translation editing functionality out of locale.admin.inc and locale.inc.
parent 9ecb9a89
......@@ -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
* @{
......
......@@ -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.
......
......@@ -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.
......
<?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%)');