Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -5,7 +5,7 @@ javascript:
|
|||
translation:
|
||||
use_source: remote_and_local
|
||||
default_filename: '%project-%version.%language.po'
|
||||
default_server_pattern: 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po'
|
||||
default_server_pattern: 'https://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po'
|
||||
overwrite_customized: false
|
||||
overwrite_not_customized: true
|
||||
update_interval_days: 0
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
table-layout: fixed;
|
||||
}
|
||||
.locale-translate-edit-form td {
|
||||
vertical-align: top
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.locale-translate-edit-form tr.changed {
|
||||
|
@ -49,7 +49,7 @@
|
|||
}
|
||||
|
||||
.locale-translate-filter-form .form-wrapper {
|
||||
margin-bottom:0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.locale-translate-edit-form table.changed {
|
||||
|
@ -68,18 +68,16 @@
|
|||
#locale-translation-status-form th.title {
|
||||
width: 25%;
|
||||
}
|
||||
#locale-translation-status-form th.description {
|
||||
}
|
||||
#locale-translation-status-form td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.locale-translation-update__wrapper {
|
||||
background: transparent url(../../../misc/menu-collapsed.png) left .6em no-repeat;
|
||||
background: transparent url(../../../misc/menu-collapsed.png) left 0.6em no-repeat;
|
||||
margin-left: -12px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.expanded .locale-translation-update__wrapper {
|
||||
background: transparent url(../../../misc/menu-expanded.png) left .6em no-repeat;
|
||||
background: transparent url(../../../misc/menu-expanded.png) left 0.6em no-repeat;
|
||||
}
|
||||
#locale-translation-status-form .description {
|
||||
cursor: pointer;
|
||||
|
@ -125,6 +123,7 @@
|
|||
margin: 0 0 0.25em 1.5em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 40em) {
|
||||
#locale-translation-status-form th.title {
|
||||
width: 20%;
|
||||
|
|
129
web/core/modules/locale/locale.admin.es6.js
Normal file
129
web/core/modules/locale/locale.admin.es6.js
Normal file
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* @file
|
||||
* Locale admin behavior.
|
||||
*/
|
||||
|
||||
(function($, Drupal) {
|
||||
/**
|
||||
* Marks changes of translations.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior to show the user if translations has changed.
|
||||
* @prop {Drupal~behaviorDetach} detach
|
||||
* Detach behavior to show the user if translations has changed.
|
||||
*/
|
||||
Drupal.behaviors.localeTranslateDirty = {
|
||||
attach() {
|
||||
const $form = $('#locale-translate-edit-form').once(
|
||||
'localetranslatedirty',
|
||||
);
|
||||
if ($form.length) {
|
||||
// Display a notice if any row changed.
|
||||
$form.one('formUpdated.localeTranslateDirty', 'table', function() {
|
||||
const $marker = $(
|
||||
Drupal.theme('localeTranslateChangedWarning'),
|
||||
).hide();
|
||||
$(this)
|
||||
.addClass('changed')
|
||||
.before($marker);
|
||||
$marker.fadeIn('slow');
|
||||
});
|
||||
// Highlight changed row.
|
||||
$form.on('formUpdated.localeTranslateDirty', 'tr', function() {
|
||||
const $row = $(this);
|
||||
const $rowToMark = $row.once('localemark');
|
||||
const marker = Drupal.theme('localeTranslateChangedMarker');
|
||||
|
||||
$row.addClass('changed');
|
||||
// Add an asterisk only once if row changed.
|
||||
if ($rowToMark.length) {
|
||||
$rowToMark.find('td:first-child .js-form-item').append(marker);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
detach(context, settings, trigger) {
|
||||
if (trigger === 'unload') {
|
||||
const $form = $('#locale-translate-edit-form').removeOnce(
|
||||
'localetranslatedirty',
|
||||
);
|
||||
if ($form.length) {
|
||||
$form.off('formUpdated.localeTranslateDirty');
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Show/hide the description details on Available translation updates page.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior for toggling details on the translation update page.
|
||||
*/
|
||||
Drupal.behaviors.hideUpdateInformation = {
|
||||
attach(context, settings) {
|
||||
const $table = $('#locale-translation-status-form').once(
|
||||
'expand-updates',
|
||||
);
|
||||
if ($table.length) {
|
||||
const $tbodies = $table.find('tbody');
|
||||
|
||||
// Open/close the description details by toggling a tr class.
|
||||
$tbodies.on('click keydown', '.description', function(e) {
|
||||
if (e.keyCode && (e.keyCode !== 13 && e.keyCode !== 32)) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
const $tr = $(this).closest('tr');
|
||||
|
||||
$tr.toggleClass('expanded');
|
||||
|
||||
// Change screen reader text.
|
||||
$tr.find('.locale-translation-update__prefix').text(() => {
|
||||
if ($tr.hasClass('expanded')) {
|
||||
return Drupal.t('Hide description');
|
||||
}
|
||||
|
||||
return Drupal.t('Show description');
|
||||
});
|
||||
});
|
||||
$table.find('.requirements, .links').hide();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
$.extend(
|
||||
Drupal.theme,
|
||||
/** @lends Drupal.theme */ {
|
||||
/**
|
||||
* Creates markup for a changed translation marker.
|
||||
*
|
||||
* @return {string}
|
||||
* Markup for the marker.
|
||||
*/
|
||||
localeTranslateChangedMarker() {
|
||||
return `<abbr class="warning ajax-changed" title="${Drupal.t(
|
||||
'Changed',
|
||||
)}">*</abbr>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates markup for the translation changed warning.
|
||||
*
|
||||
* @return {string}
|
||||
* Markup for the warning.
|
||||
*/
|
||||
localeTranslateChangedWarning() {
|
||||
return `<div class="clearfix messages messages--warning">${Drupal.theme(
|
||||
'localeTranslateChangedMarker',
|
||||
)} ${Drupal.t(
|
||||
'Changes made in this table will not be saved until the form is submitted.',
|
||||
)}</div>`;
|
||||
},
|
||||
},
|
||||
);
|
||||
})(jQuery, Drupal);
|
|
@ -1,47 +1,35 @@
|
|||
/**
|
||||
* @file
|
||||
* Locale admin behavior.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Marks changes of translations.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior to show the user if translations has changed.
|
||||
* @prop {Drupal~behaviorDetach} detach
|
||||
* Detach behavior to show the user if translations has changed.
|
||||
*/
|
||||
Drupal.behaviors.localeTranslateDirty = {
|
||||
attach: function () {
|
||||
attach: function attach() {
|
||||
var $form = $('#locale-translate-edit-form').once('localetranslatedirty');
|
||||
if ($form.length) {
|
||||
// Display a notice if any row changed.
|
||||
$form.one('formUpdated.localeTranslateDirty', 'table', function () {
|
||||
var $marker = $(Drupal.theme('localeTranslateChangedWarning')).hide();
|
||||
$(this).addClass('changed').before($marker);
|
||||
$marker.fadeIn('slow');
|
||||
});
|
||||
// Highlight changed row.
|
||||
|
||||
$form.on('formUpdated.localeTranslateDirty', 'tr', function () {
|
||||
var $row = $(this);
|
||||
var $rowToMark = $row.once('localemark');
|
||||
var marker = Drupal.theme('localeTranslateChangedMarker');
|
||||
|
||||
$row.addClass('changed');
|
||||
// Add an asterisk only once if row changed.
|
||||
|
||||
if ($rowToMark.length) {
|
||||
$rowToMark.find('td:first-child .js-form-item').append(marker);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
detach: function (context, settings, trigger) {
|
||||
detach: function detach(context, settings, trigger) {
|
||||
if (trigger === 'unload') {
|
||||
var $form = $('#locale-translate-edit-form').removeOnce('localetranslatedirty');
|
||||
if ($form.length) {
|
||||
|
@ -51,23 +39,14 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show/hide the description details on Available translation updates page.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior for toggling details on the translation update page.
|
||||
*/
|
||||
Drupal.behaviors.hideUpdateInformation = {
|
||||
attach: function (context, settings) {
|
||||
attach: function attach(context, settings) {
|
||||
var $table = $('#locale-translation-status-form').once('expand-updates');
|
||||
if ($table.length) {
|
||||
var $tbodies = $table.find('tbody');
|
||||
|
||||
// Open/close the description details by toggling a tr class.
|
||||
$tbodies.on('click keydown', '.description', function (e) {
|
||||
if (e.keyCode && (e.keyCode !== 13 && e.keyCode !== 32)) {
|
||||
if (e.keyCode && e.keyCode !== 13 && e.keyCode !== 32) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
@ -75,14 +54,12 @@
|
|||
|
||||
$tr.toggleClass('expanded');
|
||||
|
||||
// Change screen reader text.
|
||||
$tr.find('.locale-translation-update__prefix').text(function () {
|
||||
if ($tr.hasClass('expanded')) {
|
||||
return Drupal.t('Hide description');
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Show description');
|
||||
}
|
||||
|
||||
return Drupal.t('Show description');
|
||||
});
|
||||
});
|
||||
$table.find('.requirements, .links').hide();
|
||||
|
@ -90,27 +67,12 @@
|
|||
}
|
||||
};
|
||||
|
||||
$.extend(Drupal.theme, /** @lends Drupal.theme */{
|
||||
|
||||
/**
|
||||
* Creates markup for a changed translation marker.
|
||||
*
|
||||
* @return {string}
|
||||
* Markup for the marker.
|
||||
*/
|
||||
localeTranslateChangedMarker: function () {
|
||||
$.extend(Drupal.theme, {
|
||||
localeTranslateChangedMarker: function localeTranslateChangedMarker() {
|
||||
return '<abbr class="warning ajax-changed" title="' + Drupal.t('Changed') + '">*</abbr>';
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates markup for the translation changed warning.
|
||||
*
|
||||
* @return {string}
|
||||
* Markup for the warning.
|
||||
*/
|
||||
localeTranslateChangedWarning: function () {
|
||||
localeTranslateChangedWarning: function localeTranslateChangedWarning() {
|
||||
return '<div class="clearfix messages messages--warning">' + Drupal.theme('localeTranslateChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>';
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery, Drupal);
|
||||
})(jQuery, Drupal);
|
|
@ -104,22 +104,22 @@ function locale_translation_batch_status_finished($success, $results) {
|
|||
else {
|
||||
$message = \Drupal::translation()->formatPlural(count($results['failed_files']), 'One translation files could not be checked. See the log for details.', '@count translation files could not be checked. See the log for details.');
|
||||
}
|
||||
drupal_set_message($message, 'error');
|
||||
\Drupal::messenger()->addError($message);
|
||||
}
|
||||
if (isset($results['files'])) {
|
||||
drupal_set_message(\Drupal::translation()->formatPlural(
|
||||
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural(
|
||||
count($results['files']),
|
||||
'Checked available interface translation updates for one project.',
|
||||
'Checked available interface translation updates for @count projects.'
|
||||
));
|
||||
}
|
||||
if (!isset($results['failed_files']) && !isset($results['files'])) {
|
||||
drupal_set_message(t('Nothing to check.'));
|
||||
\Drupal::messenger()->addStatus(t('Nothing to check.'));
|
||||
}
|
||||
\Drupal::state()->set('locale.translation_last_checked', REQUEST_TIME);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('An error occurred trying to check available interface translation updates.'), 'error');
|
||||
\Drupal::messenger()->addError(t('An error occurred trying to check available interface translation updates.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,11 +238,13 @@ function locale_translation_http_check($uri) {
|
|||
$logger = \Drupal::logger('locale');
|
||||
try {
|
||||
$actual_uri = NULL;
|
||||
$response = \Drupal::service('http_client_factory')->fromOptions(['allow_redirects' => [
|
||||
'on_redirect' => function(RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) {
|
||||
$actual_uri = (string) $request_uri;
|
||||
}
|
||||
]])->head($uri);
|
||||
$response = \Drupal::service('http_client_factory')->fromOptions([
|
||||
'allow_redirects' => [
|
||||
'on_redirect' => function (RequestInterface $request, ResponseInterface $response, UriInterface $request_uri) use (&$actual_uri) {
|
||||
$actual_uri = (string) $request_uri;
|
||||
},
|
||||
],
|
||||
])->head($uri);
|
||||
$result = [];
|
||||
|
||||
// Return the effective URL if it differs from the requested.
|
||||
|
@ -290,7 +292,7 @@ function locale_translation_http_check($uri) {
|
|||
* File object if download was successful. FALSE on failure.
|
||||
*/
|
||||
function locale_translation_download_source($source_file, $directory = 'temporary://') {
|
||||
if ($uri = system_retrieve_file($source_file->uri, $directory)) {
|
||||
if ($uri = system_retrieve_file($source_file->uri, $directory, FALSE, FILE_EXISTS_REPLACE)) {
|
||||
$file = clone($source_file);
|
||||
$file->type = LOCALE_TRANSLATION_LOCAL;
|
||||
$file->uri = $uri;
|
||||
|
|
38
web/core/modules/locale/locale.bulk.es6.js
Normal file
38
web/core/modules/locale/locale.bulk.es6.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @file
|
||||
* Locale behavior.
|
||||
*/
|
||||
|
||||
(function($, Drupal) {
|
||||
/**
|
||||
* Select the language code of an imported file based on its filename.
|
||||
*
|
||||
* This only works if the file name ends with "LANGCODE.po".
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior for preselecting language code based on filename.
|
||||
*/
|
||||
Drupal.behaviors.importLanguageCodeSelector = {
|
||||
attach(context, settings) {
|
||||
const $form = $('#locale-translate-import-form').once('autodetect-lang');
|
||||
if ($form.length) {
|
||||
const $langcode = $form.find('.langcode-input');
|
||||
$form.find('.file-import-input').on('change', function() {
|
||||
// If the filename is fully the language code or the filename
|
||||
// ends with a language code, pre-select that one.
|
||||
const matches = $(this)
|
||||
.val()
|
||||
.match(/([^.][.]*)([\w-]+)\.po$/);
|
||||
if (
|
||||
matches &&
|
||||
$langcode.find(`option[value="${matches[2]}"]`).length
|
||||
) {
|
||||
$langcode.val(matches[2]);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
})(jQuery, Drupal);
|
|
@ -200,7 +200,7 @@ function locale_translate_batch_import($file, array $options, &$context) {
|
|||
try {
|
||||
if (empty($context['sandbox'])) {
|
||||
$context['sandbox']['parse_state'] = [
|
||||
'filesize' => filesize(drupal_realpath($file->uri)),
|
||||
'filesize' => filesize(\Drupal::service('file_system')->realpath($file->uri)),
|
||||
'chunk_size' => 200,
|
||||
'seek' => 0,
|
||||
];
|
||||
|
@ -372,7 +372,7 @@ function locale_translate_batch_finished($success, array $results) {
|
|||
else {
|
||||
$message = \Drupal::translation()->formatPlural(count($results['failed_files']), 'One translation file could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.');
|
||||
}
|
||||
drupal_set_message($message, 'error');
|
||||
\Drupal::messenger()->addError($message);
|
||||
}
|
||||
if (isset($results['files'])) {
|
||||
$skipped_files = [];
|
||||
|
@ -389,7 +389,7 @@ function locale_translate_batch_finished($success, array $results) {
|
|||
}
|
||||
}
|
||||
}
|
||||
drupal_set_message(\Drupal::translation()->formatPlural(count($results['files']),
|
||||
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural(count($results['files']),
|
||||
'One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
|
||||
'@count translation files imported. %number translations were added, %update translations were updated and %delete translations were removed.',
|
||||
['%number' => $additions, '%update' => $updates, '%delete' => $deletes]
|
||||
|
@ -403,7 +403,7 @@ function locale_translate_batch_finished($success, array $results) {
|
|||
else {
|
||||
$message = \Drupal::translation()->formatPlural($skips, 'One translation string was skipped because of disallowed or malformed HTML. See the log for details.', '@count translation strings were skipped because of disallowed or malformed HTML. See the log for details.');
|
||||
}
|
||||
drupal_set_message($message, 'warning');
|
||||
\Drupal::messenger()->addWarning($message);
|
||||
$logger->warning('@count disallowed HTML string(s) in files: @files.', ['@count' => $skips, '@files' => implode(',', $skipped_files)]);
|
||||
}
|
||||
}
|
||||
|
@ -630,11 +630,11 @@ function locale_config_batch_finished($success, array $results) {
|
|||
if ($success) {
|
||||
$configuration = isset($results['stats']['config']) ? $results['stats']['config'] : 0;
|
||||
if ($configuration) {
|
||||
drupal_set_message(t('The configuration was successfully updated. There are %number configuration objects updated.', ['%number' => $configuration]));
|
||||
\Drupal::messenger()->addStatus(t('The configuration was successfully updated. There are %number configuration objects updated.', ['%number' => $configuration]));
|
||||
\Drupal::logger('locale')->notice('The configuration was successfully updated. %number configuration objects updated.', ['%number' => $configuration]);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('No configuration objects have been updated.'));
|
||||
\Drupal::messenger()->addStatus(t('No configuration objects have been updated.'));
|
||||
\Drupal::logger('locale')->warning('No configuration objects have been updated.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,23 @@
|
|||
/**
|
||||
* @file
|
||||
* Locale behavior.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Select the language code of an imported file based on its filename.
|
||||
*
|
||||
* This only works if the file name ends with "LANGCODE.po".
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches behavior for preselecting language code based on filename.
|
||||
*/
|
||||
Drupal.behaviors.importLanguageCodeSelector = {
|
||||
attach: function (context, settings) {
|
||||
attach: function attach(context, settings) {
|
||||
var $form = $('#locale-translate-import-form').once('autodetect-lang');
|
||||
if ($form.length) {
|
||||
var $langcode = $form.find('.langcode-input');
|
||||
$form.find('.file-import-input')
|
||||
.on('change', function () {
|
||||
// If the filename is fully the language code or the filename
|
||||
// ends with a language code, pre-select that one.
|
||||
var matches = $(this).val().match(/([^.][\.]*)([\w-]+)\.po$/);
|
||||
if (matches && $langcode.find('option[value="' + matches[2] + '"]').length) {
|
||||
$langcode.val(matches[2]);
|
||||
}
|
||||
});
|
||||
$form.find('.file-import-input').on('change', function () {
|
||||
var matches = $(this).val().match(/([^.][.]*)([\w-]+)\.po$/);
|
||||
if (matches && $langcode.find('option[value="' + matches[2] + '"]').length) {
|
||||
$langcode.val(matches[2]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
||||
})(jQuery, Drupal);
|
|
@ -161,7 +161,7 @@ function locale_translation_default_translation_server() {
|
|||
// An additional check is required here. During the upgrade process
|
||||
// \Drupal::config()->get() returns NULL. We use the defined value as
|
||||
// fallback.
|
||||
$pattern = $pattern ? $pattern : LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN;
|
||||
$pattern = $pattern ? $pattern : LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN;
|
||||
|
||||
return [
|
||||
'pattern' => $pattern,
|
||||
|
|
87
web/core/modules/locale/locale.datepicker.es6.js
Normal file
87
web/core/modules/locale/locale.datepicker.es6.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @file
|
||||
* Datepicker JavaScript for the Locale module.
|
||||
*/
|
||||
|
||||
(function($, Drupal, drupalSettings) {
|
||||
/**
|
||||
* Attaches language support to the jQuery UI datepicker component.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.localeDatepicker = {
|
||||
attach(context, settings) {
|
||||
// This code accesses drupalSettings and localized strings via Drupal.t().
|
||||
// So this code should run after these are initialized. By placing it in an
|
||||
// attach behavior this is assured.
|
||||
$.datepicker.regional['drupal-locale'] = $.extend(
|
||||
{
|
||||
closeText: Drupal.t('Done'),
|
||||
prevText: Drupal.t('Prev'),
|
||||
nextText: Drupal.t('Next'),
|
||||
currentText: Drupal.t('Today'),
|
||||
monthNames: [
|
||||
Drupal.t('January', {}, { context: 'Long month name' }),
|
||||
Drupal.t('February', {}, { context: 'Long month name' }),
|
||||
Drupal.t('March', {}, { context: 'Long month name' }),
|
||||
Drupal.t('April', {}, { context: 'Long month name' }),
|
||||
Drupal.t('May', {}, { context: 'Long month name' }),
|
||||
Drupal.t('June', {}, { context: 'Long month name' }),
|
||||
Drupal.t('July', {}, { context: 'Long month name' }),
|
||||
Drupal.t('August', {}, { context: 'Long month name' }),
|
||||
Drupal.t('September', {}, { context: 'Long month name' }),
|
||||
Drupal.t('October', {}, { context: 'Long month name' }),
|
||||
Drupal.t('November', {}, { context: 'Long month name' }),
|
||||
Drupal.t('December', {}, { context: 'Long month name' }),
|
||||
],
|
||||
monthNamesShort: [
|
||||
Drupal.t('Jan'),
|
||||
Drupal.t('Feb'),
|
||||
Drupal.t('Mar'),
|
||||
Drupal.t('Apr'),
|
||||
Drupal.t('May'),
|
||||
Drupal.t('Jun'),
|
||||
Drupal.t('Jul'),
|
||||
Drupal.t('Aug'),
|
||||
Drupal.t('Sep'),
|
||||
Drupal.t('Oct'),
|
||||
Drupal.t('Nov'),
|
||||
Drupal.t('Dec'),
|
||||
],
|
||||
dayNames: [
|
||||
Drupal.t('Sunday'),
|
||||
Drupal.t('Monday'),
|
||||
Drupal.t('Tuesday'),
|
||||
Drupal.t('Wednesday'),
|
||||
Drupal.t('Thursday'),
|
||||
Drupal.t('Friday'),
|
||||
Drupal.t('Saturday'),
|
||||
],
|
||||
dayNamesShort: [
|
||||
Drupal.t('Sun'),
|
||||
Drupal.t('Mon'),
|
||||
Drupal.t('Tue'),
|
||||
Drupal.t('Wed'),
|
||||
Drupal.t('Thu'),
|
||||
Drupal.t('Fri'),
|
||||
Drupal.t('Sat'),
|
||||
],
|
||||
dayNamesMin: [
|
||||
Drupal.t('Su'),
|
||||
Drupal.t('Mo'),
|
||||
Drupal.t('Tu'),
|
||||
Drupal.t('We'),
|
||||
Drupal.t('Th'),
|
||||
Drupal.t('Fr'),
|
||||
Drupal.t('Sa'),
|
||||
],
|
||||
dateFormat: Drupal.t('mm/dd/yy'),
|
||||
firstDay: 0,
|
||||
isRTL: 0,
|
||||
},
|
||||
drupalSettings.jquery.ui.datepicker,
|
||||
);
|
||||
$.datepicker.setDefaults($.datepicker.regional['drupal-locale']);
|
||||
},
|
||||
};
|
||||
})(jQuery, Drupal, drupalSettings);
|
|
@ -1,82 +1,23 @@
|
|||
/**
|
||||
* @file
|
||||
* Datepicker JavaScript for the Locale module.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Attaches language support to the jQuery UI datepicker component.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*/
|
||||
Drupal.behaviors.localeDatepicker = {
|
||||
attach: function (context, settings) {
|
||||
// This code accesses drupalSettings and localized strings via Drupal.t().
|
||||
// So this code should run after these are initialized. By placing it in an
|
||||
// attach behavior this is assured.
|
||||
attach: function attach(context, settings) {
|
||||
$.datepicker.regional['drupal-locale'] = $.extend({
|
||||
closeText: Drupal.t('Done'),
|
||||
prevText: Drupal.t('Prev'),
|
||||
nextText: Drupal.t('Next'),
|
||||
currentText: Drupal.t('Today'),
|
||||
monthNames: [
|
||||
Drupal.t('January', {}, {context: 'Long month name'}),
|
||||
Drupal.t('February', {}, {context: 'Long month name'}),
|
||||
Drupal.t('March', {}, {context: 'Long month name'}),
|
||||
Drupal.t('April', {}, {context: 'Long month name'}),
|
||||
Drupal.t('May', {}, {context: 'Long month name'}),
|
||||
Drupal.t('June', {}, {context: 'Long month name'}),
|
||||
Drupal.t('July', {}, {context: 'Long month name'}),
|
||||
Drupal.t('August', {}, {context: 'Long month name'}),
|
||||
Drupal.t('September', {}, {context: 'Long month name'}),
|
||||
Drupal.t('October', {}, {context: 'Long month name'}),
|
||||
Drupal.t('November', {}, {context: 'Long month name'}),
|
||||
Drupal.t('December', {}, {context: 'Long month name'})
|
||||
],
|
||||
monthNamesShort: [
|
||||
Drupal.t('Jan'),
|
||||
Drupal.t('Feb'),
|
||||
Drupal.t('Mar'),
|
||||
Drupal.t('Apr'),
|
||||
Drupal.t('May'),
|
||||
Drupal.t('Jun'),
|
||||
Drupal.t('Jul'),
|
||||
Drupal.t('Aug'),
|
||||
Drupal.t('Sep'),
|
||||
Drupal.t('Oct'),
|
||||
Drupal.t('Nov'),
|
||||
Drupal.t('Dec')
|
||||
],
|
||||
dayNames: [
|
||||
Drupal.t('Sunday'),
|
||||
Drupal.t('Monday'),
|
||||
Drupal.t('Tuesday'),
|
||||
Drupal.t('Wednesday'),
|
||||
Drupal.t('Thursday'),
|
||||
Drupal.t('Friday'),
|
||||
Drupal.t('Saturday')
|
||||
],
|
||||
dayNamesShort: [
|
||||
Drupal.t('Sun'),
|
||||
Drupal.t('Mon'),
|
||||
Drupal.t('Tue'),
|
||||
Drupal.t('Wed'),
|
||||
Drupal.t('Thu'),
|
||||
Drupal.t('Fri'),
|
||||
Drupal.t('Sat')
|
||||
],
|
||||
dayNamesMin: [
|
||||
Drupal.t('Su'),
|
||||
Drupal.t('Mo'),
|
||||
Drupal.t('Tu'),
|
||||
Drupal.t('We'),
|
||||
Drupal.t('Th'),
|
||||
Drupal.t('Fr'),
|
||||
Drupal.t('Sa')
|
||||
],
|
||||
monthNames: [Drupal.t('January', {}, { context: 'Long month name' }), Drupal.t('February', {}, { context: 'Long month name' }), Drupal.t('March', {}, { context: 'Long month name' }), Drupal.t('April', {}, { context: 'Long month name' }), Drupal.t('May', {}, { context: 'Long month name' }), Drupal.t('June', {}, { context: 'Long month name' }), Drupal.t('July', {}, { context: 'Long month name' }), Drupal.t('August', {}, { context: 'Long month name' }), Drupal.t('September', {}, { context: 'Long month name' }), Drupal.t('October', {}, { context: 'Long month name' }), Drupal.t('November', {}, { context: 'Long month name' }), Drupal.t('December', {}, { context: 'Long month name' })],
|
||||
monthNamesShort: [Drupal.t('Jan'), Drupal.t('Feb'), Drupal.t('Mar'), Drupal.t('Apr'), Drupal.t('May'), Drupal.t('Jun'), Drupal.t('Jul'), Drupal.t('Aug'), Drupal.t('Sep'), Drupal.t('Oct'), Drupal.t('Nov'), Drupal.t('Dec')],
|
||||
dayNames: [Drupal.t('Sunday'), Drupal.t('Monday'), Drupal.t('Tuesday'), Drupal.t('Wednesday'), Drupal.t('Thursday'), Drupal.t('Friday'), Drupal.t('Saturday')],
|
||||
dayNamesShort: [Drupal.t('Sun'), Drupal.t('Mon'), Drupal.t('Tue'), Drupal.t('Wed'), Drupal.t('Thu'), Drupal.t('Fri'), Drupal.t('Sat')],
|
||||
dayNamesMin: [Drupal.t('Su'), Drupal.t('Mo'), Drupal.t('Tu'), Drupal.t('We'), Drupal.t('Th'), Drupal.t('Fr'), Drupal.t('Sa')],
|
||||
dateFormat: Drupal.t('mm/dd/yy'),
|
||||
firstDay: 0,
|
||||
isRTL: 0
|
||||
|
@ -84,5 +25,4 @@
|
|||
$.datepicker.setDefaults($.datepicker.regional['drupal-locale']);
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
})(jQuery, Drupal, drupalSettings);
|
|
@ -6,5 +6,5 @@ package: Multilingual
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- language
|
||||
- file
|
||||
- drupal:language
|
||||
- drupal:file
|
||||
|
|
|
@ -111,7 +111,8 @@ function locale_schema() {
|
|||
'customized' => [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0, // LOCALE_NOT_CUSTOMIZED
|
||||
// LOCALE_NOT_CUSTOMIZED
|
||||
'default' => 0,
|
||||
'description' => 'Boolean indicating whether the translation is custom to this site.',
|
||||
],
|
||||
],
|
||||
|
@ -259,7 +260,7 @@ function locale_requirements($phase) {
|
|||
if ($available_updates || $untranslated) {
|
||||
if ($available_updates) {
|
||||
$requirements['locale_translation'] = [
|
||||
'title' => 'Translation update status',
|
||||
'title' => t('Translation update status'),
|
||||
'value' => \Drupal::l(t('Updates available'), new Url('locale.translate_status')),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
'description' => t('Updates available for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $available_updates), ':updates' => \Drupal::url('locale.translate_status')]),
|
||||
|
@ -267,7 +268,7 @@ function locale_requirements($phase) {
|
|||
}
|
||||
else {
|
||||
$requirements['locale_translation'] = [
|
||||
'title' => 'Translation update status',
|
||||
'title' => t('Translation update status'),
|
||||
'value' => t('Missing translations'),
|
||||
'severity' => REQUIREMENT_INFO,
|
||||
'description' => t('Missing translations for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $untranslated), ':updates' => \Drupal::url('locale.translate_status')]),
|
||||
|
@ -276,7 +277,7 @@ function locale_requirements($phase) {
|
|||
}
|
||||
else {
|
||||
$requirements['locale_translation'] = [
|
||||
'title' => 'Translation update status',
|
||||
'title' => t('Translation update status'),
|
||||
'value' => t('Up to date'),
|
||||
'severity' => REQUIREMENT_OK,
|
||||
];
|
||||
|
@ -284,7 +285,7 @@ function locale_requirements($phase) {
|
|||
}
|
||||
else {
|
||||
$requirements['locale_translation'] = [
|
||||
'title' => 'Translation update status',
|
||||
'title' => t('Translation update status'),
|
||||
'value' => \Drupal::l(t('Can not determine status'), new Url('locale.translate_status')),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
'description' => t('No translation status is available. See the <a href=":updates">Available translation updates</a> page for more information.', [':updates' => \Drupal::url('locale.translate_status')]),
|
||||
|
@ -303,3 +304,15 @@ function locale_update_8300() {
|
|||
// the new key value collection.
|
||||
\Drupal::state()->delete('locale.translation_status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update default server pattern value to use https.
|
||||
*/
|
||||
function locale_update_8500() {
|
||||
$update_url = \Drupal::config('locale.settings')->get('translation.default_server_pattern');
|
||||
if ($update_url == 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po') {
|
||||
\Drupal::configFactory()->getEditable('locale.settings')
|
||||
->set('translation.default_server_pattern', 'https://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po')
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,6 @@ translations:
|
|||
# any time.
|
||||
js:
|
||||
# This file does not actually exist; it's a placeholder file that will be
|
||||
# overriden by locale_js_alter(), to use the file that contains the actual
|
||||
# overridden by locale_js_alter(), to use the file that contains the actual
|
||||
# translations, for the language used in the current request.
|
||||
locale.translation.js: {}
|
||||
|
|
|
@ -99,7 +99,7 @@ const LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL = 'remote_and_local';
|
|||
*
|
||||
* @see locale_translation_default_translation_server().
|
||||
*/
|
||||
const LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN = 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
|
||||
const LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN = 'https://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
|
||||
|
||||
/**
|
||||
* The number of seconds that the translations status entry should be considered.
|
||||
|
@ -261,13 +261,13 @@ function locale_translatable_language_list() {
|
|||
*
|
||||
* The index is computed from the formula of this language.
|
||||
*
|
||||
* @param $count
|
||||
* @param int $count
|
||||
* Number to return plural for.
|
||||
* @param $langcode
|
||||
* Optional language code to translate to a language other than
|
||||
* what is used to display the page.
|
||||
* @param string|null $langcode
|
||||
* (optional) Language code to translate to a language other than what is used
|
||||
* to display the page, or NULL for current language. Defaults to NULL.
|
||||
*
|
||||
* @return
|
||||
* @return int
|
||||
* The numeric index of the plural variant to use for this $langcode and
|
||||
* $count combination or -1 if the language was not found or does not have a
|
||||
* plural formula.
|
||||
|
@ -308,7 +308,6 @@ function locale_get_plural($count, $langcode = NULL) {
|
|||
return $plural_indexes[$langcode][$count];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_modules_installed().
|
||||
*/
|
||||
|
@ -1113,9 +1112,6 @@ function _locale_strip_quotes($string) {
|
|||
* @param string $filepath
|
||||
* File name to parse.
|
||||
*
|
||||
* @return array
|
||||
* Array of string objects to update indexed by context and source.
|
||||
*
|
||||
* @throws Exception
|
||||
* If a non-local file is attempted to be parsed.
|
||||
*/
|
||||
|
@ -1217,10 +1213,11 @@ function _locale_parse_js_file($filepath) {
|
|||
* files are rebuilt (with locale_update_js_files()) the next time a
|
||||
* request is served in that language.
|
||||
*
|
||||
* @param $langcode
|
||||
* The language code for which the file needs to be refreshed.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language code for which the file needs to be refreshed, or
|
||||
* NULL to refresh all languages. Defaults to NULL.
|
||||
*
|
||||
* @return
|
||||
* @return array
|
||||
* New content of the 'system.javascript_parsed' variable.
|
||||
*/
|
||||
function _locale_invalidate_js($langcode = NULL) {
|
||||
|
@ -1245,8 +1242,9 @@ function _locale_invalidate_js($langcode = NULL) {
|
|||
/**
|
||||
* (Re-)Creates the JavaScript translation file for a language.
|
||||
*
|
||||
* @param $langcode
|
||||
* The language, the translation file should be (re)created for.
|
||||
* @param string|null $langcode
|
||||
* (optional) The language that the translation file should be (re)created
|
||||
* for, or NULL for the current language. Defaults to NULL.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if translation file exists, FALSE otherwise.
|
||||
|
@ -1373,6 +1371,7 @@ function _locale_rebuild_js($langcode = NULL) {
|
|||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element callback: After build changes to the language update table.
|
||||
*
|
||||
|
|
|
@ -13,9 +13,14 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
|||
*
|
||||
* Manually checks the translation status without the use of cron.
|
||||
*
|
||||
* @see locale_menu()
|
||||
* @deprecated in Drupal 8.5.0 and will be removed before 9.0.0. It is unused by
|
||||
* Drupal core. Duplicate this function in your own extension if you need its
|
||||
* behavior.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2931188
|
||||
*/
|
||||
function locale_translation_manual_status() {
|
||||
@trigger_error('locale_translation_manual_status() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. It is unused by Drupal core. Duplicate this function in your own extension if you need its behavior.', E_USER_DEPRECATED);
|
||||
module_load_include('compare.inc', 'locale');
|
||||
|
||||
// Check the translation status of all translatable projects in all languages.
|
||||
|
|
13
web/core/modules/locale/locale.post_update.php
Normal file
13
web/core/modules/locale/locale.post_update.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post-update functions for Locale module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Clear cache to ensure plural translations are removed from it.
|
||||
*/
|
||||
function locale_post_update_clear_cache_for_old_translations() {
|
||||
// Remove cache of translations, like '@count[2] words'.
|
||||
}
|
|
@ -67,7 +67,7 @@ function locale_translation_get_projects(array $project_names = []) {
|
|||
locale_translation_build_projects();
|
||||
}
|
||||
$projects = \Drupal::service('locale.project')->getAll();
|
||||
array_walk($projects, function(&$project) {
|
||||
array_walk($projects, function (&$project) {
|
||||
$project = (object) $project;
|
||||
});
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ function locale_cron_fill_queue() {
|
|||
// Determine which project+language should be updated.
|
||||
$last = REQUEST_TIME - $config->get('translation.update_interval_days') * 3600 * 24;
|
||||
$projects = \Drupal::service('locale.project')->getAll();
|
||||
$projects = array_filter($projects, function($project) {
|
||||
$projects = array_filter($projects, function ($project) {
|
||||
return $project['status'] == 1;
|
||||
});
|
||||
$files = db_select('locale_file', 'f')
|
||||
|
@ -377,7 +377,7 @@ function locale_cron_fill_queue() {
|
|||
function _locale_translation_file_is_remote($uri) {
|
||||
$scheme = file_uri_scheme($uri);
|
||||
if ($scheme) {
|
||||
return !drupal_realpath($scheme . '://');
|
||||
return !\Drupal::service('file_system')->realpath($scheme . '://');
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ label: Locale configuration
|
|||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
- Configuration
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
||||
- locale_cache_strings
|
||||
- locale_js_directory
|
||||
source_module: locale
|
||||
process:
|
||||
cache_strings: locale_cache_strings
|
||||
'javascript/directory': locale_js_directory
|
|
@ -14,6 +14,8 @@ use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|||
|
||||
/**
|
||||
* Form for the Gettext translation files export form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ExportForm extends FormBase {
|
||||
|
||||
|
@ -165,7 +167,7 @@ class ExportForm extends FormBase {
|
|||
$header->setLanguageName($language_name);
|
||||
|
||||
$writer = new PoStreamWriter();
|
||||
$writer->setUri($uri);
|
||||
$writer->setURI($uri);
|
||||
$writer->setHeader($header);
|
||||
|
||||
$writer->open();
|
||||
|
@ -178,7 +180,7 @@ class ExportForm extends FormBase {
|
|||
$form_state->setResponse($response);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Nothing to export.'));
|
||||
$this->messenger()->addStatus($this->t('Nothing to export.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Form constructor for the translation import screen.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ImportForm extends FormBase {
|
||||
|
||||
|
@ -44,6 +46,7 @@ class ImportForm extends FormBase {
|
|||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a form for language import.
|
||||
*
|
||||
|
@ -108,6 +111,7 @@ class ImportForm extends FormBase {
|
|||
],
|
||||
'#size' => 50,
|
||||
'#upload_validators' => $validators,
|
||||
'#upload_location' => 'translations://',
|
||||
'#attributes' => ['class' => ['file-import-input']],
|
||||
];
|
||||
$form['langcode'] = [
|
||||
|
@ -154,7 +158,7 @@ class ImportForm extends FormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->file = file_save_upload('file', $form['file']['#upload_validators'], 'translations://', 0);
|
||||
$this->file = _file_save_upload_from_form($form['file'], $form_state, 0);
|
||||
|
||||
// Ensure we have the file uploaded.
|
||||
if (!$this->file) {
|
||||
|
@ -166,13 +170,13 @@ class ImportForm extends FormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
\Drupal::moduleHandler()->loadInclude('locale', 'translation.inc');
|
||||
$this->moduleHandler->loadInclude('locale', 'translation.inc');
|
||||
// Add language, if not yet supported.
|
||||
$language = $this->languageManager->getLanguage($form_state->getValue('langcode'));
|
||||
if (empty($language)) {
|
||||
$language = ConfigurableLanguage::createFromLangcode($form_state->getValue('langcode'));
|
||||
$language->save();
|
||||
drupal_set_message($this->t('The language %language has been created.', ['%language' => $this->t($language->label())]));
|
||||
$this->messenger()->addStatus($this->t('The language %language has been created.', ['%language' => $this->t($language->label())]));
|
||||
}
|
||||
$options = array_merge(_locale_translation_default_update_options(), [
|
||||
'langcode' => $form_state->getValue('langcode'),
|
||||
|
@ -185,7 +189,6 @@ class ImportForm extends FormBase {
|
|||
batch_set($batch);
|
||||
|
||||
// Create or update all configuration translations for this language.
|
||||
\Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
|
||||
if ($batch = locale_config_batch_update_components($options, [$form_state->getValue('langcode')])) {
|
||||
batch_set($batch);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Configure locale settings for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LocaleSettingsForm extends ConfigFormBase {
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ use Drupal\locale\SourceString;
|
|||
|
||||
/**
|
||||
* Defines a translation edit form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TranslateEditForm extends TranslateFormBase {
|
||||
|
||||
|
@ -217,7 +219,7 @@ class TranslateEditForm extends TranslateFormBase {
|
|||
}
|
||||
}
|
||||
|
||||
drupal_set_message($this->t('The strings have been saved.'));
|
||||
$this->messenger()->addStatus($this->t('The strings have been saved.'));
|
||||
|
||||
// Keep the user on the current pager page.
|
||||
$page = $this->getRequest()->query->get('page');
|
||||
|
|
|
@ -6,6 +6,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Provides a filtered translation edit form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TranslateFilterForm extends TranslateFormBase {
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Provides a translation status form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TranslationStatusForm extends FormBase {
|
||||
|
||||
|
@ -40,7 +42,7 @@ class TranslationStatusForm extends FormBase {
|
|||
/**
|
||||
* Constructs a TranslationStatusForm object.
|
||||
*
|
||||
* @param ModuleHandlerInterface $module_handler
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* A module handler.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state service.
|
||||
|
|
|
@ -43,7 +43,7 @@ class LocaleConfigManager {
|
|||
/**
|
||||
* The string storage for reading and writing translations.
|
||||
*
|
||||
* @var \Drupal\locale\StringStorageInterface;
|
||||
* @var \Drupal\locale\StringStorageInterface
|
||||
*/
|
||||
protected $localeStorage;
|
||||
|
||||
|
@ -140,9 +140,7 @@ class LocaleConfigManager {
|
|||
if ($this->isSupported($name)) {
|
||||
// Create typed configuration wrapper based on install storage data.
|
||||
$data = $this->defaultConfigStorage->read($name);
|
||||
$type_definition = $this->typedConfigManager->getDefinition($name);
|
||||
$data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data);
|
||||
$typed_config = $this->typedConfigManager->create($data_definition, $data);
|
||||
$typed_config = $this->typedConfigManager->createFromNameAndData($name, $data);
|
||||
if ($typed_config instanceof TraversableTypedDataInterface) {
|
||||
return $this->getTranslatableData($typed_config);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use Drupal\Core\Cache\CacheCollector;
|
|||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
|
@ -172,6 +173,12 @@ class LocaleLookup extends CacheCollector {
|
|||
}
|
||||
}
|
||||
|
||||
if (is_string($value) && strpos($value, PluralTranslatableMarkup::DELIMITER) !== FALSE) {
|
||||
// Community translations imported from localize.drupal.org as well as
|
||||
// migrated translations may contain @count[number].
|
||||
$value = preg_replace('!@count\[\d+\]!', '@count', $value);
|
||||
}
|
||||
|
||||
$this->storage[$offset] = $value;
|
||||
// Disabling the usage of string caching allows a module to watch for
|
||||
// the exact list of strings used on a page. From a performance
|
||||
|
|
|
@ -99,7 +99,7 @@ class LocaleTranslation extends QueueWorkerBase implements ContainerFactoryPlugi
|
|||
}
|
||||
else {
|
||||
$batch_context = $args[$last];
|
||||
unset ($args[$last]);
|
||||
unset($args[$last]);
|
||||
}
|
||||
$args = array_merge($args, [&$batch_context]);
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class PluralFormula implements PluralFormulaInterface {
|
|||
* ],
|
||||
* ]
|
||||
* @endcode
|
||||
* @var []
|
||||
* @var array
|
||||
*/
|
||||
protected $formulae;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class PoDatabaseWriter implements PoWriterInterface {
|
|||
* An associative array indicating what data should be overwritten, if any.
|
||||
*
|
||||
* Elements of the array:
|
||||
* - override_options
|
||||
* - overwrite_options
|
||||
* - not_customized: boolean indicating that not customized strings should
|
||||
* be overwritten.
|
||||
* - customized: boolean indicating that customized strings should be
|
||||
|
@ -109,6 +109,16 @@ class PoDatabaseWriter implements PoWriterInterface {
|
|||
|
||||
/**
|
||||
* Set the options for the current writer.
|
||||
*
|
||||
* @param array $options
|
||||
* An associative array containing:
|
||||
* - overwrite_options: An array of options. Each option contains:
|
||||
* - not_customized: Boolean indicating that not customized strings should
|
||||
* be overwritten.
|
||||
* - customized: Boolean indicating that customized strings should be
|
||||
* overwritten.
|
||||
* - customized: The strings being imported should be saved as customized.
|
||||
* One of LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
|
||||
*/
|
||||
public function setOptions(array $options) {
|
||||
if (!isset($options['overwrite_options'])) {
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\locale;
|
|||
* value, and is assumed to be in English language.
|
||||
*/
|
||||
class SourceString extends StringBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\locale;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
|
||||
/**
|
||||
* Defines a class to store localized strings in the database.
|
||||
|
@ -416,7 +417,7 @@ class StringDatabaseStorage implements StringStorageInterface {
|
|||
elseif ($table_alias == 't' && $join === 'leftJoin') {
|
||||
// Conditions for target fields when doing an outer join only make
|
||||
// sense if we add also OR field IS NULL.
|
||||
$query->condition(db_or()
|
||||
$query->condition((new Condition('OR'))
|
||||
->condition($field_alias, (array) $value, 'IN')
|
||||
->isNull($field_alias)
|
||||
);
|
||||
|
@ -429,7 +430,7 @@ class StringDatabaseStorage implements StringStorageInterface {
|
|||
// Process other options, string filter, query limit, etc.
|
||||
if (!empty($options['filters'])) {
|
||||
if (count($options['filters']) > 1) {
|
||||
$filter = db_or();
|
||||
$filter = new Condition('OR');
|
||||
$query->condition($filter);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Tests parsing js files for translatable strings.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleJavascriptTranslationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['locale', 'locale_test'];
|
||||
|
||||
public function testFileParsing() {
|
||||
$filename = __DIR__ . '/../../tests/locale_test.js';
|
||||
|
||||
// Parse the file to look for source strings.
|
||||
_locale_parse_js_file($filename);
|
||||
|
||||
// Get all of the source strings that were found.
|
||||
$strings = $this->container
|
||||
->get('locale.storage')
|
||||
->getStrings([
|
||||
'type' => 'javascript',
|
||||
'name' => $filename,
|
||||
]);
|
||||
|
||||
$source_strings = [];
|
||||
foreach ($strings as $string) {
|
||||
$source_strings[$string->source] = $string->context;
|
||||
}
|
||||
|
||||
$etx = LOCALE_PLURAL_DELIMITER;
|
||||
// List of all strings that should be in the file.
|
||||
$test_strings = [
|
||||
'Standard Call t' => '',
|
||||
'Whitespace Call t' => '',
|
||||
|
||||
'Single Quote t' => '',
|
||||
"Single Quote \\'Escaped\\' t" => '',
|
||||
'Single Quote Concat strings t' => '',
|
||||
|
||||
'Double Quote t' => '',
|
||||
"Double Quote \\\"Escaped\\\" t" => '',
|
||||
'Double Quote Concat strings t' => '',
|
||||
|
||||
'Context !key Args t' => 'Context string',
|
||||
|
||||
'Context Unquoted t' => 'Context string unquoted',
|
||||
'Context Single Quoted t' => 'Context string single quoted',
|
||||
'Context Double Quoted t' => 'Context string double quoted',
|
||||
|
||||
"Standard Call plural{$etx}Standard Call @count plural" => '',
|
||||
"Whitespace Call plural{$etx}Whitespace Call @count plural" => '',
|
||||
|
||||
"Single Quote plural{$etx}Single Quote @count plural" => '',
|
||||
"Single Quote \\'Escaped\\' plural{$etx}Single Quote \\'Escaped\\' @count plural" => '',
|
||||
|
||||
"Double Quote plural{$etx}Double Quote @count plural" => '',
|
||||
"Double Quote \\\"Escaped\\\" plural{$etx}Double Quote \\\"Escaped\\\" @count plural" => '',
|
||||
|
||||
"Context !key Args plural{$etx}Context !key Args @count plural" => 'Context string',
|
||||
|
||||
"Context Unquoted plural{$etx}Context Unquoted @count plural" => 'Context string unquoted',
|
||||
"Context Single Quoted plural{$etx}Context Single Quoted @count plural" => 'Context string single quoted',
|
||||
"Context Double Quoted plural{$etx}Context Double Quoted @count plural" => 'Context string double quoted',
|
||||
];
|
||||
|
||||
// Assert that all strings were found properly.
|
||||
foreach ($test_strings as $str => $context) {
|
||||
$args = ['%source' => $str, '%context' => $context];
|
||||
|
||||
// Make sure that the string was found in the file.
|
||||
$this->assertTrue(isset($source_strings[$str]), SafeMarkup::format('Found source string: %source', $args));
|
||||
|
||||
// Make sure that the proper context was matched.
|
||||
$message = $context ? SafeMarkup::format('Context for %source is %context', $args) : SafeMarkup::format('Context for %source is blank', $args);
|
||||
$this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, $message);
|
||||
}
|
||||
|
||||
$this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert translations JS is added before drupal.js, because it depends on it.
|
||||
*/
|
||||
public function testLocaleTranslationJsDependencies() {
|
||||
// User to add and remove language.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'translate interface']);
|
||||
|
||||
// Add custom language.
|
||||
$this->drupalLogin($admin_user);
|
||||
// Code for the language.
|
||||
$langcode = 'es';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
// The domain prefix.
|
||||
$prefix = $langcode;
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
|
||||
// Set path prefix.
|
||||
$edit = ["prefix[$langcode]" => $prefix];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
|
||||
|
||||
// This forces locale.admin.js string sources to be imported, which contains
|
||||
// the next translation.
|
||||
$this->drupalGet($prefix . '/admin/config/regional/translate');
|
||||
|
||||
// Translate a string in locale.admin.js to our new language.
|
||||
$strings = \Drupal::service('locale.storage')
|
||||
->getStrings([
|
||||
'source' => 'Show description',
|
||||
'type' => 'javascript',
|
||||
'name' => 'core/modules/locale/locale.admin.js',
|
||||
]);
|
||||
$string = $strings[0];
|
||||
|
||||
$this->drupalPostForm(NULL, ['string' => 'Show description'], t('Filter'));
|
||||
$edit = ['strings[' . $string->lid . '][translations][0]' => $this->randomString(16)];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save translations'));
|
||||
|
||||
// Calculate the filename of the JS including the translations.
|
||||
$js_translation_files = \Drupal::state()->get('locale.translation.javascript');
|
||||
$js_filename = $prefix . '_' . $js_translation_files[$prefix] . '.js';
|
||||
|
||||
// Assert translations JS is included before drupal.js.
|
||||
$this->assertTrue(strpos($this->content, $js_filename) < strpos($this->content, 'core/misc/drupal.js'), 'Translations are included before Drupal.t.');
|
||||
}
|
||||
|
||||
}
|
52
web/core/modules/locale/tests/locale_test.es6.js
Normal file
52
web/core/modules/locale/tests/locale_test.es6.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @file
|
||||
* JavaScript for locale_test.module.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
|
||||
Drupal.t("Standard Call t");
|
||||
Drupal
|
||||
.
|
||||
t
|
||||
(
|
||||
"Whitespace Call t"
|
||||
)
|
||||
;
|
||||
|
||||
Drupal.t('Single Quote t');
|
||||
Drupal.t('Single Quote \'Escaped\' t');
|
||||
Drupal.t('Single Quote ' + 'Concat ' + 'strings ' + 't');
|
||||
|
||||
Drupal.t("Double Quote t");
|
||||
Drupal.t("Double Quote \"Escaped\" t");
|
||||
Drupal.t("Double Quote " + "Concat " + "strings " + "t");
|
||||
|
||||
Drupal.t("Context Unquoted t", {}, {context: "Context string unquoted"});
|
||||
Drupal.t("Context Single Quoted t", {}, {'context': "Context string single quoted"});
|
||||
Drupal.t("Context Double Quoted t", {}, {"context": "Context string double quoted"});
|
||||
|
||||
Drupal.t("Context !key Args t", {'!key': 'value'}, {context: "Context string"});
|
||||
|
||||
Drupal.formatPlural(1, "Standard Call plural", "Standard Call @count plural");
|
||||
Drupal
|
||||
.
|
||||
formatPlural
|
||||
(
|
||||
1,
|
||||
"Whitespace Call plural",
|
||||
"Whitespace Call @count plural"
|
||||
)
|
||||
;
|
||||
|
||||
Drupal.formatPlural(1, 'Single Quote plural', 'Single Quote @count plural');
|
||||
Drupal.formatPlural(1, 'Single Quote \'Escaped\' plural', 'Single Quote \'Escaped\' @count plural');
|
||||
|
||||
Drupal.formatPlural(1, "Double Quote plural", "Double Quote @count plural");
|
||||
Drupal.formatPlural(1, "Double Quote \"Escaped\" plural", "Double Quote \"Escaped\" @count plural");
|
||||
|
||||
Drupal.formatPlural(1, "Context Unquoted plural", "Context Unquoted @count plural", {}, {context: "Context string unquoted"});
|
||||
Drupal.formatPlural(1, "Context Single Quoted plural", "Context Single Quoted @count plural", {}, {'context': "Context string single quoted"});
|
||||
Drupal.formatPlural(1, "Context Double Quoted plural", "Context Double Quoted @count plural", {}, {"context": "Context string double quoted"});
|
||||
|
||||
Drupal.formatPlural(1, "Context !key Args plural", "Context !key Args @count plural", {'!key': 'value'}, {context: "Context string"});
|
|
@ -1,18 +1,12 @@
|
|||
/**
|
||||
* @file
|
||||
* JavaScript for locale_test.module.
|
||||
*
|
||||
* @ignore
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
Drupal.t("Standard Call t");
|
||||
Drupal
|
||||
.
|
||||
t
|
||||
(
|
||||
"Whitespace Call t"
|
||||
)
|
||||
;
|
||||
Drupal.t("Whitespace Call t");
|
||||
|
||||
Drupal.t('Single Quote t');
|
||||
Drupal.t('Single Quote \'Escaped\' t');
|
||||
|
@ -22,22 +16,14 @@ Drupal.t("Double Quote t");
|
|||
Drupal.t("Double Quote \"Escaped\" t");
|
||||
Drupal.t("Double Quote " + "Concat " + "strings " + "t");
|
||||
|
||||
Drupal.t("Context Unquoted t", {}, {context: "Context string unquoted"});
|
||||
Drupal.t("Context Single Quoted t", {}, {'context': "Context string single quoted"});
|
||||
Drupal.t("Context Double Quoted t", {}, {"context": "Context string double quoted"});
|
||||
Drupal.t("Context Unquoted t", {}, { context: "Context string unquoted" });
|
||||
Drupal.t("Context Single Quoted t", {}, { 'context': "Context string single quoted" });
|
||||
Drupal.t("Context Double Quoted t", {}, { "context": "Context string double quoted" });
|
||||
|
||||
Drupal.t("Context !key Args t", {'!key': 'value'}, {context: "Context string"});
|
||||
Drupal.t("Context !key Args t", { '!key': 'value' }, { context: "Context string" });
|
||||
|
||||
Drupal.formatPlural(1, "Standard Call plural", "Standard Call @count plural");
|
||||
Drupal
|
||||
.
|
||||
formatPlural
|
||||
(
|
||||
1,
|
||||
"Whitespace Call plural",
|
||||
"Whitespace Call @count plural"
|
||||
)
|
||||
;
|
||||
Drupal.formatPlural(1, "Whitespace Call plural", "Whitespace Call @count plural");
|
||||
|
||||
Drupal.formatPlural(1, 'Single Quote plural', 'Single Quote @count plural');
|
||||
Drupal.formatPlural(1, 'Single Quote \'Escaped\' plural', 'Single Quote \'Escaped\' @count plural');
|
||||
|
@ -45,8 +31,8 @@ Drupal.formatPlural(1, 'Single Quote \'Escaped\' plural', 'Single Quote \'Escape
|
|||
Drupal.formatPlural(1, "Double Quote plural", "Double Quote @count plural");
|
||||
Drupal.formatPlural(1, "Double Quote \"Escaped\" plural", "Double Quote \"Escaped\" @count plural");
|
||||
|
||||
Drupal.formatPlural(1, "Context Unquoted plural", "Context Unquoted @count plural", {}, {context: "Context string unquoted"});
|
||||
Drupal.formatPlural(1, "Context Single Quoted plural", "Context Single Quoted @count plural", {}, {'context': "Context string single quoted"});
|
||||
Drupal.formatPlural(1, "Context Double Quoted plural", "Context Double Quoted @count plural", {}, {"context": "Context string double quoted"});
|
||||
Drupal.formatPlural(1, "Context Unquoted plural", "Context Unquoted @count plural", {}, { context: "Context string unquoted" });
|
||||
Drupal.formatPlural(1, "Context Single Quoted plural", "Context Single Quoted @count plural", {}, { 'context': "Context string single quoted" });
|
||||
Drupal.formatPlural(1, "Context Double Quoted plural", "Context Double Quoted @count plural", {}, { "context": "Context string double quoted" });
|
||||
|
||||
Drupal.formatPlural(1, "Context !key Args plural", "Context !key Args @count plural", {'!key': 'value'}, {context: "Context string"});
|
||||
Drupal.formatPlural(1, "Context !key Args plural", "Context !key Args @count plural", { '!key': 'value' }, { context: "Context string" });
|
|
@ -4,4 +4,3 @@ description: 'Support module for testing early bootstrap getting of annotations
|
|||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\locale\Locale;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ use Drupal\language\Entity\ConfigurableLanguage;
|
|||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleConfigTranslationImportTest extends WebTestBase {
|
||||
class LocaleConfigTranslationImportTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -20,6 +20,13 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
*/
|
||||
public static $modules = ['language', 'locale_test_translate'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test update changes configuration translations if enabled after language.
|
||||
*/
|
||||
|
@ -39,6 +46,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
|
||||
// Add translation permissions now that the locale module has been enabled.
|
||||
|
@ -84,6 +92,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
|
||||
// Add predefined language.
|
||||
|
@ -122,7 +131,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
$expected = [
|
||||
'translatable_no_default' => 'This translation is preserved',
|
||||
'translatable_default_with_translation' => 'This translation is preserved',
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved'
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved',
|
||||
];
|
||||
$this->assertEqual($expected, $override->get());
|
||||
}
|
||||
|
@ -142,6 +151,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
|
||||
// Add predefined language.
|
||||
|
@ -179,6 +189,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
|
||||
// Add predefined language.
|
||||
|
@ -193,7 +204,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
$expected = [
|
||||
'translatable_default_with_translation' => 'Locale can translate Afrikaans',
|
||||
'translatable_no_default' => 'This translation is preserved',
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved'
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved',
|
||||
];
|
||||
$this->assertEqual($expected, $override->get());
|
||||
|
||||
|
@ -206,7 +217,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textareas = $this->xpath('//textarea');
|
||||
$textarea = current($textareas);
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => '',
|
||||
];
|
||||
|
@ -215,7 +226,7 @@ class LocaleConfigTranslationImportTest extends WebTestBase {
|
|||
$override = \Drupal::languageManager()->getLanguageConfigOverride('af', 'locale_test_translate.settings');
|
||||
$expected = [
|
||||
'translatable_no_default' => 'This translation is preserved',
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved'
|
||||
'translatable_default_with_no_translation' => 'This translation is preserved',
|
||||
];
|
||||
$this->assertEqual($expected, $override->get());
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleConfigTranslationTest extends WebTestBase {
|
||||
class LocaleConfigTranslationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The language code used.
|
||||
|
@ -38,6 +38,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
|
||||
// Add custom language.
|
||||
|
@ -76,7 +77,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textareas = $this->xpath('//textarea');
|
||||
$textarea = current($textareas);
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $message,
|
||||
];
|
||||
|
@ -100,7 +101,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textareas = $this->xpath('//textarea');
|
||||
$textarea = current($textareas);
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => 'D',
|
||||
];
|
||||
|
@ -143,7 +144,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
];
|
||||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $image_style_label,
|
||||
];
|
||||
|
@ -175,7 +176,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
];
|
||||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $category_label,
|
||||
];
|
||||
|
@ -231,7 +232,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
|
|||
}
|
||||
|
||||
// Check the optional default configuration in node module.
|
||||
$string = $this->storage->findString(['source' => 'No front page content has been created yet.', 'context' => '', 'type' => 'configuration']);
|
||||
$string = $this->storage->findString(['source' => 'No front page content has been created yet.<br/>Follow the <a target="_blank" href="https://www.drupal.org/docs/user_guide/en/index.html">User Guide</a> to start building your site.', 'context' => '', 'type' => 'configuration']);
|
||||
if ($optional) {
|
||||
$this->assertFalse($this->config('views.view.frontpage')->isNew());
|
||||
$this->assertTrue($string, 'Node view text can be found with node and views modules.');
|
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the exportation of locale files.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleExportTest extends WebTestBase {
|
||||
class LocaleExportTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the locale functionality in the altered file settings form.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleFileSystemFormTest extends WebTestBase {
|
||||
class LocaleFileSystemFormTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -21,7 +21,7 @@ class LocaleFileSystemFormTest extends WebTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(){
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$account = $this->drupalCreateUser(['administer site configuration']);
|
||||
$this->drupalLogin($account);
|
||||
|
@ -45,7 +45,7 @@ class LocaleFileSystemFormTest extends WebTestBase {
|
|||
// The setting should persist.
|
||||
$translation_path = $this->publicFilesDirectory . '/translations_changed';
|
||||
$fields = [
|
||||
'translation_path' => $translation_path
|
||||
'translation_path' => $translation_path,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $fields, t('Save configuration'));
|
||||
$this->drupalGet('admin/config/media/file-system');
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleImportFunctionalTest extends WebTestBase {
|
||||
class LocaleImportFunctionalTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -52,6 +52,7 @@ class LocaleImportFunctionalTest extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
}
|
||||
|
||||
|
@ -341,7 +342,8 @@ class LocaleImportFunctionalTest extends WebTestBase {
|
|||
|
||||
// Import a .po file to translate.
|
||||
$this->importPoFile($this->getPoFileWithConfigDe(), [
|
||||
'langcode' => $langcode]);
|
||||
'langcode' => $langcode,
|
||||
]);
|
||||
|
||||
// Check that the 'Anonymous' string is translated.
|
||||
$config = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'user.settings');
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
/**
|
||||
* Tests parsing js files for translatable strings.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleJavascriptTranslationTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['locale', 'locale_test'];
|
||||
|
||||
public function testFileParsing() {
|
||||
|
||||
// This test is for ensuring that the regular expression in
|
||||
// _locale_parse_js_file() finds translatable source strings in all valid
|
||||
// JavaScript syntax regardless of the coding style used, especially with
|
||||
// respect to optional whitespace, line breaks, etc.
|
||||
// - We test locale_test.es6.js, because that is the one that contains a
|
||||
// variety of whitespace styles.
|
||||
// - We also test the transpiled locale_test.js as an extra double-check
|
||||
// that JavaScript transpilation doesn't change what
|
||||
// _locale_parse_js_file() finds.
|
||||
$files[] = __DIR__ . '/../../locale_test.es6.js';
|
||||
$files[] = __DIR__ . '/../../locale_test.js';
|
||||
|
||||
foreach ($files as $filename) {
|
||||
// Parse the file to look for source strings.
|
||||
_locale_parse_js_file($filename);
|
||||
|
||||
// Get all of the source strings that were found.
|
||||
$strings = $this->container
|
||||
->get('locale.storage')
|
||||
->getStrings([
|
||||
'type' => 'javascript',
|
||||
'name' => $filename,
|
||||
]);
|
||||
|
||||
$source_strings = [];
|
||||
foreach ($strings as $string) {
|
||||
$source_strings[$string->source] = $string->context;
|
||||
}
|
||||
|
||||
$etx = LOCALE_PLURAL_DELIMITER;
|
||||
// List of all strings that should be in the file.
|
||||
$test_strings = [
|
||||
'Standard Call t' => '',
|
||||
'Whitespace Call t' => '',
|
||||
|
||||
'Single Quote t' => '',
|
||||
"Single Quote \\'Escaped\\' t" => '',
|
||||
'Single Quote Concat strings t' => '',
|
||||
|
||||
'Double Quote t' => '',
|
||||
"Double Quote \\\"Escaped\\\" t" => '',
|
||||
'Double Quote Concat strings t' => '',
|
||||
|
||||
'Context !key Args t' => 'Context string',
|
||||
|
||||
'Context Unquoted t' => 'Context string unquoted',
|
||||
'Context Single Quoted t' => 'Context string single quoted',
|
||||
'Context Double Quoted t' => 'Context string double quoted',
|
||||
|
||||
"Standard Call plural{$etx}Standard Call @count plural" => '',
|
||||
"Whitespace Call plural{$etx}Whitespace Call @count plural" => '',
|
||||
|
||||
"Single Quote plural{$etx}Single Quote @count plural" => '',
|
||||
"Single Quote \\'Escaped\\' plural{$etx}Single Quote \\'Escaped\\' @count plural" => '',
|
||||
|
||||
"Double Quote plural{$etx}Double Quote @count plural" => '',
|
||||
"Double Quote \\\"Escaped\\\" plural{$etx}Double Quote \\\"Escaped\\\" @count plural" => '',
|
||||
|
||||
"Context !key Args plural{$etx}Context !key Args @count plural" => 'Context string',
|
||||
|
||||
"Context Unquoted plural{$etx}Context Unquoted @count plural" => 'Context string unquoted',
|
||||
"Context Single Quoted plural{$etx}Context Single Quoted @count plural" => 'Context string single quoted',
|
||||
"Context Double Quoted plural{$etx}Context Double Quoted @count plural" => 'Context string double quoted',
|
||||
];
|
||||
|
||||
// Assert that all strings were found properly.
|
||||
foreach ($test_strings as $str => $context) {
|
||||
$args = ['%source' => $str, '%context' => $context];
|
||||
|
||||
// Make sure that the string was found in the file.
|
||||
$this->assertTrue(isset($source_strings[$str]), new FormattableMarkup('Found source string: %source', $args));
|
||||
|
||||
// Make sure that the proper context was matched.
|
||||
$message = $context ? new FormattableMarkup('Context for %source is %context', $args) : new FormattableMarkup('Context for %source is blank', $args);
|
||||
$this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, $message);
|
||||
}
|
||||
|
||||
$this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert translations JS is added before drupal.js, because it depends on it.
|
||||
*/
|
||||
public function testLocaleTranslationJsDependencies() {
|
||||
// User to add and remove language.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'translate interface']);
|
||||
|
||||
// Add custom language.
|
||||
$this->drupalLogin($admin_user);
|
||||
// Code for the language.
|
||||
$langcode = 'es';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
// The domain prefix.
|
||||
$prefix = $langcode;
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
|
||||
// Set path prefix.
|
||||
$edit = ["prefix[$langcode]" => $prefix];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
|
||||
|
||||
// This forces locale.admin.js string sources to be imported, which contains
|
||||
// the next translation.
|
||||
$this->drupalGet($prefix . '/admin/config/regional/translate');
|
||||
|
||||
// Translate a string in locale.admin.js to our new language.
|
||||
$strings = \Drupal::service('locale.storage')
|
||||
->getStrings([
|
||||
'source' => 'Show description',
|
||||
'type' => 'javascript',
|
||||
'name' => 'core/modules/locale/locale.admin.js',
|
||||
]);
|
||||
$string = $strings[0];
|
||||
|
||||
$this->drupalPostForm(NULL, ['string' => 'Show description'], t('Filter'));
|
||||
$edit = ['strings[' . $string->lid . '][translations][0]' => $this->randomString(16)];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save translations'));
|
||||
|
||||
// Calculate the filename of the JS including the translations.
|
||||
$js_translation_files = \Drupal::state()->get('locale.translation.javascript');
|
||||
$js_filename = $prefix . '_' . $js_translation_files[$prefix] . '.js';
|
||||
|
||||
$content = $this->getSession()->getPage()->getContent();
|
||||
// Assert translations JS is included before drupal.js.
|
||||
$this->assertTrue(strpos($content, $js_filename) < strpos($content, 'core/misc/drupal.js'), 'Translations are included before Drupal.t.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\Asset\AttachedAssets;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests localization of the JavaScript libraries.
|
||||
|
@ -12,7 +12,7 @@ use Drupal\simpletest\WebTestBase;
|
|||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleLibraryAlterTest extends WebTestBase {
|
||||
class LocaleLibraryAlterTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
|
@ -55,4 +56,45 @@ class LocaleLocaleLookupTest extends BrowserTestBase {
|
|||
$this->assertEqual($context['operation'], 'locale_lookup');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test old plural style @count[number] fix.
|
||||
*
|
||||
* @dataProvider providerTestFixOldPluralStyle
|
||||
*/
|
||||
public function testFixOldPluralStyle($translation_value, $expected) {
|
||||
$string_storage = \Drupal::service('locale.storage');
|
||||
$string = $string_storage->findString(['source' => 'Member for', 'context' => '']);
|
||||
$lid = $string->getId();
|
||||
$string_storage->createTranslation([
|
||||
'lid' => $lid,
|
||||
'language' => 'fr',
|
||||
'translation' => $translation_value,
|
||||
])->save();
|
||||
_locale_refresh_translations(['fr'], [$lid]);
|
||||
|
||||
// Check that 'count[2]' was fixed for render value.
|
||||
$this->drupalGet('');
|
||||
$this->assertSession()->pageTextContains($expected);
|
||||
|
||||
// Check that 'count[2]' was saved for source value.
|
||||
$translation = $string_storage->findTranslation(['language' => 'fr', 'lid' => $lid])->translation;
|
||||
$this->assertSame($translation_value, $translation, 'Source value not changed');
|
||||
$this->assertNotFalse(strpos($translation, '@count[2]'), 'Source value contains @count[2]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testFixOldPluralStyle().
|
||||
*
|
||||
* @return array
|
||||
* An array of test data:
|
||||
* - translation value
|
||||
* - expected result
|
||||
*/
|
||||
public function providerTestFixOldPluralStyle() {
|
||||
return [
|
||||
'non-plural translation' => ['@count[2] non-plural test', '@count[2] non-plural test'],
|
||||
'plural translation' => ['@count[2] plural test' . PluralTranslatableMarkup::DELIMITER, '@count plural test'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
/**
|
||||
* Tests installing in a different language with a dev version string.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleNonInteractiveDevInstallTest extends LocaleNonInteractiveInstallTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getVersionStringToTest() {
|
||||
include_once $this->root . '/core/includes/install.core.inc';
|
||||
$version = _install_get_version_info(\Drupal::VERSION);
|
||||
return $version['major'] . '.' . $version['minor'] . '.x';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests installing in a different language with a non-dev version string.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleNonInteractiveInstallTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Gets the version string to use in the translation file.
|
||||
*
|
||||
* @return string
|
||||
* The version string to test, for example, '8.0.0' or '8.6.x'.
|
||||
*/
|
||||
protected function getVersionStringToTest() {
|
||||
include_once $this->root . '/core/includes/install.core.inc';
|
||||
$version = _install_get_version_info(\Drupal::VERSION);
|
||||
return $version['major'] . '.0.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function installParameters() {
|
||||
$parameters = parent::installParameters();
|
||||
// Install Drupal in German.
|
||||
$parameters['parameters']['langcode'] = 'de';
|
||||
// Create a po file so we don't attempt to download one from
|
||||
// localize.drupal.org and to have a test translation that will not change.
|
||||
\Drupal::service('file_system')->mkdir($this->publicFilesDirectory . '/translations', NULL, TRUE);
|
||||
$contents = <<<ENDPO
|
||||
msgid ""
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the password that accompanies your username."
|
||||
msgstr "Geben sie das Passwort für ihren Benutzernamen ein."
|
||||
|
||||
ENDPO;
|
||||
$version = $this->getVersionStringToTest();
|
||||
file_put_contents($this->publicFilesDirectory . "/translations/drupal-{$version}.de.po", $contents);
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the expected translated text appears on the login screen.
|
||||
*/
|
||||
public function testInstallerTranslations() {
|
||||
$this->drupalGet('user/login');
|
||||
$this->assertSession()->responseContains('Geben sie das Passwort für ihren Benutzernamen ein.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests plural handling for various languages.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocalePluralFormatTest extends WebTestBase {
|
||||
class LocalePluralFormatTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* An admin user.
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\tour\Tests\TourTestBase;
|
||||
use Drupal\Tests\tour\Functional\TourTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Translate Interface tour.
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
/**
|
||||
* Tests locale translation download.
|
||||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleTranslationDownloadTest extends LocaleUpdateBase {
|
||||
|
||||
/**
|
||||
* The virtual file stream for storing translations.
|
||||
*
|
||||
* @var \org\bovigo\vfs\vfsStreamDirectory
|
||||
*/
|
||||
protected $translationsStream;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$moduleHandler = $this->container->get('module_handler');
|
||||
$moduleHandler->loadInclude('locale', 'inc', 'locale.batch');
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
// Let the translations:// stream wrapper point to a virtual file system to
|
||||
// make it independent from the test environment.
|
||||
$this->translationsStream = vfsStream::setup('translations');
|
||||
\Drupal::configFactory()->getEditable('locale.settings')
|
||||
->set('translation.path', $this->translationsStream->url())
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation download from remote sources.
|
||||
*/
|
||||
public function testUpdateImportSourceRemote() {
|
||||
|
||||
// Provide remote and 'previously' downloaded translation file.
|
||||
$this->setTranslationFiles();
|
||||
vfsStream::create([
|
||||
'contrib_module_one-8.x-1.1.de._po' => '__old_content__',
|
||||
], $this->translationsStream);
|
||||
|
||||
$url = \Drupal::service('url_generator')->generateFromRoute('<front>', [], ['absolute' => TRUE]);
|
||||
$uri = $url . PublicStream::basePath() . '/remote/8.x/contrib_module_one/contrib_module_one-8.x-1.1.de._po';
|
||||
$source_file = (object) [
|
||||
'uri' => $uri,
|
||||
];
|
||||
|
||||
$result = locale_translation_download_source($source_file, 'translations://');
|
||||
|
||||
$this->assertEquals('translations://contrib_module_one-8.x-1.1.de._po', $result->uri);
|
||||
$this->assertFalse(file_exists('translations://contrib_module_one-8.x-1.1.de_0._po'));
|
||||
$this->assertTrue(file_exists('translations://contrib_module_one-8.x-1.1.de._po'));
|
||||
$this->assertNotContains('__old_content__', file_get_contents('translations://contrib_module_one-8.x-1.1.de._po'));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
/**
|
||||
* Adds a new locale and translates its name. Checks the validation of
|
||||
|
@ -13,7 +13,7 @@ use Drupal\Component\Utility\SafeMarkup;
|
|||
*
|
||||
* @group locale
|
||||
*/
|
||||
class LocaleTranslationUiTest extends WebTestBase {
|
||||
class LocaleTranslationUiTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -44,7 +44,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
// Code for the language.
|
||||
$langcode = 'xx';
|
||||
// The English name for the language. This will be translated.
|
||||
$name = $this->randomMachineName(16);
|
||||
$name = 'cucurbitaceae';
|
||||
// This will be the translation of $name.
|
||||
$translation = $this->randomMachineName(16);
|
||||
$translation_to_en = $this->randomMachineName(16);
|
||||
|
@ -89,7 +89,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
|
||||
// Assume this is the only result, given the random name.
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $translation,
|
||||
];
|
||||
|
@ -112,7 +112,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
];
|
||||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $translation_to_en,
|
||||
];
|
||||
|
@ -155,7 +155,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
];
|
||||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => 'Please enter your Llama username.',
|
||||
];
|
||||
|
@ -189,7 +189,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
// Assume this is the only result, given the random name.
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => '',
|
||||
];
|
||||
|
@ -248,7 +248,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $search, t('Filter'));
|
||||
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $this->randomMachineName(),
|
||||
];
|
||||
|
@ -259,13 +259,13 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
|
||||
$locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: [];
|
||||
$js_file = 'public://' . $config->get('javascript.directory') . '/' . $langcode . '_' . $locale_javascripts[$langcode] . '.js';
|
||||
$this->assertTrue($result = file_exists($js_file), SafeMarkup::format('JavaScript file created: %file', ['%file' => $result ? $js_file : 'not found']));
|
||||
$this->assertTrue($result = file_exists($js_file), new FormattableMarkup('JavaScript file created: %file', ['%file' => $result ? $js_file : 'not found']));
|
||||
|
||||
// Test JavaScript translation rebuilding.
|
||||
file_unmanaged_delete($js_file);
|
||||
$this->assertTrue($result = !file_exists($js_file), SafeMarkup::format('JavaScript file deleted: %file', ['%file' => $result ? $js_file : 'found']));
|
||||
$this->assertTrue($result = !file_exists($js_file), new FormattableMarkup('JavaScript file deleted: %file', ['%file' => $result ? $js_file : 'found']));
|
||||
_locale_rebuild_js($langcode);
|
||||
$this->assertTrue($result = file_exists($js_file), SafeMarkup::format('JavaScript file rebuilt: %file', ['%file' => $result ? $js_file : 'not found']));
|
||||
$this->assertTrue($result = file_exists($js_file), new FormattableMarkup('JavaScript file rebuilt: %file', ['%file' => $result ? $js_file : 'not found']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -309,7 +309,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
// Find the edit path.
|
||||
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
foreach ($bad_translations as $translation) {
|
||||
$edit = [
|
||||
$lid => $translation,
|
||||
|
@ -317,7 +317,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
$this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations'));
|
||||
// Check for a form error on the textarea.
|
||||
$form_class = $this->xpath('//form[@id="locale-translate-edit-form"]//textarea/@class');
|
||||
$this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), 'The string was rejected as unsafe.');
|
||||
$this->assertContains('error', $form_class[0]->getText(), 'The string was rejected as unsafe.');
|
||||
$this->assertNoText(t('The string has been saved.'), 'The string was not saved.');
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +399,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
// Assume this is the only result, given the random name.
|
||||
// We save the lid from the path.
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $translation,
|
||||
];
|
||||
|
@ -503,7 +503,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
|
||||
// Submit the translations without changing the translation.
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $translation->getString(),
|
||||
];
|
||||
|
@ -522,7 +522,7 @@ class LocaleTranslationUiTest extends WebTestBase {
|
|||
|
||||
// Submit the translations with a new translation.
|
||||
$textarea = current($this->xpath('//textarea'));
|
||||
$lid = (string) $textarea[0]['name'];
|
||||
$lid = $textarea->getAttribute('name');
|
||||
$edit = [
|
||||
$lid => $this->randomMachineName(100),
|
||||
];
|
|
@ -1,16 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
|
||||
/**
|
||||
* Base class for testing updates to string translations.
|
||||
*/
|
||||
abstract class LocaleUpdateBase extends WebTestBase {
|
||||
abstract class LocaleUpdateBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Timestamp for an old translation.
|
||||
|
@ -63,6 +63,7 @@ abstract class LocaleUpdateBase extends WebTestBase {
|
|||
// tests.
|
||||
$this->config('locale.settings')
|
||||
->set('translation.import_enabled', TRUE)
|
||||
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
|
||||
->save();
|
||||
}
|
||||
|
||||
|
@ -88,7 +89,7 @@ abstract class LocaleUpdateBase extends WebTestBase {
|
|||
$edit = ['predefined_langcode' => $langcode];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
$this->container->get('language_manager')->reset();
|
||||
$this->assertTrue(\Drupal::languageManager()->getLanguage($langcode), SafeMarkup::format('Language %langcode added.', ['%langcode' => $langcode]));
|
||||
$this->assertTrue(\Drupal::languageManager()->getLanguage($langcode), new FormattableMarkup('Language %langcode added.', ['%langcode' => $langcode]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,7 +139,7 @@ EOF;
|
|||
'status' => FILE_STATUS_PERMANENT,
|
||||
]);
|
||||
file_put_contents($file->getFileUri(), $po_header . $text);
|
||||
touch(drupal_realpath($file->getFileUri()), $timestamp);
|
||||
touch(\Drupal::service('file_system')->realpath($file->getFileUri()), $timestamp);
|
||||
$file->save();
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Tests\Traits\Core\CronRunTrait;
|
||||
|
||||
/**
|
||||
* Tests for using cron to update project interface translations.
|
||||
|
@ -9,6 +11,8 @@ namespace Drupal\locale\Tests;
|
|||
*/
|
||||
class LocaleUpdateCronTest extends LocaleUpdateBase {
|
||||
|
||||
use CronRunTrait;
|
||||
|
||||
protected $batchOutput = [];
|
||||
|
||||
/**
|
||||
|
@ -43,7 +47,7 @@ class LocaleUpdateCronTest extends LocaleUpdateBase {
|
|||
// Prepare for test: Simulate new translations being available.
|
||||
// Change the last updated timestamp of a translation file.
|
||||
$contrib_module_two_uri = 'public://local/contrib_module_two-8.x-2.0-beta4.de._po';
|
||||
touch(drupal_realpath($contrib_module_two_uri), REQUEST_TIME);
|
||||
touch(\Drupal::service('file_system')->realpath($contrib_module_two_uri), REQUEST_TIME);
|
||||
|
||||
// Prepare for test: Simulate that the file has not been checked for a long
|
||||
// time. Set the last_check timestamp to zero.
|
||||
|
@ -80,7 +84,7 @@ class LocaleUpdateCronTest extends LocaleUpdateBase {
|
|||
|
||||
// Check whether tasks are added to the queue.
|
||||
$queue = \Drupal::queue('locale_translation', TRUE);
|
||||
$this->assertEqual($queue->numberOfItems(), 3, 'Queue holds tasks for one project.');
|
||||
$this->assertEqual($queue->numberOfItems(), 2, 'Queue holds tasks for one project.');
|
||||
$item = $queue->claimItem();
|
||||
$queue->releaseItem($item);
|
||||
$this->assertEqual($item->data[1][0], 'contrib_module_two', 'Queue holds tasks for contrib module one.');
|
||||
|
@ -91,7 +95,7 @@ class LocaleUpdateCronTest extends LocaleUpdateBase {
|
|||
|
||||
// Check whether no more tasks are added to the queue.
|
||||
$queue = \Drupal::queue('locale_translation', TRUE);
|
||||
$this->assertEqual($queue->numberOfItems(), 3, 'Queue holds tasks for one project.');
|
||||
$this->assertEqual($queue->numberOfItems(), 2, 'Queue holds tasks for one project.');
|
||||
|
||||
// Ensure last checked is updated to a greater time than the initial value.
|
||||
sleep(1);
|
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Test for proper version fallback in case of a development release.
|
||||
*
|
||||
* @group language
|
||||
*/
|
||||
class LocaleUpdateDevelopmentReleaseTest extends WebTestBase {
|
||||
class LocaleUpdateDevelopmentReleaseTest extends BrowserTestBase {
|
||||
|
||||
public static $modules = ['locale', 'locale_test_development_release'];
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Tests for the user interface of project interface translations.
|
||||
|
@ -86,7 +85,7 @@ class LocaleUpdateInterfaceTest extends LocaleUpdateBase {
|
|||
$release_details = new FormattableMarkup('@module (@version). @info', [
|
||||
'@module' => 'Locale test translate',
|
||||
'@version' => '1.3-dev',
|
||||
'@info' => t('File not found at %local_path', ['%local_path' => 'core/modules/locale/tests/test.de.po'])
|
||||
'@info' => t('File not found at %local_path', ['%local_path' => 'core/modules/locale/tests/test.de.po']),
|
||||
]);
|
||||
$this->assertRaw($release_details->__toString(), 'Release details');
|
||||
|
||||
|
@ -112,7 +111,7 @@ class LocaleUpdateInterfaceTest extends LocaleUpdateBase {
|
|||
// Check if translations are available for Drupal core.
|
||||
$this->drupalGet('admin/reports/translations');
|
||||
$this->assertText(t('Updates for: @project', ['@project' => t('Drupal core')]), 'Translations found');
|
||||
$this->assertText(SafeMarkup::format('@module (@date)', ['@module' => t('Drupal core'), '@date' => format_date(REQUEST_TIME, 'html_date')]), 'Core translation update');
|
||||
$this->assertText(new FormattableMarkup('@module (@date)', ['@module' => t('Drupal core'), '@date' => format_date(REQUEST_TIME, 'html_date')]), 'Core translation update');
|
||||
$update_button = $this->xpath('//input[@type="submit"][@value="' . t('Update translations') . '"]');
|
||||
$this->assertTrue($update_button, 'Update translations button');
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\locale\Tests;
|
||||
namespace Drupal\Tests\locale\Functional;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
|
@ -37,7 +37,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase {
|
|||
/**
|
||||
* The string storage used in this test.
|
||||
*
|
||||
* @var \Drupal\locale\StringStorageInterface;
|
||||
* @var \Drupal\locale\StringStorageInterface
|
||||
*/
|
||||
protected $stringStorage;
|
||||
|
||||
|
@ -187,7 +187,6 @@ class LocaleConfigSubscriberTest extends KernelTestBase {
|
|||
$this->assertNoTranslation($config_name, $langcode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up a configuration string with a translation.
|
||||
*
|
||||
|
|
|
@ -6,7 +6,6 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests that the configurable language manager and locale operate correctly.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\locale\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests deprecations in the locale module.
|
||||
*
|
||||
* @group locale
|
||||
* @group legacy
|
||||
*/
|
||||
class LocaleDeprecationsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['locale', 'system'];
|
||||
|
||||
/**
|
||||
* @expectedDeprecation locale_translation_manual_status() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. It is unused by Drupal core. Duplicate this function in your own extension if you need its behavior.
|
||||
*/
|
||||
public function testLocaleTranslationManualStatusDeprecation() {
|
||||
module_load_include('pages.inc', 'locale');
|
||||
$this->assertNotNull(\locale_translation_manual_status());
|
||||
}
|
||||
|
||||
}
|
|
@ -41,7 +41,6 @@ class LocaleTranslationProjectsTest extends KernelTestBase {
|
|||
\Drupal::state()->set('locale.remove_core_project', TRUE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests locale_translation_clear_cache_projects().
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\locale\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
|
||||
use Drupal\locale\LocaleLookup;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -266,4 +267,66 @@ class LocaleLookupTest extends UnitTestCase {
|
|||
$this->assertTrue($locale_lookup->get('test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests locale lookups with old plural style of translations.
|
||||
*
|
||||
* @param array $translations
|
||||
* The source with translations.
|
||||
* @param string $langcode
|
||||
* The language code of translation string.
|
||||
* @param string $string
|
||||
* The string for translation.
|
||||
* @param bool $is_fix
|
||||
* The flag about expected fix translation.
|
||||
*
|
||||
* @covers ::resolveCacheMiss
|
||||
* @dataProvider providerFixOldPluralTranslationProvider
|
||||
*/
|
||||
public function testFixOldPluralStyleTranslations($translations, $langcode, $string, $is_fix) {
|
||||
$this->storage->expects($this->any())
|
||||
->method('findTranslation')
|
||||
->will($this->returnCallback(function ($argument) use ($translations) {
|
||||
if (isset($translations[$argument['language']][$argument['source']])) {
|
||||
return (object) ['translation' => $translations[$argument['language']][$argument['source']]];
|
||||
}
|
||||
return TRUE;
|
||||
}));
|
||||
$this->languageManager->expects($this->any())
|
||||
->method('getFallbackCandidates')
|
||||
->will($this->returnCallback(function (array $context = []) {
|
||||
switch ($context['langcode']) {
|
||||
case 'by':
|
||||
return ['ru'];
|
||||
}
|
||||
}));
|
||||
$this->cache->expects($this->once())
|
||||
->method('get')
|
||||
->with('locale:' . $langcode . '::anonymous', FALSE);
|
||||
|
||||
$locale_lookup = new LocaleLookup($langcode, '', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
|
||||
$this->assertSame($is_fix, strpos($locale_lookup->get($string), '@count[2]') === FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for testResolveCacheMissWithFallback().
|
||||
*/
|
||||
public function providerFixOldPluralTranslationProvider() {
|
||||
$translations = [
|
||||
'by' => [
|
||||
'word1' => '@count[2] word-by',
|
||||
'word2' => implode(PluralTranslatableMarkup::DELIMITER, ['word-by', '@count[2] word-by']),
|
||||
],
|
||||
'ru' => [
|
||||
'word3' => '@count[2] word-ru',
|
||||
'word4' => implode(PluralTranslatableMarkup::DELIMITER, ['word-ru', '@count[2] word-ru']),
|
||||
],
|
||||
];
|
||||
return [
|
||||
'no-plural' => [$translations, 'by', 'word1', FALSE],
|
||||
'no-plural from other language' => [$translations, 'by', 'word3', FALSE],
|
||||
'plural' => [$translations, 'by', 'word2', TRUE],
|
||||
'plural from other language' => [$translations, 'by', 'word4', TRUE],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ class StringBaseTest extends UnitTestCase {
|
|||
$string->save();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers ::delete
|
||||
*/
|
||||
|
|
Reference in a new issue