locale.pages.inc 15.3 KB
Newer Older
1 2 3
<?php

/**
4 5
 * @file
 * Interface translation summary, editing and deletion user interfaces.
6 7
 */

8 9
use Drupal\locale\SourceString;
use Drupal\locale\TranslationString;
10 11
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

12
/**
13 14 15
 * Page callback: Shows the string search screen.
 *
 * @see locale_menu()
16
 */
17 18 19 20 21
function locale_translate_page() {
  return array(
    'filter' => drupal_get_form('locale_translate_filter_form'),
    'form' => drupal_get_form('locale_translate_edit_form'),
  );
22 23 24
}

/**
25 26 27 28
 * Builds a string search query and returns an array of string objects.
 *
 * @return array
 *   Array of Drupal\locale\TranslationString objects.
29
 */
30
function locale_translate_filter_load_strings() {
31
  $filter_values = locale_translate_filter_values();
32

33 34
  // Language is sanitized to be one of the possible options in
  // locale_translate_filter_values().
35 36
  $conditions = array('language' => $filter_values['langcode']);
  $options = array('pager limit' => 30, 'translated' => TRUE, 'untranslated' => TRUE);
37

38
  // Add translation status conditions and options.
39
  switch ($filter_values['translation']) {
40
    case 'translated':
41
      $conditions['translated'] = TRUE;
42
      if ($filter_values['customized'] != 'all') {
43
        $conditions['customized'] = $filter_values['customized'];
44
      }
45
      break;
46

47
    case 'untranslated':
48
      $conditions['translated'] = FALSE;
49 50 51 52
      break;

  }

53 54 55 56 57 58 59 60
  if (!empty($filter_values['string'])) {
    $options['filters']['source'] = $filter_values['string'];
    if ($options['translated']) {
      $options['filters']['translation'] = $filter_values['string'];
    }
  }

  return locale_storage()->getTranslations($conditions, $options);
61 62 63
}

/**
64
 * Build array out of search criteria specified in request variables.
65
 */
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
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];
        }
87 88 89
      }
    }
  }
90
  return $filter_values;
91 92 93 94 95
}

/**
 * List locale translation filters that can be applied.
 */
96
function locale_translate_filters() {
97 98
  $filters = array();

99
  // Get all languages, except English.
100
  drupal_static_reset('language_list');
101
  $languages = language_list();
102 103 104 105 106
  $language_options = array();
  foreach ($languages as $langcode => $language) {
    if ($langcode != 'en' || locale_translate_english()) {
      $language_options[$langcode] = $language->name;
    }
107
  }
108

109
  // Pick the current interface language code for the filter.
110
  $default_langcode = language(LANGUAGE_TYPE_INTERFACE)->langcode;
111 112 113 114 115
  if (!isset($language_options[$default_langcode])) {
    $available_langcodes = array_keys($language_options);
    $default_langcode = array_shift($available_langcodes);
  }

116 117 118
  $filters['string'] = array(
    'title' => t('String contains'),
    'description' => t('Leave blank to show all strings. The search is case sensitive.'),
119
    'default' => '',
120 121
  );

122 123 124 125
  $filters['langcode'] = array(
    'title' => t('Translation language'),
    'options' => $language_options,
    'default' => $default_langcode,
126 127 128 129
  );

  $filters['translation'] = array(
    'title' => t('Search in'),
130 131 132
    'options' => array(
      'all' => t('Both translated and untranslated strings'),
      'translated' => t('Only translated strings'),
133
      'untranslated' => t('Only untranslated strings'),
134
    ),
135
    'default' => 'all',
136 137 138 139 140 141 142 143 144 145 146 147
  );

  $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'),
148
      ),
149
    ),
150
    'default' => 'all',
151 152 153 154 155 156 157 158 159 160
  );

  return $filters;
}

/**
 * Return form for locale translation filters.
 *
 * @ingroup forms
 */
161 162 163 164 165 166 167
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',
  );
168 169 170 171 172 173 174 175 176 177 178

  $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(
179
        '#type' => 'search',
180 181
        '#title' => $filter['title'],
        '#description' => $filter['description'],
182
        '#default_value' => $filter_values[$key],
183 184 185
      );
    }
    else {
186
      $empty_option = isset($filter['options'][$filter['default']]) ? $filter['options'][$filter['default']] : '<none>';
187 188 189
      $form['filters']['status'][$key] = array(
        '#title' => $filter['title'],
        '#type' => 'select',
190 191
        '#empty_value' => $filter['default'],
        '#empty_option' => $empty_option,
192 193
        '#size' => 0,
        '#options' => $filter['options'],
194
        '#default_value' => $filter_values[$key],
195
      );
196 197 198
      if (isset($filter['states'])) {
        $form['filters']['status'][$key]['#states'] = $filter['states'];
      }
199 200 201 202 203 204 205 206 207 208 209
    }
  }

  $form['filters']['actions'] = array(
    '#type' => 'actions',
    '#attributes' => array('class' => array('container-inline')),
  );
  $form['filters']['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Filter'),
  );
210
  if (!empty($_SESSION['locale_translate_filter'])) {
211 212
    $form['filters']['actions']['reset'] = array(
      '#type' => 'submit',
213
      '#value' => t('Reset'),
214 215 216 217 218 219 220 221 222
    );
  }

  return $form;
}

/**
 * Process result from locale translation filter form.
 */
223
function locale_translate_filter_form_submit($form, &$form_state) {
224
  $op = $form_state['values']['op'];
225
  $filters = locale_translate_filters();
226 227 228 229
  switch ($op) {
    case t('Filter'):
      foreach ($filters as $name => $filter) {
        if (isset($form_state['values'][$name])) {
230
          $_SESSION['locale_translate_filter'][$name] = $form_state['values'][$name];
231 232 233
        }
      }
      break;
234

235
    case t('Reset'):
236
      $_SESSION['locale_translate_filter'] = array();
237
      break;
238

239 240 241 242 243 244
  }

  $form_state['redirect'] = 'admin/config/regional/translate/translate';
}

/**
245 246 247 248 249
 * Form constructor for the string editing form.
 *
 * @see locale_menu()
 * @see locale_translate_edit_form_validate()
 * @see locale_translate_edit_form_submit()
250 251
 *
 * @ingroup forms
252
 */
253 254 255 256 257 258 259 260 261 262 263 264
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',
265
  );
266
  $form['#attached']['library'][] = array('locale', 'drupal.locale.admin');
267

268 269 270 271
  $form['langcode'] = array(
    '#type' => 'value',
    '#value' => $filter_values['langcode'],
  );
272

273 274 275 276 277
  $form['strings'] = array(
    '#type' => 'item',
    '#tree' => TRUE,
    '#language' => $langname,
    '#theme' => 'locale_translate_edit_form_strings',
278 279
  );

280
  if (isset($langcode)) {
281
    $strings = locale_translate_filter_load_strings();
282 283 284 285

    $plural_formulas = variable_get('locale_translation_plurals', array());

    foreach ($strings as $string) {
286 287
      // Cast into source string, will do for our purposes.
      $source = new SourceString($string);
288
      // Split source to work with plural values.
289 290
      $source_array = $source->getPlurals();
      $translation_array = $string->getPlurals();
291 292 293 294 295 296 297 298 299 300 301 302
      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]),
        );
303 304
      }
      else {
305 306 307 308 309 310 311
        // 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',
312
          '#title' => t('Singular form'),
313
          '#markup' => check_plain($source_array[0]),
314
        );
315 316
        $form['strings'][$string->lid]['original_plural'] = array(
          '#type' => 'item',
317
          '#title' => t('Plural form'),
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
          '#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',
339
          '#rows' => $rows,
340
          '#default_value' => $translation_array[0],
341 342
        );
      }
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
      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] : '',
          );
        }
      }
372
    }
373 374 375
    if (count(element_children($form['strings']))) {
      $form['actions'] = array('#type' => 'actions');
      $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save translations'));
376
    }
377 378 379 380 381
  }
  return $form;
}

/**
382 383 384
 * Form validation handler for locale_translate_edit_form().
 *
 * @see locale_translate_edit_form_submit()
385 386
 */
function locale_translate_edit_form_validate($form, &$form_state) {
387 388 389
  $langcode = $form_state['values']['langcode'];
  foreach ($form_state['values']['strings'] as $lid => $translations) {
    foreach ($translations['translations'] as $key => $value) {
390
      if (!locale_string_is_safe($value)) {
391
        form_set_error("strings][$lid][translations][$key", t('The submitted string contains disallowed HTML: %string', array('%string' => $value)));
392 393 394
        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);
      }
395 396 397 398 399
    }
  }
}

/**
400 401 402
 * Form submission handler for locale_translate_edit_form().
 *
 * @see locale_translate_edit_form_validate()
403 404
 */
function locale_translate_edit_form_submit($form, &$form_state) {
405 406
  $langcode = $form_state['values']['langcode'];
  foreach ($form_state['values']['strings'] as $lid => $translations) {
407 408
    // Get target string, that may be NULL if there's no translation.
    $target = locale_storage()->findTranslation(array('language' => $langcode, 'lid' => $lid));
409 410
    // No translation when all strings are empty.
    $has_translation = FALSE;
411
    foreach ($translations['translations'] as $string) {
412 413 414 415 416 417
      if (!empty($string)) {
        $has_translation = TRUE;
        break;
      }
    }
    if ($has_translation) {
418
      // Only update or insert if we have a value to use.
419 420 421 422
      $target = $target && !$target->isNew() ? $target : locale_storage()->createTranslation(array('lid' => $lid, 'language' => $langcode));
      $target->setPlurals($translations['translations'])
        ->setCustomized()
        ->save();
423
    }
424
    elseif ($target) {
425
      // Empty translation entered: remove existing entry from database.
426
      $target->delete();
427 428 429
    }
  }

430 431 432 433 434 435
  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'])));
  }
436

437 438
  // Force JavaScript translation file recreation for this language.
  _locale_invalidate_js($langcode);
439
  // Clear locale cache.
440
  cache()->invalidateTags(array('locale' => TRUE));
441 442 443
}

/**
444
 * Default theme function for translatione edit form.
445
 */
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
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') . ':&nbsp;' . $string['context']['#value'] . '</small>';
    $rows[] = array(
      array('data' => $source),
      array('data' => $string['translations']),
    );
467
  }
468 469 470 471 472 473 474 475
  $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;
476
}