summaryrefslogtreecommitdiff
path: root/modules/locale
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-06-08 05:00:12 +0000
committerDries Buytaert <dries@buytaert.net>2009-06-08 05:00:12 +0000
commit28b2f098beb2dcf1e3b6745c65c1194d45da0691 (patch)
tree799076def12eb7e551b6e0825a0924dd0fb9630c /modules/locale
parentece9032afd035bbe4ca55b6fd671df03b96b4e35 (diff)
downloadbrdo-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.install28
-rw-r--r--modules/locale/locale.module20
-rw-r--r--modules/locale/locale.test117
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;
+ }
+
+
}
/**