r18n-core 3.1.1 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +398 -0
  3. data/README.md +63 -47
  4. data/base/cy.yml +34 -0
  5. data/lib/r18n-core.rb +37 -38
  6. data/lib/r18n-core/filter_list.rb +21 -13
  7. data/lib/r18n-core/filters.rb +43 -48
  8. data/lib/r18n-core/helpers.rb +2 -2
  9. data/lib/r18n-core/i18n.rb +70 -78
  10. data/lib/r18n-core/locale.rb +137 -107
  11. data/lib/r18n-core/locales/af.rb +36 -0
  12. data/lib/r18n-core/locales/az.rb +38 -0
  13. data/lib/r18n-core/locales/bg.rb +29 -0
  14. data/{locales → lib/r18n-core/locales}/ca.rb +9 -6
  15. data/{locales → lib/r18n-core/locales}/cs.rb +15 -11
  16. data/lib/r18n-core/locales/cy.rb +50 -0
  17. data/{locales → lib/r18n-core/locales}/da.rb +10 -7
  18. data/lib/r18n-core/locales/de.rb +33 -0
  19. data/{locales → lib/r18n-core/locales}/en-au.rb +2 -2
  20. data/{locales → lib/r18n-core/locales}/en-gb.rb +2 -2
  21. data/{locales → lib/r18n-core/locales}/en-us.rb +4 -3
  22. data/lib/r18n-core/locales/en.rb +51 -0
  23. data/{locales → lib/r18n-core/locales}/eo.rb +6 -4
  24. data/lib/r18n-core/locales/es-cl.rb +16 -0
  25. data/{locales → lib/r18n-core/locales}/es-us.rb +5 -4
  26. data/{locales → lib/r18n-core/locales}/es.rb +7 -3
  27. data/{locales → lib/r18n-core/locales}/fa.rb +2 -2
  28. data/lib/r18n-core/locales/fi.rb +39 -0
  29. data/lib/r18n-core/locales/fr.rb +39 -0
  30. data/{locales → lib/r18n-core/locales}/gl.rb +5 -3
  31. data/{locales → lib/r18n-core/locales}/hr.rb +13 -10
  32. data/{locales → lib/r18n-core/locales}/hu.rb +10 -7
  33. data/{locales → lib/r18n-core/locales}/id.rb +8 -4
  34. data/lib/r18n-core/locales/it.rb +37 -0
  35. data/{locales → lib/r18n-core/locales}/ja.rb +5 -4
  36. data/{locales → lib/r18n-core/locales}/kk.rb +12 -8
  37. data/{locales → lib/r18n-core/locales}/ko.rb +0 -0
  38. data/lib/r18n-core/locales/lv.rb +46 -0
  39. data/lib/r18n-core/locales/mn.rb +30 -0
  40. data/{locales → lib/r18n-core/locales}/nb.rb +6 -4
  41. data/lib/r18n-core/locales/nl.rb +31 -0
  42. data/{locales → lib/r18n-core/locales}/no.rb +0 -0
  43. data/lib/r18n-core/locales/pl.rb +45 -0
  44. data/{locales → lib/r18n-core/locales}/pt-br.rb +1 -1
  45. data/lib/r18n-core/locales/pt.rb +37 -0
  46. data/lib/r18n-core/locales/ru.rb +46 -0
  47. data/{locales → lib/r18n-core/locales}/sk.rb +13 -9
  48. data/{locales → lib/r18n-core/locales}/sr-latn.rb +10 -8
  49. data/{locales → lib/r18n-core/locales}/sv-se.rb +7 -5
  50. data/lib/r18n-core/locales/th.rb +41 -0
  51. data/{locales → lib/r18n-core/locales}/tr.rb +7 -6
  52. data/lib/r18n-core/locales/uk.rb +31 -0
  53. data/lib/r18n-core/locales/vi.rb +37 -0
  54. data/{locales → lib/r18n-core/locales}/zh-cn.rb +2 -2
  55. data/{locales → lib/r18n-core/locales}/zh-tw.rb +4 -4
  56. data/{locales → lib/r18n-core/locales}/zh.rb +7 -4
  57. data/lib/r18n-core/translated.rb +38 -37
  58. data/lib/r18n-core/translated_string.rb +19 -8
  59. data/lib/r18n-core/translation.rb +24 -19
  60. data/lib/r18n-core/unsupported_locale.rb +14 -6
  61. data/lib/r18n-core/untranslated.rb +23 -7
  62. data/lib/r18n-core/utils.rb +19 -23
  63. data/lib/r18n-core/version.rb +1 -1
  64. data/lib/r18n-core/yaml_loader.rb +17 -13
  65. data/lib/r18n-core/yaml_methods.rb +3 -1
  66. metadata +245 -118
  67. data/.rspec +0 -1
  68. data/Rakefile +0 -13
  69. data/locales/af.rb +0 -32
  70. data/locales/az.rb +0 -34
  71. data/locales/bg.rb +0 -25
  72. data/locales/de.rb +0 -29
  73. data/locales/en.rb +0 -48
  74. data/locales/fi.rb +0 -35
  75. data/locales/fr.rb +0 -35
  76. data/locales/it.rb +0 -34
  77. data/locales/lv.rb +0 -41
  78. data/locales/mn.rb +0 -27
  79. data/locales/nl.rb +0 -28
  80. data/locales/pl.rb +0 -40
  81. data/locales/pt.rb +0 -29
  82. data/locales/ru.rb +0 -41
  83. data/locales/th.rb +0 -37
  84. data/locales/uk.rb +0 -27
  85. data/locales/vi.rb +0 -33
  86. data/r18n-core.gemspec +0 -29
  87. data/spec/filters_spec.rb +0 -324
  88. data/spec/i18n_spec.rb +0 -260
  89. data/spec/locale_spec.rb +0 -216
  90. data/spec/locales/cs_spec.rb +0 -23
  91. data/spec/locales/en-us_spec.rb +0 -13
  92. data/spec/locales/en_spec.rb +0 -13
  93. data/spec/locales/fa_spec.rb +0 -10
  94. data/spec/locales/fr_spec.rb +0 -9
  95. data/spec/locales/hu_spec.rb +0 -17
  96. data/spec/locales/it_spec.rb +0 -10
  97. data/spec/locales/no_spec.rb +0 -9
  98. data/spec/locales/pl_spec.rb +0 -23
  99. data/spec/locales/ru_spec.rb +0 -23
  100. data/spec/locales/sk_spec.rb +0 -23
  101. data/spec/locales/th_spec.rb +0 -9
  102. data/spec/locales/vi_spec.rb +0 -8
  103. data/spec/r18n_spec.rb +0 -172
  104. data/spec/spec_helper.rb +0 -41
  105. data/spec/translated_spec.rb +0 -163
  106. data/spec/translation_spec.rb +0 -164
  107. data/spec/translations/extension/deep/en.yml +0 -1
  108. data/spec/translations/extension/en.yml +0 -2
  109. data/spec/translations/extension/notransl.yml +0 -1
  110. data/spec/translations/general/en.yml +0 -47
  111. data/spec/translations/general/nolocale.yml +0 -6
  112. data/spec/translations/general/ru.yml +0 -7
  113. data/spec/translations/two/en.yml +0 -2
  114. data/spec/translations/two/fr.yml +0 -0
  115. data/spec/yaml_loader_spec.rb +0 -61
@@ -32,12 +32,12 @@ module R18n
32
32
  end
33
33
  alias i18n r18n
34
34
 
35
- # Translate message. Alias for <tt>r18n.t</tt>.
35
+ # Translate message. Alias for `r18n.t`.
36
36
  def t(*params)
37
37
  R18n.get.t(*params)
38
38
  end
39
39
 
40
- # Localize object. Alias for <tt>r18n.l</tt>.
40
+ # Localize object. Alias for `r18n.l`.
41
41
  def l(*params)
42
42
  R18n.get.l(*params)
43
43
  end
@@ -24,14 +24,14 @@ module R18n
24
24
  # Locale classes and create pretty way to use it.
25
25
  #
26
26
  # To get translation you can use same with Translation way – use method with
27
- # translations name or <tt>[name]</tt> method. Translations will be also
28
- # loaded for default locale, +sublocales+ from first in +locales+ and general
29
- # languages for dialects (it will load +fr+ for +fr_CA+ too).
27
+ # translation's name or `[name]` method. Translations will be also
28
+ # loaded for default locale, `sublocales` from first in `locales` and general
29
+ # languages for dialects (it will load `fr` for `fr_CA` too).
30
30
  #
31
31
  # Translations will loaded by loader object, which must have 2 methods:
32
- # * <tt>available</tt> – return array of locales of available translations;
33
- # * <tt>load(locale)</tt> – return Hash of translation.
34
- # If you will use default loader (+R18n.default_loader+) you can pass to I18n
32
+ # * `available` – return array of locales of available translations;
33
+ # * `load(locale)` – return Hash of translation.
34
+ # If you will use default loader (`R18n.default_loader`) you can pass to I18n
35
35
  # only constructor argument for loader:
36
36
  #
37
37
  # R18n::I18n.new('en', R18n::Loader::YAML.new('dir/with/translations'))
@@ -41,16 +41,16 @@ module R18n
41
41
  # R18n::I18n.new('en', 'dir/with/translations')
42
42
  #
43
43
  # In translation file you can use strings, numbers, floats (any YAML types)
44
- # and pluralizable values (<tt>!!pl</tt>). You can use params in string
45
- # values, which you can replace in program. Just write <tt>%1</tt>,
46
- # <tt>%2</tt>, etc and set it values as method arguments, when you will be get
44
+ # and pluralizable values (`!!pl`). You can use params in string
45
+ # values, which you can replace in program. Just write `%1`,
46
+ # `%2`, etc and set it values as method arguments, when you will be get
47
47
  # value.
48
48
  #
49
49
  # You can use filters for some YAML type or for all strings. See R18n::Filters
50
50
  # for details.
51
51
  #
52
- # R18n contain translations for common words (such as OK”, Cancel”, etc)
53
- # for most supported locales. See <tt>base/</tt> dir.
52
+ # R18n contain translations for common words (such as "OK", "Cancel", etc)
53
+ # for most supported locales. See `base/` dir.
54
54
  #
55
55
  # == Usage
56
56
  # translations/ru.yml
@@ -83,43 +83,37 @@ module R18n
83
83
  # i18n.ok #=> "OK"
84
84
  # i18n.cancel #=> "Cancel"
85
85
  class I18n
86
- @@default = 'en'
86
+ @default = 'en'
87
87
 
88
- # Set default locale code to use when any user locales willn't be founded.
89
- # It should has all translations and locale file.
90
- def self.default=(locale)
91
- @@default = locale
92
- end
88
+ class << self
89
+ attr_accessor :default
93
90
 
94
- # Get default locale code
95
- def self.default
96
- @@default
97
- end
91
+ # Parse HTTP_ACCEPT_LANGUAGE and return array of user locales
92
+ def parse_http(str)
93
+ return [] if str.nil?
98
94
 
99
- # Parse HTTP_ACCEPT_LANGUAGE and return array of user locales
100
- def self.parse_http(str)
101
- return [] if str.nil?
102
- locales = str.split(',')
103
- locales.map! do |locale|
104
- locale = locale.split ';q='
105
- if locale.size == 1
106
- [locale[0], 1.0]
107
- else
108
- [locale[0], locale[1].to_f]
95
+ locales = str.split(',')
96
+ locales.map! do |locale|
97
+ locale = locale.split ';q='
98
+ if locale.size == 1
99
+ [locale[0], 1.0]
100
+ else
101
+ [locale[0], locale[1].to_f]
102
+ end
109
103
  end
104
+ locales.sort! { |a, b| b[1] <=> a[1] }
105
+ locales.map! { |i| i[0] }
110
106
  end
111
- locales.sort! { |a, b| b[1] <=> a[1] }
112
- locales.map! { |i| i[0] }
113
- end
114
107
 
115
- # Load default loader for elements in +places+ with only constructor
116
- # argument.
117
- def self.convert_places(places)
118
- Array(places).map! do |loader|
119
- if loader.respond_to?(:available) && loader.respond_to?(:load)
120
- loader
121
- else
122
- R18n.default_loader.new(loader)
108
+ # Load default loader for elements in `places` with only constructor
109
+ # argument.
110
+ def convert_places(places)
111
+ Array(places).map! do |loader|
112
+ if loader.respond_to?(:available) && loader.respond_to?(:load)
113
+ loader
114
+ else
115
+ R18n.default_loader.new(loader)
116
+ end
123
117
  end
124
118
  end
125
119
  end
@@ -133,28 +127,29 @@ module R18n
133
127
  # First locale with locale file
134
128
  attr_reader :locale
135
129
 
136
- # Create i18n for +locales+ with translations from +translation_places+ and
130
+ # Create i18n for `locales` with translations from `translation_places` and
137
131
  # locales data. Translations will be also loaded for default locale,
138
- # +sublocales+ from first in +locales+ and general languages for dialects
139
- # (it will load +fr+ for +fr_CA+ too).
132
+ # `sublocales` from first in `locales` and general languages for dialects
133
+ # (it will load `fr` for `fr_CA` too).
140
134
  #
141
- # +Locales+ must be a locale code (RFC 3066) or array, ordered by priority.
142
- # +Translation_places+ must be a string with path or array.
135
+ # `locales` must be a locale code (RFC 3066) or array, ordered by priority.
136
+ # `translation_places` must be a string with path or array.
143
137
  def initialize(locales, translation_places = nil, opts = {})
144
138
  locales = Array(locales)
145
139
 
146
- if !locales.empty? && Locale.exists?(locales.first)
147
- locales += Locale.load(locales.first).sublocales
148
- end
149
- locales << @@default
150
140
  locales.each_with_index do |locale, i|
151
- if locale =~ /[^_-]+[_-]/
152
- locales.insert(i + 1, locale.match(/([^_-]+)[_-]/)[1])
153
- 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)
154
143
  end
144
+ locales << self.class.default
155
145
  locales.map! { |i| i.to_s.downcase }.uniq!
156
146
  @locales_codes = locales
157
- @locales = locales.map { |i| Locale.load(i) }
147
+ @locales = locales.each_with_object([]) do |locale, result|
148
+ locale = Locale.load(locale)
149
+ next unless locale
150
+
151
+ result << locale
152
+ end
158
153
 
159
154
  if translation_places
160
155
  @original_places = translation_places
@@ -190,16 +185,14 @@ module R18n
190
185
  @available_codes ||= @translation_places
191
186
  .inject([]) { |all, i| all + i.available }
192
187
  .uniq.map { |i| i.code.downcase }
193
- (@locales_codes & @available_codes).join(',') + '@' +
194
- @filters.hash.to_s + '_' +
195
- R18n.default_loader.hash.to_s + '_' +
196
- @translation_places.hash.to_s + '_' +
188
+ "#{(@locales_codes & @available_codes).join(',')}@" \
189
+ "#{@filters.hash}_#{R18n.default_loader.hash}_#{@translation_places.hash}_" +
197
190
  R18n.extension_places.hash.to_s
198
191
  end
199
192
 
200
193
  # Reload translations.
201
194
  def reload!
202
- @available = @available_codes = nil
195
+ @available_locales = @available_codes = nil
203
196
  @translation_places = self.class.convert_places(@original_places)
204
197
 
205
198
  available_in_places = @translation_places.map { |i| [i, i.available] }
@@ -207,8 +200,6 @@ module R18n
207
200
  R18n.extension_places.map { |i| [i, i.available] }
208
201
 
209
202
  unless defined? @locale
210
- # It's array!
211
- # rubocop:disable Perfomance/HashEachMethods
212
203
  available_in_places.each do |_place, available|
213
204
  @locales.each do |locale|
214
205
  if available.include? locale
@@ -218,7 +209,6 @@ module R18n
218
209
  end
219
210
  break if defined? @locale
220
211
  end
221
- # rubocop:enable Perfomance/HashEachMethods
222
212
  end
223
213
  @locale ||= @locales.first
224
214
  unless @locale.supported?
@@ -244,8 +234,10 @@ module R18n
244
234
  next unless loaded
245
235
 
246
236
  available_in_extensions.each do |extension, available|
247
- if available.include? locale
248
- @translation.merge! extension.load(locale), locale
237
+ @translation.merge! extension.load(locale), locale if available.include? locale
238
+
239
+ if available.include? locale.parent
240
+ @translation.merge! extension.load(locale.parent), locale.parent
249
241
  end
250
242
  end
251
243
  end
@@ -253,26 +245,26 @@ module R18n
253
245
  R18n.cache[translation_cache_key] = [@locale, @translation]
254
246
  end
255
247
 
256
- # Return Array of locales with available translations.
248
+ # Return `Array` of locales with available translations.
257
249
  def available_locales
258
- @available ||= R18n.available_locales(@translation_places)
250
+ @available_locales ||= R18n.available_locales(@translation_places)
259
251
  end
260
252
 
261
- # Convert +object+ to String, according to the rules of the current locale.
262
- # It support Integer, Float, Time, Date and DateTime.
253
+ # Convert `object` to `String`, according to the rules of the current
254
+ # locale. It support `Integer`, `Float`, `Time`, `Date` and `DateTime`.
263
255
  #
264
- # For time classes you can set +format+ in standard +strftime+ form,
265
- # <tt>:full</tt> (01 Jule, 2009), <tt>:human</tt> (yesterday),
266
- # <tt>:standard</tt> (07/01/09) or <tt>:month</tt> for standalone month
267
- # name. Default format is <tt>:standard</tt>.
256
+ # For time classes you can set `format` in standard `strftime` form,
257
+ # `:full` ("01 Jule, 2009"), `:human` ("yesterday"),
258
+ # `:standard` ("07/01/09") or `:month` for standalone month
259
+ # name. Default format is `:standard`.
268
260
  #
269
261
  # i18n.l -12000.5 #=> "−12,000.5"
270
262
  # i18n.l Time.now #=> "07/01/09 12:59"
271
263
  # i18n.l Time.now.to_date #=> "07/01/09"
272
264
  # i18n.l Time.now, :human #=> "now"
273
265
  # i18n.l Time.now, :full #=> "Jule 1st, 2009 12:59"
274
- def localize(object, format = nil, *params)
275
- locale.localize(object, format, self, *params)
266
+ def localize(object, format = nil, **kwargs)
267
+ locale.localize(object, format, i18n: self, **kwargs)
276
268
  end
277
269
  alias l localize
278
270
 
@@ -281,10 +273,10 @@ module R18n
281
273
  @translation
282
274
  end
283
275
 
284
- # Return translation with special +name+.
276
+ # Return translation with special `name`.
285
277
  #
286
- # Translation can contain variable part. Just set is as <tt>%1</tt>,
287
- # <tt>%2</tt>, etc in translations file and set values in next +params+.
278
+ # Translation can contain variable part. Just set is as `%1`,
279
+ # `%2`, etc in translations file and set values in next `params`.
288
280
  def [](name, *params)
289
281
  @translation[name, *params]
290
282
  end
@@ -23,14 +23,14 @@ require 'bigdecimal'
23
23
  module R18n
24
24
  # Information about locale (language, country and other special variant
25
25
  # preferences). Locale was named by RFC 3066. For example, locale for French
26
- # speaking people in Canada will be +fr-CA+.
26
+ # speaking people in Canada will be `fr-CA`.
27
27
  #
28
- # Locale classes are placed in <tt>R18n::Locales</tt> module and storage
29
- # install <tt>locales/</tt> dir.
28
+ # Locale classes are placed in `R18n::Locales` module and storage
29
+ # install `locales/` dir.
30
30
  #
31
- # Each locale has +sublocales+ – often known languages for people from this
31
+ # Each locale has `sublocales` – often known languages for people from this
32
32
  # locale. For example, many Belorussians know Russian and English. If there
33
- # ist translation for Belorussian, it will be searched in Russian and next in
33
+ # is't translation for Belorussian, it will be searched in Russian and next in
34
34
  # English translations.
35
35
  #
36
36
  # == Usage
@@ -44,78 +44,97 @@ module R18n
44
44
  #
45
45
  # == Available data
46
46
  #
47
- # * +code+ – locale RFC 3066 code;
48
- # * +title+ – locale name on it language;
49
- # * +ltr?+ – true on left-to-right writing direction, false for Arabic and
47
+ # * `code` – locale RFC 3066 code;
48
+ # * `title` – locale name on it language;
49
+ # * `ltr?` – true on left-to-right writing direction, false for Arabic and
50
50
  # Hebrew);
51
- # * +sublocales+ – often known languages for people from this locale;
52
- # * +week_start+ – does week start from +:monday+ or +:sunday+.
51
+ # * `sublocales` – often known languages for people from this locale;
52
+ # * `week_start` – does week start from `:monday` or `:sunday`.
53
53
  #
54
54
  # You can see more available data about locale in samples in
55
- # <tt>locales/</tt> dir.
55
+ # `locales/` dir.
56
56
  class Locale
57
- LOCALES_DIR = File.join(__dir__, '..', '..', 'locales')
57
+ @loaded = {}
58
58
 
59
- @@loaded = {}
59
+ class << self
60
+ # Is `locale` constant defined?
61
+ def exists?(locale)
62
+ locale = sanitize_code locale
63
+ name = capitalize(locale)
64
+ return false unless name
60
65
 
61
- # Codes of all available locales.
62
- def self.available
63
- Dir.glob(File.join(LOCALES_DIR, '*.rb')).map do |i|
64
- File.basename(i, '.rb')
66
+ R18n::Locales.const_defined?(name)
65
67
  end
66
- end
67
68
 
68
- # Is +locale+ has info file.
69
- def self.exists?(locale)
70
- File.exist?(File.join(LOCALES_DIR, "#{locale}.rb"))
71
- end
69
+ def sanitize_code(code)
70
+ code.to_s.gsub(/[^-_a-zA-Z]/, '').tr('_', '-').downcase
71
+ end
72
72
 
73
- # Load locale by RFC 3066 +code+.
74
- def self.load(code)
75
- original = code.to_s.gsub(/[^-_a-zA-Z]/, '')
76
- code = original.tr('_', '-').downcase
77
-
78
- @@loaded[code] ||= begin
79
- if exists? code
80
- require File.join(LOCALES_DIR, "#{code}.rb")
81
- name = code.gsub(/\w+/, &:capitalize).delete('-')
82
- R18n::Locales.const_get(name).new
83
- else
84
- UnsupportedLocale.new(original)
73
+ def capitalize(code)
74
+ lang, region = code.gsub(/\..+/, '').split('-')
75
+ return unless lang
76
+
77
+ lang.capitalize!
78
+ return lang unless region
79
+
80
+ region.size > 2 ? region.capitalize! : region.upcase!
81
+ "#{lang}#{region}"
82
+ end
83
+
84
+ # Load locale by RFC 3066 `code`.
85
+ def load(code)
86
+ code = sanitize_code code
87
+ name = capitalize(code)
88
+ return unless name
89
+
90
+ @loaded[code] ||= begin
91
+ if exists?(code)
92
+ R18n::Locales.const_get(name).new
93
+ else
94
+ UnsupportedLocale.new(code)
95
+ end
85
96
  end
86
97
  end
87
- end
88
98
 
89
- # Set locale +properties+. Locale class will have methods for each propetry
90
- # name, which return propetry value:
91
- #
92
- # class R18n::Locales::En < R18n::Locale
93
- # set title: 'English',
94
- # code: 'en'
95
- # end
96
- #
97
- # locale = R18n::Locales::En.new
98
- # locale.title #=> "English"
99
- # locale.code #=> "en"
100
- def self.set(properties)
101
- properties.each_pair do |key, value|
102
- define_method(key) { value }
99
+ # Set locale `properties`. Locale class will have methods
100
+ # for each property name, which return property value:
101
+ #
102
+ # class R18n::Locales::En < R18n::Locale
103
+ # set title: 'English',
104
+ # code: 'en'
105
+ # end
106
+ #
107
+ # locale = R18n::Locales::En.new
108
+ # locale.title #=> "English"
109
+ # locale.code #=> "en"
110
+ def set(properties)
111
+ properties.each_pair do |key, value|
112
+ undef_method key if method_defined? key
113
+ define_method(key) { value }
114
+ end
103
115
  end
104
116
  end
105
117
 
106
- # Locale RFC 3066 code.
107
- def code
108
- name = self.class.name.split('::').last
109
- lang, culture = name.match(/([A-Z][a-z]+)([A-Z]\w+)?/).captures
110
- lang.downcase + (culture ? '-' + culture.upcase : '')
118
+ attr_reader :code, :language, :region, :downcased_code, :parent
119
+
120
+ def initialize
121
+ language, region =
122
+ self.class.name.split('::').last.split(/([A-Z][a-z]+)/)[1, 2]
123
+ @language = language.downcase.freeze
124
+ @region = region.upcase.freeze if region
125
+ @code = "#{@language}#{"-#{region}" if region}"
126
+ @downcased_code = @code.downcase.tr('-', '_').freeze
127
+
128
+ @parent = self.class.superclass.new
111
129
  end
112
130
 
113
- set sublocales: %w[en],
114
- week_start: :monday,
115
- time_am: 'AM',
116
- time_pm: 'PM',
117
- time_format: '_ %H:%M',
118
- full_format: '%e %B',
131
+ set sublocales: [],
132
+ week_start: :monday,
133
+ time_am: 'AM',
134
+ time_pm: 'PM',
135
+ time_format: '_ %R',
136
+ time_with_seconds_format: '_ %T',
137
+ full_format: '%-d %B',
119
138
  year_format: '_ %Y'
120
139
 
121
140
  def month_standalone
@@ -150,14 +169,14 @@ module R18n
150
169
  "Locale #{code} (#{title})"
151
170
  end
152
171
 
153
- # Convert +object+ to String. It support Integer, Float, Time, Date
154
- # and DateTime.
172
+ # Convert `object` to `String`. It support `Integer`, `Float`, `Time`,
173
+ # `Date` and `DateTime`.
155
174
  #
156
- # For time classes you can set +format+ in standard +strftime+ form,
157
- # <tt>:full</tt> (01 Jule, 2009), <tt>:human</tt> (yesterday),
158
- # <tt>:standard</tt> (07/01/09) or <tt>:month</tt> for standalone month
159
- # name. Default format is <tt>:standard</tt>.
160
- def localize(obj, format = nil, *params)
175
+ # For time classes you can set `format` in standard `strftime` form,
176
+ # `:full` ("01 Jule, 2009"), `:human` ("yesterday"),
177
+ # `:standard` ("07/01/09") or `:month` for standalone month
178
+ # name. Default format is `:standard`.
179
+ def localize(obj, format = nil, *args, **kwargs)
161
180
  case obj
162
181
  when Integer
163
182
  format_integer(obj)
@@ -166,7 +185,7 @@ module R18n
166
185
  when Time, DateTime, Date
167
186
  return strftime(obj, format) if format.is_a? String
168
187
  return month_standalone[obj.month - 1] if format == :month
169
- return obj.to_s if format == :human && !params.first.is_a?(I18n)
188
+ return obj.to_s if format == :human && !kwargs.key?(:i18n)
170
189
 
171
190
  type = obj.is_a?(Date) && !obj.is_a?(DateTime) ? 'date' : 'time'
172
191
  format ||= :standard
@@ -176,17 +195,22 @@ module R18n
176
195
  raise ArgumentError, "Unknown time formatter #{format}"
177
196
  end
178
197
 
179
- send format_method_name, obj, *params
198
+ send format_method_name, obj, *args, **kwargs
180
199
  else
181
- obj.to_s
200
+ format_method_name =
201
+ "format_#{Utils.underscore(obj.class.name).tr('/', '_')}_#{format}"
202
+
203
+ return obj.to_s unless respond_to? format_method_name
204
+
205
+ send format_method_name, obj, *args, **kwargs
182
206
  end
183
207
  end
184
208
 
185
- # Returns the integer in String form, according to the rules of the locale.
186
- # It will also put real typographic minus.
209
+ # Returns the integer in `String` form, according to the rules
210
+ # of the locale. It will also put real typographic minus.
187
211
  def format_integer(integer)
188
212
  str = integer.to_s
189
- str[0] = '−' if integer < 0 # Real typographic minus
213
+ str[0] = '−' if integer.negative? # Real typographic minus
190
214
  group = number_group
191
215
 
192
216
  str.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) do |match|
@@ -194,16 +218,16 @@ module R18n
194
218
  end
195
219
  end
196
220
 
197
- # Returns the float in String form, according to the rules of the locale.
221
+ # Returns the float in `String` form, according to the rules of the locale.
198
222
  # It will also put real typographic minus.
199
223
  def format_float(float)
200
224
  decimal = number_decimal
201
225
  format_integer(float.to_i) + decimal + float.to_s.split('.').last
202
226
  end
203
227
 
204
- # Same that <tt>Time.strftime</tt>, but translate months and week days
205
- # names. In +time+ you can use Time, DateTime or Date object. In +format+
206
- # you can use standard +strftime+ format.
228
+ # Same that `Time.strftime`, but translate months and week days
229
+ # names. In `time` you can use `Time`, `DateTime` or `Date` object.
230
+ # In `format` you can use standard `strftime` format.
207
231
  def strftime(time, format)
208
232
  translated = ''
209
233
  format.scan(/%[EO]?.|./o) do |c|
@@ -226,48 +250,50 @@ module R18n
226
250
  time.strftime(translated)
227
251
  end
228
252
 
229
- # Format +time+ and set +date+
230
- def format_time(date, time)
231
- strftime(time, time_format).sub('_', date.to_s)
253
+ # Format `time` and set `date`
254
+ def format_time(date, time, with_seconds: false, **_kwargs)
255
+ strftime(
256
+ time, with_seconds ? time_with_seconds_format : time_format
257
+ ).sub('_', date.to_s)
232
258
  end
233
259
 
234
- # Format +time+ in human usable form. For example 5 minutes ago or
235
- # yesterday”. In +now+ you can set base time, which be used to get relative
236
- # time. For special cases you can replace it in locales class.
237
- def format_time_human(time, i18n, now = Time.now, *_params)
260
+ # Format `time` in human usable form. For example "5 minutes ago" or
261
+ # "yesterday". In `now` you can set base time, which be used to get relative
262
+ # time. For special cases you can replace it in locale's class.
263
+ def format_time_human(time, i18n:, now: time.class.now, **_kwargs)
238
264
  diff = time - now
239
265
  minutes = time.is_a?(DateTime) ? diff * 24 * 60.0 : diff / 60.0
240
266
  diff = minutes.abs
241
267
  if (diff > 24 * 60) || (time.mday != now.mday && diff > 12 * 24)
242
- 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)
243
269
  elsif minutes > -1 && minutes < 1
244
270
  i18n.human_time.now
245
271
  elsif minutes >= 60
246
272
  i18n.human_time.after_hours((diff / 60.0).floor)
247
273
  elsif minutes <= -60
248
274
  i18n.human_time.hours_ago((diff / 60.0).floor)
249
- elsif minutes > 0
275
+ elsif minutes.positive?
250
276
  i18n.human_time.after_minutes(minutes.round)
251
277
  else
252
278
  i18n.human_time.minutes_ago(minutes.round.abs)
253
279
  end
254
280
  end
255
281
 
256
- # Format +time+ in compact form. For example, 12/31/09 12:59”.
257
- def format_time_standard(time, *_params)
258
- format_time(format_date_standard(time), time)
282
+ # Format `time` in compact form. For example, "12/31/09 12:59".
283
+ def format_time_standard(time, *args, **kwargs)
284
+ format_time(format_date_standard(time), time, *args, **kwargs)
259
285
  end
260
286
 
261
- # Format +time+ in most official form. For example, December 31st, 2009
262
- # 12:59”. For special cases you can replace it in locales class.
263
- def format_time_full(time, *_params)
264
- format_time(format_date_full(time), time)
287
+ # Format `time` in most official form. For example, "December 31st, 2009
288
+ # 12:59". For special cases you can replace it in locale's class.
289
+ def format_time_full(time, **kwargs)
290
+ format_time(format_date_full(time, **kwargs), time, **kwargs)
265
291
  end
266
292
 
267
- # Format +date+ in human usable form. For example 5 days ago or
268
- # “yesterday”. In +now+ you can set base time, which be used to get relative
269
- # time. For special cases you can replace it in locales class.
270
- 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)
271
297
  days = (date - now).to_i
272
298
  case days
273
299
  when -6..-2
@@ -281,28 +307,28 @@ module R18n
281
307
  when 2..6
282
308
  i18n.human_time.after_days(days)
283
309
  else
284
- format_date_full(date, date.year != now.year)
310
+ format_date_full(date, year: date.year != now.year)
285
311
  end
286
312
  end
287
313
 
288
- # Format +date+ in compact form. For example, 12/31/09”.
314
+ # Format `date` in compact form. For example, "12/31/09".
289
315
  def format_date_standard(date, *_params)
290
316
  strftime(date, date_format)
291
317
  end
292
318
 
293
- # Format +date+ in most official form. For example, December 31st, 2009”.
294
- # For special cases you can replace it in locales class. If +year+ is false
319
+ # Format `date` in most official form. For example, "December 31st, 2009".
320
+ # For special cases you can replace it in locale's class. If `year` is false
295
321
  # date will be without year.
296
- def format_date_full(date, year = true, *_params)
322
+ def format_date_full(date, year: true, **_kwargs)
297
323
  format = full_format
298
324
  format = year_format.sub('_', format) if year
299
325
  strftime(date, format)
300
326
  end
301
327
 
302
- # Return pluralization type for +n+ items. This is simple form. For special
303
- # cases you can replace it in locales class.
304
- def pluralize(n)
305
- case n
328
+ # Return pluralization type for `number` of items. This is simple form.
329
+ # For special cases you can replace it in locale's class.
330
+ def pluralize(number)
331
+ case number
306
332
  when 0
307
333
  0
308
334
  when 1
@@ -314,5 +340,9 @@ module R18n
314
340
  end
315
341
 
316
342
  # Namespace for Locale sub-classes
317
- module Locales; end
343
+ module Locales
344
+ Dir.glob(File.join(__dir__, 'locales', '*.rb')) do |file|
345
+ autoload Locale.capitalize(File.basename(file, '.*')), file
346
+ end
347
+ end
318
348
  end