r18n-core 4.0.0 → 5.0.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +398 -0
  3. data/README.md +23 -13
  4. data/lib/r18n-core.rb +10 -25
  5. data/lib/r18n-core/filter_list.rb +6 -3
  6. data/lib/r18n-core/filters.rb +21 -22
  7. data/lib/r18n-core/i18n.rb +9 -16
  8. data/lib/r18n-core/locale.rb +20 -21
  9. data/lib/r18n-core/locales/cy.rb +1 -1
  10. data/lib/r18n-core/locales/en-au.rb +1 -1
  11. data/lib/r18n-core/locales/en-gb.rb +1 -1
  12. data/lib/r18n-core/locales/en-us.rb +1 -1
  13. data/lib/r18n-core/locales/en.rb +2 -2
  14. data/lib/r18n-core/locales/es-cl.rb +1 -1
  15. data/lib/r18n-core/locales/es-us.rb +1 -1
  16. data/lib/r18n-core/locales/es.rb +2 -0
  17. data/lib/r18n-core/locales/fi.rb +2 -2
  18. data/lib/r18n-core/locales/fr.rb +3 -3
  19. data/lib/r18n-core/locales/it.rb +3 -3
  20. data/lib/r18n-core/locales/pt.rb +2 -0
  21. data/lib/r18n-core/locales/zh-cn.rb +1 -1
  22. data/lib/r18n-core/locales/zh-tw.rb +1 -1
  23. data/lib/r18n-core/locales/zh.rb +2 -0
  24. data/lib/r18n-core/translated.rb +4 -4
  25. data/lib/r18n-core/translated_string.rb +1 -6
  26. data/lib/r18n-core/translation.rb +1 -1
  27. data/lib/r18n-core/unsupported_locale.rb +2 -0
  28. data/lib/r18n-core/utils.rb +1 -1
  29. data/lib/r18n-core/version.rb +1 -1
  30. metadata +198 -89
  31. data/.rspec +0 -1
  32. data/Rakefile +0 -13
  33. data/r18n-core.gemspec +0 -29
  34. data/spec/filters_spec.rb +0 -327
  35. data/spec/i18n_spec.rb +0 -274
  36. data/spec/locale_spec.rb +0 -251
  37. data/spec/locales/af_spec.rb +0 -9
  38. data/spec/locales/cs_spec.rb +0 -23
  39. data/spec/locales/en-us_spec.rb +0 -28
  40. data/spec/locales/en_spec.rb +0 -13
  41. data/spec/locales/es-us_spec.rb +0 -11
  42. data/spec/locales/fa_spec.rb +0 -10
  43. data/spec/locales/fi_spec.rb +0 -9
  44. data/spec/locales/fr_spec.rb +0 -9
  45. data/spec/locales/hu_spec.rb +0 -19
  46. data/spec/locales/id_spec.rb +0 -23
  47. data/spec/locales/it_spec.rb +0 -10
  48. data/spec/locales/no_spec.rb +0 -9
  49. data/spec/locales/pl_spec.rb +0 -23
  50. data/spec/locales/ru_spec.rb +0 -23
  51. data/spec/locales/sk_spec.rb +0 -23
  52. data/spec/locales/th_spec.rb +0 -9
  53. data/spec/locales/vi_spec.rb +0 -9
  54. data/spec/r18n_spec.rb +0 -192
  55. data/spec/spec_helper.rb +0 -38
  56. data/spec/translated_spec.rb +0 -225
  57. data/spec/translation_spec.rb +0 -189
  58. data/spec/translations/extension/deep/en.yml +0 -1
  59. data/spec/translations/extension/en.yml +0 -2
  60. data/spec/translations/extension/notransl.yml +0 -1
  61. data/spec/translations/general/en.yml +0 -52
  62. data/spec/translations/general/nolocale.yml +0 -6
  63. data/spec/translations/general/ru.yml +0 -7
  64. data/spec/translations/two/en.yml +0 -2
  65. data/spec/translations/two/fr.yml +0 -0
  66. data/spec/translations/with_regions/en-US.yml +0 -0
  67. data/spec/translations/yaml/en-GB.yml +0 -1
  68. data/spec/translations/yaml/en-us.yml +0 -1
  69. data/spec/translations/yaml/en.yaml +0 -1
  70. data/spec/yaml_loader_spec.rb +0 -63
data/lib/r18n-core.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Main file to load all neccessary classes for i18n support.
3
+ # Main file to load all necessary classes for i18n support.
4
4
  #
5
5
  # Copyright (C) 2008 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
6
6
  #
@@ -35,40 +35,25 @@
35
35
  require_relative File.join('r18n-core', file)
36
36
  end
37
37
 
38
+ # Root module with singleton methods
38
39
  module R18n
39
40
  autoload :Translated, 'r18n-core/translated'
40
41
 
41
42
  class << self
42
43
  # Set I18n object globally. You can miss translation `places`, it will be
43
44
  # taken from `R18n.default_places`.
44
- def set(i18n = nil, places = R18n.default_places, &block)
45
- @i18n =
46
- if block_given?
47
- @setter = block
48
- nil
49
- elsif i18n.is_a? I18n
50
- i18n
51
- else
52
- I18n.new(i18n, places)
53
- end
45
+ def set(i18n, places = R18n.default_places)
46
+ @i18n = i18n.is_a?(I18n) ? i18n : I18n.new(i18n, places)
54
47
  end
55
48
 
56
49
  # Set I18n object to current thread.
57
- def thread_set(i18n = nil, &block)
58
- if block_given?
59
- thread[:r18n_setter] = block
60
- thread[:r18n_i18n] = nil
61
- else
62
- thread[:r18n_i18n] = i18n
63
- end
50
+ def thread_set(i18n)
51
+ thread[:r18n_i18n] = i18n
64
52
  end
65
53
 
66
54
  # Get I18n object for current thread.
67
55
  def get
68
- thread[:r18n_i18n] ||
69
- (thread[:r18n_setter] && thread_set(thread[:r18n_setter].call)) ||
70
- @i18n ||
71
- (@setter && set(@setter.call))
56
+ thread[:r18n_i18n] || @i18n
72
57
  end
73
58
 
74
59
  # Clean translations cache.
@@ -145,7 +130,7 @@ module R18n
145
130
  attr_writer :default_places
146
131
 
147
132
  def default_places(&block)
148
- if block_given?
133
+ if block
149
134
  @default_places = block
150
135
  elsif @default_places.is_a? Proc
151
136
  @default_places.call
@@ -154,12 +139,12 @@ module R18n
154
139
  end
155
140
  end
156
141
 
157
- # Default loader class, which will be used if you didn't send loader to
142
+ # Default loader class, which will be used if you did not send loader to
158
143
  # `I18n.new` (object with `available` and `load` methods).
159
144
  attr_accessor :default_loader
160
145
 
161
146
  # Loaders with extension translations. If application translations with
162
- # same locale isn't exists, extension file willn't be used.
147
+ # same locale isn't exists, extension file will not be used.
163
148
  attr_accessor :extension_places
164
149
 
165
150
  # `Hash` of hash-like (see Moneta) object to store loaded translations.
@@ -57,7 +57,7 @@ module R18n
57
57
  value = f.call(value, config, *params)
58
58
  end
59
59
 
60
- if value.class == String
60
+ if value.instance_of? String
61
61
  TranslatedString.new(value, config[:locale], config[:path], self)
62
62
  else
63
63
  value
@@ -66,9 +66,10 @@ module R18n
66
66
 
67
67
  # `Array` of enabled filters with `filters_type` for `type`.
68
68
  def enabled(filters_type, type)
69
- if filters_type == :passive
69
+ case filters_type
70
+ when :passive
70
71
  passive(type)
71
- elsif filters_type == :active
72
+ when :active
72
73
  active(type)
73
74
  else
74
75
  all(type)
@@ -111,6 +112,8 @@ module R18n
111
112
  # Filter list for I18n object with custom disabled/enabled filters.
112
113
  class CustomFilterList < FilterList
113
114
  def initialize(on, off)
115
+ super()
116
+
114
117
  @on = Array(on).map { |i| Filters.defined[i] }
115
118
  @off = Array(off).map { |i| Filters.defined[i] }
116
119
  @changed_types = (@on + @off).map(&:types).flatten.uniq
@@ -210,7 +210,7 @@ module R18n
210
210
 
211
211
  Filter = Struct.new(:name, :types, :block, :enabled, :passive) do
212
212
  def call(*params)
213
- block.call(*params)
213
+ instance_exec(*params, &block)
214
214
  end
215
215
 
216
216
  def enabled?
@@ -224,7 +224,7 @@ module R18n
224
224
  end
225
225
 
226
226
  # Class to mark unpluralized translation.
227
- class UnpluralizetedTranslation < Translation
227
+ class UnpluralizedTranslation < Translation
228
228
  end
229
229
 
230
230
  Filters.add('pl', :pluralization) do |content, config, param|
@@ -234,7 +234,7 @@ module R18n
234
234
  type = 'n' unless content.key? type
235
235
  content[type]
236
236
  else
237
- UnpluralizetedTranslation.new(
237
+ UnpluralizedTranslation.new(
238
238
  config[:locale], config[:path],
239
239
  locale: config[:locale], translations: content
240
240
  )
@@ -243,13 +243,11 @@ module R18n
243
243
 
244
244
  Filters.add(String, :variables) do |content, config, *params|
245
245
  cached_params = []
246
- content.to_s.gsub(/\%\d/) do |key|
246
+ content.to_s.gsub(/%\d/) do |key|
247
247
  i = key[1..-1].to_i
248
248
  unless cached_params.include? i - 1
249
249
  param = config[:locale].localize(params[i - 1])
250
- if defined? ActiveSupport::SafeBuffer
251
- param = ActiveSupport::SafeBuffer.new + param
252
- end
250
+ param = ActiveSupport::SafeBuffer.new + param if defined? ActiveSupport::SafeBuffer
253
251
 
254
252
  cached_params[i - 1] = param
255
253
  end
@@ -260,13 +258,11 @@ module R18n
260
258
  Filters.add(String, :named_variables) do |content, config, params|
261
259
  if params.is_a? Hash
262
260
  content = content.clone
263
- params.each_pair do |name, value|
264
- value = config[:locale].localize(value)
265
- if defined? ActiveSupport::SafeBuffer
266
- value = ActiveSupport::SafeBuffer.new + value
267
- end
268
- content.gsub! "%{#{name}}", value
269
- content.gsub! "{{#{name}}}", value
261
+ content.gsub!(/(?:%{(\w+)}|{{(\w+)}})/) do |name|
262
+ key = name.delete('%{}').to_sym
263
+ value = config[:locale].localize(params[key])
264
+ value = ActiveSupport::SafeBuffer.new + value if defined? ActiveSupport::SafeBuffer
265
+ value
270
266
  end
271
267
  end
272
268
  content
@@ -277,20 +273,23 @@ module R18n
277
273
  "#{translated}[#{untranslated}]"
278
274
  end
279
275
 
280
- Filters.add(Untranslated, :untranslated_bash) do |_v, _c, transl, untransl|
281
- "#{transl}\e[0;31m[#{untransl}]\e[0m"
276
+ Filters.add(Untranslated, :untranslated_bash) do |_v, _c, translated, untranslated|
277
+ "#{translated}\e[0;31m[#{untranslated}]\e[0m"
282
278
  end
283
279
  Filters.off(:untranslated_bash)
284
280
 
285
- Filters.add(Untranslated, :untranslated_html) do |_v, _c, transl, untransl|
281
+ Filters.add(Untranslated, :untranslated_html) do |_v, _c, translated, untranslated|
286
282
  prefix = '<span style="color: red">['
287
283
  postfix = ']</span>'
288
- if prefix.respond_to? :html_safe
289
- prefix = prefix.html_safe
290
- postfix = postfix.html_safe
291
- transl + prefix + untransl + postfix
284
+ if defined? ActiveSupport::SafeBuffer
285
+ ## Not every project with `ActiveSupport` has `ActionView`
286
+ ## https://github.com/r18n/r18n/issues/251
287
+ ## I guess, we can trust to developers here
288
+ translated = ActiveSupport::SafeBuffer.new + translated
289
+ untranslated = ActiveSupport::SafeBuffer.new + untranslated
290
+ translated + prefix.html_safe + untranslated + postfix.html_safe
292
291
  else
293
- Utils.escape_html(transl) + prefix + Utils.escape_html(untransl) + postfix
292
+ Utils.escape_html(translated) + prefix + Utils.escape_html(untranslated) + postfix
294
293
  end
295
294
  end
296
295
  Filters.off(:untranslated_html)
@@ -137,15 +137,11 @@ module R18n
137
137
  def initialize(locales, translation_places = nil, opts = {})
138
138
  locales = Array(locales)
139
139
 
140
- if !locales.empty? && Locale.exists?(locales.first)
141
- locales += Locale.load(locales.first).sublocales
142
- end
143
- locales << self.class.default
144
140
  locales.each_with_index do |locale, i|
145
- if locale =~ /[^_-]+[_-]/
146
- locales.insert(i + 1, locale.match(/([^_-]+)[_-]/)[1])
147
- end
141
+ locales.insert(i + 1, locale.match(/([^_-]+)[_-]/)[1]) if locale.match?(/[^_-]+[_-]/)
142
+ locales.insert(i + 1, *(Locale.load(locale).sublocales - locales)) if Locale.exists?(locale)
148
143
  end
144
+ locales << self.class.default
149
145
  locales.map! { |i| i.to_s.downcase }.uniq!
150
146
  @locales_codes = locales
151
147
  @locales = locales.each_with_object([]) do |locale, result|
@@ -189,10 +185,8 @@ module R18n
189
185
  @available_codes ||= @translation_places
190
186
  .inject([]) { |all, i| all + i.available }
191
187
  .uniq.map { |i| i.code.downcase }
192
- (@locales_codes & @available_codes).join(',') + '@' +
193
- @filters.hash.to_s + '_' +
194
- R18n.default_loader.hash.to_s + '_' +
195
- @translation_places.hash.to_s + '_' +
188
+ "#{(@locales_codes & @available_codes).join(',')}@" \
189
+ "#{@filters.hash}_#{R18n.default_loader.hash}_#{@translation_places.hash}_" +
196
190
  R18n.extension_places.hash.to_s
197
191
  end
198
192
 
@@ -240,9 +234,8 @@ module R18n
240
234
  next unless loaded
241
235
 
242
236
  available_in_extensions.each do |extension, available|
243
- if available.include? locale
244
- @translation.merge! extension.load(locale), locale
245
- end
237
+ @translation.merge! extension.load(locale), locale if available.include? locale
238
+
246
239
  if available.include? locale.parent
247
240
  @translation.merge! extension.load(locale.parent), locale.parent
248
241
  end
@@ -270,8 +263,8 @@ module R18n
270
263
  # i18n.l Time.now.to_date #=> "07/01/09"
271
264
  # i18n.l Time.now, :human #=> "now"
272
265
  # i18n.l Time.now, :full #=> "Jule 1st, 2009 12:59"
273
- def localize(object, format = nil, *params)
274
- locale.localize(object, format, self, *params)
266
+ def localize(object, format = nil, **kwargs)
267
+ locale.localize(object, format, i18n: self, **kwargs)
275
268
  end
276
269
  alias l localize
277
270
 
@@ -109,6 +109,7 @@ module R18n
109
109
  # locale.code #=> "en"
110
110
  def set(properties)
111
111
  properties.each_pair do |key, value|
112
+ undef_method key if method_defined? key
112
113
  define_method(key) { value }
113
114
  end
114
115
  end
@@ -127,7 +128,7 @@ module R18n
127
128
  @parent = self.class.superclass.new
128
129
  end
129
130
 
130
- set sublocales: %w[en],
131
+ set sublocales: [],
131
132
  week_start: :monday,
132
133
  time_am: 'AM',
133
134
  time_pm: 'PM',
@@ -175,7 +176,7 @@ module R18n
175
176
  # `:full` ("01 Jule, 2009"), `:human` ("yesterday"),
176
177
  # `:standard` ("07/01/09") or `:month` for standalone month
177
178
  # name. Default format is `:standard`.
178
- def localize(obj, format = nil, *params)
179
+ def localize(obj, format = nil, *args, **kwargs)
179
180
  case obj
180
181
  when Integer
181
182
  format_integer(obj)
@@ -184,7 +185,7 @@ module R18n
184
185
  when Time, DateTime, Date
185
186
  return strftime(obj, format) if format.is_a? String
186
187
  return month_standalone[obj.month - 1] if format == :month
187
- return obj.to_s if format == :human && !params.first.is_a?(I18n)
188
+ return obj.to_s if format == :human && !kwargs.key?(:i18n)
188
189
 
189
190
  type = obj.is_a?(Date) && !obj.is_a?(DateTime) ? 'date' : 'time'
190
191
  format ||= :standard
@@ -194,14 +195,14 @@ module R18n
194
195
  raise ArgumentError, "Unknown time formatter #{format}"
195
196
  end
196
197
 
197
- send format_method_name, obj, *params
198
+ send format_method_name, obj, *args, **kwargs
198
199
  else
199
200
  format_method_name =
200
- "format_#{Utils.underscore(obj.class.name)}_#{format}"
201
+ "format_#{Utils.underscore(obj.class.name).tr('/', '_')}_#{format}"
201
202
 
202
203
  return obj.to_s unless respond_to? format_method_name
203
204
 
204
- send format_method_name, obj, *params
205
+ send format_method_name, obj, *args, **kwargs
205
206
  end
206
207
  end
207
208
 
@@ -250,7 +251,7 @@ module R18n
250
251
  end
251
252
 
252
253
  # Format `time` and set `date`
253
- def format_time(date, time, with_seconds: false)
254
+ def format_time(date, time, with_seconds: false, **_kwargs)
254
255
  strftime(
255
256
  time, with_seconds ? time_with_seconds_format : time_format
256
257
  ).sub('_', date.to_s)
@@ -259,12 +260,12 @@ module R18n
259
260
  # Format `time` in human usable form. For example "5 minutes ago" or
260
261
  # "yesterday". In `now` you can set base time, which be used to get relative
261
262
  # time. For special cases you can replace it in locale's class.
262
- def format_time_human(time, i18n, now = time.class.now, *_params)
263
+ def format_time_human(time, i18n:, now: time.class.now, **_kwargs)
263
264
  diff = time - now
264
265
  minutes = time.is_a?(DateTime) ? diff * 24 * 60.0 : diff / 60.0
265
266
  diff = minutes.abs
266
267
  if (diff > 24 * 60) || (time.mday != now.mday && diff > 12 * 24)
267
- format_time(format_date_human(time.to_date, i18n, now.to_date), time)
268
+ format_time(format_date_human(time.to_date, now: now.to_date, i18n: i18n), time)
268
269
  elsif minutes > -1 && minutes < 1
269
270
  i18n.human_time.now
270
271
  elsif minutes >= 60
@@ -279,22 +280,20 @@ module R18n
279
280
  end
280
281
 
281
282
  # Format `time` in compact form. For example, "12/31/09 12:59".
282
- def format_time_standard(time, *params)
283
- options = params.last.is_a?(Hash) ? params.last : {}
284
- format_time(format_date_standard(time), time, **options)
283
+ def format_time_standard(time, *args, **kwargs)
284
+ format_time(format_date_standard(time), time, *args, **kwargs)
285
285
  end
286
286
 
287
287
  # Format `time` in most official form. For example, "December 31st, 2009
288
288
  # 12:59". For special cases you can replace it in locale's class.
289
- def format_time_full(time, *params)
290
- options = params.last.is_a?(Hash) ? params.last : {}
291
- format_time(format_date_full(time), time, **options)
289
+ def format_time_full(time, **kwargs)
290
+ format_time(format_date_full(time, **kwargs), time, **kwargs)
292
291
  end
293
292
 
294
- # Format `date` in human usable form. For example "5 days ago" or
295
- # "yesterday". In `now` you can set base time, which be used to get relative
296
- # time. For special cases you can replace it in locale's class.
297
- def format_date_human(date, i18n, now = Date.today, *_params)
293
+ # Format `date` in human usable form. For example "5 days ago" or "yesterday".
294
+ # In `:now` you can set base time, which be used to get relative time.
295
+ # For special cases you can replace it in locale's class.
296
+ def format_date_human(date, *_args, i18n:, now: Date.today, **_kwargs)
298
297
  days = (date - now).to_i
299
298
  case days
300
299
  when -6..-2
@@ -308,7 +307,7 @@ module R18n
308
307
  when 2..6
309
308
  i18n.human_time.after_days(days)
310
309
  else
311
- format_date_full(date, date.year != now.year)
310
+ format_date_full(date, year: date.year != now.year)
312
311
  end
313
312
  end
314
313
 
@@ -320,7 +319,7 @@ module R18n
320
319
  # Format `date` in most official form. For example, "December 31st, 2009".
321
320
  # For special cases you can replace it in locale's class. If `year` is false
322
321
  # date will be without year.
323
- def format_date_full(date, year = true, *_params)
322
+ def format_date_full(date, year: true, **_kwargs)
324
323
  format = full_format
325
324
  format = year_format.sub('_', format) if year
326
325
  strftime(date, format)
@@ -40,7 +40,7 @@ module R18n
40
40
  end
41
41
  end
42
42
 
43
- def format_date_full(date, year = true, *_params)
43
+ def format_date_full(date, year: true, **_kwargs)
44
44
  format = full_format
45
45
  format = year_format.sub('_', format) if year
46
46
  strftime(date, format.sub('%e', ordinalize(date.mday)))
@@ -7,7 +7,7 @@ module R18n
7
7
  # Australian English locale
8
8
  class EnAU < En
9
9
  set title: 'Australian English',
10
- sublocales: %w[en]
10
+ sublocales: %w[en en-US en-GB]
11
11
  end
12
12
  end
13
13
  end
@@ -8,7 +8,7 @@ module R18n
8
8
  class EnGB < En
9
9
  set(
10
10
  title: 'British English',
11
- sublocales: %w[en],
11
+ sublocales: %w[en en-US en-AU],
12
12
 
13
13
  date_format: '%d/%m/%Y'
14
14
  )
@@ -8,7 +8,7 @@ module R18n
8
8
  class EnUS < En
9
9
  set(
10
10
  title: 'American English',
11
- sublocales: %w[en],
11
+ sublocales: %w[en en-GB en-AU],
12
12
 
13
13
  time_format: '_ %I:%M %p',
14
14
  time_with_seconds_format: '_ %r',
@@ -6,7 +6,7 @@ module R18n
6
6
  class En < Locale
7
7
  set(
8
8
  title: 'English',
9
- sublocales: [],
9
+ sublocales: %w[en-US en-GB en-AU],
10
10
 
11
11
  week_start: :sunday,
12
12
  wday_names: %w[
@@ -41,7 +41,7 @@ module R18n
41
41
  end
42
42
  end
43
43
 
44
- def format_date_full(date, year = true, *_params)
44
+ def format_date_full(date, year: true, **_kwargs)
45
45
  format = full_format
46
46
  format = year_format.sub('_', format) if year
47
47
  strftime(date, format.sub('%-d', ordinalize(date.mday)))
@@ -8,7 +8,7 @@ module R18n
8
8
  class EsCL < Es
9
9
  set(
10
10
  title: 'Español Chile',
11
- sublocales: %w[es],
11
+ sublocales: %w[es es-US],
12
12
  date_format: '%d-%m-%Y'
13
13
  )
14
14
  end
@@ -8,7 +8,7 @@ module R18n
8
8
  class EsUS < Es
9
9
  set(
10
10
  title: 'Español Estadounidense',
11
- sublocales: %w[es en],
11
+ sublocales: %w[es es-CL en],
12
12
 
13
13
  time_format: '_ %I:%M %p',
14
14
  time_with_seconds_format: '_ %r',