first commit

This commit is contained in:
2018-04-23 21:08:22 +02:00
commit c68c1dd9b0
12994 changed files with 1500824 additions and 0 deletions
+52
View 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"});
@@ -0,0 +1,13 @@
name: 'Early translation test'
type: module
description: 'Support module for testing early bootstrap getting of annotations with translations.'
# core: 8.x
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,6 @@
services:
early_translation_test.authentication.early_translation_test:
class: Drupal\early_translation_test\Auth
arguments: ['@entity.manager']
tags:
- { name: authentication_provider, provider_id: 'early_translation_test', priority: 100 }
@@ -0,0 +1,50 @@
<?php
namespace Drupal\early_translation_test;
use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Test authentication provider.
*/
class Auth implements AuthenticationProviderInterface {
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* Constructs an authentication provider object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
*/
public function __construct(EntityManagerInterface $entity_manager) {
// Authentication providers are called early during in the bootstrap.
// Getting the user storage used to result in a circular reference since
// translation involves a call to \Drupal\locale\LocaleLookup that tries to
// get the user roles.
// @see https://www.drupal.org/node/2241461
$this->userStorage = $entity_manager->getStorage('user');
}
/**
* {@inheritdoc}
*/
public function applies(Request $request) {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function authenticate(Request $request) {
return NULL;
}
}
@@ -0,0 +1 @@
test: English test
@@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
theme:
- classy
id: test_default_config
theme: classy
region: content
weight: -40
provider: null
plugin: local_tasks_block
settings:
id: local_tasks_block
label: Tabs
provider: core
label_display: '0'
primary: true
secondary: true
visibility: { }
@@ -0,0 +1,21 @@
# Schema for the configuration files of the Locale Test module.
locale_test.no_translation:
type: config_object
label: 'No traslation settings'
mapping:
test:
type: string
label: 'Test'
# See \Drupal\Tests\locale\Kernel\LocaleConfigSubscriberTest
translatable: true
locale_test.translation:
type: config_object
label: 'translation settings'
mapping:
test:
type: string
label: 'Test'
# See \Drupal\Tests\locale\Kernel\LocaleConfigSubscriberTest
translatable: true
@@ -0,0 +1,15 @@
name: 'Locale test'
type: module
description: 'Support module for locale module testing.'
package: Testing
# version: '1.2'
# core: 8.x
hidden: true
'interface translation project': locale_test
'interface translation server pattern': core/modules/locale/test/test.%language.po
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,15 @@
<?php
/**
* @file
* Install, update and uninstall functions for the locale_test module.
*/
/**
* Implements hook_uninstall().
*/
function locale_test_uninstall() {
// Clear variables.
\Drupal::state()->delete('locale.test_system_info_alter');
\Drupal::state()->delete('locale.test_projects_alter');
}
@@ -0,0 +1,205 @@
<?php
/**
* @file
* Simulate a custom module with a local po file.
*/
use Drupal\Core\Extension\Extension;
use Drupal\Core\StreamWrapper\PublicStream;
/**
* Implements hook_system_info_alter().
*
* Make the test scripts to be believe this is not a hidden test module, but
* a regular custom module.
*/
function locale_test_system_info_alter(&$info, Extension $file, $type) {
// Only modify the system info if required.
// By default the locale_test modules are hidden and have a project specified.
// To test the module detection process by locale_project_list() the
// test modules should mimic a custom module. I.e. be non-hidden.
if (\Drupal::state()->get('locale.test_system_info_alter')) {
if ($file->getName() == 'locale_test' || $file->getName() == 'locale_test_translate') {
// Don't hide the module.
$info['hidden'] = FALSE;
}
}
}
/**
* Implements hook_locale_translation_projects_alter().
*
* The translation status process by default checks the status of the installed
* projects. This function replaces the data of the installed modules by a
* predefined set of modules with fixed file names and release versions. Project
* names, versions, timestamps etc must be fixed because they must match the
* files created by the test script.
*
* The "locale.test_projects_alter" state variable must be set by the
* test script in order for this hook to take effect.
*/
function locale_test_locale_translation_projects_alter(&$projects) {
// Drupal core should not be translated. By overriding the server pattern we
// make sure that no translation for drupal core will be found and that the
// translation update system will not go out to l.d.o to check.
$projects['drupal']['server_pattern'] = 'translations://';
if (\Drupal::state()->get('locale.remove_core_project')) {
unset($projects['drupal']);
}
if (\Drupal::state()->get('locale.test_projects_alter')) {
// Instead of the default ftp.drupal.org we use the file system of the test
// instance to simulate a remote file location.
$url = \Drupal::url('<front>', [], ['absolute' => TRUE]);
$remote_url = $url . PublicStream::basePath() . '/remote/';
// Completely replace the project data with a set of test projects.
$projects = [
'contrib_module_one' => [
'name' => 'contrib_module_one',
'info' => [
'name' => 'Contributed module one',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language._po',
'package' => 'Other',
'version' => '8.x-1.1',
'project' => 'contrib_module_one',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
],
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
],
'contrib_module_two' => [
'name' => 'contrib_module_two',
'info' => [
'name' => 'Contributed module two',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language._po',
'package' => 'Other',
'version' => '8.x-2.0-beta4',
'project' => 'contrib_module_two',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
],
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
],
'contrib_module_three' => [
'name' => 'contrib_module_three',
'info' => [
'name' => 'Contributed module three',
'interface translation server pattern' => $remote_url . '%core/%project/%project-%version.%language._po',
'package' => 'Other',
'version' => '8.x-1.0',
'project' => 'contrib_module_three',
'datestamp' => '1344471537',
'_info_file_ctime' => 1348767306,
],
'datestamp' => '1344471537',
'project_type' => 'module',
'project_status' => TRUE,
],
'locale_test' => [
'name' => 'locale_test',
'info' => [
'name' => 'Locale test',
'interface translation project' => 'locale_test',
'interface translation server pattern' => 'core/modules/locale/tests/test.%language.po',
'package' => 'Other',
'version' => NULL,
'project' => 'locale_test',
'_info_file_ctime' => 1348767306,
'datestamp' => 0,
],
'datestamp' => 0,
'project_type' => 'module',
'project_status' => TRUE,
],
'custom_module_one' => [
'name' => 'custom_module_one',
'info' => [
'name' => 'Custom module one',
'interface translation project' => 'custom_module_one',
'interface translation server pattern' => 'translations://custom_module_one.%language.po',
'package' => 'Other',
'version' => NULL,
'project' => 'custom_module_one',
'_info_file_ctime' => 1348767306,
'datestamp' => 0,
],
'datestamp' => 0,
'project_type' => 'module',
'project_status' => TRUE,
],
];
}
}
/**
* Implements hook_language_fallback_candidates_OPERATION_alter().
*/
function locale_test_language_fallback_candidates_locale_lookup_alter(array &$candidates, array $context) {
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_candidates', $candidates);
\Drupal::state()->set('locale.test_language_fallback_candidates_locale_lookup_alter_context', $context);
}
/**
* Implements hook_theme().
*/
function locale_test_theme() {
$return = [];
$return['locale_test_tokenized'] = [
'variable' => ['content' => ''],
];
return $return;
}
/**
* Implements hook_token_info().
*/
function locale_test_token_info() {
$info = [];
$info['types']['locale_test'] = [
'name' => t('Locale test'),
'description' => t('Locale test'),
];
$info['tokens']['locale_test']['security_test1'] = [
'type' => 'text',
'name' => t('Security test 1'),
];
$info['tokens']['locale_test']['security_test2'] = [
'type' => 'text',
'name' => t('Security test 2'),
];
return $info;
}
/**
* Implements hook_tokens().
*/
function locale_test_tokens($type, $tokens, array $data = [], array $options = []) {
$return = [];
if ($type == 'locale_test') {
foreach ($tokens as $name => $original) {
switch ($name) {
case 'security_test1':
$return[$original] = "javascript:alert('Mooooh!');";
break;
case 'security_test2':
$return[$original] = "<script>alert('Mooooh!');</script>";
break;
}
}
}
return $return;
}
@@ -0,0 +1,13 @@
name: 'Locale Test Development Release'
type: module
description: 'Helper module to test the behaviour when the core verison is a development release.'
package: Testing
# version: VERSION
# core: 8.x
hidden: true
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,42 @@
<?php
/**
* @file
* Simulate a Drupal version.
*/
use Drupal\Core\Extension\Extension;
/**
* Implements hook_system_info_alter().
*
* Change the core version number to a development one for testing.
* 8.0.0-alpha102-dev is the simulated version.
*/
function locale_test_development_release_system_info_alter(&$info, Extension $file, $type) {
if (isset($info['package']) && $info['package'] == 'Core') {
$info['version'] = '8.0.0-alpha102-dev';
}
}
/**
* Implements hook_locale_translation_projects_alter().
*
* Add a contrib module with a dev release to list of translatable modules.
*/
function locale_test_development_release_locale_translation_projects_alter(&$projects) {
$projects['contrib'] = [
'name' => 'contrib',
'info' => [
'name' => 'Contributed module',
'package' => 'Other',
'version' => '12.x-10.4-unstable11+14-dev',
'project' => 'contrib_module',
'datestamp' => '0',
'_info_file_ctime' => 1442933959,
],
'datestamp' => '0',
'project_type' => 'module',
'project_status' => TRUE,
];
}
@@ -0,0 +1,3 @@
translatable_no_default: ''
translatable_default_with_translation: 'Locale can translate'
translatable_default_with_no_translation: 'Locale can not translate'
@@ -0,0 +1,10 @@
locale_test_translate.settings:
type: config_object
label: 'Test for locale translations'
mapping:
translatable_no_default:
type: label
translatable_default_with_translation:
type: label
translatable_default_with_no_translation:
type: label
@@ -0,0 +1,15 @@
name: 'Locale test translate'
type: module
description: 'Translation test module for locale module testing.'
package: Testing
# version: '1.3'
# core: 8.x
hidden: true
'interface translation project': locale_test_translate
'interface translation server pattern': core/modules/locale/tests/test.%language.po
# Information added by Drupal.org packaging script on 2017-04-19
version: '8.3.1'
core: '8.x'
project: 'drupal'
datestamp: 1492622988
@@ -0,0 +1,22 @@
<?php
/**
* @file
* Simulates a custom module with a local po file.
*/
use Drupal\Core\Extension\Extension;
/**
* Implements hook_system_info_alter().
*
* By default this modules is hidden but once enabled it behaves like a normal
* (not hidden) module. This hook implementation changes the .info.yml data by
* setting the hidden status to FALSE.
*/
function locale_test_translate_system_info_alter(&$info, Extension $file, $type) {
if ($file->getName() == 'locale_test_translate') {
// Don't hide the module.
$info['hidden'] = FALSE;
}
}
@@ -0,0 +1,202 @@
<?php
namespace Drupal\Tests\locale\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\Core\Language\LanguageInterface;
use Drupal\node\NodeInterface;
/**
* Tests you can enable multilingual support on content types and configure a
* language for a node.
*
* @group locale
*/
class LocaleContentTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'locale'];
/**
* Verifies that machine name fields are always LTR.
*/
public function testMachineNameLTR() {
// User to add and remove language.
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages', 'administer site configuration']);
// Log in as admin.
$this->drupalLogin($admin_user);
// Verify that the machine name field is LTR for a new content type.
$this->drupalGet('admin/structure/types/add');
$this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when no additional language is configured.');
// Install the Arabic language (which is RTL) and configure as the default.
$edit = [];
$edit['predefined_langcode'] = 'ar';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$edit = [
'site_default_language' => 'ar',
];
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
// Verify that the machine name field is still LTR for a new content type.
$this->drupalGet('admin/structure/types/add');
$this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when the default language is RTL.');
}
/**
* Test if a content type can be set to multilingual and language is present.
*/
public function testContentTypeLanguageConfiguration() {
$type1 = $this->drupalCreateContentType();
$type2 = $this->drupalCreateContentType();
// User to add and remove language.
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages']);
// User to create a node.
$web_user = $this->drupalCreateUser(["create {$type1->id()} content", "create {$type2->id()} content", "edit any {$type2->id()} content"]);
// Add custom language.
$this->drupalLogin($admin_user);
// Code for the language.
$langcode = 'xx';
// The English name for the language.
$name = $this->randomMachineName(16);
$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 the content type to use multilingual support.
$this->drupalGet("admin/structure/types/manage/{$type2->id()}");
$this->assertText(t('Language settings'), 'Multilingual support widget present on content type configuration form.');
$edit = [
'language_configuration[language_alterable]' => TRUE,
];
$this->drupalPostForm("admin/structure/types/manage/{$type2->id()}", $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', ['%type' => $type2->label()]));
$this->drupalLogout();
\Drupal::languageManager()->reset();
// Verify language selection is not present on the node add form.
$this->drupalLogin($web_user);
$this->drupalGet("node/add/{$type1->id()}");
// Verify language select list is not present.
$this->assertNoFieldByName('langcode[0][value]', NULL, 'Language select not present on the node add form.');
// Verify language selection appears on the node add form.
$this->drupalGet("node/add/{$type2->id()}");
// Verify language select list is present.
$this->assertFieldByName('langcode[0][value]', NULL, 'Language select present on the node add form.');
// Ensure language appears.
$this->assertText($name, 'Language present.');
// Create a node.
$node_title = $this->randomMachineName();
$node_body = $this->randomMachineName();
$edit = [
'type' => $type2->id(),
'title' => $node_title,
'body' => [['value' => $node_body]],
'langcode' => $langcode,
];
$node = $this->drupalCreateNode($edit);
// Edit the content and ensure correct language is selected.
$path = 'node/' . $node->id() . '/edit';
$this->drupalGet($path);
$this->assertRaw('<option value="' . $langcode . '" selected="selected">' . $name . '</option>', 'Correct language selected.');
// Ensure we can change the node language.
$edit = [
'langcode[0][value]' => 'en',
];
$this->drupalPostForm($path, $edit, t('Save'));
$this->assertText(t('@title has been updated.', ['@title' => $node_title]));
// Verify that the creation message contains a link to a node.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'node/' . $node->id()]);
$this->assert(isset($view_link), 'The message area contains the link to the edited node');
$this->drupalLogout();
}
/**
* Test if a dir and lang tags exist in node's attributes.
*/
public function testContentTypeDirLang() {
$type = $this->drupalCreateContentType();
// User to add and remove language.
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages']);
// User to create a node.
$web_user = $this->drupalCreateUser(["create {$type->id()} content", "edit own {$type->id()} content"]);
// Log in as admin.
$this->drupalLogin($admin_user);
// Install Arabic language.
$edit = [];
$edit['predefined_langcode'] = 'ar';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
// Install Spanish language.
$edit = [];
$edit['predefined_langcode'] = 'es';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
\Drupal::languageManager()->reset();
// Set the content type to use multilingual support.
$this->drupalGet("admin/structure/types/manage/{$type->id()}");
$edit = [
'language_configuration[language_alterable]' => TRUE,
];
$this->drupalPostForm("admin/structure/types/manage/{$type->id()}", $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', ['%type' => $type->label()]));
$this->drupalLogout();
// Log in as web user to add new node.
$this->drupalLogin($web_user);
// Create three nodes: English, Arabic and Spanish.
$nodes = [];
foreach (['en', 'es', 'ar'] as $langcode) {
$nodes[$langcode] = $this->drupalCreateNode([
'langcode' => $langcode,
'type' => $type->id(),
'promote' => NodeInterface::PROMOTED,
]);
}
// Check if English node does not have lang tag.
$this->drupalGet('node/' . $nodes['en']->id());
$element = $this->cssSelect('article.node[lang="en"]');
$this->assertTrue(empty($element), 'The lang tag has not been assigned to the English node.');
// Check if English node does not have dir tag.
$element = $this->cssSelect('article.node[dir="ltr"]');
$this->assertTrue(empty($element), 'The dir tag has not been assigned to the English node.');
// Check if Arabic node has lang="ar" & dir="rtl" tags.
$this->drupalGet('node/' . $nodes['ar']->id());
$element = $this->cssSelect('article.node[lang="ar"][dir="rtl"]');
$this->assertTrue(!empty($element), 'The lang and dir tags have been assigned correctly to the Arabic node.');
// Check if Spanish node has lang="es" tag.
$this->drupalGet('node/' . $nodes['es']->id());
$element = $this->cssSelect('article.node[lang="es"]');
$this->assertTrue(!empty($element), 'The lang tag has been assigned correctly to the Spanish node.');
// Check if Spanish node does not have dir="ltr" tag.
$element = $this->cssSelect('article.node[lang="es"][dir="ltr"]');
$this->assertTrue(empty($element), 'The dir tag has not been assigned to the Spanish node.');
}
}
@@ -0,0 +1,58 @@
<?php
namespace Drupal\Tests\locale\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests LocaleLookup.
*
* @group locale
*/
class LocaleLocaleLookupTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['locale', 'locale_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Change the language default object to different values.
ConfigurableLanguage::createFromLangcode('fr')->save();
$this->config('system.site')->set('default_langcode', 'fr')->save();
$this->drupalLogin($this->rootUser);
}
/**
* Tests that there are no circular dependencies.
*/
public function testCircularDependency() {
// Ensure that we can enable early_translation_test on a non-english site.
$this->drupalPostForm('admin/modules', ['modules[early_translation_test][enable]' => TRUE], t('Install'));
$this->assertResponse(200);
}
/**
* Test language fallback defaults.
*/
public function testLanguageFallbackDefaults() {
$this->drupalGet('');
// Ensure state of fallback languages persisted by
// locale_test_language_fallback_candidates_locale_lookup_alter() is empty.
$this->assertEqual(\Drupal::state()->get('locale.test_language_fallback_candidates_locale_lookup_alter_candidates'), []);
// Make sure there is enough information provided for alter hooks.
$context = \Drupal::state()->get('locale.test_language_fallback_candidates_locale_lookup_alter_context');
$this->assertEqual($context['langcode'], 'fr');
$this->assertEqual($context['operation'], 'locale_lookup');
}
}
@@ -0,0 +1,151 @@
<?php
namespace Drupal\Tests\locale\Functional;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
/**
* Tests you can configure a language for individual URL aliases.
*
* @group locale
*/
class LocalePathTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'locale', 'path', 'views'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->config('system.site')->set('page.front', '/node')->save();
}
/**
* Test if a language can be associated with a path alias.
*/
public function testPathLanguageConfiguration() {
// User to add and remove language.
$admin_user = $this->drupalCreateUser(['administer languages', 'create page content', 'administer url aliases', 'create url aliases', 'access administration pages', 'access content overview']);
// Add custom language.
$this->drupalLogin($admin_user);
// Code for the language.
$langcode = 'xx';
// 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'));
// Check that the "xx" front page is readily available because path prefix
// negotiation is pre-configured.
$this->drupalGet($prefix);
$this->assertText(t('Welcome to Drupal'), 'The "xx" front page is readibly available.');
// Create a node.
$node = $this->drupalCreateNode(['type' => 'page']);
// Create a path alias in default language (English).
$path = 'admin/config/search/path/add';
$english_path = $this->randomMachineName(8);
$edit = [
'source' => '/node/' . $node->id(),
'alias' => '/' . $english_path,
'langcode' => 'en',
];
$this->drupalPostForm($path, $edit, t('Save'));
// Create a path alias in new custom language.
$custom_language_path = $this->randomMachineName(8);
$edit = [
'source' => '/node/' . $node->id(),
'alias' => '/' . $custom_language_path,
'langcode' => $langcode,
];
$this->drupalPostForm($path, $edit, t('Save'));
// Confirm English language path alias works.
$this->drupalGet($english_path);
$this->assertText($node->label(), 'English alias works.');
// Confirm custom language path alias works.
$this->drupalGet($prefix . '/' . $custom_language_path);
$this->assertText($node->label(), 'Custom language alias works.');
// Create a custom path.
$custom_path = $this->randomMachineName(8);
// Check priority of language for alias by source path.
$edit = [
'source' => '/node/' . $node->id(),
'alias' => '/' . $custom_path,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
];
$this->container->get('path.alias_storage')->save($edit['source'], $edit['alias'], $edit['langcode']);
$lookup_path = $this->container->get('path.alias_manager')->getAliasByPath('/node/' . $node->id(), 'en');
$this->assertEqual('/' . $english_path, $lookup_path, 'English language alias has priority.');
// Same check for language 'xx'.
$lookup_path = $this->container->get('path.alias_manager')->getAliasByPath('/node/' . $node->id(), $prefix);
$this->assertEqual('/' . $custom_language_path, $lookup_path, 'Custom language alias has priority.');
$this->container->get('path.alias_storage')->delete($edit);
// Create language nodes to check priority of aliases.
$first_node = $this->drupalCreateNode(['type' => 'page', 'promote' => 1, 'langcode' => 'en']);
$second_node = $this->drupalCreateNode(['type' => 'page', 'promote' => 1, 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED]);
// Assign a custom path alias to the first node with the English language.
$edit = [
'source' => '/node/' . $first_node->id(),
'alias' => '/' . $custom_path,
'langcode' => $first_node->language()->getId(),
];
$this->container->get('path.alias_storage')->save($edit['source'], $edit['alias'], $edit['langcode']);
// Assign a custom path alias to second node with
// LanguageInterface::LANGCODE_NOT_SPECIFIED.
$edit = [
'source' => '/node/' . $second_node->id(),
'alias' => '/' . $custom_path,
'langcode' => $second_node->language()->getId(),
];
$this->container->get('path.alias_storage')->save($edit['source'], $edit['alias'], $edit['langcode']);
// Test that both node titles link to our path alias.
$this->drupalGet('admin/content');
$custom_path_url = Url::fromUserInput('/' . $custom_path)->toString();
$elements = $this->xpath('//a[@href=:href and normalize-space(text())=:title]', [':href' => $custom_path_url, ':title' => $first_node->label()]);
$this->assertTrue(!empty($elements), 'First node links to the path alias.');
$elements = $this->xpath('//a[@href=:href and normalize-space(text())=:title]', [':href' => $custom_path_url, ':title' => $second_node->label()]);
$this->assertTrue(!empty($elements), 'Second node links to the path alias.');
// Confirm that the custom path leads to the first node.
$this->drupalGet($custom_path);
$this->assertText($first_node->label(), 'Custom alias returns first node.');
// Confirm that the custom path with prefix leads to the second node.
$this->drupalGet($prefix . '/' . $custom_path);
$this->assertText($second_node->label(), 'Custom alias with prefix returns second node.');
}
}
@@ -0,0 +1,205 @@
<?php
namespace Drupal\Tests\locale\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the locale string storage, string objects and data API.
*
* @group locale
*/
class LocaleStringTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['locale'];
/**
* The locale storage.
*
* @var \Drupal\locale\StringStorageInterface
*/
protected $storage;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Add a default locale storage for all these tests.
$this->storage = $this->container->get('locale.storage');
// Create two languages: Spanish and German.
foreach (['es', 'de'] as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
}
/**
* Test CRUD API.
*/
public function testStringCRUDAPI() {
// Create source string.
$source = $this->buildSourceString();
$source->save();
$this->assertTrue($source->lid, format_string('Successfully created string %string', ['%string' => $source->source]));
// Load strings by lid and source.
$string1 = $this->storage->findString(['lid' => $source->lid]);
$this->assertEqual($source, $string1, 'Successfully retrieved string by identifier.');
$string2 = $this->storage->findString(['source' => $source->source, 'context' => $source->context]);
$this->assertEqual($source, $string2, 'Successfully retrieved string by source and context.');
$string3 = $this->storage->findString(['source' => $source->source, 'context' => '']);
$this->assertFalse($string3, 'Cannot retrieve string with wrong context.');
// Check version handling and updating.
$this->assertEqual($source->version, 'none', 'String originally created without version.');
$string = $this->storage->findTranslation(['lid' => $source->lid]);
$this->assertEqual($string->version, \Drupal::VERSION, 'Checked and updated string version to Drupal version.');
// Create translation and find it by lid and source.
$langcode = 'es';
$translation = $this->createTranslation($source, $langcode);
$this->assertEqual($translation->customized, LOCALE_NOT_CUSTOMIZED, 'Translation created as not customized by default.');
$string1 = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
$this->assertEqual($string1->translation, $translation->translation, 'Successfully loaded translation by string identifier.');
$string2 = $this->storage->findTranslation(['language' => $langcode, 'source' => $source->source, 'context' => $source->context]);
$this->assertEqual($string2->translation, $translation->translation, 'Successfully loaded translation by source and context.');
$translation
->setCustomized()
->save();
$translation = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
$this->assertEqual($translation->customized, LOCALE_CUSTOMIZED, 'Translation successfully marked as customized.');
// Delete translation.
$translation->delete();
$deleted = $this->storage->findTranslation(['language' => $langcode, 'lid' => $source->lid]);
$this->assertFalse(isset($deleted->translation), 'Successfully deleted translation string.');
// Create some translations and then delete string and all of its
// translations.
$lid = $source->lid;
$this->createAllTranslations($source);
$search = $this->storage->getTranslations(['lid' => $source->lid]);
$this->assertEqual(count($search), 3, 'Created and retrieved all translations for our source string.');
$source->delete();
$string = $this->storage->findString(['lid' => $lid]);
$this->assertFalse($string, 'Successfully deleted source string.');
$deleted = $search = $this->storage->getTranslations(['lid' => $lid]);
$this->assertFalse($deleted, 'Successfully deleted all translation strings.');
// Tests that locations of different types and arbitrary lengths can be
// added to a source string. Too long locations will be cut off.
$source_string = $this->buildSourceString();
$source_string->addLocation('javascript', $this->randomString(8));
$source_string->addLocation('configuration', $this->randomString(50));
$source_string->addLocation('code', $this->randomString(100));
$source_string->addLocation('path', $location = $this->randomString(300));
$source_string->save();
$rows = db_query('SELECT * FROM {locales_location} WHERE sid = :sid', [':sid' => $source_string->lid])->fetchAllAssoc('type');
$this->assertEqual(count($rows), 4, '4 source locations have been persisted.');
$this->assertEqual($rows['path']->name, substr($location, 0, 255), 'Too long location has been limited to 255 characters.');
}
/**
* Test Search API loading multiple objects.
*/
public function testStringSearchAPI() {
$language_count = 3;
// Strings 1 and 2 will have some common prefix.
// Source 1 will have all translations, not customized.
// Source 2 will have all translations, customized.
// Source 3 will have no translations.
$prefix = $this->randomMachineName(100);
$source1 = $this->buildSourceString(['source' => $prefix . $this->randomMachineName(100)])->save();
$source2 = $this->buildSourceString(['source' => $prefix . $this->randomMachineName(100)])->save();
$source3 = $this->buildSourceString()->save();
// Load all source strings.
$strings = $this->storage->getStrings([]);
$this->assertEqual(count($strings), 3, 'Found 3 source strings in the database.');
// Load all source strings matching a given string.
$filter_options['filters'] = ['source' => $prefix];
$strings = $this->storage->getStrings([], $filter_options);
$this->assertEqual(count($strings), 2, 'Found 2 strings using some string filter.');
// Not customized translations.
$translate1 = $this->createAllTranslations($source1);
// Customized translations.
$this->createAllTranslations($source2, ['customized' => LOCALE_CUSTOMIZED]);
// Try quick search function with different field combinations.
$langcode = 'es';
$found = $this->storage->findTranslation(['language' => $langcode, 'source' => $source1->source, 'context' => $source1->context]);
$this->assertTrue($found && isset($found->language) && isset($found->translation) && !$found->isNew(), 'Translation found searching by source and context.');
$this->assertEqual($found->translation, $translate1[$langcode]->translation, 'Found the right translation.');
// Now try a translation not found.
$found = $this->storage->findTranslation(['language' => $langcode, 'source' => $source3->source, 'context' => $source3->context]);
$this->assertTrue($found && $found->lid == $source3->lid && !isset($found->translation) && $found->isNew(), 'Translation not found but source string found.');
// Load all translations. For next queries we'll be loading only translated
// strings.
$translations = $this->storage->getTranslations(['translated' => TRUE]);
$this->assertEqual(count($translations), 2 * $language_count, 'Created and retrieved all translations for source strings.');
// Load all customized translations.
$translations = $this->storage->getTranslations(['customized' => LOCALE_CUSTOMIZED, 'translated' => TRUE]);
$this->assertEqual(count($translations), $language_count, 'Retrieved all customized translations for source strings.');
// Load all Spanish customized translations.
$translations = $this->storage->getTranslations(['language' => 'es', 'customized' => LOCALE_CUSTOMIZED, 'translated' => TRUE]);
$this->assertEqual(count($translations), 1, 'Found only Spanish and customized translations.');
// Load all source strings without translation (1).
$translations = $this->storage->getStrings(['translated' => FALSE]);
$this->assertEqual(count($translations), 1, 'Found 1 source string without translations.');
// Load Spanish translations using string filter.
$filter_options['filters'] = ['source' => $prefix];
$translations = $this->storage->getTranslations(['language' => 'es'], $filter_options);
$this->assertEqual(count($translations), 2, 'Found 2 translations using some string filter.');
}
/**
* Creates random source string object.
*
* @return \Drupal\locale\StringInterface
* A locale string.
*/
public function buildSourceString($values = []) {
return $this->storage->createString($values += [
'source' => $this->randomMachineName(100),
'context' => $this->randomMachineName(20),
]);
}
/**
* Creates translations for source string and all languages.
*/
public function createAllTranslations($source, $values = []) {
$list = [];
/* @var $language_manager \Drupal\Core\Language\LanguageManagerInterface */
$language_manager = $this->container->get('language_manager');
foreach ($language_manager->getLanguages() as $language) {
$list[$language->getId()] = $this->createTranslation($source, $language->getId(), $values);
}
return $list;
}
/**
* Creates single translation for source string.
*/
public function createTranslation($source, $langcode, $values = []) {
return $this->storage->createTranslation($values + [
'lid' => $source->lid,
'language' => $langcode,
'translation' => $this->randomMachineName(100),
])->save();
}
}
@@ -0,0 +1,91 @@
<?php
namespace Drupal\Tests\locale\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Adds and configures languages to check field schema definition.
*
* @group locale
*/
class LocaleTranslatedSchemaDefinitionTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'locale', 'node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
ConfigurableLanguage::createFromLangcode('fr')->save();
$this->config('system.site')->set('default_langcode', 'fr')->save();
// Make sure new entity type definitions are processed.
\Drupal::service('entity.definition_update_manager')->applyUpdates();
// Clear all caches so that the base field definition, its cache in the
// entity manager, the t() cache, etc. are all cleared.
drupal_flush_all_caches();
}
/**
* Tests that translated field descriptions do not affect the update system.
*/
public function testTranslatedSchemaDefinition() {
/** @var \Drupal\locale\StringDatabaseStorage $stringStorage */
$stringStorage = \Drupal::service('locale.storage');
$source = $stringStorage->createString([
'source' => 'Revision ID',
])->save();
$stringStorage->createTranslation([
'lid' => $source->lid,
'language' => 'fr',
'translation' => 'Translated Revision ID',
])->save();
// Ensure that the field is translated when access through the API.
$this->assertEqual('Translated Revision ID', \Drupal::entityManager()->getBaseFieldDefinitions('node')['vid']->getLabel());
// Assert there are no updates.
$this->assertFalse(\Drupal::service('entity.definition_update_manager')->needsUpdates());
}
/**
* Tests that translations do not affect the update system.
*/
public function testTranslatedUpdate() {
// Visit the update page to collect any strings that may be translatable.
$user = $this->drupalCreateUser(['administer software updates']);
$this->drupalLogin($user);
$update_url = $GLOBALS['base_url'] . '/update.php';
$this->drupalGet($update_url, ['external' => TRUE]);
/** @var \Drupal\locale\StringDatabaseStorage $stringStorage */
$stringStorage = \Drupal::service('locale.storage');
$sources = $stringStorage->getStrings();
// Translate all source strings found.
foreach ($sources as $source) {
$stringStorage->createTranslation([
'lid' => $source->lid,
'language' => 'fr',
'translation' => $this->randomMachineName(100),
])->save();
}
// Ensure that there are no updates just due to translations. Check for
// markup and a link instead of specific text because text may be
// translated.
$this->drupalGet($update_url . '/selection', ['external' => TRUE]);
$this->assertRaw('messages--status', 'No pending updates.');
$this->assertNoLinkByHref('fr/update.php/run', 'No link to run updates.');
}
}
@@ -0,0 +1,123 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\block\Entity\Block;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests that the locale config manager operates correctly.
*
* @group locale
*/
class LocaleConfigManagerTest extends KernelTestBase {
/**
* A list of modules to install for this test.
*
* @var array
*/
public static $modules = ['system', 'language', 'locale', 'locale_test', 'block'];
/**
* This test creates simple config on the fly breaking schema checking.
*
* @var bool
*/
protected $strictConfigSchema = FALSE;
/**
* Tests hasTranslation().
*/
public function testHasTranslation() {
$this->installSchema('locale', ['locales_location', 'locales_source', 'locales_target']);
$this->installConfig(['locale_test']);
$locale_config_manager = \Drupal::service('locale.config_manager');
$language = ConfigurableLanguage::createFromLangcode('de');
$language->save();
$result = $locale_config_manager->hasTranslation('locale_test.no_translation', $language->getId());
$this->assertFalse($result, 'There is no translation for locale_test.no_translation configuration.');
$result = $locale_config_manager->hasTranslation('locale_test.translation', $language->getId());
$this->assertTrue($result, 'There is a translation for locale_test.translation configuration.');
}
/**
* Tests getStringTranslation().
*/
public function testGetStringTranslation() {
$this->installSchema('locale', ['locales_location', 'locales_source', 'locales_target']);
$this->installConfig(['locale_test']);
$locale_config_manager = \Drupal::service('locale.config_manager');
$language = ConfigurableLanguage::createFromLangcode('de');
$language->save();
$translation_before = $locale_config_manager->getStringTranslation('locale_test.no_translation', $language->getId(), 'Test', '');
$this->assertTrue($translation_before->isNew());
$translation_before->setString('translation')->save();
$translation_after = $locale_config_manager->getStringTranslation('locale_test.no_translation', $language->getId(), 'Test', '');
$this->assertFalse($translation_after->isNew());
$translation_after->setString('updated_translation')->save();
}
/**
* Tests getDefaultConfigLangcode().
*/
public function testGetDefaultConfigLangcode() {
// Install the Language module's configuration so we can use the
// module_installer service.
$this->installConfig(['language']);
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('locale_test_translate.settings'), 'Before installing a module the locale config manager can not access the shipped configuration.');
\Drupal::service('module_installer')->install(['locale_test_translate']);
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('locale_test_translate.settings'), 'After installing a module the locale config manager can get the shipped configuration langcode.');
$simple_config = \Drupal::configFactory()->getEditable('locale_test_translate.simple_config_extra');
$simple_config->set('foo', 'bar')->save();
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode($simple_config->getName()), 'Simple config created through the API is not treated as shipped configuration.');
$block = Block::create([
'id' => 'test_default_config',
'theme' => 'classy',
'status' => TRUE,
'region' => 'content',
'plugin' => 'local_tasks_block',
'settings' => [
'id' => 'local_tasks_block',
'label' => $this->randomMachineName(),
'provider' => 'core',
'label_display' => FALSE,
'primary' => TRUE,
'secondary' => TRUE,
],
]);
$block->save();
// Install the theme after creating the block as installing the theme will
// install the block provided by the locale_test module.
\Drupal::service('theme_installer')->install(['classy']);
// The test_default_config block provided by the locale_test module will not
// be installed because a block with the same ID already exists.
$this->installConfig(['locale_test']);
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('block.block.test_default_config'), 'The block.block.test_default_config is not shipped configuration.');
// Delete the block so we can install the one provided by the locale_test
// module.
$block->delete();
$this->installConfig(['locale_test']);
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('block.block.test_default_config'), 'The block.block.test_default_config is shipped configuration.');
// Test the special case for configurable_language config entities.
$fr_language = ConfigurableLanguage::createFromLangcode('fr');
$fr_language->save();
$this->assertEqual('en', \Drupal::service('locale.config_manager')->getDefaultConfigLangcode('language.entity.fr'), 'The language.entity.fr is treated as shipped configuration because it is a configurable_language config entity and in the standard language list.');
$custom_language = ConfigurableLanguage::createFromLangcode('custom');
$custom_language->save();
$this->assertNull(\Drupal::service('locale.config_manager')->getDefaultConfigLangcode('language.entity.custom'), 'The language.entity.custom is not shipped configuration because it is not in the standard language list.');
}
}
@@ -0,0 +1,168 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\Language;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests default configuration handling with a foreign default language.
*
* @group locale
*/
class LocaleConfigSubscriberForeignTest extends LocaleConfigSubscriberTest {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
parent::register($container);
$language = Language::$defaultValues;
$language['id'] = 'hu';
$language['name'] = 'Hungarian';
$container->setParameter('language.default_values', $language);
}
/**
* {@inheritdoc}
*/
protected function setUpLanguages() {
parent::setUpLanguages();
ConfigurableLanguage::createFromLangcode('hu')->save();
}
/**
* {@inheritdoc}
*/
protected function setUpLocale() {
parent::setUpLocale();
$this->setUpTranslation('locale_test.translation', 'test', 'English test', 'Hungarian test', 'hu', TRUE);
}
/**
* Tests that the language of default configuration was updated.
*/
public function testDefaultConfigLanguage() {
$this->assertEqual('hu', $this->configFactory->getEditable('locale_test.no_translation')->get('langcode'));
$this->assertEqual('hu', $this->configFactory->getEditable('locale_test.translation')->get('langcode'));
$this->assertEqual($this->configFactory->getEditable('locale_test.translation')->get('test'), 'Hungarian test');
}
/**
* Tests creating translations of shipped configuration.
*/
public function testCreateActiveTranslation() {
$config_name = 'locale_test.no_translation';
$this->saveLanguageActive($config_name, 'test', 'Test (Hungarian)', 'hu');
$this->assertTranslation($config_name, 'Test (Hungarian)', 'hu');
}
/**
* Tests importing community translations of shipped configuration.
*/
public function testLocaleCreateActiveTranslation() {
$config_name = 'locale_test.no_translation';
$this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (Hungarian)', 'hu', TRUE);
$this->assertTranslation($config_name, 'Test (Hungarian)', 'hu', FALSE);
}
/**
* Tests updating translations of shipped configuration.
*/
public function testUpdateActiveTranslation() {
$config_name = 'locale_test.translation';
$this->saveLanguageActive($config_name, 'test', 'Updated Hungarian test', 'hu');
$this->assertTranslation($config_name, 'Updated Hungarian test', 'hu');
}
/**
* Tests updating community translations of shipped configuration.
*/
public function testLocaleUpdateActiveTranslation() {
$config_name = 'locale_test.translation';
$this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated Hungarian test', 'hu', TRUE);
$this->assertTranslation($config_name, 'Updated Hungarian test', 'hu', FALSE);
}
/**
* Tests deleting a translation override.
*/
public function testDeleteTranslation() {
$config_name = 'locale_test.translation';
$this->deleteLanguageOverride($config_name, 'test', 'English test', 'de');
// The German translation in this case will be forced to the Hungarian
// source so its not overwritten with locale data later.
$this->assertTranslation($config_name, 'Hungarian test', 'de');
}
/**
* Tests deleting translations of shipped configuration.
*/
public function testDeleteActiveTranslation() {
$config_name = 'locale_test.translation';
$this->configFactory->getEditable($config_name)->delete();
// Deleting active configuration should not change the locale translation.
$this->assertTranslation($config_name, 'Hungarian test', 'hu', FALSE);
}
/**
* Tests deleting community translations of shipped configuration.
*/
public function testLocaleDeleteActiveTranslation() {
$config_name = 'locale_test.translation';
$this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'hu');
// Deleting the locale translation should not change active config.
$this->assertEqual($this->configFactory->getEditable($config_name)->get('test'), 'Hungarian test');
}
/**
* Tests that adding English creates a translation override.
*/
public function testEnglish() {
$config_name = 'locale_test.translation';
ConfigurableLanguage::createFromLangcode('en')->save();
// Adding a language on the UI would normally call updateConfigTranslations.
$this->localeConfigManager->updateConfigTranslations([$config_name], ['en']);
$this->assertConfigOverride($config_name, 'test', 'English test', 'en');
$this->configFactory->getEditable('locale.settings')->set('translate_english', TRUE)->save();
$this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated English test', 'en');
$this->assertTranslation($config_name, 'Updated English test', 'en', FALSE);
$this->saveLanguageOverride($config_name, 'test', 'Updated English', 'en');
$this->assertTranslation($config_name, 'Updated English', 'en');
$this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'en');
$this->assertNoConfigOverride($config_name, 'en');
}
/**
* Saves a language override.
*
* This will invoke LocaleConfigSubscriber through the event dispatcher. To
* make sure the configuration was persisted correctly, the configuration
* value is checked. Because LocaleConfigSubscriber temporarily disables the
* override state of the configuration factory we check that the correct value
* is restored afterwards.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $value
* The configuration value to save.
* @param string $langcode
* The language code.
*/
protected function saveLanguageActive($config_name, $key, $value, $langcode) {
$this
->configFactory
->getEditable($config_name)
->set($key, $value)
->save();
$this->assertActiveConfig($config_name, $key, $value, $langcode);
}
}
@@ -0,0 +1,486 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\locale\Locale;
use Drupal\locale\StringInterface;
use Drupal\locale\TranslationString;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests that shipped configuration translations are updated correctly.
*
* @group locale
*/
class LocaleConfigSubscriberTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'locale', 'system', 'locale_test'];
/**
* The configurable language manager used in this test.
*
* @var \Drupal\language\ConfigurableLanguageManagerInterface
*/
protected $languageManager;
/**
* The configuration factory used in this test.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The string storage used in this test.
*
* @var \Drupal\locale\StringStorageInterface;
*/
protected $stringStorage;
/**
* The locale configuration manager used in this test.
*
* @var \Drupal\locale\LocaleConfigManager
*/
protected $localeConfigManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->setUpDefaultLanguage();
$this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']);
$this->setupLanguages();
$this->installConfig(['locale_test']);
// Simulate this hook invoked which would happen if in a non-kernel test
// or normal environment.
// @see locale_modules_installed()
// @see locale_system_update()
locale_system_set_config_langcodes();
$langcodes = array_keys(\Drupal::languageManager()->getLanguages());
$names = Locale::config()->getComponentNames();
Locale::config()->updateConfigTranslations($names, $langcodes);
$this->configFactory = $this->container->get('config.factory');
$this->stringStorage = $this->container->get('locale.storage');
$this->localeConfigManager = $this->container->get('locale.config_manager');
$this->languageManager = $this->container->get('language_manager');
$this->setUpLocale();
}
/**
* Sets up default language for this test.
*/
protected function setUpDefaultLanguage() {
// Keep the default English.
}
/**
* Sets up languages needed for this test.
*/
protected function setUpLanguages() {
ConfigurableLanguage::createFromLangcode('de')->save();
}
/**
* Sets up the locale storage strings to be in line with configuration.
*/
protected function setUpLocale() {
// Set up the locale database the same way we have in the config samples.
$this->setUpNoTranslation('locale_test.no_translation', 'test', 'Test', 'de');
$this->setUpTranslation('locale_test.translation', 'test', 'English test', 'German test', 'de');
}
/**
* Tests creating translations of shipped configuration.
*/
public function testCreateTranslation() {
$config_name = 'locale_test.no_translation';
$this->saveLanguageOverride($config_name, 'test', 'Test (German)', 'de');
$this->assertTranslation($config_name, 'Test (German)', 'de');
}
/**
* Tests importing community translations of shipped configuration.
*/
public function testLocaleCreateTranslation() {
$config_name = 'locale_test.no_translation';
$this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (German)', 'de');
$this->assertTranslation($config_name, 'Test (German)', 'de', FALSE);
}
/**
* Tests updating translations of shipped configuration.
*/
public function testUpdateTranslation() {
$config_name = 'locale_test.translation';
$this->saveLanguageOverride($config_name, 'test', 'Updated German test', 'de');
$this->assertTranslation($config_name, 'Updated German test', 'de');
}
/**
* Tests updating community translations of shipped configuration.
*/
public function testLocaleUpdateTranslation() {
$config_name = 'locale_test.translation';
$this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated German test', 'de');
$this->assertTranslation($config_name, 'Updated German test', 'de', FALSE);
}
/**
* Tests deleting translations of shipped configuration.
*/
public function testDeleteTranslation() {
$config_name = 'locale_test.translation';
$this->deleteLanguageOverride($config_name, 'test', 'English test', 'de');
// Instead of deleting the translation, we need to keep a translation with
// the source value and mark it as customized to prevent the deletion being
// reverted by importing community translations.
$this->assertTranslation($config_name, 'English test', 'de');
}
/**
* Tests deleting community translations of shipped configuration.
*/
public function testLocaleDeleteTranslation() {
$config_name = 'locale_test.translation';
$this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'de');
$this->assertNoTranslation($config_name, 'de');
}
/**
* Sets up a configuration string without a translation.
*
* The actual configuration is already available by installing locale_test
* module, as it is done in LocaleConfigSubscriberTest::setUp(). This sets up
* the necessary source string and verifies that everything is as expected to
* avoid false positives.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $source
* The source string.
* @param string $langcode
* The language code.
*/
protected function setUpNoTranslation($config_name, $key, $source, $langcode) {
$this->localeConfigManager->updateConfigTranslations([$config_name], [$langcode]);
$this->assertNoConfigOverride($config_name, $key, $source, $langcode);
$this->assertNoTranslation($config_name, $langcode);
}
/**
* Sets up a configuration string with a translation.
*
* The actual configuration is already available by installing locale_test
* module, as it is done in LocaleConfigSubscriberTest::setUp(). This sets up
* the necessary source and translation strings and verifies that everything
* is as expected to avoid false positives.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $source
* The source string.
* @param string $translation
* The translation string.
* @param string $langcode
* The language code.
* @param bool $is_active
* Whether the update will affect the active configuration.
*/
protected function setUpTranslation($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) {
// Create source and translation strings for the configuration value and add
// the configuration name as a location. This would be performed by
// locale_translate_batch_import() invoking
// LocaleConfigManager::updateConfigTranslations() normally.
$this->localeConfigManager->reset();
$this->localeConfigManager
->getStringTranslation($config_name, $langcode, $source, '')
->setString($translation)
->setCustomized(FALSE)
->save();
$this->configFactory->reset($config_name);
$this->localeConfigManager->reset();
$this->localeConfigManager->updateConfigTranslations([$config_name], [$langcode]);
if ($is_active) {
$this->assertActiveConfig($config_name, $key, $translation, $langcode);
}
else {
$this->assertConfigOverride($config_name, $key, $translation, $langcode);
}
$this->assertTranslation($config_name, $translation, $langcode, FALSE);
}
/**
* Saves a language override.
*
* This will invoke LocaleConfigSubscriber through the event dispatcher. To
* make sure the configuration was persisted correctly, the configuration
* value is checked. Because LocaleConfigSubscriber temporarily disables the
* override state of the configuration factory we check that the correct value
* is restored afterwards.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $value
* The configuration value to save.
* @param string $langcode
* The language code.
*/
protected function saveLanguageOverride($config_name, $key, $value, $langcode) {
$translation_override = $this->languageManager
->getLanguageConfigOverride($langcode, $config_name);
$translation_override
->set($key, $value)
->save();
$this->configFactory->reset($config_name);
$this->assertConfigOverride($config_name, $key, $value, $langcode);
}
/**
* Saves translation data from locale module.
*
* This will invoke LocaleConfigSubscriber through the event dispatcher. To
* make sure the configuration was persisted correctly, the configuration
* value is checked. Because LocaleConfigSubscriber temporarily disables the
* override state of the configuration factory we check that the correct value
* is restored afterwards.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $source
* The source string.
* @param string $translation
* The translation string to save.
* @param string $langcode
* The language code.
* @param bool $is_active
* Whether the update will affect the active configuration.
*/
protected function saveLocaleTranslationData($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) {
$this->localeConfigManager->reset();
$this->localeConfigManager
->getStringTranslation($config_name, $langcode, $source, '')
->setString($translation)
->save();
$this->localeConfigManager->reset();
$this->localeConfigManager->updateConfigTranslations([$config_name], [$langcode]);
$this->configFactory->reset($config_name);
if ($is_active) {
$this->assertActiveConfig($config_name, $key, $translation, $langcode);
}
else {
$this->assertConfigOverride($config_name, $key, $translation, $langcode);
}
}
/**
* Deletes a language override.
*
* This will invoke LocaleConfigSubscriber through the event dispatcher. To
* make sure the configuration was persisted correctly, the configuration
* value is checked. Because LocaleConfigSubscriber temporarily disables the
* override state of the configuration factory we check that the correct value
* is restored afterwards.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $source_value
* The source configuration value to verify the correct value is returned
* from the configuration factory after the deletion.
* @param string $langcode
* The language code.
*/
protected function deleteLanguageOverride($config_name, $key, $source_value, $langcode) {
$translation_override = $this->languageManager
->getLanguageConfigOverride($langcode, $config_name);
$translation_override
->clear($key)
->save();
$this->configFactory->reset($config_name);
$this->assertNoConfigOverride($config_name, $key, $source_value, $langcode);
}
/**
* Deletes translation data from locale module.
*
* This will invoke LocaleConfigSubscriber through the event dispatcher. To
* make sure the configuration was persisted correctly, the configuration
* value is checked. Because LocaleConfigSubscriber temporarily disables the
* override state of the configuration factory we check that the correct value
* is restored afterwards.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $source_value
* The source configuration value to verify the correct value is returned
* from the configuration factory after the deletion.
* @param string $langcode
* The language code.
*/
protected function deleteLocaleTranslationData($config_name, $key, $source_value, $langcode) {
$this->localeConfigManager
->getStringTranslation($config_name, $langcode, $source_value, '')
->delete();
$this->localeConfigManager->reset();
$this->localeConfigManager->updateConfigTranslations([$config_name], [$langcode]);
$this->configFactory->reset($config_name);
$this->assertNoConfigOverride($config_name, $key, $source_value, $langcode);
}
/**
* Ensures configuration override is not present anymore.
*
* @param string $config_name
* The configuration name.
* @param string $langcode
* The language code.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertNoConfigOverride($config_name, $langcode) {
$config_langcode = $this->configFactory->getEditable($config_name)->get('langcode');
$override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name);
return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->isNew(), TRUE);
}
/**
* Ensures configuration was saved correctly.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $value
* The configuration value.
* @param string $langcode
* The language code.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertConfigOverride($config_name, $key, $value, $langcode) {
$config_langcode = $this->configFactory->getEditable($config_name)->get('langcode');
$override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name);
return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->get($key), $value);
}
/**
* Ensures configuration was saved correctly.
*
* @param string $config_name
* The configuration name.
* @param string $key
* The configuration key.
* @param string $value
* The configuration value.
* @param string $langcode
* The language code.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertActiveConfig($config_name, $key, $value, $langcode) {
$config = $this->configFactory->getEditable($config_name);
return
$this->assertEqual($config->get('langcode'), $langcode) &&
$this->assertIdentical($config->get($key), $value);
}
/**
* Ensures no translation exists.
*
* @param string $config_name
* The configuration name.
* @param string $langcode
* The language code.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertNoTranslation($config_name, $langcode) {
$strings = $this->stringStorage->getTranslations([
'type' => 'configuration',
'name' => $config_name,
'language' => $langcode,
'translated' => TRUE,
]);
return $this->assertIdentical([], $strings);
}
/**
* Ensures a translation exists and is marked as customized.
*
* @param string $config_name
* The configuration name.
* @param string $translation
* The translation.
* @param string $langcode
* The language code.
* @param bool $customized
* Whether or not the string should be asserted to be customized or not
* customized.
*
* @return bool
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertTranslation($config_name, $translation, $langcode, $customized = TRUE) {
// Make sure a string exists.
$strings = $this->stringStorage->getTranslations([
'type' => 'configuration',
'name' => $config_name,
'language' => $langcode,
'translated' => TRUE,
]);
$pass = $this->assertIdentical(1, count($strings));
$string = reset($strings);
if ($this->assertTrue($string instanceof StringInterface)) {
/** @var \Drupal\locale\StringInterface $string */
$pass = $pass && $this->assertIdentical($translation, $string->getString());
$pass = $pass && $this->assertTrue($string->isTranslation());
if ($this->assertTrue($string instanceof TranslationString)) {
/** @var \Drupal\locale\TranslationString $string */
// Make sure the string is marked as customized so that it does not get
// overridden when the string translations are updated.
return $pass && $this->assertEqual($customized, $string->customized);
}
}
return FALSE;
}
}
@@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\Core\Language\LanguageInterface;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests that the configurable language manager and locale operate correctly.
*
* @group locale
*/
class LocaleConfigurableLanguageManagerTest extends KernelTestBase {
/**
* A list of modules to install for this test.
*
* @var array
*/
public static $modules = ['language', 'locale'];
public function testGetLanguages() {
$this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']);
$default_language = new ConfigurableLanguage(['label' => $this->randomMachineName(), 'id' => 'default', 'weight' => 0], 'configurable_language');
$default_language->save();
// Set new default language.
\Drupal::service('language.default')->set($default_language);
\Drupal::service('string_translation')->setDefaultLangcode($default_language->getId());
$languages = \Drupal::service('language_manager')->getLanguages(LanguageInterface::STATE_ALL);
$this->assertEqual(['default', 'und', 'zxx'], array_keys($languages));
$configurableLanguage = new ConfigurableLanguage(['label' => $this->randomMachineName(), 'id' => 'test', 'weight' => 1], 'configurable_language');
// Simulate a configuration sync by setting the flag otherwise the locked
// language weights would be updated whilst saving.
// @see \Drupal\language\Entity\ConfigurableLanguage::postSave()
$configurableLanguage->setSyncing(TRUE)->save();
$languages = \Drupal::service('language_manager')->getLanguages(LanguageInterface::STATE_ALL);
$this->assertEqual(['default', 'test', 'und', 'zxx'], array_keys($languages));
}
}
@@ -0,0 +1,101 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests locale translation safe string handling.
*
* @group locale
*/
class LocaleStringIsSafeTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['locale', 'locale_test'];
/**
* Tests for locale_string_is_safe().
*/
public function testLocaleStringIsSafe() {
// Check a translatable string without HTML.
$string = 'Hello world!';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
// Check a translatable string which includes trustable HTML.
$string = 'Hello <strong>world</strong>!';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
// Check an untranslatable string which includes untrustable HTML (according
// to the locale_string_is_safe() function definition).
$string = 'Hello <img src="world.png" alt="world" />!';
$result = locale_string_is_safe($string);
$this->assertFalse($result);
// Check a translatable string which includes a token in an href attribute.
$string = 'Hi <a href="[current-user:url]">user</a>';
$result = locale_string_is_safe($string);
$this->assertTrue($result);
}
/**
* Tests if a translated and tokenized string is properly escaped by Twig.
*
* In each assert* call we add a new line at the expected result to match the
* newline at the end of the template file.
*/
public function testLocalizedTokenizedString() {
$tests_to_do = [
1 => [
'original' => 'Go to the <a href="[locale_test:security_test1]">frontpage</a>',
'replaced' => 'Go to the &lt;a href=&quot;javascript:alert(&amp;#039;Mooooh!&amp;#039;);&quot;&gt;frontpage&lt;/a&gt;',
],
2 => [
'original' => 'Hello <strong>[locale_test:security_test2]</strong>!',
'replaced' => 'Hello &lt;strong&gt;&amp;lt;script&amp;gt;alert(&amp;#039;Mooooh!&amp;#039;);&amp;lt;/script&amp;gt;&lt;/strong&gt;!',
],
];
foreach ($tests_to_do as $i => $test) {
$original_string = $test['original'];
$rendered_original_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $original_string]);
// Twig assumes that strings are unsafe so it escapes them, and so the
// original and the rendered version should be different.
$this->assertNotEqual(
$rendered_original_string,
$original_string . "\n",
'Security test ' . $i . ' before translation'
);
// Pass the original string to the t() function to get it marked as safe.
$safe_string = t($original_string);
$rendered_safe_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $safe_string]);
// t() function always marks the string as safe so it won't be escaped,
// and should be the same as the original.
$this->assertEqual(
$rendered_safe_string,
$original_string . "\n",
'Security test ' . $i . ' after translation before token replacement'
);
// Replace tokens in the safe string to inject it with dangerous content.
// @see locale_test_tokens().
$unsafe_string = \Drupal::token()->replace($safe_string);
$rendered_unsafe_string = \Drupal::theme()->render('locale_test_tokenized', ['content' => $unsafe_string]);
// Token replacement changes the string so it is not marked as safe
// anymore. Check it is escaped the way we expect.
$this->assertEqual(
$rendered_unsafe_string,
$test['replaced'] . "\n",
'Security test ' . $i . ' after translation after token replacement'
);
}
}
}
@@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\locale\Kernel;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests locale translation project handling.
*
* @group locale
*/
class LocaleTranslationProjectsTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['locale', 'locale_test', 'system'];
/**
* The module handler used in this test.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The locale project storage used in this test.
*
* @var \Drupal\locale\LocaleProjectStorageInterface
*/
protected $projectStorage;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->moduleHandler = $this->container->get('module_handler');
$this->projectStorage = $this->container->get('locale.project');
\Drupal::state()->set('locale.remove_core_project', TRUE);
}
/**
* Tests locale_translation_clear_cache_projects().
*/
public function testLocaleTranslationClearCacheProjects() {
$this->moduleHandler->loadInclude('locale', 'inc', 'locale.translation');
$expected = [];
$this->assertIdentical($expected, locale_translation_get_projects());
$this->projectStorage->set('foo', []);
$expected['foo'] = new \stdClass();
$this->assertEqual($expected, locale_translation_get_projects());
$this->projectStorage->set('bar', []);
locale_translation_clear_cache_projects();
$expected['bar'] = new \stdClass();
$this->assertEqual($expected, locale_translation_get_projects());
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\locale\Kernel\Migrate;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to locale.settings.yml.
*
* @group migrate_drupal_6
*/
class MigrateLocaleConfigsTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['locale', 'language'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('locale_settings');
}
/**
* Tests migration of locale variables to locale.settings.yml.
*/
public function testLocaleSettings() {
$config = $this->config('locale.settings');
$this->assertIdentical(TRUE, $config->get('cache_strings'));
$this->assertIdentical('languages', $config->get('javascript.directory'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'locale.settings', $config->get());
}
}
@@ -0,0 +1,269 @@
<?php
namespace Drupal\Tests\locale\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\locale\LocaleLookup;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* @coversDefaultClass \Drupal\locale\LocaleLookup
* @group locale
*/
class LocaleLookupTest extends UnitTestCase {
/**
* A mocked storage to use when instantiating LocaleTranslation objects.
*
* @var \Drupal\locale\StringStorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* A mocked cache object.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cache;
/**
* A mocked lock object.
*
* @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $lock;
/**
* A mocked user object built from AccountInterface.
*
* @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $user;
/**
* A mocked config factory built with UnitTestCase::getConfigFactoryStub().
*
* @var \Drupal\Core\Config\ConfigFactory|\PHPUnit_Framework_MockObject_MockBuilder
*/
protected $configFactory;
/**
* A mocked language manager built from LanguageManagerInterface.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->storage = $this->getMock('Drupal\locale\StringStorageInterface');
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$this->lock->expects($this->never())
->method($this->anything());
$this->user = $this->getMock('Drupal\Core\Session\AccountInterface');
$this->user->expects($this->any())
->method('getRoles')
->will($this->returnValue(['anonymous']));
$this->configFactory = $this->getConfigFactoryStub(['locale.settings' => ['cache_strings' => FALSE]]);
$this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->requestStack = new RequestStack();
$container = new ContainerBuilder();
$container->set('current_user', $this->user);
\Drupal::setContainer($container);
}
/**
* Tests locale lookups without fallback.
*
* @covers ::resolveCacheMiss
*/
public function testResolveCacheMissWithoutFallback() {
$args = [
'language' => 'en',
'source' => 'test',
'context' => 'irrelevant',
];
$result = (object) [
'translation' => 'test',
];
$this->cache->expects($this->once())
->method('get')
->with('locale:en:irrelevant:anonymous', FALSE);
$this->storage->expects($this->once())
->method('findTranslation')
->with($this->equalTo($args))
->will($this->returnValue($result));
$locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
->setConstructorArgs(['en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack])
->setMethods(['persist'])
->getMock();
$locale_lookup->expects($this->never())
->method('persist');
$this->assertSame('test', $locale_lookup->get('test'));
}
/**
* Tests locale lookups with fallback.
*
* Note that context is irrelevant here. It is not used but it is required.
*
* @covers ::resolveCacheMiss
*
* @dataProvider resolveCacheMissWithFallbackProvider
*/
public function testResolveCacheMissWithFallback($langcode, $string, $context, $expected) {
// These are fake words!
$translations = [
'en' => [
'test' => 'test',
'fake' => 'fake',
'missing pl' => 'missing pl',
'missing cs' => 'missing cs',
'missing both' => 'missing both',
],
'pl' => [
'test' => 'test po polsku',
'fake' => 'ściema',
'missing cs' => 'zaginiony czech',
],
'cs' => [
'test' => 'test v české',
'fake' => 'falešný',
'missing pl' => 'chybějící pl',
],
];
$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 'pl':
return ['cs', 'en'];
case 'cs':
return ['en'];
default:
return [];
}
}));
$this->cache->expects($this->once())
->method('get')
->with('locale:' . $langcode . ':' . $context . ':anonymous', FALSE);
$locale_lookup = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
$this->assertSame($expected, $locale_lookup->get($string));
}
/**
* Provides test data for testResolveCacheMissWithFallback().
*/
public function resolveCacheMissWithFallbackProvider() {
return [
['cs', 'test', 'irrelevant', 'test v české'],
['cs', 'fake', 'irrelevant', 'falešný'],
['cs', 'missing pl', 'irrelevant', 'chybějící pl'],
['cs', 'missing cs', 'irrelevant', 'missing cs'],
['cs', 'missing both', 'irrelevant', 'missing both'],
// Testing PL with fallback to cs, en.
['pl', 'test', 'irrelevant', 'test po polsku'],
['pl', 'fake', 'irrelevant', 'ściema'],
['pl', 'missing pl', 'irrelevant', 'chybějící pl'],
['pl', 'missing cs', 'irrelevant', 'zaginiony czech'],
['pl', 'missing both', 'irrelevant', 'missing both'],
];
}
/**
* Tests locale lookups with persistent tracking.
*
* @covers ::resolveCacheMiss
*/
public function testResolveCacheMissWithPersist() {
$args = [
'language' => 'en',
'source' => 'test',
'context' => 'irrelevant',
];
$result = (object) [
'translation' => 'test',
];
$this->storage->expects($this->once())
->method('findTranslation')
->with($this->equalTo($args))
->will($this->returnValue($result));
$this->configFactory = $this->getConfigFactoryStub(['locale.settings' => ['cache_strings' => TRUE]]);
$locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
->setConstructorArgs(['en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack])
->setMethods(['persist'])
->getMock();
$locale_lookup->expects($this->once())
->method('persist');
$this->assertSame('test', $locale_lookup->get('test'));
}
/**
* Tests locale lookups without a found translation.
*
* @covers ::resolveCacheMiss
*/
public function testResolveCacheMissNoTranslation() {
$string = $this->getMock('Drupal\locale\StringInterface');
$string->expects($this->once())
->method('addLocation')
->will($this->returnSelf());
$this->storage->expects($this->once())
->method('findTranslation')
->will($this->returnValue(NULL));
$this->storage->expects($this->once())
->method('createString')
->will($this->returnValue($string));
$request = Request::create('/test');
$this->requestStack->push($request);
$locale_lookup = $this->getMockBuilder('Drupal\locale\LocaleLookup')
->setConstructorArgs(['en', 'irrelevant', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack])
->setMethods(['persist'])
->getMock();
$locale_lookup->expects($this->never())
->method('persist');
$this->assertTrue($locale_lookup->get('test'));
}
}
@@ -0,0 +1,57 @@
<?php
namespace Drupal\Tests\locale\Unit;
use Drupal\locale\LocaleTranslation;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* @coversDefaultClass \Drupal\locale\LocaleTranslation
* @group locale
*/
class LocaleTranslationTest extends UnitTestCase {
/**
* A mocked storage to use when instantiating LocaleTranslation objects.
*
* @var \PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* A mocked language manager built from LanguageManagerInterface.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->storage = $this->getMock('Drupal\locale\StringStorageInterface');
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$this->requestStack = new RequestStack();
}
/**
* Tests for \Drupal\locale\LocaleTranslation::destruct().
*/
public function testDestruct() {
$translation = new LocaleTranslation($this->storage, $this->cache, $this->lock, $this->getConfigFactoryStub(), $this->languageManager, $this->requestStack);
// Prove that destruction works without errors when translations are empty.
$this->assertAttributeEmpty('translations', $translation);
$translation->destruct();
}
}
@@ -0,0 +1,48 @@
<?php
namespace Drupal\Tests\locale\Unit\Menu;
use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
/**
* Tests locale local tasks.
*
* @group locale
*/
class LocaleLocalTasksTest extends LocalTaskIntegrationTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->directoryList = [
'locale' => 'core/modules/locale',
];
parent::setUp();
}
/**
* Checks locale listing local tasks.
*
* @dataProvider getLocalePageRoutes
*/
public function testLocalePageLocalTasks($route) {
$tasks = [
0 => ['locale.translate_page', 'locale.translate_import', 'locale.translate_export', 'locale.settings'],
];
$this->assertLocalTasks($route, $tasks);
}
/**
* Provides a list of routes to test.
*/
public function getLocalePageRoutes() {
return [
['locale.translate_page'],
['locale.translate_import'],
['locale.translate_export'],
['locale.settings'],
];
}
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\locale\Unit;
use Drupal\locale\SourceString;
use Drupal\locale\StringStorageException;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\locale\StringBase
* @group locale
*/
class StringBaseTest extends UnitTestCase {
/**
* @covers ::save
*/
public function testSaveWithoutStorage() {
$string = new SourceString(['source' => 'test']);
$this->setExpectedException(StringStorageException::class, 'The string cannot be saved because its not bound to a storage: test');
$string->save();
}
/**
* @covers ::delete
*/
public function testDeleteWithoutStorage() {
$string = new SourceString(['lid' => 1, 'source' => 'test']);
$this->setExpectedException(StringStorageException::class, 'The string cannot be deleted because its not bound to a storage: test');
$string->delete();
}
}
+13
View File
@@ -0,0 +1,13 @@
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "@site is currently under maintenance. We should be back shortly. Thank you for your patience."
msgstr "Ons is tans besig met onderhoud op @site. Wees asseblief geduldig, ons sal binnekort weer terug wees."
msgid "Locale can translate"
msgstr "Locale can translate Afrikaans"
+28
View File
@@ -0,0 +1,28 @@
msgid ""
msgstr ""
"Project-Id-Version: Drupal 7\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Monday"
msgstr "Montag"
msgid "Tuesday"
msgstr "Dienstag"
msgid "Wednesday"
msgstr "Mittwoch"
msgid "Thursday"
msgstr "Donnerstag"
msgid "Friday"
msgstr "Freitag"
msgid "Saturday"
msgstr "Samstag"
msgid "Sunday"
msgstr "Sonntag"
+31
View File
@@ -0,0 +1,31 @@
msgid ""
msgstr ""
"Project-Id-Version: Drupal 7\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Monday"
msgstr "maandag"
msgid "Tuesday"
msgstr "dinsdag"
msgid "Wednesday"
msgstr "woensdag"
msgid "Thursday"
msgstr "donderdag"
msgid "Extraday"
msgstr "extra dag"
msgid "Friday"
msgstr "vrijdag"
msgid "Saturday"
msgstr "zaterdag"
msgid "Sunday"
msgstr "zondag"
+40
View File
@@ -0,0 +1,40 @@
msgid ""
msgstr ""
"Project-Id-Version: Drupal 7\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Monday"
msgstr "lundi"
msgid "Tuesday"
msgstr "mardi"
msgid "Wednesday"
msgstr "mercredi"
msgid "Thursday"
msgstr "jeudi"
msgid "Friday"
msgstr "vendredi"
msgid "Saturday"
msgstr "samedi"
msgid "Sunday"
msgstr "dimanche"
msgid "Allowed HTML source string"
msgstr "<strong>Allowed HTML translation string</strong>"
msgid "Another allowed HTML source string"
msgstr "<script>Disallowed HTML translation string</script>"
msgid "Source string for multiline translation"
msgstr ""
"Multiline translation string "
"to make sure that "
"import works with it."