Add basic sprintf implementation to JavaScript

This commit is contained in:
Winnie Hellmann 2017-09-26 11:46:59 +02:00
parent 92173ac55c
commit b509588a28
6 changed files with 119 additions and 2 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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,
},
});
};

View File

@ -0,0 +1,5 @@
---
title: Add basic sprintf implementation to JavaScript
merge_request: 14506
author:
type: other

View File

@ -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

View File

@ -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 &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>');
});
});
});