Skip to content

Instantly share code, notes, and snippets.

@westonganger
Last active May 3, 2022 13:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonganger/7d56856ff441253f9145380c6fab0d3b to your computer and use it in GitHub Desktop.
Save westonganger/7d56856ff441253f9145380c6fab0d3b to your computer and use it in GitHub Desktop.
Good Rails I18n Backend Setup
module I18n
class CustomExceptionHandler < ExceptionHandler
def call(exception, locale, key, options)
if exception.is_a?(MissingTranslation) && key.to_s != 'i18n.plural.rule'
raise exception.to_exception
else
super
end
end
end
end
### Exception handler to actually raise translation missing exceptions instead of showing 'translation missing' text
I18n.exception_handler = I18n::CustomExceptionHandler.new
module I18n
module Backend
class Custom
### Translations are stored in DB and synced to redis
### Translations are loaded per-app from Redis
include I18n::Backend::Base
include I18n::Backend::Flatten
def translations
@translations || set_translations
end
def available_locales
@available_locales || set_available_locales
end
def initialized?
!@translations.nil?
end
def reload!
set_translations
return true
end
def translate(locale, key, options = EMPTY_HASH)
split_keys = I18n.normalize_keys(locale, key, options[:scope], options[:separator])
val = translations.dig(*split_keys)
if val.blank? && options[:fallback]
if val.blank? && I18n.locale != I18n.default_locale
alternate_split_keys = split_keys.dup
alternate_split_keys[2] = I18n.default_locale.to_s
val = translations.dig(*alternate_split_keys)
end
# if val.blank?
# generic_key = "common.#{key.to_s.split(".").last}"
# if I18n.exists?(generic_key)
# return I18n.t(generic_key, fallback: false)
# end
# end
end
if val.blank?
#throw(:exception, I18n::MissingTranslation.new(locale, key, options))
return nil
else
return val
end
end
def store_translations(locale, data, options = EMPTY_HASH)
### IN-MEMORY STORE
if @translations.nil?
set_translations
end
locale = locale.to_sym
@temporary_translations ||= {}
#flattened_h = flatten_translations(locale, data, options.fetch(:escape, true), false)
#raise "#{flattened_h}" ### DEBUG
@temporary_translations[locale] ||= Concurrent::Hash.new
@temporary_translations[locale] = @temporary_translations[locale].deep_merge(data.deep_symbolize_keys)
@translations = @translations.deep_merge(@temporary_translations)
set_available_locales
return true
end
protected
def set_translations
@translations = Concurrent::Hash.new
db_translation_data = REDIS.get("translations").fetch(APP_NAME).with_indifferent_access rescue {}
@translations = @translations.deep_merge(db_translation_data)
if @temporary_translations
@translations = @translations.deep_merge(@temporary_translations)
end
set_available_locales
return @translations
end
def set_available_locales
@available_locales = translations.keys.map{|x| x.to_sym}
end
end
end
end
backend_chain = [I18n::Backend::Custom.new]
if Rails.env.development? || Rails.env.test?
### Keeps the original YAML backend as a fallback so that local development still works without a populated translation DB
backend_chain << I18n::Backend::Simple.new
end
I18n.backend = I18n::Backend::Chain.new(*backend_chain)
### Manually add any additional translations here
# I18n.backend.store_translations :en, foo: {bar: {foobar: 'asd'} }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment