language_translations.drush.inc in Drush Language Commands 7
Drush language commands related to translations.
- language-import-translations
- language-export-translations
- language-refresh-translations
File
language_translations.drush.incView source
<?php
/**
* @file
* Drush language commands related to translations.
* - language-import-translations
* - language-export-translations
* - language-refresh-translations
*/
/**
* Implements of hook_drush_command().
*
* @See drush_parse_command() for a list of recognized keys.
*
* @return array An associative array describing your command(s).
* An associative array describing your command(s).
*/
function language_translations_drush_command() {
$commands = array();
// Show enabled locale groups.
$commands['language-groups-info'] = array(
'description' => "Print a list with enabled locale groups.",
'aliases' => array(
'language-groups',
'language-info',
),
'callback' => 'drush_language_translations_groups_info',
);
// Import translations.
$commands['language-import-translations'] = array(
'description' => "Import translations to one or more locale groups.",
'arguments' => array(
'langcode' => "The langcode of the language to be imported.",
'path' => "Path to a directory with the translation files or URL of translation file from external site. Can be a file-name if one locale-group is imported.",
),
'options' => array(
'groups' => array(
'description' => "Comma separated list of locale groups. Defaults to 'all'.",
'value' => 'optional',
),
'replace' => array(
'description' => "Replace existing translations in the database. Defaults to 'false'.",
'value' => 'optional',
),
),
'examples' => array(
'Import all locale groups' => "\$ drush language-import-translations fr sites/all/translations/fr" . "\n" . "Reads from directory with filename pattern <locale-group>.<langcode>.po (default.fr.po)",
'Import specific locale groups, replace existing translations' => "\$ drush language-import-translations fr sites/all/translations/fr --groups=default,menu --replace",
'Import menu locale group from file' => "\$ drush language-import-translations fr sites/all/translations/fr/menu.fr.po --groups-menu",
),
'aliases' => array(
'langimp',
'language-import',
),
'callback' => 'drush_language_translations_import',
'drupal dependencies' => array(
'locale',
),
);
// Export translations.
$commands['language-export-translations'] = array(
'description' => "Export translations from one or more locale groups. Note: To export only custom translations install l10n_update and use language-export-translations-ng",
'arguments' => array(
'langcode' => array(
'description' => "The langcode of the language to be exported.",
'value' => 'required',
),
'path' => "Path to a directory where the translation files should be saved. Can be a file-name if one locale-group is exported.",
),
'options' => array(
'groups' => array(
'description' => "Comma separated list of locale groups. Defaults to 'all'.",
'value' => 'optional',
),
'replace' => array(
'description' => "Replace existing translations in the file-system. Defaults to 'false'.",
'value' => 'optional',
),
'sort' => array(
'description' => "Sorts the generated po file by source and context, defaults to 'true'.",
'value' => 'optional',
),
'filter' => array(
'description' => "Removes empty translations from the generated po file, defaults to 'true'.",
'value' => 'optional',
),
),
'examples' => array(
'Export all locale groups' => "\$ drush language-export-translations fr sites/all/translations/fr" . "\n" . "Saves to directory with filename pattern <locale-group>.<langcode>.po (default.fr.po)",
'Export specific locale groups, replace existing export files' => '$ drush language-export-translations fr sites/all/translations/fr --groups=default,menu --replace',
),
'aliases' => array(
'langexp',
'language-export',
),
'callback' => 'drush_language_translations_export',
'drupal dependencies' => array(
'locale',
),
);
// Refresh translations.
$commands['language-refresh-translations'] = array(
'description' => "Refresh translations from one or more locale groups.",
'options' => array(
'groups' => array(
'description' => "Comma separated list of locale groups. Defaults to 'all'.",
'value' => 'optional',
),
'delete' => array(
'description' => "Delete left over strings.",
),
),
'examples' => array(
'Refresh strings in all locale groups' => '$ drush language-export-translations fr',
'Refresh strings in specific locale groups, delete unused' => '$ drush language-export-translations fr --groups=default,menu --delete',
),
'aliases' => array(
'language-refresh',
),
'callback' => 'drush_language_translations_refresh',
'drupal dependencies' => array(
'locale',
'i18n_string',
),
);
$commands['language-export-translations-ng'] = array(
'description' => "Export string of a language and textgroup as a .po file",
'arguments' => array(
'langcode' => 'The langcode of the language to exported.',
'.po file' => 'Path to a .po file. Use "-" or /dev/stdout to use standard output.',
),
'examples' => array(
'Export the french menu translation' => 'drush langexp --group="menu" fr menu.fr.po',
),
'options' => array(
'group' => array(
'description' => 'The text group to export. Defaults to "default".',
'value' => 'optional',
),
'status' => array(
'description' => 'The statuses to export, defaults to \'customized\'.' . "\n" . 'This can be a comma-separated list of \'customized\', \'not-customized\', \'not-translated\', or \'all\'.',
'value' => 'optional',
),
'force' => array(
'description' => 'Write file even if no translations. Defaults to 1.',
'value' => 'optional',
),
),
'aliases' => array(
'langexpng',
'language-export-ng',
),
'callback' => 'drush_language_translations_export_ng',
'drupal dependencies' => array(
'locale',
'l10n_update',
),
);
$commands['language-export-all-translations-ng'] = array(
'description' => 'Export all translations to files',
'options' => array(
'langcodes' => array(
'description' => 'The language codes to export. Defaults to all enabled languages.',
'value' => 'optional',
),
'groups' => array(
'description' => "Comma separated list of text groups. If none given, all are exported.",
'value' => 'optional',
),
'file-pattern' => array(
'description' => 'The target file pattern. Defaults to \'../translations/custom/%group.%language.po(t)\' (pot if --status=not-translated is given).' . "\n" . 'Note that this is the only place where this module\'s auto-importing works.',
'value' => 'optional',
),
'status' => array(
'description' => 'The statuses to export, defaults to \'customized\'.' . "\n" . 'This can be a comma-separated list of \'customized\', \'not-customized\', \'not-translated\', or \'all\'.',
'value' => 'optional',
),
'force' => array(
'description' => 'Write file even if no translations. Defaults to 1.',
'value' => 'optional',
),
),
'aliases' => array(
'langexpallng',
'language-export-all-ng',
),
'callback' => 'drush_language_translations_export_all_ng',
'drupal dependencies' => array(
'locale',
'l10n_update',
),
);
return $commands;
}
/**
* Returns a list with enabled locale groups.
*/
function drush_language_translations_groups_info() {
// Print header.
$locale_groups = array(
'**Group**' => '**Label**',
);
// Get enabled system text groups.
$locale_groups += module_invoke_all('locale', 'groups');
// Output.
drush_print_table(drush_key_value_to_array_table($locale_groups));
}
/**
* Import translations to one or more locale groups.
*
* @todo Add language if not existing on site.
*
* @param string $langcode
* The langcode of the language to be exported.
* @param string $path
* Path to a directory with the translation files. Can be a file-name if one
* locale-group is imported.
*
* @option array $groups
* Comma separated list of locale groups. Defaults to 'all'.
* @option bool $replace
* Replace existing translations in the database. Defaults to 'false'.
*
* @return void
*/
function drush_language_translations_import($langcode, $path) {
// Check required arguments.
if (empty($langcode) || empty($path)) {
drush_log(dt('drush language: missing required argument'), 'error');
return;
}
// Parse arguments and options.
$path = _drush_language_translations_get_absolute_path($path);
$language = _drush_language_translations_get_language($langcode);
$groups = explode(',', drush_get_option('groups', 'all'));
$groups = _drush_language_translations_groups_filter($groups);
if (empty($path) || empty($language) || empty($groups)) {
drush_log(dt("drush language: could not parse arguments or groups"), 'error');
return;
}
// If one group is given, it is possible to read from one specific file.
if (count($groups) == 1 && reset($groups) != 'all' && is_file($path)) {
// Execute single file import.
_drush_language_translations_import_file($path, $language, reset($groups));
return;
}
elseif (!is_dir($path)) {
drush_set_error(dt("drush language-export: you must supply a valid directory path." . "\n" . "drush language-export: supplied path = " . $path));
return;
}
// Execution.
foreach ($groups as $group) {
$file_name = $group . "." . $langcode . ".po";
$tmp_file_path = $path . DIRECTORY_SEPARATOR . $file_name;
_drush_language_translations_import_file($tmp_file_path, $language, $group);
}
}
/**
* Export translations from one or more locale groups.
*
* @param string $langcode
* The langcode of the language to be exported.
* @param string $path
* Path to a directory where the translation files should be saved. Can be a
* file-name if one locale-group is exported.
*
* @option array $groups
* Comma separated list of locale groups. Defaults to 'all'.
* @option bool $replace
* Replace existing translations in the file-system. Defaults to 'false'.
* @option bool $sort
* Sorts the generated po file by source and context, defaults to 'true'.
* @option bool $filter
* Removes empty translations from the generated po file, defaults to 'true'.
*
* @return void
* Save a file or prints it to StOut.
*/
function drush_language_translations_export($langcode, $path) {
// Check required arguments.
if (empty($langcode) || empty($path)) {
drush_log(dt('drush language: missing required argument'), 'error');
return;
}
// Parse arguments and options.
$path = _drush_language_translations_get_absolute_path($path);
$language = _drush_language_translations_get_language($langcode);
$groups = explode(',', drush_get_option('groups', 'all'));
$groups = _drush_language_translations_groups_filter($groups);
if (empty($path) || empty($language) || empty($groups)) {
drush_log(dt("drush language: could not parse arguments or groups"), 'error');
return;
}
// If one group is exported, it is possible to save to one file.
// Default behavior is saving to a directory.
if (!is_dir($path)) {
drush_set_error(dt("drush language-export: you must supply a valid directory path." . "\n" . "drush language-export: supplied path = " . $path));
return;
}
// Execution.
foreach ($groups as $group) {
$file_name = $group . "." . $langcode . ".po";
$tmp_file_path = $path . DIRECTORY_SEPARATOR . $file_name;
_drush_language_translations_export_file($tmp_file_path, $language, $group);
}
}
/**
* Refresh translations from one or more locale groups.
*
* @option array $groups
* Comma separated list of locale groups. Defaults to 'all'.
* @option bool $delete
* Delete left over strings.
*
* @return void
*/
function drush_language_translations_refresh() {
// Parse arguments and options.
$groups = explode(',', drush_get_option('groups', 'all'));
$groups = _drush_language_translations_groups_filter($groups);
if (empty($groups)) {
drush_log(dt("drush language: could not parse arguments or groups"), 'error');
return;
}
// Perform batch operations
if (!empty($groups)) {
module_load_include('admin.inc', 'i18n_string');
// Should left over strings be deleted?
$delete = drush_get_option('delete') ? TRUE : FALSE;
$batch = i18n_string_refresh_batch($groups, $delete);
batch_set($batch);
drush_backend_batch_process();
drush_log(dt('Strings refreshed'), 'success');
}
}
/**
* Exports .po file(s).
*
* @see \Drupal\locale\Form\ExportForm::submitForm
*
* @todo Implement \Drupal\locale\Form\ExportForm::buildForm
* @todo This can be simplified once https://www.drupal.org/node/2631584 lands
* in Drupal core.
*
*/
function drush_language_translations_export_ng() {
$args = func_get_args();
if (count($args) < 2) {
return drush_set_error(dt('Usage: drush language-export <langcode> <path_to/file.po>'));
}
// Get arguments and options.
$langcode = array_shift($args);
$file_path_arg = array_shift($args);
$textgroup = drush_get_option('group', 'default');
$force = drush_get_option('force', TRUE);
// Ensure the langcode match an existing language.
$language = _drush_language_translations_get_language($langcode);
if ($language == NULL) {
return drush_set_error(dt('drush language-export: no such language'));
}
// Validate export statuses.
$export_statuses_allowed = [
// internal-value => input-value
'customized' => 'customized',
'not_customized' => 'not-customized',
'not_translated' => 'not-translated',
];
$export_statuses_input = drush_get_option_list('status', 'customized');
$export_statuses_input = array_values($export_statuses_input);
if ($export_statuses_input == [
'all',
]) {
$export_statuses_input = $export_statuses_allowed;
}
$export_statuses_unknown = array_diff($export_statuses_input, $export_statuses_allowed);
if ($export_statuses_unknown) {
$t_args = [
'@options' => implode(', ', $export_statuses_unknown),
];
return drush_set_error(dt('drush language-export: Unknown status options: @options', $t_args));
}
$export_statuses_filtered = array_intersect($export_statuses_allowed, $export_statuses_input);
$export_statuses = array_fill_keys(array_keys($export_statuses_filtered), TRUE);
// Relative path should be relative to cwd(), rather than Drupal root-dir.
if (drush_is_absolute_path($file_path_arg)) {
$file_path = $file_path_arg;
}
else {
$file_path = drush_get_context('DRUSH_DRUPAL_ROOT') . DIRECTORY_SEPARATOR . $file_path_arg;
}
// Check if file_path exists and is writable.
$dir = dirname($file_path);
if (!file_prepare_directory($dir)) {
file_prepare_directory($dir, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY);
}
$reader = new PoDatabaseReader();
if (!method_exists($reader, 'setTextgroup')) {
return drush_set_error(dt('Patch for l10n_update module is needed from https://www.drupal.org/node/2869244.'));
}
$language_name = '';
if ($language != NULL) {
$reader
->setLangcode($language->language);
$reader
->setOptions($export_statuses);
$reader
->setTextgroup($textgroup);
$languages = l10n_update_translatable_language_list();
$language_name = isset($languages[$language->language]) ? $languages[$language->language]->name : '';
}
$item = $reader
->readItem();
if ($item || $force) {
$header = $reader
->getHeader();
$header
->setProjectName(variable_get('site_name'));
$header
->setLanguageName($language_name);
$writer = new PoStreamWriter();
$writer
->setUri($file_path);
$writer
->setHeader($header);
$writer
->open();
if ($item) {
$writer
->writeItem($item);
}
$writer
->writeItems($reader);
$writer
->close();
drush_log('Export complete.', 'success');
}
else {
drush_set_error(dt('Nothing to export.'));
}
}
/**
* Export all translations to files with customizable pattern.
*/
function drush_language_translations_export_all_ng() {
$langcodes = drush_get_option_list('langcode');
if (!$langcodes) {
$languages = l10n_update_translatable_language_list();
$langcodes = array_keys($languages);
}
$textgroups = drush_get_option_list('groups');
if (!$textgroups) {
$textgroups = array_keys(module_invoke_all('locale', 'groups'));
}
$default_file_pattern = drush_get_option('status') === 'not-translated' ? '../translations/custom/%group.%language.pot' : '../translations/custom/%group.%language.po';
$file_pattern = drush_get_option('file-pattern', $default_file_pattern);
$options = [
'status' => drush_get_option('status'),
'force' => drush_get_option('force'),
];
foreach ($langcodes as $langcode) {
foreach ($textgroups as $textgroup) {
$options['group'] = $textgroup;
$file_path_relative = preg_replace(array(
'/%language/u',
'/%group/u',
), array(
$langcode,
$textgroup,
), $file_pattern);
$file_name_absolute = drush_is_absolute_path($file_path_relative) ? $file_path_relative : drush_get_context('DRUSH_DRUPAL_ROOT') . '/' . $file_path_relative;
drush_invoke_process('@self', 'language-export-translations-ng', [
$langcode,
$file_name_absolute,
], $options);
$t_args = [
'!language' => $langcode,
'!file' => $file_path_relative,
];
drush_log(dt('Exported translations for language !language to file !file.', $t_args), 'ok');
}
}
}
/**
* Sort callback for sorting translation strings by source and context.
*
* @param array $a
* Translation string information from _locale_export_get_strings().
* @param array $b
* Translation string information from _locale_export_get_strings().
*
* @return int
*/
function _drush_language_translations_strings_sort($a, $b) {
if ($a['source'] != $b['source']) {
return $a['source'] > $b['source'] ? 1 : -1;
}
if ($a['context'] != $b['context']) {
return $a['context'] > $b['context'] ? 1 : -1;
}
}
/**
* Filter callback for empty translation strings.
*
* @param array $item
* Translation string information from _locale_export_get_strings().
*
* @return bool
*/
function _drush_language_translations_strings_filter($item) {
// Filter string, if no translation is given.
if ($item['translation'] === '') {
return FALSE;
}
return TRUE;
}
/**
* Filters and validates locale groups.
*
* @param array $groups
* Array of locale groups.
* Defaults to 'default'.
* Placeholder 'all' returns all enabled locale_groups.
*
* @return array
* Array with existing locale group.
*/
function _drush_language_translations_groups_filter($groups = array(
'default',
)) {
// Enabled system text groups.
$locale_groups = module_invoke_all('locale', 'groups');
// 'all' placeholder.
if ($groups == array(
'all',
)) {
$groups = array_keys($locale_groups);
}
// Copy values to keys for better parsing.
$groups = array_combine($groups, $groups);
// Filter un-existing groups.
foreach ($groups as $group) {
if (!in_array($group, array_keys($locale_groups))) {
unset($groups[$group]);
// Print a readable error message.
$locale_groups_readable = implode(", ", array_keys($locale_groups));
drush_log(dt("drush language: locale group '" . $group . "' is not valid. Available groups are: " . $locale_groups_readable), 'warning');
}
}
return $groups;
}
/**
* Import function for language files.
*
* @param string $file_path
* Absolute file path.
* @param string $language
* Language code.
* @param string $group
* Text group to export (field, default).
*
* @option bool $replace
* Replace existing translations in the file-system. Defaults to 'false'.
*
* @return void
* Imports .po file to database.
*/
function _drush_language_translations_import_file($file_path = '-', $language, $group) {
// Parse $replace option.
drush_get_option('replace') ? $mode = LOCALE_IMPORT_OVERWRITE : ($mode = LOCALE_IMPORT_KEEP);
// Ensure we have the file intended for upload
if (file_exists($file_path)) {
// Construct fake file object
$file = new stdClass();
$file->uid = 1;
$file->status = 0;
$file->filename = trim(drupal_basename($file_path), '.');
$file->uri = $file_path;
// Now import strings into the language
if ($return = _locale_import_po($file, $language->language, $mode, $group) == FALSE) {
$variables = array(
'%filename' => $file->filename,
);
drush_log(dt('The translation import of %filename failed.', $variables), 'error');
watchdog('locale', 'The translation import of %filename failed.', $variables, WATCHDOG_ERROR);
}
else {
drush_log(dt('drush language-import: ' . $file_path), 'success');
}
}
else {
$variables = array(
'!filepath' => $file_path,
);
drush_log(dt('File to import at !filepath not found.', $variables), 'error');
}
}
/**
* Saving function for language exports.
*
* @param string $file_path
* Absolute file path.
* @param string $language
* Language code.
* @param string $group
* Text group to export (fields, default).
*
* @option bool $replace
* Replace existing translations in the file-system. Defaults to 'false'.
* @option bool $sort
* Sorts the generated po file by source and context, defaults to 'true'.
* @option bool $filter
* Removes empty translations from the generated po file, defaults to 'true'.
*
* @return void
* Saves the export file to file system.
*/
function _drush_language_translations_export_file($file_path = '-', $language, $group) {
// Don't override destination file
// Unlike file_exists(), this condition allow the use of /dev/stdin.
if (!drush_get_option('replace') && (is_file($file_path) || is_dir($file_path))) {
drush_log(dt('drush language-export: will not override destination file: ' . $file_path), 'warning');
return;
}
$strings = _locale_export_get_strings($language, $group);
// In the case the filter option is given, we remove all empty translations.
if (drush_get_option('filter', TRUE)) {
$strings = array_filter($strings, '_drush_language_translations_strings_filter');
}
// In case the sort option is given, we sort the strings by source string and
// context.
if (drush_get_option('sort', TRUE)) {
uasort($strings, '_drush_language_translations_strings_sort');
}
if ($file_path == '-') {
echo _locale_export_po_generate($language, $strings);
}
else {
file_put_contents($file_path, _locale_export_po_generate($language, $strings));
drush_log(dt('drush language-export: ' . $file_path), 'success');
}
}
/**
* Returns an absolute path.
*
* @param string $path
* Absolute or relative (to drupal root) path.
*
* @return string
* Absolute path.
*/
function _drush_language_translations_get_absolute_path($path) {
// relative path should be relative to cwd(), rather than Drupal root-dir
$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
if (drush_is_absolute_path($path)) {
return $path;
}
elseif (valid_url($path, TRUE)) {
// Get name of this file.
$parsed_url = drupal_parse_url($path);
$parsed_path = explode('/', $parsed_url['path']);
$filename = end($parsed_path);
$destination = drush_find_tmp() . DIRECTORY_SEPARATOR . $filename;
// Download file content
$content = file_get_contents($path);
// Save file to unmanaged file. It will be saved to managed temporary later.
return file_unmanaged_save_data($content, $destination);
}
else {
return $drupal_root . DIRECTORY_SEPARATOR . $path;
}
}
/**
* Returns a language object.
*
* @param string $langcode
* Langcode.
*
* @return object
* Language object.
*/
function _drush_language_translations_get_language($langcode) {
// Ensure the langcode match an existing language.
drupal_static_reset('language_list');
$languages = language_list('language');
// Return language object.
if (isset($languages[$langcode])) {
return $languages[$langcode];
}
drush_log(dt("drush language: langcode '" . $langcode . "' not installed . "), 'error');
}
Functions
Name | Description |
---|---|
drush_language_translations_export | Export translations from one or more locale groups. |
drush_language_translations_export_all_ng | Export all translations to files with customizable pattern. |
drush_language_translations_export_ng | Exports .po file(s). |
drush_language_translations_groups_info | Returns a list with enabled locale groups. |
drush_language_translations_import | Import translations to one or more locale groups. |
drush_language_translations_refresh | Refresh translations from one or more locale groups. |
language_translations_drush_command | Implements of hook_drush_command(). |
_drush_language_translations_export_file | Saving function for language exports. |
_drush_language_translations_get_absolute_path | Returns an absolute path. |
_drush_language_translations_get_language | Returns a language object. |
_drush_language_translations_groups_filter | Filters and validates locale groups. |
_drush_language_translations_import_file | Import function for language files. |
_drush_language_translations_strings_filter | Filter callback for empty translation strings. |
_drush_language_translations_strings_sort | Sort callback for sorting translation strings by source and context. |