FIX: Allow message format translations to be overridden

This commit is contained in:
Robin Ward 2016-04-08 14:49:50 -04:00
parent adb3810f67
commit cc25716e47
9 changed files with 84 additions and 27 deletions

View file

@ -12,8 +12,16 @@ export default {
const overrides = PreloadStore.get('translationOverrides') || {}; const overrides = PreloadStore.get('translationOverrides') || {};
Object.keys(overrides).forEach(k => { Object.keys(overrides).forEach(k => {
const v = overrides[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('.'); const segs = k.split('.');
let node = I18n.translations[I18n.locale]; let node = I18n.translations[I18n.locale];
let i = 0; let i = 0;

View file

@ -14,12 +14,12 @@ class Admin::SiteTextsController < Admin::AdminController
query = params[:q] || "" query = params[:q] || ""
if query.blank? && !overridden if query.blank? && !overridden
extras[:recommended] = true 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 else
results = [] results = []
translations = I18n.search(query, overridden: overridden) translations = I18n.search(query, overridden: overridden)
translations.each do |k, v| translations.each do |k, v|
results << {id: k, value: v} results << record_for(k, v)
end end
results.sort! do |x, y| results.sort! do |x, y|
@ -62,9 +62,19 @@ class Admin::SiteTextsController < Admin::AdminController
protected 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 def find_site_text
raise Discourse::NotFound unless I18n.exists?(params[:id]) raise Discourse::NotFound unless I18n.exists?(params[:id])
{id: params[:id], value: I18n.t(params[:id]) } record_for(params[:id])
end end
end end

View file

@ -1,11 +1,19 @@
require 'js_locale_helper'
class TranslationOverride < ActiveRecord::Base class TranslationOverride < ActiveRecord::Base
validates_uniqueness_of :translation_key, scope: :locale validates_uniqueness_of :translation_key, scope: :locale
validates_presence_of :locale, :translation_key, :value validates_presence_of :locale, :translation_key, :value
def self.upsert!(locale, key, value) def self.upsert!(locale, key, value)
params = { locale: locale, translation_key: key } 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 i18n_changed
end end

View file

@ -0,0 +1,5 @@
class AddCompiledJsToTranslationOverrides < ActiveRecord::Migration
def change
add_column :translation_overrides, :compiled_js, :text, null: true
end
end

View file

@ -110,14 +110,14 @@ module I18n
by_site = @overrides_by_site[site] = {} by_site = @overrides_by_site[site] = {}
# Load overrides # 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? if translations_overrides.empty?
by_site[locale] = {} by_site[locale] = {}
else else
translations_overrides.each do |tuple| translations_overrides.each do |tuple|
by_locale = by_site[locale] ||= {} by_locale = by_site[locale] ||= {}
by_locale[tuple[0]] = tuple[1] by_locale[tuple[0]] = tuple[2] || tuple[1]
end end
end end
end end

View file

@ -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);
};
})();

View file

@ -139,31 +139,19 @@ module JsLocaleHelper
end end
def self.generate_message_format(message_formats, locale_str) 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" 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/#{locale_str}.js"
filename = Rails.root + "lib/javascripts/locale/en.js" unless File.exists?(filename) 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){ result
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}});"
end end
def self.compile_message_format(locale, format) def self.compile_message_format(locale, format)

View file

@ -54,7 +54,6 @@ describe JsLocaleHelper do
other {# apples} other {# apples}
}') }')
expect(localize(NUM_RESULTS: 1, NUM_APPLES: 2)).to eq('1 result and 2 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') expect(localize(NUM_RESULTS: 2, NUM_APPLES: 1)).to eq('2 results and 1 apple')
end end

View file

@ -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