Same name and namespace in other branches
  1. 8.9.x core/modules/locale/src/Form/TranslateEditForm.php \Drupal\locale\Form\TranslateEditForm
  2. 9 core/modules/locale/src/Form/TranslateEditForm.php \Drupal\locale\Form\TranslateEditForm

Defines a translation edit form.

@internal

Hierarchy

Expanded class hierarchy of TranslateEditForm

File

core/modules/locale/src/Form/TranslateEditForm.php, line 15

Namespace

Drupal\locale\Form
View source
class TranslateEditForm extends TranslateFormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'locale_translate_edit_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $filter_values = $this
      ->translateFilterValues();
    $langcode = $filter_values['langcode'];
    $this->languageManager
      ->reset();
    $languages = $this->languageManager
      ->getLanguages();
    $language_name = isset($langcode) ? $languages[$langcode]
      ->getName() : "- None -";
    $form['#attached']['library'][] = 'locale/drupal.locale.admin';
    $form['langcode'] = [
      '#type' => 'value',
      '#value' => $filter_values['langcode'],
    ];
    $form['strings'] = [
      '#type' => 'table',
      '#tree' => TRUE,
      '#language' => $language_name,
      '#header' => [
        $this
          ->t('Source string'),
        $this
          ->t('Translation for @language', [
          '@language' => $language_name,
        ]),
      ],
      '#empty' => $this
        ->t('No strings available.'),
      '#attributes' => [
        'class' => [
          'locale-translate-edit-table',
        ],
      ],
    ];
    if (isset($langcode)) {
      $strings = $this
        ->translateFilterLoadStrings();
      $plurals = $this
        ->getNumberOfPlurals($langcode);
      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.
          $plural = FALSE;
          $form['strings'][$string->lid]['original'] = [
            '#type' => 'item',
            '#title' => $this
              ->t('Source string (@language)', [
              '@language' => $this
                ->t('Built-in English'),
            ]),
            '#title_display' => 'invisible',
            '#plain_text' => $source_array[0],
            '#prefix' => '<span lang="en">',
            '#suffix' => '</span>',
          ];
        }
        else {

          // Add original string value and mark as plural.
          $plural = TRUE;
          $original_singular = [
            '#type' => 'item',
            '#title' => $this
              ->t('Singular form'),
            '#plain_text' => $source_array[0],
            '#prefix' => '<span class="visually-hidden">' . $this
              ->t('Source string (@language)', [
              '@language' => $this
                ->t('Built-in English'),
            ]) . '</span><span lang="en">',
            '#suffix' => '</span>',
          ];
          $original_plural = [
            '#type' => 'item',
            '#title' => $this
              ->t('Plural form'),
            '#plain_text' => $source_array[1],
            '#prefix' => '<span lang="en">',
            '#suffix' => '</span>',
          ];
          $form['strings'][$string->lid]['original'] = [
            $original_singular,
            [
              '#markup' => '<br>',
            ],
            $original_plural,
          ];
        }
        if (!empty($string->context)) {
          $form['strings'][$string->lid]['original'][] = [
            '#type' => 'inline_template',
            '#template' => '<br><small>{{ context_title }}: <span lang="en">{{ context }}</span></small>',
            '#context' => [
              'context_title' => $this
                ->t('In Context'),
              'context' => $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 (!$plural) {
          $form['strings'][$string->lid]['translations'][0] = [
            '#type' => 'textarea',
            '#title' => $this
              ->t('Translated string (@language)', [
              '@language' => $language_name,
            ]),
            '#title_display' => 'invisible',
            '#rows' => $rows,
            '#default_value' => $translation_array[0],
            '#attributes' => [
              'lang' => $langcode,
            ],
          ];
        }
        else {

          // Add a textarea for each plural variant.
          for ($i = 0; $i < $plurals; $i++) {
            $form['strings'][$string->lid]['translations'][$i] = [
              '#type' => 'textarea',
              // @todo Should use better labels https://www.drupal.org/node/2499639
              '#title' => $i == 0 ? $this
                ->t('Singular form') : $this
                ->formatPlural($i, 'First plural form', '@count. plural form'),
              '#rows' => $rows,
              '#default_value' => $translation_array[$i] ?? '',
              '#attributes' => [
                'lang' => $langcode,
              ],
              '#prefix' => $i == 0 ? '<span class="visually-hidden">' . $this
                ->t('Translated string (@language)', [
                '@language' => $language_name,
              ]) . '</span>' : '',
            ];
          }
          if ($plurals == 2) {

            // Simplify interface text for the most common case.
            $form['strings'][$string->lid]['translations'][1]['#title'] = $this
              ->t('Plural form');
          }
        }
      }
      if (count(Element::children($form['strings']))) {
        $form['actions'] = [
          '#type' => 'actions',
        ];
        $form['actions']['submit'] = [
          '#type' => 'submit',
          '#value' => $this
            ->t('Save translations'),
        ];
      }
    }
    $form['pager']['#type'] = 'pager';
    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', [
            '%string' => $value,
          ]));
          $form_state
            ->setErrorByName("translations][{$langcode}][{$key}", $this
            ->t('The submitted string contains disallowed HTML: %string', [
            '%string' => $value,
          ]));
          $this
            ->logger('locale')
            ->warning('Attempted submission of a translation string with disallowed HTML: %string', [
            '%string' => $value,
          ]);
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $langcode = $form_state
      ->getValue('langcode');
    $updated = [];

    // Preload all translations for strings in the form.
    $lids = array_keys($form_state
      ->getValue('strings'));
    $existing_translation_objects = [];
    foreach ($this->localeStorage
      ->getTranslations([
      '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(PoItem::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 = $existing_translation_objects[$lid] ?? $this->localeStorage
          ->createTranslation([
          '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;
      }
    }
    $this
      ->messenger()
      ->addStatus($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', [], [
        'page' => $page,
      ]);
    }
    if ($updated) {

      // Clear cache and force refresh of JavaScript translations.
      _locale_refresh_translations([
        $langcode,
      ], $updated);
      _locale_refresh_configuration([
        $langcode,
      ], $updated);
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 2
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 2
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 8
MessengerTrait::messenger public function Gets the messenger. 8
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 1
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TranslateEditForm::buildForm public function Form constructor. Overrides FormInterface::buildForm
TranslateEditForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
TranslateEditForm::submitForm public function Form submission handler. Overrides FormInterface::submitForm
TranslateEditForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
TranslateFormBase::$filterValues protected static property Filter values. Shared between objects that inherit this class.
TranslateFormBase::$languageManager protected property The language manager.
TranslateFormBase::$localeStorage protected property The locale storage.
TranslateFormBase::$state protected property The state store.
TranslateFormBase::create public static function Instantiates a new instance of this class. Overrides FormBase::create
TranslateFormBase::translateFilterLoadStrings protected function Builds a string search query and returns an array of string objects.
TranslateFormBase::translateFilters protected function Lists locale translation filters that can be applied.
TranslateFormBase::translateFilterValues protected function Builds an array out of search criteria specified in request variables.
TranslateFormBase::__construct public function Constructs a new TranslationFormBase object.