i18n 1.6.0

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