summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel2011-10-26 07:38:48 (GMT)
committerNathaniel2011-10-26 07:38:48 (GMT)
commit8e967deef0e244d9dbe79e058d404a86b4b8248e (patch)
treeb5910003ec738e07f05e10d58a24243b5ce1e59e
parent9fdc47e87debec795733bd98168db923cf8efe5f (diff)
Issue #488496 by loganfsmyth: Implement missing msgctx context in JS translation for feature parity with t().
-rw-r--r--includes/locale.inc125
-rw-r--r--misc/drupal.js24
-rw-r--r--modules/locale/locale.test68
-rw-r--r--modules/locale/tests/locale_test.js11
4 files changed, 162 insertions, 66 deletions
diff --git a/includes/locale.inc b/includes/locale.inc
index 906a36e..2ce70c4 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -43,6 +43,36 @@ define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+');
/**
+ * Regular expression pattern used to match simple JS object literal.
+ *
+ * This pattern matches a basic JS object, but will fail on an object with
+ * nested objects. Used in JS file parsing for string arg processing.
+ */
+define('LOCALE_JS_OBJECT', '\{.*?\}');
+
+/**
+ * Regular expression to match an object containing a key 'context'.
+ *
+ * Pattern to match a JS object containing a 'context key' with a string value,
+ * which is captured. Will fail if there are nested objects.
+ */
+define('LOCALE_JS_OBJECT_CONTEXT', '
+ \{ # match object literal start
+ .*? # match anything, non-greedy
+ (?: # match a form of "context"
+ \'context\'
+ |
+ "context"
+ |
+ context
+ )
+ \s*:\s* # match key-value separator ":"
+ (' . LOCALE_JS_STRING . ') # match context string
+ .*? # match anything, non-greedy
+ \} # match end of object literal
+');
+
+/**
* Translation import mode overwriting all existing translations
* if new translated version available.
*/
@@ -553,6 +583,9 @@ function _locale_parse_js_file($filepath) {
[^\w]Drupal\s*\.\s*t\s* # match "Drupal.t" with whitespace
\(\s* # match "(" argument list start
(' . LOCALE_JS_STRING . ')\s* # capture string argument
+ (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture str args
+ (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*) # optionally capture context
+ ?)? # close optional args
[,\)] # match ")" or "," to finish
~sx', $file, $t_matches);
@@ -580,54 +613,74 @@ function _locale_parse_js_file($filepath) {
(?:\s*\+\s*)? # match "+" with possible whitespace, for str concat
)+ # match multiple because we supports concatenating strs
)\s* # end capturing of plural string argument
+ (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture string args
+ (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*)? # optionally capture context
+ )?
[,\)]
~sx', $file, $plural_matches);
+ $matches = array();
- // Loop through all matches and process them.
- $all_matches = array_merge($plural_matches[1], $t_matches[1]);
- foreach ($all_matches as $key => $string) {
- $strings = array($string);
+ // Add strings from Drupal.t().
+ foreach ($t_matches[1] as $key => $string) {
+ $matches[] = array(
+ 'string' => $string,
+ 'context' => $t_matches[2][$key],
+ );
+ }
+
+ // Add string from Drupal.formatPlural().
+ foreach ($plural_matches[1] as $key => $string) {
+ $matches[] = array(
+ 'string' => $string,
+ 'context' => $plural_matches[3][$key],
+ );
// If there is also a plural version of this string, add it to the strings array.
if (isset($plural_matches[2][$key])) {
- $strings[] = $plural_matches[2][$key];
+ $matches[] = array(
+ 'string' => $plural_matches[2][$key],
+ 'context' => $plural_matches[3][$key],
+ );
}
+ }
- foreach ($strings as $key => $string) {
- // Remove the quotes and string concatenations from the string.
- $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1)));
-
- $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source", array(':source' => $string))->fetchObject();
- if ($source) {
- // We already have this source string and now have to add the location
- // to the location column, if this file is not yet present in there.
- $locations = preg_split('~\s*;\s*~', $source->location);
-
- if (!in_array($filepath, $locations)) {
- $locations[] = $filepath;
- $locations = implode('; ', $locations);
-
- // Save the new locations string to the database.
- db_update('locales_source')
- ->fields(array(
- 'location' => $locations,
- ))
- ->condition('lid', $source->lid)
- ->execute();
- }
- }
- else {
- // We don't have the source string yet, thus we insert it into the database.
- db_insert('locales_source')
+ // Loop through all matches and process them.
+ foreach ($matches as $key => $match) {
+
+ // Remove the quotes and string concatenations from the string and context.
+ $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['string'], 1, -1)));
+ $context = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['context'], 1, -1)));
+
+ $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND context = :context", array(':source' => $string, ':context' => $context))->fetchObject();
+ if ($source) {
+ // We already have this source string and now have to add the location
+ // to the location column, if this file is not yet present in there.
+ $locations = preg_split('~\s*;\s*~', $source->location);
+
+ if (!in_array($filepath, $locations)) {
+ $locations[] = $filepath;
+ $locations = implode('; ', $locations);
+
+ // Save the new locations string to the database.
+ db_update('locales_source')
->fields(array(
- 'location' => $filepath,
- 'source' => $string,
- 'context' => '',
+ 'location' => $locations,
))
+ ->condition('lid', $source->lid)
->execute();
}
}
+ else {
+ // We don't have the source string yet, thus we insert it into the database.
+ db_insert('locales_source')
+ ->fields(array(
+ 'location' => $filepath,
+ 'source' => $string,
+ 'context' => $context,
+ ))
+ ->execute();
+ }
}
}
@@ -683,11 +736,11 @@ function _locale_rebuild_js($langcode = NULL) {
// Construct the array for JavaScript translations.
// Only add strings with a translation to the translations array.
- $result = db_query("SELECT s.lid, s.source, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%'", array(':language' => $language->language));
+ $result = db_query("SELECT s.lid, s.source, s.context, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%'", array(':language' => $language->language));
$translations = array();
foreach ($result as $data) {
- $translations[$data->source] = $data->translation;
+ $translations[$data->context][$data->source] = $data->translation;
}
// Construct the JavaScript file, if there are translations.
diff --git a/misc/drupal.js b/misc/drupal.js
index 7e2cc4d..7ae737c 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -177,13 +177,21 @@ Drupal.formatString = function(str, args) {
* An object of replacements pairs to make after translation. Incidences
* of any key in this array are replaced with the corresponding value.
* See Drupal.formatString().
+ *
+ * @param options
+ * - 'context' (defaults to the empty context): The context the source string
+ * belongs to.
+ *
* @return
* The translated string.
*/
-Drupal.t = function (str, args) {
+Drupal.t = function (str, args, options) {
+ options = options || {};
+ options.context = options.context || '';
+
// Fetch the localized version of the string.
- if (Drupal.locale.strings && Drupal.locale.strings[str]) {
- str = Drupal.locale.strings[str];
+ if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) {
+ str = Drupal.locale.strings[options.context][str];
}
if (args) {
@@ -216,25 +224,27 @@ Drupal.t = function (str, args) {
* See Drupal.formatString().
* Note that you do not need to include @count in this array.
* This replacement is done automatically for the plural case.
+ * @param options
+ * The options to pass to the Drupal.t() function.
* @return
* A translated string.
*/
-Drupal.formatPlural = function (count, singular, plural, args) {
+Drupal.formatPlural = function (count, singular, plural, args, options) {
var args = args || {};
args['@count'] = count;
// Determine the index of the plural form.
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
if (index == 0) {
- return Drupal.t(singular, args);
+ return Drupal.t(singular, args, options);
}
else if (index == 1) {
- return Drupal.t(plural, args);
+ return Drupal.t(plural, args, options);
}
else {
args['@count[' + index + ']'] = args['@count'];
delete args['@count'];
- return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args);
+ return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options);
}
};
diff --git a/modules/locale/locale.test b/modules/locale/locale.test
index b94f565..4c225c6 100644
--- a/modules/locale/locale.test
+++ b/modules/locale/locale.test
@@ -200,43 +200,65 @@ class LocaleJavascriptTranslationTest extends DrupalWebTestCase {
// Get all of the source strings that were found.
$source_strings = db_select('locales_source', 's')
- ->fields('s', array('source', 'lid'))
+ ->fields('s', array('source', 'context'))
->condition('s.location', $filename)
->execute()
->fetchAllKeyed();
// List of all strings that should be in the file.
$test_strings = array(
- "Standard Call t",
- "Whitespace Call t",
+ "Standard Call t" => '',
+ "Whitespace Call t" => '',
- "Single Quote t",
- "Single Quote \\'Escaped\\' t",
- "Single Quote Concat strings t",
+ "Single Quote t" => '',
+ "Single Quote \\'Escaped\\' t" => '',
+ "Single Quote Concat strings t" => '',
- "Double Quote t",
- "Double Quote \\\"Escaped\\\" t",
- "Double Quote Concat strings t",
+ "Double Quote t" => '',
+ "Double Quote \\\"Escaped\\\" t" => '',
+ "Double Quote Concat strings t" => '',
- "Standard Call plural",
- "Standard Call @count plural",
- "Whitespace Call plural",
- "Whitespace Call @count plural",
+ "Context !key Args t" => "Context string",
- "Single Quote plural",
- "Single Quote @count plural",
- "Single Quote \\'Escaped\\' plural",
- "Single Quote \\'Escaped\\' @count plural",
+ "Context Unquoted t" => "Context string unquoted",
+ "Context Single Quoted t" => "Context string single quoted",
+ "Context Double Quoted t" => "Context string double quoted",
- "Double Quote plural",
- "Double Quote @count plural",
- "Double Quote \\\"Escaped\\\" plural",
- "Double Quote \\\"Escaped\\\" @count plural",
+ "Standard Call plural" => '',
+ "Standard Call @count plural" => '',
+ "Whitespace Call plural" => '',
+ "Whitespace Call @count plural" => '',
+
+ "Single Quote plural" => '',
+ "Single Quote @count plural" => '',
+ "Single Quote \\'Escaped\\' plural" => '',
+ "Single Quote \\'Escaped\\' @count plural" => '',
+
+ "Double Quote plural" => '',
+ "Double Quote @count plural" => '',
+ "Double Quote \\\"Escaped\\\" plural" => '',
+ "Double Quote \\\"Escaped\\\" @count plural" => '',
+
+ "Context !key Args plural" => "Context string",
+ "Context !key Args @count plural" => "Context string",
+
+ "Context Unquoted plural" => "Context string unquoted",
+ "Context Unquoted @count plural" => "Context string unquoted",
+ "Context Single Quoted plural" => "Context string single quoted",
+ "Context Single Quoted @count plural" => "Context string single quoted",
+ "Context Double Quoted plural" => "Context string double quoted",
+ "Context Double Quoted @count plural" => "Context string double quoted",
);
// Assert that all strings were found properly.
- foreach ($test_strings as $str) {
- $this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", array('%source' => $str)));
+ foreach ($test_strings as $str => $context) {
+ $args = array('%source' => $str, '%context' => $context);
+
+ // Make sure that the string was found in the file.
+ $this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", $args));
+
+ // Make sure that the proper context was matched.
+ $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? t("Context for %source is %context", $args) : t("Context for %source is blank", $args));
}
$this->assertEqual(count($source_strings), count($test_strings), t("Found correct number of source strings."));
diff --git a/modules/locale/tests/locale_test.js b/modules/locale/tests/locale_test.js
index 251d115..515bb34 100644
--- a/modules/locale/tests/locale_test.js
+++ b/modules/locale/tests/locale_test.js
@@ -16,6 +16,11 @@ Drupal.t("Double Quote t");
Drupal.t("Double Quote \"Escaped\" t");
Drupal.t("Double Quote " + "Concat " + "strings " + "t");
+Drupal.t("Context Unquoted t", {}, {context: "Context string unquoted"});
+Drupal.t("Context Single Quoted t", {}, {'context': "Context string single quoted"});
+Drupal.t("Context Double Quoted t", {}, {"context": "Context string double quoted"});
+
+Drupal.t("Context !key Args t", {'!key': 'value'}, {context: "Context string"});
Drupal.formatPlural(1, "Standard Call plural", "Standard Call @count plural");
Drupal
@@ -33,3 +38,9 @@ Drupal.formatPlural(1, 'Single Quote \'Escaped\' plural', 'Single Quote \'Escape
Drupal.formatPlural(1, "Double Quote plural", "Double Quote @count plural");
Drupal.formatPlural(1, "Double Quote \"Escaped\" plural", "Double Quote \"Escaped\" @count plural");
+
+Drupal.formatPlural(1, "Context Unquoted plural", "Context Unquoted @count plural", {}, {context: "Context string unquoted"});
+Drupal.formatPlural(1, "Context Single Quoted plural", "Context Single Quoted @count plural", {}, {'context': "Context string single quoted"});
+Drupal.formatPlural(1, "Context Double Quoted plural", "Context Double Quoted @count plural", {}, {"context": "Context string double quoted"});
+
+Drupal.formatPlural(1, "Context !key Args plural", "Context !key Args @count plural", {'!key': 'value'}, {context: "Context string"});