diff --git a/.eslintignore b/.eslintignore
index c7699ff44..c3a3d13ad 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -19,6 +19,7 @@ test/views/*.js
!js/backup.js
!js/database.js
!js/logging.js
+!js/i18n.js
!js/models/conversations.js
!js/views/attachment_view.js
!js/views/conversation_search_view.js
diff --git a/background.html b/background.html
index b3a2517b4..9661d8698 100644
--- a/background.html
+++ b/background.html
@@ -909,7 +909,6 @@
-
diff --git a/js/i18n.js b/js/i18n.js
index d5985d29e..4ec0c183c 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -1,29 +1,34 @@
-/*
- * vim: ts=4:sw=4:expandtab
- */
-;(function() {
- 'use strict';
+/* eslint-env node */
- // preload.js loads this, pulling it from main.js (where it was loaded from disk)
- var messages = window.config.localeMessages;
- var locale = window.config.locale;
+exports.setup = (locale, messages) => {
+ if (!locale) {
+ throw new Error('i18n: locale parameter is required');
+ }
+ if (!messages) {
+ throw new Error('i18n: messages parameter is required');
+ }
- window.i18n = function (message, substitutions) {
- if (!messages[message]) {
- return;
- }
- var s = messages[message].message;
- if (substitutions instanceof Array) {
- substitutions.forEach(function(sub) {
- s = s.replace(/\$.+?\$/, sub);
- });
- } else if (substitutions) {
- s = s.replace(/\$.+?\$/, substitutions);
- }
- return s;
- };
+ function getMessage(key, substitutions) {
+ const entry = messages[key];
+ if (!entry) {
+ console.error(`i18n: Attempted to get translation for nonexistent key '${key}'`);
+ return '';
+ }
- i18n.getLocale = function() {
- return locale;
- };
-})();
+ const { message } = entry;
+ if (substitutions instanceof Array) {
+ return substitutions.reduce(
+ (result, substitution) => result.replace(/\$.+?\$/, substitution),
+ message
+ );
+ } else if (substitutions) {
+ return message.replace(/\$.+?\$/, substitutions);
+ }
+
+ return message;
+ }
+
+ getMessage.getLocale = () => locale;
+
+ return getMessage;
+};
diff --git a/js/views/timestamp_view.js b/js/views/timestamp_view.js
index 185224ef6..755538d8b 100644
--- a/js/views/timestamp_view.js
+++ b/js/views/timestamp_view.js
@@ -5,15 +5,6 @@
'use strict';
window.Whisper = window.Whisper || {};
- moment.updateLocale(i18n.getLocale(), {
- relativeTime : {
- s: i18n('timestamp_s') || 'now',
- m: i18n('timestamp_m') || '1 minute',
- h: i18n('timestamp_h') || '1 hour'
- }
- });
- moment.locale(i18n.getLocale());
-
Whisper.TimestampView = Whisper.View.extend({
initialize: function(options) {
extension.windows.onClosed(this.clearTimeout.bind(this));
diff --git a/preload.js b/preload.js
index ec554c22b..5eb791caa 100644
--- a/preload.js
+++ b/preload.js
@@ -110,6 +110,19 @@ window.nodeNotifier = require('node-notifier');
window.ProxyAgent = require('proxy-agent');
window.moment = require('moment');
+const { setup } = require('./js/i18n');
+
+const { locale, localeMessages } = window.config;
+window.i18n = setup(locale, localeMessages);
+window.moment.updateLocale(locale, {
+ relativeTime: {
+ s: window.i18n('timestamp_s'),
+ m: window.i18n('timestamp_m'),
+ h: window.i18n('timestamp_h'),
+ },
+});
+window.moment.locale(locale);
+
// ES2015+ modules
const attachmentsPath = Attachments.getPath(app.getPath('userData'));
const deleteAttachmentData = Attachments.createDeleter(attachmentsPath);
diff --git a/test/i18n_test.js b/test/i18n_test.js
index aea603f5c..6c094487d 100644
--- a/test/i18n_test.js
+++ b/test/i18n_test.js
@@ -1,7 +1,7 @@
describe('i18n', function() {
describe('i18n', function() {
- it('returns undefined for unknown string', function() {
- assert.strictEqual(i18n('random'), undefined);
+ it('returns empty string for unknown string', function() {
+ assert.strictEqual(i18n('random'), '');
});
it('returns message for given string', function() {
assert.equal(i18n('reportIssue'), 'Report an issue');
diff --git a/test/index.html b/test/index.html
index 5a8c945f4..3384f5986 100644
--- a/test/index.html
+++ b/test/index.html
@@ -562,8 +562,6 @@
-
-