diff --git a/app/assets/javascripts/locale/sprintf.js b/app/assets/javascripts/locale/sprintf.js index c8c6b51f374ec4d528d4d4a3e1fdf0db255122c2..12df67670f9bc012f4a12f1c6445b304d5913764 100644 --- a/app/assets/javascripts/locale/sprintf.js +++ b/app/assets/javascripts/locale/sprintf.js @@ -22,7 +22,9 @@ export default (input, parameters, escapeParameters = true) => { mappedParameters.forEach((key, parameterName) => { const parameterValue = mappedParameters.get(parameterName); const escapedParameterValue = escapeParameters ? escape(parameterValue) : parameterValue; - output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), escapedParameterValue); + // Pass the param value as a function to ignore special replacement patterns like $` and $'. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#syntax + output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), () => escapedParameterValue); }); } diff --git a/spec/frontend/locale/sprintf_spec.js b/spec/frontend/locale/sprintf_spec.js index e0d0e117ea433f6219a8e0951c326ff4fecc5df5..a7e245e2b7864febfbb29bfe5163d6d596641a90 100644 --- a/spec/frontend/locale/sprintf_spec.js +++ b/spec/frontend/locale/sprintf_spec.js @@ -84,5 +84,16 @@ describe('locale', () => { expect(output).toBe('contains duplicated 15%'); }); }); + + describe('ignores special replacements in the input', () => { + it.each(['$$', '$&', '$`', `$'`])('replacement "%s" is ignored', (replacement) => { + const input = 'My odd %{replacement} is preserved'; + + const parameters = { replacement }; + + const output = sprintf(input, parameters, false); + expect(output).toBe(`My odd ${replacement} is preserved`); + }); + }); }); });