summaryrefslogtreecommitdiff
path: root/modules/locale
diff options
context:
space:
mode:
Diffstat (limited to 'modules/locale')
-rw-r--r--modules/locale/locale.admin.inc3
-rw-r--r--modules/locale/locale.api.php155
-rw-r--r--modules/locale/locale.install68
-rw-r--r--modules/locale/locale.module54
-rw-r--r--modules/locale/locale.test246
-rw-r--r--modules/locale/tests/locale_test.module127
6 files changed, 481 insertions, 172 deletions
diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc
index 174349750..253535751 100644
--- a/modules/locale/locale.admin.inc
+++ b/modules/locale/locale.admin.inc
@@ -275,8 +275,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) {
else {
$form['langcode'] = array('#type' => 'textfield',
'#title' => t('Language code'),
- '#size' => 12,
- '#maxlength' => 60,
+ '#maxlength' => 12,
'#required' => TRUE,
'#default_value' => @$language->language,
'#disabled' => (isset($language->language)),
diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php
index 2808f338c..1f11fc903 100644
--- a/modules/locale/locale.api.php
+++ b/modules/locale/locale.api.php
@@ -24,150 +24,6 @@ function hook_locale($op = 'groups') {
}
/**
- * Allows modules to act after language initialization has been performed.
- *
- * This is primarily needed to provide translation for configuration variables
- * in the proper bootstrap phase. Variables are user-defined strings and
- * therefore should not be translated via t(), since the source string can
- * change without notice and any previous translation would be lost. Moreover,
- * since variables can be used in the bootstrap phase, we need a bootstrap hook
- * to provide a translation early enough to avoid misalignments between code
- * using the original values and code using the translated values. However
- * modules implementing hook_boot() should be aware that language initialization
- * did not happen yet and thus they cannot rely on translated variables.
- */
-function hook_language_init() {
- global $language, $conf;
-
- switch ($language->language) {
- case 'it':
- $conf['site_name'] = 'Il mio sito Drupal';
- break;
-
- case 'fr':
- $conf['site_name'] = 'Mon site Drupal';
- break;
- }
-}
-
-/**
- * Perform alterations on language switcher links.
- *
- * A language switcher link may need to point to a different path or use a
- * translated link text before going through l(), which will just handle the
- * path aliases.
- *
- * @param $links
- * Nested array of links keyed by language code.
- * @param $type
- * The language type the links will switch.
- * @param $path
- * The current path.
- */
-function hook_language_switch_links_alter(array &$links, $type, $path) {
- global $language;
-
- if ($type == LANGUAGE_TYPE_CONTENT && isset($links[$language->language])) {
- foreach ($links[$language->language] as $link) {
- $link['attributes']['class'][] = 'active-language';
- }
- }
-}
-
-/**
- * Allow modules to define their own language types.
- *
- * @return
- * An array of language type definitions. Each language type has an identifier
- * key. The language type definition is an associative array that may contain
- * the following key-value pairs:
- * - "name": The human-readable language type identifier.
- * - "description": A description of the language type.
- * - "fixed": An array of language provider identifiers. Defining this key
- * makes the language type non-configurable.
- */
-function hook_language_types_info() {
- return array(
- 'custom_language_type' => array(
- 'name' => t('Custom language'),
- 'description' => t('A custom language type.'),
- ),
- 'fixed_custom_language_type' => array(
- 'fixed' => array('custom_language_provider'),
- ),
- );
-}
-
-/**
- * Perform alterations on language types.
- *
- * @param $language_types
- * Array of language type definitions.
- */
-function hook_language_types_info_alter(array &$language_types) {
- if (isset($language_types['custom_language_type'])) {
- $language_types['custom_language_type_custom']['description'] = t('A far better description.');
- }
-}
-
-/**
- * Allow modules to define their own language providers.
- *
- * @return
- * An array of language provider definitions. Each language provider has an
- * identifier key. The language provider definition is an associative array
- * that may contain the following key-value pairs:
- * - "types": An array of allowed language types. If a language provider does
- * not specify which language types it should be used with, it will be
- * available for all the configurable language types.
- * - "callbacks": An array of functions that will be called to perform various
- * tasks. Possible key-value pairs are:
- * - "language": Required. The callback that will determine the language
- * value.
- * - "switcher": The callback that will determine the language switch links
- * associated to the current language provider.
- * - "url_rewrite": The callback that will provide URL rewriting.
- * - "file": A file that will be included before the callback is invoked; this
- * allows callback functions to be in separate files.
- * - "weight": The default weight the language provider has.
- * - "name": A human-readable identifier.
- * - "description": A description of the language provider.
- * - "config": An internal path pointing to the language provider
- * configuration page.
- * - "cache": The value Drupal's page cache should be set to for the current
- * language provider to be invoked.
- */
-function hook_language_negotiation_info() {
- return array(
- 'custom_language_provider' => array(
- 'callbacks' => array(
- 'language' => 'custom_language_provider_callback',
- 'switcher' => 'custom_language_switcher_callback',
- 'url_rewrite' => 'custom_language_url_rewrite_callback',
- ),
- 'file' => drupal_get_path('module', 'custom') . '/custom.module',
- 'weight' => -4,
- 'types' => array('custom_language_type'),
- 'name' => t('Custom language provider'),
- 'description' => t('This is a custom language provider.'),
- 'cache' => 0,
- ),
- );
-}
-
-/**
- * Perform alterations on language providers.
- *
- * @param $language_providers
- * Array of language provider definitions.
- */
-function hook_language_negotiation_info_alter(array &$language_providers) {
- if (isset($language_providers['custom_language_provider'])) {
- $language_providers['custom_language_provider']['config'] = 'admin/config/regional/language/configure/custom-language-provider';
- }
-}
-
-/**
* Allow modules to react to language settings changes.
*
* Every module needing to act when the number of enabled languages changes
@@ -181,16 +37,5 @@ function hook_multilingual_settings_changed() {
}
/**
- * Perform alterations on the language fallback candidates.
- *
- * @param $fallback_candidates
- * An array of language codes whose order will determine the language fallback
- * order.
- */
-function hook_language_fallback_candidates_alter(array &$fallback_candidates) {
- $fallback_candidates = array_reverse($fallback_candidates);
-}
-
-/**
* @} End of "addtogroup hooks".
*/
diff --git a/modules/locale/locale.install b/modules/locale/locale.install
index 80b20c454..cb7cefe01 100644
--- a/modules/locale/locale.install
+++ b/modules/locale/locale.install
@@ -138,6 +138,74 @@ function locale_update_7003() {
*/
/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Remove duplicates in {locales_source}.
+ */
+function locale_update_7004() {
+ // Look up all duplicates. For each set of duplicates, we select the row
+ // with the lowest lid as the "master" that will be preserved.
+ $result_source = db_query("SELECT MIN(lid) AS lid, source, context FROM {locales_source} WHERE textgroup = 'default' GROUP BY source, context HAVING COUNT(*) > 1");
+
+ $conflict = FALSE;
+ foreach ($result_source as $source) {
+ // Find all rows in {locales_target} that are translations of the same
+ // string (incl. context).
+ $result_target = db_query("SELECT t.lid, t.language, t.plural, t.translation FROM {locales_source} s JOIN {locales_target} t ON s.lid = t.lid WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default' ORDER BY lid", array(
+ ':source' => $source->source,
+ ':context' => $source->context,
+ ));
+
+ $translations = array();
+ $keep_lids = array($source->lid);
+ foreach ($result_target as $target) {
+ if (!isset($translations[$target->language])) {
+ $translations[$target->language] = $target->translation;
+ if ($target->lid != $source->lid) {
+ // Move translation to the master lid.
+ db_query('UPDATE {locales_target} SET lid = :new_lid WHERE lid = :old_lid', array(
+ ':new_lid' => $source->lid,
+ ':old_lid' => $target->lid));
+ }
+ }
+ elseif ($translations[$target->language] == $target->translation) {
+ // Delete duplicate translation.
+ db_query('DELETE FROM {locales_target} WHERE lid = :lid AND language = :language', array(
+ ':lid' => $target->lid,
+ ':language' => $target->language));
+ }
+ else {
+ // The same string is translated into several different strings in one
+ // language. We do not know which is the preferred, so we keep them all.
+ $keep_lids[] = $target->lid;
+ $conflict = TRUE;
+ }
+ }
+
+ // Delete rows in {locales_source} that are no longer referenced from
+ // {locales_target}.
+ db_delete('locales_source')
+ ->condition('source', $source->source)
+ ->condition('context', $source->context)
+ ->condition('textgroup', 'default')
+ ->condition('lid', $keep_lids, 'NOT IN')
+ ->execute();
+ }
+
+ if ($conflict) {
+ $url = 'http://drupal.org/node/746240';
+ drupal_set_message('Your {locales_source} table contains duplicates that could not be removed automatically. See <a href="' . $url .'" target="_blank">' . $url . '</a> for more information.', 'warning');
+ }
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra"
+ */
+
+/**
* Implements hook_uninstall().
*/
function locale_uninstall() {
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
index caa01f2f3..e0981b2fb 100644
--- a/modules/locale/locale.module
+++ b/modules/locale/locale.module
@@ -22,7 +22,7 @@ function locale_help($path, $arg) {
case 'admin/help#locale':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
- $output .= '<p>' . t('The Locale module allows your Drupal site to be presented in languages other than the default English, and to be multilingual. The Locale module works by maintaining a database of translations, and examining text as it is about to be displayed. When a translation of the text is available in the language to be displayed, the translation is displayed rather than the original text. When a translation is unavailable, the original text is displayed, and then stored for review by a translator. For more information, see the online handbook entry for <a href="@locale">Locale module</a>.', array('@locale' => 'http://drupal.org/handbook/modules/locale/')) . '</p>';
+ $output .= '<p>' . t('The Locale module allows your Drupal site to be presented in languages other than the default English, and to be multilingual. The Locale module works by maintaining a database of translations, and examining text as it is about to be displayed. When a translation of the text is available in the language to be displayed, the translation is displayed rather than the original text. When a translation is unavailable, the original text is displayed, and then stored for review by a translator. For more information, see the online handbook entry for <a href="@locale">Locale module</a>.', array('@locale' => 'http://drupal.org/documentation/modules/locale/')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Translating interface text') . '</dt>';
@@ -253,6 +253,7 @@ function locale_permission() {
),
'translate interface' => array(
'title' => t('Translate interface texts'),
+ 'restrict access' => TRUE,
),
);
}
@@ -699,13 +700,15 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) {
}
else {
// We don't have the source string, cache this as untranslated.
- db_insert('locales_source')
- ->fields(array(
+ db_merge('locales_source')
+ ->insertFields(array(
'location' => request_uri(),
+ 'version' => VERSION,
+ ))
+ ->key(array(
'source' => $string,
'context' => (string) $context,
'textgroup' => 'default',
- 'version' => VERSION,
))
->execute();
$locale_t[$langcode][$context][$string] = TRUE;
@@ -734,30 +737,51 @@ function locale_reset() {
* @param $langcode
* Optional language code to translate to a language other than
* what is used to display the page.
+ *
+ * @return
+ * The numeric index of the plural variant to use for this $langcode and
+ * $count combination or -1 if the language was not found or does not have a
+ * plural formula.
*/
function locale_get_plural($count, $langcode = NULL) {
global $language;
- $locale_formula = &drupal_static(__FUNCTION__, array());
- $plurals = &drupal_static(__FUNCTION__ . ':plurals', array());
+
+ // Used to locally cache the plural formulas for all languages.
+ $plural_formulas = &drupal_static(__FUNCTION__, array());
+
+ // Used to store precomputed plural indexes corresponding to numbers
+ // individually for each language.
+ $plural_indexes = &drupal_static(__FUNCTION__ . ':plurals', array());
$langcode = $langcode ? $langcode : $language->language;
- if (!isset($plurals[$langcode][$count])) {
- if (empty($locale_formula)) {
+ if (!isset($plural_indexes[$langcode][$count])) {
+ // Retrieve and statically cache the plural formulas for all languages.
+ if (empty($plural_formulas)) {
$language_list = language_list();
- $locale_formula[$langcode] = $language_list[$langcode]->formula;
+ foreach ($language_list as $langcode => $lang) {
+ $plural_formulas[$langcode] = $lang->formula;
+ }
}
- if ($locale_formula[$langcode]) {
+ // If there is a plural formula for the language, evaluate it for the given
+ // $count and statically cache the result for the combination of language
+ // and count, since the result will always be identical.
+ if (!empty($plural_formulas[$langcode])) {
+ // $n is used inside the expression in the eval().
$n = $count;
- $plurals[$langcode][$count] = @eval('return intval(' . $locale_formula[$langcode] . ');');
- return $plurals[$langcode][$count];
+ $plural_indexes[$langcode][$count] = @eval('return intval(' . $plural_formulas[$langcode] . ');');
+ }
+ // In case there is no plural formula for English (no imported translation
+ // for English), use a default formula.
+ elseif ($langcode == 'en') {
+ $plural_indexes[$langcode][$count] = (int) ($count != 1);
}
+ // Otherwise, return -1 (unknown).
else {
- $plurals[$langcode][$count] = -1;
- return -1;
+ $plural_indexes[$langcode][$count] = -1;
}
}
- return $plurals[$langcode][$count];
+ return $plural_indexes[$langcode][$count];
}
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index ca945569a..ffda6f580 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -694,6 +694,136 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase {
}
/**
+ * Tests plural index computation functionality.
+ */
+class LocalePluralFormatTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Plural formula evaluation',
+ 'description' => 'Tests plural formula evaluation for various languages.',
+ 'group' => 'Locale',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale', 'locale_test');
+
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages'));
+ $this->drupalLogin($admin_user);
+
+ // 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',
+ ));
+ }
+
+ /**
+ * Tests locale_get_plural() functionality.
+ */
+ function testGetPluralFormat() {
+ $this->drupalGet('locale_test_plural_format_page');
+ $tests = _locale_test_plural_format_tests();
+ $result = array();
+ foreach ($tests as $test) {
+ $this->assertPluralFormat($test['count'], $test['language'], $test['expected-result']);
+ }
+ }
+
+ /**
+ * Helper assert to test locale_get_plural page.
+ *
+ * @param $count
+ * Number for testing.
+ * @param $lang
+ * Language for testing
+ * @param $expected_result
+ * Expected result.
+ * @param $message
+ */
+ function assertPluralFormat($count, $lang, $expected_result) {
+ $message_param = array(
+ '@lang' => $lang,
+ '@count' => $count,
+ '@expected_result' => $expected_result,
+ );
+ $message = t("Computed plural index for '@lang' with count @count is @expected_result.", $message_param);
+
+ $message_param = array(
+ '@lang' => $lang,
+ '@expected_result' => $expected_result,
+ );
+ $this->assertText(format_string('Language: @lang, locale_get_plural: @expected_result.', $message_param, $message));
+ }
+
+ /**
+ * 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 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\\n"
+
+msgid "One sheep"
+msgid_plural "@count sheep"
+msgstr[0] "un mouton"
+msgstr[1] "@count moutons"
+
+msgid "Monday"
+msgstr "lundi"
+EOF;
+ }
+
+ /**
+ * Returns a .po file with a complex plural formula.
+ */
+ function getPoFileWithComplexPlural() {
+ return <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=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;
+ }
+}
+
+/**
* Functional tests for the import of translation files.
*/
class LocaleImportFunctionalTest extends DrupalWebTestCase {
@@ -793,6 +923,25 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase {
// This import should not have changed number of plural forms.
$this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('Plural numbers untouched.'));
+ $this->importPoFile($this->getPoFileWithBrokenPlural(), array(
+ 'langcode' => 'fr',
+ 'mode' => 1, // Existing strings are kept, only new strings are added.
+ ));
+
+ // Attempt to import broken .po file as well to prove that this
+ // will not overwrite the proper plural formula imported above.
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('Broken plurals: plural numbers untouched.'));
+
+ $this->importPoFile($this->getPoFileWithMissingPlural(), array(
+ 'langcode' => 'fr',
+ 'mode' => 1, // Existing strings are kept, only new strings are added.
+ ));
+
+ // Attempt to import .po file which has no plurals and prove that this
+ // will not overwrite the proper plural formula imported above.
+ $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, t('No plurals: plural numbers untouched.'));
+
+
// Try importing a .po file with overriding strings, and ensure existing
// strings are overwritten.
$this->importPoFile($this->getOverwritePoFile(), array(
@@ -1072,6 +1221,42 @@ msgstr ""
EOF;
}
+
+ /**
+ * Returns a .po file with a missing plural formula.
+ */
+ function getPoFileWithMissingPlural() {
+ return <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+
+msgid "Monday"
+msgstr "Ponedjeljak"
+EOF;
+ }
+
+ /**
+ * Returns a .po file with a broken plural formula.
+ */
+ function getPoFileWithBrokenPlural() {
+ return <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 7\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: broken, will not parse\\n"
+
+msgid "Monday"
+msgstr "lundi"
+EOF;
+ }
+
}
/**
@@ -1483,6 +1668,7 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase {
'EN' => 'en',
' en' => 'en',
'en ' => 'en',
+ 'en, fr' => 'en',
// A less specific language from the browser matches a more specific one
// from the website, and the other way around for compatibility with
@@ -1517,6 +1703,7 @@ class LocaleBrowserDetectionTest extends DrupalUnitTestCase {
// Unresolvable cases.
'' => FALSE,
'de,pl' => FALSE,
+ 'iecRswK4eh' => FALSE,
$this->randomName(10) => FALSE,
);
@@ -2118,6 +2305,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'path' => 'admin/config',
'expect' => $default_string,
+ 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.',
),
@@ -2126,6 +2314,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT),
'path' => "$language/admin/config",
'expect' => $language_string,
+ 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix',
),
@@ -2134,6 +2323,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER),
'path' => 'admin/config',
'expect' => $language_browser_fallback_string,
+ 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_BROWSER,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference',
),
@@ -2142,6 +2332,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER),
'path' => "$language/admin/config",
'expect' => $language_string,
+ 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (PATH) > BROWSER: with langage prefix, UI language is based on path prefix',
),
@@ -2150,6 +2341,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_DEFAULT),
'path' => 'admin/config',
'expect' => $default_string,
+ 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT,
'http_header' => $http_header_blah,
'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language',
),
@@ -2177,6 +2369,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN,
'path' => 'admin/config',
'expect' => $default_string,
+ 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (DOMAIN) > DEFAULT: default domain should get default language',
),
@@ -2188,6 +2381,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
'locale_test_domain' => $language_domain,
'path' => 'admin/config',
'expect' => $language_string,
+ 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL,
'http_header' => $http_header_browser_fallback,
'message' => 'URL (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese',
),
@@ -2211,6 +2405,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
}
$this->drupalGet($test['path'], array(), $test['http_header']);
$this->assertText($test['expect'], $test['message']);
+ $this->assertText(t('Language negotiation provider: @name', array('@name' => $test['expected_provider'])));
}
/**
@@ -2256,6 +2451,56 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
$fields = $this->xpath('//div[@id="site-name"]//a[@rel="home" and @href=:url]//span', $args);
$this->assertTrue($fields[0] == 'Drupal', t('URLs are rewritten using the browser language.'));
}
+
+ /**
+ * Tests url() when separate domains are used for multiple languages.
+ */
+ function testLanguageDomain() {
+ // Add the Italian language, without protocol.
+ $langcode = 'it';
+ locale_add_language($langcode, 'Italian', 'Italian', LANGUAGE_LTR, 'it.example.com', '', TRUE, FALSE);
+
+ // Add the French language, with protocol.
+ $langcode = 'fr';
+ locale_add_language($langcode, 'French', 'French', LANGUAGE_LTR, 'http://fr.example.com', '', TRUE, FALSE);
+
+ // Enable language URL detection.
+ $negotiation = array_flip(array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT));
+ language_negotiation_set(LANGUAGE_TYPE_INTERFACE, $negotiation);
+
+ variable_set('locale_language_negotiation_url_part', 1);
+
+ global $is_https;
+ $languages = language_list();
+
+ foreach (array('it', 'fr') as $langcode) {
+ // Build the link we're going to test based on the clean url setting.
+ $link = (!empty($GLOBALS['conf']['clean_url'])) ? $langcode . '.example.com/admin' : $langcode . '.example.com/?q=admin';
+
+ // Test URL in another language.
+ // Base path gives problems on the testbot, so $correct_link is hard-coded.
+ // @see UrlAlterFunctionalTest::assertUrlOutboundAlter (path.test).
+ $url = url('admin', array('language' => $languages[$langcode]));
+ $url_scheme = ($is_https) ? 'https://' : 'http://';
+ $correct_link = $url_scheme . $link;
+ $this->assertTrue($url == $correct_link, t('The url() function returns the right url (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+
+ // Test https via options.
+ variable_set('https', TRUE);
+ $url = url('admin', array('https' => TRUE, 'language' => $languages[$langcode]));
+ $correct_link = 'https://' . $link;
+ $this->assertTrue($url == $correct_link, t('The url() function returns the right https url (via options) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+ variable_set('https', FALSE);
+
+ // Test https via current url scheme.
+ $temp_https = $is_https;
+ $is_https = TRUE;
+ $url = url('admin', array('language' => $languages[$langcode]));
+ $correct_link = 'https://' . $link;
+ $this->assertTrue($url == $correct_link, t('The url() function returns the right url (via current url scheme) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link)));
+ $is_https = $temp_https;
+ }
+ }
}
/**
@@ -2772,3 +3017,4 @@ class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase {
}
}
}
+
diff --git a/modules/locale/tests/locale_test.module b/modules/locale/tests/locale_test.module
index 14a2588dd..64f4aed57 100644
--- a/modules/locale/tests/locale_test.module
+++ b/modules/locale/tests/locale_test.module
@@ -32,6 +32,9 @@ function locale_test_boot() {
*/
function locale_test_init() {
locale_test_store_language_negotiation();
+ if (isset($GLOBALS['language']) && isset($GLOBALS['language']->provider)) {
+ drupal_set_message(t('Language negotiation provider: @name', array('@name' => $GLOBALS['language']->provider)));
+ }
}
/**
@@ -52,6 +55,22 @@ function locale_test_language_types_info() {
}
/**
+ * Implements hook_menu().
+ *
+ * @return array
+ */
+function locale_test_menu() {
+ $items = array();
+ $items['locale_test_plural_format_page'] = array(
+ 'page callback' => 'locale_test_plural_format_page',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
* Implements hook_language_types_info_alter().
*/
function locale_test_language_types_info_alter(array &$language_types) {
@@ -113,3 +132,111 @@ function locale_test_store_language_negotiation() {
function locale_test_language_provider($languages) {
return 'it';
}
+
+/**
+ * Returns markup for locale_get_plural testing.
+ *
+ * @return array
+ */
+function locale_test_plural_format_page() {
+ $tests = _locale_test_plural_format_tests();
+ $result = array();
+ foreach ($tests as $test) {
+ $string_param = array(
+ '@lang' => $test['language'],
+ '@locale_get_plural' => locale_get_plural($test['count'], $test['language'])
+ );
+ $result[] = array(
+ '#prefix' => '<br/>',
+ '#markup' => format_string('Language: @lang, locale_get_plural: @locale_get_plural.', $string_param),
+ );
+ }
+ return $result;
+}
+
+/**
+ * Helper function with list of test cases
+ *
+ * @return array
+ */
+function _locale_test_plural_format_tests() {
+ return array(
+ // Test data for English (no formula present).
+ array(
+ 'count' => 1,
+ 'language' => 'en',
+ 'expected-result' => 0,
+ ),
+ array(
+ 'count' => 0,
+ 'language' => 'en',
+ 'expected-result' => 1,
+ ),
+ array(
+ 'count' => 5,
+ 'language' => 'en',
+ 'expected-result' => 1,
+ ),
+
+ // Test data for French (simpler formula).
+ array(
+ 'count' => 1,
+ 'language' => 'fr',
+ 'expected-result' => 0,
+ ),
+ array(
+ 'count' => 0,
+ 'language' => 'fr',
+ 'expected-result' => 1,
+ ),
+ array(
+ 'count' => 5,
+ 'language' => 'fr',
+ 'expected-result' => 1,
+ ),
+
+ // Test data for Croatian (more complex formula).
+ array(
+ 'count' => 1,
+ 'language' => 'hr',
+ 'expected-result' => 0,
+ ),
+ array(
+ 'count' => 21,
+ 'language' => 'hr',
+ 'expected-result' => 0,
+ ),
+ array(
+ 'count' => 0,
+ 'language' => 'hr',
+ 'expected-result' => 2,
+ ),
+ array(
+ 'count' => 2,
+ 'language' => 'hr',
+ 'expected-result' => 1,
+ ),
+ array(
+ 'count' => 8,
+ 'language' => 'hr',
+ 'expected-result' => 2,
+ ),
+
+ // Test data for Hungarian (nonexistent language).
+ array(
+ 'count' => 1,
+ 'language' => 'hu',
+ 'expected-result' => -1,
+ ),
+ array(
+ 'count' => 21,
+ 'language' => 'hu',
+ 'expected-result' => -1,
+ ),
+ array(
+ 'count' => 0,
+ 'language' => 'hu',
+ 'expected-result' => -1,
+ ),
+ );
+}