diff options
author | Dries Buytaert <dries@buytaert.net> | 2009-06-08 05:00:12 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2009-06-08 05:00:12 +0000 |
commit | 28b2f098beb2dcf1e3b6745c65c1194d45da0691 (patch) | |
tree | 799076def12eb7e551b6e0825a0924dd0fb9630c /modules/locale | |
parent | ece9032afd035bbe4ca55b6fd671df03b96b4e35 (diff) | |
download | brdo-28b2f098beb2dcf1e3b6745c65c1194d45da0691.tar.gz brdo-28b2f098beb2dcf1e3b6745c65c1194d45da0691.tar.bz2 |
- Patch #334283 by Damien Tournoud, andypost, Freso et al: add context to t() to help deal with ambigious strings (and improved the locale APIs a bit). Example: May is both a short month name as a long month name in English, but not necessarily in other languages.
Diffstat (limited to 'modules/locale')
-rw-r--r-- | modules/locale/locale.install | 28 | ||||
-rw-r--r-- | modules/locale/locale.module | 20 | ||||
-rw-r--r-- | modules/locale/locale.test | 117 |
3 files changed, 120 insertions, 45 deletions
diff --git a/modules/locale/locale.install b/modules/locale/locale.install index 205aa2eae..2c7a7db7c 100644 --- a/modules/locale/locale.install +++ b/modules/locale/locale.install @@ -230,6 +230,25 @@ function locale_update_6006() { */ /** + * @defgroup updates-6.x-to-7.x Locale updates from 6.x to 7.x + * @{ + */ + +/** + * Allow longer location. + */ +function locale_update_7000() { + $ret = array(); + db_drop_index($ret, 'locales_source', 'source'); + db_add_index($ret, 'locales_source', 'source_context', array(array('source', 30), 'context')); + return $ret; +} + +/** + * @} End of "defgroup updates-6.x-to-7.x" + */ + +/** * Implement hook_uninstall(). */ function locale_uninstall() { @@ -381,6 +400,13 @@ function locale_schema() { 'not null' => TRUE, 'description' => 'The original string in English.', ), + 'context' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The context this string applies to.', + ), 'version' => array( 'type' => 'varchar', 'length' => 20, @@ -391,7 +417,7 @@ function locale_schema() { ), 'primary key' => array('lid'), 'indexes' => array( - 'source' => array(array('source', 30)), + 'source_context' => array(array('source', 30), 'context'), ), ); diff --git a/modules/locale/locale.module b/modules/locale/locale.module index ac4a96eb4..1f4214c58 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -341,12 +341,14 @@ function locale_theme() { * A string to look up translation for. If omitted, all the * cached strings will be returned in all languages already * used on the page. + * @param $context + * The context of this string. * @param $langcode * Language code to use for the lookup. * @param $reset * Set to TRUE to reset the in-memory cache. */ -function locale($string = NULL, $langcode = NULL, $reset = FALSE) { +function locale($string = NULL, $context = NULL, $langcode = NULL, $reset = FALSE) { global $language; static $locale_t; @@ -377,9 +379,9 @@ function locale($string = NULL, $langcode = NULL, $reset = FALSE) { // Refresh database stored cache of translations for given language. // We only store short strings used in current version, to improve // performance and consume less memory. - $result = db_query("SELECT s.source, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < 75", array(':language' => $langcode, ':version' => VERSION)); + $result = db_query("SELECT s.source, s.context, t.translation, t.language FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.textgroup = 'default' AND s.version = :version AND LENGTH(s.source) < 75", array(':language' => $langcode, ':version' => VERSION)); foreach ($result as $data) { - $locale_t[$langcode][$data->source] = (empty($data->translation) ? TRUE : $data->translation); + $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation); } cache_set('locale:' . $langcode, $locale_t[$langcode]); } @@ -387,17 +389,18 @@ function locale($string = NULL, $langcode = NULL, $reset = FALSE) { } // If we have the translation cached, skip checking the database - if (!isset($locale_t[$langcode][$string])) { + if (!isset($locale_t[$langcode][$context][$string])) { // We do not have this translation cached, so get it from the DB. - $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.textgroup = 'default'", array( + $translation = db_query("SELECT s.lid, t.translation, s.version FROM {locales_source} s LEFT JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.source = :source AND s.context = :context AND s.textgroup = 'default'", array( ':language' => $langcode, ':source' => $string, + ':context' => (string) $context, ))->fetchObject(); if ($translation) { // We have the source string at least. // Cache translation string or TRUE if no translation exists. - $locale_t[$langcode][$string] = (empty($translation->translation) ? TRUE : $translation->translation); + $locale_t[$langcode][$context][$string] = (empty($translation->translation) ? TRUE : $translation->translation); if ($translation->version != VERSION) { // This is the first use of this string under current Drupal version. Save version @@ -416,17 +419,18 @@ function locale($string = NULL, $langcode = NULL, $reset = FALSE) { ->fields(array( 'location' => request_uri(), 'source' => $string, + 'context' => (string) $context, 'textgroup' => 'default', 'version' => VERSION, )) ->execute(); - $locale_t[$langcode][$string] = TRUE; + $locale_t[$langcode][$context][$string] = TRUE; // Clear locale cache so this string can be added in a later request. cache_clear_all('locale:', 'cache', TRUE); } } - return ($locale_t[$langcode][$string] === TRUE ? $string : $locale_t[$langcode][$string]); + return ($locale_t[$langcode][$context][$string] === TRUE ? $string : $locale_t[$langcode][$context][$string]); } /** diff --git a/modules/locale/locale.test b/modules/locale/locale.test index e1020a398..95ab2d998 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -212,9 +212,9 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { ); $this->drupalPost('admin/international/language/add', $edit, t('Add custom language')); // Add string. - t($name, array(), $langcode); + t($name, array(), array('langcode' => $langcode)); // Reset locale cache. - locale(NULL, NULL, TRUE); + locale(NULL, NULL, NULL, TRUE); $this->assertText($langcode, t('Language code found.')); $this->assertText($name, t('Name found.')); $this->assertText($native, t('Native found.')); @@ -251,7 +251,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $this->drupalPost(NULL, $edit, t('Save translations')); $this->assertText(t('The string has been saved.'), t('The string has been saved.')); $this->assertEqual($this->getUrl(), url('admin/international/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.')); - $this->assertTrue($name != $translation && t($name, array(), $langcode) == $translation, t('t() works.')); + $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works.')); $this->drupalPost('admin/international/translate/translate', $search, t('Filter')); // The indicator should not be here. $this->assertNoRaw($language_indicator, t('String is translated.')); @@ -344,7 +344,7 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { ); $this->drupalPost('admin/international/language/add', $edit, t('Add custom language')); // Add string. - t($name, array(), $langcode); + t($name, array(), array('langcode' => $langcode)); // Reset locale cache. $search = array( 'string' => $name, @@ -405,9 +405,9 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { ); $this->drupalPost('admin/international/language/add', $edit, t('Add custom language')); // Add string. - t($name, array(), $langcode); + t($name, array(), array('langcode' => $langcode)); // Reset locale cache. - locale(NULL, NULL, TRUE); + locale(NULL, NULL, NULL, TRUE); $this->drupalLogout(); // Search for the name. @@ -553,13 +553,9 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { */ function testStandalonePoFile() { // Try importing a .po file. - $name = tempnam(file_directory_temp(), "po_"); - file_put_contents($name, $this->getPoFile()); - $this->drupalPost('admin/international/translate/import', array( + $this->importPoFile($this->getPoFile(), array( 'langcode' => 'fr', - 'files[file]' => $name, - ), t('Import')); - unlink($name); + )); // 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.')); @@ -570,31 +566,28 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { // Ensure we were redirected correctly. $this->assertEqual($this->getUrl(), url('admin/international/translate', array('absolute' => TRUE)), t('Correct page redirection.')); + // Try importing a .po file with invalid tags in the default text group. - $name = tempnam(file_directory_temp(), "po_"); - file_put_contents($name, $this->getBadPoFile()); - $this->drupalPost('admin/international/translate/import', array( + $this->importPoFile($this->getBadPoFile(), array( 'langcode' => 'fr', - 'files[file]' => $name, - ), t('Import')); - unlink($name); + )); + // The import should have created 1 string and rejected 2. $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.')); $skip_message = format_plural(2, '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.')); + // Try importing a .po file with invalid tags in a non default text group. - $name = tempnam(file_directory_temp(), "po_"); - file_put_contents($name, $this->getBadPoFile()); - $this->drupalPost('admin/international/translate/import', array( + $this->importPoFile($this->getBadPoFile(), array( 'langcode' => 'fr', - 'files[file]' => $name, 'group' => 'custom', - ), t('Import')); - unlink($name); + )); + // The import should have created 3 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' => 3, '%update' => 0, '%delete' => 0)), t('The translation file was successfully imported.')); + // Try importing a .po file which doesn't exist. $name = $this->randomName(16); $this->drupalPost('admin/international/translate/import', array( @@ -605,16 +598,14 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { $this->assertEqual($this->getUrl(), url('admin/international/translate/import', array('absolute' => TRUE)), t('Correct page redirection.')); $this->assertText(t('File to import not found.'), t('File to import not found message.')); + // Try importing a .po file with overriding strings, and ensure existing // strings are kept. - $name = tempnam(file_directory_temp(), "po_"); - file_put_contents($name, $this->getOverwritePoFile()); - $this->drupalPost('admin/international/translate/import', array( + $this->importPoFile($this->getOverwritePoFile(), array( 'langcode' => 'fr', - 'files[file]' => $name, 'mode' => 1, // Existing strings are kept, only new strings are added. - ), t('Import')); - unlink($name); + )); + // 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. @@ -627,16 +618,14 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { $this->drupalPost('admin/international/translate/translate', $search, t('Filter')); $this->assertText(t('No strings found for your search.'), t('String not overwritten by imported string.')); + // Try importing a .po file with overriding strings, and ensure existing // strings are overwritten. - $name = tempnam(file_directory_temp(), "po_"); - file_put_contents($name, $this->getOverwritePoFile()); - $this->drupalPost('admin/international/translate/import', array( + $this->importPoFile($this->getOverwritePoFile(), array( 'langcode' => 'fr', - 'files[file]' => $name, 'mode' => 0, // Strings in the uploaded file replace existing ones, new ones are added. - ), t('Import')); - unlink($name); + )); + // 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. @@ -690,6 +679,36 @@ class LocaleImportFunctionalTest extends DrupalWebTestCase { } /** + * 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', 'context' => 'Short month name')), 'Svi.', t('Short month name context is working.')); + } + + /** + * 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_temp(), "po_"); + file_put_contents($name, $contents); + $options['files[file]'] = $name; + $this->drupalPost('admin/international/translate/import', $options, t('Import')); + unlink($name); + } + + /** * Helper function that returns a proper .po file. */ function getPoFile() { @@ -772,6 +791,32 @@ msgstr "Jour" EOF; } + /** + * Helper function that returns a .po file with context. + */ + function getPoFileWithContext() { + // Croatian (code hr) is one the the languages that have a different + // form for the full name and the abbreviated name for the month May. + return <<< EOF +msgid "" +msgstr "" +"Project-Id-Version: Drupal 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" + +msgctxt "Short month name" +msgid "May" +msgstr "Svi." +EOF; + } + + } /** |