tater 3.0.5 → 3.0.6
Sign up to get free protection for your applications and to get access to all the features.
- 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.svg)](https://badge.fury.io/rb/tater)
|
4
|
+
[![](https://github.com/evanleck/tater/actions/workflows/main.yml/badge.svg)](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
|