Skip to content

Commit

Permalink
Allow to set different globals per locale
Browse files Browse the repository at this point in the history
This changes the globals that when it has have a `default` key it can
have locale aware globals. For example:

```
I18n.config.globals = {
  salutation: 'Bye bye',
  hello: 'Hello',
  de: {
    salutation: 'Tschüss'
  },
  fr: {
    salutation: 'Salut'
  }
}
```

Depending on the locale, the global variable `salutation` will be
different. The default value is used as a fallback, so that `hello` will
be available in each language, too.
  • Loading branch information
doits committed Sep 9, 2016
1 parent 113110c commit 29ef126
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 5 deletions.
79 changes: 74 additions & 5 deletions lib/i18n/globals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,90 @@

module I18n
class Config
class CachedGlobals < Hash
def []=(key, val)
clear_cache
annotate_hash(val) if val.is_a?(Hash) # see annotate hash below why whis must be done
super(key, val)
end

def for_locale(locale)
if key?(locale)
globals_cache[locale] ||= merge(fetch(locale)).reject { |_, i| i.is_a?(Hash) }
else
globals_cache[:default] ||= reject { |_, i| i.is_a?(Hash) }
end
end

def clear
clear_cache
super
end

def merge!(val)
clear_cache
# see annotate hash below why whis must be done
val.select { |_, v| v.is_a?(Hash) }.each { |_, v| annotate_hash(v) }
super(val)
end

private

def globals_cache
@globals_cache ||= {}
end

def clear_cache
@globals_cache = {}
end

# This is a little bit cumbersome. It might happen that this is done:
#
# I18n.config.globals[:en][:welcome] = 'Hello'
#
# What this does is changing the locale dependent version of `welcome`.
# Unfortunately we only override `:[]=` for our globals hash so it
# does not detect that the globals have been changed.
#
# To overcome this we annotate every hash that might passed in with this
# method. So when the sub hash is changed like above, the whole cache
# is cleared like it should.
def annotate_hash(hash)
return if hash.instance_variable_defined?(:@cached_global)
hash.instance_variable_set(:@cached_global, self)

def hash.[]=(key, value)
super(key, value)
@cached_global.send(:clear_cache)
end

def hash.merge!(other_hash)
super(other_hash)
@cached_global.send(:clear_cache)
end

def hash.clear
super
@cached_global.send(:clear_cache)
end
end
end

def globals
@@globals ||= {}
@@globals ||= CachedGlobals.new
end

def globals=(globals)
@@globals = globals
def globals=(new_globals)
globals.clear.merge!(new_globals) # maybe there is something better than `clear` and `merge!`
end
end

class << self
def translate(*args)
if args.last.is_a?(Hash)
args[-1] = config.globals.merge(args.last)
args[-1] = config.globals.for_locale(locale).merge(args.last)
else
args << config.globals
args << config.globals.for_locale(locale)
end
super(*args)
end
Expand Down
98 changes: 98 additions & 0 deletions test/test_i18n_globals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class TestI18nGlobals < Minitest::Test
def setup
I18n.backend.load_translations 'test/fixtures/translations.yml'
I18n.config = I18n::Config.new
I18n.config.globals = {} # glear globals between runs
I18n.locale = :en
end

def test_that_simple_translations_work
Expand Down Expand Up @@ -70,4 +72,100 @@ def test_that_global_variables_are_shared_between_config_instances

assert_equal I18n.translate('greeting'), 'Hi there, Greg!'
end

def test_that_locale_dependent_variable_overrides_default_one
I18n.config.globals = {
name: 'Greg',
en: { name: 'Debby' }
}

assert_equal 'Hi there, Debby!', I18n.translate('greeting')
end

def test_that_default_variable_is_used_if_no_special_locale_version_is_present
I18n.config.globals = {
name: 'Greg',
fr: { name: 'Debora' }
}

assert_equal 'Hi there, Greg!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_setting_a_new_locale_global
I18n.config.globals = {
name: 'Greg',
en: { name: 'Debby' }
}

assert_equal 'Hi there, Debby!', I18n.translate('greeting')

I18n.config.globals[:en][:name] = 'Elisa'

assert_equal 'Hi there, Elisa!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_setting_a_new_locale_hash
I18n.config.globals = {
name: 'Greg',
en: { name: 'Debby' }
}

assert_equal 'Hi there, Debby!', I18n.translate('greeting')

I18n.config.globals[:en] = {
name: 'Elisa'
}

assert_equal 'Hi there, Elisa!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_merging_a_new_locale_hash
I18n.config.globals = {
name: 'Greg',
en: { name: 'Debby' }
}

assert_equal 'Hi there, Debby!', I18n.translate('greeting')

I18n.config.globals[:en].merge!(name: 'Elisa')

assert_equal 'Hi there, Elisa!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_clearing_locale_hash
I18n.config.globals = {
name: 'Greg',
en: { name: 'Debby' }
}

assert_equal 'Hi there, Debby!', I18n.translate('greeting')

I18n.config.globals[:en].clear

assert_equal 'Hi there, Greg!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_setting_a_new_global
I18n.config.globals = {
name: 'Greg'
}

assert_equal 'Hi there, Greg!', I18n.translate('greeting')

I18n.config.globals[:name] = 'Dobby'

assert_equal 'Hi there, Dobby!', I18n.translate('greeting')
end

def test_that_cache_is_cleared_after_merging_a_new_global
I18n.config.globals = {
name: 'Greg'
}

assert_equal 'Hi there, Greg!', I18n.translate('greeting')

I18n.config.globals.merge!(name: 'Dobby')

assert_equal 'Hi there, Dobby!', I18n.translate('greeting')
end
end

0 comments on commit 29ef126

Please sign in to comment.