summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2014-02-03 14:25:26 (GMT)
committerNathaniel Catchpole2014-02-03 14:25:26 (GMT)
commitb3e112fe67d17e37eb7fca0de8e7fa955ab372ae (patch)
tree634f4602b29f3c7e2fcf962cb429478cfb7e76d9
parentadedd24455f3d8752a668c4cc9951cafe7c238c8 (diff)
Issue #2182265 by Sweetchuck, nod_: Javascript file translations are several bugs away from working at all.
-rw-r--r--core/misc/drupal.js111
-rw-r--r--core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php76
-rw-r--r--core/modules/locale/locale.module60
3 files changed, 150 insertions, 97 deletions
diff --git a/core/misc/drupal.js b/core/misc/drupal.js
index 1905976..3435dc0 100644
--- a/core/misc/drupal.js
+++ b/core/misc/drupal.js
@@ -192,9 +192,9 @@ if (window.jQuery) {
/**
* Replace placeholders with sanitized values in a string.
*
- * @param str
+ * @param {String} str
* A string with placeholders.
- * @param args
+ * @param {Object} args
* An object of replacements pairs to make. Incidences of any key in this
* array are replaced with the corresponding value. Based on the first
* character of the key, the value is escaped and/or themed:
@@ -203,6 +203,9 @@ if (window.jQuery) {
* - %variable: escape text and theme as a placeholder for user-submitted
* content (checkPlain + Drupal.theme('placeholder'))
*
+ * @return {String}
+ * Returns the replaced string.
+ *
* @see Drupal.t()
* @ingroup sanitization
*/
@@ -223,10 +226,61 @@ if (window.jQuery) {
args[key] = Drupal.theme('placeholder', args[key]);
break;
}
- str = str.replace(key, args[key]);
}
}
- return str;
+
+ return Drupal.stringReplace(str, args, null);
+ };
+
+ /**
+ * Replace substring.
+ *
+ * The longest keys will be tried first. Once a substring has been replaced,
+ * its new value will not be searched again.
+ *
+ * @param {String} str
+ * A string with placeholders.
+ * @param {Object} args
+ * Key-value pairs.
+ * @param {Array|null} keys
+ * Array of keys from the "args". Internal use only.
+ *
+ * @return {String}
+ * Returns the replaced string.
+ */
+ Drupal.stringReplace = function (str, args, keys) {
+ if (str.length === 0) {
+ return str;
+ }
+
+ // If the array of keys is not passed then collect the keys from the args.
+ if (!Array.isArray(keys)) {
+ keys = [];
+ for (var k in args) {
+ if (args.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+
+ // Order the keys by the character length. The shortest one is the first.
+ keys.sort(function (a, b) { return a.length - b.length; });
+ }
+
+ if (keys.length === 0) {
+ return str;
+ }
+
+ // Take next longest one from the end.
+ var key = keys.pop();
+ var fragments = str.split(key);
+
+ if (keys.length) {
+ for (var i = 0; i < fragments.length; i++) {
+ fragments[i] = Drupal.stringReplace(fragments[i], args, keys);
+ }
+ }
+
+ return fragments.join(args[key]);
};
/**
@@ -273,49 +327,48 @@ if (window.jQuery) {
/**
* Format a string containing a count of items.
*
- * This function ensures that the string is pluralized correctly. Since Drupal.t() is
- * called by this function, make sure not to pass already-localized strings to it.
+ * This function ensures that the string is pluralized correctly. Since
+ * Drupal.t() is called by this function, make sure not to pass
+ * already-localized strings to it.
*
- * See the documentation of the server-side format_plural() function for further details.
+ * See the documentation of the server-side format_plural() function for
+ * further details.
*
- * @param count
+ * @param {Number} count
* The item count to display.
- * @param singular
+ * @param {String} singular
* The string for the singular case. Please make sure it is clear this is
- * singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
- * Do not use @count in the singular string.
- * @param plural
- * The string for the plural case. Please make sure it is clear this is plural,
- * to ease translation. Use @count in place of the item count, as in "@count
- * new comments".
- * @param args
+ * singular, to ease translation (e.g. use "1 new comment" instead of "1
+ * new"). Do not use @count in the singular string.
+ * @param {String} plural
+ * The string for the plural case. Please make sure it is clear this is
+ * plural, to ease translation. Use @count in place of the item count, as in
+ * "@count new comments".
+ * @param {Object} 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().
* Note that you do not need to include @count in this array.
* This replacement is done automatically for the plural case.
- * @param options
+ * @param {Object} options
* The options to pass to the Drupal.t() function.
- * @return
+ *
+ * @return {String}
* A translated string.
*/
Drupal.formatPlural = function (count, singular, plural, args, options) {
args = args || {};
args['@count'] = count;
+
+ var pluralDelimiter = Drupal.locale.pluralDelimiter;
+
// Determine the index of the plural form.
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] === 1) ? 0 : 1);
+ var translations = Drupal
+ .t(singular + pluralDelimiter + plural, args, options)
+ .split(pluralDelimiter);
- if (index === 0) {
- return Drupal.t(singular, args, options);
- }
- else if (index === 1) {
- return Drupal.t(plural, args, options);
- }
- else {
- args['@count[' + index + ']'] = args['@count'];
- delete args['@count'];
- return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options);
- }
+ return translations[index];
};
/**
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
index 3d43b3d..8356693 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleJavascriptTranslation.php
@@ -31,7 +31,6 @@ class LocaleJavascriptTranslation extends WebTestBase {
}
function testFileParsing() {
-
$filename = drupal_get_path('module', 'locale') . '/tests/locale_test.js';
// Parse the file to look for source strings.
@@ -44,52 +43,46 @@ class LocaleJavascriptTranslation extends WebTestBase {
'type' => 'javascript',
'name' => $filename,
));
+
+ $source_strings = array();
foreach ($strings as $string) {
$source_strings[$string->source] = $string->context;
}
+
+ $etx = LOCALE_PLURAL_DELIMITER;
// 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 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 Concat strings t" => '',
-
- "Context !key Args t" => "Context string",
-
- "Context Unquoted t" => "Context string unquoted",
- "Context Single Quoted t" => "Context string single quoted",
- "Context Double Quoted t" => "Context string double quoted",
-
- "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",
+ 'Double Quote Concat strings t' => '',
+
+ 'Context !key Args t' => 'Context string',
+
+ 'Context Unquoted t' => 'Context string unquoted',
+ 'Context Single Quoted t' => 'Context string single quoted',
+ 'Context Double Quoted t' => 'Context string double quoted',
+
+ "Standard Call plural{$etx}Standard Call @count plural" => '',
+ "Whitespace Call plural{$etx}Whitespace Call @count plural" => '',
+
+ "Single Quote plural{$etx}Single Quote @count plural" => '',
+ "Single Quote \\'Escaped\\' plural{$etx}Single Quote \\'Escaped\\' @count plural" => '',
+
+ "Double Quote plural{$etx}Double Quote @count plural" => '',
+ "Double Quote \\\"Escaped\\\" plural{$etx}Double Quote \\\"Escaped\\\" @count plural" => '',
+
+ "Context !key Args plural{$etx}Context !key Args @count plural" => 'Context string',
+
+ "Context Unquoted plural{$etx}Context Unquoted @count plural" => 'Context string unquoted',
+ "Context Single Quoted plural{$etx}Context Single Quoted @count plural" => 'Context string single quoted',
+ "Context Double Quoted plural{$etx}Context Double Quoted @count plural" => 'Context string double quoted',
);
// Assert that all strings were found properly.
@@ -97,12 +90,13 @@ class LocaleJavascriptTranslation extends WebTestBase {
$args = array('%source' => $str, '%context' => $context);
// Make sure that the string was found in the file.
- $this->assertTrue(isset($source_strings[$str]), String::format("Found source string: %source", $args));
+ $this->assertTrue(isset($source_strings[$str]), String::format('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 ? String::format("Context for %source is %context", $args) : String::format("Context for %source is blank", $args));
+ $message = $context ? String::format('Context for %source is %context', $args) : String::format('Context for %source is blank', $args);
+ $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, $message);
}
- $this->assertEqual(count($source_strings), count($test_strings), "Found correct number of source strings.");
+ $this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.');
}
}
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 21748ae..8386be5 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -10,6 +10,7 @@
* object files are supported.
*/
+use Drupal\Component\Utility\Json;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\Language;
use Drupal\Component\Utility\Crypt;
@@ -644,7 +645,7 @@ function locale_js_translate(array $files = array()) {
}
// Add the translation JavaScript file to the page.
- $locale_javascripts = \Drupal::state()->get('translation.javascript') ?: array();
+ $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: array();
$translation_file = NULL;
if (!empty($files) && !empty($locale_javascripts[$language_interface->id])) {
// Add the translation JavaScript file to the page.
@@ -1168,6 +1169,19 @@ function _locale_refresh_configuration(array $langcodes, array $lids) {
}
/**
+ * Removes the quotes and string concatenations from the string.
+ *
+ * @param string $string
+ * Single or double quoted strings, optionally concatenated by plus (+) sign.
+ *
+ * @return string
+ * String with leading and trailing quotes removed.
+ */
+function _locale_strip_quotes($string) {
+ return implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1)));
+}
+
+/**
* Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
* Drupal.formatPlural() and inserts them into the database.
*
@@ -1241,43 +1255,28 @@ function _locale_parse_js_file($filepath) {
// Add strings from Drupal.t().
foreach ($t_matches[1] as $key => $string) {
$matches[] = array(
- 'string' => $string,
- 'context' => $t_matches[2][$key],
+ 'source' => _locale_strip_quotes($string),
+ 'context' => _locale_strip_quotes($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],
+ 'source' => _locale_strip_quotes($string) . LOCALE_PLURAL_DELIMITER . _locale_strip_quotes($plural_matches[2][$key]),
+ 'context' => _locale_strip_quotes($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])) {
- $matches[] = array(
- 'string' => $plural_matches[2][$key],
- 'context' => $plural_matches[3][$key],
- );
- }
}
// Loop through all matches and process them.
foreach ($matches as $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 = \Drupal::service('locale.storage')->findString(array('source' => $string, 'context' => $context));
+ $source = \Drupal::service('locale.storage')->findString($match);
if (!$source) {
// We don't have the source string yet, thus we insert it into the database.
- $source = \Drupal::service('locale.storage')->createString(array(
- 'source' => $string,
- 'context' => $context,
- ));
+ $source = \Drupal::service('locale.storage')->createString($match);
}
+
// Besides adding the location this will tag it for current version.
$source->addLocation('javascript', $filepath);
$source->save();
@@ -1322,6 +1321,9 @@ function _locale_invalidate_js($langcode = NULL) {
*
* @param $langcode
* The language, the translation file should be (re)created for.
+ *
+ * @return bool
+ * TRUE if translation file exists, FALSE otherwise.
*/
function _locale_rebuild_js($langcode = NULL) {
$config = \Drupal::config('locale.settings');
@@ -1350,14 +1352,18 @@ function _locale_rebuild_js($langcode = NULL) {
$data_hash = NULL;
$data = $status = '';
if (!empty($translations)) {
+ $data = array(
+ 'pluralDelimiter: ' . Json::encode(LOCALE_PLURAL_DELIMITER),
+ );
- $data = "Drupal.locale = { ";
$locale_plurals = \Drupal::state()->get('locale.translation.plurals') ?: array();
- if (!empty($locale_plurals[$language->id])) {
- $data .= "'pluralFormula': function (\$n) { return Number({$locale_plurals[$language->id]['formula']}); }, ";
+ if (!empty($locale_plurals[$language->id]['formula'])) {
+ $data[] = "pluralFormula: function (\$n) { return Number({$locale_plurals[$language->id]['formula']}); }";
}
- $data .= "'strings': " . drupal_json_encode($translations) . " };";
+ $data[] = 'strings: ' . Json::encode($translations);
+
+ $data = 'Drupal.locale = { ' . implode(', ', $data) . ' };';
$data_hash = Crypt::hashBase64($data);
}