i18n 1.6.0

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +125 -0
  4. data/lib/i18n.rb +398 -0
  5. data/lib/i18n/backend.rb +21 -0
  6. data/lib/i18n/backend/base.rb +284 -0
  7. data/lib/i18n/backend/cache.rb +113 -0
  8. data/lib/i18n/backend/cache_file.rb +36 -0
  9. data/lib/i18n/backend/cascade.rb +56 -0
  10. data/lib/i18n/backend/chain.rb +127 -0
  11. data/lib/i18n/backend/fallbacks.rb +84 -0
  12. data/lib/i18n/backend/flatten.rb +115 -0
  13. data/lib/i18n/backend/gettext.rb +85 -0
  14. data/lib/i18n/backend/interpolation_compiler.rb +123 -0
  15. data/lib/i18n/backend/key_value.rb +206 -0
  16. data/lib/i18n/backend/memoize.rb +54 -0
  17. data/lib/i18n/backend/metadata.rb +71 -0
  18. data/lib/i18n/backend/pluralization.rb +55 -0
  19. data/lib/i18n/backend/simple.rb +111 -0
  20. data/lib/i18n/backend/transliterator.rb +108 -0
  21. data/lib/i18n/config.rb +165 -0
  22. data/lib/i18n/core_ext/hash.rb +47 -0
  23. data/lib/i18n/exceptions.rb +111 -0
  24. data/lib/i18n/gettext.rb +28 -0
  25. data/lib/i18n/gettext/helpers.rb +75 -0
  26. data/lib/i18n/gettext/po_parser.rb +329 -0
  27. data/lib/i18n/interpolate/ruby.rb +39 -0
  28. data/lib/i18n/locale.rb +8 -0
  29. data/lib/i18n/locale/fallbacks.rb +96 -0
  30. data/lib/i18n/locale/tag.rb +28 -0
  31. data/lib/i18n/locale/tag/parents.rb +22 -0
  32. data/lib/i18n/locale/tag/rfc4646.rb +74 -0
  33. data/lib/i18n/locale/tag/simple.rb +39 -0
  34. data/lib/i18n/middleware.rb +17 -0
  35. data/lib/i18n/tests.rb +14 -0
  36. data/lib/i18n/tests/basics.rb +60 -0
  37. data/lib/i18n/tests/defaults.rb +52 -0
  38. data/lib/i18n/tests/interpolation.rb +159 -0
  39. data/lib/i18n/tests/link.rb +56 -0
  40. data/lib/i18n/tests/localization.rb +19 -0
  41. data/lib/i18n/tests/localization/date.rb +117 -0
  42. data/lib/i18n/tests/localization/date_time.rb +103 -0
  43. data/lib/i18n/tests/localization/procs.rb +116 -0
  44. data/lib/i18n/tests/localization/time.rb +103 -0
  45. data/lib/i18n/tests/lookup.rb +81 -0
  46. data/lib/i18n/tests/pluralization.rb +35 -0
  47. data/lib/i18n/tests/procs.rb +55 -0
  48. data/lib/i18n/version.rb +5 -0
  49. metadata +124 -0
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+
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
+
16
+ class ArgumentError < ::ArgumentError; end
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 explictly with the `locale` argument, e.g. `I18n.#{method}(..., locale: :en)`
28
+ MESSAGE
29
+ end
30
+ end
31
+
32
+ class InvalidLocale < ArgumentError
33
+ attr_reader :locale
34
+ def initialize(locale)
35
+ @locale = locale
36
+ super "#{locale.inspect} is not a valid locale"
37
+ end
38
+ end
39
+
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
+ attr_reader :locale, :key, :options
51
+
52
+ def initialize(locale, key, options = EMPTY_HASH)
53
+ @key, @locale, @options = key, locale, options.dup
54
+ options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
55
+ end
56
+
57
+ def keys
58
+ @keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
59
+ keys << 'no key' if keys.size < 2
60
+ end
61
+ end
62
+
63
+ def message
64
+ "translation missing: #{keys.join('.')}"
65
+ end
66
+ alias :to_s :message
67
+
68
+ def to_exception
69
+ MissingTranslationData.new(locale, key, options)
70
+ end
71
+ end
72
+
73
+ include Base
74
+ end
75
+
76
+ class MissingTranslationData < ArgumentError
77
+ include MissingTranslation::Base
78
+ end
79
+
80
+ class InvalidPluralizationData < ArgumentError
81
+ attr_reader :entry, :count, :key
82
+ def initialize(entry, count, key)
83
+ @entry, @count, @key = entry, count, key
84
+ super "translation data #{entry.inspect} can not be used with :count => #{count}. key '#{key}' is missing."
85
+ end
86
+ end
87
+
88
+ class MissingInterpolationArgument < ArgumentError
89
+ attr_reader :key, :values, :string
90
+ def initialize(key, values, string)
91
+ @key, @values, @string = key, values, string
92
+ super "missing interpolation argument #{key.inspect} in #{string.inspect} (#{values.inspect} given)"
93
+ end
94
+ end
95
+
96
+ class ReservedInterpolationKey < ArgumentError
97
+ attr_reader :key, :string
98
+ def initialize(key, string)
99
+ @key, @string = key, string
100
+ super "reserved key #{key.inspect} used in #{string.inspect}"
101
+ end
102
+ end
103
+
104
+ class UnknownFileType < ArgumentError
105
+ attr_reader :type, :filename
106
+ def initialize(type, filename)
107
+ @type, @filename = type, filename
108
+ super "can not load translations from #{filename}, the file type #{type} is not known"
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module I18n
4
+ module Gettext
5
+ PLURAL_SEPARATOR = "\001"
6
+ CONTEXT_SEPARATOR = "\004"
7
+
8
+ autoload :Helpers, 'i18n/gettext/helpers'
9
+
10
+ @@plural_keys = { :en => [:one, :other] }
11
+
12
+ class << self
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
16
+ # TODO move this information to the pluralization module
17
+ def plural_keys(*args)
18
+ args.empty? ? @@plural_keys : @@plural_keys[args.first] || @@plural_keys[:en]
19
+ end
20
+
21
+ def extract_scope(msgid, separator)
22
+ scope = msgid.to_s.split(separator)
23
+ msgid = scope.pop
24
+ [scope, msgid]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'i18n/gettext'
4
+
5
+ module I18n
6
+ module Gettext
7
+ # Implements classical Gettext style accessors. To use this include the
8
+ # module to the global namespace or wherever you want to use it.
9
+ #
10
+ # include I18n::Gettext::Helpers
11
+ module Helpers
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))
23
+ end
24
+ alias _ gettext
25
+
26
+ def sgettext(msgid, separator = '|')
27
+ scope, msgid = I18n::Gettext.extract_scope(msgid, separator)
28
+ I18n.t(msgid, :scope => scope, :default => msgid, :separator => separator)
29
+ end
30
+ alias s_ sgettext
31
+
32
+ def pgettext(msgctxt, msgid)
33
+ separator = I18n::Gettext::CONTEXT_SEPARATOR
34
+ sgettext([msgctxt, msgid].join(separator), separator)
35
+ end
36
+ alias p_ pgettext
37
+
38
+ def ngettext(msgid, msgid_plural, n = 1)
39
+ nsgettext(msgid, msgid_plural, n)
40
+ end
41
+ alias n_ ngettext
42
+
43
+ # Method signatures:
44
+ # nsgettext('Fruits|apple', 'apples', 2)
45
+ # nsgettext(['Fruits|apple', 'apples'], 2)
46
+ def nsgettext(msgid, msgid_plural, n = 1, separator = '|')
47
+ if msgid.is_a?(Array)
48
+ msgid, msgid_plural, n, separator = msgid[0], msgid[1], msgid_plural, n
49
+ separator = '|' unless separator.is_a?(::String)
50
+ end
51
+
52
+ scope, msgid = I18n::Gettext.extract_scope(msgid, separator)
53
+ default = { :one => msgid, :other => msgid_plural }
54
+ I18n.t(msgid, :default => default, :count => n, :scope => scope, :separator => separator)
55
+ end
56
+ alias ns_ nsgettext
57
+
58
+ # Method signatures:
59
+ # npgettext('Fruits', 'apple', 'apples', 2)
60
+ # npgettext('Fruits', ['apple', 'apples'], 2)
61
+ def npgettext(msgctxt, msgid, msgid_plural, n = 1)
62
+ separator = I18n::Gettext::CONTEXT_SEPARATOR
63
+
64
+ if msgid.is_a?(Array)
65
+ msgid_plural, msgid, n = msgid[1], [msgctxt, msgid[0]].join(separator), msgid_plural
66
+ else
67
+ msgid = [msgctxt, msgid].join(separator)
68
+ end
69
+
70
+ nsgettext(msgid, msgid_plural, n, separator)
71
+ end
72
+ alias np_ npgettext
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,329 @@
1
+ =begin
2
+ poparser.rb - Generate a .mo
3
+
4
+ Copyright (C) 2003-2009 Masao Mutoh <mutoh at highway.ne.jp>
5
+
6
+ You may redistribute it and/or modify it under the same
7
+ license terms as Ruby.
8
+ =end
9
+
10
+ #MODIFIED
11
+ # removed include GetText etc
12
+ # added stub translation method _(x)
13
+ require 'racc/parser'
14
+
15
+ module GetText
16
+
17
+ class PoParser < Racc::Parser
18
+
19
+ def _(x)
20
+ x
21
+ end
22
+
23
+ module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry', 108
24
+ def unescape(orig)
25
+ ret = orig.gsub(/\\n/, "\n")
26
+ ret.gsub!(/\\t/, "\t")
27
+ ret.gsub!(/\\r/, "\r")
28
+ ret.gsub!(/\\"/, "\"")
29
+ ret
30
+ end
31
+
32
+ def parse(str, data, ignore_fuzzy = true)
33
+ @comments = []
34
+ @data = data
35
+ @fuzzy = false
36
+ @msgctxt = ""
37
+ $ignore_fuzzy = ignore_fuzzy
38
+
39
+ str.strip!
40
+ @q = []
41
+ until str.empty? do
42
+ case str
43
+ when /\A\s+/
44
+ str = $'
45
+ when /\Amsgctxt/
46
+ @q.push [:MSGCTXT, $&]
47
+ str = $'
48
+ when /\Amsgid_plural/
49
+ @q.push [:MSGID_PLURAL, $&]
50
+ str = $'
51
+ when /\Amsgid/
52
+ @q.push [:MSGID, $&]
53
+ str = $'
54
+ when /\Amsgstr/
55
+ @q.push [:MSGSTR, $&]
56
+ str = $'
57
+ when /\A\[(\d+)\]/
58
+ @q.push [:PLURAL_NUM, $1]
59
+ str = $'
60
+ when /\A\#~(.*)/
61
+ $stderr.print _("Warning: obsolete msgid exists.\n")
62
+ $stderr.print " #{$&}\n"
63
+ @q.push [:COMMENT, $&]
64
+ str = $'
65
+ when /\A\#(.*)/
66
+ @q.push [:COMMENT, $&]
67
+ str = $'
68
+ when /\A\"(.*)\"/
69
+ @q.push [:STRING, $1]
70
+ str = $'
71
+ else
72
+ #c = str[0,1]
73
+ #@q.push [:STRING, c]
74
+ str = str[1..-1]
75
+ end
76
+ end
77
+ @q.push [false, '$end']
78
+ if $DEBUG
79
+ @q.each do |a,b|
80
+ puts "[#{a}, #{b}]"
81
+ end
82
+ end
83
+ @yydebug = true if $DEBUG
84
+ do_parse
85
+
86
+ if @comments.size > 0
87
+ @data.set_comment(:last, @comments.join("\n"))
88
+ end
89
+ @data
90
+ end
91
+
92
+ def next_token
93
+ @q.shift
94
+ end
95
+
96
+ def on_message(msgid, msgstr)
97
+ if msgstr.size > 0
98
+ @data[msgid] = msgstr
99
+ @data.set_comment(msgid, @comments.join("\n"))
100
+ end
101
+ @comments.clear
102
+ @msgctxt = ""
103
+ end
104
+
105
+ def on_comment(comment)
106
+ @fuzzy = true if (/fuzzy/ =~ comment)
107
+ @comments << comment
108
+ end
109
+
110
+
111
+ ..end src/poparser.ry modeval..id7a99570e05
112
+
113
+ ##### racc 1.4.5 generates ###
114
+
115
+ racc_reduce_table = [
116
+ 0, 0, :racc_error,
117
+ 0, 10, :_reduce_none,
118
+ 2, 10, :_reduce_none,
119
+ 2, 10, :_reduce_none,
120
+ 2, 10, :_reduce_none,
121
+ 2, 12, :_reduce_5,
122
+ 1, 13, :_reduce_none,
123
+ 1, 13, :_reduce_none,
124
+ 4, 15, :_reduce_8,
125
+ 5, 16, :_reduce_9,
126
+ 2, 17, :_reduce_10,
127
+ 1, 17, :_reduce_none,
128
+ 3, 18, :_reduce_12,
129
+ 1, 11, :_reduce_13,
130
+ 2, 14, :_reduce_14,
131
+ 1, 14, :_reduce_15 ]
132
+
133
+ racc_reduce_n = 16
134
+
135
+ racc_shift_n = 26
136
+
137
+ racc_action_table = [
138
+ 3, 13, 5, 7, 9, 15, 16, 17, 20, 17,
139
+ 13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ]
140
+
141
+ racc_action_check = [
142
+ 1, 16, 1, 1, 1, 12, 12, 12, 18, 18,
143
+ 7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ]
144
+
145
+ racc_action_pointer = [
146
+ nil, 0, nil, 14, nil, nil, nil, 3, nil, 6,
147
+ nil, nil, 0, nil, 4, 5, -6, nil, 2, 8,
148
+ 8, 11, nil, 11, nil, 12 ]
149
+
150
+ racc_action_default = [
151
+ -1, -16, -2, -16, -3, -13, -4, -16, -6, -16,
152
+ -7, 26, -16, -15, -5, -16, -16, -14, -16, -8,
153
+ -16, -9, -11, -16, -10, -12 ]
154
+
155
+ racc_goto_table = [
156
+ 12, 22, 14, 4, 24, 6, 2, 8, 18, 19,
157
+ 10, 21, 1, nil, nil, nil, 25 ]
158
+
159
+ racc_goto_check = [
160
+ 5, 9, 5, 3, 9, 4, 2, 6, 5, 5,
161
+ 7, 8, 1, nil, nil, nil, 5 ]
162
+
163
+ racc_goto_pointer = [
164
+ nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ]
165
+
166
+ racc_goto_default = [
167
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ]
168
+
169
+ racc_token_table = {
170
+ false => 0,
171
+ Object.new => 1,
172
+ :COMMENT => 2,
173
+ :MSGID => 3,
174
+ :MSGCTXT => 4,
175
+ :MSGID_PLURAL => 5,
176
+ :MSGSTR => 6,
177
+ :STRING => 7,
178
+ :PLURAL_NUM => 8 }
179
+
180
+ racc_use_result_var = true
181
+
182
+ racc_nt_base = 9
183
+
184
+ Racc_arg = [
185
+ racc_action_table,
186
+ racc_action_check,
187
+ racc_action_default,
188
+ racc_action_pointer,
189
+ racc_goto_table,
190
+ racc_goto_check,
191
+ racc_goto_default,
192
+ racc_goto_pointer,
193
+ racc_nt_base,
194
+ racc_reduce_table,
195
+ racc_token_table,
196
+ racc_shift_n,
197
+ racc_reduce_n,
198
+ racc_use_result_var ]
199
+
200
+ Racc_token_to_s_table = [
201
+ '$end',
202
+ 'error',
203
+ 'COMMENT',
204
+ 'MSGID',
205
+ 'MSGCTXT',
206
+ 'MSGID_PLURAL',
207
+ 'MSGSTR',
208
+ 'STRING',
209
+ 'PLURAL_NUM',
210
+ '$start',
211
+ 'msgfmt',
212
+ 'comment',
213
+ 'msgctxt',
214
+ 'message',
215
+ 'string_list',
216
+ 'single_message',
217
+ 'plural_message',
218
+ 'msgstr_plural',
219
+ 'msgstr_plural_line']
220
+
221
+ Racc_debug_parser = true
222
+
223
+ ##### racc system variables end #####
224
+
225
+ # reduce 0 omitted
226
+
227
+ # reduce 1 omitted
228
+
229
+ # reduce 2 omitted
230
+
231
+ # reduce 3 omitted
232
+
233
+ # reduce 4 omitted
234
+
235
+ module_eval <<'.,.,', 'src/poparser.ry', 25
236
+ def _reduce_5( val, _values, result )
237
+ @msgctxt = unescape(val[1]) + "\004"
238
+ result
239
+ end
240
+ .,.,
241
+
242
+ # reduce 6 omitted
243
+
244
+ # reduce 7 omitted
245
+
246
+ module_eval <<'.,.,', 'src/poparser.ry', 48
247
+ def _reduce_8( val, _values, result )
248
+ if @fuzzy and $ignore_fuzzy
249
+ if val[1] != ""
250
+ $stderr.print _("Warning: fuzzy message was ignored.\n")
251
+ $stderr.print " msgid '#{val[1]}'\n"
252
+ else
253
+ on_message('', unescape(val[3]))
254
+ end
255
+ @fuzzy = false
256
+ else
257
+ on_message(@msgctxt + unescape(val[1]), unescape(val[3]))
258
+ end
259
+ result = ""
260
+ result
261
+ end
262
+ .,.,
263
+
264
+ module_eval <<'.,.,', 'src/poparser.ry', 65
265
+ def _reduce_9( val, _values, result )
266
+ if @fuzzy and $ignore_fuzzy
267
+ if val[1] != ""
268
+ $stderr.print _("Warning: fuzzy message was ignored.\n")
269
+ $stderr.print "msgid = '#{val[1]}\n"
270
+ else
271
+ on_message('', unescape(val[3]))
272
+ end
273
+ @fuzzy = false
274
+ else
275
+ on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4]))
276
+ end
277
+ result = ""
278
+ result
279
+ end
280
+ .,.,
281
+
282
+ module_eval <<'.,.,', 'src/poparser.ry', 76
283
+ def _reduce_10( val, _values, result )
284
+ if val[0].size > 0
285
+ result = val[0] + "\000" + val[1]
286
+ else
287
+ result = ""
288
+ end
289
+ result
290
+ end
291
+ .,.,
292
+
293
+ # reduce 11 omitted
294
+
295
+ module_eval <<'.,.,', 'src/poparser.ry', 84
296
+ def _reduce_12( val, _values, result )
297
+ result = val[2]
298
+ result
299
+ end
300
+ .,.,
301
+
302
+ module_eval <<'.,.,', 'src/poparser.ry', 91
303
+ def _reduce_13( val, _values, result )
304
+ on_comment(val[0])
305
+ result
306
+ end
307
+ .,.,
308
+
309
+ module_eval <<'.,.,', 'src/poparser.ry', 99
310
+ def _reduce_14( val, _values, result )
311
+ result = val.delete_if{|item| item == ""}.join
312
+ result
313
+ end
314
+ .,.,
315
+
316
+ module_eval <<'.,.,', 'src/poparser.ry', 103
317
+ def _reduce_15( val, _values, result )
318
+ result = val[0]
319
+ result
320
+ end
321
+ .,.,
322
+
323
+ def _reduce_none( val, _values, result )
324
+ result
325
+ end
326
+
327
+ end # class PoParser
328
+
329
+ end # module GetText