tater 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{README.md → README.org} +66 -84
- data/lib/tater.rb +11 -6
- data/test/tater_test.rb +23 -5
- metadata +45 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb7166fcdf9b548f8837e4411bfe5d83712f202e1de27c299fa0e5694b50bc2b
|
4
|
+
data.tar.gz: ab3f53e56fa8538f39ce59ba9f2e42496e5bea116a880fc26e23ad2091e5e93c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ceb821ff34a086711feb0df9e21a7aa4c7891a3e84fc52a9cf62d5972462c6ab0a5f81eef2ab820ef95df7211dda82e2a56299a1ced017e468c50358b6e537db
|
7
|
+
data.tar.gz: 2ad3f4c078bb63e39b5650254f92cb3859c39c73f4eaf2dfae18320e60712bf2c632e42c38770c39a456ee62f13fbf6262e1fef527846c17c53d7ebd859fa6de
|
data/{README.md → README.org}
RENAMED
@@ -1,42 +1,39 @@
|
|
1
|
-
|
1
|
+
* Tater
|
2
2
|
|
3
|
-
[
|
4
|
-
[![Build Status](https://travis-ci.com/evanleck/tater.svg?branch=main)](https://travis-ci.com/evanleck/tater)
|
3
|
+
[[https://badge.fury.io/rb/tater][https://badge.fury.io/rb/tater.svg]]
|
5
4
|
|
6
5
|
Tater is an internationalization (i18n) and localization (l10n) library designed
|
7
6
|
for simplicity. It doesn't do everything that other libraries do, but that's by
|
8
7
|
design.
|
9
8
|
|
10
|
-
Under the hood, Tater uses a Hash to store the messages, the
|
11
|
-
lookups,
|
9
|
+
Under the hood, Tater uses a Hash to store the messages, the =dig= method for
|
10
|
+
lookups, =strftime= for date and time localizations, and =format= for
|
12
11
|
interpolation. That's probably 90% of what Tater does.
|
13
12
|
|
14
|
-
|
15
|
-
## Installation
|
13
|
+
** Installation
|
16
14
|
|
17
15
|
Tater requires Ruby 2.5 or higher. To install Tater, add this line to your
|
18
16
|
application's Gemfile (or gems.rb):
|
19
17
|
|
20
|
-
|
18
|
+
#+begin_src ruby
|
21
19
|
gem 'tater'
|
22
|
-
|
20
|
+
#+end_src
|
23
21
|
|
24
22
|
And then execute:
|
25
23
|
|
26
|
-
|
24
|
+
#+begin_src sh
|
27
25
|
bundle
|
28
|
-
|
26
|
+
#+end_src
|
29
27
|
|
30
28
|
Or install it yourself by running:
|
31
29
|
|
32
|
-
|
30
|
+
#+begin_src sh
|
33
31
|
gem install tater
|
34
|
-
|
35
|
-
|
32
|
+
#+end_src
|
36
33
|
|
37
|
-
|
34
|
+
** Usage
|
38
35
|
|
39
|
-
|
36
|
+
#+begin_src ruby
|
40
37
|
require 'tater'
|
41
38
|
|
42
39
|
messages = {
|
@@ -59,59 +56,56 @@ i18n.translate('some.key') # => 'This here string!'
|
|
59
56
|
|
60
57
|
# Interpolation:
|
61
58
|
i18n.translate('interpolated', you: 'world') # => 'Hello world!'
|
62
|
-
|
59
|
+
#+end_src
|
63
60
|
|
64
|
-
|
65
|
-
## Array localization
|
61
|
+
** Array localization
|
66
62
|
|
67
63
|
Given an array, Tater will do it's best to join the elements of the array into a
|
68
64
|
sentence based on how many elements there are.
|
69
65
|
|
70
|
-
|
66
|
+
#+begin_example
|
71
67
|
en:
|
72
68
|
array:
|
73
69
|
last_word_connector: ", and "
|
74
70
|
two_words_connector: " and "
|
75
71
|
words_connector: ", "
|
76
|
-
|
72
|
+
#+end_example
|
77
73
|
|
78
|
-
|
74
|
+
#+begin_src ruby
|
79
75
|
i18n.localize(%w[tacos enchiladas burritos]) # => "tacos, enchiladas, and burritos"
|
80
|
-
|
81
|
-
|
76
|
+
#+end_src
|
82
77
|
|
83
|
-
|
78
|
+
** Numeric localization
|
84
79
|
|
85
|
-
Numeric localization (
|
80
|
+
Numeric localization (=Numeric=, =Integer=, =Float=, and =BigDecimal=) require
|
86
81
|
filling in a separator and delimiter. For example:
|
87
82
|
|
88
|
-
|
83
|
+
#+begin_example
|
89
84
|
en:
|
90
85
|
numeric:
|
91
86
|
delimiter: ','
|
92
87
|
separator: '.'
|
93
|
-
|
88
|
+
#+end_example
|
94
89
|
|
95
90
|
With that, you can do things like this:
|
96
91
|
|
97
|
-
|
92
|
+
#+begin_src ruby
|
98
93
|
i18n.localize(1000.2) # => "1,000.20"
|
99
|
-
|
94
|
+
#+end_src
|
100
95
|
|
101
96
|
The separator and delimiter can also be passed in per-call:
|
102
97
|
|
103
|
-
|
98
|
+
#+begin_src ruby
|
104
99
|
i18n.localize(1000.2, delimiter: '_', separator: '+') # => "1_000+20"
|
105
|
-
|
100
|
+
#+end_src
|
106
101
|
|
102
|
+
** Date and time localization
|
107
103
|
|
108
|
-
|
109
|
-
|
110
|
-
Date and time localization (`Date`, `Time`, and `DateTime`) require filling in
|
104
|
+
Date and time localization (=Date=, =Time=, and =DateTime=) require filling in
|
111
105
|
all of the needed names and abbreviations for days and months. Here's the
|
112
106
|
example for French, which is used in the tests.
|
113
107
|
|
114
|
-
|
108
|
+
#+begin_example
|
115
109
|
fr:
|
116
110
|
time:
|
117
111
|
am: 'am'
|
@@ -174,28 +168,27 @@ fr:
|
|
174
168
|
- oct.
|
175
169
|
- nov.
|
176
170
|
- déc.
|
177
|
-
|
171
|
+
#+end_example
|
178
172
|
|
179
|
-
The statically defined keys for dates are
|
180
|
-
and
|
181
|
-
you plan on using the
|
173
|
+
The statically defined keys for dates are =days=, =abbreviated_days=, =months=,
|
174
|
+
and =abbreviated_months=. Only =am= and =pm= are needed for times and only if
|
175
|
+
you plan on using the =%p= or =%P= format strings.
|
182
176
|
|
183
177
|
With all of that, you can do something like:
|
184
178
|
|
185
|
-
|
179
|
+
#+begin_src ruby
|
186
180
|
i18n.localize(Date.new(1970, 1, 1), format: '%A') # => 'jeudi'
|
187
181
|
|
188
182
|
# Or, using a key defined in "formats":
|
189
183
|
i18n.localize(Date.new(1970, 1, 1), format: 'day') # => 'jeudi'
|
190
|
-
|
191
|
-
|
184
|
+
#+end_src
|
192
185
|
|
193
|
-
|
186
|
+
** Cascading lookups
|
194
187
|
|
195
|
-
Lookups can be cascaded, i.e.
|
188
|
+
Lookups can be cascaded, i.e. pieces of the scope of the can be lopped off
|
196
189
|
incrementally.
|
197
190
|
|
198
|
-
|
191
|
+
#+begin_src ruby
|
199
192
|
messages = {
|
200
193
|
'en' => {
|
201
194
|
'login' => {
|
@@ -214,42 +207,40 @@ i18n.translate('login.special.title') # => 'Special Login'
|
|
214
207
|
i18n.translate('login.special.description') # => 'Tater lookup failed'
|
215
208
|
|
216
209
|
i18n.translate('login.special.description', cascade: true) # => 'Normal description.'
|
217
|
-
|
210
|
+
#+end_src
|
218
211
|
|
219
212
|
With cascade, the final key stays the same, but pieces of the scope get lopped
|
220
213
|
off. In this case, lookups will be tried in this order:
|
221
214
|
|
222
|
-
1.
|
223
|
-
2.
|
215
|
+
1. ='login.special.description'=
|
216
|
+
2. ='login.description'=
|
224
217
|
|
225
218
|
This can be useful when you want to override some messages but don't want to
|
226
219
|
have to copy all of the other, non-overwritten messages.
|
227
220
|
|
228
221
|
Cascading can also be enabled by default when initializing an instance of Tater.
|
229
222
|
|
230
|
-
|
223
|
+
#+begin_src ruby
|
231
224
|
Tater.new(cascade: true)
|
232
|
-
|
225
|
+
#+end_src
|
233
226
|
|
234
227
|
Cascading is off by default.
|
235
228
|
|
236
|
-
|
237
|
-
## Defaults
|
229
|
+
** Defaults
|
238
230
|
|
239
231
|
If you'd like to default to another value in case of a missed lookup, you can
|
240
|
-
provide the
|
232
|
+
provide the =:default= option to =#translate=.
|
241
233
|
|
242
|
-
|
234
|
+
#+begin_src ruby
|
243
235
|
Tater.new.translate('nope', default: 'Yep!') # => 'Yep!'
|
244
|
-
|
236
|
+
#+end_src
|
245
237
|
|
246
|
-
|
247
|
-
## Procs and messages in Ruby
|
238
|
+
** Procs and messages in Ruby
|
248
239
|
|
249
240
|
Ruby files can be used to store messages in addition to YAML, so long as the
|
250
|
-
Ruby file returns a
|
241
|
+
Ruby file returns a =Hash= when evalled.
|
251
242
|
|
252
|
-
|
243
|
+
#+begin_src ruby
|
253
244
|
{
|
254
245
|
'en' => {
|
255
246
|
ruby: proc do |key, options = {}|
|
@@ -257,16 +248,15 @@ Ruby file returns a `Hash` when evalled.
|
|
257
248
|
end
|
258
249
|
}
|
259
250
|
}
|
260
|
-
|
261
|
-
|
251
|
+
#+end_src
|
262
252
|
|
263
|
-
|
253
|
+
** Multiple locales
|
264
254
|
|
265
255
|
If you would like to check multiple locales and pull the first matching one out,
|
266
|
-
you can pass the
|
256
|
+
you can pass the =:locales= option to initialization or the =translate= method
|
267
257
|
with an array of top-level locale keys.
|
268
258
|
|
269
|
-
|
259
|
+
#+begin_src ruby
|
270
260
|
messages = {
|
271
261
|
'en' => {
|
272
262
|
'title' => 'Login',
|
@@ -285,31 +275,23 @@ i18n.translate('description', locales: %w[fr en]) # => 'English description.'
|
|
285
275
|
i18n = Tater.new(messages: messages, locales: %w[fr en])
|
286
276
|
i18n.translate('title') # => 'la connexion'
|
287
277
|
i18n.translate('description') # => 'English description.'
|
288
|
-
|
278
|
+
#+end_src
|
289
279
|
|
290
280
|
Locales will be tried in order and whichever one matches first will be returned.
|
291
281
|
|
282
|
+
** Limitations
|
292
283
|
|
293
|
-
|
294
|
-
|
295
|
-
- It is not "pluggable", it does what it does and that's it.
|
284
|
+
- It is not pluggable, it does what it does and that's it.
|
296
285
|
- It doesn't handle pluralization yet, though it may in the future.
|
297
|
-
- It doesn't cache anything, that's up to you.
|
298
|
-
|
299
|
-
|
300
|
-
## Why?
|
301
|
-
|
302
|
-
Because [Ruby I18n][rubyi18n] is amazing and I wanted to try to create a minimum
|
303
|
-
viable implementation of the bits of I18n that I use 90% of the time. Tater is a
|
304
|
-
single file that handles the basics of lookup and interpolation.
|
305
286
|
|
287
|
+
** Why?
|
306
288
|
|
307
|
-
|
289
|
+
Because [[https://github.com/ruby-i18n/i18n][Ruby I18n]] is amazing and I wanted to try to create a minimum viable
|
290
|
+
implementation of the bits of I18n that I use 90% of the time. Tater is a single
|
291
|
+
file that handles the basics of lookup and interpolation.
|
308
292
|
|
309
|
-
|
310
|
-
[numeronym][numeronym] like I18n: "t8r". I looked at it for a while but I read
|
311
|
-
it as "tater" instead of "tee-eight-arr" so I figured I'd just name it Tater.
|
312
|
-
Tater the translator.
|
293
|
+
** Trivia
|
313
294
|
|
314
|
-
[
|
315
|
-
|
295
|
+
I was orininally going to call this library "Translator" but with a [[https://en.wikipedia.org/wiki/Numeronym][numeronym]]
|
296
|
+
like I18n: "t8r". I looked at it for a while but I read it as "tater" instead
|
297
|
+
of "tee-eight-arr" so I figured I'd just name it Tater. Tater the translator.
|
data/lib/tater.rb
CHANGED
@@ -6,6 +6,7 @@ require 'yaml'
|
|
6
6
|
# designed for speed and simplicity.
|
7
7
|
class Tater
|
8
8
|
class MissingLocalizationFormat < ArgumentError; end
|
9
|
+
|
9
10
|
class UnLocalizableObject < ArgumentError; end
|
10
11
|
|
11
12
|
module Utils # :nodoc:
|
@@ -214,16 +215,20 @@ class Tater
|
|
214
215
|
raise(MissingLocalizationFormat, "Numeric localization delimiter ('numeric.delimiter') missing or not passed as option :delimiter") unless delimiter
|
215
216
|
raise(MissingLocalizationFormat, "Numeric localization separator ('numeric.separator') missing or not passed as option :separator") unless separator
|
216
217
|
|
217
|
-
#
|
218
|
-
integer
|
219
|
-
integer.
|
220
|
-
|
218
|
+
# Break the number up into integer and fraction parts.
|
219
|
+
integer = Utils.string_from_numeric(object)
|
220
|
+
integer, fraction = integer.split('.') unless object.is_a?(Integer)
|
221
|
+
|
222
|
+
if integer.length > 3
|
223
|
+
integer.gsub!(DELIMITING_REGEX) do |number|
|
224
|
+
"#{ number }#{ delimiter }"
|
225
|
+
end
|
221
226
|
end
|
222
227
|
|
223
|
-
if precision.zero?
|
228
|
+
if precision.zero? || fraction.nil?
|
224
229
|
integer
|
225
230
|
else
|
226
|
-
|
231
|
+
"#{ integer }#{ separator }#{ fraction.ljust(precision, '0').slice(0, precision) }"
|
227
232
|
end
|
228
233
|
when Date, Time, DateTime
|
229
234
|
format = lookup("#{ object.class.to_s.downcase }.formats.#{ options[:format] || DEFAULT }", locale: options[:locale]) || options[:format] || DEFAULT
|
data/test/tater_test.rb
CHANGED
@@ -118,7 +118,7 @@ describe Tater do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
it 'updates the available list when new messages are loaded' do
|
121
|
-
i18n.load(messages: { 'added' => { 'hey' => 'yeah' }})
|
121
|
+
i18n.load(messages: { 'added' => { 'hey' => 'yeah' } })
|
122
122
|
|
123
123
|
assert_equal %w[en delimiter_only separator_only fr added].sort, i18n.available.sort
|
124
124
|
end
|
@@ -265,10 +265,28 @@ describe Tater do
|
|
265
265
|
assert_equal '1NAH12', i18n.localize(BigDecimal('1.12'))
|
266
266
|
end
|
267
267
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
268
|
+
describe 'precision option' do
|
269
|
+
it 'defaults to 2' do
|
270
|
+
assert_equal '10NAH00', i18n.localize(BigDecimal('10'))
|
271
|
+
assert_equal '10NAH00', i18n.localize(10.0)
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'defaults to zero for integers' do
|
275
|
+
assert_equal '10', i18n.localize(10)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'removes fractional pieces when the precision is 0' do
|
279
|
+
assert_equal '10', i18n.localize(BigDecimal('10.123456'), precision: 0)
|
280
|
+
assert_equal '10', i18n.localize(10.123456, precision: 0)
|
281
|
+
|
282
|
+
assert_equal '10', i18n.localize(BigDecimal('10.12'), precision: 0)
|
283
|
+
assert_equal '10', i18n.localize(10.12, precision: 0)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'truncates long values to the desired precision' do
|
287
|
+
assert_equal '10NAH00', i18n.localize(BigDecimal('10.00234'))
|
288
|
+
assert_equal '10NAH00', i18n.localize(10.00234)
|
289
|
+
end
|
272
290
|
end
|
273
291
|
|
274
292
|
it 'allows overriding the delimiter and separator' do
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tater
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Lecklider
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-21 00:00:00.000000000 Z
|
12
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'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: minitest
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
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'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rubocop-performance
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - ">="
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rake
|
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'
|
69
111
|
description: Minimal internationalization and localization library.
|
70
112
|
email:
|
71
113
|
- evan@lecklider.com
|
@@ -74,7 +116,7 @@ extensions: []
|
|
74
116
|
extra_rdoc_files: []
|
75
117
|
files:
|
76
118
|
- LICENSE.txt
|
77
|
-
- README.
|
119
|
+
- README.org
|
78
120
|
- lib/tater.rb
|
79
121
|
- test/fixtures/another.yml
|
80
122
|
- test/fixtures/fixtures.yml
|