diff --git a/app/assets/javascripts/locales/i18n.js b/app/assets/javascripts/locales/i18n.js index cffc2e214ad..68657e28b68 100644 --- a/app/assets/javascripts/locales/i18n.js +++ b/app/assets/javascripts/locales/i18n.js @@ -116,7 +116,15 @@ I18n.interpolate = function(message, options) { for (var i = 0; placeholder = matches[i]; i++) { name = placeholder.replace(this.PLACEHOLDER, "$1"); - value = options[name]; + if (typeof options[name] === "string") { + // The dollar sign (`$`) is a special replace pattern, and `$&` inserts + // the matched string. Thus dollars signs need to be escaped with the + // special pattern `$$`, which inserts a single `$`. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter + value = options[name].replace(/\$/g, "$$$$"); + } else { + value = options[name]; + } if (!this.isValidNode(options, name)) { value = "[missing " + placeholder + " value]"; diff --git a/test/javascripts/lib/i18n-test.js.es6 b/test/javascripts/lib/i18n-test.js.es6 index f1c628ab25c..9655f4420bc 100644 --- a/test/javascripts/lib/i18n-test.js.es6 +++ b/test/javascripts/lib/i18n-test.js.es6 @@ -60,7 +60,8 @@ QUnit.module("lib:i18n", { days: { one: "%{count} day", other: "%{count} days" - } + }, + dollar_sign: "Hi {{description}}" } } }; @@ -223,3 +224,12 @@ QUnit.test("fallback", assert => { "falls back to English translations" ); }); + +QUnit.test("Dollar signs are properly escaped", assert => { + assert.equal( + I18n.t("dollar_sign", { + description: "$& $&" + }), + "Hi $& $&" + ); +});