i18n 1.9.1 → 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 +1 -0
- data/lib/i18n/backend/base.rb +8 -2
- data/lib/i18n/backend/fallbacks.rb +12 -5
- data/lib/i18n/backend/lazy_loadable.rb +184 -0
- data/lib/i18n/backend/simple.rb +1 -1
- data/lib/i18n/backend.rb +2 -1
- data/lib/i18n/exceptions.rb +34 -0
- data/lib/i18n/tests/basics.rb +3 -5
- data/lib/i18n/version.rb +1 -1
- data/lib/i18n.rb +5 -5
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcce939890b82f2f78ef93aa98b2be5995f172f6c37b95c53800c155f2036eec
|
4
|
+
data.tar.gz: ae4d72446e698fc7c0666f0e9dad6f11fa669d768fe9272d0c89e14aba8d2274
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf36e9389526d5a48bbfc4043bc2c4587ddbfbfab41a2ccaafa801570c3a838530a001c6c3df1669d5e0beb14b63b394253cef6d6fdd12aa936ae8c07ecdbf72
|
7
|
+
data.tar.gz: 82b972e13f2342ca864e4ead724008aa75ad24bbf6d4f14603330e2999501cfb5c05ee6a9e5cd2cffbf48f07f6da868fccc33b4771f7b4b085989c4def36a7b9
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Ruby I18n
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/i18n.svg)](https://badge.fury.io/rb/i18n)
|
3
4
|
[![Build Status](https://github.com/ruby-i18n/i18n/workflows/Ruby/badge.svg)](https://github.com/ruby-i18n/i18n/actions?query=workflow%3ARuby)
|
4
5
|
|
5
6
|
Ruby internationalization and localization (i18n) solution.
|
data/lib/i18n/backend/base.rb
CHANGED
@@ -13,7 +13,10 @@ module I18n
|
|
13
13
|
# for details.
|
14
14
|
def load_translations(*filenames)
|
15
15
|
filenames = I18n.load_path if filenames.empty?
|
16
|
-
filenames.flatten.each
|
16
|
+
filenames.flatten.each do |filename|
|
17
|
+
loaded_translations = load_file(filename)
|
18
|
+
yield filename, loaded_translations if block_given?
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
# This method receives a locale, a data hash and options for storing translations.
|
@@ -32,7 +35,7 @@ module I18n
|
|
32
35
|
if entry.nil? && options.key?(:default)
|
33
36
|
entry = default(locale, key, options[:default], options)
|
34
37
|
else
|
35
|
-
entry =
|
38
|
+
entry = resolve_entry(locale, key, entry, options)
|
36
39
|
end
|
37
40
|
|
38
41
|
count = options[:count]
|
@@ -151,6 +154,7 @@ module I18n
|
|
151
154
|
end
|
152
155
|
result unless result.is_a?(MissingTranslation)
|
153
156
|
end
|
157
|
+
alias_method :resolve_entry, :resolve
|
154
158
|
|
155
159
|
# Picks a translation from a pluralized mnemonic subkey according to English
|
156
160
|
# pluralization rules :
|
@@ -226,6 +230,8 @@ module I18n
|
|
226
230
|
raise InvalidLocaleData.new(filename, 'expects it to return a hash, but does not')
|
227
231
|
end
|
228
232
|
data.each { |locale, d| store_translations(locale, d || {}, skip_symbolize_keys: keys_symbolized) }
|
233
|
+
|
234
|
+
data
|
229
235
|
end
|
230
236
|
|
231
237
|
# Loads a plain Ruby translations file. eval'ing the file must yield
|
@@ -64,13 +64,20 @@ module I18n
|
|
64
64
|
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
67
|
+
def resolve_entry(locale, object, subject, options = EMPTY_HASH)
|
68
68
|
return subject if options[:resolve] == false
|
69
|
-
return super unless subject.is_a?(Symbol)
|
70
|
-
|
71
69
|
result = catch(:exception) do
|
72
|
-
options.delete(:fallback_in_progress)
|
73
|
-
|
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
|
74
81
|
end
|
75
82
|
result unless result.is_a?(MissingTranslation)
|
76
83
|
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/simple.rb
CHANGED
@@ -94,7 +94,7 @@ module I18n
|
|
94
94
|
return nil unless result.has_key?(_key)
|
95
95
|
end
|
96
96
|
result = result[_key]
|
97
|
-
result =
|
97
|
+
result = resolve_entry(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
|
98
98
|
result
|
99
99
|
end
|
100
100
|
end
|
data/lib/i18n/backend.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
module I18n
|
4
4
|
module Backend
|
5
5
|
autoload :Base, 'i18n/backend/base'
|
6
|
-
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
7
6
|
autoload :Cache, 'i18n/backend/cache'
|
8
7
|
autoload :CacheFile, 'i18n/backend/cache_file'
|
9
8
|
autoload :Cascade, 'i18n/backend/cascade'
|
@@ -11,7 +10,9 @@ module I18n
|
|
11
10
|
autoload :Fallbacks, 'i18n/backend/fallbacks'
|
12
11
|
autoload :Flatten, 'i18n/backend/flatten'
|
13
12
|
autoload :Gettext, 'i18n/backend/gettext'
|
13
|
+
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
14
14
|
autoload :KeyValue, 'i18n/backend/key_value'
|
15
|
+
autoload :LazyLoadable, 'i18n/backend/lazy_loadable'
|
15
16
|
autoload :Memoize, 'i18n/backend/memoize'
|
16
17
|
autoload :Metadata, 'i18n/backend/metadata'
|
17
18
|
autoload :Pluralization, 'i18n/backend/pluralization'
|
data/lib/i18n/exceptions.rb
CHANGED
@@ -110,4 +110,38 @@ module I18n
|
|
110
110
|
super "can not load translations from #{filename}, the file type #{type} is not known"
|
111
111
|
end
|
112
112
|
end
|
113
|
+
|
114
|
+
class UnsupportedMethod < ArgumentError
|
115
|
+
attr_reader :method, :backend_klass, :msg
|
116
|
+
def initialize(method, backend_klass, msg)
|
117
|
+
@method = method
|
118
|
+
@backend_klass = backend_klass
|
119
|
+
@msg = msg
|
120
|
+
super "#{backend_klass} does not support the ##{method} method. #{msg}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class InvalidFilenames < ArgumentError
|
125
|
+
NUMBER_OF_ERRORS_SHOWN = 20
|
126
|
+
def initialize(file_errors)
|
127
|
+
super <<~MSG
|
128
|
+
Found #{file_errors.count} error(s).
|
129
|
+
The first #{[file_errors.count, NUMBER_OF_ERRORS_SHOWN].min} error(s):
|
130
|
+
#{file_errors.map(&:message).first(NUMBER_OF_ERRORS_SHOWN).join("\n")}
|
131
|
+
|
132
|
+
To use the LazyLoadable backend:
|
133
|
+
1. Filenames must start with the locale.
|
134
|
+
2. An underscore must separate the locale with any optional text that follows.
|
135
|
+
3. The file must only contain translation data for the single locale.
|
136
|
+
|
137
|
+
Example:
|
138
|
+
"/config/locales/fr.yml" which contains:
|
139
|
+
```yml
|
140
|
+
fr:
|
141
|
+
dog:
|
142
|
+
chien
|
143
|
+
```
|
144
|
+
MSG
|
145
|
+
end
|
146
|
+
end
|
113
147
|
end
|
data/lib/i18n/tests/basics.rb
CHANGED
@@ -5,12 +5,11 @@ module I18n
|
|
5
5
|
I18n.available_locales = nil
|
6
6
|
end
|
7
7
|
|
8
|
-
test "available_locales returns the
|
8
|
+
test "available_locales returns the available_locales produced by the backend, by default" do
|
9
9
|
I18n.backend.store_translations('de', :foo => 'bar')
|
10
10
|
I18n.backend.store_translations('en', :foo => 'foo')
|
11
11
|
|
12
|
-
|
13
|
-
assert I18n.available_locales.include?(:en)
|
12
|
+
assert_equal I18n.available_locales, I18n.backend.available_locales
|
14
13
|
end
|
15
14
|
|
16
15
|
test "available_locales can be set to something else independently from the actual locale data" do
|
@@ -24,8 +23,7 @@ module I18n
|
|
24
23
|
assert_equal [:foo, :bar], I18n.available_locales
|
25
24
|
|
26
25
|
I18n.available_locales = nil
|
27
|
-
|
28
|
-
assert I18n.available_locales.include?(:en)
|
26
|
+
assert_equal I18n.available_locales, I18n.backend.available_locales
|
29
27
|
end
|
30
28
|
|
31
29
|
test "available_locales memoizes when set explicitely" do
|
data/lib/i18n/version.rb
CHANGED
data/lib/i18n.rb
CHANGED
@@ -338,11 +338,11 @@ module I18n
|
|
338
338
|
def normalize_keys(locale, key, scope, separator = nil)
|
339
339
|
separator ||= I18n.default_separator
|
340
340
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
341
|
+
[
|
342
|
+
*normalize_key(locale, separator),
|
343
|
+
*normalize_key(scope, separator),
|
344
|
+
*normalize_key(key, separator)
|
345
|
+
]
|
346
346
|
end
|
347
347
|
|
348
348
|
# Returns true when the passed locale, which can be either a String or a
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Fuchs
|
@@ -10,10 +10,10 @@ authors:
|
|
10
10
|
- Stephan Soller
|
11
11
|
- Saimon Moore
|
12
12
|
- Ryan Bigg
|
13
|
-
autorequire:
|
13
|
+
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2022-
|
16
|
+
date: 2022-02-14 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: concurrent-ruby
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- lib/i18n/backend/gettext.rb
|
50
50
|
- lib/i18n/backend/interpolation_compiler.rb
|
51
51
|
- lib/i18n/backend/key_value.rb
|
52
|
+
- lib/i18n/backend/lazy_loadable.rb
|
52
53
|
- lib/i18n/backend/memoize.rb
|
53
54
|
- lib/i18n/backend/metadata.rb
|
54
55
|
- lib/i18n/backend/pluralization.rb
|
@@ -90,7 +91,7 @@ metadata:
|
|
90
91
|
changelog_uri: https://github.com/ruby-i18n/i18n/releases
|
91
92
|
documentation_uri: https://guides.rubyonrails.org/i18n.html
|
92
93
|
source_code_uri: https://github.com/ruby-i18n/i18n
|
93
|
-
post_install_message:
|
94
|
+
post_install_message:
|
94
95
|
rdoc_options: []
|
95
96
|
require_paths:
|
96
97
|
- lib
|
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
107
|
version: 1.3.5
|
107
108
|
requirements: []
|
108
109
|
rubygems_version: 3.1.6
|
109
|
-
signing_key:
|
110
|
+
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: New wave Internationalization support for Ruby
|
112
113
|
test_files: []
|