FIX: eradicate I18n fallback issues 💣

FIX: client's translation overrides were not working when the current locale was missing a key
FIX: ExtraLocalesController.show was not properly handling multiple translations
FIX: JsLocaleHelper#output_locale was not properly handling multiple translations

FIX: ExtraLocalesController.show's spec which was randomly failing
FIX: JsLocaleHelper#output_locale was muting cached translations hashes

REFACTOR: move 'enableVerboseLocalization' to the 'localization' initializer
REFACTOR: remove unused I18n.js methods (getFallbacks, localize, parseDate, toTime, strftime, toCurrency, toPercentage)
REFACTOR: remove all I18n.pluralizationRules and instead use MessageFormat's pluralization rules

TEST: add tests for localization initializer
TEST: add tests for I18n.js
This commit is contained in:
Régis Hanol
2017-02-24 11:31:21 +01:00
parent 1060239e2d
commit a2c04be718
20 changed files with 314 additions and 532 deletions

View File

@@ -15,6 +15,7 @@ describe JsLocaleHelper do
@loaded_merges = nil
end
end
JsLocaleHelper.extend StubLoadTranslations
after do
@@ -22,64 +23,68 @@ describe JsLocaleHelper do
JsLocaleHelper.clear_cache!
end
it 'should be able to generate translations' do
expect(JsLocaleHelper.output_locale('en').length).to be > 0
describe "#output_locale" do
it "doesn't change the cached translations hash" do
I18n.locale = :fr
expect(JsLocaleHelper.output_locale('fr').length).to be > 0
expect(JsLocaleHelper.translations_for('fr')['fr'].keys).to contain_exactly("js", "admin_js", "wizard_js")
end
end
def setup_message_format(format)
@ctx = MiniRacer::Context.new
@ctx.eval('MessageFormat = {locale: {}};')
@ctx.load(Rails.root + 'lib/javascripts/locale/en.js')
compiled = JsLocaleHelper.compile_message_format('en', format)
@ctx.eval("var test = #{compiled}")
end
context "message format" do
def localize(opts)
@ctx.eval("test(#{opts.to_json})")
end
def setup_message_format(format)
@ctx = MiniRacer::Context.new
@ctx.eval('MessageFormat = {locale: {}};')
@ctx.load(Rails.root + 'lib/javascripts/locale/en.js')
compiled = JsLocaleHelper.compile_message_format('en', format)
@ctx.eval("var test = #{compiled}")
end
it 'handles plurals' do
setup_message_format('{NUM_RESULTS, plural,
one {1 result}
other {# results}
}')
expect(localize(NUM_RESULTS: 1)).to eq('1 result')
expect(localize(NUM_RESULTS: 2)).to eq('2 results')
end
def localize(opts)
@ctx.eval("test(#{opts.to_json})")
end
it 'handles double plurals' do
setup_message_format('{NUM_RESULTS, plural,
one {1 result}
other {# results}
} and {NUM_APPLES, plural,
one {1 apple}
other {# apples}
}')
it 'handles plurals' do
setup_message_format('{NUM_RESULTS, plural,
one {1 result}
other {# results}
}')
expect(localize(NUM_RESULTS: 1)).to eq('1 result')
expect(localize(NUM_RESULTS: 2)).to eq('2 results')
end
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
it 'handles double plurals' do
setup_message_format('{NUM_RESULTS, plural,
one {1 result}
other {# results}
} and {NUM_APPLES, plural,
one {1 apple}
other {# apples}
}')
it 'handles select' do
setup_message_format('{GENDER, select, male {He} female {She} other {They}} read a book')
expect(localize(GENDER: 'male')).to eq('He read a book')
expect(localize(GENDER: 'female')).to eq('She read a book')
expect(localize(GENDER: 'none')).to eq('They read a book')
end
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
it 'can strip out message formats' do
hash = {"a" => "b", "c" => { "d" => {"f_MF" => "bob"} }}
expect(JsLocaleHelper.strip_out_message_formats!(hash)).to eq({"c.d.f_MF" => "bob"})
expect(hash["c"]["d"]).to eq({})
end
it 'handles select' do
setup_message_format('{GENDER, select, male {He} female {She} other {They}} read a book')
expect(localize(GENDER: 'male')).to eq('He read a book')
expect(localize(GENDER: 'female')).to eq('She read a book')
expect(localize(GENDER: 'none')).to eq('They read a book')
end
it 'handles message format special keys' do
ctx = MiniRacer::Context.new
ctx.eval("I18n = {};")
it 'can strip out message formats' do
hash = {"a" => "b", "c" => { "d" => {"f_MF" => "bob"} }}
expect(JsLocaleHelper.strip_out_message_formats!(hash)).to eq({"c.d.f_MF" => "bob"})
expect(hash["c"]["d"]).to eq({})
end
JsLocaleHelper.set_translations 'en', {
"en" =>
{
it 'handles message format special keys' do
JsLocaleHelper.set_translations('en', {
"en" => {
"js" => {
"hello" => "world",
"test_MF" => "{HELLO} {COUNT, plural, one {1 duck} other {# ducks}}",
@@ -87,26 +92,29 @@ describe JsLocaleHelper do
"simple_MF" => "{COUNT, plural, one {1} other {#}}"
}
}
}
})
ctx.eval(JsLocaleHelper.output_locale('en'))
ctx = MiniRacer::Context.new
ctx.eval("I18n = { pluralizationRules: {} };")
ctx.eval(JsLocaleHelper.output_locale('en'))
expect(ctx.eval('I18n.translations["en"]["js"]["hello"]')).to eq("world")
expect(ctx.eval('I18n.translations["en"]["js"]["test_MF"]')).to eq(nil)
expect(ctx.eval('I18n.translations["en"]["js"]["hello"]')).to eq("world")
expect(ctx.eval('I18n.translations["en"]["js"]["test_MF"]')).to eq(nil)
expect(ctx.eval('I18n.messageFormat("test_MF", { HELLO: "hi", COUNT: 3 })')).to eq("hi 3 ducks")
expect(ctx.eval('I18n.messageFormat("error_MF", { HELLO: "hi", COUNT: 3 })')).to match(/Invalid Format/)
expect(ctx.eval('I18n.messageFormat("missing", {})')).to match(/missing/)
expect(ctx.eval('I18n.messageFormat("simple_MF", {})')).to match(/COUNT/) # error
end
expect(ctx.eval('I18n.messageFormat("test_MF", { HELLO: "hi", COUNT: 3 })')).to eq("hi 3 ducks")
expect(ctx.eval('I18n.messageFormat("error_MF", { HELLO: "hi", COUNT: 3 })')).to match(/Invalid Format/)
expect(ctx.eval('I18n.messageFormat("missing", {})')).to match(/missing/)
expect(ctx.eval('I18n.messageFormat("simple_MF", {})')).to match(/COUNT/) # error
end
it 'load pluralizations rules before precompile' do
message = JsLocaleHelper.compile_message_format('ru', 'format')
expect(message).not_to match 'Plural Function not found'
it 'load pluralizations rules before precompile' do
message = JsLocaleHelper.compile_message_format('ru', 'format')
expect(message).not_to match 'Plural Function not found'
end
end
it 'performs fallbacks to english if a translation is not available' do
JsLocaleHelper.set_translations 'en', {
JsLocaleHelper.set_translations('en', {
"en" => {
"js" => {
"only_english" => "1-en",
@@ -115,8 +123,9 @@ describe JsLocaleHelper do
"all_three" => "7-en",
}
}
}
JsLocaleHelper.set_translations 'ru', {
})
JsLocaleHelper.set_translations('ru', {
"ru" => {
"js" => {
"only_site" => "2-ru",
@@ -125,8 +134,9 @@ describe JsLocaleHelper do
"all_three" => "7-ru",
}
}
}
JsLocaleHelper.set_translations 'uk', {
})
JsLocaleHelper.set_translations('uk', {
"uk" => {
"js" => {
"only_user" => "4-uk",
@@ -135,7 +145,7 @@ describe JsLocaleHelper do
"all_three" => "7-uk",
}
}
}
})
expected = {
"none" => "[uk.js.none]",
@@ -157,9 +167,9 @@ describe JsLocaleHelper do
ctx.eval(JsLocaleHelper.output_locale(I18n.locale))
ctx.eval('I18n.defaultLocale = "ru";')
# Test - unneeded translations are not emitted
expect(ctx.eval('I18n.translations.en.js').keys).to eq(["only_english"])
expect(ctx.eval('I18n.translations.ru.js').keys).to eq(["only_site", "english_and_site"])
expect(ctx.eval('I18n.translations.en.js').keys).to contain_exactly("only_english")
expect(ctx.eval('I18n.translations.ru.js').keys).to contain_exactly("only_site", "english_and_site")
expect(ctx.eval('I18n.translations.uk.js').keys).to contain_exactly("all_three", "english_and_user", "only_user", "site_and_user")
expected.each do |key, expect|
expect(ctx.eval("I18n.t(#{"js.#{key}".inspect})")).to eq(expect)