diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCommentLanguageTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCommentLanguageTest.php
new file mode 100644
index 00000000000..16fc83ff778
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCommentLanguageTest.php
@@ -0,0 +1,108 @@
+ 'Comment language',
+ 'description' => 'Tests for comment language.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ // We also use language_test module here to be able to turn on content
+ // language negotiation. Drupal core does not provide a way in itself
+ // to do that.
+ parent::setUp('locale', 'language_test');
+
+ // Create and login user.
+ $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'create article content'));
+ $this->drupalLogin($admin_user);
+
+ // Add language.
+ $edit = array('predefined_langcode' => 'fr');
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Set "Article" content type to use multilingual support.
+ $edit = array('node_type_language' => 1);
+ $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
+
+ // Enable content language negotiation UI.
+ variable_set('language_test_content_language_type', TRUE);
+
+ // Set interface language detection to user and content language detection
+ // to URL. Disable inheritance from interface language to ensure content
+ // language will fall back to the default language if no URL language can be
+ // detected.
+ $edit = array(
+ 'language_interface[enabled][language-user]' => TRUE,
+ 'language_content[enabled][language-url]' => TRUE,
+ 'language_content[enabled][language-interface]' => FALSE,
+ );
+ $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
+
+ // Change user language preference, this way interface language is always
+ // French no matter what path prefix the URLs have.
+ $edit = array('preferred_langcode' => 'fr');
+ $this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save'));
+ }
+
+ /**
+ * Test that comment language is properly set.
+ */
+ function testCommentLanguage() {
+ drupal_static_reset('language_list');
+
+ // Create two nodes, one for english and one for french, and comment each
+ // node using both english and french as content language by changing URL
+ // language prefixes. Meanwhile interface language is always French, which
+ // is the user language preference. This way we can ensure that node
+ // language and interface language do not influence comment language, as
+ // only content language has to.
+ foreach (language_list() as $node_langcode => $node_language) {
+ $langcode_not_specified = LANGUAGE_NOT_SPECIFIED;
+
+ // Create "Article" content.
+ $title = $this->randomName();
+ $edit = array(
+ "title" => $title,
+ "body[$langcode_not_specified][0][value]" => $this->randomName(),
+ "langcode" => $node_langcode,
+ );
+ $this->drupalPost("node/add/article", $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($title);
+
+ $prefixes = language_negotiation_url_prefixes();
+ foreach (language_list() as $langcode => $language) {
+ // Post a comment with content language $langcode.
+ $prefix = empty($prefixes[$langcode]) ? '' : $prefixes[$langcode] . '/';
+ $edit = array("comment_body[$langcode_not_specified][0][value]" => $this->randomName());
+ $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Save'));
+
+ // Check that comment language matches the current content language.
+ $comment = db_select('comment', 'c')
+ ->fields('c')
+ ->condition('nid', $node->nid)
+ ->orderBy('cid', 'DESC')
+ ->execute()
+ ->fetchObject();
+ $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode, '%langcode' => $langcode);
+ $this->assertEqual($comment->langcode, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
+ }
+ }
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
new file mode 100644
index 00000000000..0c5cea3689d
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
@@ -0,0 +1,210 @@
+ 'Content language settings',
+ 'description' => 'Checks you can enable multilingual support on content types and configure a language for a node.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+ }
+
+ /**
+ * Verifies that machine name fields are always LTR.
+ */
+ function testMachineNameLTR() {
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
+
+ // 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 = array();
+ $edit['predefined_langcode'] = 'ar';
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ $edit = array();
+ $edit['site_default'] = 'ar';
+ $this->drupalPost(NULL, $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.
+ */
+ function testContentTypeLanguageConfiguration() {
+ global $base_url;
+
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
+ // User to create a node.
+ $web_user = $this->drupalCreateUser(array('create article content', 'create page content', 'edit any page content'));
+
+ // Add custom language.
+ $this->drupalLogin($admin_user);
+ // Code for the language.
+ $langcode = 'xx';
+ // The English name for the language.
+ $name = $this->randomName(16);
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Set "Basic page" content type to use multilingual support.
+ $this->drupalGet('admin/structure/types/manage/page');
+ $this->assertText(t('Multilingual support'), t('Multilingual support fieldset present on content type configuration form.'));
+ $edit = array(
+ 'node_type_language' => 1,
+ );
+ $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
+ $this->drupalLogout();
+
+ // Verify language selection is not present on add article form.
+ $this->drupalLogin($web_user);
+ $this->drupalGet('node/add/article');
+ // Verify language select list is not present.
+ $this->assertNoFieldByName('language', NULL, t('Language select not present on add article form.'));
+
+ // Verify language selection appears on add "Basic page" form.
+ $this->drupalGet('node/add/page');
+ // Verify language select list is present.
+ $this->assertFieldByName('langcode', NULL, t('Language select present on add Basic page form.'));
+ // Ensure language appears.
+ $this->assertText($name, t('Language present.'));
+
+ // Create "Basic page" content.
+ $node_title = $this->randomName();
+ $node_body = $this->randomName();
+ $edit = array(
+ 'type' => 'page',
+ 'title' => $node_title,
+ 'body' => array($langcode => array(array('value' => $node_body))),
+ 'langcode' => $langcode,
+ );
+ $node = $this->drupalCreateNode($edit);
+ // Edit the content and ensure correct language is selected.
+ $path = 'node/' . $node->nid . '/edit';
+ $this->drupalGet($path);
+ $this->assertRaw('', t('Correct language selected.'));
+ // Ensure we can change the node language.
+ $edit = array(
+ 'langcode' => 'en',
+ );
+ $this->drupalPost($path, $edit, t('Save'));
+ $this->assertRaw(t('%title has been updated.', array('%title' => $node_title)), t('Basic page content updated.'));
+
+ $this->drupalLogout();
+ }
+
+ /**
+ * Test if a dir and lang tags exist in node's attributes.
+ */
+ function testContentTypeDirLang() {
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
+ // User to create a node.
+ $web_user = $this->drupalCreateUser(array('create article content', 'edit own article content'));
+
+ // Login as admin.
+ $this->drupalLogin($admin_user);
+
+ // Install Arabic language.
+ $edit = array();
+ $edit['predefined_langcode'] = 'ar';
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Install Spanish language.
+ $edit = array();
+ $edit['predefined_langcode'] = 'es';
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Set "Article" content type to use multilingual support.
+ $this->drupalGet('admin/structure/types/manage/article');
+ $edit = array(
+ 'node_type_language' => 1,
+ );
+ $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), t('Article content type has been updated.'));
+ $this->drupalLogout();
+
+ // Login as web user to add new article.
+ $this->drupalLogin($web_user);
+
+ // Create three nodes: English, Arabic and Spanish.
+ $node_en = $this->createNodeArticle('en');
+ $node_ar = $this->createNodeArticle('ar');
+ $node_es = $this->createNodeArticle('es');
+
+ $this->drupalGet('node');
+
+ // Check if English node does not have lang tag.
+ $pattern = '|id="node-' . $node_en->nid . '"[^<>]*lang="en"|';
+ $this->assertNoPattern($pattern, t('The lang tag has not been assigned to the English node.'));
+
+ // Check if English node does not have dir tag.
+ $pattern = '|id="node-' . $node_en->nid . '"[^<>]*dir="ltr"|';
+ $this->assertNoPattern($pattern, t('The dir tag has not been assigned to the English node.'));
+
+ // Check if Arabic node has lang="ar" & dir="rtl" tags.
+ $pattern = '|id="node-' . $node_ar->nid . '"[^<>]*lang="ar" dir="rtl"|';
+ $this->assertPattern($pattern, t('The lang and dir tags have been assigned correctly to the Arabic node.'));
+
+ // Check if Spanish node has lang="es" tag.
+ $pattern = '|id="node-' . $node_es->nid . '"[^<>]*lang="es"|';
+ $this->assertPattern($pattern, t('The lang tag has been assigned correctly to the Spanish node.'));
+
+ // Check if Spanish node does not have dir="ltr" tag.
+ $pattern = '|id="node-' . $node_es->nid . '"[^<>]*lang="es" dir="ltr"|';
+ $this->assertNoPattern($pattern, t('The dir tag has not been assigned to the Spanish node.'));
+
+ $this->drupalLogout();
+ }
+
+ /**
+ * Create node in a specific language.
+ */
+ protected function createNodeArticle($langcode) {
+ $this->drupalGet('node/add/article');
+ $node_title = $this->randomName();
+ $node_body = $this->randomName();
+ $edit = array(
+ 'type' => 'article',
+ 'title' => $node_title,
+ 'body' => array($langcode => array(array('value' => $node_body))),
+ 'langcode' => $langcode,
+ 'promote' => 1,
+ );
+ return $this->drupalCreateNode($edit);
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleDateFormatsTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleDateFormatsTest.php
new file mode 100644
index 00000000000..e5514b8b1a9
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleDateFormatsTest.php
@@ -0,0 +1,82 @@
+ 'Localize date formats',
+ 'description' => 'Tests for the localization of date formats.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('node', 'locale'));
+
+ // Create Article node type.
+ $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+
+ // Create and login user.
+ $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'create article content'));
+ $this->drupalLogin($admin_user);
+ }
+
+ /**
+ * Functional tests for localizing date formats.
+ */
+ function testLocalizeDateFormats() {
+ // Add language.
+ $edit = array(
+ 'predefined_langcode' => 'fr',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Set language negotiation.
+ $language_type = LANGUAGE_TYPE_INTERFACE;
+ $edit = array(
+ "{$language_type}[enabled][language-url]" => TRUE,
+ );
+ $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
+
+ // Configure date formats.
+ $this->drupalGet('admin/config/regional/date-time/locale');
+ $this->assertText('French', 'Configured languages appear.');
+ $edit = array(
+ 'date_format_long' => 'd.m.Y - H:i',
+ 'date_format_medium' => 'd.m.Y - H:i',
+ 'date_format_short' => 'd.m.Y - H:i',
+ );
+ $this->drupalPost('admin/config/regional/date-time/locale/fr/edit', $edit, t('Save configuration'));
+ $this->assertText(t('Configuration saved.'), 'French date formats updated.');
+ $edit = array(
+ 'date_format_long' => 'j M Y - g:ia',
+ 'date_format_medium' => 'j M Y - g:ia',
+ 'date_format_short' => 'j M Y - g:ia',
+ );
+ $this->drupalPost('admin/config/regional/date-time/locale/en/edit', $edit, t('Save configuration'));
+ $this->assertText(t('Configuration saved.'), 'English date formats updated.');
+
+ // Create node content.
+ $node = $this->drupalCreateNode(array('type' => 'article'));
+
+ // Configure format for the node posted date changes with the language.
+ $this->drupalGet('node/' . $node->nid);
+ $english_date = format_date($node->created, 'custom', 'j M Y');
+ $this->assertText($english_date, t('English date format appears'));
+ $this->drupalGet('fr/node/' . $node->nid);
+ $french_date = format_date($node->created, 'custom', 'd.m.Y');
+ $this->assertText($french_date, t('French date format appears'));
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
new file mode 100644
index 00000000000..4ad935669e4
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleExportTest.php
@@ -0,0 +1,152 @@
+ 'Translation export',
+ 'description' => 'Tests the exportation of locale files.',
+ 'group' => 'Locale',
+ );
+ }
+
+ /**
+ * A user able to create languages and export translations.
+ */
+ protected $admin_user = NULL;
+
+ function setUp() {
+ parent::setUp('locale');
+
+ $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
+ $this->drupalLogin($this->admin_user);
+ }
+
+ /**
+ * Test exportation of translations.
+ */
+ function testExportTranslation() {
+ // First import some known translations.
+ // This will also automatically enable the 'fr' language.
+ $name = tempnam('temporary://', "po_") . '.po';
+ file_put_contents($name, $this->getPoFile());
+ $this->drupalPost('admin/config/regional/translate/import', array(
+ 'langcode' => 'fr',
+ 'files[file]' => $name,
+ ), t('Import'));
+ drupal_unlink($name);
+
+ // Get the French translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'fr',
+ ), t('Export'));
+
+ // Ensure we have a translation file.
+ $this->assertRaw('# French translation of Drupal', t('Exported French translation file.'));
+ // Ensure our imported translations exist in the file.
+ $this->assertRaw('msgstr "lundi"', t('French translations present in exported file.'));
+
+ // Import some more French translations which will be marked as customized.
+ $name = tempnam('temporary://', "po2_") . '.po';
+ file_put_contents($name, $this->getCustomPoFile());
+ $this->drupalPost('admin/config/regional/translate/import', array(
+ 'langcode' => 'fr',
+ 'files[file]' => $name,
+ 'customized' => 1,
+ ), t('Import'));
+ drupal_unlink($name);
+
+ // We can't import a string with an empty translation, but calling
+ // locale() for an new string creates an entry in the locales_source table.
+ locale('February', NULL, 'fr');
+
+ // Export only customized French translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'fr',
+ 'content_options[not_customized]' => FALSE,
+ 'content_options[customized]' => TRUE,
+ 'content_options[not_translated]' => FALSE,
+ ), t('Export'));
+
+ // Ensure we have a translation file.
+ $this->assertRaw('# French translation of Drupal', t('Exported French translation file with only customized strings.'));
+ // Ensure the customized translations exist in the file.
+ $this->assertRaw('msgstr "janvier"', t('French custom translation present in exported file.'));
+ // Ensure no untranslated strings exist in the file.
+ $this->assertNoRaw('msgid "February"', t('Untranslated string not present in exported file.'));
+
+ // Export only untranslated French translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'fr',
+ 'content_options[not_customized]' => FALSE,
+ 'content_options[customized]' => FALSE,
+ 'content_options[not_translated]' => TRUE,
+ ), t('Export'));
+
+ // Ensure we have a translation file.
+ $this->assertRaw('# French translation of Drupal', t('Exported French translation file with only untranslated strings.'));
+ // Ensure no customized translations exist in the file.
+ $this->assertNoRaw('msgstr "janvier"', t('French custom translation not present in exported file.'));
+ // Ensure the untranslated strings exist in the file.
+ $this->assertRaw('msgid "February"', t('Untranslated string present in exported file.'));
+ }
+
+ /**
+ * Test exportation of translation template file.
+ */
+ function testExportTranslationTemplateFile() {
+ // Get the translation template file.
+ $this->drupalPost('admin/config/regional/translate/export', array(), t('Export'));
+ // Ensure we have a translation file.
+ $this->assertRaw('# LANGUAGE translation of PROJECT', t('Exported translation template file.'));
+ }
+
+ /**
+ * Helper function that returns a proper .po file.
+ */
+ function getPoFile() {
+ return <<< EOF
+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 "Monday"
+msgstr "lundi"
+EOF;
+ }
+
+ /**
+ * Helper function that returns a .po file which strings will be marked
+ * as customized.
+ */
+ function getCustomPoFile() {
+ return <<< EOF
+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 "January"
+msgstr "janvier"
+EOF;
+ }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
new file mode 100644
index 00000000000..3beee64b24a
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
@@ -0,0 +1,487 @@
+ 'Translation import',
+ 'description' => 'Tests the import of locale files.',
+ 'group' => 'Locale',
+ );
+ }
+
+ /**
+ * A user able to create languages and import translations.
+ */
+ protected $admin_user = NULL;
+
+ function setUp() {
+ parent::setUp(array('locale', 'dblog'));
+
+ // Set the translation file directory.
+ variable_set('locale_translate_file_directory', drupal_get_path('module', 'locale') . '/tests');
+
+ $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
+ $this->drupalLogin($this->admin_user);
+ }
+
+ /**
+ * Test import of standalone .po files.
+ */
+ function testStandalonePoFile() {
+ // Try importing a .po file.
+ $this->importPoFile($this->getPoFile(), array(
+ 'langcode' => 'fr',
+ ));
+
+ // The import should automatically create the corresponding language.
+ $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), t('The language has been automatically created.'));
+
+ // The import should have created 8 strings.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 8, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+
+ // This import should have saved plural forms to have 2 variants.
+ $locale_plurals = variable_get('locale_translation_plurals', array());
+ $this->assert($locale_plurals['fr']['plurals'] == 2, t('Plural number initialized.'));
+
+ // Ensure we were redirected correctly.
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+
+
+ // Try importing a .po file with invalid tags.
+ $this->importPoFile($this->getBadPoFile(), array(
+ 'langcode' => 'fr',
+ ));
+
+ // The import should have created 1 string and rejected 2.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $skip_message = format_plural(2, 'A 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.', array('@url' => url('admin/reports/dblog')));
+ $this->assertRaw($skip_message, t('Unsafe strings were skipped.'));
+
+
+ // Try importing a .po file which doesn't exist.
+ $name = $this->randomName(16);
+ $this->drupalPost('admin/config/regional/translate/import', array(
+ 'langcode' => 'fr',
+ 'files[file]' => $name,
+ ), t('Import'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertText(t('File to import not found.'), t('File to import not found message.'));
+
+
+ // Try importing a .po file with overriding strings, and ensure existing
+ // strings are kept.
+ $this->importPoFile($this->getOverwritePoFile(), array(
+ 'langcode' => 'fr',
+ ));
+
+ // The import should have created 1 string.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ // Ensure string wasn't overwritten.
+ $search = array(
+ 'string' => 'Montag',
+ 'language' => 'fr',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t('String not overwritten by imported string.'));
+
+ // This import should not have changed number of plural forms.
+ $locale_plurals = variable_get('locale_translation_plurals', array());
+ $this->assert($locale_plurals['fr']['plurals'] == 2, t('Plural numbers untouched.'));
+
+ // Try importing a .po file with overriding strings, and ensure existing
+ // strings are overwritten.
+ $this->importPoFile($this->getOverwritePoFile(), array(
+ 'langcode' => 'fr',
+ 'overwrite_options[not_customized]' => TRUE,
+ ));
+
+ // The import should have updated 2 strings.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), t('The translation file was successfully imported.'));
+ // Ensure string was overwritten.
+ $search = array(
+ 'string' => 'Montag',
+ 'language' => 'fr',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('String overwritten by imported string.'));
+ // This import should have changed number of plural forms.
+ $locale_plurals = variable_get('locale_translation_plurals', array());
+ $this->assert($locale_plurals['fr']['plurals'] == 3, t('Plural numbers changed.'));
+
+ // Importing a .po file and mark its strings as customized strings.
+ $this->importPoFile($this->getCustomPoFile(), array(
+ 'langcode' => 'fr',
+ 'customized' => TRUE,
+ ));
+
+ // The import should have created 6 strings.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 6, '%update' => 0, '%delete' => 0)), t('The customized translation file was successfully imported.'));
+
+ // The database should now contain 6 customized strings (two imported
+ // strings are not translated).
+ $count = db_query('SELECT lid FROM {locales_target} WHERE customized = :custom', array(':custom' => 1))->rowCount();
+ $this->assertEqual($count, 6, t('Customized translations succesfully imported.'));
+
+ // Try importing a .po file with overriding strings, and ensure existing
+ // customized strings are kept.
+ $this->importPoFile($this->getCustomOverwritePoFile(), array(
+ 'langcode' => 'fr',
+ 'overwrite_options[not_customized]' => TRUE,
+ 'overwrite_options[customized]' => FALSE,
+ ));
+
+ // The import should have created 1 string.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The customized translation file was successfully imported.'));
+ // Ensure string wasn't overwritten.
+ $search = array(
+ 'string' => 'januari',
+ 'language' => 'fr',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t('Customized string not overwritten by imported string.'));
+
+ // Try importing a .po file with overriding strings, and ensure existing
+ // customized strings are overwritten.
+ $this->importPoFile($this->getCustomOverwritePoFile(), array(
+ 'langcode' => 'fr',
+ 'overwrite_options[not_customized]' => FALSE,
+ 'overwrite_options[customized]' => TRUE,
+ ));
+
+ // The import should have updated 2 strings.
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), t('The customized translation file was successfully imported.'));
+ // Ensure string was overwritten.
+ $search = array(
+ 'string' => 'januari',
+ 'language' => 'fr',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('Customized string overwritten by imported string.'));
+
+ }
+
+ /**
+ * Test automatic import of a module's translation files.
+ */
+ function testAutomaticModuleTranslationImportLanguageEnable() {
+ // Code for the language - manually set to match the test translation file.
+ $langcode = 'xx';
+ // The English name for the language.
+ $name = $this->randomName(16);
+
+ // Create a custom language.
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Ensure the translation file was automatically imported when language was
+ // added.
+ $this->assertText(t('One translation file imported.'), t('Language file automatically imported.'));
+
+ // Ensure strings were successfully imported.
+ $search = array(
+ 'string' => 'lundi',
+ 'language' => $langcode,
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
+ }
+
+ /**
+ * Test msgctxt context support.
+ */
+ function testLanguageContext() {
+ // Try importing a .po file.
+ $this->importPoFile($this->getPoFileWithContext(), array(
+ 'langcode' => 'hr',
+ ));
+
+ $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', t('Long month name context is working.'));
+ $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', t('Default context is working.'));
+ }
+
+ /**
+ * Test empty msgstr at end of .po file see #611786.
+ */
+ function testEmptyMsgstr() {
+ $langcode = 'hu';
+
+ // Try importing a .po file.
+ $this->importPoFile($this->getPoFileWithMsgstr(), array(
+ 'langcode' => $langcode,
+ ));
+
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
+ $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', t('String imported and translated.'));
+
+ // Try importing a .po file.
+ $this->importPoFile($this->getPoFileWithEmptyMsgstr(), array(
+ 'langcode' => $langcode,
+ 'overwrite_options[not_customized]' => TRUE,
+ ));
+ $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), t('The translation file was successfully imported.'));
+ // This is the language indicator on the translation search screen for
+ // untranslated strings.
+ $language_indicator = "$langcode ";
+ $str = "Operations";
+ $search = array(
+ 'string' => $str,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // assertText() seems to remove the input field where $str always could be
+ // found, so this is not a false assert.
+ $this->assertText($str, t('Search found the string.'));
+ $this->assertRaw($language_indicator, t('String is untranslated again.'));
+ }
+
+ /**
+ * Helper function: import a standalone .po file in a given language.
+ *
+ * @param $contents
+ * Contents of the .po file to import.
+ * @param $options
+ * Additional options to pass to the translation import form.
+ */
+ function importPoFile($contents, array $options = array()) {
+ $name = tempnam('temporary://', "po_") . '.po';
+ file_put_contents($name, $contents);
+ $options['files[file]'] = $name;
+ $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
+ drupal_unlink($name);
+ }
+
+ /**
+ * Helper function that returns a proper .po file.
+ */
+ function getPoFile() {
+ return <<< EOF
+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 "One sheep"
+msgid_plural "@count sheep"
+msgstr[0] "un mouton"
+msgstr[1] "@count moutons"
+
+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"
+EOF;
+ }
+
+ /**
+ * Helper function that returns a bad .po file.
+ */
+ function getBadPoFile() {
+ return <<< EOF
+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 "Save configuration"
+msgstr "Enregistrer la configuration"
+
+msgid "edit"
+msgstr "modifier
"
+
+msgid "delete"
+msgstr "supprimer"
+
+EOF;
+ }
+
+ /**
+ * Helper function that returns a proper .po file for testing.
+ */
+ function getOverwritePoFile() {
+ return <<< EOF
+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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
+
+msgid "Monday"
+msgstr "Montag"
+
+msgid "Day"
+msgstr "Jour"
+EOF;
+ }
+
+ /**
+ * Helper function that returns a .po file which strings will be marked
+ * as customized.
+ */
+ function getCustomPoFile() {
+ return <<< EOF
+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 "One dog"
+msgid_plural "@count dogs"
+msgstr[0] "un chien"
+msgstr[1] "@count chiens"
+
+msgid "January"
+msgstr "janvier"
+
+msgid "February"
+msgstr "février"
+
+msgid "March"
+msgstr "mars"
+
+msgid "April"
+msgstr "avril"
+
+msgid "June"
+msgstr "juin"
+EOF;
+ }
+
+ /**
+ * Helper function that returns a .po file for testing customized strings.
+ */
+ function getCustomOverwritePoFile() {
+ return <<< EOF
+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 "January"
+msgstr "januari"
+
+msgid "February"
+msgstr "februari"
+
+msgid "July"
+msgstr "juillet"
+EOF;
+ }
+
+ /**
+ * Helper function that returns a .po file with context.
+ */
+ function getPoFileWithContext() {
+ // Croatian (code hr) is one the the languages that have a different
+ // form for the full name and the abbreviated name for the month May.
+ return <<< EOF
+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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
+
+msgctxt "Long month name"
+msgid "May"
+msgstr "Svibanj"
+
+msgid "May"
+msgstr "Svi."
+EOF;
+ }
+
+ /**
+ * Helper function that returns a .po file with an empty last item.
+ */
+ function getPoFileWithEmptyMsgstr() {
+ return <<< EOF
+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 "Operations"
+msgstr ""
+
+EOF;
+ }
+ /**
+ * Helper function that returns a .po file with an empty last item.
+ */
+ function getPoFileWithMsgstr() {
+ return <<< EOF
+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 "Operations"
+msgstr "Műveletek"
+
+msgid "Will not appear in Drupal core, so we can ensure the test passes"
+msgstr ""
+
+EOF;
+ }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleInstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleInstallTest.php
new file mode 100644
index 00000000000..f1677eb21e4
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleInstallTest.php
@@ -0,0 +1,40 @@
+ 'String translation using st()',
+ 'description' => 'Tests that st() works like t().',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+
+ // st() lives in install.inc, so ensure that it is loaded for all tests.
+ require_once DRUPAL_ROOT . '/core/includes/install.inc';
+ }
+
+ /**
+ * Verify that function signatures of t() and st() are equal.
+ */
+ function testFunctionSignatures() {
+ $reflector_t = new ReflectionFunction('t');
+ $reflector_st = new ReflectionFunction('st');
+ $this->assertEqual($reflector_t->getParameters(), $reflector_st->getParameters(), t('Function signatures of t() and st() are equal.'));
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
new file mode 100644
index 00000000000..97395379f18
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
@@ -0,0 +1,100 @@
+ 'Javascript translation',
+ 'description' => 'Tests parsing js files for translatable strings',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+ }
+
+ function testFileParsing() {
+
+ $filename = drupal_get_path('module', 'locale') . '/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.
+ $source_strings = db_select('locales_source', 's')
+ ->fields('s', array('source', 'context'))
+ ->condition('s.location', $filename)
+ ->execute()
+ ->fetchAllKeyed();
+
+ // List of all strings that should be in the file.
+ $test_strings = array(
+ "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" => '',
+ "Standard Call @count plural" => '',
+ "Whitespace Call plural" => '',
+ "Whitespace Call @count plural" => '',
+
+ "Single Quote plural" => '',
+ "Single Quote @count plural" => '',
+ "Single Quote \\'Escaped\\' plural" => '',
+ "Single Quote \\'Escaped\\' @count plural" => '',
+
+ "Double Quote plural" => '',
+ "Double Quote @count plural" => '',
+ "Double Quote \\\"Escaped\\\" plural" => '',
+ "Double Quote \\\"Escaped\\\" @count plural" => '',
+
+ "Context !key Args plural" => "Context string",
+ "Context !key Args @count plural" => "Context string",
+
+ "Context Unquoted plural" => "Context string unquoted",
+ "Context Unquoted @count plural" => "Context string unquoted",
+ "Context Single Quoted plural" => "Context string single quoted",
+ "Context Single Quoted @count plural" => "Context string single quoted",
+ "Context Double Quoted plural" => "Context string double quoted",
+ "Context Double Quoted @count plural" => "Context string double quoted",
+ );
+
+ // Assert that all strings were found properly.
+ foreach ($test_strings as $str => $context) {
+ $args = array('%source' => $str, '%context' => $context);
+
+ // Make sure that the string was found in the file.
+ $this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", $args));
+
+ // Make sure that the proper context was matched.
+ $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? t("Context for %source is %context", $args) : t("Context for %source is blank", $args));
+ }
+
+ $this->assertEqual(count($source_strings), count($test_strings), t("Found correct number of source strings."));
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleMultilingualFieldsTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleMultilingualFieldsTest.php
new file mode 100644
index 00000000000..2a8299ade56
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleMultilingualFieldsTest.php
@@ -0,0 +1,137 @@
+ 'Multilingual fields',
+ 'description' => 'Test multilingual support for fields.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('node', 'locale'));
+
+ // Create Basic page node type.
+ $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+ // Setup users.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content'));
+ $this->drupalLogin($admin_user);
+
+ // Add a new language.
+ $language = (object) array(
+ 'langcode' => 'it',
+ 'name' => 'Italian',
+ );
+ language_save($language);
+
+ // Enable URL language detection and selection.
+ $edit = array('language_interface[enabled][language-url]' => '1');
+ $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
+
+ // Set "Basic page" content type to use multilingual support.
+ $edit = array(
+ 'node_type_language' => 1,
+ );
+ $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
+
+ // Make node body translatable.
+ $field = field_info_field('body');
+ $field['translatable'] = TRUE;
+ field_update_field($field);
+ }
+
+ /**
+ * Test if field languages are correctly set through the node form.
+ */
+ function testMultilingualNodeForm() {
+ // Create "Basic page" content.
+ $langcode = LANGUAGE_NOT_SPECIFIED;
+ $title_key = "title";
+ $title_value = $this->randomName(8);
+ $body_key = "body[$langcode][0][value]";
+ $body_value = $this->randomName(16);
+
+ // Create node to edit.
+ $edit = array();
+ $edit[$title_key] = $title_value;
+ $edit[$body_key] = $body_value;
+ $edit['langcode'] = 'en';
+ $this->drupalPost('node/add/page', $edit, t('Save'));
+
+ // Check that the node exists in the database.
+ $node = $this->drupalGetNodeByTitle($edit[$title_key]);
+ $this->assertTrue($node, t('Node found in database.'));
+
+ $assert = isset($node->body['en']) && !isset($node->body[LANGUAGE_NOT_SPECIFIED]) && $node->body['en'][0]['value'] == $body_value;
+ $this->assertTrue($assert, t('Field language correctly set.'));
+
+ // Change node language.
+ $this->drupalGet("node/$node->nid/edit");
+ $edit = array(
+ $title_key => $this->randomName(8),
+ 'langcode' => 'it'
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($edit[$title_key]);
+ $this->assertTrue($node, t('Node found in database.'));
+
+ $assert = isset($node->body['it']) && !isset($node->body['en']) && $node->body['it'][0]['value'] == $body_value;
+ $this->assertTrue($assert, t('Field language correctly changed.'));
+
+ // Enable content language URL detection.
+ language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0));
+
+ // Test multilingual field language fallback logic.
+ $this->drupalGet("it/node/$node->nid");
+ $this->assertRaw($body_value, t('Body correctly displayed using Italian as requested language'));
+
+ $this->drupalGet("node/$node->nid");
+ $this->assertRaw($body_value, t('Body correctly displayed using English as requested language'));
+ }
+
+ /*
+ * Test multilingual field display settings.
+ */
+ function testMultilingualDisplaySettings() {
+ // Create "Basic page" content.
+ $langcode = LANGUAGE_NOT_SPECIFIED;
+ $title_key = "title";
+ $title_value = $this->randomName(8);
+ $body_key = "body[$langcode][0][value]";
+ $body_value = $this->randomName(16);
+
+ // Create node to edit.
+ $edit = array();
+ $edit[$title_key] = $title_value;
+ $edit[$body_key] = $body_value;
+ $edit['langcode'] = 'en';
+ $this->drupalPost('node/add/page', $edit, t('Save'));
+
+ // Check that the node exists in the database.
+ $node = $this->drupalGetNodeByTitle($edit[$title_key]);
+ $this->assertTrue($node, t('Node found in database.'));
+
+ // Check if node body is showed.
+ $this->drupalGet("node/$node->nid");
+ $body = $this->xpath('//article[@id=:id]//div[@class=:class]/descendant::p', array(
+ ':id' => 'node-' . $node->nid,
+ ':class' => 'content',
+ ));
+ $this->assertEqual(current($body), $node->body['en'][0]['value'], 'Node body found.');
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePathTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePathTest.php
new file mode 100644
index 00000000000..61fd67fef87
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePathTest.php
@@ -0,0 +1,149 @@
+ 'Path language settings',
+ 'description' => 'Checks you can configure a language for individual url aliases.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('node', 'locale', 'path'));
+
+ $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+ variable_set('site_frontpage', 'node');
+ }
+
+ /**
+ * Test if a language can be associated with a path alias.
+ */
+ function testPathLanguageConfiguration() {
+ global $base_url;
+
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'create page content', 'administer url aliases', 'create url aliases', 'access administration pages'));
+
+ // Add custom language.
+ $this->drupalLogin($admin_user);
+ // Code for the language.
+ $langcode = 'xx';
+ // The English name for the language.
+ $name = $this->randomName(16);
+ // The domain prefix.
+ $prefix = $langcode;
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Set path prefix.
+ $edit = array( "prefix[$langcode]" => $prefix );
+ $this->drupalPost('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'), t('The "xx" front page is readibly available.'));
+
+ // Create a node.
+ $node = $this->drupalCreateNode(array('type' => 'page'));
+
+ // Create a path alias in default language (English).
+ $path = 'admin/config/search/path/add';
+ $english_path = $this->randomName(8);
+ $edit = array(
+ 'source' => 'node/' . $node->nid,
+ 'alias' => $english_path,
+ 'langcode' => 'en',
+ );
+ $this->drupalPost($path, $edit, t('Save'));
+
+ // Create a path alias in new custom language.
+ $custom_language_path = $this->randomName(8);
+ $edit = array(
+ 'source' => 'node/' . $node->nid,
+ 'alias' => $custom_language_path,
+ 'langcode' => $langcode,
+ );
+ $this->drupalPost($path, $edit, t('Save'));
+
+ // Confirm English language path alias works.
+ $this->drupalGet($english_path);
+ $this->assertText($node->title, t('English alias works.'));
+
+ // Confirm custom language path alias works.
+ $this->drupalGet($prefix . '/' . $custom_language_path);
+ $this->assertText($node->title, t('Custom language alias works.'));
+
+ // Create a custom path.
+ $custom_path = $this->randomName(8);
+
+ // Check priority of language for alias by source path.
+ $edit = array(
+ 'source' => 'node/' . $node->nid,
+ 'alias' => $custom_path,
+ 'langcode' => LANGUAGE_NOT_SPECIFIED,
+ );
+ path_save($edit);
+ $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, 'en');
+ $this->assertEqual($english_path, $lookup_path, t('English language alias has priority.'));
+ // Same check for language 'xx'.
+ $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, $prefix);
+ $this->assertEqual($custom_language_path, $lookup_path, t('Custom language alias has priority.'));
+ path_delete($edit);
+
+ // Create language nodes to check priority of aliases.
+ $first_node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
+ $second_node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
+
+ // Assign a custom path alias to the first node with the English language.
+ $edit = array(
+ 'source' => 'node/' . $first_node->nid,
+ 'alias' => $custom_path,
+ 'langcode' => 'en',
+ );
+ path_save($edit);
+
+ // Assign a custom path alias to second node with LANGUAGE_NOT_SPECIFIED.
+ $edit = array(
+ 'source' => 'node/' . $second_node->nid,
+ 'alias' => $custom_path,
+ 'langcode' => LANGUAGE_NOT_SPECIFIED,
+ );
+ path_save($edit);
+
+ // Test that both node titles link to our path alias.
+ $this->drupalGet('');
+ $custom_path_url = base_path() . $GLOBALS['script_path'] . $custom_path;
+ $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $first_node->title));
+ $this->assertTrue(!empty($elements), t('First node links to the path alias.'));
+ $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $second_node->title));
+ $this->assertTrue(!empty($elements), t('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->title, t('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->title, t('Custom alias with prefix returns second node.'));
+
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
new file mode 100644
index 00000000000..6d45d215225
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocalePluralFormatTest.php
@@ -0,0 +1,323 @@
+ 'Plural handling',
+ 'description' => 'Tests plural handling for various languages.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
+ $this->drupalLogin($admin_user);
+ }
+
+ /**
+ * Tests locale_get_plural() and format_plural() functionality.
+ */
+ function testGetPluralFormat() {
+ // Import some .po files with formulas to set up the environment.
+ // These will also add the languages to the system and enable them.
+ $this->importPoFile($this->getPoFileWithSimplePlural(), array(
+ 'langcode' => 'fr',
+ ));
+ $this->importPoFile($this->getPoFileWithComplexPlural(), array(
+ 'langcode' => 'hr',
+ ));
+
+ // Attempt to import some broken .po files as well to prove that these
+ // will not overwrite the proper plural formula imported above.
+ $this->importPoFile($this->getPoFileWithMissingPlural(), array(
+ 'langcode' => 'fr',
+ 'overwrite_options[not_customized]' => TRUE,
+ ));
+ $this->importPoFile($this->getPoFileWithBrokenPlural(), array(
+ 'langcode' => 'hr',
+ 'overwrite_options[not_customized]' => TRUE,
+ ));
+
+ // Reset static caches from locale_get_plural() to ensure we get fresh data.
+ drupal_static_reset('locale_get_plural');
+ drupal_static_reset('locale_get_plural:plurals');
+ drupal_static_reset('locale');
+
+ // Expected plural translation strings for each plural index.
+ $plural_strings = array(
+ // English is not imported in this case, so we assume built-in text
+ // and formulas.
+ 'en' => array(
+ 0 => '1 hour',
+ 1 => '@count hours',
+ ),
+ 'fr' => array(
+ 0 => '1 heure',
+ 1 => '@count heures',
+ ),
+ 'hr' => array(
+ 0 => '@count sat',
+ 1 => '@count sata',
+ 2 => '@count sati',
+ ),
+ // Hungarian is not imported, so it should assume the same text as
+ // English, but it will always pick the plural form as per the built-in
+ // logic, so only index -1 is relevant with the plural value.
+ 'hu' => array(
+ 0 => '1 hour',
+ -1 => '@count hours',
+ ),
+ );
+
+ // Expected plural indexes precomputed base on the plural formulas with
+ // given $count value.
+ $plural_tests = array(
+ 'en' => array(
+ 1 => 0,
+ 0 => 1,
+ 5 => 1,
+ ),
+ 'fr' => array(
+ 1 => 0,
+ 0 => 0,
+ 5 => 1,
+ ),
+ 'hr' => array(
+ 1 => 0,
+ 21 => 0,
+ 0 => 2,
+ 2 => 1,
+ 8 => 2,
+ ),
+ 'hu' => array(
+ 1 => -1,
+ 21 => -1,
+ 0 => -1,
+ ),
+ );
+
+ foreach ($plural_tests as $langcode => $tests) {
+ foreach ($tests as $count => $expected_plural_index) {
+ // Assert that the we get the right plural index.
+ $this->assertIdentical(locale_get_plural($count, $langcode), $expected_plural_index, 'Computed plural index for ' . $langcode . ' for count ' . $count . ' is ' . $expected_plural_index);
+ // Assert that the we get the right translation for that. Change the
+ // expected index as per the logic for translation lookups.
+ $expected_plural_index = ($count == 1) ? 0 : $expected_plural_index;
+ $expected_plural_string = str_replace('@count', $count, $plural_strings[$langcode][$expected_plural_index]);
+ $this->assertIdentical(format_plural($count, '1 hour', '@count hours', array(), array('langcode' => $langcode)), $expected_plural_string, 'Plural translation of 1 hours / @count hours for count ' . $count . ' in ' . $langcode . ' is ' . $expected_plural_string);
+ }
+ }
+ }
+
+ /**
+ * Tests plural editing and export functionality.
+ */
+ function testPluralEditExport() {
+ // Import some .po files with formulas to set up the environment.
+ // These will also add the languages to the system and enable them.
+ $this->importPoFile($this->getPoFileWithSimplePlural(), array(
+ 'langcode' => 'fr',
+ ));
+ $this->importPoFile($this->getPoFileWithComplexPlural(), array(
+ 'langcode' => 'hr',
+ ));
+
+ // Get the French translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'fr',
+ ), t('Export'));
+ // Ensure we have a translation file.
+ $this->assertRaw('# French translation of Drupal', t('Exported French translation file.'));
+ // Ensure our imported translations exist in the file.
+ $this->assertRaw("msgid \"Monday\"\nmsgstr \"lundi\"", t('French translations present in exported file.'));
+ // Check for plural export specifically.
+ $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"1 heure\"\nmsgstr[1] \"@count heures\"", t('Plural translations exported properly.'));
+
+ // Get the Croatian translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'hr',
+ ), t('Export'));
+ // Ensure we have a translation file.
+ $this->assertRaw('# Croatian translation of Drupal', t('Exported Croatian translation file.'));
+ // Ensure our imported translations exist in the file.
+ $this->assertRaw("msgid \"Monday\"\nmsgstr \"Ponedjeljak\"", t('Croatian translations present in exported file.'));
+ // Check for plural export specifically.
+ $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"@count sat\"\nmsgstr[1] \"@count sata\"\nmsgstr[2] \"@count sati\"", t('Plural translations exported properly.'));
+
+ // Check if the source appears on the translation page.
+ $this->drupalGet('admin/config/regional/translate');
+ $this->assertText("1 hour, @count hours");
+
+ // Look up editing page for this plural string and check fields.
+ $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 hour" . LOCALE_PLURAL_DELIMITER . "@count hours"))->fetchField();
+ $path = 'admin/config/regional/translate/edit/' . $lid;
+ $this->drupalGet($path);
+ // Labels for plural editing elements.
+ $this->assertText('Singular form');
+ $this->assertText('First plural form');
+ $this->assertText('2. plural form');
+ // Plural values for both languages.
+ $this->assertFieldById('edit-translations-hr-0', '@count sat');
+ $this->assertFieldById('edit-translations-hr-1', '@count sata');
+ $this->assertFieldById('edit-translations-hr-2', '@count sati');
+ $this->assertNoFieldById('edit-translations-hr-3');
+ $this->assertFieldById('edit-translations-fr-0', '1 heure');
+ $this->assertFieldById('edit-translations-fr-1', '@count heures');
+ $this->assertNoFieldById('edit-translations-fr-2');
+
+ // Edit some translations and see if that took effect.
+ $edit = array(
+ 'translations[fr][0]' => '1 heure edited',
+ 'translations[hr][1]' => '@count sata edited',
+ );
+ $this->drupalPost($path, $edit, t('Save translations'));
+
+ // Inject a plural source string to the database. We need to use a specific
+ // langcode here because the language will be English by default and will
+ // not save our source string for performance optimization if we do not ask
+ // specifically for a language.
+ format_plural(1, '1 day', '@count days', array(), array('langcode' => 'fr'));
+ // Look up editing page for this plural string and check fields.
+ $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 day" . LOCALE_PLURAL_DELIMITER . "@count days"))->fetchField();
+ $path = 'admin/config/regional/translate/edit/' . $lid;
+
+ // Save complete translations for the string in both languages.
+ $edit = array(
+ 'translations[fr][0]' => '1 jour',
+ 'translations[fr][1]' => '@count jours',
+ 'translations[hr][0]' => '@count dan',
+ 'translations[hr][1]' => '@count dana',
+ 'translations[hr][2]' => '@count dana',
+ );
+ $this->drupalPost($path, $edit, t('Save translations'));
+
+ // Get the French translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'fr',
+ ), t('Export'));
+ // Check for plural export specifically.
+ $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"1 heure edited\"\nmsgstr[1] \"@count heures\"", t('Edited French plural translations for hours exported properly.'));
+ $this->assertRaw("msgid \"1 day\"\nmsgid_plural \"@count days\"\nmsgstr[0] \"1 jour\"\nmsgstr[1] \"@count jours\"", t('Added French plural translations for days exported properly.'));
+
+ // Get the Croatian translations.
+ $this->drupalPost('admin/config/regional/translate/export', array(
+ 'langcode' => 'hr',
+ ), t('Export'));
+ // Check for plural export specifically.
+ $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"@count sat\"\nmsgstr[1] \"@count sata edited\"\nmsgstr[2] \"@count sati\"", t('Edited Croatian plural translations exported properly.'));
+ $this->assertRaw("msgid \"1 day\"\nmsgid_plural \"@count days\"\nmsgstr[0] \"@count dan\"\nmsgstr[1] \"@count dana\"\nmsgstr[2] \"@count dana\"", t('Added Croatian plural translations exported properly.'));
+ }
+
+
+ /**
+ * Imports a standalone .po file in a given language.
+ *
+ * @param $contents
+ * Contents of the .po file to import.
+ * @param $options
+ * Additional options to pass to the translation import form.
+ */
+ function importPoFile($contents, array $options = array()) {
+ $name = tempnam('temporary://', "po_") . '.po';
+ file_put_contents($name, $contents);
+ $options['files[file]'] = $name;
+ $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
+ drupal_unlink($name);
+ }
+
+ /**
+ * Returns a .po file with a simple plural formula.
+ */
+ function getPoFileWithSimplePlural() {
+ return <<< EOF
+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 "1 hour"
+msgid_plural "@count hours"
+msgstr[0] "1 heure"
+msgstr[1] "@count heures"
+
+msgid "Monday"
+msgstr "lundi"
+EOF;
+ }
+
+ /**
+ * Returns a .po file with a complex plural formula.
+ */
+ function getPoFileWithComplexPlural() {
+ return <<< EOF
+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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
+
+msgid "1 hour"
+msgid_plural "@count hours"
+msgstr[0] "@count sat"
+msgstr[1] "@count sata"
+msgstr[2] "@count sati"
+
+msgid "Monday"
+msgstr "Ponedjeljak"
+EOF;
+ }
+
+ /**
+ * Returns a .po file with a missing plural formula.
+ */
+ function getPoFileWithMissingPlural() {
+ return <<< EOF
+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"
+
+msgid "Monday"
+msgstr "lundi"
+EOF;
+ }
+
+ /**
+ * Returns a .po file with a broken plural formula.
+ */
+ function getPoFileWithBrokenPlural() {
+ return <<< EOF
+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: broken, will not parse\\n"
+
+msgid "Monday"
+msgstr "Ponedjeljak"
+EOF;
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
new file mode 100644
index 00000000000..28e0834ba48
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationTest.php
@@ -0,0 +1,418 @@
+ 'String translate, search and validate',
+ 'description' => 'Adds a new locale and translates its name. Checks the validation of translation strings and search results.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+ }
+
+ /**
+ * Adds a language and tests string translation by users with the appropriate permissions.
+ */
+ function testStringTranslation() {
+ global $base_url;
+
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
+ // User to translate and delete string.
+ $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
+ // Code for the language.
+ $langcode = 'xx';
+ // The English name for the language. This will be translated.
+ $name = $this->randomName(16);
+ // This is the language indicator on the translation search screen for
+ // untranslated strings.
+ $language_indicator = "$langcode ";
+ // This will be the translation of $name.
+ $translation = $this->randomName(16);
+ $translation_to_en = $this->randomName(16);
+
+ // Add custom language.
+ $this->drupalLogin($admin_user);
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+ // Add string.
+ t($name, array(), array('langcode' => $langcode));
+ // Reset locale cache.
+ locale_reset();
+ $this->assertRaw('"edit-site-default-' . $langcode .'"', t('Language code found.'));
+ $this->assertText(t($name), t('Test language added.'));
+ $this->drupalLogout();
+
+ // Search for the name and translate it.
+ $this->drupalLogin($translate_user);
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // assertText() seems to remove the input field where $name always could be
+ // found, so this is not a false assert. See how assertNoText succeeds
+ // later.
+ $this->assertText($name, t('Search found the name.'));
+ $this->assertRaw($language_indicator, t('Name is untranslated.'));
+ // Assume this is the only result, given the random name.
+ $this->clickLink(t('edit'));
+ // We save the lid from the path.
+ $matches = array();
+ preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches);
+ $lid = $matches[1];
+ // No t() here, it's surely not translated yet.
+ $this->assertText($name, t('name found on edit screen.'));
+ $this->assertNoText('English', t('No way to translate the string to English.'));
+ $this->drupalLogout();
+ $this->drupalLogin($admin_user);
+ $this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
+ $this->drupalLogout();
+ $this->drupalLogin($translate_user);
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // assertText() seems to remove the input field where $name always could be
+ // found, so this is not a false assert. See how assertNoText succeeds
+ // later.
+ $this->assertText($name, t('Search found the name.'));
+ $this->assertRaw($language_indicator, t('Name is untranslated.'));
+ // Assume this is the only result, given the random name.
+ $this->clickLink(t('edit'));
+ $string_edit_url = $this->getUrl();
+ $edit = array(
+ "translations[$langcode][0]" => $translation,
+ 'translations[en][0]' => $translation_to_en,
+ );
+ $this->drupalPost(NULL, $edit, t('Save translations'));
+ $this->assertText(t('The string has been saved.'), t('The string has been saved.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->drupalGet($string_edit_url);
+ $this->assertRaw($translation, t('Non-English translation properly saved.'));
+ $this->assertRaw($translation_to_en, t('English translation properly saved.'));
+ $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works for non-English.'));
+ // Refresh the locale() cache to get fresh data from t() below. We are in
+ // the same HTTP request and therefore t() is not refreshed by saving the
+ // translation above.
+ locale_reset();
+ // Now we should get the proper fresh translation from t().
+ $this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.'));
+ $this->assertTrue(t($name, array(), array('langcode' => LANGUAGE_SYSTEM)) == $name, t('t() works for LANGUAGE_SYSTEM.'));
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // The indicator should not be here.
+ $this->assertNoRaw($language_indicator, t('String is translated.'));
+
+ // Try to edit a non-existent string and ensure we're redirected correctly.
+ // Assuming we don't have 999,999 strings already.
+ $random_lid = 999999;
+ $this->drupalGet('admin/config/regional/translate/edit/' . $random_lid);
+ $this->assertText(t('String not found'), t('String not found.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->drupalLogout();
+
+ // Delete the language.
+ $this->drupalLogin($admin_user);
+ $path = 'admin/config/regional/language/delete/' . $langcode;
+ // This a confirm form, we do not need any fields changed.
+ $this->drupalPost($path, array(), t('Delete'));
+ // We need raw here because %language and %langcode will add HTML.
+ $t_args = array('%language' => $name, '%langcode' => $langcode);
+ $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), t('The test language has been removed.'));
+ // Reload to remove $name.
+ $this->drupalGet($path);
+ // Verify that language is no longer found.
+ $this->assertResponse(404, t('Language no longer found.'));
+ $this->drupalLogout();
+
+ // Delete the string.
+ $this->drupalLogin($translate_user);
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // Assume this is the only result, given the random name.
+ $this->clickLink(t('delete'));
+ $this->assertText(t('Are you sure you want to delete the string'), t('"delete" link is correct.'));
+ // Delete the string.
+ $path = 'admin/config/regional/translate/delete/' . $lid;
+ $this->drupalGet($path);
+ // First test the 'cancel' link.
+ $this->clickLink(t('Cancel'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->assertRaw($name, t('The string was not deleted.'));
+ // Delete the name string.
+ $this->drupalPost('admin/config/regional/translate/delete/' . $lid, array(), t('Delete'));
+ $this->assertText(t('The string has been removed.'), t('The string has been removed message.'));
+ $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText($name, t('Search now can not find the name.'));
+ }
+
+ /*
+ * Adds a language and checks that the JavaScript translation files are
+ * properly created and rebuilt on deletion.
+ */
+ function testJavaScriptTranslation() {
+ $user = $this->drupalCreateUser(array('translate interface', 'administer languages', 'access administration pages'));
+ $this->drupalLogin($user);
+
+ $langcode = 'xx';
+ // The English name for the language. This will be translated.
+ $name = $this->randomName(16);
+
+ // Add custom language.
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+ drupal_static_reset('language_list');
+
+ // Build the JavaScript translation file.
+ $this->drupalGet('admin/config/regional/translate/translate');
+
+ // Retrieve the id of the first string available in the {locales_source}
+ // table and translate it.
+ $query = db_select('locales_source', 'l');
+ $query->addExpression('min(l.lid)', 'lid');
+ $result = $query->condition('l.location', '%.js%', 'LIKE')->execute();
+ $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid;
+ $edit = array('translations['. $langcode .'][0]' => $this->randomName());
+ $this->drupalPost($url, $edit, t('Save translations'));
+
+ // Trigger JavaScript translation parsing and building.
+ _locale_rebuild_js($langcode);
+
+ $locale_javascripts = variable_get('locale_translation_javascript', array());
+ $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/' . $langcode . '_' . $locale_javascripts[$langcode] . '.js';
+ $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('not found'))));
+
+ // Test JavaScript translation rebuilding.
+ file_unmanaged_delete($js_file);
+ $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
+ cache_clear_all();
+ _locale_rebuild_js($langcode);
+ $this->assertTrue($result = file_exists($js_file), t('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : t('not found'))));
+ }
+
+ /**
+ * Tests the validation of the translation input.
+ */
+ function testStringValidation() {
+ global $base_url;
+
+ // User to add language and strings.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface'));
+ $this->drupalLogin($admin_user);
+ $langcode = 'xx';
+ // The English name for the language. This will be translated.
+ $name = $this->randomName(16);
+ // This is the language indicator on the translation search screen for
+ // untranslated strings.
+ $language_indicator = "$langcode ";
+ // These will be the invalid translations of $name.
+ $key = $this->randomName(16);
+ $bad_translations[$key] = "" . $key;
+ $key = $this->randomName(16);
+ $bad_translations[$key] = '
' . $key;
+ $key = $this->randomName(16);
+ $bad_translations[$key] = '<' . $key;
+ $key = $this->randomName(16);
+ $bad_translations[$key] ="" . $key;
+
+ // Add custom language.
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+ // Add string.
+ t($name, array(), array('langcode' => $langcode));
+ // Reset locale cache.
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // Find the edit path.
+ $content = $this->drupalGetContent();
+ $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), t('Found the edit path.'));
+ $path = $matches[0];
+ foreach ($bad_translations as $key => $translation) {
+ $edit = array(
+ "translations[$langcode][0]" => $translation,
+ );
+ $this->drupalPost($path, $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'), t('The string was rejected as unsafe.'));
+ $this->assertNoText(t('The string has been saved.'), t('The string was not saved.'));
+ }
+ }
+
+ /**
+ * Tests translation search form.
+ */
+ function testStringSearch() {
+ global $base_url;
+
+ // User to add and remove language.
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
+ // User to translate and delete string.
+ $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
+
+ // Code for the language.
+ $langcode = 'xx';
+ // The English name for the language. This will be translated.
+ $name = $this->randomName(16);
+ // This is the language indicator on the translation search screen for
+ // untranslated strings.
+ $language_indicator = "$langcode ";
+ // This will be the translation of $name.
+ $translation = $this->randomName(16);
+
+ // Add custom language.
+ $this->drupalLogin($admin_user);
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+ // Add string.
+ t($name, array(), array('langcode' => $langcode));
+ // Reset locale cache.
+ locale_reset();
+ $this->drupalLogout();
+
+ // Search for the name.
+ $this->drupalLogin($translate_user);
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ // assertText() seems to remove the input field where $name always could be
+ // found, so this is not a false assert. See how assertNoText succeeds
+ // later.
+ $this->assertText($name, t('Search found the string.'));
+
+ // Ensure untranslated string doesn't appear if searching on 'only
+ // translated strings'.
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t("Search didn't find the string."));
+
+ // Ensure untranslated string appears if searching on 'only untranslated
+ // strings'.
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'untranslated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('Search found the string.'));
+
+ // Add translation.
+ // Assume this is the only result, given the random name.
+ $this->clickLink(t('edit'));
+ // We save the lid from the path.
+ $matches = array();
+ preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches);
+ $lid = $matches[1];
+ $edit = array(
+ "translations[$langcode][0]" => $translation,
+ );
+ $this->drupalPost(NULL, $edit, t('Save translations'));
+
+ // Ensure translated string does appear if searching on 'only
+ // translated strings'.
+ $search = array(
+ 'string' => $translation,
+ 'language' => 'all',
+ 'translation' => 'translated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
+
+ // Ensure translated source string doesn't appear if searching on 'only
+ // untranslated strings'.
+ $search = array(
+ 'string' => $name,
+ 'language' => 'all',
+ 'translation' => 'untranslated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t("Search didn't find the source string."));
+
+ // Ensure translated string doesn't appear if searching on 'only
+ // untranslated strings'.
+ $search = array(
+ 'string' => $translation,
+ 'language' => 'all',
+ 'translation' => 'untranslated',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
+
+ // Ensure translated string does appear if searching on the custom language.
+ $search = array(
+ 'string' => $translation,
+ 'language' => $langcode,
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
+
+ // Ensure translated string doesn't appear if searching in System (English).
+ $search = array(
+ 'string' => $translation,
+ 'language' => LANGUAGE_SYSTEM,
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
+
+ // Search for a string that isn't in the system.
+ $unavailable_string = $this->randomName(16);
+ $search = array(
+ 'string' => $unavailable_string,
+ 'language' => 'all',
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText(t('No strings available.'), t("Search didn't find the invalid string."));
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallFrenchTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallFrenchTest.php
new file mode 100644
index 00000000000..047ff630670
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallFrenchTest.php
@@ -0,0 +1,31 @@
+ 'Locale uninstall (FR)',
+ 'description' => 'Tests the uninstall process using French as interface language.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ $this->langcode = 'fr';
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
new file mode 100644
index 00000000000..72868e9e1bc
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
@@ -0,0 +1,132 @@
+ 'Locale uninstall (EN)',
+ 'description' => 'Tests the uninstall process using the built-in UI language.',
+ 'group' => 'Locale',
+ );
+ }
+
+ /**
+ * The default language set for the UI before uninstall.
+ */
+ protected $language;
+
+ function setUp() {
+ parent::setUp(array('node', 'locale'));
+ $this->langcode = 'en';
+
+ // Create Article node type.
+ $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+ }
+
+ /**
+ * Check if the values of the Locale variables are correct after uninstall.
+ */
+ function testUninstallProcess() {
+ $locale_module = array('locale', 'language');
+
+ $language = (object) array(
+ 'langcode' => 'fr',
+ 'name' => 'French',
+ 'default' => $this->langcode == 'fr',
+ );
+ language_save($language);
+
+ // Check the UI language.
+ drupal_language_initialize();
+ $this->assertEqual(drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode, $this->langcode, t('Current language: %lang', array('%lang' => drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode)));
+
+ // Enable multilingual workflow option for articles.
+ variable_set('node_type_language_article', 1);
+
+ // Change JavaScript translations directory.
+ variable_set('locale_js_directory', 'js_translations');
+
+ // Build the JavaScript translation file for French.
+ $user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
+ $this->drupalLogin($user);
+ $this->drupalGet('admin/config/regional/translate/translate');
+ $string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location', array(
+ ':location' => '%.js%',
+ ))->fetchObject();
+ $edit = array('translations[fr][0]' => 'french translation');
+ $this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations'));
+ _locale_rebuild_js('fr');
+ $locale_javascripts = variable_get('locale_translation_javascript', array());
+ $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $locale_javascripts['fr'] . '.js';
+ $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('none'))));
+
+ // Disable string caching.
+ variable_set('locale_cache_strings', 0);
+
+ // Change language negotiation options.
+ drupal_load('module', 'locale');
+ variable_set('language_types', language_types_get_default() + array('language_custom' => TRUE));
+ variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, language_language_negotiation_info());
+ variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, language_language_negotiation_info());
+ variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, language_language_negotiation_info());
+
+ // Change language negotiation settings.
+ variable_set('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX);
+ variable_set('language_negotiation_session_param', TRUE);
+
+ // Uninstall Locale.
+ module_disable($locale_module);
+ drupal_uninstall_modules($locale_module);
+
+ // Visit the front page.
+ $this->drupalGet('');
+
+ // Check the init language logic.
+ drupal_language_initialize();
+ $this->assertEqual(drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode, 'en', t('Language after uninstall: %lang', array('%lang' => drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode)));
+
+ // Check JavaScript files deletion.
+ $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
+
+ // Check language count.
+ $language_count = variable_get('language_count', 1);
+ $this->assertEqual($language_count, 1, t('Language count: %count', array('%count' => $language_count)));
+
+ // Check language negotiation.
+ require_once DRUPAL_ROOT . '/core/includes/language.inc';
+ $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), t('Language types reset'));
+ $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT;
+ $this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+ $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT;
+ $this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+ $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT;
+ $this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
+
+ // Check language negotiation method settings.
+ $this->assertFalse(variable_get('language_negotiation_url_part', FALSE), t('URL language negotiation method indicator settings cleared.'));
+ $this->assertFalse(variable_get('language_negotiation_session_param', FALSE), t('Visit language negotiation method settings cleared.'));
+
+ // Check JavaScript parsed.
+ $javascript_parsed_count = count(variable_get('javascript_parsed', array()));
+ $this->assertEqual($javascript_parsed_count, 0, t('JavaScript parsed count: %count', array('%count' => $javascript_parsed_count)));
+
+ // Check JavaScript translations directory.
+ $locale_js_directory = variable_get('locale_js_directory', 'languages');
+ $this->assertEqual($locale_js_directory, 'languages', t('JavaScript translations directory: %dir', array('%dir' => $locale_js_directory)));
+
+ // Check string caching.
+ $locale_cache_strings = variable_get('locale_cache_strings', 1);
+ $this->assertEqual($locale_cache_strings, 1, t('String caching: %status', array('%status' => t($locale_cache_strings ? 'enabled': 'disabled'))));
+ }
+}
diff --git a/core/modules/locale/locale.info b/core/modules/locale/locale.info
index 8b3e5f74aca..e0749dba9d6 100644
--- a/core/modules/locale/locale.info
+++ b/core/modules/locale/locale.info
@@ -4,4 +4,3 @@ package = Core
version = VERSION
core = 8.x
dependencies[] = language
-files[] = locale.test
diff --git a/core/modules/locale/locale.test b/core/modules/locale/locale.test
deleted file mode 100644
index 5e252bf4d82..00000000000
--- a/core/modules/locale/locale.test
+++ /dev/null
@@ -1,2263 +0,0 @@
- 'Javascript translation',
- 'description' => 'Tests parsing js files for translatable strings',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp('locale');
- }
-
- function testFileParsing() {
-
- $filename = drupal_get_path('module', 'locale') . '/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.
- $source_strings = db_select('locales_source', 's')
- ->fields('s', array('source', 'context'))
- ->condition('s.location', $filename)
- ->execute()
- ->fetchAllKeyed();
-
- // List of all strings that should be in the file.
- $test_strings = array(
- "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" => '',
- "Standard Call @count plural" => '',
- "Whitespace Call plural" => '',
- "Whitespace Call @count plural" => '',
-
- "Single Quote plural" => '',
- "Single Quote @count plural" => '',
- "Single Quote \\'Escaped\\' plural" => '',
- "Single Quote \\'Escaped\\' @count plural" => '',
-
- "Double Quote plural" => '',
- "Double Quote @count plural" => '',
- "Double Quote \\\"Escaped\\\" plural" => '',
- "Double Quote \\\"Escaped\\\" @count plural" => '',
-
- "Context !key Args plural" => "Context string",
- "Context !key Args @count plural" => "Context string",
-
- "Context Unquoted plural" => "Context string unquoted",
- "Context Unquoted @count plural" => "Context string unquoted",
- "Context Single Quoted plural" => "Context string single quoted",
- "Context Single Quoted @count plural" => "Context string single quoted",
- "Context Double Quoted plural" => "Context string double quoted",
- "Context Double Quoted @count plural" => "Context string double quoted",
- );
-
- // Assert that all strings were found properly.
- foreach ($test_strings as $str => $context) {
- $args = array('%source' => $str, '%context' => $context);
-
- // Make sure that the string was found in the file.
- $this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", $args));
-
- // Make sure that the proper context was matched.
- $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? t("Context for %source is %context", $args) : t("Context for %source is blank", $args));
- }
-
- $this->assertEqual(count($source_strings), count($test_strings), t("Found correct number of source strings."));
- }
-}
-
-/**
- * Functional test for string translation and validation.
- */
-class LocaleTranslationFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'String translate, search and validate',
- 'description' => 'Adds a new locale and translates its name. Checks the validation of translation strings and search results.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp('locale');
- }
-
- /**
- * Adds a language and tests string translation by users with the appropriate permissions.
- */
- function testStringTranslation() {
- global $base_url;
-
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
- // User to translate and delete string.
- $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
- // Code for the language.
- $langcode = 'xx';
- // The English name for the language. This will be translated.
- $name = $this->randomName(16);
- // This is the language indicator on the translation search screen for
- // untranslated strings.
- $language_indicator = "$langcode ";
- // This will be the translation of $name.
- $translation = $this->randomName(16);
- $translation_to_en = $this->randomName(16);
-
- // Add custom language.
- $this->drupalLogin($admin_user);
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
- // Add string.
- t($name, array(), array('langcode' => $langcode));
- // Reset locale cache.
- locale_reset();
- $this->assertRaw('"edit-site-default-' . $langcode .'"', t('Language code found.'));
- $this->assertText(t($name), t('Test language added.'));
- $this->drupalLogout();
-
- // Search for the name and translate it.
- $this->drupalLogin($translate_user);
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // assertText() seems to remove the input field where $name always could be
- // found, so this is not a false assert. See how assertNoText succeeds
- // later.
- $this->assertText($name, t('Search found the name.'));
- $this->assertRaw($language_indicator, t('Name is untranslated.'));
- // Assume this is the only result, given the random name.
- $this->clickLink(t('edit'));
- // We save the lid from the path.
- $matches = array();
- preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches);
- $lid = $matches[1];
- // No t() here, it's surely not translated yet.
- $this->assertText($name, t('name found on edit screen.'));
- $this->assertNoText('English', t('No way to translate the string to English.'));
- $this->drupalLogout();
- $this->drupalLogin($admin_user);
- $this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
- $this->drupalLogout();
- $this->drupalLogin($translate_user);
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // assertText() seems to remove the input field where $name always could be
- // found, so this is not a false assert. See how assertNoText succeeds
- // later.
- $this->assertText($name, t('Search found the name.'));
- $this->assertRaw($language_indicator, t('Name is untranslated.'));
- // Assume this is the only result, given the random name.
- $this->clickLink(t('edit'));
- $string_edit_url = $this->getUrl();
- $edit = array(
- "translations[$langcode][0]" => $translation,
- 'translations[en][0]' => $translation_to_en,
- );
- $this->drupalPost(NULL, $edit, t('Save translations'));
- $this->assertText(t('The string has been saved.'), t('The string has been saved.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->drupalGet($string_edit_url);
- $this->assertRaw($translation, t('Non-English translation properly saved.'));
- $this->assertRaw($translation_to_en, t('English translation properly saved.'));
- $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works for non-English.'));
- // Refresh the locale() cache to get fresh data from t() below. We are in
- // the same HTTP request and therefore t() is not refreshed by saving the
- // translation above.
- locale_reset();
- // Now we should get the proper fresh translation from t().
- $this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.'));
- $this->assertTrue(t($name, array(), array('langcode' => LANGUAGE_SYSTEM)) == $name, t('t() works for LANGUAGE_SYSTEM.'));
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // The indicator should not be here.
- $this->assertNoRaw($language_indicator, t('String is translated.'));
-
- // Try to edit a non-existent string and ensure we're redirected correctly.
- // Assuming we don't have 999,999 strings already.
- $random_lid = 999999;
- $this->drupalGet('admin/config/regional/translate/edit/' . $random_lid);
- $this->assertText(t('String not found'), t('String not found.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->drupalLogout();
-
- // Delete the language.
- $this->drupalLogin($admin_user);
- $path = 'admin/config/regional/language/delete/' . $langcode;
- // This a confirm form, we do not need any fields changed.
- $this->drupalPost($path, array(), t('Delete'));
- // We need raw here because %language and %langcode will add HTML.
- $t_args = array('%language' => $name, '%langcode' => $langcode);
- $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), t('The test language has been removed.'));
- // Reload to remove $name.
- $this->drupalGet($path);
- // Verify that language is no longer found.
- $this->assertResponse(404, t('Language no longer found.'));
- $this->drupalLogout();
-
- // Delete the string.
- $this->drupalLogin($translate_user);
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // Assume this is the only result, given the random name.
- $this->clickLink(t('delete'));
- $this->assertText(t('Are you sure you want to delete the string'), t('"delete" link is correct.'));
- // Delete the string.
- $path = 'admin/config/regional/translate/delete/' . $lid;
- $this->drupalGet($path);
- // First test the 'cancel' link.
- $this->clickLink(t('Cancel'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertRaw($name, t('The string was not deleted.'));
- // Delete the name string.
- $this->drupalPost('admin/config/regional/translate/delete/' . $lid, array(), t('Delete'));
- $this->assertText(t('The string has been removed.'), t('The string has been removed message.'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText($name, t('Search now can not find the name.'));
- }
-
- /*
- * Adds a language and checks that the JavaScript translation files are
- * properly created and rebuilt on deletion.
- */
- function testJavaScriptTranslation() {
- $user = $this->drupalCreateUser(array('translate interface', 'administer languages', 'access administration pages'));
- $this->drupalLogin($user);
-
- $langcode = 'xx';
- // The English name for the language. This will be translated.
- $name = $this->randomName(16);
-
- // Add custom language.
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
- drupal_static_reset('language_list');
-
- // Build the JavaScript translation file.
- $this->drupalGet('admin/config/regional/translate/translate');
-
- // Retrieve the id of the first string available in the {locales_source}
- // table and translate it.
- $query = db_select('locales_source', 'l');
- $query->addExpression('min(l.lid)', 'lid');
- $result = $query->condition('l.location', '%.js%', 'LIKE')->execute();
- $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid;
- $edit = array('translations['. $langcode .'][0]' => $this->randomName());
- $this->drupalPost($url, $edit, t('Save translations'));
-
- // Trigger JavaScript translation parsing and building.
- _locale_rebuild_js($langcode);
-
- $locale_javascripts = variable_get('locale_translation_javascript', array());
- $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/' . $langcode . '_' . $locale_javascripts[$langcode] . '.js';
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('not found'))));
-
- // Test JavaScript translation rebuilding.
- file_unmanaged_delete($js_file);
- $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
- cache_clear_all();
- _locale_rebuild_js($langcode);
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : t('not found'))));
- }
-
- /**
- * Tests the validation of the translation input.
- */
- function testStringValidation() {
- global $base_url;
-
- // User to add language and strings.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface'));
- $this->drupalLogin($admin_user);
- $langcode = 'xx';
- // The English name for the language. This will be translated.
- $name = $this->randomName(16);
- // This is the language indicator on the translation search screen for
- // untranslated strings.
- $language_indicator = "$langcode ";
- // These will be the invalid translations of $name.
- $key = $this->randomName(16);
- $bad_translations[$key] = "" . $key;
- $key = $this->randomName(16);
- $bad_translations[$key] = '
' . $key;
- $key = $this->randomName(16);
- $bad_translations[$key] = '<' . $key;
- $key = $this->randomName(16);
- $bad_translations[$key] ="" . $key;
-
- // Add custom language.
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
- // Add string.
- t($name, array(), array('langcode' => $langcode));
- // Reset locale cache.
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // Find the edit path.
- $content = $this->drupalGetContent();
- $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), t('Found the edit path.'));
- $path = $matches[0];
- foreach ($bad_translations as $key => $translation) {
- $edit = array(
- "translations[$langcode][0]" => $translation,
- );
- $this->drupalPost($path, $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'), t('The string was rejected as unsafe.'));
- $this->assertNoText(t('The string has been saved.'), t('The string was not saved.'));
- }
- }
-
- /**
- * Tests translation search form.
- */
- function testStringSearch() {
- global $base_url;
-
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
- // User to translate and delete string.
- $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
-
- // Code for the language.
- $langcode = 'xx';
- // The English name for the language. This will be translated.
- $name = $this->randomName(16);
- // This is the language indicator on the translation search screen for
- // untranslated strings.
- $language_indicator = "$langcode ";
- // This will be the translation of $name.
- $translation = $this->randomName(16);
-
- // Add custom language.
- $this->drupalLogin($admin_user);
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
- // Add string.
- t($name, array(), array('langcode' => $langcode));
- // Reset locale cache.
- locale_reset();
- $this->drupalLogout();
-
- // Search for the name.
- $this->drupalLogin($translate_user);
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // assertText() seems to remove the input field where $name always could be
- // found, so this is not a false assert. See how assertNoText succeeds
- // later.
- $this->assertText($name, t('Search found the string.'));
-
- // Ensure untranslated string doesn't appear if searching on 'only
- // translated strings'.
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the string."));
-
- // Ensure untranslated string appears if searching on 'only untranslated
- // strings'.
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'untranslated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the string.'));
-
- // Add translation.
- // Assume this is the only result, given the random name.
- $this->clickLink(t('edit'));
- // We save the lid from the path.
- $matches = array();
- preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches);
- $lid = $matches[1];
- $edit = array(
- "translations[$langcode][0]" => $translation,
- );
- $this->drupalPost(NULL, $edit, t('Save translations'));
-
- // Ensure translated string does appear if searching on 'only
- // translated strings'.
- $search = array(
- 'string' => $translation,
- 'language' => 'all',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
-
- // Ensure translated source string doesn't appear if searching on 'only
- // untranslated strings'.
- $search = array(
- 'string' => $name,
- 'language' => 'all',
- 'translation' => 'untranslated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the source string."));
-
- // Ensure translated string doesn't appear if searching on 'only
- // untranslated strings'.
- $search = array(
- 'string' => $translation,
- 'language' => 'all',
- 'translation' => 'untranslated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
-
- // Ensure translated string does appear if searching on the custom language.
- $search = array(
- 'string' => $translation,
- 'language' => $langcode,
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Search found the translation.'));
-
- // Ensure translated string doesn't appear if searching in System (English).
- $search = array(
- 'string' => $translation,
- 'language' => LANGUAGE_SYSTEM,
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the translation."));
-
- // Search for a string that isn't in the system.
- $unavailable_string = $this->randomName(16);
- $search = array(
- 'string' => $unavailable_string,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t("Search didn't find the invalid string."));
- }
-}
-
-/**
- * Tests plural format handling functionality.
- */
-class LocalePluralFormatTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Plural handling',
- 'description' => 'Tests plural handling for various languages.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp('locale');
-
- $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
- $this->drupalLogin($admin_user);
- }
-
- /**
- * Tests locale_get_plural() and format_plural() functionality.
- */
- function testGetPluralFormat() {
- // Import some .po files with formulas to set up the environment.
- // These will also add the languages to the system and enable them.
- $this->importPoFile($this->getPoFileWithSimplePlural(), array(
- 'langcode' => 'fr',
- ));
- $this->importPoFile($this->getPoFileWithComplexPlural(), array(
- 'langcode' => 'hr',
- ));
-
- // Attempt to import some broken .po files as well to prove that these
- // will not overwrite the proper plural formula imported above.
- $this->importPoFile($this->getPoFileWithMissingPlural(), array(
- 'langcode' => 'fr',
- 'overwrite_options[not_customized]' => TRUE,
- ));
- $this->importPoFile($this->getPoFileWithBrokenPlural(), array(
- 'langcode' => 'hr',
- 'overwrite_options[not_customized]' => TRUE,
- ));
-
- // Reset static caches from locale_get_plural() to ensure we get fresh data.
- drupal_static_reset('locale_get_plural');
- drupal_static_reset('locale_get_plural:plurals');
- drupal_static_reset('locale');
-
- // Expected plural translation strings for each plural index.
- $plural_strings = array(
- // English is not imported in this case, so we assume built-in text
- // and formulas.
- 'en' => array(
- 0 => '1 hour',
- 1 => '@count hours',
- ),
- 'fr' => array(
- 0 => '1 heure',
- 1 => '@count heures',
- ),
- 'hr' => array(
- 0 => '@count sat',
- 1 => '@count sata',
- 2 => '@count sati',
- ),
- // Hungarian is not imported, so it should assume the same text as
- // English, but it will always pick the plural form as per the built-in
- // logic, so only index -1 is relevant with the plural value.
- 'hu' => array(
- 0 => '1 hour',
- -1 => '@count hours',
- ),
- );
-
- // Expected plural indexes precomputed base on the plural formulas with
- // given $count value.
- $plural_tests = array(
- 'en' => array(
- 1 => 0,
- 0 => 1,
- 5 => 1,
- ),
- 'fr' => array(
- 1 => 0,
- 0 => 0,
- 5 => 1,
- ),
- 'hr' => array(
- 1 => 0,
- 21 => 0,
- 0 => 2,
- 2 => 1,
- 8 => 2,
- ),
- 'hu' => array(
- 1 => -1,
- 21 => -1,
- 0 => -1,
- ),
- );
-
- foreach ($plural_tests as $langcode => $tests) {
- foreach ($tests as $count => $expected_plural_index) {
- // Assert that the we get the right plural index.
- $this->assertIdentical(locale_get_plural($count, $langcode), $expected_plural_index, 'Computed plural index for ' . $langcode . ' for count ' . $count . ' is ' . $expected_plural_index);
- // Assert that the we get the right translation for that. Change the
- // expected index as per the logic for translation lookups.
- $expected_plural_index = ($count == 1) ? 0 : $expected_plural_index;
- $expected_plural_string = str_replace('@count', $count, $plural_strings[$langcode][$expected_plural_index]);
- $this->assertIdentical(format_plural($count, '1 hour', '@count hours', array(), array('langcode' => $langcode)), $expected_plural_string, 'Plural translation of 1 hours / @count hours for count ' . $count . ' in ' . $langcode . ' is ' . $expected_plural_string);
- }
- }
- }
-
- /**
- * Tests plural editing and export functionality.
- */
- function testPluralEditExport() {
- // Import some .po files with formulas to set up the environment.
- // These will also add the languages to the system and enable them.
- $this->importPoFile($this->getPoFileWithSimplePlural(), array(
- 'langcode' => 'fr',
- ));
- $this->importPoFile($this->getPoFileWithComplexPlural(), array(
- 'langcode' => 'hr',
- ));
-
- // Get the French translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'fr',
- ), t('Export'));
- // Ensure we have a translation file.
- $this->assertRaw('# French translation of Drupal', t('Exported French translation file.'));
- // Ensure our imported translations exist in the file.
- $this->assertRaw("msgid \"Monday\"\nmsgstr \"lundi\"", t('French translations present in exported file.'));
- // Check for plural export specifically.
- $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"1 heure\"\nmsgstr[1] \"@count heures\"", t('Plural translations exported properly.'));
-
- // Get the Croatian translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'hr',
- ), t('Export'));
- // Ensure we have a translation file.
- $this->assertRaw('# Croatian translation of Drupal', t('Exported Croatian translation file.'));
- // Ensure our imported translations exist in the file.
- $this->assertRaw("msgid \"Monday\"\nmsgstr \"Ponedjeljak\"", t('Croatian translations present in exported file.'));
- // Check for plural export specifically.
- $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"@count sat\"\nmsgstr[1] \"@count sata\"\nmsgstr[2] \"@count sati\"", t('Plural translations exported properly.'));
-
- // Check if the source appears on the translation page.
- $this->drupalGet('admin/config/regional/translate');
- $this->assertText("1 hour, @count hours");
-
- // Look up editing page for this plural string and check fields.
- $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 hour" . LOCALE_PLURAL_DELIMITER . "@count hours"))->fetchField();
- $path = 'admin/config/regional/translate/edit/' . $lid;
- $this->drupalGet($path);
- // Labels for plural editing elements.
- $this->assertText('Singular form');
- $this->assertText('First plural form');
- $this->assertText('2. plural form');
- // Plural values for both languages.
- $this->assertFieldById('edit-translations-hr-0', '@count sat');
- $this->assertFieldById('edit-translations-hr-1', '@count sata');
- $this->assertFieldById('edit-translations-hr-2', '@count sati');
- $this->assertNoFieldById('edit-translations-hr-3');
- $this->assertFieldById('edit-translations-fr-0', '1 heure');
- $this->assertFieldById('edit-translations-fr-1', '@count heures');
- $this->assertNoFieldById('edit-translations-fr-2');
-
- // Edit some translations and see if that took effect.
- $edit = array(
- 'translations[fr][0]' => '1 heure edited',
- 'translations[hr][1]' => '@count sata edited',
- );
- $this->drupalPost($path, $edit, t('Save translations'));
-
- // Inject a plural source string to the database. We need to use a specific
- // langcode here because the language will be English by default and will
- // not save our source string for performance optimization if we do not ask
- // specifically for a language.
- format_plural(1, '1 day', '@count days', array(), array('langcode' => 'fr'));
- // Look up editing page for this plural string and check fields.
- $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = ''", array(':source' => "1 day" . LOCALE_PLURAL_DELIMITER . "@count days"))->fetchField();
- $path = 'admin/config/regional/translate/edit/' . $lid;
-
- // Save complete translations for the string in both languages.
- $edit = array(
- 'translations[fr][0]' => '1 jour',
- 'translations[fr][1]' => '@count jours',
- 'translations[hr][0]' => '@count dan',
- 'translations[hr][1]' => '@count dana',
- 'translations[hr][2]' => '@count dana',
- );
- $this->drupalPost($path, $edit, t('Save translations'));
-
- // Get the French translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'fr',
- ), t('Export'));
- // Check for plural export specifically.
- $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"1 heure edited\"\nmsgstr[1] \"@count heures\"", t('Edited French plural translations for hours exported properly.'));
- $this->assertRaw("msgid \"1 day\"\nmsgid_plural \"@count days\"\nmsgstr[0] \"1 jour\"\nmsgstr[1] \"@count jours\"", t('Added French plural translations for days exported properly.'));
-
- // Get the Croatian translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'hr',
- ), t('Export'));
- // Check for plural export specifically.
- $this->assertRaw("msgid \"1 hour\"\nmsgid_plural \"@count hours\"\nmsgstr[0] \"@count sat\"\nmsgstr[1] \"@count sata edited\"\nmsgstr[2] \"@count sati\"", t('Edited Croatian plural translations exported properly.'));
- $this->assertRaw("msgid \"1 day\"\nmsgid_plural \"@count days\"\nmsgstr[0] \"@count dan\"\nmsgstr[1] \"@count dana\"\nmsgstr[2] \"@count dana\"", t('Added Croatian plural translations exported properly.'));
- }
-
-
- /**
- * Imports a standalone .po file in a given language.
- *
- * @param $contents
- * Contents of the .po file to import.
- * @param $options
- * Additional options to pass to the translation import form.
- */
- function importPoFile($contents, array $options = array()) {
- $name = tempnam('temporary://', "po_") . '.po';
- file_put_contents($name, $contents);
- $options['files[file]'] = $name;
- $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
- drupal_unlink($name);
- }
-
- /**
- * Returns a .po file with a simple plural formula.
- */
- function getPoFileWithSimplePlural() {
- return <<< EOF
-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 "1 hour"
-msgid_plural "@count hours"
-msgstr[0] "1 heure"
-msgstr[1] "@count heures"
-
-msgid "Monday"
-msgstr "lundi"
-EOF;
- }
-
- /**
- * Returns a .po file with a complex plural formula.
- */
- function getPoFileWithComplexPlural() {
- return <<< EOF
-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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
-
-msgid "1 hour"
-msgid_plural "@count hours"
-msgstr[0] "@count sat"
-msgstr[1] "@count sata"
-msgstr[2] "@count sati"
-
-msgid "Monday"
-msgstr "Ponedjeljak"
-EOF;
- }
-
- /**
- * Returns a .po file with a missing plural formula.
- */
- function getPoFileWithMissingPlural() {
- return <<< EOF
-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"
-
-msgid "Monday"
-msgstr "lundi"
-EOF;
- }
-
- /**
- * Returns a .po file with a broken plural formula.
- */
- function getPoFileWithBrokenPlural() {
- return <<< EOF
-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: broken, will not parse\\n"
-
-msgid "Monday"
-msgstr "Ponedjeljak"
-EOF;
- }
-}
-
-/**
- * Functional tests for the import of translation files.
- */
-class LocaleImportFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Translation import',
- 'description' => 'Tests the import of locale files.',
- 'group' => 'Locale',
- );
- }
-
- /**
- * A user able to create languages and import translations.
- */
- protected $admin_user = NULL;
-
- function setUp() {
- parent::setUp(array('locale', 'dblog'));
-
- // Set the translation file directory.
- variable_set('locale_translate_file_directory', drupal_get_path('module', 'locale') . '/tests');
-
- $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
- $this->drupalLogin($this->admin_user);
- }
-
- /**
- * Test import of standalone .po files.
- */
- function testStandalonePoFile() {
- // Try importing a .po file.
- $this->importPoFile($this->getPoFile(), array(
- 'langcode' => 'fr',
- ));
-
- // The import should automatically create the corresponding language.
- $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), t('The language has been automatically created.'));
-
- // The import should have created 8 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 8, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
-
- // This import should have saved plural forms to have 2 variants.
- $locale_plurals = variable_get('locale_translation_plurals', array());
- $this->assert($locale_plurals['fr']['plurals'] == 2, t('Plural number initialized.'));
-
- // Ensure we were redirected correctly.
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
-
-
- // Try importing a .po file with invalid tags.
- $this->importPoFile($this->getBadPoFile(), array(
- 'langcode' => 'fr',
- ));
-
- // The import should have created 1 string and rejected 2.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
- $skip_message = format_plural(2, 'A 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.', array('@url' => url('admin/reports/dblog')));
- $this->assertRaw($skip_message, t('Unsafe strings were skipped.'));
-
-
- // Try importing a .po file which doesn't exist.
- $name = $this->randomName(16);
- $this->drupalPost('admin/config/regional/translate/import', array(
- 'langcode' => 'fr',
- 'files[file]' => $name,
- ), t('Import'));
- $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), t('Correct page redirection.'));
- $this->assertText(t('File to import not found.'), t('File to import not found message.'));
-
-
- // Try importing a .po file with overriding strings, and ensure existing
- // strings are kept.
- $this->importPoFile($this->getOverwritePoFile(), array(
- 'langcode' => 'fr',
- ));
-
- // The import should have created 1 string.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
- // Ensure string wasn't overwritten.
- $search = array(
- 'string' => 'Montag',
- 'language' => 'fr',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t('String not overwritten by imported string.'));
-
- // This import should not have changed number of plural forms.
- $locale_plurals = variable_get('locale_translation_plurals', array());
- $this->assert($locale_plurals['fr']['plurals'] == 2, t('Plural numbers untouched.'));
-
- // Try importing a .po file with overriding strings, and ensure existing
- // strings are overwritten.
- $this->importPoFile($this->getOverwritePoFile(), array(
- 'langcode' => 'fr',
- 'overwrite_options[not_customized]' => TRUE,
- ));
-
- // The import should have updated 2 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), t('The translation file was successfully imported.'));
- // Ensure string was overwritten.
- $search = array(
- 'string' => 'Montag',
- 'language' => 'fr',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('String overwritten by imported string.'));
- // This import should have changed number of plural forms.
- $locale_plurals = variable_get('locale_translation_plurals', array());
- $this->assert($locale_plurals['fr']['plurals'] == 3, t('Plural numbers changed.'));
-
- // Importing a .po file and mark its strings as customized strings.
- $this->importPoFile($this->getCustomPoFile(), array(
- 'langcode' => 'fr',
- 'customized' => TRUE,
- ));
-
- // The import should have created 6 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 6, '%update' => 0, '%delete' => 0)), t('The customized translation file was successfully imported.'));
-
- // The database should now contain 6 customized strings (two imported
- // strings are not translated).
- $count = db_query('SELECT lid FROM {locales_target} WHERE customized = :custom', array(':custom' => 1))->rowCount();
- $this->assertEqual($count, 6, t('Customized translations succesfully imported.'));
-
- // Try importing a .po file with overriding strings, and ensure existing
- // customized strings are kept.
- $this->importPoFile($this->getCustomOverwritePoFile(), array(
- 'langcode' => 'fr',
- 'overwrite_options[not_customized]' => TRUE,
- 'overwrite_options[customized]' => FALSE,
- ));
-
- // The import should have created 1 string.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The customized translation file was successfully imported.'));
- // Ensure string wasn't overwritten.
- $search = array(
- 'string' => 'januari',
- 'language' => 'fr',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertText(t('No strings available.'), t('Customized string not overwritten by imported string.'));
-
- // Try importing a .po file with overriding strings, and ensure existing
- // customized strings are overwritten.
- $this->importPoFile($this->getCustomOverwritePoFile(), array(
- 'langcode' => 'fr',
- 'overwrite_options[not_customized]' => FALSE,
- 'overwrite_options[customized]' => TRUE,
- ));
-
- // The import should have updated 2 strings.
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), t('The customized translation file was successfully imported.'));
- // Ensure string was overwritten.
- $search = array(
- 'string' => 'januari',
- 'language' => 'fr',
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('Customized string overwritten by imported string.'));
-
- }
-
- /**
- * Test automatic import of a module's translation files.
- */
- function testAutomaticModuleTranslationImportLanguageEnable() {
- // Code for the language - manually set to match the test translation file.
- $langcode = 'xx';
- // The English name for the language.
- $name = $this->randomName(16);
-
- // Create a custom language.
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
-
- // Ensure the translation file was automatically imported when language was
- // added.
- $this->assertText(t('One translation file imported.'), t('Language file automatically imported.'));
-
- // Ensure strings were successfully imported.
- $search = array(
- 'string' => 'lundi',
- 'language' => $langcode,
- 'translation' => 'translated',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- $this->assertNoText(t('No strings available.'), t('String successfully imported.'));
- }
-
- /**
- * Test msgctxt context support.
- */
- function testLanguageContext() {
- // Try importing a .po file.
- $this->importPoFile($this->getPoFileWithContext(), array(
- 'langcode' => 'hr',
- ));
-
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', t('Long month name context is working.'));
- $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', t('Default context is working.'));
- }
-
- /**
- * Test empty msgstr at end of .po file see #611786.
- */
- function testEmptyMsgstr() {
- $langcode = 'hu';
-
- // Try importing a .po file.
- $this->importPoFile($this->getPoFileWithMsgstr(), array(
- 'langcode' => $langcode,
- ));
-
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
- $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', t('String imported and translated.'));
-
- // Try importing a .po file.
- $this->importPoFile($this->getPoFileWithEmptyMsgstr(), array(
- 'langcode' => $langcode,
- 'overwrite_options[not_customized]' => TRUE,
- ));
- $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), t('The translation file was successfully imported.'));
- // This is the language indicator on the translation search screen for
- // untranslated strings.
- $language_indicator = "$langcode ";
- $str = "Operations";
- $search = array(
- 'string' => $str,
- 'language' => 'all',
- 'translation' => 'all',
- );
- $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
- // assertText() seems to remove the input field where $str always could be
- // found, so this is not a false assert.
- $this->assertText($str, t('Search found the string.'));
- $this->assertRaw($language_indicator, t('String is untranslated again.'));
- }
-
- /**
- * Helper function: import a standalone .po file in a given language.
- *
- * @param $contents
- * Contents of the .po file to import.
- * @param $options
- * Additional options to pass to the translation import form.
- */
- function importPoFile($contents, array $options = array()) {
- $name = tempnam('temporary://', "po_") . '.po';
- file_put_contents($name, $contents);
- $options['files[file]'] = $name;
- $this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
- drupal_unlink($name);
- }
-
- /**
- * Helper function that returns a proper .po file.
- */
- function getPoFile() {
- return <<< EOF
-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 "One sheep"
-msgid_plural "@count sheep"
-msgstr[0] "un mouton"
-msgstr[1] "@count moutons"
-
-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"
-EOF;
- }
-
- /**
- * Helper function that returns a bad .po file.
- */
- function getBadPoFile() {
- return <<< EOF
-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 "Save configuration"
-msgstr "Enregistrer la configuration"
-
-msgid "edit"
-msgstr "modifier
"
-
-msgid "delete"
-msgstr "supprimer"
-
-EOF;
- }
-
- /**
- * Helper function that returns a proper .po file for testing.
- */
- function getOverwritePoFile() {
- return <<< EOF
-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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
-
-msgid "Monday"
-msgstr "Montag"
-
-msgid "Day"
-msgstr "Jour"
-EOF;
- }
-
- /**
- * Helper function that returns a .po file which strings will be marked
- * as customized.
- */
- function getCustomPoFile() {
- return <<< EOF
-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 "One dog"
-msgid_plural "@count dogs"
-msgstr[0] "un chien"
-msgstr[1] "@count chiens"
-
-msgid "January"
-msgstr "janvier"
-
-msgid "February"
-msgstr "février"
-
-msgid "March"
-msgstr "mars"
-
-msgid "April"
-msgstr "avril"
-
-msgid "June"
-msgstr "juin"
-EOF;
- }
-
- /**
- * Helper function that returns a .po file for testing customized strings.
- */
- function getCustomOverwritePoFile() {
- return <<< EOF
-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 "January"
-msgstr "januari"
-
-msgid "February"
-msgstr "februari"
-
-msgid "July"
-msgstr "juillet"
-EOF;
- }
-
- /**
- * Helper function that returns a .po file with context.
- */
- function getPoFileWithContext() {
- // Croatian (code hr) is one the the languages that have a different
- // form for the full name and the abbreviated name for the month May.
- return <<< EOF
-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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n"
-
-msgctxt "Long month name"
-msgid "May"
-msgstr "Svibanj"
-
-msgid "May"
-msgstr "Svi."
-EOF;
- }
-
- /**
- * Helper function that returns a .po file with an empty last item.
- */
- function getPoFileWithEmptyMsgstr() {
- return <<< EOF
-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 "Operations"
-msgstr ""
-
-EOF;
- }
- /**
- * Helper function that returns a .po file with an empty last item.
- */
- function getPoFileWithMsgstr() {
- return <<< EOF
-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 "Operations"
-msgstr "Műveletek"
-
-msgid "Will not appear in Drupal core, so we can ensure the test passes"
-msgstr ""
-
-EOF;
- }
-
-}
-
-/**
- * Functional tests for the export of translation files.
- */
-class LocaleExportFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Translation export',
- 'description' => 'Tests the exportation of locale files.',
- 'group' => 'Locale',
- );
- }
-
- /**
- * A user able to create languages and export translations.
- */
- protected $admin_user = NULL;
-
- function setUp() {
- parent::setUp('locale');
-
- $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
- $this->drupalLogin($this->admin_user);
- }
-
- /**
- * Test exportation of translations.
- */
- function testExportTranslation() {
- // First import some known translations.
- // This will also automatically enable the 'fr' language.
- $name = tempnam('temporary://', "po_") . '.po';
- file_put_contents($name, $this->getPoFile());
- $this->drupalPost('admin/config/regional/translate/import', array(
- 'langcode' => 'fr',
- 'files[file]' => $name,
- ), t('Import'));
- drupal_unlink($name);
-
- // Get the French translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'fr',
- ), t('Export'));
-
- // Ensure we have a translation file.
- $this->assertRaw('# French translation of Drupal', t('Exported French translation file.'));
- // Ensure our imported translations exist in the file.
- $this->assertRaw('msgstr "lundi"', t('French translations present in exported file.'));
-
- // Import some more French translations which will be marked as customized.
- $name = tempnam('temporary://', "po2_") . '.po';
- file_put_contents($name, $this->getCustomPoFile());
- $this->drupalPost('admin/config/regional/translate/import', array(
- 'langcode' => 'fr',
- 'files[file]' => $name,
- 'customized' => 1,
- ), t('Import'));
- drupal_unlink($name);
-
- // We can't import a string with an empty translation, but calling
- // locale() for an new string creates an entry in the locales_source table.
- locale('February', NULL, 'fr');
-
- // Export only customized French translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'fr',
- 'content_options[not_customized]' => FALSE,
- 'content_options[customized]' => TRUE,
- 'content_options[not_translated]' => FALSE,
- ), t('Export'));
-
- // Ensure we have a translation file.
- $this->assertRaw('# French translation of Drupal', t('Exported French translation file with only customized strings.'));
- // Ensure the customized translations exist in the file.
- $this->assertRaw('msgstr "janvier"', t('French custom translation present in exported file.'));
- // Ensure no untranslated strings exist in the file.
- $this->assertNoRaw('msgid "February"', t('Untranslated string not present in exported file.'));
-
- // Export only untranslated French translations.
- $this->drupalPost('admin/config/regional/translate/export', array(
- 'langcode' => 'fr',
- 'content_options[not_customized]' => FALSE,
- 'content_options[customized]' => FALSE,
- 'content_options[not_translated]' => TRUE,
- ), t('Export'));
-
- // Ensure we have a translation file.
- $this->assertRaw('# French translation of Drupal', t('Exported French translation file with only untranslated strings.'));
- // Ensure no customized translations exist in the file.
- $this->assertNoRaw('msgstr "janvier"', t('French custom translation not present in exported file.'));
- // Ensure the untranslated strings exist in the file.
- $this->assertRaw('msgid "February"', t('Untranslated string present in exported file.'));
- }
-
- /**
- * Test exportation of translation template file.
- */
- function testExportTranslationTemplateFile() {
- // Get the translation template file.
- $this->drupalPost('admin/config/regional/translate/export', array(), t('Export'));
- // Ensure we have a translation file.
- $this->assertRaw('# LANGUAGE translation of PROJECT', t('Exported translation template file.'));
- }
-
- /**
- * Helper function that returns a proper .po file.
- */
- function getPoFile() {
- return <<< EOF
-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 "Monday"
-msgstr "lundi"
-EOF;
- }
-
- /**
- * Helper function that returns a .po file which strings will be marked
- * as customized.
- */
- function getCustomPoFile() {
- return <<< EOF
-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 "January"
-msgstr "janvier"
-EOF;
- }
-
-}
-
-/**
- * Tests for the st() function.
- */
-class LocaleInstallTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'String translation using st()',
- 'description' => 'Tests that st() works like t().',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp('locale');
-
- // st() lives in install.inc, so ensure that it is loaded for all tests.
- require_once DRUPAL_ROOT . '/core/includes/install.inc';
- }
-
- /**
- * Verify that function signatures of t() and st() are equal.
- */
- function testFunctionSignatures() {
- $reflector_t = new ReflectionFunction('t');
- $reflector_st = new ReflectionFunction('st');
- $this->assertEqual($reflector_t->getParameters(), $reflector_st->getParameters(), t('Function signatures of t() and st() are equal.'));
- }
-}
-
-/**
- * Locale uninstall with English UI functional test.
- */
-class LocaleUninstallFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Locale uninstall (EN)',
- 'description' => 'Tests the uninstall process using the built-in UI language.',
- 'group' => 'Locale',
- );
- }
-
- /**
- * The default language set for the UI before uninstall.
- */
- protected $language;
-
- function setUp() {
- parent::setUp(array('node', 'locale'));
- $this->langcode = 'en';
-
- // Create Article node type.
- $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
- }
-
- /**
- * Check if the values of the Locale variables are correct after uninstall.
- */
- function testUninstallProcess() {
- $locale_module = array('locale', 'language');
-
- $language = (object) array(
- 'langcode' => 'fr',
- 'name' => 'French',
- 'default' => $this->langcode == 'fr',
- );
- language_save($language);
-
- // Check the UI language.
- drupal_language_initialize();
- $this->assertEqual(drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode, $this->langcode, t('Current language: %lang', array('%lang' => drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode)));
-
- // Enable multilingual workflow option for articles.
- variable_set('node_type_language_article', 1);
-
- // Change JavaScript translations directory.
- variable_set('locale_js_directory', 'js_translations');
-
- // Build the JavaScript translation file for French.
- $user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
- $this->drupalLogin($user);
- $this->drupalGet('admin/config/regional/translate/translate');
- $string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location', array(
- ':location' => '%.js%',
- ))->fetchObject();
- $edit = array('translations[fr][0]' => 'french translation');
- $this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations'));
- _locale_rebuild_js('fr');
- $locale_javascripts = variable_get('locale_translation_javascript', array());
- $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $locale_javascripts['fr'] . '.js';
- $this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('none'))));
-
- // Disable string caching.
- variable_set('locale_cache_strings', 0);
-
- // Change language negotiation options.
- drupal_load('module', 'locale');
- variable_set('language_types', language_types_get_default() + array('language_custom' => TRUE));
- variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, language_language_negotiation_info());
- variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, language_language_negotiation_info());
- variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, language_language_negotiation_info());
-
- // Change language negotiation settings.
- variable_set('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX);
- variable_set('language_negotiation_session_param', TRUE);
-
- // Uninstall Locale.
- module_disable($locale_module);
- drupal_uninstall_modules($locale_module);
-
- // Visit the front page.
- $this->drupalGet('');
-
- // Check the init language logic.
- drupal_language_initialize();
- $this->assertEqual(drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode, 'en', t('Language after uninstall: %lang', array('%lang' => drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode)));
-
- // Check JavaScript files deletion.
- $this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
-
- // Check language count.
- $language_count = variable_get('language_count', 1);
- $this->assertEqual($language_count, 1, t('Language count: %count', array('%count' => $language_count)));
-
- // Check language negotiation.
- require_once DRUPAL_ROOT . '/core/includes/language.inc';
- $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), t('Language types reset'));
- $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('Interface language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
- $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('Content language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
- $language_negotiation = language_negotiation_method_get_first(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT;
- $this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
-
- // Check language negotiation method settings.
- $this->assertFalse(variable_get('language_negotiation_url_part', FALSE), t('URL language negotiation method indicator settings cleared.'));
- $this->assertFalse(variable_get('language_negotiation_session_param', FALSE), t('Visit language negotiation method settings cleared.'));
-
- // Check JavaScript parsed.
- $javascript_parsed_count = count(variable_get('javascript_parsed', array()));
- $this->assertEqual($javascript_parsed_count, 0, t('JavaScript parsed count: %count', array('%count' => $javascript_parsed_count)));
-
- // Check JavaScript translations directory.
- $locale_js_directory = variable_get('locale_js_directory', 'languages');
- $this->assertEqual($locale_js_directory, 'languages', t('JavaScript translations directory: %dir', array('%dir' => $locale_js_directory)));
-
- // Check string caching.
- $locale_cache_strings = variable_get('locale_cache_strings', 1);
- $this->assertEqual($locale_cache_strings, 1, t('String caching: %status', array('%status' => t($locale_cache_strings ? 'enabled': 'disabled'))));
- }
-}
-
-/**
- * Locale uninstall with French UI functional test.
- *
- * Because this class extends LocaleUninstallFunctionalTest, it doesn't require a new
- * test of its own. Rather, it switches the default UI language in setUp and then
- * runs the testUninstallProcess (which it inherits from LocaleUninstallFunctionalTest)
- * to test with this new language.
- */
-class LocaleUninstallFrenchFunctionalTest extends LocaleUninstallFunctionalTest {
- public static function getInfo() {
- return array(
- 'name' => 'Locale uninstall (FR)',
- 'description' => 'Tests the uninstall process using French as interface language.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp();
- $this->langcode = 'fr';
- }
-}
-
-/**
- * Functional tests for configuring a different path alias per language.
- */
-class LocalePathFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Path language settings',
- 'description' => 'Checks you can configure a language for individual url aliases.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp(array('node', 'locale', 'path'));
-
- $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
- variable_set('site_frontpage', 'node');
- }
-
- /**
- * Test if a language can be associated with a path alias.
- */
- function testPathLanguageConfiguration() {
- global $base_url;
-
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'create page content', 'administer url aliases', 'create url aliases', 'access administration pages'));
-
- // Add custom language.
- $this->drupalLogin($admin_user);
- // Code for the language.
- $langcode = 'xx';
- // The English name for the language.
- $name = $this->randomName(16);
- // The domain prefix.
- $prefix = $langcode;
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
-
- // Set path prefix.
- $edit = array( "prefix[$langcode]" => $prefix );
- $this->drupalPost('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'), t('The "xx" front page is readibly available.'));
-
- // Create a node.
- $node = $this->drupalCreateNode(array('type' => 'page'));
-
- // Create a path alias in default language (English).
- $path = 'admin/config/search/path/add';
- $english_path = $this->randomName(8);
- $edit = array(
- 'source' => 'node/' . $node->nid,
- 'alias' => $english_path,
- 'langcode' => 'en',
- );
- $this->drupalPost($path, $edit, t('Save'));
-
- // Create a path alias in new custom language.
- $custom_language_path = $this->randomName(8);
- $edit = array(
- 'source' => 'node/' . $node->nid,
- 'alias' => $custom_language_path,
- 'langcode' => $langcode,
- );
- $this->drupalPost($path, $edit, t('Save'));
-
- // Confirm English language path alias works.
- $this->drupalGet($english_path);
- $this->assertText($node->title, t('English alias works.'));
-
- // Confirm custom language path alias works.
- $this->drupalGet($prefix . '/' . $custom_language_path);
- $this->assertText($node->title, t('Custom language alias works.'));
-
- // Create a custom path.
- $custom_path = $this->randomName(8);
-
- // Check priority of language for alias by source path.
- $edit = array(
- 'source' => 'node/' . $node->nid,
- 'alias' => $custom_path,
- 'langcode' => LANGUAGE_NOT_SPECIFIED,
- );
- path_save($edit);
- $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, 'en');
- $this->assertEqual($english_path, $lookup_path, t('English language alias has priority.'));
- // Same check for language 'xx'.
- $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, $prefix);
- $this->assertEqual($custom_language_path, $lookup_path, t('Custom language alias has priority.'));
- path_delete($edit);
-
- // Create language nodes to check priority of aliases.
- $first_node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
- $second_node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
-
- // Assign a custom path alias to the first node with the English language.
- $edit = array(
- 'source' => 'node/' . $first_node->nid,
- 'alias' => $custom_path,
- 'langcode' => 'en',
- );
- path_save($edit);
-
- // Assign a custom path alias to second node with LANGUAGE_NOT_SPECIFIED.
- $edit = array(
- 'source' => 'node/' . $second_node->nid,
- 'alias' => $custom_path,
- 'langcode' => LANGUAGE_NOT_SPECIFIED,
- );
- path_save($edit);
-
- // Test that both node titles link to our path alias.
- $this->drupalGet('');
- $custom_path_url = base_path() . $GLOBALS['script_path'] . $custom_path;
- $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $first_node->title));
- $this->assertTrue(!empty($elements), t('First node links to the path alias.'));
- $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $second_node->title));
- $this->assertTrue(!empty($elements), t('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->title, t('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->title, t('Custom alias with prefix returns second node.'));
-
- }
-}
-
-/**
- * Functional tests for multilingual support on nodes.
- */
-class LocaleContentFunctionalTest extends WebTestBase {
- protected $profile = 'standard';
-
- public static function getInfo() {
- return array(
- 'name' => 'Content language settings',
- 'description' => 'Checks you can enable multilingual support on content types and configure a language for a node.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp('locale');
- }
-
- /**
- * Verifies that machine name fields are always LTR.
- */
- function testMachineNameLTR() {
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
-
- // 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 = array();
- $edit['predefined_langcode'] = 'ar';
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-
- $edit = array();
- $edit['site_default'] = 'ar';
- $this->drupalPost(NULL, $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.
- */
- function testContentTypeLanguageConfiguration() {
- global $base_url;
-
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
- // User to create a node.
- $web_user = $this->drupalCreateUser(array('create article content', 'create page content', 'edit any page content'));
-
- // Add custom language.
- $this->drupalLogin($admin_user);
- // Code for the language.
- $langcode = 'xx';
- // The English name for the language.
- $name = $this->randomName(16);
- $edit = array(
- 'predefined_langcode' => 'custom',
- 'langcode' => $langcode,
- 'name' => $name,
- 'direction' => '0',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
-
- // Set "Basic page" content type to use multilingual support.
- $this->drupalGet('admin/structure/types/manage/page');
- $this->assertText(t('Multilingual support'), t('Multilingual support fieldset present on content type configuration form.'));
- $edit = array(
- 'node_type_language' => 1,
- );
- $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
- $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
- $this->drupalLogout();
-
- // Verify language selection is not present on add article form.
- $this->drupalLogin($web_user);
- $this->drupalGet('node/add/article');
- // Verify language select list is not present.
- $this->assertNoFieldByName('language', NULL, t('Language select not present on add article form.'));
-
- // Verify language selection appears on add "Basic page" form.
- $this->drupalGet('node/add/page');
- // Verify language select list is present.
- $this->assertFieldByName('langcode', NULL, t('Language select present on add Basic page form.'));
- // Ensure language appears.
- $this->assertText($name, t('Language present.'));
-
- // Create "Basic page" content.
- $node_title = $this->randomName();
- $node_body = $this->randomName();
- $edit = array(
- 'type' => 'page',
- 'title' => $node_title,
- 'body' => array($langcode => array(array('value' => $node_body))),
- 'langcode' => $langcode,
- );
- $node = $this->drupalCreateNode($edit);
- // Edit the content and ensure correct language is selected.
- $path = 'node/' . $node->nid . '/edit';
- $this->drupalGet($path);
- $this->assertRaw('', t('Correct language selected.'));
- // Ensure we can change the node language.
- $edit = array(
- 'langcode' => 'en',
- );
- $this->drupalPost($path, $edit, t('Save'));
- $this->assertRaw(t('%title has been updated.', array('%title' => $node_title)), t('Basic page content updated.'));
-
- $this->drupalLogout();
- }
-
- /**
- * Test if a dir and lang tags exist in node's attributes.
- */
- function testContentTypeDirLang() {
- // User to add and remove language.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages'));
- // User to create a node.
- $web_user = $this->drupalCreateUser(array('create article content', 'edit own article content'));
-
- // Login as admin.
- $this->drupalLogin($admin_user);
-
- // Install Arabic language.
- $edit = array();
- $edit['predefined_langcode'] = 'ar';
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-
- // Install Spanish language.
- $edit = array();
- $edit['predefined_langcode'] = 'es';
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-
- // Set "Article" content type to use multilingual support.
- $this->drupalGet('admin/structure/types/manage/article');
- $edit = array(
- 'node_type_language' => 1,
- );
- $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
- $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), t('Article content type has been updated.'));
- $this->drupalLogout();
-
- // Login as web user to add new article.
- $this->drupalLogin($web_user);
-
- // Create three nodes: English, Arabic and Spanish.
- $node_en = $this->createNodeArticle('en');
- $node_ar = $this->createNodeArticle('ar');
- $node_es = $this->createNodeArticle('es');
-
- $this->drupalGet('node');
-
- // Check if English node does not have lang tag.
- $pattern = '|id="node-' . $node_en->nid . '"[^<>]*lang="en"|';
- $this->assertNoPattern($pattern, t('The lang tag has not been assigned to the English node.'));
-
- // Check if English node does not have dir tag.
- $pattern = '|id="node-' . $node_en->nid . '"[^<>]*dir="ltr"|';
- $this->assertNoPattern($pattern, t('The dir tag has not been assigned to the English node.'));
-
- // Check if Arabic node has lang="ar" & dir="rtl" tags.
- $pattern = '|id="node-' . $node_ar->nid . '"[^<>]*lang="ar" dir="rtl"|';
- $this->assertPattern($pattern, t('The lang and dir tags have been assigned correctly to the Arabic node.'));
-
- // Check if Spanish node has lang="es" tag.
- $pattern = '|id="node-' . $node_es->nid . '"[^<>]*lang="es"|';
- $this->assertPattern($pattern, t('The lang tag has been assigned correctly to the Spanish node.'));
-
- // Check if Spanish node does not have dir="ltr" tag.
- $pattern = '|id="node-' . $node_es->nid . '"[^<>]*lang="es" dir="ltr"|';
- $this->assertNoPattern($pattern, t('The dir tag has not been assigned to the Spanish node.'));
-
- $this->drupalLogout();
- }
-
- /**
- * Create node in a specific language.
- */
- protected function createNodeArticle($langcode) {
- $this->drupalGet('node/add/article');
- $node_title = $this->randomName();
- $node_body = $this->randomName();
- $edit = array(
- 'type' => 'article',
- 'title' => $node_title,
- 'body' => array($langcode => array(array('value' => $node_body))),
- 'langcode' => $langcode,
- 'promote' => 1,
- );
- return $this->drupalCreateNode($edit);
- }
-}
-
-/**
- * Functional test for multilingual fields.
- */
-class LocaleMultilingualFieldsFunctionalTest extends WebTestBase {
- public static function getInfo() {
- return array(
- 'name' => 'Multilingual fields',
- 'description' => 'Test multilingual support for fields.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp(array('node', 'locale'));
-
- // Create Basic page node type.
- $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
-
- // Setup users.
- $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content'));
- $this->drupalLogin($admin_user);
-
- // Add a new language.
- $language = (object) array(
- 'langcode' => 'it',
- 'name' => 'Italian',
- );
- language_save($language);
-
- // Enable URL language detection and selection.
- $edit = array('language_interface[enabled][language-url]' => '1');
- $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
-
- // Set "Basic page" content type to use multilingual support.
- $edit = array(
- 'node_type_language' => 1,
- );
- $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
- $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
-
- // Make node body translatable.
- $field = field_info_field('body');
- $field['translatable'] = TRUE;
- field_update_field($field);
- }
-
- /**
- * Test if field languages are correctly set through the node form.
- */
- function testMultilingualNodeForm() {
- // Create "Basic page" content.
- $langcode = LANGUAGE_NOT_SPECIFIED;
- $title_key = "title";
- $title_value = $this->randomName(8);
- $body_key = "body[$langcode][0][value]";
- $body_value = $this->randomName(16);
-
- // Create node to edit.
- $edit = array();
- $edit[$title_key] = $title_value;
- $edit[$body_key] = $body_value;
- $edit['langcode'] = 'en';
- $this->drupalPost('node/add/page', $edit, t('Save'));
-
- // Check that the node exists in the database.
- $node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
-
- $assert = isset($node->body['en']) && !isset($node->body[LANGUAGE_NOT_SPECIFIED]) && $node->body['en'][0]['value'] == $body_value;
- $this->assertTrue($assert, t('Field language correctly set.'));
-
- // Change node language.
- $this->drupalGet("node/$node->nid/edit");
- $edit = array(
- $title_key => $this->randomName(8),
- 'langcode' => 'it'
- );
- $this->drupalPost(NULL, $edit, t('Save'));
- $node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
-
- $assert = isset($node->body['it']) && !isset($node->body['en']) && $node->body['it'][0]['value'] == $body_value;
- $this->assertTrue($assert, t('Field language correctly changed.'));
-
- // Enable content language URL detection.
- language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0));
-
- // Test multilingual field language fallback logic.
- $this->drupalGet("it/node/$node->nid");
- $this->assertRaw($body_value, t('Body correctly displayed using Italian as requested language'));
-
- $this->drupalGet("node/$node->nid");
- $this->assertRaw($body_value, t('Body correctly displayed using English as requested language'));
- }
-
- /*
- * Test multilingual field display settings.
- */
- function testMultilingualDisplaySettings() {
- // Create "Basic page" content.
- $langcode = LANGUAGE_NOT_SPECIFIED;
- $title_key = "title";
- $title_value = $this->randomName(8);
- $body_key = "body[$langcode][0][value]";
- $body_value = $this->randomName(16);
-
- // Create node to edit.
- $edit = array();
- $edit[$title_key] = $title_value;
- $edit[$body_key] = $body_value;
- $edit['langcode'] = 'en';
- $this->drupalPost('node/add/page', $edit, t('Save'));
-
- // Check that the node exists in the database.
- $node = $this->drupalGetNodeByTitle($edit[$title_key]);
- $this->assertTrue($node, t('Node found in database.'));
-
- // Check if node body is showed.
- $this->drupalGet("node/$node->nid");
- $body = $this->xpath('//article[@id=:id]//div[@class=:class]/descendant::p', array(
- ':id' => 'node-' . $node->nid,
- ':class' => 'content',
- ));
- $this->assertEqual(current($body), $node->body['en'][0]['value'], 'Node body found.');
- }
-}
-
-/**
- * Functional tests for comment language.
- */
-class LocaleCommentLanguageFunctionalTest extends WebTestBase {
- protected $profile = 'standard';
-
- public static function getInfo() {
- return array(
- 'name' => 'Comment language',
- 'description' => 'Tests for comment language.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- // We also use language_test module here to be able to turn on content
- // language negotiation. Drupal core does not provide a way in itself
- // to do that.
- parent::setUp('locale', 'language_test');
-
- // Create and login user.
- $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'create article content'));
- $this->drupalLogin($admin_user);
-
- // Add language.
- $edit = array('predefined_langcode' => 'fr');
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-
- // Set "Article" content type to use multilingual support.
- $edit = array('node_type_language' => 1);
- $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
-
- // Enable content language negotiation UI.
- variable_set('language_test_content_language_type', TRUE);
-
- // Set interface language detection to user and content language detection
- // to URL. Disable inheritance from interface language to ensure content
- // language will fall back to the default language if no URL language can be
- // detected.
- $edit = array(
- 'language_interface[enabled][language-user]' => TRUE,
- 'language_content[enabled][language-url]' => TRUE,
- 'language_content[enabled][language-interface]' => FALSE,
- );
- $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
-
- // Change user language preference, this way interface language is always
- // French no matter what path prefix the URLs have.
- $edit = array('preferred_langcode' => 'fr');
- $this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save'));
- }
-
- /**
- * Test that comment language is properly set.
- */
- function testCommentLanguage() {
- drupal_static_reset('language_list');
-
- // Create two nodes, one for english and one for french, and comment each
- // node using both english and french as content language by changing URL
- // language prefixes. Meanwhile interface language is always French, which
- // is the user language preference. This way we can ensure that node
- // language and interface language do not influence comment language, as
- // only content language has to.
- foreach (language_list() as $node_langcode => $node_language) {
- $langcode_not_specified = LANGUAGE_NOT_SPECIFIED;
-
- // Create "Article" content.
- $title = $this->randomName();
- $edit = array(
- "title" => $title,
- "body[$langcode_not_specified][0][value]" => $this->randomName(),
- "langcode" => $node_langcode,
- );
- $this->drupalPost("node/add/article", $edit, t('Save'));
- $node = $this->drupalGetNodeByTitle($title);
-
- $prefixes = language_negotiation_url_prefixes();
- foreach (language_list() as $langcode => $language) {
- // Post a comment with content language $langcode.
- $prefix = empty($prefixes[$langcode]) ? '' : $prefixes[$langcode] . '/';
- $edit = array("comment_body[$langcode_not_specified][0][value]" => $this->randomName());
- $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Save'));
-
- // Check that comment language matches the current content language.
- $comment = db_select('comment', 'c')
- ->fields('c')
- ->condition('nid', $node->nid)
- ->orderBy('cid', 'DESC')
- ->execute()
- ->fetchObject();
- $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode, '%langcode' => $langcode);
- $this->assertEqual($comment->langcode, $langcode, t('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
- }
- }
- }
-}
-
-/**
- * Functional tests for localizing date formats.
- */
-class LocaleDateFormatsFunctionalTest extends WebTestBase {
-
- public static function getInfo() {
- return array(
- 'name' => 'Localize date formats',
- 'description' => 'Tests for the localization of date formats.',
- 'group' => 'Locale',
- );
- }
-
- function setUp() {
- parent::setUp(array('node', 'locale'));
-
- // Create Article node type.
- $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
-
- // Create and login user.
- $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'create article content'));
- $this->drupalLogin($admin_user);
- }
-
- /**
- * Functional tests for localizing date formats.
- */
- function testLocalizeDateFormats() {
- // Add language.
- $edit = array(
- 'predefined_langcode' => 'fr',
- );
- $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
-
- // Set language negotiation.
- $language_type = LANGUAGE_TYPE_INTERFACE;
- $edit = array(
- "{$language_type}[enabled][language-url]" => TRUE,
- );
- $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
-
- // Configure date formats.
- $this->drupalGet('admin/config/regional/date-time/locale');
- $this->assertText('French', 'Configured languages appear.');
- $edit = array(
- 'date_format_long' => 'd.m.Y - H:i',
- 'date_format_medium' => 'd.m.Y - H:i',
- 'date_format_short' => 'd.m.Y - H:i',
- );
- $this->drupalPost('admin/config/regional/date-time/locale/fr/edit', $edit, t('Save configuration'));
- $this->assertText(t('Configuration saved.'), 'French date formats updated.');
- $edit = array(
- 'date_format_long' => 'j M Y - g:ia',
- 'date_format_medium' => 'j M Y - g:ia',
- 'date_format_short' => 'j M Y - g:ia',
- );
- $this->drupalPost('admin/config/regional/date-time/locale/en/edit', $edit, t('Save configuration'));
- $this->assertText(t('Configuration saved.'), 'English date formats updated.');
-
- // Create node content.
- $node = $this->drupalCreateNode(array('type' => 'article'));
-
- // Configure format for the node posted date changes with the language.
- $this->drupalGet('node/' . $node->nid);
- $english_date = format_date($node->created, 'custom', 'j M Y');
- $this->assertText($english_date, t('English date format appears'));
- $this->drupalGet('fr/node/' . $node->nid);
- $french_date = format_date($node->created, 'custom', 'd.m.Y');
- $this->assertText($french_date, t('French date format appears'));
- }
-}