Newer
Older
<?php
// $Id$
/**
* @file
* Tests for Locale module.
*
* The test file includes:
* - a functional test for the language configuration forms;
* - functional tests for the translation functionalities, including searching;
* - a functional test for the PO files import feature, including validation;
* - functional tests for translations and templates export feature;
* - functional tests for the uninstall process;
* - a functional test for the language switching feature;
* - a functional test for a user's ability to change their default language;
* - a functional test for configuring a different path alias per language;
* - a functional test for configuring a different path alias per language;
* - a functional test for multilingual support by content type and on nodes.
Angie Byron
committed
* - a functional test for multilingual fields.
/**
* Functional tests for the language configuration forms.
*/
class LocaleConfigurationTest extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Language configuration',
'description' => 'Adds a new locale and tests changing its status and the default language.',
'group' => 'Locale',
);
}
function setUp() {
parent::setUp('locale');
}
/**
* Functional tests for adding, editing and deleting languages.
*/
function testLanguageConfiguration() {
global $base_url;
// User to add and remove language.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
$this->drupalLogin($admin_user);
// Add predefined language.
$edit = array(
'langcode' => 'fr',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertText('fr', t('Language added successfully.'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
// Add custom language.
// Code for the language.
// The English name for the language.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
$prefix = $langcode;
$edit = array(
'langcode' => $langcode,
'name' => $name,
'native' => $native,
'prefix' => $prefix,
'direction' => '0',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertText($langcode, t('Language code found.'));
$this->assertText($name, t('Name found.'));
$this->assertText($native, t('Native found.'));
$this->assertText($native, t('Test language added.'));
// Check if we can change the default language.
Dries Buytaert
committed
$path = 'admin/config/regional/language';
$this->drupalGet($path);
Angie Byron
committed
$this->assertFieldChecked('edit-site-default-en', t('English is the default language.'));
// Change the default language.
$edit = array(
'site_default' => $langcode,
);
$this->drupalPost($path, $edit, t('Save configuration'));
Angie Byron
committed
$this->assertNoFieldChecked('edit-site-default-en', t('Default language updated.'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
// Ensure we can't delete the default language.
Dries Buytaert
committed
$path = 'admin/config/regional/language/delete/' . $langcode;
$this->drupalGet($path);
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertText(t('The default language cannot be deleted.'), t('Failed to delete the default language.'));
// Check if we can disable a language.
$edit = array(
'enabled[en]' => FALSE,
);
$this->drupalPost($path, $edit, t('Save configuration'));
Angie Byron
committed
$this->assertNoFieldChecked('edit-enabled-en', t('Language disabled.'));
// Set disabled language to be the default and ensure it is re-enabled.
$edit = array(
'site_default' => 'en',
);
$this->drupalPost($path, $edit, t('Save configuration'));
Angie Byron
committed
$this->assertFieldChecked('edit-enabled-en', t('Default language re-enabled.'));
// Ensure 'edit' link works.
$this->clickLink(t('edit'));
$this->assertTitle(t('Edit language | Drupal'), t('Page title is "Edit language".'));
// Edit a language.
Dries Buytaert
committed
$path = 'admin/config/regional/language/edit/' . $langcode;
$name = $this->randomName(16);
$edit = array(
'name' => $name,
);
$this->drupalPost($path, $edit, t('Save language'));
$this->assertRaw($name, t('The language has been updated.'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
// Ensure 'delete' link works.
Dries Buytaert
committed
$path = 'admin/config/regional/language';
$this->drupalGet($path);
$this->clickLink(t('delete'));
$this->assertText(t('Are you sure you want to delete the language'), t('"delete" link is correct.'));
// Delete the language.
Dries Buytaert
committed
$path = 'admin/config/regional/language/delete/' . $langcode;
$this->drupalGet($path);
// First test the 'cancel' link.
$this->clickLink(t('Cancel'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertRaw($name, t('The language was not deleted.'));
// Delete the language for real. This a confirm form, we do not need any
// fields changed.
$this->drupalPost($path, array(), t('Delete'));
// We need raw here because %locale will add HTML.
$this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
// Reload to remove $name.
$this->drupalGet($path);
$this->assertNoText($langcode, t('Language code not found.'));
$this->assertNoText($name, t('Name not found.'));
$this->assertNoText($native, t('Native not found.'));
// Ensure we can't delete the English language.
Dries Buytaert
committed
$path = 'admin/config/regional/language/delete/en';
$this->drupalGet($path);
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), t('Correct page redirection.'));
$this->assertText(t('The English language cannot be deleted.'), t('Failed to delete English language.'));
$this->drupalLogout();
}
}
/**
* Functional test for string translation and validation.
*/
class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
Angie Byron
committed
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);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
$prefix = $langcode;
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "<em class=\"locale-untranslated\">$langcode</em> ";
// This will be the translation of $name.
$translation = $this->randomName(16);
// Add custom language.
$this->drupalLogin($admin_user);
Angie Byron
committed
$edit = array(
'langcode' => $langcode,
'name' => $name,
'native' => $native,
'prefix' => $prefix,
'direction' => '0',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
Dries Buytaert
committed
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
Dries Buytaert
committed
locale(NULL, NULL, NULL, TRUE);
$this->assertText($langcode, t('Language code found.'));
$this->assertText($name, t('Name found.'));
$this->assertText($native, t('Native found.'));
// No t() here, we do not want to add this string to the database and it's
// surely not translated yet.
$this->assertText($native, 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',
'group' => 'all',
);
Dries Buytaert
committed
$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.
Angie Byron
committed
$matches = array();
Angie Byron
committed
preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches);
Angie Byron
committed
$lid = $matches[1];
// No t() here, it's surely not translated yet.
$this->assertText($name, t('name found on edit screen.'));
$edit = array(
"translations[$langcode]" => $translation,
);
$this->drupalPost(NULL, $edit, t('Save translations'));
$this->assertText(t('The string has been saved.'), t('The string has been saved.'));
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
Dries Buytaert
committed
$this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works.'));
Dries Buytaert
committed
$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;
Dries Buytaert
committed
$this->drupalGet('admin/config/regional/translate/edit/' . $random_lid);
$this->assertText(t('String not found'), t('String not found.'));
Dries Buytaert
committed
$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);
Dries Buytaert
committed
$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 %locale will add HTML.
$this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.'));
// Reload to remove $name.
$this->drupalGet($path);
$this->assertNoText($langcode, t('Language code not found.'));
$this->assertNoText($name, t('Name not found.'));
$this->assertNoText($native, t('Native not found.'));
$this->drupalLogout();
// Delete the string.
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
'language' => 'all',
'translation' => 'all',
'group' => 'all',
);
Dries Buytaert
committed
$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.
Dries Buytaert
committed
$path = 'admin/config/regional/translate/delete/' . $lid;
$this->drupalGet($path);
// First test the 'cancel' link.
$this->clickLink(t('Cancel'));
Dries Buytaert
committed
$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.
Dries Buytaert
committed
$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.'));
Dries Buytaert
committed
$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.'));
}
Dries Buytaert
committed
/**
* Tests the validation of the translation input.
*/
function testStringValidation() {
Dries Buytaert
committed
global $base_url;
// User to add language and strings.
Dries Buytaert
committed
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
$langcode = 'xx';
Dries Buytaert
committed
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
$prefix = $langcode;
Dries Buytaert
committed
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "<em class=\"locale-untranslated\">$langcode</em> ";
// These will be the invalid translations of $name.
$key = $this->randomName(16);
$bad_translations[$key] = "<script>alert('xss');</script>" . $key;
$key = $this->randomName(16);
$bad_translations[$key] = '<img SRC="javascript:alert(\'xss\');">' . $key;
$key = $this->randomName(16);
$bad_translations[$key] = '<<SCRIPT>alert("xss");//<</SCRIPT>' . $key;
$key = $this->randomName(16);
$bad_translations[$key] ="<BODY ONLOAD=alert('xss')>" . $key;
// Add custom language.
$edit = array(
Dries Buytaert
committed
'langcode' => $langcode,
'name' => $name,
'native' => $native,
'prefix' => $prefix,
'direction' => '0',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
Dries Buytaert
committed
// Add string.
Dries Buytaert
committed
t($name, array(), array('langcode' => $langcode));
Dries Buytaert
committed
// Reset locale cache.
$search = array(
Dries Buytaert
committed
'string' => $name,
'language' => 'all',
'translation' => 'all',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// Find the edit path.
Dries Buytaert
committed
$content = $this->drupalGetContent();
Dries Buytaert
committed
$this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), t('Found the edit path.'));
Dries Buytaert
committed
$path = $matches[0];
foreach ($bad_translations as $key => $translation) {
$edit = array(
Dries Buytaert
committed
"translations[$langcode]" => $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);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
$prefix = $langcode;
// This is the language indicator on the translation search screen for
// untranslated strings. Copied straight from locale.inc.
$language_indicator = "<em class=\"locale-untranslated\">$langcode</em> ";
// This will be the translation of $name.
$translation = $this->randomName(16);
// Add custom language.
$this->drupalLogin($admin_user);
$edit = array(
'langcode' => $langcode,
'name' => $name,
'native' => $native,
'prefix' => $prefix,
'direction' => '0',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
Dries Buytaert
committed
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
Dries Buytaert
committed
locale(NULL, NULL, NULL, TRUE);
$this->drupalLogout();
// Search for the name.
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
'language' => 'all',
'translation' => 'all',
'group' => 'all',
);
Dries Buytaert
committed
$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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), 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();
Dries Buytaert
committed
preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches);
$lid = $matches[1];
$edit = array(
"translations[$langcode]" => $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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), t('Search found the translation.'));
// Ensure translated string doesn't appear if searching on English.
$search = array(
'string' => $translation,
'language' => 'en',
'translation' => 'all',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), t("Search didn't find the invalid string."));
}
}
Dries Buytaert
committed
/**
* Functional tests for the import of translation files.
*/
class LocaleImportFunctionalTest extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
Dries Buytaert
committed
return array(
'name' => 'Translation import',
'description' => 'Tests the importation of locale files.',
'group' => 'Locale',
Dries Buytaert
committed
);
}
/**
* A user able to create languages and import translations.
*/
protected $admin_user = NULL;
function setUp() {
Angie Byron
committed
parent::setUp('locale', 'locale_test');
Dries Buytaert
committed
$this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
$this->drupalLogin($this->admin_user);
}
/**
* Test importation of standalone .po files.
*/
function testStandalonePoFile() {
// Try importing a .po file.
Dries Buytaert
committed
$this->importPoFile($this->getPoFile(), array(
Dries Buytaert
committed
'langcode' => 'fr',
Dries Buytaert
committed
));
Dries Buytaert
committed
// 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 7 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' => 7, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
Dries Buytaert
committed
// Ensure we were redirected correctly.
Dries Buytaert
committed
$this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
Dries Buytaert
committed
Dries Buytaert
committed
Angie Byron
committed
// Try importing a .po file with invalid tags in the default text group.
Dries Buytaert
committed
$this->importPoFile($this->getBadPoFile(), array(
Dries Buytaert
committed
'langcode' => 'fr',
Dries Buytaert
committed
));
// The import should have created 1 string and rejected 2.
Dries Buytaert
committed
$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, 'One translation string was skipped because it contains disallowed HTML.', '@count translation strings were skipped because they contain disallowed HTML.');
$this->assertRaw($skip_message, t('Unsafe strings were skipped.'));
Dries Buytaert
committed
Angie Byron
committed
// Try importing a .po file with invalid tags in a non default text group.
Dries Buytaert
committed
$this->importPoFile($this->getBadPoFile(), array(
Angie Byron
committed
'langcode' => 'fr',
'group' => 'custom',
Dries Buytaert
committed
));
// The import should have created 3 strings.
Angie Byron
committed
$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' => 3, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.'));
Dries Buytaert
committed
// Try importing a .po file which doesn't exist.
$name = $this->randomName(16);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/import', array(
'langcode' => 'fr',
'files[file]' => $name,
'group' => 'custom',
), t('Import'));
Dries Buytaert
committed
$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.'));
Dries Buytaert
committed
// Try importing a .po file with overriding strings, and ensure existing
// strings are kept.
Dries Buytaert
committed
$this->importPoFile($this->getOverwritePoFile(), array(
'langcode' => 'fr',
'mode' => 1, // Existing strings are kept, only new strings are added.
Dries Buytaert
committed
));
// 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings found for your search.'), t('String not overwritten by imported string.'));
Dries Buytaert
committed
// Try importing a .po file with overriding strings, and ensure existing
// strings are overwritten.
Dries Buytaert
committed
$this->importPoFile($this->getOverwritePoFile(), array(
'langcode' => 'fr',
'mode' => 0, // Strings in the uploaded file replace existing ones, new ones are added.
Dries Buytaert
committed
));
// 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',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), t('String overwritten by imported string.'));
}
/**
* Test automatic importation of a module's translation files when a language
* is enabled.
*/
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);
// The native name for the language.
$native = $this->randomName(16);
// The domain prefix.
$prefix = $langcode;
// Create a custom language.
$edit = array(
'langcode' => $langcode,
'name' => $name,
'native' => $native,
'prefix' => $prefix,
'direction' => '0',
);
Dries Buytaert
committed
$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 for the enabled modules.'), t('Language file automatically imported.'));
// Ensure strings were successfully imported.
$search = array(
'string' => 'lundi',
'language' => $langcode,
'translation' => 'translated',
'group' => 'all',
);
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings found for your search.'), t('String successfully imported.'));
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
* Test automatic importation of a module's translation files when a language
* is enabled.
*/
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.'));
Dries Buytaert
committed
}
/**
* 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(file_directory_path('temporary'), "po_");
Dries Buytaert
committed
file_put_contents($name, $contents);
$options['files[file]'] = $name;
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/import', $options, t('Import'));
Dries Buytaert
committed
unlink($name);
}
Dries Buytaert
committed
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
/**
* Helper function that returns a proper .po file.
*/
function getPoFile() {
return <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 6\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Monday"
msgstr "lundi"
msgid "Tuesday"
msgstr "mardi"
msgid "Wednesday"
msgstr "mercredi"
msgid "Thursday"
msgstr "jeudi"
msgid "Friday"
msgstr "vendredi"
msgid "Saturday"
msgstr "samedi"
msgid "Sunday"
msgstr "dimanche"
Dries Buytaert
committed
EOF;
}
/**
* Helper function that returns a bad .po file.
Dries Buytaert
committed
*/
function getBadPoFile() {
return <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 6\\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<img SRC="javascript:alert(\'xss\');">"
msgid "delete"
msgstr "supprimer<script>alert('xss');</script>"
Dries Buytaert
committed
EOF;
}
/**
* Helper function that returns a proper .po file, for testing overwriting
* existing translations.
*/
function getOverwritePoFile() {
return <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 6\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
msgid "Monday"
msgstr "Montag"
msgid "Day"
msgstr "Jour"
EOF;
}
Dries Buytaert
committed
/**
* 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 6\\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;
}
}
/**
* Functional tests for the export of translation files.
*/
class LocaleExportFunctionalTest extends DrupalWebTestCase {
Angie Byron
committed
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', 'locale_test');
$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(file_directory_path('temporary'), "po_");
file_put_contents($name, $this->getPoFile());
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/import', array(
'langcode' => 'fr',
'files[file]' => $name,
), t('Import'));
unlink($name);
// Get the French translations.
Dries Buytaert
committed
$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.'));
}
/**
* Test exportation of translation template file.
*/
function testExportTranslationTemplateFile() {
// Get the translation template file.
// There are two 'Export' buttons on this page, but it somehow works. It'd
// be better if we could use the submit button id like documented but that
// doesn't work.
Dries Buytaert
committed
$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 6\\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;
}
Dries Buytaert
committed
}
Angie Byron
committed
/**
* Locale uninstall with English UI functional test.
*/
class LocaleUninstallFunctionalTest extends DrupalWebTestCase {
Angie Byron
committed
public static function getInfo() {
'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.
*/
Angie Byron
committed
protected $language_interface;
function setUp() {
parent::setUp('locale');
Angie Byron
committed
$this->language_interface = 'en';
/**
* Check if the values of the Locale variables are correct after uninstall.
*/
function testUninstallProcess() {
$locale_module = array('locale');
// Add a new language and optionally set it as default.
require_once DRUPAL_ROOT . '/includes/locale.inc';
Angie Byron
committed
locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language_interface == 'fr');
// Check the UI language.
Dries Buytaert
committed
drupal_language_initialize();
Angie Byron
committed
global $language_interface;
$this->assertEqual($language_interface->language, $this->language_interface, t('Current language: %lang', array('%lang' => $language_interface->language)));
// Enable multilingual workflow option for articles.
variable_set('language_content_type_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);
Dries Buytaert
committed
$this->drupalGet('admin/config/regional/translate/translate');
Dries Buytaert
committed
$string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location AND textgroup = :textgroup', array(
':location' => '%.js%',
':textgroup' => 'default',
))->fetchObject();
$edit = array('translations[fr]' => 'french translation');
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations'));
_locale_rebuild_js('fr');
Dries Buytaert
committed
$file = db_query('SELECT javascript FROM {languages} WHERE language = :language', array(':language' => 'fr'))->fetchObject();
$js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $file->javascript . '.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);
Angie Byron
committed
// Change language negotiation options.
drupal_load('module', 'locale');
variable_set('language_types', drupal_language_types() + array('language_custom' => TRUE));
variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info());
variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info());
variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, locale_language_negotiation_info());
// Change language providers settings.
variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX);
variable_set('locale_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.
Dries Buytaert
committed
drupal_language_initialize();
Angie Byron
committed
$this->assertEqual($language_interface->language, 'en', t('Language after uninstall: %lang', array('%lang' => $language_interface->language)));
// 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.
Angie Byron
committed
require_once DRUPAL_ROOT . '/includes/language.inc';
Angie Byron
committed
$this->assertTrue(count(language_types()) == count(drupal_language_types()), t('Language types reset'));
$language_negotiation = language_negotiation_get(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_get(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_get(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT;
$this->assertTrue($language_negotiation, t('URL language negotiation: %setting', array('%setting' => t($language_negotiation ? 'none' : 'set'))));
// Check language providers settings.
$this->assertFalse(variable_get('locale_language_negotiation_url_part', FALSE), t('URL language provider indicator settings cleared.'));