Commit b509588a authored by Winnie Hellmann's avatar Winnie Hellmann

Add basic sprintf implementation to JavaScript

parent 92173ac5
import Jed from 'jed'; import Jed from 'jed';
import sprintf from './sprintf';
/** /**
This is required to require all the translation folders in the current directory This is required to require all the translation folders in the current directory
this saves us having to do this manually & keep up to date with new languages this saves us having to do this manually & keep up to date with new languages
...@@ -67,4 +69,5 @@ export { lang }; ...@@ -67,4 +69,5 @@ export { lang };
export { gettext as __ }; export { gettext as __ };
export { ngettext as n__ }; export { ngettext as n__ };
export { pgettext as s__ }; export { pgettext as s__ };
export { sprintf };
export default locale; export default locale;
import _ from 'underscore';
/**
Very limited implementation of sprintf supporting only named parameters.
@param input (translated) text with parameters (e.g. '%{num_users} users use us')
@param parameters object mapping parameter names to values (e.g. { num_users: 5 })
@param escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape)
@returns {String} the text with parameters replaces (e.g. '5 users use us')
@see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
@see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
**/
export default (input, parameters, escapeParameters = true) => {
let output = input;
if (parameters) {
Object.keys(parameters).forEach((parameterName) => {
const parameterValue = parameters[parameterName];
const escapedParameterValue = escapeParameters ? _.escape(parameterValue) : parameterValue;
output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), escapedParameterValue);
});
}
return output;
}
...@@ -2,6 +2,7 @@ import { ...@@ -2,6 +2,7 @@ import {
__, __,
n__, n__,
s__, s__,
sprintf,
} from '../locale'; } from '../locale';
export default (Vue) => { export default (Vue) => {
...@@ -37,6 +38,7 @@ export default (Vue) => { ...@@ -37,6 +38,7 @@ export default (Vue) => {
@returns {String} Translated context based text @returns {String} Translated context based text
**/ **/
s__, s__,
sprintf,
}, },
}); });
}; };
---
title: Add basic sprintf implementation to JavaScript
merge_request: 14506
author:
type: other
...@@ -183,13 +183,20 @@ aren't in the message with id `1 pipeline`. ...@@ -183,13 +183,20 @@ aren't in the message with id `1 pipeline`.
### Interpolation ### Interpolation
- In Ruby/HAML: - In Ruby/HAML (see [sprintf]):
```ruby ```ruby
_("Hello %{name}") % { name: 'Joe' } _("Hello %{name}") % { name: 'Joe' }
``` ```
- In JavaScript: Not supported at this moment. - In JavaScript: Only named parameters are supported (see also [#37992]):
```javascript
__("Hello %{name}") % { name: 'Joe' }
```
[sprintf]: http://ruby-doc.org/core/Kernel.html#method-i-sprintf
[#37992]: https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
### Plurals ### Plurals
......
import sprintf from '~/locale/sprintf';
describe('locale', () => {
describe('sprintf', () => {
it('does not modify string without parameters', () => {
const input = 'No parameters';
const output = sprintf(input);
expect(output).toBe(input);
});
it('ignores extraneous parameters', () => {
const input = 'No parameters';
const output = sprintf(input, { ignore: 'this' });
expect(output).toBe(input);
});
it('ignores extraneous placeholders', () => {
const input = 'No %{parameters}';
const output = sprintf(input);
expect(output).toBe(input);
});
it('replaces parameters', () => {
const input = '%{name} has %{count} parameters';
const parameters = {
name: 'this',
count: 2,
};
const output = sprintf(input, parameters);
expect(output).toBe('this has 2 parameters');
});
it('replaces multiple occurrences', () => {
const input = 'to %{verb} or not to %{verb}';
const parameters = {
verb: 'be',
};
const output = sprintf(input, parameters);
expect(output).toBe('to be or not to be');
});
it('escapes parameters', () => {
const input = 'contains %{userContent}';
const parameters = {
userContent: '<script>alert("malicious!")</script>',
};
const output = sprintf(input, parameters);
expect(output).toBe('contains &lt;script&gt;alert(&quot;malicious!&quot;)&lt;/script&gt;');
});
it('does not escape parameters for escapeParameters = false', () => {
const input = 'contains %{safeContent}';
const parameters = {
safeContent: '<strong>bold attempt</strong>',
};
const output = sprintf(input, parameters, false);
expect(output).toBe('contains <strong>bold attempt</strong>');
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment