summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/locale/tests/src/Kernel/LocaleStringTest.php
blob: b007c7ab55b04a36ecafc2962247ca3c7ab12d63 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
<?php

declare(strict_types=1);

namespace Drupal\Tests\locale\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\locale\StringInterface;

/**
 * Tests the locale string storage, string objects and data API.
 *
 * @group locale
 */
class LocaleStringTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'language',
    'locale',
  ];

  /**
   * The locale storage.
   *
   * @var \Drupal\locale\StringStorageInterface
   */
  protected $storage;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    // Add a default locale storage for all these tests.
    $this->storage = $this->container->get('locale.storage');
    // Create two languages: Spanish and German.
    foreach (['es', 'de'] as $langcode) {
      ConfigurableLanguage::createFromLangcode($langcode)->save();
    }
    $this->installSchema('locale', [
      'locales_location',
      'locales_source',
      'locales_target',
    ]);
  }

  /**
   * Tests CRUD API.
   */
  public function testStringCrudApi(): void {
    // Create source string.
    $source = $this->buildSourceString()->save();
    $this->assertNotEmpty($source->lid);

    // Load strings by lid and source.
    $string1 = $this->storage->findString(['lid' => $source->lid]);
    $this->assertEquals($source, $string1);
    $string2 = $this->storage->findString(['source' => $source->source, 'context' => $source->context]);
    $this->assertEquals($source, $string2);
    $string3 = $this->storage->findString(['source' => $source->source, 'context' => '']);
    $this->assertNull($string3);

    // Check version handling and updating.
    $this->assertEquals('none', $source->version);
    $string = $this->storage->findTranslation(['lid' => $source->lid]);
    $this->assertEquals(\Drupal::VERSION, $string->version);

    // Create translation and find it by lid and source.
    $langcode = 'es';
    $translation = $this->createTranslation($source, $langcode);
    $this->assertEquals(LOCALE_NOT_CUSTOMIZED, $translation->customized);
    $string1 = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
    $this->assertEquals($translation->translation, $string1->translation);
    $string2 = $this->storage->findTranslation([
      'language' => $langcode,
      'source' => $source->source,
      'context' => $source->context,
    ]);
    $this->assertEquals($translation->translation, $string2->translation);
    $translation
      ->setCustomized()
      ->save();
    $translation = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
    $this->assertEquals(LOCALE_CUSTOMIZED, $translation->customized);

    // Delete translation.
    $translation->delete();
    $deleted = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
    $this->assertNull($deleted->translation);

    // Create some translations and then delete string and all of its
    // translations.
    $lid = $source->lid;
    $this->createAllTranslations($source);
    $search = $this->storage->getTranslations(['lid' => $source->lid]);
    $this->assertCount(3, $search);

    $source->delete();
    $string = $this->storage->findString(['lid' => $lid]);
    $this->assertNull($string);
    $deleted = $search = $this->storage->getTranslations(['lid' => $lid]);
    $this->assertEmpty($deleted);

    // Tests that locations of different types and arbitrary lengths can be
    // added to a source string. Too long locations will be cut off.
    $source_string = $this->buildSourceString();
    $source_string->addLocation('javascript', $this->randomString(8));
    $source_string->addLocation('configuration', $this->randomString(50));
    $source_string->addLocation('code', $this->randomString(100));
    $source_string->addLocation('path', $location = $this->randomString(300));
    $source_string->save();

    $rows = $this->container->get('database')->select('locales_location')
      ->fields('locales_location')
      ->condition('sid', $source_string->lid)
      ->execute()
      ->fetchAllAssoc('type');
    $this->assertCount(4, $rows);
    $this->assertEquals(substr($location, 0, 255), $rows['path']->name);
  }

  /**
   * Tests Search API loading multiple objects.
   */
  public function testStringSearchApi(): void {
    $language_count = 3;
    // Strings 1 and 2 will have some common prefix.
    // Source 1 will have all translations, not customized.
    // Source 2 will have all translations, customized.
    // Source 3 will have no translations.
    $prefix = $this->randomMachineName(100);
    $source1 = $this->buildSourceString(['source' => $prefix . $this->randomMachineName(100)])->save();
    $source2 = $this->buildSourceString(['source' => $prefix . $this->randomMachineName(100)])->save();
    $source3 = $this->buildSourceString()->save();

    // Load all source strings.
    $strings = $this->storage->getStrings([]);
    $this->assertCount(3, $strings);
    // Load all source strings matching a given string.
    $filter_options['filters'] = ['source' => $prefix];
    $strings = $this->storage->getStrings([], $filter_options);
    $this->assertCount(2, $strings);

    // Not customized translations.
    $translate1 = $this->createAllTranslations($source1);
    // Customized translations.
    $this->createAllTranslations($source2, ['customized' => LOCALE_CUSTOMIZED]);
    // Try quick search function with different field combinations.
    $langcode = 'es';
    $found = $this->storage->findTranslation([
      'language' => $langcode,
      'source' => $source1->source,
      'context' => $source1->context,
    ]);
    $this->assertNotNull($found, 'Translation not found searching by source and context.');
    $this->assertNotNull($found->language);
    $this->assertNotNull($found->translation);
    $this->assertFalse($found->isNew());
    $this->assertEquals($translate1[$langcode]->translation, $found->translation);
    // Now try a translation not found.
    $found = $this->storage->findTranslation([
      'language' => $langcode,
      'source' => $source3->source,
      'context' => $source3->context,
    ]);
    $this->assertNotNull($found);
    $this->assertSame($source3->lid, $found->lid);
    $this->assertNull($found->translation);
    $this->assertTrue($found->isNew());

    // Load all translations. For next queries we'll be loading only translated
    // strings.
    $translations = $this->storage->getTranslations(['translated' => TRUE]);
    $this->assertCount(2 * $language_count, $translations);

    // Load all customized translations.
    $translations = $this->storage->getTranslations(['customized' => LOCALE_CUSTOMIZED, 'translated' => TRUE]);
    $this->assertCount($language_count, $translations);

    // Load all Spanish customized translations.
    $translations = $this->storage->getTranslations([
      'language' => 'es',
      'customized' => LOCALE_CUSTOMIZED,
      'translated' => TRUE,
    ]);
    $this->assertCount(1, $translations);

    // Load all source strings without translation (1).
    $translations = $this->storage->getStrings(['translated' => FALSE]);
    $this->assertCount(1, $translations);

    // Load Spanish translations using string filter.
    $filter_options['filters'] = ['source' => $prefix];
    $translations = $this->storage->getTranslations(['language' => 'es'], $filter_options);
    $this->assertCount(2, $translations);
  }

  /**
   * Creates random source string object.
   *
   * @param array $values
   *   The values array.
   *
   * @return \Drupal\locale\StringInterface
   *   A locale string.
   */
  protected function buildSourceString(array $values = []) {
    return $this->storage->createString($values += [
      'source' => $this->randomMachineName(100),
      'context' => $this->randomMachineName(20),
    ]);
  }

  /**
   * Creates translations for source string and all languages.
   *
   * @param \Drupal\locale\StringInterface $source
   *   The source string.
   * @param array $values
   *   The values array.
   *
   * @return array
   *   Translation list.
   */
  protected function createAllTranslations(StringInterface $source, array $values = []): array {
    $list = [];
    /** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */
    $language_manager = $this->container->get('language_manager');
    foreach ($language_manager->getLanguages() as $language) {
      $list[$language->getId()] = $this->createTranslation($source, $language->getId(), $values);
    }
    return $list;
  }

  /**
   * Creates single translation for source string.
   *
   * @param \Drupal\locale\StringInterface $source
   *   The source string.
   * @param string $langcode
   *   The language code.
   * @param array $values
   *   The values array.
   *
   * @return \Drupal\locale\StringInterface
   *   The translated string object.
   */
  protected function createTranslation(StringInterface $source, $langcode, array $values = []) {
    return $this->storage->createTranslation($values + [
      'lid' => $source->lid,
      'language' => $langcode,
      'translation' => $this->randomMachineName(100),
    ])->save();
  }

  /**
   * Tests that strings are correctly deleted.
   */
  public function testDeleteStrings(): void {
    $source = $this->storage->createString([
      'source' => 'Revision ID',
    ])->save();

    $this->storage->createTranslation([
      'lid' => $source->lid,
      'language' => 'fr',
      'translation' => 'Translated Revision ID',
    ])->save();

    // Confirm that the string has been created.
    $this->assertNotEmpty($this->storage->findString(['lid' => $source->lid]));

    $this->storage->deleteStrings(['lid' => $source->lid]);

    // Confirm that the string has been deleted.
    $this->assertEmpty($this->storage->findString(['lid' => $source->lid]));
  }

}