FIX: Fallback locale was not available for extra translations

FIX: Fallback locale was not available for extra translations

Translations from fallback locales were not sent to the client for admin_js and wizard_js.

diff --git a/app/assets/javascripts/locales/i18n.js b/app/assets/javascripts/locales/i18n.js
index 74991a7..58b824f 100644
--- a/app/assets/javascripts/locales/i18n.js
+++ b/app/assets/javascripts/locales/i18n.js
@@ -28,24 +28,6 @@ I18n.isValidNode = function(obj, node, undefined) {
   return obj[node] !== null && obj[node] !== undefined;
 };
 
-function checkExtras(origScope, sep, extras) {
-  if (!extras || extras.length === 0) { return; }
-
-  for (var i = 0; i < extras.length; i++) {
-    var messages = extras[i];
-    scope = origScope.split(sep);
-
-    if (scope[0] === 'js') { scope.shift(); }
-
-    while (messages && scope.length > 0) {
-      currentScope = scope.shift();
-      messages = messages[currentScope];
-    }
-
-    if (messages !== undefined) { return messages; }
-  }
-}
-
 I18n.lookup = function(scope, options) {
   options = options || {};
 
@@ -64,17 +46,26 @@ I18n.lookup = function(scope, options) {
     scope = options.scope.toString() + this.SEPARATOR + scope;
   }
 
-  var origScope = "" + scope;
+  var originalScope = scope;
+  scope = scope.split(this.SEPARATOR);
 
-  scope = origScope.split(this.SEPARATOR);
+  if (scope.length > 0 && scope[0] !== "js") {
+    scope.unshift("js");
+  }
 
   while (messages && scope.length > 0) {
     currentScope = scope.shift();
     messages = messages[currentScope];
   }
 
-  if (messages === undefined) {
-    messages = checkExtras(origScope, this.SEPARATOR, this.extras);
+  if (messages === undefined && this.extras && this.extras[locale]) {
+    messages = this.extras[locale];
+    scope = originalScope.split(this.SEPARATOR);
+
+    while (messages && scope.length > 0) {
+      currentScope = scope.shift();
+      messages = messages[currentScope];
+    }
   }
 
   if (messages === undefined) {
diff --git a/app/controllers/extra_locales_controller.rb b/app/controllers/extra_locales_controller.rb
index dfcc647..1baadeb 100644
--- a/app/controllers/extra_locales_controller.rb
+++ b/app/controllers/extra_locales_controller.rb
@@ -40,21 +40,20 @@ class ExtraLocalesController < ApplicationController
 
     translations = JsLocaleHelper.translations_for(locale_str)
 
-    for_key = {}
-    translations.values.each { |v| for_key.deep_merge!(v[bundle_str]) if v.has_key?(bundle_str) }
+    translations.keys.each do |l|
+      translations[l].keys.each do |k|
+        bundle_translations = translations[l].delete(k)
+        translations[l].deep_merge!(bundle_translations) if k == bundle_str
+      end
+    end
 
     js = ""
 
-    if for_key.present?
-      if plugin_for_key = JsLocaleHelper.plugin_translations(locale_str)[bundle_str]
-        for_key.deep_merge!(plugin_for_key)
-      end
-
+    if translations.present?
       js = <<~JS.squish
         (function() {
           if (window.I18n) {
-            window.I18n.extras = window.I18n.extras || [];
-            window.I18n.extras.push(#{for_key.to_json});
+            window.I18n.extras = #{translations.to_json};
           }
         })();
       JS
diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb
index 9856766..40c48aa 100644
--- a/lib/js_locale_helper.rb
+++ b/lib/js_locale_helper.rb
@@ -104,28 +104,32 @@ module JsLocaleHelper
     end
   end
 
+  def self.clear_cache!
+    @loaded_translations = nil
+    @plugin_translations = nil
+    @loaded_merges = nil
+  end
+
   def self.translations_for(locale_str)
-    if Rails.env.development?
-      @loaded_translations = nil
-      @plugin_translations = nil
-      @loaded_merges = nil
-    end
+    clear_cache! if Rails.env.development?
 
     locale_sym = locale_str.to_sym
 
-    I18n.with_locale(locale_sym) do
+    translations = I18n.with_locale(locale_sym) do
       if locale_sym == :en
         load_translations(locale_sym)
       else
         load_translations_merged(*I18n.fallbacks[locale_sym])
       end
     end
+
+    Marshal.load(Marshal.dump(translations))
   end
 
   def self.output_locale(locale)
     locale_str = locale.to_s
     fallback_locale_str = LocaleSiteSetting.fallback_locale(locale_str)&.to_s
-    translations = Marshal.load(Marshal.dump(translations_for(locale_str)))
+    translations = translations_for(locale_str)
 
     message_formats = remove_message_formats!(translations, locale)
     mf_locale, mf_filename = find_message_format_locale([locale_str], fallback_to_english: true)
diff --git a/spec/requests/extra_locales_controller_spec.rb b/spec/requests/extra_locales_controller_spec.rb
index bd47227..9615bf3 100644
--- a/spec/requests/extra_locales_controller_spec.rb
+++ b/spec/requests/extra_locales_controller_spec.rb
@@ -26,23 +26,32 @@ describe ExtraLocalesController do
       expect(response.status).to eq(404)
     end
 
-    it "includes plugin translations" do
-      JsLocaleHelper.expects(:plugin_translations)
-        .with(any_of("en", "en_US"))
-        .returns("admin_js" => {
-          "admin" => {
-            "site_settings" => {
-              "categories" => {
-                "github_badges" => "Github Badges"
+    context "with plugin" do
+      before do
+        JsLocaleHelper.clear_cache!
+        JsLocaleHelper.expects(:plugin_translations)
+          .with(any_of("en", "en_US"))
+          .returns("admin_js" => {
+            "admin" => {
+              "site_settings" => {
+                "categories" => {
+                  "github_badges" => "Github Badges"
+                }
               }
             }
-          }
-        }).at_least_once
+          }).at_least_once
+      end
 
-      get "/extra-locales/admin"
+      after do
+        JsLocaleHelper.clear_cache!
+      end
 
-      expect(response.status).to eq(200)
-      expect(response.body.include?("github_badges")).to eq(true)
+      it "includes plugin translations" do
+        get "/extra-locales/admin"
+
+        expect(response.status).to eq(200)
+        expect(response.body.include?("github_badges")).to eq(true)
+      end
     end
   end
 end
diff --git a/test/javascripts/helpers/component-test.js.es6 b/test/javascripts/helpers/component-test.js.es6
index 969f5aa..c653946 100644
--- a/test/javascripts/helpers/component-test.js.es6
+++ b/test/javascripts/helpers/component-test.js.es6
@@ -54,8 +54,15 @@ export default function(name, opts) {
     andThen(() => {
       return this.render(opts.template);
     });
+
     andThen(() => {
-      opts.test.call(this, assert);
+      try {
+        opts.test.call(this, assert);
+      } finally {
+        if (opts.afterEach) {
+          opts.afterEach.call(opts);
+        }
+      }
     });
   });
 }
diff --git a/test/javascripts/lib/i18n-test.js.es6 b/test/javascripts/lib/i18n-test.js.es6
index 0f14258..b68c871 100644
--- a/test/javascripts/lib/i18n-test.js.es6
+++ b/test/javascripts/lib/i18n-test.js.es6
@@ -99,12 +99,68 @@ QUnit.test("translations", assert => {
 });
 
 QUnit.test("extra translations", assert => {
-  I18n.extras = [{ admin: { title: "Discourse Admin" } }];
+  I18n.locale = "pl_PL";
+  I18n.extras = {
+    en: {
+      admin: {
+        dashboard: {
+          title: "Dashboard",
+          backup_count: {
+            one: "%{count} backup",
+            other: "%{count} backups"
+          }
+        },
+        web_hooks: {
+          events: {
+            incoming: {
+              one: "There is a new event.",
+              other: "There are %{count} new events."
+            }
+          }
+        }
+      }
+    },
+    pl_PL: {
+      admin: {
+        dashboard: {
+          title: "Raporty"
+        },
+        web_hooks: {
+          events: {
+            incoming: {
+              one: "Istnieje nowe wydarzenie",
+              few: "Istnieją %{count} nowe wydarzenia.",
+              many: "Istnieje %{count} nowych wydarzeń.",
+              other: "Istnieje %{count} nowych wydarzeń."
+            }
+          }
+        }
+      }
+    }
+  };
+  I18n.pluralizationRules.pl_PL = function(n) {
+    if (n === 1) return "one";
+    if (n % 10 >= 2 && n % 10 <= 4) return "few";
+    if (n % 10 === 0) return "many";

[... diff too long, it was truncated ...]

GitHub sha: c1e9a70d

1 Like

This commit has been mentioned on Discourse Meta. There might be relevant details there: