translateFilterValues(); $langcode = $filter_values['langcode']; $this->languageManager->reset(); $languages = language_list(); $langname = isset($langcode) ? $languages[$langcode]->getName() : "- None -"; $form['#attached']['library'][] = 'locale/drupal.locale.admin'; $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 = $this->translateFilterLoadStrings(); $plural_formulas = $this->state->get('locale.translation.plurals') ?: array(); foreach ($strings as $string) { // Cast into source string, will do for our purposes. $source = new SourceString($string); // Split source to work with plural values. $source_array = $source->getPlurals(); $translation_array = $string->getPlurals(); 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' => $this->t('Source string (@language)', array('@language' => $this->t('Built-in English'))), '#title_display' => 'invisible', '#markup' => '' . String::checkPlain($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' => $this->t('Singular form'), '#markup' => '' . String::checkPlain($source_array[0]) . '', '#prefix' => '' . $this->t('Source string (@language)', array('@language' => $this->t('Built-in English'))) . '', ); $form['strings'][$string->lid]['original_plural'] = array( '#type' => 'item', '#title' => $this->t('Plural form'), '#markup' => '' . String::checkPlain($source_array[1]) . '', ); } if (!empty($string->context)) { $form['strings'][$string->lid]['context'] = array( '#type' => 'value', '#value' => '' . String::checkPlain($string->context) . '', ); } // 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' => $this->t('Translated string (@language)', array('@language' => $langname)), '#title_display' => 'invisible', '#rows' => $rows, '#default_value' => $translation_array[0], '#attributes' => array('lang' => $langcode), ); } 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 ? $this->t('Singular form') : format_plural($i, 'First plural form', '@count. plural form')), '#rows' => $rows, '#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '', '#attributes' => array('lang' => $langcode), '#prefix' => $i == 0 ? ('' . $this->t('Translated string (@language)', array('@language' => $langname)) . '') : '', ); } } else { // Fallback for unknown number of plurals. $form['strings'][$string->lid]['translations'][0] = array( '#type' => 'textarea', '#title' => $this->t('Singular form'), '#rows' => $rows, '#default_value' => $translation_array[0], '#attributes' => array('lang' => $langcode), '#prefix' => '' . $this->t('Translated string (@language)', array('@language' => $langname)) . '', ); $form['strings'][$string->lid]['translations'][1] = array( '#type' => 'textarea', '#title' => $this->t('Plural form'), '#rows' => $rows, '#default_value' => isset($translation_array[1]) ? $translation_array[1] : '', '#attributes' => array('lang' => $langcode), ); } } } if (count(Element::children($form['strings']))) { $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => $this->t('Save translations'), ); } } return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { $langcode = $form_state->getValue('langcode'); foreach ($form_state->getValue('strings') as $lid => $translations) { foreach ($translations['translations'] as $key => $value) { if (!locale_string_is_safe($value)) { $form_state->setErrorByName("strings][$lid][translations][$key", $this->t('The submitted string contains disallowed HTML: %string', array('%string' => $value))); $form_state->setErrorByName("translations][$langcode][$key", $this->t('The submitted string contains disallowed HTML: %string', array('%string' => $value))); $this->logger('locale')->warning('Attempted submission of a translation string with disallowed HTML: %string', array('%string' => $value)); } } } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $langcode = $form_state->getValue('langcode'); $updated = array(); // Preload all translations for strings in the form. $lids = array_keys($form_state->getValue('strings')); $existing_translation_objects = array(); foreach ($this->localeStorage->getTranslations(array('lid' => $lids, 'language' => $langcode, 'translated' => TRUE)) as $existing_translation_object) { $existing_translation_objects[$existing_translation_object->lid] = $existing_translation_object; } foreach ($form_state->getValue('strings') as $lid => $new_translation) { $existing_translation = isset($existing_translation_objects[$lid]); // Plural translations are saved in a delimited string. To be able to // compare the new strings with the existing strings a string in the same // format is created. $new_translation_string_delimited = implode(LOCALE_PLURAL_DELIMITER, $new_translation['translations']); // Generate an imploded string without delimiter, to be able to run // empty() on it. $new_translation_string = implode('', $new_translation['translations']); $is_changed = FALSE; if ($existing_translation && $existing_translation_objects[$lid]->translation != $new_translation_string_delimited) { // If there is an existing translation in the DB and the new translation // is not the same as the existing one. $is_changed = TRUE; } elseif (!$existing_translation && !empty($new_translation_string)) { // Newly entered translation. $is_changed = TRUE; } if ($is_changed) { // Only update or insert if we have a value to use. $target = isset($existing_translation_objects[$lid]) ? $existing_translation_objects[$lid] : $this->localeStorage->createTranslation(array('lid' => $lid, 'language' => $langcode)); $target->setPlurals($new_translation['translations']) ->setCustomized() ->save(); $updated[] = $target->getId(); } if (empty($new_translation_string) && isset($existing_translation_objects[$lid])) { // Empty new translation entered: remove existing entry from database. $existing_translation_objects[$lid]->delete(); $updated[] = $lid; } } drupal_set_message($this->t('The strings have been saved.')); // Keep the user on the current pager page. $page = $this->getRequest()->query->get('page'); if (isset($page)) { $form_state->setRedirect( 'locale.translate_page', array(), array('page' => $page) ); } if ($updated) { // Clear cache and force refresh of JavaScript translations. _locale_refresh_translations(array($langcode), $updated); _locale_refresh_configuration(array($langcode), $updated); } } }