diff options
Diffstat (limited to 'modules/locale')
-rw-r--r-- | modules/locale/locale.admin.inc | 3 | ||||
-rw-r--r-- | modules/locale/locale.api.php | 155 | ||||
-rw-r--r-- | modules/locale/locale.install | 68 | ||||
-rw-r--r-- | modules/locale/locale.module | 54 | ||||
-rw-r--r-- | modules/locale/locale.test | 246 | ||||
-rw-r--r-- | modules/locale/tests/locale_test.module | 127 |
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, + ), + ); +} |