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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08a174e27ecb08955ba15997b8b71995aff8a77cbf82bb08514ba5dcd6e404dd'
4
- data.tar.gz: c9f78478bc5f1b7a48b038add4f3c05230dc6083ccada5d1de00974b1dbd08f8
3
+ metadata.gz: 18944b6a0817574ffa53d289a770dc0d014f7e6cc2a7288bcea2f759ed317aa4
4
+ data.tar.gz: 0716a6538423a4722bae9929a4fa7668eeace79517680c1e5c30a9e5a9635a28
5
5
  SHA512:
6
- metadata.gz: 27b9c2ad757cb5a51bf0ce3a792a92912bc3d8a2f9e82c761621396417480b6c82088222cd9e2c552d2309381d8feb0399466cade27b75fd2fb29d6eab2552b0
7
- data.tar.gz: baf324c10b838927b0aedc2ac5babafed538acbdaeb7a4f7a5e6aebf8731d53037ec2dad4870d459a90056c9b9c2442d59ed6efaf5d94dbb74341f6a6ae3c14d
6
+ metadata.gz: '0690c57c5f348ace3b92d3156efadb40de9256843b20bd8e0ea36be327f48649e70d7c0baf68abe337d0cd4883a9c6c4690c9f54b5017dd3298bc6fa8b4920eb'
7
+ data.tar.gz: 91b9b6d51ed90b46b27263d6e911bace16959ea21a8da3aa501bf684acac3fada973c3bf48aec3ef6c0cf67567f7dd5f4494ca5312f23573474c9f71272441ce
@@ -1,40 +1,40 @@
1
- * Tater
1
+ # Tater
2
2
 
3
- [[https://badge.fury.io/rb/tater][https://badge.fury.io/rb/tater.svg]]
4
- [[https://github.com/evanleck/tater/actions/workflows/main.yml][https://github.com/evanleck/tater/actions/workflows/main.yml/badge.svg]]
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 by
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 =dig= method for
11
- lookups, =strftime= for date and time localizations, and =format= for
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
- ** Installation
14
+ ## Installation
15
15
 
16
- Tater requires Ruby 2.5 or higher. To install Tater, add this line to your
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
- #+begin_src ruby
19
+ ``` ruby
20
20
  gem 'tater'
21
- #+end_src
21
+ ```
22
22
 
23
23
  And then execute:
24
24
 
25
- #+begin_src sh
25
+ ``` sh
26
26
  bundle
27
- #+end_src
27
+ ```
28
28
 
29
29
  Or install it yourself by running:
30
30
 
31
- #+begin_src sh
31
+ ``` sh
32
32
  gem install tater
33
- #+end_src
33
+ ```
34
34
 
35
- ** Usage
35
+ ## Usage
36
36
 
37
- #+begin_src ruby
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
- #+end_src
60
+ ```
61
61
 
62
- ** Array localization
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
- #+begin_example
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
- #+end_example
73
+ ```
74
74
 
75
- #+begin_src ruby
75
+ ``` ruby
76
76
  i18n.localize(%w[tacos enchiladas burritos]) # => "tacos, enchiladas, and burritos"
77
- #+end_src
77
+ ```
78
78
 
79
- ** Numeric localization
79
+ ## Numeric localization
80
80
 
81
- Numeric localization (=Numeric=, =Integer=, =Float=, and =BigDecimal=) require
81
+ Numeric localization (`Numeric`, `Integer`, `Float`, and `BigDecimal`) require
82
82
  filling in a separator and delimiter. For example:
83
83
 
84
- #+begin_example
84
+ ``` example
85
85
  en:
86
86
  numeric:
87
87
  delimiter: ','
88
88
  separator: '.'
89
- #+end_example
89
+ ```
90
90
 
91
91
  With that, you can do things like this:
92
92
 
93
- #+begin_src ruby
93
+ ``` ruby
94
94
  i18n.localize(1000.2) # => "1,000.20"
95
- #+end_src
95
+ ```
96
96
 
97
97
  The separator and delimiter can also be passed in per-call:
98
98
 
99
- #+begin_src ruby
99
+ ``` ruby
100
100
  i18n.localize(1000.2, delimiter: '_', separator: '+') # => "1_000+20"
101
- #+end_src
101
+ ```
102
102
 
103
- ** Date and time localization
103
+ ## Date and time localization
104
104
 
105
- Date and time localization (=Date=, =Time=, and =DateTime=) require filling in
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
- #+begin_example
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
- #+end_example
172
+ ```
173
173
 
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.
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
- #+begin_src ruby
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
- #+end_src
185
+ ```
186
186
 
187
- ** Cascading lookups
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
- #+begin_src ruby
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
- #+end_src
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. =login.special.description=
217
- 2. =login.description=
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
- #+begin_src ruby
224
+ ``` ruby
225
225
  Tater.new(cascade: true)
226
- #+end_src
226
+ ```
227
227
 
228
228
  Cascading is off by default.
229
229
 
230
- ** Defaults
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 =:default= option to =#translate=.
233
+ provide the `:default` option to `#translate`.
234
234
 
235
- #+begin_src ruby
235
+ ``` ruby
236
236
  Tater.new.translate('nope', default: 'Yep!') # => 'Yep!'
237
- #+end_src
237
+ ```
238
238
 
239
- ** Procs and messages in Ruby
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 =Hash= when evaluated.
242
+ Ruby file returns a `Hash` when evaluated.
243
243
 
244
- #+begin_src ruby
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
- #+end_src
252
+ ```
253
253
 
254
- ** Multiple locales
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 =:locales= option to initialization or the =translate= method
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
- #+begin_src ruby
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
- #+end_src
279
+ ```
280
280
 
281
281
  Locales will be tried in order and whichever one matches first will be returned.
282
282
 
283
- ** Limitations
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
- ** Why?
288
+ ## Why?
289
289
 
290
- Because [[https://github.com/ruby-i18n/i18n][Ruby I18n]] is amazing and I wanted to try to create a minimum viable
291
- implementation of the bits of I18n that I use 90% of the time. Tater is a single
292
- file that handles the basics of lookup and interpolation.
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
- ** Trivia
295
+ ## Trivia
295
296
 
296
- I was originally going to call this library "Translator" but with a [[https://en.wikipedia.org/wiki/Numeronym][numeronym]]
297
- like I18n: "t8r". I looked at it for a while but I read it as "tater" instead
298
- of "tee-eight-arr" so I figured I'd just name it Tater. Tater the translator.
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 if appropriate.
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Tater
3
- VERSION = '3.0.5'
3
+ VERSION = '3.0.6'
4
4
  end
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
- options[:locales].find do |accept|
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
- if options.key?(:locales)
279
- options[:locales].append(@locale) if @locale && !options[:locales].include?(@locale)
261
+ case (message = lookup_with_options(key, options))
262
+ when String
263
+ return message unless Utils.interpolation_string?(message)
280
264
 
281
- options[:locales].find do |accept|
282
- found = lookup(key, locale: accept, cascade: options[:cascade])
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.5
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: 2022-02-26 00:00:00.000000000 Z
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.org
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.5.0
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.3.7
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: []
@@ -1,2 +0,0 @@
1
- en:
2
- another: 'This is from a different file'
@@ -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.
@@ -1,12 +0,0 @@
1
- en:
2
- more: "Oh there's more!"
3
-
4
- delimiter_only:
5
- numeric:
6
- formats:
7
- delimiter: 'Just me'
8
-
9
- separator_only:
10
- numeric:
11
- formats:
12
- separator: 'Just me'
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
- {
3
- en: {
4
- ruby: proc do |key, _options = {}|
5
- "Hey #{ key }!"
6
- end,
7
- options: proc do |_key, _options = {}|
8
- 'Hey %{options}!'
9
- end
10
- }
11
- }
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