From 1506eba28dbc883b51b4f99e251dcafafb37181c Mon Sep 17 00:00:00 2001
From: Robin Ward <robin.ward@gmail.com>
Date: Fri, 20 Nov 2015 17:13:37 -0500
Subject: [PATCH] Support for overriding client side translation keys

---
 .../initializers/localization.js.es6          | 28 ++++++++++++
 .../initializers/verbose-localization.js.es6  | 11 -----
 app/controllers/application_controller.rb     |  1 +
 lib/freedom_patches/translate_accelerator.rb  | 43 ++++++++++++-------
 spec/components/discourse_i18n_spec.rb        | 19 ++++++++
 5 files changed, 75 insertions(+), 27 deletions(-)
 create mode 100644 app/assets/javascripts/discourse/initializers/localization.js.es6
 delete mode 100644 app/assets/javascripts/discourse/initializers/verbose-localization.js.es6

diff --git a/app/assets/javascripts/discourse/initializers/localization.js.es6 b/app/assets/javascripts/discourse/initializers/localization.js.es6
new file mode 100644
index 000000000..5c9f3292c
--- /dev/null
+++ b/app/assets/javascripts/discourse/initializers/localization.js.es6
@@ -0,0 +1,28 @@
+export default {
+  name: 'localization',
+  after: 'inject-objects',
+
+  initialize: function(container) {
+    const siteSettings = container.lookup('site-settings:main');
+    if (siteSettings.verbose_localization) {
+      I18n.enable_verbose_localization();
+    }
+
+    // Merge any overrides into our object
+    const overrides = PreloadStore.get('translationOverrides') || {};
+    Object.keys(overrides).forEach(k => {
+      const v = overrides[k];
+
+      const segs = k.split('.');
+      let node = I18n.translations[I18n.locale];
+      let i = 0;
+      for (; node && i<segs.length-1; i++) {
+        node = node[segs[i]];
+      }
+
+      if (node && i == segs.length-1) {
+        node[segs[segs.length-1]] = v;
+      }
+    });
+  }
+};
diff --git a/app/assets/javascripts/discourse/initializers/verbose-localization.js.es6 b/app/assets/javascripts/discourse/initializers/verbose-localization.js.es6
deleted file mode 100644
index 71aca9f3f..000000000
--- a/app/assets/javascripts/discourse/initializers/verbose-localization.js.es6
+++ /dev/null
@@ -1,11 +0,0 @@
-export default {
-  name: 'verbose-localization',
-  after: 'inject-objects',
-
-  initialize: function(container) {
-    var siteSettings = container.lookup('site-settings:main');
-    if (siteSettings.verbose_localization) {
-      I18n.enable_verbose_localization();
-    }
-  }
-};
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d162e27f0..b9e597151 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -288,6 +288,7 @@ class ApplicationController < ActionController::Base
       store_preloaded("customHTML", custom_html_json)
       store_preloaded("banner", banner_json)
       store_preloaded("customEmoji", custom_emoji)
+      store_preloaded("translationOverrides", I18n.client_overrides_json)
     end
 
     def preload_current_user_data
diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb
index 68ac930c9..4bdeaf5cd 100644
--- a/lib/freedom_patches/translate_accelerator.rb
+++ b/lib/freedom_patches/translate_accelerator.rb
@@ -72,26 +72,37 @@ module I18n
       end
     end
 
+    def overrides_by_locale
+      return unless @overrides_enabled
+
+      site = RailsMultisite::ConnectionManagement.current_db
+
+      by_site = @overrides_by_site[site]
+
+      by_locale = nil
+      unless by_site
+        by_site = @overrides_by_site[site] = {}
+
+        # Load overrides
+        TranslationOverride.where(locale: locale).pluck(:translation_key, :value).each do |tuple|
+          by_locale = by_site[locale] ||= {}
+          by_locale[tuple[0]] = tuple[1]
+        end
+      end
+
+      by_site[config.locale]
+    end
+
+    def client_overrides_json
+      client_json = (overrides_by_locale || {}).select {|k, _| k.starts_with?('js.')}
+      MultiJson.dump(client_json)
+    end
+
     def translate(key, *args)
       load_locale(config.locale) unless @loaded_locales.include?(config.locale)
 
       if @overrides_enabled
-        site = RailsMultisite::ConnectionManagement.current_db
-
-        by_site = @overrides_by_site[site]
-
-        by_locale = nil
-        unless by_site
-          by_site = @overrides_by_site[site] = {}
-
-          # Load overrides
-          TranslationOverride.where(locale: locale).pluck(:translation_key, :value).each do |tuple|
-            by_locale = by_site[locale] ||= {}
-            by_locale[tuple[0]] = tuple[1]
-          end
-        end
-
-        by_locale = by_site[config.locale]
+        by_locale = overrides_by_locale
         if by_locale
           if args.size > 0 && args[0].is_a?(Hash)
             args[0][:overrides] = by_locale
diff --git a/spec/components/discourse_i18n_spec.rb b/spec/components/discourse_i18n_spec.rb
index 551c05aa3..dc6cd2293 100644
--- a/spec/components/discourse_i18n_spec.rb
+++ b/spec/components/discourse_i18n_spec.rb
@@ -91,6 +91,25 @@ describe I18n::Backend::DiscourseI18n do
       expect(I18n.translate('items', count: 13)).to eq('13 fishies')
       expect(I18n.translate('items', count: 1)).to eq('one fish')
     end
+
+    describe "client json" do
+      it "is empty by default" do
+        expect(I18n.client_overrides_json).to eq("{}")
+      end
+
+      it "doesn't return server overrides" do
+        TranslationOverride.upsert!('en', 'foo', 'bar')
+        expect(I18n.client_overrides_json).to eq("{}")
+      end
+
+      it "returns client overrides" do
+        TranslationOverride.upsert!('en', 'js.foo', 'bar')
+        json = ::JSON.parse(I18n.client_overrides_json)
+
+        expect(json).to be_present
+        expect(json['js.foo']).to eq('bar')
+      end
+    end
   end
 
 end