Add basic sprintf implementation to JavaScript
This commit is contained in:
parent
92173ac55c
commit
b509588a28
|
@ -1,5 +1,7 @@
|
|||
import Jed from 'jed';
|
||||
|
||||
import sprintf from './sprintf';
|
||||
|
||||
/**
|
||||
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
|
||||
|
@ -67,4 +69,5 @@ export { lang };
|
|||
export { gettext as __ };
|
||||
export { ngettext as n__ };
|
||||
export { pgettext as s__ };
|
||||
export { sprintf };
|
||||
export default locale;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
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 {
|
|||
__,
|
||||
n__,
|
||||
s__,
|
||||
sprintf,
|
||||
} from '../locale';
|
||||
|
||||
export default (Vue) => {
|
||||
|
@ -37,6 +38,7 @@ export default (Vue) => {
|
|||
@returns {String} Translated context based text
|
||||
**/
|
||||
s__,
|
||||
sprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
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`.
|
|||
|
||||
### Interpolation
|
||||
|
||||
- In Ruby/HAML:
|
||||
- In Ruby/HAML (see [sprintf]):
|
||||
|
||||
```ruby
|
||||
_("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
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
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 <script>alert("malicious!")</script>');
|
||||
});
|
||||
|
||||
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>');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue