i18n 0.4.0 → 1.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +0 -0
- data/README.md +127 -0
- data/lib/i18n/backend/base.rb +189 -111
- data/lib/i18n/backend/cache.rb +58 -22
- data/lib/i18n/backend/cache_file.rb +36 -0
- data/lib/i18n/backend/cascade.rb +9 -10
- data/lib/i18n/backend/chain.rb +95 -42
- data/lib/i18n/backend/fallbacks.rb +68 -22
- data/lib/i18n/backend/flatten.rb +13 -8
- data/lib/i18n/backend/gettext.rb +33 -25
- data/lib/i18n/backend/interpolation_compiler.rb +12 -14
- data/lib/i18n/backend/key_value.rb +112 -10
- data/lib/i18n/backend/lazy_loadable.rb +184 -0
- data/lib/i18n/backend/memoize.rb +13 -7
- data/lib/i18n/backend/metadata.rb +12 -6
- data/lib/i18n/backend/pluralization.rb +64 -25
- data/lib/i18n/backend/simple.rb +44 -18
- data/lib/i18n/backend/transliterator.rb +43 -33
- data/lib/i18n/backend.rb +5 -3
- data/lib/i18n/config.rb +91 -10
- data/lib/i18n/exceptions.rb +118 -22
- data/lib/i18n/gettext/helpers.rb +14 -4
- data/lib/i18n/gettext/po_parser.rb +7 -7
- data/lib/i18n/gettext.rb +6 -5
- data/lib/i18n/interpolate/ruby.rb +53 -0
- data/lib/i18n/locale/fallbacks.rb +33 -26
- data/lib/i18n/locale/tag/parents.rb +8 -8
- data/lib/i18n/locale/tag/rfc4646.rb +0 -2
- data/lib/i18n/locale/tag/simple.rb +2 -4
- data/lib/i18n/locale.rb +2 -0
- data/lib/i18n/middleware.rb +17 -0
- data/lib/i18n/tests/basics.rb +58 -0
- data/lib/i18n/tests/defaults.rb +52 -0
- data/lib/i18n/tests/interpolation.rb +167 -0
- data/lib/i18n/tests/link.rb +66 -0
- data/lib/i18n/tests/localization/date.rb +122 -0
- data/lib/i18n/tests/localization/date_time.rb +103 -0
- data/lib/i18n/tests/localization/procs.rb +118 -0
- data/lib/i18n/tests/localization/time.rb +103 -0
- data/lib/i18n/tests/localization.rb +19 -0
- data/lib/i18n/tests/lookup.rb +81 -0
- data/lib/i18n/tests/pluralization.rb +35 -0
- data/lib/i18n/tests/procs.rb +66 -0
- data/lib/i18n/tests.rb +14 -0
- data/lib/i18n/utils.rb +55 -0
- data/lib/i18n/version.rb +3 -1
- data/lib/i18n.rb +200 -87
- metadata +64 -56
- data/CHANGELOG.textile +0 -135
- data/README.textile +0 -93
- data/lib/i18n/backend/active_record/missing.rb +0 -65
- data/lib/i18n/backend/active_record/store_procs.rb +0 -38
- data/lib/i18n/backend/active_record/translation.rb +0 -93
- data/lib/i18n/backend/active_record.rb +0 -61
- data/lib/i18n/backend/cldr.rb +0 -100
- data/lib/i18n/core_ext/hash.rb +0 -29
- data/lib/i18n/core_ext/string/interpolate.rb +0 -98
@@ -1,4 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
module I18n
|
3
5
|
module Backend
|
4
6
|
module Transliterator
|
@@ -43,55 +45,63 @@ module I18n
|
|
43
45
|
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
|
44
46
|
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
|
45
47
|
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
|
46
|
-
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"
|
51
|
-
"
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG",
|
62
|
-
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o",
|
63
|
-
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R",
|
64
|
-
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s",
|
65
|
-
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T",
|
66
|
-
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u",
|
67
|
-
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W",
|
68
|
-
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z",
|
69
|
-
"Ž"=>"Z", "ž"=>"z"
|
70
|
-
}
|
48
|
+
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "ẞ"=>"SS", "à"=>"a",
|
49
|
+
"á"=>"a", "â"=>"a", "ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c",
|
50
|
+
"è"=>"e", "é"=>"e", "ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i",
|
51
|
+
"ï"=>"i", "ð"=>"d", "ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o",
|
52
|
+
"ö"=>"o", "ø"=>"o", "ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y",
|
53
|
+
"þ"=>"th", "ÿ"=>"y", "Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A",
|
54
|
+
"ą"=>"a", "Ć"=>"C", "ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c",
|
55
|
+
"Č"=>"C", "č"=>"c", "Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E",
|
56
|
+
"ē"=>"e", "Ĕ"=>"E", "ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e",
|
57
|
+
"Ě"=>"E", "ě"=>"e", "Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G",
|
58
|
+
"ġ"=>"g", "Ģ"=>"G", "ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h",
|
59
|
+
"Ĩ"=>"I", "ĩ"=>"i", "Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I",
|
60
|
+
"į"=>"i", "İ"=>"I", "ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j",
|
61
|
+
"Ķ"=>"K", "ķ"=>"k", "ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l",
|
62
|
+
"Ľ"=>"L", "ľ"=>"l", "Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N",
|
63
|
+
"ń"=>"n", "Ņ"=>"N", "ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG",
|
64
|
+
"ŋ"=>"ng", "Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o",
|
65
|
+
"Œ"=>"OE", "œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R",
|
66
|
+
"ř"=>"r", "Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s",
|
67
|
+
"Š"=>"S", "š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T",
|
68
|
+
"ŧ"=>"t", "Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u",
|
69
|
+
"Ů"=>"U", "ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W",
|
70
|
+
"ŵ"=>"w", "Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z",
|
71
|
+
"ż"=>"z", "Ž"=>"Z", "ž"=>"z"
|
72
|
+
}.freeze
|
71
73
|
|
72
74
|
def initialize(rule = nil)
|
73
75
|
@rule = rule
|
74
|
-
|
76
|
+
add_default_approximations
|
75
77
|
add rule if rule
|
76
78
|
end
|
77
79
|
|
78
80
|
def transliterate(string, replacement = nil)
|
81
|
+
replacement ||= DEFAULT_REPLACEMENT_CHAR
|
79
82
|
string.gsub(/[^\x00-\x7f]/u) do |char|
|
80
|
-
approximations[char] || replacement
|
83
|
+
approximations[char] || replacement
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
84
87
|
private
|
85
88
|
|
86
|
-
|
87
|
-
|
89
|
+
def approximations
|
90
|
+
@approximations ||= {}
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_default_approximations
|
94
|
+
DEFAULT_APPROXIMATIONS.each do |key, value|
|
95
|
+
approximations[key] = value
|
88
96
|
end
|
97
|
+
end
|
89
98
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
approximations.
|
99
|
+
# Add transliteration rules to the approximations hash.
|
100
|
+
def add(hash)
|
101
|
+
hash.each do |key, value|
|
102
|
+
approximations[key.to_s] = value.to_s
|
94
103
|
end
|
104
|
+
end
|
95
105
|
end
|
96
106
|
end
|
97
107
|
end
|
data/lib/i18n/backend.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module I18n
|
2
4
|
module Backend
|
3
|
-
autoload :ActiveRecord, 'i18n/backend/active_record'
|
4
5
|
autoload :Base, 'i18n/backend/base'
|
5
|
-
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
6
6
|
autoload :Cache, 'i18n/backend/cache'
|
7
|
+
autoload :CacheFile, 'i18n/backend/cache_file'
|
7
8
|
autoload :Cascade, 'i18n/backend/cascade'
|
8
9
|
autoload :Chain, 'i18n/backend/chain'
|
9
|
-
autoload :Cldr, 'i18n/backend/cldr'
|
10
10
|
autoload :Fallbacks, 'i18n/backend/fallbacks'
|
11
11
|
autoload :Flatten, 'i18n/backend/flatten'
|
12
12
|
autoload :Gettext, 'i18n/backend/gettext'
|
13
|
+
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
13
14
|
autoload :KeyValue, 'i18n/backend/key_value'
|
15
|
+
autoload :LazyLoadable, 'i18n/backend/lazy_loadable'
|
14
16
|
autoload :Memoize, 'i18n/backend/memoize'
|
15
17
|
autoload :Metadata, 'i18n/backend/metadata'
|
16
18
|
autoload :Pluralization, 'i18n/backend/pluralization'
|
data/lib/i18n/config.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
1
5
|
module I18n
|
2
6
|
class Config
|
3
7
|
# The only configuration value that is not global and scoped to thread is :locale.
|
4
8
|
# It defaults to the default_locale.
|
5
9
|
def locale
|
6
|
-
@locale
|
10
|
+
defined?(@locale) && @locale != nil ? @locale : default_locale
|
7
11
|
end
|
8
12
|
|
9
13
|
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
|
10
14
|
def locale=(locale)
|
11
|
-
|
15
|
+
I18n.enforce_available_locales!(locale)
|
16
|
+
@locale = locale && locale.to_sym
|
12
17
|
end
|
13
18
|
|
14
19
|
# Returns the current backend. Defaults to +Backend::Simple+.
|
@@ -28,19 +33,42 @@ module I18n
|
|
28
33
|
|
29
34
|
# Sets the current default locale. Used to set a custom default locale.
|
30
35
|
def default_locale=(locale)
|
31
|
-
|
36
|
+
I18n.enforce_available_locales!(locale)
|
37
|
+
@@default_locale = locale && locale.to_sym
|
32
38
|
end
|
33
39
|
|
34
40
|
# Returns an array of locales for which translations are available.
|
35
|
-
# Unless you
|
36
|
-
# the call will be delegated to the backend
|
41
|
+
# Unless you explicitly set these through I18n.available_locales=
|
42
|
+
# the call will be delegated to the backend.
|
37
43
|
def available_locales
|
38
|
-
@@available_locales ||=
|
44
|
+
@@available_locales ||= nil
|
45
|
+
@@available_locales || backend.available_locales
|
46
|
+
end
|
47
|
+
|
48
|
+
# Caches the available locales list as both strings and symbols in a Set, so
|
49
|
+
# that we can have faster lookups to do the available locales enforce check.
|
50
|
+
def available_locales_set #:nodoc:
|
51
|
+
@@available_locales_set ||= available_locales.inject(Set.new) do |set, locale|
|
52
|
+
set << locale.to_s << locale.to_sym
|
53
|
+
end
|
39
54
|
end
|
40
55
|
|
41
56
|
# Sets the available locales.
|
42
57
|
def available_locales=(locales)
|
43
|
-
@@available_locales = locales
|
58
|
+
@@available_locales = Array(locales).map { |locale| locale.to_sym }
|
59
|
+
@@available_locales = nil if @@available_locales.empty?
|
60
|
+
@@available_locales_set = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns true if the available_locales have been initialized
|
64
|
+
def available_locales_initialized?
|
65
|
+
( !!defined?(@@available_locales) && !!@@available_locales )
|
66
|
+
end
|
67
|
+
|
68
|
+
# Clears the available locales set so it can be recomputed again after I18n
|
69
|
+
# gets reloaded.
|
70
|
+
def clear_available_locales_set #:nodoc:
|
71
|
+
@@available_locales_set = nil
|
44
72
|
end
|
45
73
|
|
46
74
|
# Returns the current default scope separator. Defaults to '.'
|
@@ -53,9 +81,10 @@ module I18n
|
|
53
81
|
@@default_separator = separator
|
54
82
|
end
|
55
83
|
|
56
|
-
#
|
84
|
+
# Returns the current exception handler. Defaults to an instance of
|
85
|
+
# I18n::ExceptionHandler.
|
57
86
|
def exception_handler
|
58
|
-
@@exception_handler ||=
|
87
|
+
@@exception_handler ||= ExceptionHandler.new
|
59
88
|
end
|
60
89
|
|
61
90
|
# Sets the exception handler.
|
@@ -63,6 +92,29 @@ module I18n
|
|
63
92
|
@@exception_handler = exception_handler
|
64
93
|
end
|
65
94
|
|
95
|
+
# Returns the current handler for situations when interpolation argument
|
96
|
+
# is missing. MissingInterpolationArgument will be raised by default.
|
97
|
+
def missing_interpolation_argument_handler
|
98
|
+
@@missing_interpolation_argument_handler ||= lambda do |missing_key, provided_hash, string|
|
99
|
+
raise MissingInterpolationArgument.new(missing_key, provided_hash, string)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets the missing interpolation argument handler. It can be any
|
104
|
+
# object that responds to #call. The arguments that will be passed to #call
|
105
|
+
# are the same as for MissingInterpolationArgument initializer. Use +Proc.new+
|
106
|
+
# if you don't care about arity.
|
107
|
+
#
|
108
|
+
# == Example:
|
109
|
+
# You can suppress raising an exception and return string instead:
|
110
|
+
#
|
111
|
+
# I18n.config.missing_interpolation_argument_handler = Proc.new do |key|
|
112
|
+
# "#{key} is missing"
|
113
|
+
# end
|
114
|
+
def missing_interpolation_argument_handler=(exception_handler)
|
115
|
+
@@missing_interpolation_argument_handler = exception_handler
|
116
|
+
end
|
117
|
+
|
66
118
|
# Allow clients to register paths providing translation data sources. The
|
67
119
|
# backend defines acceptable sources.
|
68
120
|
#
|
@@ -79,6 +131,35 @@ module I18n
|
|
79
131
|
# behave like a Ruby Array.
|
80
132
|
def load_path=(load_path)
|
81
133
|
@@load_path = load_path
|
134
|
+
@@available_locales_set = nil
|
135
|
+
backend.reload!
|
136
|
+
end
|
137
|
+
|
138
|
+
# Whether or not to verify if locales are in the list of available locales.
|
139
|
+
# Defaults to true.
|
140
|
+
@@enforce_available_locales = true
|
141
|
+
def enforce_available_locales
|
142
|
+
@@enforce_available_locales
|
143
|
+
end
|
144
|
+
|
145
|
+
def enforce_available_locales=(enforce_available_locales)
|
146
|
+
@@enforce_available_locales = enforce_available_locales
|
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
|
82
163
|
end
|
83
164
|
end
|
84
|
-
end
|
165
|
+
end
|
data/lib/i18n/exceptions.rb
CHANGED
@@ -1,14 +1,34 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
def initialize(message = nil)
|
5
|
-
super(message || "key not found")
|
6
|
-
end
|
7
|
-
end unless defined?(KeyError)
|
3
|
+
require 'cgi'
|
8
4
|
|
9
5
|
module I18n
|
6
|
+
class ExceptionHandler
|
7
|
+
def call(exception, _locale, _key, _options)
|
8
|
+
if exception.is_a?(MissingTranslation)
|
9
|
+
exception.message
|
10
|
+
else
|
11
|
+
raise exception
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
class ArgumentError < ::ArgumentError; end
|
11
17
|
|
18
|
+
class Disabled < ArgumentError
|
19
|
+
def initialize(method)
|
20
|
+
super(<<~MESSAGE)
|
21
|
+
I18n.#{method} is currently disabled, likely because your application is still in its loading phase.
|
22
|
+
|
23
|
+
This method is meant to display text in the user locale, so calling it before the user locale has
|
24
|
+
been set is likely to display text from the wrong locale to some users.
|
25
|
+
|
26
|
+
If you have a legitimate reason to access i18n data outside of the user flow, you can do so by passing
|
27
|
+
the desired locale explicitly with the `locale` argument, e.g. `I18n.#{method}(..., locale: :en)`
|
28
|
+
MESSAGE
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
12
32
|
class InvalidLocale < ArgumentError
|
13
33
|
attr_reader :locale
|
14
34
|
def initialize(locale)
|
@@ -17,29 +37,71 @@ module I18n
|
|
17
37
|
end
|
18
38
|
end
|
19
39
|
|
20
|
-
class
|
21
|
-
attr_reader :
|
22
|
-
def initialize(
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
|
40
|
+
class InvalidLocaleData < ArgumentError
|
41
|
+
attr_reader :filename
|
42
|
+
def initialize(filename, exception_message)
|
43
|
+
@filename, @exception_message = filename, exception_message
|
44
|
+
super "can not load translations from #{filename}: #{exception_message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class MissingTranslation < ArgumentError
|
49
|
+
module Base
|
50
|
+
PERMITTED_KEYS = [:scope, :default].freeze
|
51
|
+
|
52
|
+
attr_reader :locale, :key, :options
|
53
|
+
|
54
|
+
def initialize(locale, key, options = EMPTY_HASH)
|
55
|
+
@key, @locale, @options = key, locale, options.slice(*PERMITTED_KEYS)
|
56
|
+
options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def keys
|
60
|
+
@keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
|
61
|
+
keys << 'no key' if keys.size < 2
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def message
|
66
|
+
if (default = options[:default]).is_a?(Array) && default.any?
|
67
|
+
other_options = ([key, *default]).map { |k| normalized_option(k).prepend('- ') }.join("\n")
|
68
|
+
"Translation missing. Options considered were:\n#{other_options}"
|
69
|
+
else
|
70
|
+
"Translation missing: #{keys.join('.')}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def normalized_option(key)
|
75
|
+
I18n.normalize_keys(locale, key, options[:scope]).join('.')
|
76
|
+
end
|
77
|
+
|
78
|
+
alias :to_s :message
|
79
|
+
|
80
|
+
def to_exception
|
81
|
+
MissingTranslationData.new(locale, key, options)
|
82
|
+
end
|
27
83
|
end
|
84
|
+
|
85
|
+
include Base
|
86
|
+
end
|
87
|
+
|
88
|
+
class MissingTranslationData < ArgumentError
|
89
|
+
include MissingTranslation::Base
|
28
90
|
end
|
29
91
|
|
30
92
|
class InvalidPluralizationData < ArgumentError
|
31
|
-
attr_reader :entry, :count
|
32
|
-
def initialize(entry, count)
|
33
|
-
@entry, @count = entry, count
|
34
|
-
super "translation data #{entry.inspect} can not be used with :count => #{count}"
|
93
|
+
attr_reader :entry, :count, :key
|
94
|
+
def initialize(entry, count, key)
|
95
|
+
@entry, @count, @key = entry, count, key
|
96
|
+
super "translation data #{entry.inspect} can not be used with :count => #{count}. key '#{key}' is missing."
|
35
97
|
end
|
36
98
|
end
|
37
99
|
|
38
100
|
class MissingInterpolationArgument < ArgumentError
|
39
|
-
attr_reader :values, :string
|
40
|
-
def initialize(values, string)
|
41
|
-
@values, @string = values, string
|
42
|
-
super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
|
101
|
+
attr_reader :key, :values, :string
|
102
|
+
def initialize(key, values, string)
|
103
|
+
@key, @values, @string = key, values, string
|
104
|
+
super "missing interpolation argument #{key.inspect} in #{string.inspect} (#{values.inspect} given)"
|
43
105
|
end
|
44
106
|
end
|
45
107
|
|
@@ -58,4 +120,38 @@ module I18n
|
|
58
120
|
super "can not load translations from #{filename}, the file type #{type} is not known"
|
59
121
|
end
|
60
122
|
end
|
61
|
-
|
123
|
+
|
124
|
+
class UnsupportedMethod < ArgumentError
|
125
|
+
attr_reader :method, :backend_klass, :msg
|
126
|
+
def initialize(method, backend_klass, msg)
|
127
|
+
@method = method
|
128
|
+
@backend_klass = backend_klass
|
129
|
+
@msg = msg
|
130
|
+
super "#{backend_klass} does not support the ##{method} method. #{msg}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class InvalidFilenames < ArgumentError
|
135
|
+
NUMBER_OF_ERRORS_SHOWN = 20
|
136
|
+
def initialize(file_errors)
|
137
|
+
super <<~MSG
|
138
|
+
Found #{file_errors.count} error(s).
|
139
|
+
The first #{[file_errors.count, NUMBER_OF_ERRORS_SHOWN].min} error(s):
|
140
|
+
#{file_errors.map(&:message).first(NUMBER_OF_ERRORS_SHOWN).join("\n")}
|
141
|
+
|
142
|
+
To use the LazyLoadable backend:
|
143
|
+
1. Filenames must start with the locale.
|
144
|
+
2. An underscore must separate the locale with any optional text that follows.
|
145
|
+
3. The file must only contain translation data for the single locale.
|
146
|
+
|
147
|
+
Example:
|
148
|
+
"/config/locales/fr.yml" which contains:
|
149
|
+
```yml
|
150
|
+
fr:
|
151
|
+
dog:
|
152
|
+
chien
|
153
|
+
```
|
154
|
+
MSG
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/i18n/gettext/helpers.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'i18n/gettext'
|
3
4
|
|
4
5
|
module I18n
|
@@ -6,10 +7,19 @@ module I18n
|
|
6
7
|
# Implements classical Gettext style accessors. To use this include the
|
7
8
|
# module to the global namespace or wherever you want to use it.
|
8
9
|
#
|
9
|
-
# include I18n::Helpers
|
10
|
+
# include I18n::Gettext::Helpers
|
10
11
|
module Helpers
|
11
|
-
|
12
|
-
|
12
|
+
# Makes dynamic translation messages readable for the gettext parser.
|
13
|
+
# <tt>_(fruit)</tt> cannot be understood by the gettext parser. To help the parser find all your translations,
|
14
|
+
# you can add <tt>fruit = N_("Apple")</tt> which does not translate, but tells the parser: "Apple" needs translation.
|
15
|
+
# * msgid: the message id.
|
16
|
+
# * Returns: msgid.
|
17
|
+
def N_(msgsid)
|
18
|
+
msgsid
|
19
|
+
end
|
20
|
+
|
21
|
+
def gettext(msgid, options = EMPTY_HASH)
|
22
|
+
I18n.t(msgid, **{:default => msgid, :separator => '|'}.merge(options))
|
13
23
|
end
|
14
24
|
alias _ gettext
|
15
25
|
|
@@ -28,7 +28,7 @@ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry',
|
|
28
28
|
ret.gsub!(/\\"/, "\"")
|
29
29
|
ret
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def parse(str, data, ignore_fuzzy = true)
|
33
33
|
@comments = []
|
34
34
|
@data = data
|
@@ -64,7 +64,7 @@ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry',
|
|
64
64
|
str = $'
|
65
65
|
when /\A\#(.*)/
|
66
66
|
@q.push [:COMMENT, $&]
|
67
|
-
str = $'
|
67
|
+
str = $'
|
68
68
|
when /\A\"(.*)\"/
|
69
69
|
@q.push [:STRING, $1]
|
70
70
|
str = $'
|
@@ -73,7 +73,7 @@ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry',
|
|
73
73
|
#@q.push [:STRING, c]
|
74
74
|
str = str[1..-1]
|
75
75
|
end
|
76
|
-
end
|
76
|
+
end
|
77
77
|
@q.push [false, '$end']
|
78
78
|
if $DEBUG
|
79
79
|
@q.each do |a,b|
|
@@ -88,7 +88,7 @@ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry',
|
|
88
88
|
end
|
89
89
|
@data
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def next_token
|
93
93
|
@q.shift
|
94
94
|
end
|
@@ -101,11 +101,11 @@ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry',
|
|
101
101
|
@comments.clear
|
102
102
|
@msgctxt = ""
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
def on_comment(comment)
|
106
106
|
@fuzzy = true if (/fuzzy/ =~ comment)
|
107
107
|
@comments << comment
|
108
|
-
end
|
108
|
+
end
|
109
109
|
|
110
110
|
|
111
111
|
..end src/poparser.ry modeval..id7a99570e05
|
@@ -245,7 +245,7 @@ module_eval <<'.,.,', 'src/poparser.ry', 25
|
|
245
245
|
|
246
246
|
module_eval <<'.,.,', 'src/poparser.ry', 48
|
247
247
|
def _reduce_8( val, _values, result )
|
248
|
-
if @fuzzy and $ignore_fuzzy
|
248
|
+
if @fuzzy and $ignore_fuzzy
|
249
249
|
if val[1] != ""
|
250
250
|
$stderr.print _("Warning: fuzzy message was ignored.\n")
|
251
251
|
$stderr.print " msgid '#{val[1]}'\n"
|
data/lib/i18n/gettext.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module I18n
|
4
4
|
module Gettext
|
@@ -10,11 +10,12 @@ module I18n
|
|
10
10
|
@@plural_keys = { :en => [:one, :other] }
|
11
11
|
|
12
12
|
class << self
|
13
|
-
# returns an array of plural keys for the given locale
|
14
|
-
# convert from gettext's
|
13
|
+
# returns an array of plural keys for the given locale or the whole hash
|
14
|
+
# of locale mappings to plural keys so that we can convert from gettext's
|
15
|
+
# integer-index based style
|
15
16
|
# TODO move this information to the pluralization module
|
16
|
-
def plural_keys(
|
17
|
-
@@plural_keys[
|
17
|
+
def plural_keys(*args)
|
18
|
+
args.empty? ? @@plural_keys : @@plural_keys[args.first] || @@plural_keys[:en]
|
18
19
|
end
|
19
20
|
|
20
21
|
def extract_scope(msgid, separator)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# heavily based on Masao Mutoh's gettext String interpolation extension
|
4
|
+
# http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
|
5
|
+
|
6
|
+
module I18n
|
7
|
+
DEFAULT_INTERPOLATION_PATTERNS = [
|
8
|
+
/%%/,
|
9
|
+
/%\{([\w|]+)\}/, # matches placeholders like "%{foo} or %{foo|word}"
|
10
|
+
/%<(\w+)>([^\d]*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
|
11
|
+
].freeze
|
12
|
+
INTERPOLATION_PATTERN = Regexp.union(DEFAULT_INTERPOLATION_PATTERNS)
|
13
|
+
deprecate_constant :INTERPOLATION_PATTERN
|
14
|
+
|
15
|
+
INTERPOLATION_PATTERNS_CACHE = Hash.new do |hash, patterns|
|
16
|
+
hash[patterns] = Regexp.union(patterns)
|
17
|
+
end
|
18
|
+
private_constant :INTERPOLATION_PATTERNS_CACHE
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Return String or raises MissingInterpolationArgument exception.
|
22
|
+
# Missing argument's logic is handled by I18n.config.missing_interpolation_argument_handler.
|
23
|
+
def interpolate(string, values)
|
24
|
+
raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ I18n.reserved_keys_pattern
|
25
|
+
raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
|
26
|
+
interpolate_hash(string, values)
|
27
|
+
end
|
28
|
+
|
29
|
+
def interpolate_hash(string, values)
|
30
|
+
pattern = INTERPOLATION_PATTERNS_CACHE[config.interpolation_patterns]
|
31
|
+
interpolated = false
|
32
|
+
|
33
|
+
interpolated_string = string.gsub(pattern) do |match|
|
34
|
+
interpolated = true
|
35
|
+
|
36
|
+
if match == '%%'
|
37
|
+
'%'
|
38
|
+
else
|
39
|
+
key = ($1 || $2 || match.tr("%{}", "")).to_sym
|
40
|
+
value = if values.key?(key)
|
41
|
+
values[key]
|
42
|
+
else
|
43
|
+
config.missing_interpolation_argument_handler.call(key, values, string)
|
44
|
+
end
|
45
|
+
value = value.call(values) if value.respond_to?(:call)
|
46
|
+
$3 ? sprintf("%#{$3}", value) : value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
interpolated ? interpolated_string : string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|