Issue #488496 by loganfsmyth, Gábor Hojtsy: Implement missing msgctx context in JS translation for feature parity with t().
parent
f77015e645
commit
cfc35a0b3e
|
@ -42,6 +42,36 @@ define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
|
||||||
*/
|
*/
|
||||||
define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+');
|
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
|
* Translation import mode overwriting all existing translations
|
||||||
* if new translated version available.
|
* if new translated version available.
|
||||||
|
@ -1447,6 +1477,9 @@ function _locale_parse_js_file($filepath) {
|
||||||
[^\w]Drupal\s*\.\s*t\s* # match "Drupal.t" with whitespace
|
[^\w]Drupal\s*\.\s*t\s* # match "Drupal.t" with whitespace
|
||||||
\(\s* # match "(" argument list start
|
\(\s* # match "(" argument list start
|
||||||
(' . LOCALE_JS_STRING . ')\s* # capture string argument
|
(' . 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
|
[,\)] # match ")" or "," to finish
|
||||||
~sx', $file, $t_matches);
|
~sx', $file, $t_matches);
|
||||||
|
|
||||||
|
@ -1474,55 +1507,73 @@ function _locale_parse_js_file($filepath) {
|
||||||
(?:\s*\+\s*)? # match "+" with possible whitespace, for str concat
|
(?:\s*\+\s*)? # match "+" with possible whitespace, for str concat
|
||||||
)+ # match multiple because we supports concatenating strs
|
)+ # match multiple because we supports concatenating strs
|
||||||
)\s* # end capturing of plural string argument
|
)\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);
|
~sx', $file, $plural_matches);
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
|
||||||
// Loop through all matches and process them.
|
// Add strings from Drupal.t().
|
||||||
$all_matches = array_merge($plural_matches[1], $t_matches[1]);
|
foreach ($t_matches[1] as $key => $string) {
|
||||||
foreach ($all_matches as $key => $string) {
|
$matches[] = array(
|
||||||
$strings = array($string);
|
'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 there is also a plural version of this string, add it to the strings array.
|
||||||
if (isset($plural_matches[2][$key])) {
|
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) {
|
foreach ($matches as $key => $match) {
|
||||||
// Remove the quotes and string concatenations from the string.
|
// Remove the quotes and string concatenations from the string.
|
||||||
$string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1)));
|
$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 textgroup = 'default'", array(':source' => $string))->fetchObject();
|
$source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = 'default'", array(':source' => $string, ':context' => $context))->fetchObject();
|
||||||
if ($source) {
|
if ($source) {
|
||||||
// We already have this source string and now have to add the location
|
// 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.
|
// to the location column, if this file is not yet present in there.
|
||||||
$locations = preg_split('~\s*;\s*~', $source->location);
|
$locations = preg_split('~\s*;\s*~', $source->location);
|
||||||
|
|
||||||
if (!in_array($filepath, $locations)) {
|
if (!in_array($filepath, $locations)) {
|
||||||
$locations[] = $filepath;
|
$locations[] = $filepath;
|
||||||
$locations = implode('; ', $locations);
|
$locations = implode('; ', $locations);
|
||||||
|
|
||||||
// Save the new locations string to the database.
|
// Save the new locations string to the database.
|
||||||
db_update('locales_source')
|
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')
|
|
||||||
->fields(array(
|
->fields(array(
|
||||||
'location' => $filepath,
|
'location' => $locations,
|
||||||
'source' => $string,
|
|
||||||
'context' => '',
|
|
||||||
'textgroup' => 'default',
|
|
||||||
))
|
))
|
||||||
|
->condition('lid', $source->lid)
|
||||||
->execute();
|
->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,
|
||||||
|
'textgroup' => 'default',
|
||||||
|
))
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1942,11 +1993,11 @@ function _locale_rebuild_js($langcode = NULL) {
|
||||||
|
|
||||||
// Construct the array for JavaScript translations.
|
// Construct the array for JavaScript translations.
|
||||||
// Only add strings with a translation to the translations array.
|
// 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%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
|
$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%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
|
||||||
|
|
||||||
$translations = array();
|
$translations = array();
|
||||||
foreach ($result as $data) {
|
foreach ($result as $data) {
|
||||||
$translations[$data->source] = $data->translation;
|
$translations[$data->context][$data->source] = $data->translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the JavaScript file, if there are translations.
|
// Construct the JavaScript file, if there are translations.
|
||||||
|
|
|
@ -177,13 +177,21 @@ Drupal.formatString = function(str, args) {
|
||||||
* An object of replacements pairs to make after translation. Incidences
|
* An object of replacements pairs to make after translation. Incidences
|
||||||
* of any key in this array are replaced with the corresponding value.
|
* of any key in this array are replaced with the corresponding value.
|
||||||
* See Drupal.formatString().
|
* See Drupal.formatString().
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* - 'context' (defaults to the empty context): The context the source string
|
||||||
|
* belongs to.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* The translated string.
|
* 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.
|
// Fetch the localized version of the string.
|
||||||
if (Drupal.locale.strings && Drupal.locale.strings[str]) {
|
if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) {
|
||||||
str = Drupal.locale.strings[str];
|
str = Drupal.locale.strings[options.context][str];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args) {
|
if (args) {
|
||||||
|
@ -216,25 +224,27 @@ Drupal.t = function (str, args) {
|
||||||
* See Drupal.formatString().
|
* See Drupal.formatString().
|
||||||
* Note that you do not need to include @count in this array.
|
* Note that you do not need to include @count in this array.
|
||||||
* This replacement is done automatically for the plural case.
|
* This replacement is done automatically for the plural case.
|
||||||
|
* @param options
|
||||||
|
* The options to pass to the Drupal.t() function.
|
||||||
* @return
|
* @return
|
||||||
* A translated string.
|
* A translated string.
|
||||||
*/
|
*/
|
||||||
Drupal.formatPlural = function (count, singular, plural, args) {
|
Drupal.formatPlural = function (count, singular, plural, args, options) {
|
||||||
var args = args || {};
|
var args = args || {};
|
||||||
args['@count'] = count;
|
args['@count'] = count;
|
||||||
// Determine the index of the plural form.
|
// Determine the index of the plural form.
|
||||||
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
|
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return Drupal.t(singular, args);
|
return Drupal.t(singular, args, options);
|
||||||
}
|
}
|
||||||
else if (index == 1) {
|
else if (index == 1) {
|
||||||
return Drupal.t(plural, args);
|
return Drupal.t(plural, args, options);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
args['@count[' + index + ']'] = args['@count'];
|
args['@count[' + index + ']'] = args['@count'];
|
||||||
delete args['@count'];
|
delete args['@count'];
|
||||||
return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args);
|
return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -204,43 +204,65 @@ class LocaleJavascriptTranslationTest extends DrupalWebTestCase {
|
||||||
|
|
||||||
// Get all of the source strings that were found.
|
// Get all of the source strings that were found.
|
||||||
$source_strings = db_select('locales_source', 's')
|
$source_strings = db_select('locales_source', 's')
|
||||||
->fields('s', array('source', 'lid'))
|
->fields('s', array('source', 'context'))
|
||||||
->condition('s.location', $filename)
|
->condition('s.location', $filename)
|
||||||
->execute()
|
->execute()
|
||||||
->fetchAllKeyed();
|
->fetchAllKeyed();
|
||||||
|
|
||||||
// List of all strings that should be in the file.
|
// List of all strings that should be in the file.
|
||||||
$test_strings = array(
|
$test_strings = array(
|
||||||
"Standard Call t",
|
"Standard Call t" => '',
|
||||||
"Whitespace Call t",
|
"Whitespace Call t" => '',
|
||||||
|
|
||||||
"Single Quote t",
|
"Single Quote t" => '',
|
||||||
"Single Quote \\'Escaped\\' t",
|
"Single Quote \\'Escaped\\' t" => '',
|
||||||
"Single Quote Concat strings t",
|
"Single Quote Concat strings t" => '',
|
||||||
|
|
||||||
"Double Quote t",
|
"Double Quote t" => '',
|
||||||
"Double Quote \\\"Escaped\\\" t",
|
"Double Quote \\\"Escaped\\\" t" => '',
|
||||||
"Double Quote Concat strings t",
|
"Double Quote Concat strings t" => '',
|
||||||
|
|
||||||
"Standard Call plural",
|
"Context !key Args t" => "Context string",
|
||||||
"Standard Call @count plural",
|
|
||||||
"Whitespace Call plural",
|
|
||||||
"Whitespace Call @count plural",
|
|
||||||
|
|
||||||
"Single Quote plural",
|
"Context Unquoted t" => "Context string unquoted",
|
||||||
"Single Quote @count plural",
|
"Context Single Quoted t" => "Context string single quoted",
|
||||||
"Single Quote \\'Escaped\\' plural",
|
"Context Double Quoted t" => "Context string double quoted",
|
||||||
"Single Quote \\'Escaped\\' @count plural",
|
|
||||||
|
|
||||||
"Double Quote plural",
|
"Standard Call plural" => '',
|
||||||
"Double Quote @count plural",
|
"Standard Call @count plural" => '',
|
||||||
"Double Quote \\\"Escaped\\\" plural",
|
"Whitespace Call plural" => '',
|
||||||
"Double Quote \\\"Escaped\\\" @count 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.
|
// Assert that all strings were found properly.
|
||||||
foreach ($test_strings as $str) {
|
foreach ($test_strings as $str => $context) {
|
||||||
$this->assertTrue(isset($source_strings[$str]), t("Found source string: %source", array('%source' => $str)));
|
$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."));
|
$this->assertEqual(count($source_strings), count($test_strings), t("Found correct number of source strings."));
|
||||||
|
|
|
@ -16,6 +16,11 @@ Drupal.t("Double Quote t");
|
||||||
Drupal.t("Double Quote \"Escaped\" t");
|
Drupal.t("Double Quote \"Escaped\" t");
|
||||||
Drupal.t("Double Quote " + "Concat " + "strings " + "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.formatPlural(1, "Standard Call plural", "Standard Call @count plural");
|
||||||
Drupal
|
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 plural", "Double Quote @count plural");
|
||||||
Drupal.formatPlural(1, "Double Quote \"Escaped\" plural", "Double Quote \"Escaped\" @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"});
|
||||||
|
|
Loading…
Reference in New Issue