Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit af64eb7

Browse files
SutharsanTessBakker
authored andcommitted
Add command to import .po files (#3521)
1 parent ddca661 commit af64eb7

File tree

3 files changed

+283
-2
lines changed

3 files changed

+283
-2
lines changed

src/Drupal/Commands/core/LocaleCommands.php

Lines changed: 212 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,44 @@
22

33
namespace Drush\Drupal\Commands\core;
44

5+
use Consolidation\AnnotatedCommand\CommandData;
6+
use Drupal\Component\Gettext\PoStreamWriter;
7+
use Drupal\Core\Config\ConfigFactoryInterface;
58
use Drupal\Core\Extension\ModuleHandlerInterface;
9+
use Drupal\Core\Language\LanguageInterface;
10+
use Drupal\Core\Language\LanguageManagerInterface;
611
use Drupal\Core\State\StateInterface;
12+
use Drupal\language\Entity\ConfigurableLanguage;
13+
use Drupal\locale\PoDatabaseReader;
714
use Drush\Commands\DrushCommands;
815

916
class LocaleCommands extends DrushCommands
1017
{
1118

19+
protected $languageManager;
20+
21+
protected $configFactory;
22+
1223
protected $moduleHandler;
1324

1425
protected $state;
1526

27+
/**
28+
* @return \Drupal\Core\Language\LanguageManagerInterface
29+
*/
30+
protected function getLanguageManager()
31+
{
32+
return $this->languageManager;
33+
}
34+
35+
/**
36+
* @return \Drupal\Core\Config\ConfigFactoryInterface
37+
*/
38+
protected function getConfigFactory()
39+
{
40+
return $this->configFactory;
41+
}
42+
1643
/**
1744
* @return \Drupal\Core\Extension\ModuleHandlerInterface
1845
*/
@@ -29,8 +56,10 @@ public function getState()
2956
return $this->state;
3057
}
3158

32-
public function __construct(ModuleHandlerInterface $moduleHandler, StateInterface $state)
59+
public function __construct(LanguageManagerInterface $languageManager, ConfigFactoryInterface $configFactory, ModuleHandlerInterface $moduleHandler, StateInterface $state)
3360
{
61+
$this->languageManager = $languageManager;
62+
$this->configFactory = $configFactory;
3463
$this->moduleHandler = $moduleHandler;
3564
$this->state = $state;
3665
}
@@ -128,4 +157,186 @@ public function update($options = ['langcodes' => self::REQ])
128157

129158
drush_backend_batch_process();
130159
}
160+
161+
/**
162+
* Imports to a gettext translation file.
163+
*
164+
* @command locale:import
165+
* @validate-module-enabled locale
166+
* @param $langcode The language code of the imported translations.
167+
* @param $file Path and file name of the gettext file.
168+
* @option type The type of translations to be imported, defaults to 'not-customized'. Options:
169+
* - customized: Treat imported strings as custom translations.
170+
* - not-customized: Treat imported strings as not-custom translations.
171+
* @option override Whether and how imported strings will override existing translations. Defaults to the Import behavior configurred in the admin interface. Options:
172+
* - none: Don't overwrite existing translations. Only append new translations.
173+
* - customized: Only override existing customized translations.
174+
* - not-customized: Only override non-customized translations, customized translations are kept.
175+
* - all: Override any existing translation.
176+
* @usage drush locale-import nl drupal-8.4.2.nl.po
177+
* Import the Dutch drupal core translation.
178+
* @usage drush locale-import nl custom-translations.po --type=custom --override=all
179+
* Import customized Dutch translations and override any existing translation.
180+
* @aliases locale-export
181+
* @throws \Exception
182+
*/
183+
public function import($langcode, $file, $options = ['type' => self::OPT, 'override' => self::OPT])
184+
{
185+
if (!drush_file_not_empty($file)) {
186+
throw new \Exception(dt('File @file not found or empty.', ['@file' => $file]));
187+
}
188+
189+
$language = $this->getTranslatableLanguage($langcode, true);
190+
191+
$this->getModuleHandler()->loadInclude('locale', 'translation.inc');
192+
$this->getModuleHandler()->loadInclude('locale', 'bulk.inc');
193+
194+
$translationOptions = _locale_translation_default_update_options();
195+
$translationOptions['langcode'] = $language->getId();
196+
$translationOptions['customized'] = $this->convertCustomizedType($options['type']);
197+
$override = $this->convertOverrideOption($options['override']);
198+
if ($override) {
199+
$translationOptions['overwrite_options'] = $override;
200+
}
201+
202+
$poFile = (object) [
203+
'filename' => basename($file),
204+
'uri' => $file,
205+
];
206+
$poFile = locale_translate_file_attach_properties($poFile, $translationOptions);
207+
208+
// Set a batch to download and import translations.
209+
$batch = locale_translate_batch_build([$poFile->uri => $poFile], $translationOptions);
210+
batch_set($batch);
211+
if ($batch = locale_config_batch_update_components($translationOptions, [$language->getId()])) {
212+
batch_set($batch);
213+
}
214+
215+
drush_backend_batch_process();
216+
}
217+
218+
/**
219+
* Converts input of translation type.
220+
*
221+
* @param $type
222+
* @return integer
223+
*/
224+
private function convertCustomizedType($type)
225+
{
226+
switch ($type) {
227+
case 'customized':
228+
$result = LOCALE_CUSTOMIZED;
229+
break;
230+
231+
default:
232+
$result = LOCALE_NOT_CUSTOMIZED;
233+
break;
234+
}
235+
236+
return $result;
237+
}
238+
239+
/**
240+
* Converts input of override option.
241+
*
242+
* @param $override
243+
* @return array
244+
*/
245+
private function convertOverrideOption($override)
246+
{
247+
$result = [];
248+
249+
switch ($override) {
250+
case 'none':
251+
$result = [
252+
'not_customized' => false,
253+
'customized' => false,
254+
];
255+
break;
256+
257+
case 'customized':
258+
$result = [
259+
'not_customized' => false,
260+
'customized' => true,
261+
];
262+
break;
263+
264+
case 'not-customized':
265+
$result = [
266+
'not_customized' => true,
267+
'customized' => false,
268+
];
269+
break;
270+
271+
case 'all':
272+
$result = [
273+
'not_customized' => true,
274+
'customized' => true,
275+
];
276+
break;
277+
}
278+
279+
return $result;
280+
}
281+
282+
/**
283+
* Get translatable language object.
284+
*
285+
* @param string $langcode The language code of the language object.
286+
* @param bool $addLanguage Create language when not available.
287+
* @return LanguageInterface|null
288+
* @throws \Exception
289+
*/
290+
private function getTranslatableLanguage($langcode, $addLanguage = false)
291+
{
292+
if (!$langcode) {
293+
return null;
294+
}
295+
296+
$language = $this->getLanguageManager()->getLanguage($langcode);
297+
298+
if (!$language) {
299+
if ($addLanguage) {
300+
$language = ConfigurableLanguage::createFromLangcode($langcode);
301+
$language->save();
302+
303+
$this->logger->success(dt('Added language @language', [
304+
'@language' => $language->label(),
305+
]));
306+
} else {
307+
throw new \Exception(dt('Language code @langcode is not configured.', [
308+
'@langcode' => $langcode,
309+
]));
310+
}
311+
}
312+
313+
if (!$this->isTranslatable($language)) {
314+
throw new \Exception(dt('Language code @langcode is not translatable.', [
315+
'@langcode' => $langcode,
316+
]));
317+
}
318+
319+
return $language;
320+
}
321+
322+
/**
323+
* Check if language is translatable.
324+
*
325+
* @param LanguageInterface $language
326+
* @return bool
327+
*/
328+
private function isTranslatable(LanguageInterface $language)
329+
{
330+
if ($language->isLocked()) {
331+
return false;
332+
}
333+
334+
if ($language->getId() != 'en') {
335+
return true;
336+
}
337+
338+
return (bool)$this->getConfigFactory()
339+
->get('locale.settings')
340+
->get('translate_english');
341+
}
131342
}

src/Drupal/Commands/core/drush.services.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ services:
2828
- { name: drush.command }
2929
locale.commands:
3030
class: \Drush\Drupal\Commands\core\LocaleCommands
31-
arguments: ['@module_handler', '@state']
31+
arguments: ['@language_manager', '@config.factory', '@module_handler', '@state']
3232
tags:
3333
- { name: drush.command }
3434
messenger.commands:

tests/LocaleTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Unish;
4+
5+
use Webmozart\PathUtil\Path;
6+
7+
/**
8+
* @group slow
9+
* @group pm
10+
*/
11+
class LocaleTest extends CommandUnishTestCase
12+
{
13+
14+
public function testLocaleImport()
15+
{
16+
$this->setUpDrupal(1, true);
17+
$this->drush('pm:enable', ['language', 'locale']);
18+
$this->drush('language:add', ['nl'], ['skip-translations' => null]);
19+
20+
$source = Path::join(__DIR__, '/resources/devel.nl.po');
21+
22+
$this->drush('locale:import', ['nl', $source]);
23+
$this->assertTranslation('Devel', 'NL Devel', 'nl', 0);
24+
25+
// Import without override.
26+
$this->drush('sql:query', ["UPDATE locales_target SET translation = 'NO Devel'"]);
27+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 0);
28+
$this->drush('locale:import', ['nl', $source], ['override' => 'none']);
29+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 0);
30+
31+
// Import with override.
32+
$this->drush('sql:query', ["UPDATE locales_target SET translation = 'NO Devel'"]);
33+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 0);
34+
$this->drush('locale:import', ['nl', $source], ['override' => 'not-customized']);
35+
$this->assertTranslation('Devel', 'NL Devel', 'nl', 0);
36+
37+
// Import without override of custom translation
38+
$this->drush('sql:query', ["UPDATE locales_target SET translation = 'NO Devel', customized = 1"]);
39+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 1);
40+
$this->drush('locale:import', ['nl', $source], ['override' => 'not-customized']);
41+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 1);
42+
43+
// Import with override of custom translation.
44+
$this->drush('sql:query', ["UPDATE locales_target SET translation = 'NO Devel', customized = 1"]);
45+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 1);
46+
$this->drush('locale:import', ['nl', $source], ['override' => 'customized']);
47+
$this->assertTranslation('Devel', 'NL Devel', 'nl', 0);
48+
49+
// Import with override of custom translation as customized.
50+
$this->drush('sql:query', ["UPDATE locales_target SET translation = 'NO Devel', customized = 1"]);
51+
$this->assertTranslation('Devel', 'NO Devel', 'nl', 1);
52+
$this->drush('locale:import', ['nl', $source], ['type' => 'customized', 'override' => 'customized']);
53+
$this->assertTranslation('Devel', 'NL Devel', 'nl', 1);
54+
}
55+
56+
/**
57+
* @param string $source
58+
* @param string $translation
59+
* @param string $langcode
60+
* @param int $custom
61+
* @param string $context
62+
*/
63+
private function assertTranslation($source, $translation, $langcode, $custom = 0, $context = '')
64+
{
65+
$this->drush('sql:query', ["SELECT ls.source, ls.context, lt.translation, lt.language, lt.customized FROM locales_source ls JOIN locales_target lt ON ls.lid = lt.lid WHERE ls.source = '$source' AND ls.context = '$context' AND lt.language = '$langcode'"]);
66+
$output = $this->getOutputAsList();
67+
$expected = "/$source.*$context.*$translation.*$langcode.*$custom/";
68+
$this->assertRegExp($expected, array_pop($output));
69+
}
70+
}

0 commit comments

Comments
 (0)