i18n 0.4.0 → 1.14.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|