Version 1
[yaffs-website] / web / core / modules / locale / src / Form / TranslateEditForm.php
1 <?php
2
3 namespace Drupal\locale\Form;
4
5 use Drupal\Core\Form\FormStateInterface;
6 use Drupal\Core\Render\Element;
7 use Drupal\locale\SourceString;
8
9 /**
10  * Defines a translation edit form.
11  */
12 class TranslateEditForm extends TranslateFormBase {
13
14   /**
15    * {@inheritdoc}
16    */
17   public function getFormId() {
18     return 'locale_translate_edit_form';
19   }
20
21   /**
22    * {@inheritdoc}
23    */
24   public function buildForm(array $form, FormStateInterface $form_state) {
25     $filter_values = $this->translateFilterValues();
26     $langcode = $filter_values['langcode'];
27
28     $this->languageManager->reset();
29     $languages = $this->languageManager->getLanguages();
30
31     $langname = isset($langcode) ? $languages[$langcode]->getName() : "- None -";
32
33     $form['#attached']['library'][] = 'locale/drupal.locale.admin';
34
35     $form['langcode'] = [
36       '#type' => 'value',
37       '#value' => $filter_values['langcode'],
38     ];
39
40     $form['strings'] = [
41       '#type' => 'table',
42       '#tree' => TRUE,
43       '#language' => $langname,
44       '#header' => [
45         $this->t('Source string'),
46         $this->t('Translation for @language', ['@language' => $langname]),
47       ],
48       '#empty' => $this->t('No strings available.'),
49       '#attributes' => ['class' => ['locale-translate-edit-table']],
50     ];
51
52     if (isset($langcode)) {
53       $strings = $this->translateFilterLoadStrings();
54
55       $plurals = $this->getNumberOfPlurals($langcode);
56
57       foreach ($strings as $string) {
58         // Cast into source string, will do for our purposes.
59         $source = new SourceString($string);
60         // Split source to work with plural values.
61         $source_array = $source->getPlurals();
62         $translation_array = $string->getPlurals();
63         if (count($source_array) == 1) {
64           // Add original string value and mark as non-plural.
65           $plural = FALSE;
66           $form['strings'][$string->lid]['original'] = [
67             '#type' => 'item',
68             '#title' => $this->t('Source string (@language)', ['@language' => $this->t('Built-in English')]),
69             '#title_display' => 'invisible',
70             '#plain_text' => $source_array[0],
71             '#preffix' => '<span lang="en">',
72             '#suffix' => '</span>',
73           ];
74         }
75         else {
76           // Add original string value and mark as plural.
77           $plural = TRUE;
78           $original_singular = [
79             '#type' => 'item',
80             '#title' => $this->t('Singular form'),
81             '#plain_text' => $source_array[0],
82             '#prefix' => '<span class="visually-hidden">' . $this->t('Source string (@language)', ['@language' => $this->t('Built-in English')]) . '</span><span lang="en">',
83             '#suffix' => '</span>',
84           ];
85           $original_plural = [
86             '#type' => 'item',
87             '#title' => $this->t('Plural form'),
88             '#plain_text' => $source_array[1],
89             '#preffix' => '<span lang="en">',
90             '#suffix' => '</span>',
91           ];
92           $form['strings'][$string->lid]['original'] = [
93             $original_singular,
94             ['#markup' => '<br>'],
95             $original_plural,
96           ];
97         }
98         if (!empty($string->context)) {
99           $form['strings'][$string->lid]['original'][] = [
100             '#type' => 'inline_template',
101             '#template' => '<br><small>{{ context_title }}: <span lang="en">{{ context }}</span></small>',
102             '#context' => [
103               'context_title' => $this->t('In Context'),
104               'context' => $string->context,
105             ],
106           ];
107         }
108         // Approximate the number of rows to use in the default textarea.
109         $rows = min(ceil(str_word_count($source_array[0]) / 12), 10);
110         if (!$plural) {
111           $form['strings'][$string->lid]['translations'][0] = [
112             '#type' => 'textarea',
113             '#title' => $this->t('Translated string (@language)', ['@language' => $langname]),
114             '#title_display' => 'invisible',
115             '#rows' => $rows,
116             '#default_value' => $translation_array[0],
117             '#attributes' => ['lang' => $langcode],
118           ];
119         }
120         else {
121           // Add a textarea for each plural variant.
122           for ($i = 0; $i < $plurals; $i++) {
123             $form['strings'][$string->lid]['translations'][$i] = [
124               '#type' => 'textarea',
125               // @todo Should use better labels https://www.drupal.org/node/2499639
126               '#title' => ($i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form')),
127               '#rows' => $rows,
128               '#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '',
129               '#attributes' => ['lang' => $langcode],
130               '#prefix' => $i == 0 ? ('<span class="visually-hidden">' . $this->t('Translated string (@language)', ['@language' => $langname]) . '</span>') : '',
131             ];
132           }
133           if ($plurals == 2) {
134             // Simplify interface text for the most common case.
135             $form['strings'][$string->lid]['translations'][1]['#title'] = $this->t('Plural form');
136           }
137         }
138       }
139       if (count(Element::children($form['strings']))) {
140         $form['actions'] = ['#type' => 'actions'];
141         $form['actions']['submit'] = [
142           '#type' => 'submit',
143           '#value' => $this->t('Save translations'),
144         ];
145       }
146     }
147     $form['pager']['#type'] = 'pager';
148     return $form;
149   }
150
151   /**
152    * {@inheritdoc}
153    */
154   public function validateForm(array &$form, FormStateInterface $form_state) {
155     $langcode = $form_state->getValue('langcode');
156     foreach ($form_state->getValue('strings') as $lid => $translations) {
157       foreach ($translations['translations'] as $key => $value) {
158         if (!locale_string_is_safe($value)) {
159           $form_state->setErrorByName("strings][$lid][translations][$key", $this->t('The submitted string contains disallowed HTML: %string', ['%string' => $value]));
160           $form_state->setErrorByName("translations][$langcode][$key", $this->t('The submitted string contains disallowed HTML: %string', ['%string' => $value]));
161           $this->logger('locale')->warning('Attempted submission of a translation string with disallowed HTML: %string', ['%string' => $value]);
162         }
163       }
164     }
165   }
166
167   /**
168    * {@inheritdoc}
169    */
170   public function submitForm(array &$form, FormStateInterface $form_state) {
171     $langcode = $form_state->getValue('langcode');
172     $updated = [];
173
174     // Preload all translations for strings in the form.
175     $lids = array_keys($form_state->getValue('strings'));
176     $existing_translation_objects = [];
177     foreach ($this->localeStorage->getTranslations(['lid' => $lids, 'language' => $langcode, 'translated' => TRUE]) as $existing_translation_object) {
178       $existing_translation_objects[$existing_translation_object->lid] = $existing_translation_object;
179     }
180
181     foreach ($form_state->getValue('strings') as $lid => $new_translation) {
182       $existing_translation = isset($existing_translation_objects[$lid]);
183
184       // Plural translations are saved in a delimited string. To be able to
185       // compare the new strings with the existing strings a string in the same
186       // format is created.
187       $new_translation_string_delimited = implode(LOCALE_PLURAL_DELIMITER, $new_translation['translations']);
188
189       // Generate an imploded string without delimiter, to be able to run
190       // empty() on it.
191       $new_translation_string = implode('', $new_translation['translations']);
192
193       $is_changed = FALSE;
194
195       if ($existing_translation && $existing_translation_objects[$lid]->translation != $new_translation_string_delimited) {
196         // If there is an existing translation in the DB and the new translation
197         // is not the same as the existing one.
198         $is_changed = TRUE;
199       }
200       elseif (!$existing_translation && !empty($new_translation_string)) {
201         // Newly entered translation.
202         $is_changed = TRUE;
203       }
204
205       if ($is_changed) {
206         // Only update or insert if we have a value to use.
207         $target = isset($existing_translation_objects[$lid]) ? $existing_translation_objects[$lid] : $this->localeStorage->createTranslation(['lid' => $lid, 'language' => $langcode]);
208         $target->setPlurals($new_translation['translations'])
209           ->setCustomized()
210           ->save();
211         $updated[] = $target->getId();
212       }
213       if (empty($new_translation_string) && isset($existing_translation_objects[$lid])) {
214         // Empty new translation entered: remove existing entry from database.
215         $existing_translation_objects[$lid]->delete();
216         $updated[] = $lid;
217       }
218     }
219
220     drupal_set_message($this->t('The strings have been saved.'));
221
222     // Keep the user on the current pager page.
223     $page = $this->getRequest()->query->get('page');
224     if (isset($page)) {
225       $form_state->setRedirect(
226         'locale.translate_page',
227         [],
228         ['page' => $page]
229       );
230     }
231
232     if ($updated) {
233       // Clear cache and force refresh of JavaScript translations.
234       _locale_refresh_translations([$langcode], $updated);
235       _locale_refresh_configuration([$langcode], $updated);
236     }
237   }
238
239 }