1 # encoding: utf-8
2
3 # Locale Fallbacks
4 #
5 # Extends the I18n module to hold a fallbacks instance which is set to an
6 # instance of I18n::Locale::Fallbacks by default but can be swapped with a
7 # different implementation.
8 #
9 # Locale fallbacks will compute a number of fallback locales for a given locale.
10 # For example:
11 #
12 # <pre><code>
13 # I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] </code></pre>
14 #
15 # Locale fallbacks always fall back to
16 #
17 # * all parent locales of a given locale (e.g. :es for :"es-MX") first,
18 # * the current default locales and all of their parents second
19 #
20 # The default locales are set to [I18n.default_locale] by default but can be
21 # set to something else.
22 #
23 # One can additionally add any number of additional fallback locales manually.
24 # These will be added before the default locales to the fallback chain. For
25 # example:
26 #
27 # # using the default locale as default fallback locale
28 #
29 # I18n.default_locale = :"en-US"
30 # I18n.fallbacks = I18n::Fallbacks.new(:"de-AT" => :"de-DE")
31 # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en]
32 #
33 # # using a custom locale as default fallback locale
34 #
35 # I18n.fallbacks = I18n::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de)
36 # I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en]
37 # I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en]
38 #
39 # # mapping fallbacks to an existing instance
40 #
41 # # people speaking Catalan also speak Spanish as spoken in Spain
42 # fallbacks = I18n.fallbacks
43 # fallbacks.map(:ca => :"es-ES")
44 # fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en]
45 #
46 # # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel
47 # fallbacks.map(:"ar-PS" => :"he-IL")
48 # fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en]
49 # fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en]
50 #
51 # # people speaking Sami as spoken in Finnland also speak Swedish and Finnish as spoken in Finnland
52 # fallbacks.map(:sms => [:"se-FI", :"fi-FI"])
53 # fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en]
54
55 module I18n
56 module Locale
57 class Fallbacks < Hash
58 def initialize(*mappings)
59 @map = {}
60 map(mappings.pop) if mappings.last.is_a?(Hash)
61 self.defaults = mappings.empty? ? [I18n.default_locale.to_sym] : mappings
62 end
63
64 def defaults=(defaults)
65 @defaults = defaults.map { |default| compute(default, false) }.flatten
66 end
67 attr_reader :defaults
68
69 def [](locale)
70 raise InvalidLocale.new(locale) if locale.nil?
71 locale = locale.to_sym
72 super || store(locale, compute(locale))
73 end
74
75 def map(mappings)
76 mappings.each do |from, to|
77 from, to = from.to_sym, Array(to)
78 to.each do |to|
79 @map[from] ||= []
80 @map[from] << to.to_sym
81 end
82 end
83 end
84
85 protected
86
87 def compute(tags, include_defaults = true)
88 result = Array(tags).collect do |tag|
89 tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym }
90 tags.each { |tag| tags += compute(@map[tag]) if @map[tag] }
91 tags
92 end.flatten
93 result.push(*defaults) if include_defaults
94 result.uniq
95 end
96 end
97 end
98 end