sayso-i18n 0.5.0.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/CHANGELOG.textile +152 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +103 -0
  4. data/ci/Gemfile.no-rails +5 -0
  5. data/ci/Gemfile.no-rails.lock +14 -0
  6. data/ci/Gemfile.rails-2.3.x +9 -0
  7. data/ci/Gemfile.rails-2.3.x.lock +23 -0
  8. data/ci/Gemfile.rails-3.x +9 -0
  9. data/ci/Gemfile.rails-3.x.lock +23 -0
  10. data/lib/i18n.rb +324 -0
  11. data/lib/i18n/backend.rb +19 -0
  12. data/lib/i18n/backend/base.rb +174 -0
  13. data/lib/i18n/backend/cache.rb +102 -0
  14. data/lib/i18n/backend/cascade.rb +53 -0
  15. data/lib/i18n/backend/chain.rb +80 -0
  16. data/lib/i18n/backend/fallbacks.rb +70 -0
  17. data/lib/i18n/backend/flatten.rb +113 -0
  18. data/lib/i18n/backend/flatten_yml.rb +70 -0
  19. data/lib/i18n/backend/gettext.rb +71 -0
  20. data/lib/i18n/backend/interpolation_compiler.rb +121 -0
  21. data/lib/i18n/backend/key_value.rb +100 -0
  22. data/lib/i18n/backend/memoize.rb +46 -0
  23. data/lib/i18n/backend/metadata.rb +65 -0
  24. data/lib/i18n/backend/pluralization.rb +55 -0
  25. data/lib/i18n/backend/simple.rb +87 -0
  26. data/lib/i18n/backend/transliterator.rb +98 -0
  27. data/lib/i18n/config.rb +86 -0
  28. data/lib/i18n/core_ext/hash.rb +29 -0
  29. data/lib/i18n/core_ext/kernel/surpress_warnings.rb +9 -0
  30. data/lib/i18n/core_ext/string/interpolate.rb +105 -0
  31. data/lib/i18n/exceptions.rb +88 -0
  32. data/lib/i18n/gettext.rb +25 -0
  33. data/lib/i18n/gettext/helpers.rb +64 -0
  34. data/lib/i18n/gettext/po_parser.rb +329 -0
  35. data/lib/i18n/interpolate/ruby.rb +31 -0
  36. data/lib/i18n/locale.rb +6 -0
  37. data/lib/i18n/locale/fallbacks.rb +96 -0
  38. data/lib/i18n/locale/tag.rb +28 -0
  39. data/lib/i18n/locale/tag/parents.rb +22 -0
  40. data/lib/i18n/locale/tag/rfc4646.rb +74 -0
  41. data/lib/i18n/locale/tag/simple.rb +39 -0
  42. data/lib/i18n/tests.rb +12 -0
  43. data/lib/i18n/tests/basics.rb +54 -0
  44. data/lib/i18n/tests/defaults.rb +40 -0
  45. data/lib/i18n/tests/interpolation.rb +133 -0
  46. data/lib/i18n/tests/link.rb +56 -0
  47. data/lib/i18n/tests/localization.rb +19 -0
  48. data/lib/i18n/tests/localization/date.rb +84 -0
  49. data/lib/i18n/tests/localization/date_time.rb +77 -0
  50. data/lib/i18n/tests/localization/procs.rb +116 -0
  51. data/lib/i18n/tests/localization/time.rb +76 -0
  52. data/lib/i18n/tests/lookup.rb +74 -0
  53. data/lib/i18n/tests/pluralization.rb +35 -0
  54. data/lib/i18n/tests/procs.rb +55 -0
  55. data/lib/i18n/version.rb +3 -0
  56. data/test/all.rb +8 -0
  57. data/test/api/all_features_test.rb +58 -0
  58. data/test/api/cascade_test.rb +28 -0
  59. data/test/api/chain_test.rb +24 -0
  60. data/test/api/fallbacks_test.rb +30 -0
  61. data/test/api/flatten_yml_test.rb +32 -0
  62. data/test/api/key_value_test.rb +28 -0
  63. data/test/api/memoize_test.rb +60 -0
  64. data/test/api/pluralization_test.rb +30 -0
  65. data/test/api/simple_test.rb +28 -0
  66. data/test/backend/cache_test.rb +83 -0
  67. data/test/backend/cascade_test.rb +85 -0
  68. data/test/backend/chain_test.rb +72 -0
  69. data/test/backend/exceptions_test.rb +23 -0
  70. data/test/backend/fallbacks_test.rb +120 -0
  71. data/test/backend/flatten_yml_test.rb +49 -0
  72. data/test/backend/interpolation_compiler_test.rb +102 -0
  73. data/test/backend/key_value_test.rb +46 -0
  74. data/test/backend/memoize_test.rb +47 -0
  75. data/test/backend/metadata_test.rb +67 -0
  76. data/test/backend/pluralization_test.rb +44 -0
  77. data/test/backend/simple_test.rb +83 -0
  78. data/test/backend/transliterator_test.rb +81 -0
  79. data/test/core_ext/hash_test.rb +30 -0
  80. data/test/core_ext/string/interpolate_test.rb +99 -0
  81. data/test/gettext/api_test.rb +206 -0
  82. data/test/gettext/backend_test.rb +93 -0
  83. data/test/i18n/exceptions_test.rb +116 -0
  84. data/test/i18n/interpolate_test.rb +61 -0
  85. data/test/i18n/load_path_test.rb +26 -0
  86. data/test/i18n_test.rb +236 -0
  87. data/test/locale/fallbacks_test.rb +124 -0
  88. data/test/locale/tag/rfc4646_test.rb +142 -0
  89. data/test/locale/tag/simple_test.rb +32 -0
  90. data/test/run_all.rb +21 -0
  91. data/test/test_data/locales/de.po +72 -0
  92. data/test/test_data/locales/en.rb +3 -0
  93. data/test/test_data/locales/en.yml +3 -0
  94. data/test/test_data/locales/invalid/empty.yml +1 -0
  95. data/test/test_data/locales/plurals.rb +113 -0
  96. data/test/test_helper.rb +56 -0
  97. metadata +194 -0
@@ -0,0 +1,46 @@
1
+ # Memoize module simply memoizes the values returned by lookup using
2
+ # a flat hash and can tremendously speed up the lookup process in a backend.
3
+ #
4
+ # To enable it you can simply include the Memoize module to your backend:
5
+ #
6
+ # I18n::Backend::Simple.include(I18n::Backend::Memoize)
7
+ #
8
+ # Notice that it's the responsibility of the backend to define whenever the
9
+ # cache should be cleaned.
10
+ module I18n
11
+ module Backend
12
+ module Memoize
13
+ def available_locales
14
+ @memoized_locales ||= super
15
+ end
16
+
17
+ def store_translations(locale, data, options = {})
18
+ reset_memoizations!(locale)
19
+ super
20
+ end
21
+
22
+ def reload!
23
+ reset_memoizations!
24
+ super
25
+ end
26
+
27
+ protected
28
+
29
+ def lookup(locale, key, scope = nil, options = {})
30
+ flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale,
31
+ key, scope, options[:separator]).to_sym
32
+ flat_hash = memoized_lookup[locale.to_sym]
33
+ flat_hash.key?(flat_key) ? flat_hash[flat_key] : (flat_hash[flat_key] = super)
34
+ end
35
+
36
+ def memoized_lookup
37
+ @memoized_lookup ||= Hash.new { |h, k| h[k] = {} }
38
+ end
39
+
40
+ def reset_memoizations!(locale=nil)
41
+ @memoized_locales = nil
42
+ (locale ? memoized_lookup[locale.to_sym] : memoized_lookup).clear
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,65 @@
1
+ # I18n translation metadata is useful when you want to access information
2
+ # about how a translation was looked up, pluralized or interpolated in
3
+ # your application.
4
+ #
5
+ # msg = I18n.t(:message, :default => 'Hi!', :scope => :foo)
6
+ # msg.translation_metadata
7
+ # # => { :key => :message, :scope => :foo, :default => 'Hi!' }
8
+ #
9
+ # If a :count option was passed to #translate it will be set to the metadata.
10
+ # Likewise, if any interpolation variables were passed they will also be set.
11
+ #
12
+ # To enable translation metadata you can simply include the Metadata module
13
+ # into the Simple backend class - or whatever other backend you are using:
14
+ #
15
+ # I18n::Backend::Simple.include(I18n::Backend::Metadata)
16
+ #
17
+ module I18n
18
+ module Backend
19
+ module Metadata
20
+ class << self
21
+ def included(base)
22
+ Object.class_eval do
23
+ def translation_metadata
24
+ @translation_metadata ||= {}
25
+ end
26
+
27
+ def translation_metadata=(translation_metadata)
28
+ @translation_metadata = translation_metadata
29
+ end
30
+ end unless Object.method_defined?(:translation_metadata)
31
+ end
32
+ end
33
+
34
+ def translate(locale, key, options = {})
35
+ metadata = {
36
+ :locale => locale,
37
+ :key => key,
38
+ :scope => options[:scope],
39
+ :default => options[:default],
40
+ :separator => options[:separator],
41
+ :values => options.reject { |name, value| RESERVED_KEYS.include?(name) }
42
+ }
43
+ with_metadata(metadata) { super }
44
+ end
45
+
46
+ def interpolate(locale, entry, values = {})
47
+ metadata = entry.translation_metadata.merge(:original => entry)
48
+ with_metadata(metadata) { super }
49
+ end
50
+
51
+ def pluralize(locale, entry, count)
52
+ with_metadata(:count => count) { super }
53
+ end
54
+
55
+ protected
56
+
57
+ def with_metadata(metadata, &block)
58
+ result = yield
59
+ result.translation_metadata = result.translation_metadata.merge(metadata) if result
60
+ result
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,55 @@
1
+ # I18n locale fallbacks are useful when you want your application to use
2
+ # translations from other locales when translations for the current locale are
3
+ # missing. E.g. you might want to use :en translations when translations in
4
+ # your applications main locale :de are missing.
5
+ #
6
+ # To enable locale specific pluralizations you can simply include the
7
+ # Pluralization module to the Simple backend - or whatever other backend you
8
+ # are using.
9
+ #
10
+ # I18n::Backend::Simple.include(I18n::Backend::Pluralization)
11
+ #
12
+ # You also need to make sure to provide pluralization algorithms to the
13
+ # backend, i.e. include them to your I18n.load_path accordingly.
14
+ module I18n
15
+ module Backend
16
+ module Pluralization
17
+ # Overwrites the Base backend translate method so that it will check the
18
+ # translation meta data space (:i18n) for a locale specific pluralization
19
+ # rule and use it to pluralize the given entry. I.e. the library expects
20
+ # pluralization rules to be stored at I18n.t(:'i18n.plural.rule')
21
+ #
22
+ # Pluralization rules are expected to respond to #call(count) and
23
+ # return a pluralization key. Valid keys depend on the translation data
24
+ # hash (entry) but it is generally recommended to follow CLDR's style,
25
+ # i.e., return one of the keys :zero, :one, :few, :many, :other.
26
+ #
27
+ # The :zero key is always picked directly when count equals 0 AND the
28
+ # translation data has the key :zero. This way translators are free to
29
+ # either pick a special :zero translation even for languages where the
30
+ # pluralizer does not return a :zero key.
31
+ def pluralize(locale, entry, count)
32
+ return entry unless entry.is_a?(Hash) and count
33
+
34
+ pluralizer = pluralizer(locale)
35
+ if pluralizer.respond_to?(:call)
36
+ key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count)
37
+ raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
38
+ entry[key]
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+ protected
45
+
46
+ def pluralizers
47
+ @pluralizers ||= {}
48
+ end
49
+
50
+ def pluralizer(locale)
51
+ pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,87 @@
1
+ module I18n
2
+ module Backend
3
+ # A simple backend that reads translations from YAML files and stores them in
4
+ # an in-memory hash. Relies on the Base backend.
5
+ #
6
+ # The implementation is provided by a Implementation module allowing to easily
7
+ # extend Simple backend's behavior by including modules. E.g.:
8
+ #
9
+ # module I18n::Backend::Pluralization
10
+ # def pluralize(*args)
11
+ # # extended pluralization logic
12
+ # super
13
+ # end
14
+ # end
15
+ #
16
+ # I18n::Backend::Simple.include(I18n::Backend::Pluralization)
17
+ class Simple
18
+ (class << self; self; end).class_eval { public :include }
19
+
20
+ module Implementation
21
+ include Base
22
+
23
+ def initialized?
24
+ @initialized ||= false
25
+ end
26
+
27
+ # Stores translations for the given locale in memory.
28
+ # This uses a deep merge for the translations hash, so existing
29
+ # translations will be overwritten by new ones only at the deepest
30
+ # level of the hash.
31
+ def store_translations(locale, data, options = {})
32
+ locale = locale.to_sym
33
+ translations[locale] ||= {}
34
+ data = data.deep_symbolize_keys
35
+ translations[locale].deep_merge!(data)
36
+ end
37
+
38
+ # Get available locales from the translations hash
39
+ def available_locales
40
+ init_translations unless initialized?
41
+ translations.inject([]) do |locales, (locale, data)|
42
+ locales << locale unless (data.keys - [:i18n]).empty?
43
+ locales
44
+ end
45
+ end
46
+
47
+ # Clean up translations hash and set initialized to false on reload!
48
+ def reload!
49
+ @initialized = false
50
+ @translations = nil
51
+ super
52
+ end
53
+
54
+ protected
55
+
56
+ def init_translations
57
+ load_translations
58
+ @initialized = true
59
+ end
60
+
61
+ def translations
62
+ @translations ||= {}
63
+ end
64
+
65
+ # Looks up a translation from the translations hash. Returns nil if
66
+ # eiher key is nil, or locale, scope or key do not exist as a key in the
67
+ # nested translations hash. Splits keys or scopes containing dots
68
+ # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
69
+ # <tt>%w(currency format)</tt>.
70
+ def lookup(locale, key, scope = [], options = {})
71
+ init_translations unless initialized?
72
+ keys = I18n.normalize_keys(locale, key, scope, options[:separator])
73
+
74
+ keys.inject(translations) do |result, _key|
75
+ _key = _key.to_sym
76
+ return nil unless result.is_a?(Hash) && result.has_key?(_key)
77
+ result = result[_key]
78
+ result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
79
+ result
80
+ end
81
+ end
82
+ end
83
+
84
+ include Implementation
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,98 @@
1
+ # encoding: utf-8
2
+ module I18n
3
+ module Backend
4
+ module Transliterator
5
+ DEFAULT_REPLACEMENT_CHAR = "?"
6
+
7
+ # Given a locale and a UTF-8 string, return the locale's ASCII
8
+ # approximation for the string.
9
+ def transliterate(locale, string, replacement = nil)
10
+ @transliterators ||= {}
11
+ @transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
12
+ :locale => locale, :resolve => false, :default => {})
13
+ @transliterators[locale].transliterate(string, replacement)
14
+ end
15
+
16
+ # Get a transliterator instance.
17
+ def self.get(rule = nil)
18
+ if !rule || rule.kind_of?(Hash)
19
+ HashTransliterator.new(rule)
20
+ elsif rule.kind_of? Proc
21
+ ProcTransliterator.new(rule)
22
+ else
23
+ raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
24
+ end
25
+ end
26
+
27
+ # A transliterator which accepts a Proc as its transliteration rule.
28
+ class ProcTransliterator
29
+ def initialize(rule)
30
+ @rule = rule
31
+ end
32
+
33
+ def transliterate(string, replacement = nil)
34
+ @rule.call(string)
35
+ end
36
+ end
37
+
38
+ # A transliterator which accepts a Hash of characters as its translation
39
+ # rule.
40
+ class HashTransliterator
41
+ DEFAULT_APPROXIMATIONS = {
42
+ "À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
43
+ "Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
44
+ "Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
45
+ "Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
46
+ "Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
47
+ "ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
48
+ "ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
49
+ "ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
50
+ "ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
51
+ "Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
52
+ "ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
53
+ "Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
54
+ "ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
55
+ "Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
56
+ "ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
57
+ "Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
58
+ "ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
59
+ "ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
60
+ "Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
61
+ "ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
62
+ "Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
63
+ "œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
64
+ "Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
65
+ "š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
66
+ "Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
67
+ "ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
68
+ "Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
69
+ "Ž"=>"Z", "ž"=>"z"
70
+ }
71
+
72
+ def initialize(rule = nil)
73
+ @rule = rule
74
+ add DEFAULT_APPROXIMATIONS
75
+ add rule if rule
76
+ end
77
+
78
+ def transliterate(string, replacement = nil)
79
+ string.gsub(/[^\x00-\x7f]/u) do |char|
80
+ approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def approximations
87
+ @approximations ||= {}
88
+ end
89
+
90
+ # Add transliteration rules to the approximations hash.
91
+ def add(hash)
92
+ hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
93
+ approximations.merge! hash
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,86 @@
1
+ module I18n
2
+ class Config
3
+ # The only configuration value that is not global and scoped to thread is :locale.
4
+ # It defaults to the default_locale.
5
+ def locale
6
+ @locale ||= default_locale
7
+ end
8
+
9
+ # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
10
+ def locale=(locale)
11
+ @locale = locale.to_sym rescue nil
12
+ end
13
+
14
+ # Returns the current backend. Defaults to +Backend::Simple+.
15
+ def backend
16
+ @@backend ||= Backend::Simple.new
17
+ end
18
+
19
+ # Sets the current backend. Used to set a custom backend.
20
+ def backend=(backend)
21
+ @@backend = backend
22
+ end
23
+
24
+ # Returns the current default locale. Defaults to :'en'
25
+ def default_locale
26
+ @@default_locale ||= :en
27
+ end
28
+
29
+ # Sets the current default locale. Used to set a custom default locale.
30
+ def default_locale=(locale)
31
+ @@default_locale = locale.to_sym rescue nil
32
+ end
33
+
34
+ # Returns an array of locales for which translations are available.
35
+ # Unless you explicitely set these through I18n.available_locales=
36
+ # the call will be delegated to the backend.
37
+ def available_locales
38
+ @@available_locales ||= nil
39
+ @@available_locales || backend.available_locales
40
+ end
41
+
42
+ # Sets the available locales.
43
+ def available_locales=(locales)
44
+ @@available_locales = Array(locales).map { |locale| locale.to_sym }
45
+ @@available_locales = nil if @@available_locales.empty?
46
+ end
47
+
48
+ # Returns the current default scope separator. Defaults to '.'
49
+ def default_separator
50
+ @@default_separator ||= '.'
51
+ end
52
+
53
+ # Sets the current default scope separator.
54
+ def default_separator=(separator)
55
+ @@default_separator = separator
56
+ end
57
+
58
+ # Return the current exception handler. Defaults to :default_exception_handler.
59
+ def exception_handler
60
+ @@exception_handler ||= ExceptionHandler.new
61
+ end
62
+
63
+ # Sets the exception handler.
64
+ def exception_handler=(exception_handler)
65
+ @@exception_handler = exception_handler
66
+ end
67
+
68
+ # Allow clients to register paths providing translation data sources. The
69
+ # backend defines acceptable sources.
70
+ #
71
+ # E.g. the provided SimpleBackend accepts a list of paths to translation
72
+ # files which are either named *.rb and contain plain Ruby Hashes or are
73
+ # named *.yml and contain YAML data. So for the SimpleBackend clients may
74
+ # register translation files like this:
75
+ # I18n.load_path << 'path/to/locale/en.yml'
76
+ def load_path
77
+ @@load_path ||= []
78
+ end
79
+
80
+ # Sets the load path instance. Custom implementations are expected to
81
+ # behave like a Ruby Array.
82
+ def load_path=(load_path)
83
+ @@load_path = load_path
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,29 @@
1
+ class Hash
2
+ def slice(*keep_keys)
3
+ h = {}
4
+ keep_keys.each { |key| h[key] = fetch(key) }
5
+ h
6
+ end unless Hash.method_defined?(:slice)
7
+
8
+ def except(*less_keys)
9
+ slice(*keys - less_keys)
10
+ end unless Hash.method_defined?(:except)
11
+
12
+ def deep_symbolize_keys
13
+ inject({}) { |result, (key, value)|
14
+ value = value.deep_symbolize_keys if value.is_a?(Hash)
15
+ result[(key.to_sym rescue key) || key] = value
16
+ result
17
+ }
18
+ end unless Hash.method_defined?(:deep_symbolize_keys)
19
+
20
+ # deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
21
+ MERGER = proc do |key, v1, v2|
22
+ Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
23
+ end
24
+
25
+ def deep_merge!(data)
26
+ merge!(data, &MERGER)
27
+ end unless Hash.method_defined?(:deep_merge!)
28
+ end
29
+