diff --git a/app/assets/javascripts/locales/i18n.js b/app/assets/javascripts/locales/i18n.js index 58b824f5c8e..cffc2e214ad 100644 --- a/app/assets/javascripts/locales/i18n.js +++ b/app/assets/javascripts/locales/i18n.js @@ -131,39 +131,47 @@ I18n.interpolate = function(message, options) { I18n.translate = function(scope, options) { options = this.prepareOptions(options); + options.needsPluralization = typeof options.count === "number"; + options.ignoreMissing = !this.noFallbacks; - var translation = this.lookup(scope, options); + var translation = this.findTranslation(scope, options); if (!this.noFallbacks) { if (!translation && this.fallbackLocale) { options.locale = this.fallbackLocale; - translation = this.lookup(scope, options); + translation = this.findTranslation(scope, options); } + + options.ignoreMissing = false; + if (!translation && this.currentLocale() !== this.defaultLocale) { options.locale = this.defaultLocale; - translation = this.lookup(scope, options); + translation = this.findTranslation(scope, options); } + if (!translation && this.currentLocale() !== 'en') { options.locale = 'en'; - translation = this.lookup(scope, options); + translation = this.findTranslation(scope, options); } } try { - if (typeof translation === "object") { - if (typeof options.count === "number") { - return this.pluralize(translation, scope, options); - } else { - return translation; - } - } else { - return this.interpolate(translation, options); - } + return this.interpolate(translation, options); } catch (error) { return this.missingTranslation(scope); } }; +I18n.findTranslation = function(scope, options) { + var translation = this.lookup(scope, options); + + if (translation && options.needsPluralization) { + translation = this.pluralize(translation, scope, options); + } + + return translation; +}; + I18n.toNumber = function(number, options) { options = this.prepareOptions( options, @@ -260,6 +268,8 @@ I18n.findAndTranslateValidNode = function(keys, translation) { }; I18n.pluralize = function(translation, scope, options) { + if (typeof translation !== "object") return translation; + options = this.prepareOptions(options); var count = options.count.toString(); @@ -268,9 +278,12 @@ I18n.pluralize = function(translation, scope, options) { var keys = ((typeof key === "object") && (key instanceof Array)) ? key : [key]; var message = this.findAndTranslateValidNode(keys, translation); - if (message == null) message = this.missingTranslation(scope, keys[0]); - return this.interpolate(message, options); + if (message !== null || options.ignoreMissing) { + return message; + } + + return this.missingTranslation(scope, keys[0]); }; I18n.missingTranslation = function(scope, key) { diff --git a/test/javascripts/lib/i18n-test.js.es6 b/test/javascripts/lib/i18n-test.js.es6 index b68c871d437..f1c628ab25c 100644 --- a/test/javascripts/lib/i18n-test.js.es6 +++ b/test/javascripts/lib/i18n-test.js.es6 @@ -2,6 +2,8 @@ QUnit.module("lib:i18n", { _locale: I18n.locale, _fallbackLocale: I18n.fallbackLocale, _translations: I18n.translations, + _extras: I18n.extras, + _pluralizationRules: Object.assign({}, I18n.pluralizationRules), beforeEach() { I18n.locale = "fr"; @@ -34,6 +36,9 @@ QUnit.module("lib:i18n", { few: "{{count}} FEW", many: "{{count}} MANY", other: "{{count}} OTHER" + }, + days: { + other: "%{count} jours" } } }, @@ -51,12 +56,17 @@ QUnit.module("lib:i18n", { word_count: { one: "1 word", other: "{{count}} words" + }, + days: { + one: "%{count} day", + other: "%{count} days" } } } }; // fake pluralization rules + I18n.pluralizationRules = Object.assign({}, I18n.pluralizationRules); I18n.pluralizationRules.fr = function(n) { if (n === 0) return "zero"; if (n === 1) return "one"; @@ -71,6 +81,8 @@ QUnit.module("lib:i18n", { I18n.locale = this._locale; I18n.fallbackLocale = this._fallbackLocale; I18n.translations = this._translations; + I18n.extras = this._extras; + I18n.pluralizationRules = this._pluralizationRules; } }); @@ -181,6 +193,17 @@ QUnit.test("pluralizations", assert => { }); QUnit.test("fallback", assert => { + assert.equal( + I18n.t("days", { count: 1 }), + "1 day", + "uses fallback locale for missing plural key" + ); + assert.equal( + I18n.t("days", { count: 200 }), + "200 jours", + "uses existing French plural key" + ); + I18n.locale = "fr_FOO"; I18n.fallbackLocale = "fr";