tater 3.0.5 → 3.0.6
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.
- checksums.yaml +4 -4
- data/{README.org → README.md} +71 -69
- data/lib/tater/utils.rb +14 -1
- data/lib/tater/version.rb +1 -1
- data/lib/tater.rb +33 -37
- metadata +9 -129
- data/test/fixtures/another.yml +0 -2
- data/test/fixtures/fixtures.yml +0 -102
- data/test/fixtures/messages/more.yml +0 -12
- data/test/fixtures/ruby.rb +0 -11
- data/test/tater_test.rb +0 -486
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18944b6a0817574ffa53d289a770dc0d014f7e6cc2a7288bcea2f759ed317aa4
|
4
|
+
data.tar.gz: 0716a6538423a4722bae9929a4fa7668eeace79517680c1e5c30a9e5a9635a28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0690c57c5f348ace3b92d3156efadb40de9256843b20bd8e0ea36be327f48649e70d7c0baf68abe337d0cd4883a9c6c4690c9f54b5017dd3298bc6fa8b4920eb'
|
7
|
+
data.tar.gz: 91b9b6d51ed90b46b27263d6e911bace16959ea21a8da3aa501bf684acac3fada973c3bf48aec3ef6c0cf67567f7dd5f4494ca5312f23573474c9f71272441ce
|
data/{README.org → README.md}
RENAMED
@@ -1,40 +1,40 @@
|
|
1
|
-
|
1
|
+
# Tater
|
2
2
|
|
3
|
-
[[https://badge.fury.io/rb/tater]
|
4
|
-
[[https://github.com/evanleck/tater/actions/workflows/main.yml]
|
3
|
+
[](https://badge.fury.io/rb/tater)
|
4
|
+
[](https://github.com/evanleck/tater/actions/workflows/main.yml)
|
5
5
|
|
6
6
|
Tater is an internationalization (i18n) and localization (l10n) library designed
|
7
|
-
for simplicity. It doesn't do everything that other libraries do, but that's
|
8
|
-
design.
|
7
|
+
for simplicity. It doesn't do everything that other libraries do, but that's
|
8
|
+
by design.
|
9
9
|
|
10
|
-
Under the hood, Tater uses a Hash to store the messages, the
|
11
|
-
lookups,
|
10
|
+
Under the hood, Tater uses a Hash to store the messages, the `dig` method
|
11
|
+
for lookups, `strftime` for date and time localizations, and `format` for
|
12
12
|
interpolation. That's probably 90% of what Tater does.
|
13
13
|
|
14
|
-
|
14
|
+
## Installation
|
15
15
|
|
16
|
-
Tater requires Ruby 2.
|
16
|
+
Tater requires Ruby 2.7 or higher. To install Tater, add this line to your
|
17
17
|
application's Gemfile (or gems.rb):
|
18
18
|
|
19
|
-
|
19
|
+
``` ruby
|
20
20
|
gem 'tater'
|
21
|
-
|
21
|
+
```
|
22
22
|
|
23
23
|
And then execute:
|
24
24
|
|
25
|
-
|
25
|
+
``` sh
|
26
26
|
bundle
|
27
|
-
|
27
|
+
```
|
28
28
|
|
29
29
|
Or install it yourself by running:
|
30
30
|
|
31
|
-
|
31
|
+
``` sh
|
32
32
|
gem install tater
|
33
|
-
|
33
|
+
```
|
34
34
|
|
35
|
-
|
35
|
+
## Usage
|
36
36
|
|
37
|
-
|
37
|
+
``` ruby
|
38
38
|
require 'tater'
|
39
39
|
|
40
40
|
messages = {
|
@@ -57,56 +57,56 @@ i18n.translate('some.key') # => 'This here string!'
|
|
57
57
|
|
58
58
|
# Interpolation:
|
59
59
|
i18n.translate('interpolated', you: 'world') # => 'Hello world!'
|
60
|
-
|
60
|
+
```
|
61
61
|
|
62
|
-
|
62
|
+
## Array localization
|
63
63
|
|
64
64
|
Given an array, Tater will do it's best to join the elements of the array into a
|
65
65
|
sentence based on how many elements there are.
|
66
66
|
|
67
|
-
|
67
|
+
``` example
|
68
68
|
en:
|
69
69
|
array:
|
70
70
|
last_word_connector: ", and "
|
71
71
|
two_words_connector: " and "
|
72
72
|
words_connector: ", "
|
73
|
-
|
73
|
+
```
|
74
74
|
|
75
|
-
|
75
|
+
``` ruby
|
76
76
|
i18n.localize(%w[tacos enchiladas burritos]) # => "tacos, enchiladas, and burritos"
|
77
|
-
|
77
|
+
```
|
78
78
|
|
79
|
-
|
79
|
+
## Numeric localization
|
80
80
|
|
81
|
-
Numeric localization (
|
81
|
+
Numeric localization (`Numeric`, `Integer`, `Float`, and `BigDecimal`) require
|
82
82
|
filling in a separator and delimiter. For example:
|
83
83
|
|
84
|
-
|
84
|
+
``` example
|
85
85
|
en:
|
86
86
|
numeric:
|
87
87
|
delimiter: ','
|
88
88
|
separator: '.'
|
89
|
-
|
89
|
+
```
|
90
90
|
|
91
91
|
With that, you can do things like this:
|
92
92
|
|
93
|
-
|
93
|
+
``` ruby
|
94
94
|
i18n.localize(1000.2) # => "1,000.20"
|
95
|
-
|
95
|
+
```
|
96
96
|
|
97
97
|
The separator and delimiter can also be passed in per-call:
|
98
98
|
|
99
|
-
|
99
|
+
``` ruby
|
100
100
|
i18n.localize(1000.2, delimiter: '_', separator: '+') # => "1_000+20"
|
101
|
-
|
101
|
+
```
|
102
102
|
|
103
|
-
|
103
|
+
## Date and time localization
|
104
104
|
|
105
|
-
Date and time localization (
|
106
|
-
all of the needed names and abbreviations for days and months. Here's the
|
105
|
+
Date and time localization (`Date`, `Time`, and `DateTime`) require filling
|
106
|
+
in all of the needed names and abbreviations for days and months. Here's the
|
107
107
|
example for French, which is used in the tests.
|
108
108
|
|
109
|
-
|
109
|
+
``` example
|
110
110
|
fr:
|
111
111
|
time:
|
112
112
|
am: 'am'
|
@@ -169,27 +169,27 @@ fr:
|
|
169
169
|
- oct.
|
170
170
|
- nov.
|
171
171
|
- déc.
|
172
|
-
|
172
|
+
```
|
173
173
|
|
174
|
-
The statically defined keys for dates are
|
175
|
-
and
|
176
|
-
you plan on using the
|
174
|
+
The statically defined keys for dates are `days`, `abbreviated_days`, `months`,
|
175
|
+
and `abbreviated_months`. Only `am` and `pm` are needed for times and only if
|
176
|
+
you plan on using the `%p` or `%P` format strings.
|
177
177
|
|
178
178
|
With all of that, you can do something like:
|
179
179
|
|
180
|
-
|
180
|
+
``` ruby
|
181
181
|
i18n.localize(Date.new(1970, 1, 1), format: '%A') # => 'jeudi'
|
182
182
|
|
183
183
|
# Or, using a key defined in "formats":
|
184
184
|
i18n.localize(Date.new(1970, 1, 1), format: 'day') # => 'jeudi'
|
185
|
-
|
185
|
+
```
|
186
186
|
|
187
|
-
|
187
|
+
## Cascading lookups
|
188
188
|
|
189
189
|
Lookups can be cascaded, i.e. pieces of the scope of the can be lopped off
|
190
190
|
incrementally.
|
191
191
|
|
192
|
-
|
192
|
+
``` ruby
|
193
193
|
messages = {
|
194
194
|
'en' => {
|
195
195
|
'login' => {
|
@@ -208,40 +208,40 @@ i18n.translate('login.special.title') # => 'Special Login'
|
|
208
208
|
i18n.translate('login.special.description') # => 'Tater lookup failed'
|
209
209
|
|
210
210
|
i18n.translate('login.special.description', cascade: true) # => 'Normal description.'
|
211
|
-
|
211
|
+
```
|
212
212
|
|
213
213
|
With cascade, the final key stays the same, but pieces of the scope get lopped
|
214
214
|
off. In this case, lookups will be tried in this order:
|
215
215
|
|
216
|
-
1.
|
217
|
-
2.
|
216
|
+
1. `login.special.description`
|
217
|
+
2. `login.description`
|
218
218
|
|
219
219
|
This can be useful when you want to override some messages but don't want to
|
220
220
|
have to copy all of the other, non-overwritten messages.
|
221
221
|
|
222
222
|
Cascading can also be enabled by default when initializing an instance of Tater.
|
223
223
|
|
224
|
-
|
224
|
+
``` ruby
|
225
225
|
Tater.new(cascade: true)
|
226
|
-
|
226
|
+
```
|
227
227
|
|
228
228
|
Cascading is off by default.
|
229
229
|
|
230
|
-
|
230
|
+
## Defaults
|
231
231
|
|
232
232
|
If you'd like to default to another value in case of a missed lookup, you can
|
233
|
-
provide the
|
233
|
+
provide the `:default` option to `#translate`.
|
234
234
|
|
235
|
-
|
235
|
+
``` ruby
|
236
236
|
Tater.new.translate('nope', default: 'Yep!') # => 'Yep!'
|
237
|
-
|
237
|
+
```
|
238
238
|
|
239
|
-
|
239
|
+
## Procs and messages in Ruby
|
240
240
|
|
241
241
|
Ruby files can be used to store messages in addition to YAML, so long as the
|
242
|
-
Ruby file returns a
|
242
|
+
Ruby file returns a `Hash` when evaluated.
|
243
243
|
|
244
|
-
|
244
|
+
``` ruby
|
245
245
|
{
|
246
246
|
'en' => {
|
247
247
|
ruby: proc do |key, options = {}|
|
@@ -249,15 +249,15 @@ Ruby file returns a =Hash= when evaluated.
|
|
249
249
|
end
|
250
250
|
}
|
251
251
|
}
|
252
|
-
|
252
|
+
```
|
253
253
|
|
254
|
-
|
254
|
+
## Multiple locales
|
255
255
|
|
256
256
|
If you would like to check multiple locales and pull the first matching one out,
|
257
|
-
you can pass the
|
257
|
+
you can pass the `:locales` option to initialization or the `translate` method
|
258
258
|
with an array of top-level locale keys.
|
259
259
|
|
260
|
-
|
260
|
+
``` ruby
|
261
261
|
messages = {
|
262
262
|
'en' => {
|
263
263
|
'title' => 'Login',
|
@@ -276,23 +276,25 @@ i18n.translate('description', locales: %w[fr en]) # => 'English description.'
|
|
276
276
|
i18n = Tater.new(messages: messages, locales: %w[fr en])
|
277
277
|
i18n.translate('title') # => 'la connexion'
|
278
278
|
i18n.translate('description') # => 'English description.'
|
279
|
-
|
279
|
+
```
|
280
280
|
|
281
281
|
Locales will be tried in order and whichever one matches first will be returned.
|
282
282
|
|
283
|
-
|
283
|
+
## Limitations
|
284
284
|
|
285
285
|
- It is not plug-able, it does what it does and that's it.
|
286
286
|
- It doesn't handle pluralization yet, though it may in the future.
|
287
287
|
|
288
|
-
|
288
|
+
## Why?
|
289
289
|
|
290
|
-
Because [
|
291
|
-
|
292
|
-
file that handles the basics of lookup
|
290
|
+
Because [Ruby I18n](https://github.com/ruby-i18n/i18n) is amazing and I wanted
|
291
|
+
to try to create a minimum viable implementation of the bits of I18n that I
|
292
|
+
use 90% of the time. Tater is a single file that handles the basics of lookup
|
293
|
+
and interpolation.
|
293
294
|
|
294
|
-
|
295
|
+
## Trivia
|
295
296
|
|
296
|
-
I was originally going to call this library "Translator" but with a
|
297
|
-
like I18n: "t8r".
|
298
|
-
of "tee-eight-arr" so I
|
297
|
+
I was originally going to call this library "Translator" but with a
|
298
|
+
[numeronym](https://en.wikipedia.org/wiki/Numeronym) like I18n: "t8r". I looked
|
299
|
+
at it for a while but I read it as "tater" instead of "tee-eight-arr" so I
|
300
|
+
figured I'd just name it Tater. Tater the translator.
|
data/lib/tater/utils.rb
CHANGED
@@ -52,7 +52,8 @@ class Tater
|
|
52
52
|
end.freeze
|
53
53
|
end
|
54
54
|
|
55
|
-
# Format values into a string
|
55
|
+
# Format values into a string, conditionally checking the string and options
|
56
|
+
# before interpolating.
|
56
57
|
#
|
57
58
|
# @param string [String]
|
58
59
|
# The target string to interpolate into.
|
@@ -64,6 +65,18 @@ class Tater
|
|
64
65
|
return string if options.empty?
|
65
66
|
return string unless interpolation_string?(string)
|
66
67
|
|
68
|
+
interpolate!(string, options)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Format values into a string unconditionally.
|
72
|
+
#
|
73
|
+
# @param string [String]
|
74
|
+
# The target string to interpolate into.
|
75
|
+
# @param options [Hash]
|
76
|
+
# The values to interpolate into the target string.
|
77
|
+
#
|
78
|
+
# @return [String]
|
79
|
+
def self.interpolate!(string, options)
|
67
80
|
format(string, options)
|
68
81
|
end
|
69
82
|
|
data/lib/tater/version.rb
CHANGED
data/lib/tater.rb
CHANGED
@@ -217,24 +217,9 @@ class Tater
|
|
217
217
|
#
|
218
218
|
# @return [Boolean]
|
219
219
|
def includes?(key, options = HASH)
|
220
|
-
if options.empty?
|
221
|
-
!lookup(key).nil?
|
222
|
-
else
|
223
|
-
message =
|
224
|
-
if options.key?(:locales)
|
225
|
-
options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
|
220
|
+
return !lookup(key).nil? if options.empty?
|
226
221
|
|
227
|
-
|
228
|
-
found = lookup(key, locale: accept, cascade: options[:cascade])
|
229
|
-
|
230
|
-
break found unless found.nil?
|
231
|
-
end
|
232
|
-
else
|
233
|
-
lookup(key, locale: options[:locale], cascade: options[:cascade])
|
234
|
-
end
|
235
|
-
|
236
|
-
!message.nil?
|
237
|
-
end
|
222
|
+
!lookup_with_options(key, options).nil?
|
238
223
|
end
|
239
224
|
|
240
225
|
# Translate a key path and optional interpolation arguments into a string.
|
@@ -264,33 +249,22 @@ class Tater
|
|
264
249
|
# defined key.
|
265
250
|
def translate(key, options = HASH)
|
266
251
|
if options.empty?
|
267
|
-
message = lookup(key)
|
268
|
-
|
269
|
-
if message.is_a?(Proc) # rubocop:disable Style/CaseLikeIf
|
270
|
-
message.call(key)
|
271
|
-
elsif message.is_a?(String)
|
252
|
+
case (message = lookup(key))
|
253
|
+
when String
|
272
254
|
message
|
255
|
+
when Proc
|
256
|
+
message.call(key)
|
273
257
|
else
|
274
258
|
"Tater lookup failed: #{ locale }.#{ key }"
|
275
259
|
end
|
276
260
|
else
|
277
|
-
message =
|
278
|
-
|
279
|
-
|
261
|
+
case (message = lookup_with_options(key, options))
|
262
|
+
when String
|
263
|
+
return message unless Utils.interpolation_string?(message)
|
280
264
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
break found unless found.nil?
|
285
|
-
end
|
286
|
-
else
|
287
|
-
lookup(key, locale: options[:locale], cascade: options[:cascade])
|
288
|
-
end
|
289
|
-
|
290
|
-
if message.is_a?(Proc) # rubocop:disable Style/CaseLikeIf
|
265
|
+
Utils.interpolate!(message, options.except(:cascade, :default, :locale, :locales))
|
266
|
+
when Proc
|
291
267
|
message.call(key, options.except(:cascade, :default, :locale, :locales))
|
292
|
-
elsif message.is_a?(String)
|
293
|
-
Utils.interpolate(message, options.except(:cascade, :default, :locale, :locales))
|
294
268
|
else
|
295
269
|
options[:default] || "Tater lookup failed: #{ options[:locale] || options[:locales] || locale }.#{ key }"
|
296
270
|
end
|
@@ -299,6 +273,28 @@ class Tater
|
|
299
273
|
|
300
274
|
private
|
301
275
|
|
276
|
+
# @param key [String]
|
277
|
+
# The period-separated key path to look within our messages for.
|
278
|
+
# @param options [Hash]
|
279
|
+
# Options, including values to interpolate to any found string.
|
280
|
+
#
|
281
|
+
# @return [String]
|
282
|
+
# The translated and interpreted string, if found, or any data at the
|
283
|
+
# defined key.
|
284
|
+
def lookup_with_options(key, options)
|
285
|
+
if (locs = options[:locales])
|
286
|
+
locs.append(@locale) if @locale && !locs.include?(@locale)
|
287
|
+
|
288
|
+
locs.find do |accept|
|
289
|
+
found = lookup(key, locale: accept, cascade: options[:cascade])
|
290
|
+
|
291
|
+
break found unless found.nil?
|
292
|
+
end
|
293
|
+
else
|
294
|
+
lookup(key, locale: options[:locale], cascade: options[:cascade])
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
302
298
|
# Localize an Array object.
|
303
299
|
#
|
304
300
|
# @param object [Array<String>]
|
metadata
CHANGED
@@ -1,127 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tater
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Lecklider
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: minitest
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rubocop
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rubocop-minitest
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rubocop-packaging
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rubocop-performance
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rubocop-rake
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
11
|
+
date: 2023-05-26 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
125
13
|
description: Minimal internationalization and localization library.
|
126
14
|
email:
|
127
15
|
- evan@lecklider.com
|
@@ -130,22 +18,19 @@ extensions: []
|
|
130
18
|
extra_rdoc_files: []
|
131
19
|
files:
|
132
20
|
- LICENSE.txt
|
133
|
-
- README.
|
21
|
+
- README.md
|
134
22
|
- lib/tater.rb
|
135
23
|
- lib/tater/aliases.rb
|
136
24
|
- lib/tater/hash.rb
|
137
25
|
- lib/tater/utils.rb
|
138
26
|
- lib/tater/version.rb
|
139
|
-
- test/fixtures/another.yml
|
140
|
-
- test/fixtures/fixtures.yml
|
141
|
-
- test/fixtures/messages/more.yml
|
142
|
-
- test/fixtures/ruby.rb
|
143
|
-
- test/tater_test.rb
|
144
27
|
homepage: https://github.com/evanleck/tater
|
145
28
|
licenses:
|
146
29
|
- MIT
|
147
30
|
metadata:
|
148
31
|
bug_tracker_uri: https://github.com/evanleck/tater/issues
|
32
|
+
changelog_uri: https://github.com/evanleck/tater/blob/main/CHANGELOG.md
|
33
|
+
homepage_uri: https://github.com/evanleck/tater
|
149
34
|
rubygems_mfa_required: 'true'
|
150
35
|
source_code_uri: https://github.com/evanleck/tater
|
151
36
|
post_install_message:
|
@@ -156,20 +41,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
156
41
|
requirements:
|
157
42
|
- - ">="
|
158
43
|
- !ruby/object:Gem::Version
|
159
|
-
version: 2.
|
44
|
+
version: 2.7.0
|
160
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
46
|
requirements:
|
162
47
|
- - ">="
|
163
48
|
- !ruby/object:Gem::Version
|
164
49
|
version: '2.0'
|
165
50
|
requirements: []
|
166
|
-
rubygems_version: 3.
|
51
|
+
rubygems_version: 3.4.10
|
167
52
|
signing_key:
|
168
53
|
specification_version: 4
|
169
54
|
summary: Minimal internationalization and localization library.
|
170
|
-
test_files:
|
171
|
-
- test/fixtures/another.yml
|
172
|
-
- test/fixtures/fixtures.yml
|
173
|
-
- test/fixtures/messages/more.yml
|
174
|
-
- test/fixtures/ruby.rb
|
175
|
-
- test/tater_test.rb
|
55
|
+
test_files: []
|
data/test/fixtures/another.yml
DELETED
data/test/fixtures/fixtures.yml
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
en:
|
2
|
-
english: 'Found in English'
|
3
|
-
|
4
|
-
title: 'This is a title'
|
5
|
-
interpolated: 'This has some %{fancy} text'
|
6
|
-
double: '%{first} %{second}!'
|
7
|
-
|
8
|
-
deep:
|
9
|
-
key: 'This key is deeper'
|
10
|
-
|
11
|
-
array:
|
12
|
-
last_word_connector: ", and "
|
13
|
-
two_words_connector: " and "
|
14
|
-
words_connector: ", "
|
15
|
-
|
16
|
-
date:
|
17
|
-
formats:
|
18
|
-
default: '%Y/%-m/%-d'
|
19
|
-
ohmy: '%-m something %-d oh my %Y'
|
20
|
-
|
21
|
-
time:
|
22
|
-
formats:
|
23
|
-
default: '%Y/%-m/%-d/%H/%M/%S'
|
24
|
-
|
25
|
-
datetime:
|
26
|
-
formats:
|
27
|
-
default: '%Y/%-m/%-d/%H/%M/%S'
|
28
|
-
|
29
|
-
numeric:
|
30
|
-
delimiter: 'TURKEYS'
|
31
|
-
separator: 'NAH'
|
32
|
-
|
33
|
-
cascade:
|
34
|
-
tacos: 'Delicious'
|
35
|
-
|
36
|
-
another:
|
37
|
-
crazy: 'Whoaa'
|
38
|
-
|
39
|
-
fr:
|
40
|
-
french: 'Found in French'
|
41
|
-
|
42
|
-
time:
|
43
|
-
am: 'am'
|
44
|
-
pm: 'pm'
|
45
|
-
|
46
|
-
formats:
|
47
|
-
default: '%I%P'
|
48
|
-
loud: '%I%p'
|
49
|
-
|
50
|
-
date:
|
51
|
-
formats:
|
52
|
-
abbreviated_day: '%a'
|
53
|
-
day: '%A'
|
54
|
-
|
55
|
-
abbreviated_month: '%b'
|
56
|
-
month: '%B'
|
57
|
-
|
58
|
-
days:
|
59
|
-
- dimanche
|
60
|
-
- lundi
|
61
|
-
- mardi
|
62
|
-
- mercredi
|
63
|
-
- jeudi
|
64
|
-
- vendredi
|
65
|
-
- samedi
|
66
|
-
|
67
|
-
abbreviated_days:
|
68
|
-
- dim
|
69
|
-
- lun
|
70
|
-
- mar
|
71
|
-
- mer
|
72
|
-
- jeu
|
73
|
-
- ven
|
74
|
-
- sam
|
75
|
-
|
76
|
-
months:
|
77
|
-
- janvier
|
78
|
-
- février
|
79
|
-
- mars
|
80
|
-
- avril
|
81
|
-
- mai
|
82
|
-
- juin
|
83
|
-
- juillet
|
84
|
-
- août
|
85
|
-
- septembre
|
86
|
-
- octobre
|
87
|
-
- novembre
|
88
|
-
- décembre
|
89
|
-
|
90
|
-
abbreviated_months:
|
91
|
-
- jan.
|
92
|
-
- fév.
|
93
|
-
- mar.
|
94
|
-
- avr.
|
95
|
-
- mai
|
96
|
-
- juin
|
97
|
-
- juil.
|
98
|
-
- août
|
99
|
-
- sept.
|
100
|
-
- oct.
|
101
|
-
- nov.
|
102
|
-
- déc.
|
data/test/fixtures/ruby.rb
DELETED
data/test/tater_test.rb
DELETED
@@ -1,486 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
3
|
-
|
4
|
-
require 'date'
|
5
|
-
require 'minitest/autorun'
|
6
|
-
require 'tater'
|
7
|
-
|
8
|
-
describe Tater do
|
9
|
-
describe Tater::Utils do
|
10
|
-
describe '#deep_merge' do
|
11
|
-
it 'deeply merges two hashes, returning a new one' do
|
12
|
-
first = { 'one' => 'one', 'two' => { 'three' => 'three' } }
|
13
|
-
second = { 'two' => { 'four' => 'four' } }
|
14
|
-
|
15
|
-
third = Tater::Utils.deep_merge(first, second)
|
16
|
-
|
17
|
-
assert_equal({ 'one' => 'one', 'two' => { 'three' => 'three', 'four' => 'four' } }, third)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '#deep_stringify_keys' do
|
22
|
-
it 'converts all keys into strings, recursively' do
|
23
|
-
start = { en: { login: { title: 'Hello!' } } }
|
24
|
-
finish = Tater::Utils.deep_stringify_keys(start)
|
25
|
-
|
26
|
-
assert_equal({ 'en' => { 'login' => { 'title' => 'Hello!' } } }, finish)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#deep_freeze' do
|
31
|
-
it 'freezes the keys and values, recursively' do
|
32
|
-
start = Tater::Utils.deep_stringify_keys({ en: { login: { title: 'Hello!' } } })
|
33
|
-
finish = Tater::Utils.deep_freeze(start)
|
34
|
-
|
35
|
-
assert finish.frozen?
|
36
|
-
assert finish.keys.all?(&:frozen?)
|
37
|
-
assert finish.values.all?(&:frozen?)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe '#interpolate' do
|
42
|
-
it 'interpolates a string and hash' do
|
43
|
-
assert_equal 'this thing', Tater::Utils.interpolate('this %{what}', what: 'thing')
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'raises a KeyError when an argument is missing (but options are passed)' do
|
47
|
-
assert_raises(KeyError) do
|
48
|
-
Tater::Utils.interpolate('this %{what}', nope: 'thing')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'returns the string unchanged when options are empty (does not raise a KeyError)' do
|
53
|
-
assert_equal 'this %{what}', Tater::Utils.interpolate('this %{what}')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe '#string_from_numeric' do
|
58
|
-
it 'converts numerics to decimal-ish strings' do
|
59
|
-
assert_equal '1', Tater::Utils.string_from_numeric(1)
|
60
|
-
assert_equal '1.0', Tater::Utils.string_from_numeric(1.0)
|
61
|
-
assert_equal '1.0', Tater::Utils.string_from_numeric(BigDecimal('1'))
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe '#interpolation_string?' do
|
66
|
-
def is?(arg)
|
67
|
-
Tater::Utils.interpolation_string?(arg)
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'checks whether a string contains interpolation placeholders' do
|
71
|
-
assert is?('Hey %{there}!')
|
72
|
-
assert is?('Hey %<there>s!')
|
73
|
-
refute is?('Nah, this is fine')
|
74
|
-
refute is?("<b>HTML shouldn't count")
|
75
|
-
refute is?("A single % shouldn't count")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe '#available?' do
|
81
|
-
let :i18n do
|
82
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'tells you if the locale is available' do
|
86
|
-
assert i18n.available?('en')
|
87
|
-
refute i18n.available?('romulan')
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe '#load' do
|
92
|
-
it 'loads from a path on initialization' do
|
93
|
-
i18n = Tater.new(path: File.expand_path('test/fixtures'))
|
94
|
-
|
95
|
-
assert_instance_of(Hash, i18n.messages)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'loads from a path after initialization' do
|
99
|
-
i18n = Tater.new
|
100
|
-
i18n.load(path: File.expand_path('test/fixtures'))
|
101
|
-
|
102
|
-
assert_instance_of(Hash, i18n.messages)
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'loads from a hash of messages on initialization' do
|
106
|
-
i18n = Tater.new(messages: { 'hey' => 'Oh hi' })
|
107
|
-
|
108
|
-
assert_instance_of(Hash, i18n.messages)
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'loads from a hash of messages after initialization' do
|
112
|
-
i18n = Tater.new
|
113
|
-
i18n.load(messages: { 'hey' => 'Oh hi' })
|
114
|
-
|
115
|
-
assert_instance_of(Hash, i18n.messages)
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'freezes messages after loading' do
|
119
|
-
i18n = Tater.new(messages: { 'hey' => 'Oh hi' })
|
120
|
-
|
121
|
-
assert i18n.messages.frozen?
|
122
|
-
assert i18n.messages.keys.all?(&:frozen?)
|
123
|
-
assert i18n.messages.values.all?(&:frozen?)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
describe '#available' do
|
128
|
-
let :i18n do
|
129
|
-
Tater.new(path: File.expand_path('test/fixtures'))
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'returns an array with the available locales (i.e. the top-level keys in our messages hash)' do
|
133
|
-
assert_equal %w[en delimiter_only separator_only fr].sort, i18n.available.sort
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'updates the available list when new messages are loaded' do
|
137
|
-
i18n.load(messages: { 'added' => { 'hey' => 'yeah' } })
|
138
|
-
|
139
|
-
assert_equal %w[en delimiter_only separator_only fr added].sort, i18n.available.sort
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe '#lookup' do
|
144
|
-
let :i18n do
|
145
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'returns keys from messages' do
|
149
|
-
assert_equal 'This is a title', i18n.lookup('title')
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'does no interpolation' do
|
153
|
-
assert_equal 'This has some %{fancy} text', i18n.lookup('interpolated')
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'returns nil for missing lookups' do
|
157
|
-
assert_nil i18n.lookup('nope')
|
158
|
-
end
|
159
|
-
|
160
|
-
it 'returns arbitrary data at keys' do
|
161
|
-
assert_equal({ 'key' => 'This key is deeper' }, i18n.lookup('deep'))
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'cascades' do
|
165
|
-
assert_equal 'Delicious', i18n.lookup('cascade.nope.tacos', cascade: true)
|
166
|
-
assert_equal 'Whoaa', i18n.lookup('cascade.another.nope.crazy', cascade: true)
|
167
|
-
assert_nil i18n.lookup('cascade.another.nope.crazy', cascade: false)
|
168
|
-
assert_nil i18n.lookup('cascade.nahhhhhh')
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
describe '#translate' do
|
173
|
-
let :i18n do
|
174
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
175
|
-
end
|
176
|
-
|
177
|
-
it 'translates strings' do
|
178
|
-
assert_equal 'This is a title', i18n.translate('title')
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'translates nested strings' do
|
182
|
-
assert_equal 'This key is deeper', i18n.translate('deep.key')
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'does not return a hash for nested keys' do
|
186
|
-
assert_equal 'Tater lookup failed: en.deep', i18n.translate('deep')
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'interpolates additional variables' do
|
190
|
-
assert_equal 'This has some fancy text', i18n.translate('interpolated', fancy: 'fancy')
|
191
|
-
assert_equal 'Double down!', i18n.translate('double', first: 'Double', second: 'down')
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'works with multiple files' do
|
195
|
-
assert_equal 'This is from a different file', i18n.translate('another')
|
196
|
-
assert_equal "Oh there's more!", i18n.translate('more')
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'returns a message for failed translations' do
|
200
|
-
assert_equal 'Tater lookup failed: en.nope', i18n.translate('nope')
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'cascades lookups' do
|
204
|
-
assert_equal 'Tater lookup failed: en.cascade.another.nope.crazy', i18n.translate('cascade.another.nope.crazy', cascade: false)
|
205
|
-
assert_equal 'Tater lookup failed: en.cascade.nope.tacos', i18n.translate('cascade.nope.tacos')
|
206
|
-
assert_equal 'Delicious', i18n.translate('cascade.nope.tacos', cascade: true)
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'defaults lookups' do
|
210
|
-
assert_equal 'Tater lookup failed: en.default.missing', i18n.translate('default.missing')
|
211
|
-
assert_equal 'Nope', i18n.translate('default.missing', default: 'Nope')
|
212
|
-
end
|
213
|
-
|
214
|
-
it 'does lookups across different locales' do
|
215
|
-
assert_equal 'Found in French', i18n.translate('french', locales: %w[fr en])
|
216
|
-
assert_equal 'Found in English', i18n.translate('english', locales: %w[fr en])
|
217
|
-
assert_equal 'Tater lookup failed: ["fr", "en"].neither', i18n.translate('neither', locales: %w[fr en])
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'finds Ruby files' do
|
221
|
-
assert_equal 'Hey ruby!', i18n.translate('ruby')
|
222
|
-
end
|
223
|
-
|
224
|
-
it 'does not interpolate messages returned by procs' do
|
225
|
-
assert_equal 'Hey %{options}!', i18n.translate('options', options: 'options')
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
describe '#localize' do
|
230
|
-
let :i18n do
|
231
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
232
|
-
end
|
233
|
-
|
234
|
-
let :fr do
|
235
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'fr')
|
236
|
-
end
|
237
|
-
|
238
|
-
it 'localizes arrays' do
|
239
|
-
assert_equal 'tacos and burritos', i18n.localize(%w[tacos burritos])
|
240
|
-
assert_equal 'tacos', i18n.localize(%w[tacos])
|
241
|
-
assert_equal 'tacos, enchiladas, and burritos', i18n.localize(%w[tacos enchiladas burritos])
|
242
|
-
|
243
|
-
assert_equal 'tacos + enchiladas ++ burritos', fr.localize(%w[tacos enchiladas burritos], words_connector: ' + ', last_word_connector: ' ++ ')
|
244
|
-
assert_equal 'tacostwoburritos', fr.localize(%w[tacos burritos], two_words_connector: 'two')
|
245
|
-
|
246
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
247
|
-
fr.localize(%w[tacos burritos])
|
248
|
-
end
|
249
|
-
|
250
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
251
|
-
fr.localize(%w[tacos burritos], last_word_connector: 'last', words_connector: 'words')
|
252
|
-
end
|
253
|
-
|
254
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
255
|
-
fr.localize(%w[tacos burritos], last_word_connector: 'last')
|
256
|
-
end
|
257
|
-
|
258
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
259
|
-
fr.localize(%w[tacos burritos], words_connector: 'words')
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
it 'localizes Dates' do
|
264
|
-
assert_equal '1970/1/1', i18n.localize(Date.new(1970, 1, 1))
|
265
|
-
end
|
266
|
-
|
267
|
-
it 'localizes Times' do
|
268
|
-
assert_equal '1970/1/1/00/00/00', i18n.localize(Time.new(1970, 1, 1, 0, 0, 0))
|
269
|
-
end
|
270
|
-
|
271
|
-
it 'localizes DateTimes' do
|
272
|
-
assert_equal '1970/1/1/00/00/00', i18n.localize(DateTime.new(1970, 1, 1, 0, 0, 0))
|
273
|
-
end
|
274
|
-
|
275
|
-
it 'localizes Floats' do
|
276
|
-
assert_equal '10TURKEYS000NAH12', i18n.localize(10_000.12)
|
277
|
-
end
|
278
|
-
|
279
|
-
it 'localizes Integers' do
|
280
|
-
assert_equal '10TURKEYS000', i18n.localize(10_000)
|
281
|
-
end
|
282
|
-
|
283
|
-
it 'localizes BigDecimals' do
|
284
|
-
assert_equal '1NAH12', i18n.localize(BigDecimal('1.12'))
|
285
|
-
end
|
286
|
-
|
287
|
-
describe 'precision option' do
|
288
|
-
it 'defaults to 2' do
|
289
|
-
assert_equal '10NAH00', i18n.localize(BigDecimal('10'))
|
290
|
-
assert_equal '10NAH00', i18n.localize(10.0)
|
291
|
-
end
|
292
|
-
|
293
|
-
it 'defaults to zero for integers' do
|
294
|
-
assert_equal '10', i18n.localize(10)
|
295
|
-
end
|
296
|
-
|
297
|
-
it 'removes fractional pieces when the precision is 0' do
|
298
|
-
assert_equal '10', i18n.localize(BigDecimal('10.123456'), precision: 0)
|
299
|
-
assert_equal '10', i18n.localize(10.123456, precision: 0)
|
300
|
-
|
301
|
-
assert_equal '10', i18n.localize(BigDecimal('10.12'), precision: 0)
|
302
|
-
assert_equal '10', i18n.localize(10.12, precision: 0)
|
303
|
-
end
|
304
|
-
|
305
|
-
it 'truncates long values to the desired precision' do
|
306
|
-
assert_equal '10NAH00', i18n.localize(BigDecimal('10.00234'))
|
307
|
-
assert_equal '10NAH00', i18n.localize(10.00234)
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
it 'allows overriding the delimiter and separator' do
|
312
|
-
assert_equal '10WOO000NAH12', i18n.localize(10_000.12, delimiter: 'WOO')
|
313
|
-
assert_equal '10TURKEYS000YA12', i18n.localize(10_000.12, separator: 'YA')
|
314
|
-
end
|
315
|
-
|
316
|
-
it 'accepts other formats' do
|
317
|
-
assert_equal '1 something 1 oh my 1970', i18n.localize(Date.new(1970, 1, 1), format: 'ohmy')
|
318
|
-
end
|
319
|
-
|
320
|
-
it 'uses the passed in format if the specified class and format are not present' do
|
321
|
-
assert_equal 'not there', i18n.localize(Date.new(1970, 1, 1), format: 'not there')
|
322
|
-
end
|
323
|
-
|
324
|
-
it 'raises a MissingLocalizationFormat if a delimiter is missing' do
|
325
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
326
|
-
i18n.localize(10, locale: 'separator_only')
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
it 'raises a MissingLocalizationFormat if a separator is missing' do
|
331
|
-
assert_raises(Tater::MissingLocalizationFormat) do
|
332
|
-
i18n.localize(10, locale: 'delimiter_only')
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
describe 'month, day, and AM/PM names' do
|
337
|
-
let :i18n do
|
338
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'fr')
|
339
|
-
end
|
340
|
-
|
341
|
-
it 'localizes day names' do
|
342
|
-
assert_equal 'jeudi', i18n.localize(Date.new(1970, 1, 1), format: 'day')
|
343
|
-
assert_equal 'vendredi', i18n.localize(Date.new(1970, 1, 2), format: 'day')
|
344
|
-
assert_equal 'samedi', i18n.localize(Date.new(1970, 1, 3), format: 'day')
|
345
|
-
assert_equal 'dimanche', i18n.localize(Date.new(1970, 1, 4), format: 'day')
|
346
|
-
assert_equal 'lundi', i18n.localize(Date.new(1970, 1, 5), format: 'day')
|
347
|
-
assert_equal 'mardi', i18n.localize(Date.new(1970, 1, 6), format: 'day')
|
348
|
-
assert_equal 'mercredi', i18n.localize(Date.new(1970, 1, 7), format: 'day')
|
349
|
-
end
|
350
|
-
|
351
|
-
it 'localizes abbreviated day names' do
|
352
|
-
assert_equal 'jeu', i18n.localize(Date.new(1970, 1, 1), format: 'abbreviated_day')
|
353
|
-
assert_equal 'ven', i18n.localize(Date.new(1970, 1, 2), format: 'abbreviated_day')
|
354
|
-
assert_equal 'sam', i18n.localize(Date.new(1970, 1, 3), format: 'abbreviated_day')
|
355
|
-
assert_equal 'dim', i18n.localize(Date.new(1970, 1, 4), format: 'abbreviated_day')
|
356
|
-
assert_equal 'lun', i18n.localize(Date.new(1970, 1, 5), format: 'abbreviated_day')
|
357
|
-
assert_equal 'mar', i18n.localize(Date.new(1970, 1, 6), format: 'abbreviated_day')
|
358
|
-
assert_equal 'mer', i18n.localize(Date.new(1970, 1, 7), format: 'abbreviated_day')
|
359
|
-
end
|
360
|
-
|
361
|
-
it 'localizes months' do
|
362
|
-
assert_equal 'janvier', i18n.localize(Date.new(1970, 1, 1), format: 'month')
|
363
|
-
assert_equal 'février', i18n.localize(Date.new(1970, 2, 1), format: 'month')
|
364
|
-
assert_equal 'mars', i18n.localize(Date.new(1970, 3, 1), format: 'month')
|
365
|
-
assert_equal 'avril', i18n.localize(Date.new(1970, 4, 1), format: 'month')
|
366
|
-
assert_equal 'mai', i18n.localize(Date.new(1970, 5, 1), format: 'month')
|
367
|
-
assert_equal 'juin', i18n.localize(Date.new(1970, 6, 1), format: 'month')
|
368
|
-
assert_equal 'juillet', i18n.localize(Date.new(1970, 7, 1), format: 'month')
|
369
|
-
assert_equal 'août', i18n.localize(Date.new(1970, 8, 1), format: 'month')
|
370
|
-
assert_equal 'septembre', i18n.localize(Date.new(1970, 9, 1), format: 'month')
|
371
|
-
assert_equal 'octobre', i18n.localize(Date.new(1970, 10, 1), format: 'month')
|
372
|
-
assert_equal 'novembre', i18n.localize(Date.new(1970, 11, 1), format: 'month')
|
373
|
-
assert_equal 'décembre', i18n.localize(Date.new(1970, 12, 1), format: 'month')
|
374
|
-
end
|
375
|
-
|
376
|
-
it 'localizes abbreviated months' do
|
377
|
-
assert_equal 'jan.', i18n.localize(Date.new(1970, 1, 1), format: 'abbreviated_month')
|
378
|
-
assert_equal 'fév.', i18n.localize(Date.new(1970, 2, 1), format: 'abbreviated_month')
|
379
|
-
assert_equal 'mar.', i18n.localize(Date.new(1970, 3, 1), format: 'abbreviated_month')
|
380
|
-
assert_equal 'avr.', i18n.localize(Date.new(1970, 4, 1), format: 'abbreviated_month')
|
381
|
-
assert_equal 'mai', i18n.localize(Date.new(1970, 5, 1), format: 'abbreviated_month')
|
382
|
-
assert_equal 'juin', i18n.localize(Date.new(1970, 6, 1), format: 'abbreviated_month')
|
383
|
-
assert_equal 'juil.', i18n.localize(Date.new(1970, 7, 1), format: 'abbreviated_month')
|
384
|
-
assert_equal 'août', i18n.localize(Date.new(1970, 8, 1), format: 'abbreviated_month')
|
385
|
-
assert_equal 'sept.', i18n.localize(Date.new(1970, 9, 1), format: 'abbreviated_month')
|
386
|
-
assert_equal 'oct.', i18n.localize(Date.new(1970, 10, 1), format: 'abbreviated_month')
|
387
|
-
assert_equal 'nov.', i18n.localize(Date.new(1970, 11, 1), format: 'abbreviated_month')
|
388
|
-
assert_equal 'déc.', i18n.localize(Date.new(1970, 12, 1), format: 'abbreviated_month')
|
389
|
-
end
|
390
|
-
|
391
|
-
it 'localizes AM/PM' do
|
392
|
-
assert_equal '05pm', i18n.localize(Time.new(1970, 1, 1, 17))
|
393
|
-
assert_equal '05PM', i18n.localize(Time.new(1970, 1, 1, 17), format: 'loud')
|
394
|
-
assert_equal '05am', i18n.localize(Time.new(1970, 1, 1, 5))
|
395
|
-
assert_equal '05AM', i18n.localize(Time.new(1970, 1, 1, 5), format: 'loud')
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
describe '#locale=' do
|
401
|
-
let :i18n do
|
402
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
403
|
-
end
|
404
|
-
|
405
|
-
it 'overrides the locale when available' do
|
406
|
-
i18n.locale = 'delimiter_only'
|
407
|
-
assert_equal 'delimiter_only', i18n.locale
|
408
|
-
end
|
409
|
-
|
410
|
-
it 'does not override the locale when not available' do
|
411
|
-
i18n.locale = 'nopeskies'
|
412
|
-
assert_equal 'en', i18n.locale
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
describe '#cascades?' do
|
417
|
-
let :default do
|
418
|
-
Tater.new
|
419
|
-
end
|
420
|
-
|
421
|
-
let :cascade do
|
422
|
-
Tater.new(cascade: true)
|
423
|
-
end
|
424
|
-
|
425
|
-
it 'returns false by default' do
|
426
|
-
refute default.cascades?
|
427
|
-
end
|
428
|
-
|
429
|
-
it 'returns true when passed during initialization' do
|
430
|
-
assert cascade.cascades?
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
describe '#includes?' do
|
435
|
-
let :i18n do
|
436
|
-
Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
437
|
-
end
|
438
|
-
|
439
|
-
it 'tells you if you have a translation' do
|
440
|
-
assert i18n.includes?('deep')
|
441
|
-
assert i18n.includes?('deep.key')
|
442
|
-
refute i18n.includes?('deep.nope')
|
443
|
-
refute i18n.includes?('nope')
|
444
|
-
end
|
445
|
-
|
446
|
-
it 'allows overriding the locale' do
|
447
|
-
assert i18n.includes?('french', locale: 'fr')
|
448
|
-
assert i18n.includes?('french', locales: %w[en fr])
|
449
|
-
refute i18n.includes?('french', locales: %w[en])
|
450
|
-
refute i18n.includes?('french')
|
451
|
-
end
|
452
|
-
|
453
|
-
it 'allows cascading' do
|
454
|
-
assert i18n.includes?('cascade.nope.tacos', cascade: true)
|
455
|
-
refute i18n.includes?('cascade.nope.tacos', cascade: false)
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
describe '#inspect' do
|
460
|
-
it 'returns a stringified version of the object' do
|
461
|
-
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
462
|
-
assert_equal %(#<Tater:#{ obj.object_id } @cascade=#{ obj.cascades? } @locale="#{ obj.locale }" @available=#{ obj.available }>), obj.inspect
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
describe '#freeze' do
|
467
|
-
it 'can be frozen' do
|
468
|
-
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
469
|
-
obj.freeze
|
470
|
-
|
471
|
-
assert obj.includes?('deep')
|
472
|
-
assert_equal 'This key is deeper', obj.translate('deep.key')
|
473
|
-
assert_equal %w[delimiter_only en fr separator_only], obj.available
|
474
|
-
|
475
|
-
assert obj.frozen?
|
476
|
-
end
|
477
|
-
|
478
|
-
it 'throws an error if modified after being frozen' do
|
479
|
-
obj = Tater.new(path: File.expand_path('test/fixtures'), locale: 'en')
|
480
|
-
obj.freeze
|
481
|
-
|
482
|
-
assert_raises(FrozenError) { obj.locale = 'en' }
|
483
|
-
assert_raises(FrozenError) { obj.load(messages: { more: 'Messages' }) }
|
484
|
-
end
|
485
|
-
end
|
486
|
-
end
|