diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index db2812b528f..d162e27f0a0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -138,13 +138,8 @@ class ApplicationController < ActionController::Base end def set_locale - I18n.locale = if current_user - current_user.effective_locale - else - SiteSetting.default_locale - end - - I18n.fallbacks.ensure_loaded! + I18n.locale = current_user.try(:effective_locale) || SiteSetting.default_locale + I18n.ensure_all_loaded! end def store_preloaded(key, json) diff --git a/app/jobs/base.rb b/app/jobs/base.rb index a187dfb682e..b14cf595894 100644 --- a/app/jobs/base.rb +++ b/app/jobs/base.rb @@ -149,7 +149,7 @@ module Jobs begin RailsMultisite::ConnectionManagement.establish_connection(db: db) I18n.locale = SiteSetting.default_locale - I18n.fallbacks.ensure_loaded! + I18n.ensure_all_loaded! begin execute(opts) rescue => e diff --git a/config/initializers/i18n.rb b/config/initializers/i18n.rb index 616d519bfc1..ca6ee5e5695 100644 --- a/config/initializers/i18n.rb +++ b/config/initializers/i18n.rb @@ -1,26 +1,5 @@ # order: after 02-freedom_patches.rb -# Include pluralization module -require 'i18n/backend/pluralization' -I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization) - -# Include fallbacks module -require 'i18n/backend/fallbacks' -I18n.backend.class.send(:include, I18n::Backend::Fallbacks) - -# Configure custom fallback order -class FallbackLocaleList < Hash - def [](locale) - # user locale, site locale, english - # TODO - this can be extended to be per-language for a better user experience - # (e.g. fallback zh_TW to zh_CN / vice versa) - [locale, SiteSetting.default_locale.to_sym, :en].uniq.compact - end - - def ensure_loaded! - self[I18n.locale].each { |l| I18n.ensure_loaded! l } - end -end - -I18n.fallbacks = FallbackLocaleList.new +require 'i18n/backend/discourse_i18n' +I18n.backend = I18n::Backend::DiscourseI18n.new I18n.config.missing_interpolation_argument_handler = proc { throw(:exception) } diff --git a/lib/freedom_patches/i18n_fallbacks.rb b/lib/freedom_patches/i18n_fallbacks.rb deleted file mode 100644 index 859e816c08f..00000000000 --- a/lib/freedom_patches/i18n_fallbacks.rb +++ /dev/null @@ -1,19 +0,0 @@ -# This should be used until https://github.com/svenfuchs/i18n/pull/326 is merged into the I18n gem. - -module I18n - module Backend - module Fallbacks - def exists?(locale, key) - I18n.fallbacks[locale].each do |fallback| - begin - return true if super(fallback, key) - rescue I18n::InvalidLocale - # we do nothing when the locale is invalid, as this is a fallback anyways. - end - end - - false - end - end - end -end diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb index 7b3fc986a6c..77678081499 100644 --- a/lib/freedom_patches/translate_accelerator.rb +++ b/lib/freedom_patches/translate_accelerator.rb @@ -9,27 +9,7 @@ # This patch depends on the convention that locale yml files must be named [locale_name].yml module I18n - module Backend - class Simple - def available_locales - # in case you are wondering this is: - # Dir.glob( File.join(Rails.root, 'config', 'locales', 'client.*.yml') ) - # .map {|x| x.split('.')[-2]}.sort - LocaleSiteSetting.supported_locales.map(&:to_sym) - end - end - - module Base - # force explicit loading - def load_translations(*filenames) - unless filenames.empty? - filenames.flatten.each { |filename| load_file(filename) } - end - end - - end - end # this accelerates translation a tiny bit (halves the time it takes) class << self alias_method :translate_no_cache, :translate @@ -59,9 +39,13 @@ module I18n end end + def ensure_all_loaded! + backend.fallbacks(locale).each {|l| ensure_loaded!(l) } + end + def ensure_loaded!(locale) @loaded_locales ||= [] - load_locale locale unless @loaded_locales.include?(locale) + load_locale(locale) unless @loaded_locales.include?(locale) end def translate(key, *args) diff --git a/lib/i18n/backend/discourse_i18n.rb b/lib/i18n/backend/discourse_i18n.rb new file mode 100644 index 00000000000..9e34866d0b3 --- /dev/null +++ b/lib/i18n/backend/discourse_i18n.rb @@ -0,0 +1,40 @@ +require 'i18n/backend/pluralization' + +module I18n + module Backend + class DiscourseI18n < I18n::Backend::Simple + include I18n::Backend::Pluralization + + def available_locales + # in case you are wondering this is: + # Dir.glob( File.join(Rails.root, 'config', 'locales', 'client.*.yml') ) + # .map {|x| x.split('.')[-2]}.sort + LocaleSiteSetting.supported_locales.map(&:to_sym) + end + + # force explicit loading + def load_translations(*filenames) + unless filenames.empty? + filenames.flatten.each { |filename| load_file(filename) } + end + end + + def fallbacks(locale) + [locale, SiteSetting.default_locale.to_sym, :en].uniq.compact + end + + def exists?(locale, key) + fallbacks(locale).each do |fallback| + begin + return true if super(fallback, key) + rescue I18n::InvalidLocale + # we do nothing when the locale is invalid, as this is a fallback anyways. + end + end + + false + end + + end + end +end diff --git a/spec/components/discourse_i18n_spec.rb b/spec/components/discourse_i18n_spec.rb new file mode 100644 index 00000000000..98888fa42b1 --- /dev/null +++ b/spec/components/discourse_i18n_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' +require 'i18n/backend/discourse_i18n' + +describe I18n::Backend::DiscourseI18n do + + let(:backend) { I18n::Backend::DiscourseI18n.new } + + before do + backend.store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en') + backend.store_translations(:en, :items => {:one => 'one item', :other => "%{count} items" }) + backend.store_translations(:de, :bar => 'Bar in :de') + backend.store_translations(:'de-AT', :baz => 'Baz in :de-AT') + end + + it 'translates the basics as expected' do + expect(backend.translate(:en, 'foo')).to eq("Foo in :en") + expect(backend.translate(:en, 'items', count: 1)).to eq("one item") + expect(backend.translate(:en, 'items', count: 3)).to eq("3 items") + end + + describe '#exists?' do + it 'returns true when a key is given that exists' do + expect(backend.exists?(:de, :bar)).to eq(true) + end + + it 'returns true when a key is given that exists in a fallback locale of the locale' do + expect(backend.exists?(:de, :foo)).to eq(true) + end + + it 'returns true when an existing key and an existing locale is given' do + expect(backend.exists?(:en, :foo)).to eq(true) + expect(backend.exists?(:de, :bar)).to eq(true) + expect(backend.exists?(:'de-AT', :baz)).to eq(true) + end + + it 'returns false when a non-existing key and an existing locale is given' do + expect(backend.exists?(:en, :bogus)).to eq(false) + expect(backend.exists?(:de, :bogus)).to eq(false) + expect(backend.exists?(:'de-AT', :bogus)).to eq(false) + end + + it 'returns true when a key is given which is missing from the given locale and exists in a fallback locale' do + expect(backend.exists?(:de, :foo)).to eq(true) + expect(backend.exists?(:'de-AT', :foo)).to eq(true) + end + + it 'returns true when a key is given which is missing from the given locale and all its fallback locales' do + expect(backend.exists?(:de, :baz)).to eq(false) + expect(backend.exists?(:'de-AT', :bogus)).to eq(false) + end + end + +end diff --git a/spec/helpers/i18n_fallbacks_spec.rb b/spec/helpers/i18n_fallbacks_spec.rb deleted file mode 100644 index 01cd56077b3..00000000000 --- a/spec/helpers/i18n_fallbacks_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'spec_helper' - -describe 'freedom patch for I18n::Backend::Fallbacks' do - before do - SiteSetting.default_locale = 'de' - I18n.locale = :de - - store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en') - store_translations(:de, :bar => 'Bar in :de') - store_translations(:'de-AT', :baz => 'Baz in :de-AT') - end - - def store_translations(locale, data) - I18n.backend.store_translations(locale, data) - end - - describe '#exists?' do - it 'returns true when a key is given that exists in the default locale' do - expect(I18n.exists?(:bar)).to be true - end - - it 'returns true when a key is given that exists in a fallback locale of the default locale' do - expect(I18n.exists?(:foo)).to be true - end - - it 'returns false when a non-existing key is given' do - expect(I18n.exists?(:bogus)).to be false - end - - it 'returns true when an existing key and an existing locale is given' do - expect(I18n.exists?(:foo, :en)).to be true - expect(I18n.exists?(:bar, :de)).to be true - expect(I18n.exists?(:baz, :'de-AT')).to be true - end - - it 'returns false when a non-existing key and an existing locale is given' do - expect(I18n.exists?(:bogus, :en)).to be false - expect(I18n.exists?(:bogus, :de)).to be false - expect(I18n.exists?(:bogus, :'de-AT')).to be false - end - - it 'returns true when a key is given which is missing from the given locale and exists in a fallback locale' do - expect(I18n.exists?(:foo, :de)).to be true - expect(I18n.exists?(:foo, :'de-AT')).to be true - end - - it 'returns true when a key is given which is missing from the given locale and all its fallback locales' do - expect(I18n.exists?(:baz, :de)).to be false - expect(I18n.exists?(:bogus, :'de-AT')).to be false - end - end -end