PERF: bypass slow locale lookups in various cases

Previously as soon as any override was defined we would regress to the slow
path for locale lookups. Additionally if `raise: true` was specified which
rails likes to add in views we would bypass the cache

The new design manages to use the fast path for many more cases
This commit is contained in:
Sam Saffron
2019-06-05 14:30:25 +10:00
parent 78509eacb7
commit 6d8eb9c144
2 changed files with 52 additions and 11 deletions

View File

@ -19,7 +19,7 @@ module I18n
alias_method :reload_no_cache!, :reload! alias_method :reload_no_cache!, :reload!
alias_method :locale_no_cache=, :locale= alias_method :locale_no_cache=, :locale=
LRU_CACHE_SIZE = 300 LRU_CACHE_SIZE = 400
def init_accelerator! def init_accelerator!
@overrides_enabled = true @overrides_enabled = true
@ -43,7 +43,8 @@ module I18n
# load plural rules from plugins # load plural rules from plugins
DiscoursePluginRegistry.locales.each do |plugin_locale, options| DiscoursePluginRegistry.locales.each do |plugin_locale, options|
if options[:plural] if options[:plural]
I18n.backend.store_translations(plugin_locale, I18n.backend.store_translations(
plugin_locale,
i18n: { plural: options[:plural] } i18n: { plural: options[:plural] }
) )
end end
@ -94,18 +95,43 @@ module I18n
@overrides_enabled = true @overrides_enabled = true
end end
def translate_no_override(*args) class MissingTranslation; end
return translate_no_cache(*args) if args.length > 1 && args[1].present?
options = args.last.is_a?(Hash) ? args.pop.dup : {} def translate_no_override(key, options)
key = args.shift # note we skip cache for :format and :count
locale = options[:locale] || config.locale should_raise = false
locale = nil
dup_options = nil
if options
dup_options = options.dup
should_raise = dup_options.delete(:raise)
locale = dup_options.delete(:locale)
end
if dup_options.present?
return translate_no_cache(key, options)
end
locale ||= config.locale
@cache ||= LruRedux::ThreadSafeCache.new(LRU_CACHE_SIZE) @cache ||= LruRedux::ThreadSafeCache.new(LRU_CACHE_SIZE)
k = "#{key}#{locale}#{config.backend.object_id}" k = "#{key}#{locale}#{config.backend.object_id}"
@cache.getset(k) do val = @cache.getset(k) do
translate_no_cache(key, options).freeze begin
translate_no_cache(key, raise: true).freeze
rescue I18n::MissingTranslationData
MissingTranslation
end
end
if val != MissingTranslation
val
elsif should_raise
raise I18n::MissingTranslationData.new(locale, key)
else
-"translation missing: #{locale}.#{key}"
end end
end end
@ -153,11 +179,16 @@ module I18n
if @overrides_enabled if @overrides_enabled
overrides = {} overrides = {}
# for now lets do all the expensive work for keys with count
# no choice really
has_override = !!options[:count]
I18n.fallbacks[locale].each do |l| I18n.fallbacks[locale].each do |l|
overrides[l] = overrides_by_locale(l) override = overrides[l] = overrides_by_locale(l)
has_override ||= override.key?(key)
end end
if overrides.present? if has_override && overrides.present?
if options.present? if options.present?
options[:overrides] = overrides options[:overrides] = overrides

View File

@ -24,6 +24,15 @@ describe "translate accelerator" do
expect(override.persisted?).to eq(true) expect(override.persisted?).to eq(true)
end end
it "supports raising if requested, and cache bypasses" do
expect { I18n.t('i_am_an_unknown_key99', raise: true) }.to raise_error(I18n::MissingTranslationData)
orig = I18n.t('i_am_an_unknown_key99')
expect(I18n.t('i_am_an_unknown_key99').object_id).to eq(orig.object_id)
expect(I18n.t('i_am_an_unknown_key99')).to eq("translation missing: en_US.i_am_an_unknown_key99")
end
it "overrides for both string and symbol keys" do it "overrides for both string and symbol keys" do
key = 'user.email.not_allowed' key = 'user.email.not_allowed'
text_overriden = 'foobar' text_overriden = 'foobar'
@ -124,6 +133,7 @@ describe "translate accelerator" do
I18n.overrides_disabled do I18n.overrides_disabled do
expect(I18n.t('title')).to eq(orig_title) expect(I18n.t('title')).to eq(orig_title)
end end
expect(I18n.t('title')).to eq('overridden title') expect(I18n.t('title')).to eq('overridden title')
end end