mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
FIX: Allow message format translations to be overridden
This commit is contained in:
parent
adb3810f67
commit
cc25716e47
9 changed files with 84 additions and 27 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddCompiledJsToTranslationOverrides < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :translation_overrides, :compiled_js, :text, null: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -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
|
||||||
|
|
17
lib/javascripts/messageformat-lookup.js
Normal file
17
lib/javascripts/messageformat-lookup.js
Normal 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
22
spec/models/translation_override_spec.rb
Normal file
22
spec/models/translation_override_spec.rb
Normal 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
|
||||||
|
|
Loading…
Reference in a new issue