i18n 0.9.5 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +71 -32
- data/lib/i18n/backend/base.rb +75 -31
- data/lib/i18n/backend/cache.rb +10 -11
- data/lib/i18n/backend/cache_file.rb +36 -0
- data/lib/i18n/backend/cascade.rb +3 -1
- data/lib/i18n/backend/chain.rb +37 -6
- data/lib/i18n/backend/fallbacks.rb +43 -14
- data/lib/i18n/backend/flatten.rb +10 -5
- data/lib/i18n/backend/gettext.rb +4 -2
- data/lib/i18n/backend/interpolation_compiler.rb +3 -1
- data/lib/i18n/backend/key_value.rb +31 -4
- data/lib/i18n/backend/lazy_loadable.rb +184 -0
- data/lib/i18n/backend/memoize.rb +10 -2
- data/lib/i18n/backend/metadata.rb +5 -3
- data/lib/i18n/backend/pluralization.rb +3 -1
- data/lib/i18n/backend/simple.rb +29 -16
- data/lib/i18n/backend/transliterator.rb +2 -0
- data/lib/i18n/backend.rb +5 -1
- data/lib/i18n/config.rb +20 -2
- data/lib/i18n/exceptions.rb +60 -17
- data/lib/i18n/gettext/helpers.rb +4 -2
- data/lib/i18n/gettext/po_parser.rb +7 -7
- data/lib/i18n/gettext.rb +2 -0
- data/lib/i18n/interpolate/ruby.rb +8 -6
- data/lib/i18n/locale/fallbacks.rb +21 -20
- data/lib/i18n/locale/tag/parents.rb +8 -6
- data/lib/i18n/locale/tag/simple.rb +1 -1
- data/lib/i18n/locale.rb +2 -0
- data/lib/i18n/middleware.rb +2 -0
- data/lib/i18n/tests/basics.rb +3 -5
- data/lib/i18n/tests/defaults.rb +1 -1
- data/lib/i18n/tests/interpolation.rb +12 -7
- data/lib/i18n/tests/link.rb +11 -1
- data/lib/i18n/tests/localization/date.rb +32 -10
- data/lib/i18n/tests/localization/date_time.rb +28 -7
- data/lib/i18n/tests/localization/procs.rb +7 -5
- data/lib/i18n/tests/localization/time.rb +27 -5
- data/lib/i18n/tests/lookup.rb +4 -4
- data/lib/i18n/tests/pluralization.rb +1 -1
- data/lib/i18n/tests/procs.rb +12 -1
- data/lib/i18n/tests.rb +2 -0
- data/lib/i18n/utils.rb +55 -0
- data/lib/i18n/version.rb +3 -1
- data/lib/i18n.rb +123 -50
- metadata +16 -61
- data/gemfiles/Gemfile.rails-3.2.x +0 -10
- data/gemfiles/Gemfile.rails-4.0.x +0 -10
- data/gemfiles/Gemfile.rails-4.1.x +0 -10
- data/gemfiles/Gemfile.rails-4.2.x +0 -10
- data/gemfiles/Gemfile.rails-5.0.x +0 -10
- data/gemfiles/Gemfile.rails-5.1.x +0 -10
- data/gemfiles/Gemfile.rails-master +0 -10
- data/lib/i18n/core_ext/hash.rb +0 -29
- data/lib/i18n/core_ext/kernel/suppress_warnings.rb +0 -8
- data/lib/i18n/core_ext/string/interpolate.rb +0 -9
- data/test/api/all_features_test.rb +0 -58
- data/test/api/cascade_test.rb +0 -28
- data/test/api/chain_test.rb +0 -24
- data/test/api/fallbacks_test.rb +0 -30
- data/test/api/key_value_test.rb +0 -24
- data/test/api/memoize_test.rb +0 -56
- data/test/api/override_test.rb +0 -42
- data/test/api/pluralization_test.rb +0 -30
- data/test/api/simple_test.rb +0 -28
- data/test/backend/cache_test.rb +0 -109
- data/test/backend/cascade_test.rb +0 -86
- data/test/backend/chain_test.rb +0 -122
- data/test/backend/exceptions_test.rb +0 -36
- data/test/backend/fallbacks_test.rb +0 -219
- data/test/backend/interpolation_compiler_test.rb +0 -118
- data/test/backend/key_value_test.rb +0 -61
- data/test/backend/memoize_test.rb +0 -79
- data/test/backend/metadata_test.rb +0 -48
- data/test/backend/pluralization_test.rb +0 -45
- data/test/backend/simple_test.rb +0 -103
- data/test/backend/transliterator_test.rb +0 -84
- data/test/core_ext/hash_test.rb +0 -36
- data/test/gettext/api_test.rb +0 -214
- data/test/gettext/backend_test.rb +0 -92
- data/test/i18n/exceptions_test.rb +0 -117
- data/test/i18n/gettext_plural_keys_test.rb +0 -20
- data/test/i18n/interpolate_test.rb +0 -91
- data/test/i18n/load_path_test.rb +0 -34
- data/test/i18n/middleware_test.rb +0 -24
- data/test/i18n_test.rb +0 -462
- data/test/locale/fallbacks_test.rb +0 -133
- data/test/locale/tag/rfc4646_test.rb +0 -143
- data/test/locale/tag/simple_test.rb +0 -32
- data/test/run_all.rb +0 -20
- data/test/test_data/locales/de.po +0 -82
- data/test/test_data/locales/en.rb +0 -3
- data/test/test_data/locales/en.yml +0 -3
- data/test/test_data/locales/invalid/empty.yml +0 -0
- data/test/test_data/locales/invalid/syntax.yml +0 -4
- data/test/test_data/locales/plurals.rb +0 -113
- data/test/test_helper.rb +0 -61
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# I18n locale fallbacks are useful when you want your application to use
|
2
4
|
# translations from other locales when translations for the current locale are
|
3
5
|
# missing. E.g. you might want to use :en translations when translations in
|
@@ -14,11 +16,13 @@ module I18n
|
|
14
16
|
# Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
|
15
17
|
def fallbacks
|
16
18
|
@@fallbacks ||= I18n::Locale::Fallbacks.new
|
19
|
+
Thread.current[:i18n_fallbacks] || @@fallbacks
|
17
20
|
end
|
18
21
|
|
19
22
|
# Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
|
20
23
|
def fallbacks=(fallbacks)
|
21
|
-
@@fallbacks = fallbacks
|
24
|
+
@@fallbacks = fallbacks.is_a?(Array) ? I18n::Locale::Fallbacks.new(fallbacks) : fallbacks
|
25
|
+
Thread.current[:i18n_fallbacks] = @@fallbacks
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
@@ -34,25 +38,24 @@ module I18n
|
|
34
38
|
# The default option takes precedence over fallback locales only when
|
35
39
|
# it's a Symbol. When the default contains a String, Proc or Hash
|
36
40
|
# it is evaluated last after all the fallback locales have been tried.
|
37
|
-
def translate(locale, key, options =
|
41
|
+
def translate(locale, key, options = EMPTY_HASH)
|
38
42
|
return super unless options.fetch(:fallback, true)
|
39
43
|
return super if options[:fallback_in_progress]
|
40
44
|
default = extract_non_symbol_default!(options) if options[:default]
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
fallback_options = options.merge(:fallback_in_progress => true, fallback_original_locale: locale)
|
47
|
+
I18n.fallbacks[locale].each do |fallback|
|
48
|
+
begin
|
49
|
+
catch(:exception) do
|
50
|
+
result = super(fallback, key, fallback_options)
|
51
|
+
unless result.nil?
|
52
|
+
on_fallback(locale, fallback, key, options) if locale.to_s != fallback.to_s
|
53
|
+
return result
|
49
54
|
end
|
50
|
-
rescue I18n::InvalidLocale
|
51
|
-
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
52
55
|
end
|
56
|
+
rescue I18n::InvalidLocale
|
57
|
+
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
53
58
|
end
|
54
|
-
ensure
|
55
|
-
options.delete(:fallback_in_progress)
|
56
59
|
end
|
57
60
|
|
58
61
|
return if options.key?(:default) && options[:default].nil?
|
@@ -61,6 +64,24 @@ module I18n
|
|
61
64
|
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
|
62
65
|
end
|
63
66
|
|
67
|
+
def resolve_entry(locale, object, subject, options = EMPTY_HASH)
|
68
|
+
return subject if options[:resolve] == false
|
69
|
+
result = catch(:exception) do
|
70
|
+
options.delete(:fallback_in_progress) if options.key?(:fallback_in_progress)
|
71
|
+
|
72
|
+
case subject
|
73
|
+
when Symbol
|
74
|
+
I18n.translate(subject, **options.merge(:locale => options[:fallback_original_locale], :throw => true))
|
75
|
+
when Proc
|
76
|
+
date_or_time = options.delete(:object) || object
|
77
|
+
resolve_entry(options[:fallback_original_locale], object, subject.call(date_or_time, **options))
|
78
|
+
else
|
79
|
+
subject
|
80
|
+
end
|
81
|
+
end
|
82
|
+
result unless result.is_a?(MissingTranslation)
|
83
|
+
end
|
84
|
+
|
64
85
|
def extract_non_symbol_default!(options)
|
65
86
|
defaults = [options[:default]].flatten
|
66
87
|
first_non_symbol_default = defaults.detect{|default| !default.is_a?(Symbol)}
|
@@ -70,7 +91,8 @@ module I18n
|
|
70
91
|
return first_non_symbol_default
|
71
92
|
end
|
72
93
|
|
73
|
-
def exists?(locale, key)
|
94
|
+
def exists?(locale, key, options = EMPTY_HASH)
|
95
|
+
return super unless options.fetch(:fallback, true)
|
74
96
|
I18n.fallbacks[locale].each do |fallback|
|
75
97
|
begin
|
76
98
|
return true if super(fallback, key)
|
@@ -81,6 +103,13 @@ module I18n
|
|
81
103
|
|
82
104
|
false
|
83
105
|
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
# Overwrite on_fallback to add specified logic when the fallback succeeds.
|
110
|
+
def on_fallback(_original_locale, _fallback_locale, _key, _optoins)
|
111
|
+
nil
|
112
|
+
end
|
84
113
|
end
|
85
114
|
end
|
86
115
|
end
|
data/lib/i18n/backend/flatten.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module I18n
|
2
4
|
module Backend
|
3
5
|
# This module contains several helpers to assist flattening translations.
|
@@ -16,14 +18,17 @@ module I18n
|
|
16
18
|
# and creates way less objects than the one at I18n.normalize_keys.
|
17
19
|
# It also handles escaping the translation keys.
|
18
20
|
def self.normalize_flat_keys(locale, key, scope, separator)
|
19
|
-
keys = [scope, key]
|
21
|
+
keys = [scope, key]
|
22
|
+
keys.flatten!
|
23
|
+
keys.compact!
|
24
|
+
|
20
25
|
separator ||= I18n.default_separator
|
21
26
|
|
22
27
|
if separator != FLATTEN_SEPARATOR
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
from_str = "#{FLATTEN_SEPARATOR}#{separator}"
|
29
|
+
to_str = "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}"
|
30
|
+
|
31
|
+
keys.map! { |k| k.to_s.tr from_str, to_str }
|
27
32
|
end
|
28
33
|
|
29
34
|
keys.join(".")
|
data/lib/i18n/backend/gettext.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'i18n/gettext'
|
2
4
|
require 'i18n/gettext/po_parser'
|
3
5
|
|
@@ -39,7 +41,7 @@ module I18n
|
|
39
41
|
def load_po(filename)
|
40
42
|
locale = ::File.basename(filename, '.po').to_sym
|
41
43
|
data = normalize(locale, parse(filename))
|
42
|
-
{ locale => data }
|
44
|
+
[{ locale => data }, false]
|
43
45
|
end
|
44
46
|
|
45
47
|
def parse(filename)
|
@@ -57,7 +59,7 @@ module I18n
|
|
57
59
|
{ part => _normalized.empty? ? value : _normalized }
|
58
60
|
end
|
59
61
|
|
60
|
-
|
62
|
+
Utils.deep_merge!(result, normalized)
|
61
63
|
end
|
62
64
|
result
|
63
65
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# The InterpolationCompiler module contains optimizations that can tremendously
|
2
4
|
# speed up the interpolation process on the Simple backend.
|
3
5
|
#
|
@@ -104,7 +106,7 @@ module I18n
|
|
104
106
|
end
|
105
107
|
end
|
106
108
|
|
107
|
-
def store_translations(locale, data, options =
|
109
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
108
110
|
compile_all_strings_in(data)
|
109
111
|
super
|
110
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'i18n/backend/base'
|
2
4
|
|
3
5
|
module I18n
|
@@ -74,7 +76,11 @@ module I18n
|
|
74
76
|
@store, @subtrees = store, subtrees
|
75
77
|
end
|
76
78
|
|
77
|
-
def
|
79
|
+
def initialized?
|
80
|
+
!@store.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
78
84
|
escape = options.fetch(:escape, true)
|
79
85
|
flatten_translations(locale, data, escape, @subtrees).each do |key, value|
|
80
86
|
key = "#{locale}.#{key}"
|
@@ -83,7 +89,7 @@ module I18n
|
|
83
89
|
when Hash
|
84
90
|
if @subtrees && (old_value = @store[key])
|
85
91
|
old_value = JSON.decode(old_value)
|
86
|
-
value =
|
92
|
+
value = Utils.deep_merge!(Utils.deep_symbolize_keys(old_value), value) if old_value.is_a?(Hash)
|
87
93
|
end
|
88
94
|
when Proc
|
89
95
|
raise "Key-value stores cannot handle procs"
|
@@ -103,17 +109,37 @@ module I18n
|
|
103
109
|
|
104
110
|
protected
|
105
111
|
|
112
|
+
# Queries the translations from the key-value store and converts
|
113
|
+
# them into a hash such as the one returned from loading the
|
114
|
+
# haml files
|
115
|
+
def translations
|
116
|
+
@translations = Utils.deep_symbolize_keys(@store.keys.clone.map do |main_key|
|
117
|
+
main_value = JSON.decode(@store[main_key])
|
118
|
+
main_key.to_s.split(".").reverse.inject(main_value) do |value, key|
|
119
|
+
{key.to_sym => value}
|
120
|
+
end
|
121
|
+
end.inject{|hash, elem| Utils.deep_merge!(hash, elem)})
|
122
|
+
end
|
123
|
+
|
124
|
+
def init_translations
|
125
|
+
# NO OP
|
126
|
+
# This call made also inside Simple Backend and accessed by
|
127
|
+
# other plugins like I18n-js and babilu and
|
128
|
+
# to use it along with the Chain backend we need to
|
129
|
+
# provide a uniform API even for protected methods :S
|
130
|
+
end
|
131
|
+
|
106
132
|
def subtrees?
|
107
133
|
@subtrees
|
108
134
|
end
|
109
135
|
|
110
|
-
def lookup(locale, key, scope = [], options =
|
136
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
111
137
|
key = normalize_flat_keys(locale, key, scope, options[:separator])
|
112
138
|
value = @store["#{locale}.#{key}"]
|
113
139
|
value = JSON.decode(value) if value
|
114
140
|
|
115
141
|
if value.is_a?(Hash)
|
116
|
-
|
142
|
+
Utils.deep_symbolize_keys(value)
|
117
143
|
elsif !value.nil?
|
118
144
|
value
|
119
145
|
elsif !@subtrees
|
@@ -125,6 +151,7 @@ module I18n
|
|
125
151
|
if subtrees?
|
126
152
|
super
|
127
153
|
else
|
154
|
+
return entry unless entry.is_a?(Hash)
|
128
155
|
key = pluralization_key(entry, count)
|
129
156
|
entry[key]
|
130
157
|
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Backend
|
5
|
+
# Backend that lazy loads translations based on the current locale. This
|
6
|
+
# implementation avoids loading all translations up front. Instead, it only
|
7
|
+
# loads the translations that belong to the current locale. This offers a
|
8
|
+
# performance incentive in local development and test environments for
|
9
|
+
# applications with many translations for many different locales. It's
|
10
|
+
# particularly useful when the application only refers to a single locales'
|
11
|
+
# translations at a time (ex. A Rails workload). The implementation
|
12
|
+
# identifies which translation files from the load path belong to the
|
13
|
+
# current locale by pattern matching against their path name.
|
14
|
+
#
|
15
|
+
# Specifically, a translation file is considered to belong to a locale if:
|
16
|
+
# a) the filename is in the I18n load path
|
17
|
+
# b) the filename ends in a supported extension (ie. .yml, .json, .po, .rb)
|
18
|
+
# c) the filename starts with the locale identifier
|
19
|
+
# d) the locale identifier and optional proceeding text is separated by an underscore, ie. "_".
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
# Valid files that will be selected by this backend:
|
23
|
+
#
|
24
|
+
# "files/locales/en_translation.yml" (Selected for locale "en")
|
25
|
+
# "files/locales/fr.po" (Selected for locale "fr")
|
26
|
+
#
|
27
|
+
# Invalid files that won't be selected by this backend:
|
28
|
+
#
|
29
|
+
# "files/locales/translation-file"
|
30
|
+
# "files/locales/en-translation.unsupported"
|
31
|
+
# "files/locales/french/translation.yml"
|
32
|
+
# "files/locales/fr/translation.yml"
|
33
|
+
#
|
34
|
+
# The implementation uses this assumption to defer the loading of
|
35
|
+
# translation files until the current locale actually requires them.
|
36
|
+
#
|
37
|
+
# The backend has two working modes: lazy_load and eager_load.
|
38
|
+
#
|
39
|
+
# Note: This backend should only be enabled in test environments!
|
40
|
+
# When the mode is set to false, the backend behaves exactly like the
|
41
|
+
# Simple backend, with an additional check that the paths being loaded
|
42
|
+
# abide by the format. If paths can't be matched to the format, an error is raised.
|
43
|
+
#
|
44
|
+
# You can configure lazy loaded backends through the initializer or backends
|
45
|
+
# accessor:
|
46
|
+
#
|
47
|
+
# # In test environments
|
48
|
+
#
|
49
|
+
# I18n.backend = I18n::Backend::LazyLoadable.new(lazy_load: true)
|
50
|
+
#
|
51
|
+
# # In other environments, such as production and CI
|
52
|
+
#
|
53
|
+
# I18n.backend = I18n::Backend::LazyLoadable.new(lazy_load: false) # default
|
54
|
+
#
|
55
|
+
class LocaleExtractor
|
56
|
+
class << self
|
57
|
+
def locale_from_path(path)
|
58
|
+
name = File.basename(path, ".*")
|
59
|
+
locale = name.split("_").first
|
60
|
+
locale.to_sym unless locale.nil?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class LazyLoadable < Simple
|
66
|
+
def initialize(lazy_load: false)
|
67
|
+
@lazy_load = lazy_load
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns whether the current locale is initialized.
|
71
|
+
def initialized?
|
72
|
+
if lazy_load?
|
73
|
+
initialized_locales[I18n.locale]
|
74
|
+
else
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Clean up translations and uninitialize all locales.
|
80
|
+
def reload!
|
81
|
+
if lazy_load?
|
82
|
+
@initialized_locales = nil
|
83
|
+
@translations = nil
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Eager loading is not supported in the lazy context.
|
90
|
+
def eager_load!
|
91
|
+
if lazy_load?
|
92
|
+
raise UnsupportedMethod.new(__method__, self.class, "Cannot eager load translations because backend was configured with lazy_load: true.")
|
93
|
+
else
|
94
|
+
super
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Parse the load path and extract all locales.
|
99
|
+
def available_locales
|
100
|
+
if lazy_load?
|
101
|
+
I18n.load_path.map { |path| LocaleExtractor.locale_from_path(path) }
|
102
|
+
else
|
103
|
+
super
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
108
|
+
if lazy_load?
|
109
|
+
I18n.with_locale(locale) do
|
110
|
+
super
|
111
|
+
end
|
112
|
+
else
|
113
|
+
super
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
|
120
|
+
# Load translations from files that belong to the current locale.
|
121
|
+
def init_translations
|
122
|
+
file_errors = if lazy_load?
|
123
|
+
initialized_locales[I18n.locale] = true
|
124
|
+
load_translations_and_collect_file_errors(filenames_for_current_locale)
|
125
|
+
else
|
126
|
+
@initialized = true
|
127
|
+
load_translations_and_collect_file_errors(I18n.load_path)
|
128
|
+
end
|
129
|
+
|
130
|
+
raise InvalidFilenames.new(file_errors) unless file_errors.empty?
|
131
|
+
end
|
132
|
+
|
133
|
+
def initialized_locales
|
134
|
+
@initialized_locales ||= Hash.new(false)
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def lazy_load?
|
140
|
+
@lazy_load
|
141
|
+
end
|
142
|
+
|
143
|
+
class FilenameIncorrect < StandardError
|
144
|
+
def initialize(file, expected_locale, unexpected_locales)
|
145
|
+
super "#{file} can only load translations for \"#{expected_locale}\". Found translations for: #{unexpected_locales}."
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Loads each file supplied and asserts that the file only loads
|
150
|
+
# translations as expected by the name. The method returns a list of
|
151
|
+
# errors corresponding to offending files.
|
152
|
+
def load_translations_and_collect_file_errors(files)
|
153
|
+
errors = []
|
154
|
+
|
155
|
+
load_translations(files) do |file, loaded_translations|
|
156
|
+
assert_file_named_correctly!(file, loaded_translations)
|
157
|
+
rescue FilenameIncorrect => e
|
158
|
+
errors << e
|
159
|
+
end
|
160
|
+
|
161
|
+
errors
|
162
|
+
end
|
163
|
+
|
164
|
+
# Select all files from I18n load path that belong to current locale.
|
165
|
+
# These files must start with the locale identifier (ie. "en", "pt-BR"),
|
166
|
+
# followed by an "_" demarcation to separate proceeding text.
|
167
|
+
def filenames_for_current_locale
|
168
|
+
I18n.load_path.flatten.select do |path|
|
169
|
+
LocaleExtractor.locale_from_path(path) == I18n.locale
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Checks if a filename is named in correspondence to the translations it loaded.
|
174
|
+
# The locale extracted from the path must be the single locale loaded in the translations.
|
175
|
+
def assert_file_named_correctly!(file, translations)
|
176
|
+
loaded_locales = translations.keys.map(&:to_sym)
|
177
|
+
expected_locale = LocaleExtractor.locale_from_path(file)
|
178
|
+
unexpected_locales = loaded_locales.reject { |locale| locale == expected_locale }
|
179
|
+
|
180
|
+
raise FilenameIncorrect.new(file, expected_locale, unexpected_locales) unless unexpected_locales.empty?
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/lib/i18n/backend/memoize.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Memoize module simply memoizes the values returned by lookup using
|
2
4
|
# a flat hash and can tremendously speed up the lookup process in a backend.
|
3
5
|
#
|
@@ -14,7 +16,7 @@ module I18n
|
|
14
16
|
@memoized_locales ||= super
|
15
17
|
end
|
16
18
|
|
17
|
-
def store_translations(locale, data, options =
|
19
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
18
20
|
reset_memoizations!(locale)
|
19
21
|
super
|
20
22
|
end
|
@@ -24,9 +26,15 @@ module I18n
|
|
24
26
|
super
|
25
27
|
end
|
26
28
|
|
29
|
+
def eager_load!
|
30
|
+
memoized_lookup
|
31
|
+
available_locales
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
27
35
|
protected
|
28
36
|
|
29
|
-
def lookup(locale, key, scope = nil, options =
|
37
|
+
def lookup(locale, key, scope = nil, options = EMPTY_HASH)
|
30
38
|
flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale,
|
31
39
|
key, scope, options[:separator]).to_sym
|
32
40
|
flat_hash = memoized_lookup[locale.to_sym]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# I18n translation metadata is useful when you want to access information
|
2
4
|
# about how a translation was looked up, pluralized or interpolated in
|
3
5
|
# your application.
|
@@ -35,19 +37,19 @@ module I18n
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
def translate(locale, key, options =
|
40
|
+
def translate(locale, key, options = EMPTY_HASH)
|
39
41
|
metadata = {
|
40
42
|
:locale => locale,
|
41
43
|
:key => key,
|
42
44
|
:scope => options[:scope],
|
43
45
|
:default => options[:default],
|
44
46
|
:separator => options[:separator],
|
45
|
-
:values => options.reject { |name,
|
47
|
+
:values => options.reject { |name, _value| RESERVED_KEYS.include?(name) }
|
46
48
|
}
|
47
49
|
with_metadata(metadata) { super }
|
48
50
|
end
|
49
51
|
|
50
|
-
def interpolate(locale, entry, values =
|
52
|
+
def interpolate(locale, entry, values = EMPTY_HASH)
|
51
53
|
metadata = entry.translation_metadata.merge(:original => entry)
|
52
54
|
with_metadata(metadata) { super }
|
53
55
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# I18n Pluralization are useful when you want your application to
|
2
4
|
# customize pluralization rules.
|
3
5
|
#
|
@@ -27,7 +29,7 @@ module I18n
|
|
27
29
|
# either pick a special :zero translation even for languages where the
|
28
30
|
# pluralizer does not return a :zero key.
|
29
31
|
def pluralize(locale, entry, count)
|
30
|
-
return entry unless entry.is_a?(Hash)
|
32
|
+
return entry unless entry.is_a?(Hash) && count
|
31
33
|
|
32
34
|
pluralizer = pluralizer(locale)
|
33
35
|
if pluralizer.respond_to?(:call)
|
data/lib/i18n/backend/simple.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'i18n/backend/base'
|
4
|
+
|
1
5
|
module I18n
|
2
6
|
module Backend
|
3
7
|
# A simple backend that reads translations from YAML files and stores them in
|
@@ -15,8 +19,6 @@ module I18n
|
|
15
19
|
#
|
16
20
|
# I18n::Backend::Simple.include(I18n::Backend::Pluralization)
|
17
21
|
class Simple
|
18
|
-
(class << self; self; end).class_eval { public :include }
|
19
|
-
|
20
22
|
module Implementation
|
21
23
|
include Base
|
22
24
|
|
@@ -28,17 +30,16 @@ module I18n
|
|
28
30
|
# This uses a deep merge for the translations hash, so existing
|
29
31
|
# translations will be overwritten by new ones only at the deepest
|
30
32
|
# level of the hash.
|
31
|
-
def store_translations(locale, data, options =
|
33
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
32
34
|
if I18n.enforce_available_locales &&
|
33
35
|
I18n.available_locales_initialized? &&
|
34
|
-
!I18n.
|
35
|
-
!I18n.available_locales.include?(locale.to_s)
|
36
|
+
!I18n.locale_available?(locale)
|
36
37
|
return data
|
37
38
|
end
|
38
39
|
locale = locale.to_sym
|
39
|
-
translations[locale] ||=
|
40
|
-
data = data.
|
41
|
-
translations[locale]
|
40
|
+
translations[locale] ||= Concurrent::Hash.new
|
41
|
+
data = Utils.deep_symbolize_keys(data) unless options.fetch(:skip_symbolize_keys, false)
|
42
|
+
Utils.deep_merge!(translations[locale], data)
|
42
43
|
end
|
43
44
|
|
44
45
|
# Get available locales from the translations hash
|
@@ -57,6 +58,19 @@ module I18n
|
|
57
58
|
super
|
58
59
|
end
|
59
60
|
|
61
|
+
def eager_load!
|
62
|
+
init_translations unless initialized?
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
def translations(do_init: false)
|
67
|
+
# To avoid returning empty translations,
|
68
|
+
# call `init_translations`
|
69
|
+
init_translations if do_init && !initialized?
|
70
|
+
|
71
|
+
@translations ||= Concurrent::Hash.new { |h, k| h[k] = Concurrent::Hash.new }
|
72
|
+
end
|
73
|
+
|
60
74
|
protected
|
61
75
|
|
62
76
|
def init_translations
|
@@ -64,24 +78,23 @@ module I18n
|
|
64
78
|
@initialized = true
|
65
79
|
end
|
66
80
|
|
67
|
-
def translations
|
68
|
-
@translations ||= {}
|
69
|
-
end
|
70
|
-
|
71
81
|
# Looks up a translation from the translations hash. Returns nil if
|
72
82
|
# either key is nil, or locale, scope or key do not exist as a key in the
|
73
83
|
# nested translations hash. Splits keys or scopes containing dots
|
74
84
|
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
|
75
85
|
# <tt>%w(currency format)</tt>.
|
76
|
-
def lookup(locale, key, scope = [], options =
|
86
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
77
87
|
init_translations unless initialized?
|
78
88
|
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
|
79
89
|
|
80
90
|
keys.inject(translations) do |result, _key|
|
81
|
-
|
82
|
-
|
91
|
+
return nil unless result.is_a?(Hash)
|
92
|
+
unless result.has_key?(_key)
|
93
|
+
_key = _key.to_s.to_sym
|
94
|
+
return nil unless result.has_key?(_key)
|
95
|
+
end
|
83
96
|
result = result[_key]
|
84
|
-
result =
|
97
|
+
result = resolve_entry(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
|
85
98
|
result
|
86
99
|
end
|
87
100
|
end
|
data/lib/i18n/backend.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module I18n
|
2
4
|
module Backend
|
3
5
|
autoload :Base, 'i18n/backend/base'
|
4
|
-
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
5
6
|
autoload :Cache, 'i18n/backend/cache'
|
7
|
+
autoload :CacheFile, 'i18n/backend/cache_file'
|
6
8
|
autoload :Cascade, 'i18n/backend/cascade'
|
7
9
|
autoload :Chain, 'i18n/backend/chain'
|
8
10
|
autoload :Fallbacks, 'i18n/backend/fallbacks'
|
9
11
|
autoload :Flatten, 'i18n/backend/flatten'
|
10
12
|
autoload :Gettext, 'i18n/backend/gettext'
|
13
|
+
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
11
14
|
autoload :KeyValue, 'i18n/backend/key_value'
|
15
|
+
autoload :LazyLoadable, 'i18n/backend/lazy_loadable'
|
12
16
|
autoload :Memoize, 'i18n/backend/memoize'
|
13
17
|
autoload :Metadata, 'i18n/backend/metadata'
|
14
18
|
autoload :Pluralization, 'i18n/backend/pluralization'
|
data/lib/i18n/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module I18n
|
@@ -5,7 +7,7 @@ module I18n
|
|
5
7
|
# The only configuration value that is not global and scoped to thread is :locale.
|
6
8
|
# It defaults to the default_locale.
|
7
9
|
def locale
|
8
|
-
defined?(@locale) && @locale ? @locale : default_locale
|
10
|
+
defined?(@locale) && @locale != nil ? @locale : default_locale
|
9
11
|
end
|
10
12
|
|
11
13
|
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
|
@@ -57,7 +59,7 @@ module I18n
|
|
57
59
|
@@available_locales = nil if @@available_locales.empty?
|
58
60
|
@@available_locales_set = nil
|
59
61
|
end
|
60
|
-
|
62
|
+
|
61
63
|
# Returns true if the available_locales have been initialized
|
62
64
|
def available_locales_initialized?
|
63
65
|
( !!defined?(@@available_locales) && !!@@available_locales )
|
@@ -143,5 +145,21 @@ module I18n
|
|
143
145
|
def enforce_available_locales=(enforce_available_locales)
|
144
146
|
@@enforce_available_locales = enforce_available_locales
|
145
147
|
end
|
148
|
+
|
149
|
+
# Returns the current interpolation patterns. Defaults to
|
150
|
+
# I18n::DEFAULT_INTERPOLATION_PATTERNS.
|
151
|
+
def interpolation_patterns
|
152
|
+
@@interpolation_patterns ||= I18n::DEFAULT_INTERPOLATION_PATTERNS.dup
|
153
|
+
end
|
154
|
+
|
155
|
+
# Sets the current interpolation patterns. Used to set a interpolation
|
156
|
+
# patterns.
|
157
|
+
#
|
158
|
+
# E.g. using {{}} as a placeholder like "{{hello}}, world!":
|
159
|
+
#
|
160
|
+
# I18n.config.interpolation_patterns << /\{\{(\w+)\}\}/
|
161
|
+
def interpolation_patterns=(interpolation_patterns)
|
162
|
+
@@interpolation_patterns = interpolation_patterns
|
163
|
+
end
|
146
164
|
end
|
147
165
|
end
|