486 lines
16 KiB
PHP
486 lines
16 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Interface translation summary, editing and deletion user interfaces.
|
|
*/
|
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
|
|
/**
|
|
* String search screen.
|
|
*/
|
|
function locale_translate_page() {
|
|
return array(
|
|
'filter' => drupal_get_form('locale_translate_filter_form'),
|
|
'form' => drupal_get_form('locale_translate_edit_form'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Build a string search query.
|
|
*/
|
|
function locale_translate_query() {
|
|
$filter_values = locale_translate_filter_values();
|
|
|
|
$sql_query = db_select('locales_source', 's');
|
|
// Language is sanitized to be one of the possible options in
|
|
// locale_translate_filter_values().
|
|
$sql_query->leftJoin('locales_target', 't', "t.lid = s.lid AND t.language = :langcode", array(':langcode' => $filter_values['langcode']));
|
|
$sql_query->fields('s', array('source', 'location', 'context', 'lid'));
|
|
$sql_query->fields('t', array('translation', 'language', 'customized'));
|
|
|
|
if (!empty($filter_values['string'])) {
|
|
$sql_query->condition(db_or()
|
|
->condition('s.source', '%' . db_like($filter_values['string']) . '%', 'LIKE')
|
|
->condition('t.translation', '%' . db_like($filter_values['string']) . '%', 'LIKE')
|
|
);
|
|
}
|
|
|
|
// Add translation status conditions.
|
|
switch ($filter_values['translation']) {
|
|
case 'translated':
|
|
$sql_query->isNotNull('t.translation');
|
|
if ($filter_values['customized'] != 'all') {
|
|
$sql_query->condition('t.customized', $filter_values['customized']);
|
|
}
|
|
break;
|
|
|
|
case 'untranslated':
|
|
$sql_query->isNull('t.translation');
|
|
break;
|
|
|
|
}
|
|
|
|
$sql_query = $sql_query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit(30);
|
|
return $sql_query->execute();
|
|
}
|
|
|
|
/**
|
|
* Build array out of search criteria specified in request variables.
|
|
*/
|
|
function locale_translate_filter_values() {
|
|
$filter_values = &drupal_static(__FUNCTION__);
|
|
if (!isset($filter_values)) {
|
|
$filter_values = array();
|
|
$filters = locale_translate_filters();
|
|
foreach ($filters as $key => $filter) {
|
|
$filter_values[$key] = $filter['default'];
|
|
// Let the filter defaults be overwritten by parameters in the URL.
|
|
if (isset($_GET[$key])) {
|
|
// Only allow this value if it was among the options, or
|
|
// if there were no fixed options to filter for.
|
|
if (!isset($filter['options']) || isset($filter['options'][$_GET[$key]])) {
|
|
$filter_values[$key] = $_GET[$key];
|
|
}
|
|
}
|
|
elseif (isset($_SESSION['locale_translate_filter'][$key])) {
|
|
// Only allow this value if it was among the options, or
|
|
// if there were no fixed options to filter for.
|
|
if (!isset($filter['options']) || isset($filter['options'][$_SESSION['locale_translate_filter'][$key]])) {
|
|
$filter_values[$key] = $_SESSION['locale_translate_filter'][$key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $filter_values;
|
|
}
|
|
|
|
/**
|
|
* List locale translation filters that can be applied.
|
|
*/
|
|
function locale_translate_filters() {
|
|
$filters = array();
|
|
|
|
// Get all languages, except English.
|
|
drupal_static_reset('language_list');
|
|
$languages = language_list();
|
|
$language_options = array();
|
|
foreach ($languages as $langcode => $language) {
|
|
if ($langcode != 'en' || locale_translate_english()) {
|
|
$language_options[$langcode] = $language->name;
|
|
}
|
|
}
|
|
|
|
// Pick the current interface language code for the filter.
|
|
$default_langcode = drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode;
|
|
if (!isset($language_options[$default_langcode])) {
|
|
$available_langcodes = array_keys($language_options);
|
|
$default_langcode = array_shift($available_langcodes);
|
|
}
|
|
|
|
$filters['string'] = array(
|
|
'title' => t('String contains'),
|
|
'description' => t('Leave blank to show all strings. The search is case sensitive.'),
|
|
'default' => '',
|
|
);
|
|
|
|
$filters['langcode'] = array(
|
|
'title' => t('Translation language'),
|
|
'options' => $language_options,
|
|
'default' => $default_langcode,
|
|
);
|
|
|
|
$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'),
|
|
),
|
|
'default' => 'all',
|
|
);
|
|
|
|
$filters['customized'] = array(
|
|
'title' => t('Translation type'),
|
|
'options' => array(
|
|
'all' => t('All'),
|
|
LOCALE_NOT_CUSTOMIZED => t('Non-customized translation'),
|
|
LOCALE_CUSTOMIZED => t('Customized translation'),
|
|
),
|
|
'states' => array(
|
|
'visible' => array(
|
|
':input[name=translation]' => array('value' => 'translated'),
|
|
),
|
|
),
|
|
'default' => 'all',
|
|
);
|
|
|
|
return $filters;
|
|
}
|
|
|
|
/**
|
|
* Return form for locale translation filters.
|
|
*
|
|
* @ingroup forms
|
|
*/
|
|
function locale_translate_filter_form($form, &$form_state) {
|
|
$filters = locale_translate_filters();
|
|
$filter_values = locale_translate_filter_values();
|
|
|
|
$form['#attached']['css'] = array(
|
|
drupal_get_path('module', 'locale') . '/locale.admin.css',
|
|
);
|
|
|
|
$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' => 'search',
|
|
'#title' => $filter['title'],
|
|
'#description' => $filter['description'],
|
|
'#default_value' => $filter_values[$key],
|
|
);
|
|
}
|
|
else {
|
|
$empty_option = isset($filter['options'][$filter['default']]) ? $filter['options'][$filter['default']] : '<none>';
|
|
$form['filters']['status'][$key] = array(
|
|
'#title' => $filter['title'],
|
|
'#type' => 'select',
|
|
'#empty_value' => $filter['default'],
|
|
'#empty_option' => $empty_option,
|
|
'#size' => 0,
|
|
'#options' => $filter['options'],
|
|
'#default_value' => $filter_values[$key],
|
|
);
|
|
if (isset($filter['states'])) {
|
|
$form['filters']['status'][$key]['#states'] = $filter['states'];
|
|
}
|
|
}
|
|
}
|
|
|
|
$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_translate_filter'])) {
|
|
$form['filters']['actions']['reset'] = array(
|
|
'#type' => 'submit',
|
|
'#value' => t('Reset'),
|
|
);
|
|
}
|
|
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Process result from locale translation filter form.
|
|
*/
|
|
function locale_translate_filter_form_submit($form, &$form_state) {
|
|
$op = $form_state['values']['op'];
|
|
$filters = locale_translate_filters();
|
|
switch ($op) {
|
|
case t('Filter'):
|
|
foreach ($filters as $name => $filter) {
|
|
if (isset($form_state['values'][$name])) {
|
|
$_SESSION['locale_translate_filter'][$name] = $form_state['values'][$name];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case t('Reset'):
|
|
$_SESSION['locale_translate_filter'] = array();
|
|
break;
|
|
|
|
}
|
|
|
|
$form_state['redirect'] = 'admin/config/regional/translate/translate';
|
|
}
|
|
|
|
/**
|
|
* User interface for string editing as one table.
|
|
*
|
|
* @ingroup forms
|
|
*/
|
|
function locale_translate_edit_form($form, &$form_state) {
|
|
$filter_values = locale_translate_filter_values();
|
|
$langcode = $filter_values['langcode'];
|
|
|
|
drupal_static_reset('language_list');
|
|
$languages = language_list();
|
|
|
|
$langname = isset($langcode) ? $languages[$langcode]->name : "<none>";
|
|
|
|
$path = drupal_get_path('module', 'locale');
|
|
$form['#attached']['css'] = array(
|
|
$path . '/locale.admin.css',
|
|
);
|
|
$form['#attached']['js'] = array(
|
|
$path . '/locale.admin.js',
|
|
);
|
|
|
|
$form['langcode'] = array(
|
|
'#type' => 'value',
|
|
'#value' => $filter_values['langcode'],
|
|
);
|
|
|
|
$form['strings'] = array(
|
|
'#type' => 'item',
|
|
'#tree' => TRUE,
|
|
'#language' => $langname,
|
|
'#theme' => 'locale_translate_edit_form_strings',
|
|
);
|
|
|
|
if (isset($langcode)) {
|
|
$strings = locale_translate_query();
|
|
|
|
$plural_formulas = variable_get('locale_translation_plurals', array());
|
|
|
|
foreach ($strings as $string) {
|
|
// Split source to work with plural values.
|
|
$source_array = explode(LOCALE_PLURAL_DELIMITER, $string->source);
|
|
$translation_array = explode(LOCALE_PLURAL_DELIMITER, $string->translation);
|
|
if (count($source_array) == 1) {
|
|
// Add original string value and mark as non-plural.
|
|
$form['strings'][$string->lid]['plural'] = array(
|
|
'#type' => 'value',
|
|
'#value' => 0,
|
|
);
|
|
$form['strings'][$string->lid]['original'] = array(
|
|
'#type' => 'item',
|
|
'#title' => t('Source string'),
|
|
'#title_display' => 'invisible',
|
|
'#markup' => check_plain($source_array[0]),
|
|
);
|
|
}
|
|
else {
|
|
// Add original string value and mark as plural.
|
|
$form['strings'][$string->lid]['plural'] = array(
|
|
'#type' => 'value',
|
|
'#value' => 1,
|
|
);
|
|
$form['strings'][$string->lid]['original_singular'] = array(
|
|
'#type' => 'item',
|
|
'#title' => t('Singular form'),
|
|
'#markup' => check_plain($source_array[0]),
|
|
);
|
|
$form['strings'][$string->lid]['original_plural'] = array(
|
|
'#type' => 'item',
|
|
'#title' => t('Plural form'),
|
|
'#markup' => check_plain($source_array[1]),
|
|
);
|
|
}
|
|
if (!empty($string->context)) {
|
|
$form['strings'][$string->lid]['context'] = array(
|
|
'#type' => 'value',
|
|
'#value' => check_plain($string->context),
|
|
);
|
|
}
|
|
$form['strings'][$string->lid]['location'] = array(
|
|
'#type' => 'value',
|
|
'#value' => $string->location,
|
|
);
|
|
|
|
// Approximate the number of rows to use in the default textarea.
|
|
$rows = min(ceil(str_word_count($source_array[0]) / 12), 10);
|
|
if (empty($form['strings'][$string->lid]['plural']['#value'])) {
|
|
$form['strings'][$string->lid]['translations'][0] = array(
|
|
'#type' => 'textarea',
|
|
'#title' => t('Translated string'),
|
|
'#title_display' => 'invisible',
|
|
'#rows' => $rows,
|
|
'#default_value' => $translation_array[0],
|
|
);
|
|
}
|
|
else {
|
|
// Dealing with plural strings.
|
|
if (isset($plural_formulas[$langcode]['plurals']) && $plural_formulas[$langcode]['plurals'] > 2) {
|
|
// Add a textarea for each plural variant.
|
|
for ($i = 0; $i < $plural_formulas[$langcode]['plurals']; $i++) {
|
|
$form['strings'][$string->lid]['translations'][$i] = array(
|
|
'#type' => 'textarea',
|
|
'#title' => ($i == 0 ? t('Singular form') : format_plural($i, 'First plural form', '@count. plural form')),
|
|
'#rows' => $rows,
|
|
'#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '',
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
// Fallback for unknown number of plurals.
|
|
$form['strings'][$string->lid]['translations'][0] = array(
|
|
'#type' => 'textarea',
|
|
'#title' => t('Singular form'),
|
|
'#rows' => $rows,
|
|
'#default_value' => $translation_array[0],
|
|
);
|
|
$form['strings'][$string->lid]['translations'][1] = array(
|
|
'#type' => 'textarea',
|
|
'#title' => t('Plural form'),
|
|
'#rows' => $rows,
|
|
'#default_value' => isset($translation_array[1]) ? $translation_array[1] : '',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (count(element_children($form['strings']))) {
|
|
$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) {
|
|
$langcode = $form_state['values']['langcode'];
|
|
foreach ($form_state['values']['strings'] as $lid => $translations) {
|
|
foreach ($translations['translations'] as $key => $value) {
|
|
if (!locale_string_is_safe($value)) {
|
|
form_set_error("strings][$lid][translations][$key", t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
|
|
form_set_error("translations][$langcode][$key", 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.
|
|
*/
|
|
function locale_translate_edit_form_submit($form, &$form_state) {
|
|
$langcode = $form_state['values']['langcode'];
|
|
foreach ($form_state['values']['strings'] as $lid => $translations) {
|
|
// Serialize plural variants in one string by LOCALE_PLURAL_DELIMITER.
|
|
$translation_new = implode(LOCALE_PLURAL_DELIMITER, $translations['translations']);
|
|
$translation_old = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $langcode))->fetchField();
|
|
// No translation when all strings are empty.
|
|
$has_translation = FALSE;
|
|
foreach ($translations['translations'] as $string) {
|
|
if (!empty($string)) {
|
|
$has_translation = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if ($has_translation) {
|
|
// Only update or insert if we have a value to use.
|
|
if (!empty($translation_old) && $translation_old != $translation_new) {
|
|
db_update('locales_target')
|
|
->fields(array(
|
|
'translation' => $translation_new,
|
|
'customized' => LOCALE_CUSTOMIZED,
|
|
))
|
|
->condition('lid', $lid)
|
|
->condition('language', $langcode)
|
|
->execute();
|
|
}
|
|
if (empty($translation_old)) {
|
|
db_insert('locales_target')
|
|
->fields(array(
|
|
'lid' => $lid,
|
|
'translation' => $translation_new,
|
|
'language' => $langcode,
|
|
'customized' => LOCALE_CUSTOMIZED,
|
|
))
|
|
->execute();
|
|
}
|
|
}
|
|
elseif (!empty($translation_old)) {
|
|
// Empty translation entered: remove existing entry from database.
|
|
db_delete('locales_target')
|
|
->condition('lid', $lid)
|
|
->condition('language', $langcode)
|
|
->execute();
|
|
}
|
|
|
|
}
|
|
|
|
drupal_set_message(t('The strings have been saved.'));
|
|
|
|
// Keep the user on the current pager page.
|
|
if (isset($_GET['page'])) {
|
|
$form_state['redirect'] = array('admin/config/regional/translate', array('query' => array('page' => $_GET['page'])));
|
|
}
|
|
|
|
// Force JavaScript translation file recreation for this language.
|
|
_locale_invalidate_js($langcode);
|
|
// Clear locale cache.
|
|
cache()->deletePrefix('locale:');
|
|
}
|
|
|
|
/**
|
|
* Default theme function for translatione edit form.
|
|
*/
|
|
function theme_locale_translate_edit_form_strings($variables) {
|
|
$output = '';
|
|
$form = $variables['form'];
|
|
$header = array(
|
|
t('Source string'),
|
|
t('Translation for @language', array('@language' => $form['#language'])),
|
|
);
|
|
$rows = array();
|
|
foreach (element_children($form) as $lid) {
|
|
$string = $form[$lid];
|
|
if ($string['plural']['#value']) {
|
|
$source = drupal_render($string['original_singular']) . '<br />' . drupal_render($string['original_plural']);
|
|
}
|
|
else {
|
|
$source = drupal_render($string['original']);
|
|
}
|
|
$source .= empty($string['context']) ? '' : '<br /><small>' . t('In Context') . ': ' . $string['context']['#value'] . '</small>';
|
|
$rows[] = array(
|
|
array('data' => $source),
|
|
array('data' => $string['translations']),
|
|
);
|
|
}
|
|
$output .= theme('table', array(
|
|
'header' => $header,
|
|
'rows' => $rows,
|
|
'empty' => t('No strings available.'),
|
|
'attributes' => array('class' => array('locale-translate-edit-table')),
|
|
));
|
|
$output .= theme('pager');
|
|
return $output;
|
|
}
|