View source
<?php
namespace Drupal\drush_language\Service;
use Drupal\Component\Gettext\PoStreamWriter;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Site\Settings;
use Drupal\language\ConfigurableLanguageManagerInterface;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\locale\PoDatabaseReader;
use Webmozart\PathUtil\Path;
class DrushLanguageCliService {
protected $configFactory;
protected $entityTypeManager;
protected $languageManager;
protected $moduleHandler;
protected $fileSystem;
protected $errors;
public function __construct(ConfigFactoryInterface $configFactory, EntityTypeManagerInterface $entityTypeManager, ConfigurableLanguageManagerInterface $languageManager, ModuleHandlerInterface $moduleHandler, FileSystemInterface $fileSystem) {
$this->configFactory = $configFactory;
$this->entityTypeManager = $entityTypeManager;
$this->languageManager = $languageManager;
$this->moduleHandler = $moduleHandler;
$this->fileSystem = $fileSystem;
$this->errors = [];
}
public function add($io, callable $t, array $langcodes) {
if (empty($langcodes)) {
$io
->error($t('Please provide one or more comma-separated language codes as arguments.'));
return;
}
foreach ($langcodes as $langcode) {
$messageArgs = [
'@langcode' => $langcode,
];
$languages = $this->languageManager
->getLanguages();
if (isset($languages[$langcode])) {
$io
->warning($t('The language with code @langcode already exists.', $messageArgs));
continue;
}
$predefined = $this->languageManager
->getStandardLanguageListWithoutConfigured();
if (!isset($predefined[$langcode])) {
$io
->warning($t('Invalid language code: @langcode', $messageArgs));
continue;
}
$language = ConfigurableLanguage::createFromLangcode($langcode);
$language
->save();
if ($this->moduleHandler
->moduleExists('locale')) {
module_load_include('fetch.inc', 'locale');
$options = _locale_translation_default_update_options();
if ($batch = locale_translation_batch_update_build([], [
$langcode,
], $options)) {
batch_set($batch);
$batch =& batch_get();
$batch['progressive'] = FALSE;
drush_backend_batch_process();
}
}
$io
->text($t('Added language: @langcode', $messageArgs));
}
}
public function languageDefault($io, callable $t, $langcode) {
$messageArgs = [
'@langcode' => $langcode,
];
$languages = $this->languageManager
->getLanguages();
if (!isset($languages[$langcode])) {
$io
->warning($t('Specified language does not exist: @langcode', $messageArgs));
return;
}
$this->configFactory
->getEditable('system.site')
->set('default_langcode', $langcode)
->save();
$this->languageManager
->reset();
}
public function importTranslations($io, callable $t, array $poFiles, array $options = [
'langcode' => NULL,
'replace-customized' => TRUE,
'replace-not-customized' => TRUE,
'set-customized' => TRUE,
'autocreate-language' => TRUE,
]) {
$this->moduleHandler
->loadInclude('locale', 'translation.inc');
$this->moduleHandler
->loadInclude('locale', 'bulk.inc');
$opt_langcode = $options['langcode'];
$opt_set_customized = $options['set-customized'];
$opt_replace_customized = $options['replace-customized'];
$opt_replace_not_customized = $options['replace-not-customized'];
$opt_autocreate_language = $options['autocreate-language'];
if (!$poFiles) {
if ($dir = Settings::get('custom_translations_directory')) {
$poFiles = glob(DRUPAL_ROOT . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . '*.po');
}
else {
$io
->success($t('Nothing to do, no file given and no custom translation directory set.'));
}
}
$importer_options = array_merge(_locale_translation_default_update_options(), [
'langcode' => $opt_langcode,
'customized' => $opt_set_customized ? LOCALE_CUSTOMIZED : LOCALE_NOT_CUSTOMIZED,
'overwrite_options' => [
'customized' => (int) $opt_replace_customized,
'not_customized' => (int) $opt_replace_not_customized,
],
]);
$files = [];
$langcodes_to_import = [];
foreach ($poFiles as $file_path) {
if (file_exists($file_path)) {
$file = locale_translate_file_create($file_path);
$file = locale_translate_file_attach_properties($file, $importer_options);
if ($file->langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
if (!$opt_langcode) {
$io
->error($t('Can not autodetect language of file @file', [
'@file' => $file_path,
]));
return;
}
$file->langcode = $opt_langcode;
if (empty($file->version) && !empty($file->project) && !empty($file->langcode)) {
$sources = locale_translation_get_status();
$source = $sources[$file->project][$file->langcode];
if (isset($source->version)) {
$file->version = $source->version;
}
}
}
$langcodes_to_import[$file->langcode] = $file->langcode;
$files[] = $file;
}
else {
$io
->error($t('File to import at @filepath not found.', [
'@filepath' => $file_path,
]));
}
}
if ($opt_autocreate_language) {
$languages = $this->languageManager
->getLanguages();
foreach ($langcodes_to_import as $langcode_to_import) {
if (!isset($languages[$langcode_to_import])) {
try {
$language_storage = $this->entityTypeManager
->getStorage('configurable_language');
$language = $language_storage
->create([
'langcode' => $opt_langcode,
]);
$io
->success($t('The language @id (@label) has been created.', [
'@id' => $language
->id(),
'@label' => $language
->label(),
]));
} catch (InvalidPluginDefinitionException $exception) {
$io
->error($exception
->getMessage());
}
}
}
}
$batch = locale_translate_batch_build($files, $importer_options);
batch_set($batch);
if ($batch = locale_config_batch_update_components($importer_options, $langcodes_to_import)) {
batch_set($batch);
}
drush_backend_batch_process();
$io
->success($t('Import complete.'));
}
public function exportTranslations($io, callable $t, array $options = [
'statuses' => [
'customized',
],
'langcodes' => [],
'file' => '%langcode.po',
'force' => TRUE,
]) {
$opt_langcodes = $options['langcodes'];
$opt_filepath = $options['file'];
$opt_force_write = $options['force'];
$opt_statuses = $options['statuses'];
$export_statuses_allowed = [
'customized' => 'customized',
'not_customized' => 'not-customized',
'not_translated' => 'not-translated',
];
$opt_statuses = array_values($opt_statuses);
if ($opt_statuses == [
'all',
]) {
$opt_statuses = $export_statuses_allowed;
}
$export_statuses_unknown = array_diff($opt_statuses, $export_statuses_allowed);
if ($export_statuses_unknown) {
$io
->error($t('Unknown status options: @options', [
'@options' => implode(', ', $export_statuses_unknown),
]));
return;
}
$export_statuses_filtered = array_intersect($export_statuses_allowed, $opt_statuses);
$export_statuses = array_fill_keys(array_keys($export_statuses_filtered), TRUE);
if (!Path::isAbsolute($opt_filepath) && !('./' === substr($opt_filepath, 0, 2))) {
$opt_filedir = Settings::get('custom_translations_directory');
if (!$opt_filedir) {
$io
->error($t('Can not export, relative path given and no $settings[\'custom_translations_directory\'] defined. You can instead use an absolute filename or one starting with "./".'));
return;
}
if (!Path::isAbsolute($opt_filedir)) {
$opt_filedir = DRUPAL_ROOT . DIRECTORY_SEPARATOR . $opt_filedir;
}
$opt_filepath = $opt_filedir . DIRECTORY_SEPARATOR . $opt_filepath;
}
if (!$opt_langcodes) {
$languages = $this->languageManager
->getLanguages();
$opt_langcodes = array_keys($languages);
}
if (count($opt_langcodes) > 1 && !preg_match('/%langcode/u', $opt_filepath)) {
$io
->error($t('You must use %langcode file placeholder when exporting multiple languages.'));
return;
}
foreach ($opt_langcodes as $langcode) {
$language = $this->languageManager
->getLanguage($langcode);
if ($language == NULL) {
$io
->error($t('Unknown language: %langcode', [
'%langcode' => $langcode,
]));
return;
}
}
foreach ($opt_langcodes as $langcode) {
$filepath = preg_replace('/%langcode/u', $langcode, $opt_filepath);
$language = $this->languageManager
->getLanguage($langcode);
$dir = dirname($filepath);
if (!$this->fileSystem
->prepareDirectory($dir)) {
$this->fileSystem
->prepareDirectory($dir, FileSystemInterface::MODIFY_PERMISSIONS | FileSystemInterface::CREATE_DIRECTORY);
}
$reader = new PoDatabaseReader();
$language_name = '';
if ($language != NULL) {
$reader
->setLangcode($language
->getId());
$reader
->setOptions($export_statuses);
$languages = $this->languageManager
->getLanguages();
$language_name = isset($languages[$language
->getId()]) ? $languages[$language
->getId()]
->getName() : '';
}
$item = $reader
->readItem();
if ($item || $opt_force_write) {
$header = $reader
->getHeader();
$header
->setProjectName($this->configFactory
->get('system.site')
->get('name'));
$header
->setLanguageName($language_name);
$writer = new PoStreamWriter();
$writer
->setURI($filepath);
$writer
->setHeader($header);
$writer
->open();
if ($item) {
$writer
->writeItem($item);
}
$writer
->writeItems($reader);
$writer
->close();
$io
->success($t('Exported translations for language !langcode to file !file.', [
'!langcode' => $langcode,
'!file' => $filepath,
]));
}
else {
$io
->error($t('Nothing to export for language !langcode.', [
'!langcode' => $langcode,
]));
return;
}
}
$io
->success($t('Export complete.'));
}
public function getErrors() {
return $this->errors;
}
}