Extend lang with optional format and placeholders
This commit is contained in:
parent
54b25da4b7
commit
ca2edfa08b
@ -17,6 +17,8 @@
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import uUtils from './utils.js';
|
||||
|
||||
export default class uLang {
|
||||
constructor() {
|
||||
this.strings = {};
|
||||
@ -35,10 +37,18 @@ export default class uLang {
|
||||
}
|
||||
}
|
||||
|
||||
_(name) {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {...(string|number)=} params Optional parameters
|
||||
* @return {string}
|
||||
*/
|
||||
_(name, ...params) {
|
||||
if (typeof this.strings[name] === 'undefined') {
|
||||
throw new Error('Unknown localized string');
|
||||
}
|
||||
if (params.length) {
|
||||
return uUtils.sprintf(this.strings[name], ...params);
|
||||
}
|
||||
return this.strings[name];
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ export default class GoogleMapsApi {
|
||||
};
|
||||
window.gm_authFailure = () => {
|
||||
GoogleMapsApi.authError = true;
|
||||
let message = uUtils.sprintf($._('apifailure'), 'Google Maps');
|
||||
let message = $._('apifailure', 'Google Maps');
|
||||
message += '<br><br>' + $._('gmauthfailure');
|
||||
message += '<br><br>' + $._('gmapilink');
|
||||
if (GoogleMapsApi.gmInitialized) {
|
||||
|
@ -90,7 +90,7 @@ export default class MapViewModel extends ViewModel {
|
||||
this.api.init()
|
||||
.then(() => this.onReady())
|
||||
.catch((e) => {
|
||||
let txt = uUtils.sprintf($._('apifailure'), apiName);
|
||||
let txt = $._('apifailure', apiName);
|
||||
if (e && e.message) {
|
||||
txt += ` (${e.message})`;
|
||||
}
|
||||
@ -181,7 +181,7 @@ export default class MapViewModel extends ViewModel {
|
||||
${(pos.altitude !== null) ? `<img class="icon" alt="${$._('altitude')}" title="${$._('altitude')}" src="images/altitude_dark.svg">${$.getLocaleAltitude(pos.altitude, true)}<br>` : ''}
|
||||
${(pos.accuracy !== null) ? `<img class="icon" alt="${$._('accuracy')}" title="${$._('accuracy')}" src="images/accuracy_dark.svg">${$.getLocaleAccuracy(pos.accuracy, true)}${provider}<br>` : ''}
|
||||
</div>${stats}</div>
|
||||
<div id="pfooter">${uUtils.sprintf($._('pointof'), id + 1, count)}</div>
|
||||
<div id="pfooter">${$._('pointof', id + 1, count)}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ export default class TrackViewModel extends ViewModel {
|
||||
const form = this.importEl.parentElement;
|
||||
const sizeMax = form.elements['MAX_FILE_SIZE'].value;
|
||||
if (this.importEl.files && this.importEl.files.length === 1 && this.importEl.files[0].size > sizeMax) {
|
||||
uUtils.error(uUtils.sprintf($._('isizefailure'), sizeMax));
|
||||
uUtils.error($._('isizefailure', sizeMax));
|
||||
return;
|
||||
}
|
||||
if (!auth.isAuthenticated) {
|
||||
@ -169,7 +169,7 @@ export default class TrackViewModel extends ViewModel {
|
||||
.then((trackList) => {
|
||||
if (trackList.length) {
|
||||
if (trackList.length > 1) {
|
||||
alert(uUtils.sprintf($._('imultiple'), trackList.length));
|
||||
alert($._('imultiple', trackList.length));
|
||||
}
|
||||
this.model.trackList = trackList.concat(this.model.trackList);
|
||||
this.model.currentTrackId = trackList[0].listValue;
|
||||
|
@ -41,16 +41,23 @@ export default class uUtils {
|
||||
* @param {...(string|number)=} params Optional parameters
|
||||
* @returns {string}
|
||||
*/
|
||||
static sprintf(fmt, params) { // eslint-disable-line no-unused-vars
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
const format = args.shift();
|
||||
static sprintf(fmt, ...params) {
|
||||
let i = 0;
|
||||
return format.replace(/%%|%s|%d/g, (match) => {
|
||||
const ret = fmt.replace(/%%|%s|%d/g, (match) => {
|
||||
if (match === '%%') {
|
||||
return '%';
|
||||
} else if (match === '%d' && isNaN(params[i])) {
|
||||
throw new Error(`Wrong format specifier ${match} for ${params[i]} argument`);
|
||||
}
|
||||
return (typeof args[i] !== 'undefined') ? args[i++] : match;
|
||||
if (typeof params[i] === 'undefined') {
|
||||
throw new Error(`Missing argument for format specifier ${match}`);
|
||||
}
|
||||
return params[i++];
|
||||
});
|
||||
if (i < params.length) {
|
||||
throw new Error(`Unused argument for format specifier ${fmt}`);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,7 @@ describe('Lang tests', () => {
|
||||
};
|
||||
mockStrings = {
|
||||
string1: 'łańcuch1',
|
||||
placeholders: '%s : %d',
|
||||
units: 'jp',
|
||||
unitd: 'jo',
|
||||
unitdm: 'jo / 1000',
|
||||
@ -66,6 +67,15 @@ describe('Lang tests', () => {
|
||||
expect(lang._('string1')).toBe(mockStrings.string1);
|
||||
});
|
||||
|
||||
it('should return localized string with replaced placeholders', () => {
|
||||
// when
|
||||
lang.init(mockConfig, mockStrings);
|
||||
const p1 = 'str';
|
||||
const p2 = 4;
|
||||
// then
|
||||
expect(lang._('placeholders', p1, p2)).toBe(`${p1} : ${p2}`);
|
||||
});
|
||||
|
||||
it('should throw error on unknown string', () => {
|
||||
// when
|
||||
lang.init(mockConfig, mockStrings);
|
||||
|
@ -257,7 +257,6 @@ describe('MapViewModel tests', () => {
|
||||
it('should get popup html content', () => {
|
||||
// given
|
||||
const id = 0;
|
||||
spyOn(uUtils, 'sprintf');
|
||||
state.currentTrack = TrackFactory.getTrack(2);
|
||||
// when
|
||||
const html = vm.getPopupHtml(id);
|
||||
@ -265,8 +264,8 @@ describe('MapViewModel tests', () => {
|
||||
// then
|
||||
expect(element).toBeInstanceOf(HTMLDivElement);
|
||||
expect(element.id).toBe('popup');
|
||||
expect(uUtils.sprintf.calls.mostRecent().args[1]).toBe(id + 1);
|
||||
expect(uUtils.sprintf.calls.mostRecent().args[2]).toBe(state.currentTrack.length);
|
||||
expect(lang._.calls.mostRecent().args[1]).toBe(id + 1);
|
||||
expect(lang._.calls.mostRecent().args[2]).toBe(state.currentTrack.length);
|
||||
});
|
||||
|
||||
it('should get popup with stats when track does not contain only latest positions', () => {
|
||||
|
@ -451,7 +451,7 @@ describe('TrackViewModel tests', () => {
|
||||
return Promise.resolve(imported);
|
||||
});
|
||||
spyOn(uPositionSet, 'fetch').and.returnValue(Promise.resolve(positions));
|
||||
spyOn(uUtils, 'sprintf');
|
||||
spyOn(window, 'alert');
|
||||
const options = '<option selected value="1">track1</option><option value="2">track2</option>';
|
||||
trackEl.insertAdjacentHTML('afterbegin', options);
|
||||
const optLength = trackEl.options.length;
|
||||
@ -476,7 +476,7 @@ describe('TrackViewModel tests', () => {
|
||||
expect(state.currentTrack).toBe(imported[0]);
|
||||
expect(vm.model.currentTrackId).toBe(imported[0].listValue);
|
||||
expect(state.currentTrack.length).toBe(positions.length);
|
||||
expect(uUtils.sprintf.calls.mostRecent().args[1]).toBe(imported.length);
|
||||
expect(window.alert).toHaveBeenCalledTimes(1);
|
||||
expect(trackEl.options.length).toBe(optLength + imported.length);
|
||||
expect(vm.model.trackList.length).toBe(optLength + imported.length);
|
||||
expect(vm.model.inputFile).toBe('');
|
||||
@ -493,7 +493,6 @@ describe('TrackViewModel tests', () => {
|
||||
];
|
||||
spyOn(uTrack, 'import').and.returnValue(Promise.resolve(imported));
|
||||
spyOn(uPositionSet, 'fetch').and.returnValue(Promise.resolve(positions));
|
||||
spyOn(uUtils, 'sprintf');
|
||||
spyOn(uUtils, 'error');
|
||||
const options = '<option selected value="1">track1</option><option value="2">track2</option>';
|
||||
trackEl.insertAdjacentHTML('afterbegin', options);
|
||||
@ -517,7 +516,7 @@ describe('TrackViewModel tests', () => {
|
||||
expect(uTrack.import).not.toHaveBeenCalled();
|
||||
expect(state.currentTrack).toBe(track1);
|
||||
expect(vm.model.currentTrackId).toBe(track1.listValue);
|
||||
expect(uUtils.sprintf.calls.mostRecent().args[1]).toBe(MAX_FILE_SIZE.toString());
|
||||
expect(lang._.calls.mostRecent().args[1]).toBe(MAX_FILE_SIZE.toString());
|
||||
expect(trackEl.options.length).toBe(optLength);
|
||||
expect(vm.model.trackList.length).toBe(optLength);
|
||||
done();
|
||||
|
Loading…
x
Reference in New Issue
Block a user