From cc25716e475e6eed70532c8526d9e612899d61d8 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 8 Apr 2016 14:49:50 -0400 Subject: [PATCH] FIX: Allow message format translations to be overridden --- .../initializers/localization.js.es6 | 10 +++++++- .../admin/site_texts_controller.rb | 16 ++++++++++--- app/models/translation_override.rb | 12 ++++++++-- ...dd_compiled_js_to_translation_overrides.rb | 5 ++++ lib/freedom_patches/translate_accelerator.rb | 4 ++-- lib/javascripts/messageformat-lookup.js | 17 +++++++++++++ lib/js_locale_helper.rb | 24 +++++-------------- spec/components/js_locale_helper_spec.rb | 1 - spec/models/translation_override_spec.rb | 22 +++++++++++++++++ 9 files changed, 84 insertions(+), 27 deletions(-) create mode 100644 db/migrate/20160408175727_add_compiled_js_to_translation_overrides.rb create mode 100644 lib/javascripts/messageformat-lookup.js create mode 100644 spec/models/translation_override_spec.rb diff --git a/app/assets/javascripts/discourse/initializers/localization.js.es6 b/app/assets/javascripts/discourse/initializers/localization.js.es6 index 0ad607d3f..2945b5b17 100644 --- a/app/assets/javascripts/discourse/initializers/localization.js.es6 +++ b/app/assets/javascripts/discourse/initializers/localization.js.es6 @@ -12,8 +12,16 @@ export default { const overrides = PreloadStore.get('translationOverrides') || {}; Object.keys(overrides).forEach(k => { const v = overrides[k]; - k = k.replace('admin_js', 'js'); + // Special case: Message format keys are functions + if (/\_MF$/.test(k)) { + k = k.replace(/^[a-z_]*js\./, ''); + I18n._compiledMFs[k] = new Function('transKey', `return (${v})(transKey);`); + + return; + } + + k = k.replace('admin_js', 'js'); const segs = k.split('.'); let node = I18n.translations[I18n.locale]; let i = 0; diff --git a/app/controllers/admin/site_texts_controller.rb b/app/controllers/admin/site_texts_controller.rb index d22822951..500968f95 100644 --- a/app/controllers/admin/site_texts_controller.rb +++ b/app/controllers/admin/site_texts_controller.rb @@ -14,12 +14,12 @@ class Admin::SiteTextsController < Admin::AdminController query = params[:q] || "" if query.blank? && !overridden extras[:recommended] = true - results = self.class.preferred_keys.map {|k| {id: k, value: I18n.t(k) }} + results = self.class.preferred_keys.map {|k| record_for(k) } else results = [] translations = I18n.search(query, overridden: overridden) translations.each do |k, v| - results << {id: k, value: v} + results << record_for(k, v) end results.sort! do |x, y| @@ -62,9 +62,19 @@ class Admin::SiteTextsController < Admin::AdminController protected + def record_for(k, value=nil) + if k.ends_with?("_MF") + ovr = TranslationOverride.where(translation_key: k).pluck(:value) + value = ovr[0] if ovr.present? + end + + value ||= I18n.t(k) + {id: k, value: value} + end + def find_site_text raise Discourse::NotFound unless I18n.exists?(params[:id]) - {id: params[:id], value: I18n.t(params[:id]) } + record_for(params[:id]) end end diff --git a/app/models/translation_override.rb b/app/models/translation_override.rb index 3046832e6..408c46f09 100644 --- a/app/models/translation_override.rb +++ b/app/models/translation_override.rb @@ -1,11 +1,19 @@ +require 'js_locale_helper' + class TranslationOverride < ActiveRecord::Base validates_uniqueness_of :translation_key, scope: :locale validates_presence_of :locale, :translation_key, :value def self.upsert!(locale, key, value) params = { locale: locale, translation_key: key } - row_count = where(params).update_all(value: value) - create!(params.merge(value: value)) if row_count == 0 + + data = { value: value } + if key.end_with?('_MF') + data[:compiled_js] = JsLocaleHelper.compile_message_format(locale, value) + end + + row_count = where(params).update_all(data) + create!(params.merge(data)) if row_count == 0 i18n_changed end diff --git a/db/migrate/20160408175727_add_compiled_js_to_translation_overrides.rb b/db/migrate/20160408175727_add_compiled_js_to_translation_overrides.rb new file mode 100644 index 000000000..bdf28a2bf --- /dev/null +++ b/db/migrate/20160408175727_add_compiled_js_to_translation_overrides.rb @@ -0,0 +1,5 @@ +class AddCompiledJsToTranslationOverrides < ActiveRecord::Migration + def change + add_column :translation_overrides, :compiled_js, :text, null: true + end +end diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb index 4b41a24c2..c5e8abf3a 100644 --- a/lib/freedom_patches/translate_accelerator.rb +++ b/lib/freedom_patches/translate_accelerator.rb @@ -110,14 +110,14 @@ module I18n by_site = @overrides_by_site[site] = {} # Load overrides - translations_overrides = TranslationOverride.where(locale: locale).pluck(:translation_key, :value) + translations_overrides = TranslationOverride.where(locale: locale).pluck(:translation_key, :value, :compiled_js) if translations_overrides.empty? by_site[locale] = {} else translations_overrides.each do |tuple| by_locale = by_site[locale] ||= {} - by_locale[tuple[0]] = tuple[1] + by_locale[tuple[0]] = tuple[2] || tuple[1] end end end diff --git a/lib/javascripts/messageformat-lookup.js b/lib/javascripts/messageformat-lookup.js new file mode 100644 index 000000000..e34cd4803 --- /dev/null +++ b/lib/javascripts/messageformat-lookup.js @@ -0,0 +1,17 @@ +(function() { + + I18n.messageFormat = function(key, options) { + var fn = I18n._compiledMFs[key]; + if (fn) { + try { + return fn(options); + } catch(err) { + return err.message; + } + } else { + return 'Missing Key: ' + key; + } + return I18n._compiledMFs[key](options); + }; + +})(); diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb index 1374235eb..db4821a37 100644 --- a/lib/js_locale_helper.rb +++ b/lib/js_locale_helper.rb @@ -139,31 +139,19 @@ module JsLocaleHelper end def self.generate_message_format(message_formats, locale_str) - formats = message_formats.map{|k,v| k.inspect << " : " << compile_message_format(locale_str ,v)}.join(" , ") result = "MessageFormat = {locale: {}};\n" + formats = message_formats.map{|k,v| k.inspect << " : " << compile_message_format(locale_str,v)}.join(", ") + result << "I18n._compiledMFs = {#{formats}};\n\n" + filename = Rails.root + "lib/javascripts/locale/#{locale_str}.js" filename = Rails.root + "lib/javascripts/locale/en.js" unless File.exists?(filename) + result << File.read(filename) << "\n\n" - result << File.read(filename) << "\n" + result << File.read("#{Rails.root}/lib/javascripts/messageformat-lookup.js") - result << "I18n.messageFormat = (function(formats){ - var f = formats; - return function(key, options) { - var fn = f[key]; - if(fn){ - try { - return fn(options); - } catch(err) { - return err.message; - } - } else { - return 'Missing Key: ' + key - } - return f[key](options); - }; - })({#{formats}});" + result end def self.compile_message_format(locale, format) diff --git a/spec/components/js_locale_helper_spec.rb b/spec/components/js_locale_helper_spec.rb index 337553f7f..1b26efe88 100644 --- a/spec/components/js_locale_helper_spec.rb +++ b/spec/components/js_locale_helper_spec.rb @@ -54,7 +54,6 @@ describe JsLocaleHelper do other {# apples} }') - expect(localize(NUM_RESULTS: 1, NUM_APPLES: 2)).to eq('1 result and 2 apples') expect(localize(NUM_RESULTS: 2, NUM_APPLES: 1)).to eq('2 results and 1 apple') end diff --git a/spec/models/translation_override_spec.rb b/spec/models/translation_override_spec.rb new file mode 100644 index 000000000..d1c40d61c --- /dev/null +++ b/spec/models/translation_override_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +describe TranslationOverride do + + it "upserts values" do + TranslationOverride.upsert!('en', 'some.key', 'some value') + + ovr = TranslationOverride.where(locale: 'en', translation_key: 'some.key').first + expect(ovr).to be_present + expect(ovr.value).to eq('some value') + end + + it "stores js for a message format key" do + TranslationOverride.upsert!('en', 'some.key_MF', '{NUM_RESULTS, plural, one {1 result} other {many} }') + + ovr = TranslationOverride.where(locale: 'en', translation_key: 'some.key_MF').first + expect(ovr).to be_present + expect(ovr.compiled_js).to match(/function/) + end + +end +