roda-i18n 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,217 +1,211 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'r18n-core'
2
4
 
3
- #
4
5
  class Roda
5
-
6
- #
7
6
  module RodaPlugins
8
-
9
7
  # The i18n plugin allows you to easily add internationalisation (i18n) and
10
8
  # localisation support to your Roda app, by adding the following:
11
- #
9
+ #
12
10
  # plugin :i18n
13
- #
14
- # By default the default locale is set to <tt>'en'</tt> and the translations directory
11
+ #
12
+ # By default the default locale is set to <tt>'en'</tt> and the translations directory
15
13
  # is set to <tt>'i18n'</tt> in the rooot of your app.
16
- #
14
+ #
17
15
  # Both <tt>:locale</tt> and <tt>:translations</tt> can be overridden during configuration:
18
- #
16
+ #
19
17
  # plugin :i18n, :locale => ['de'], :translations => ['absolute/path/2/i18n']
20
- #
21
- # Please note!
18
+ #
19
+ # Please note!
22
20
  # 1) You must set +opts[:root]+ in your app if you don't define the +:translations+ path.
23
- #
21
+ #
24
22
  # 2) When overriding <tt>:translations</tt> the path given <b>must be absolute</b>.
25
- #
23
+ #
26
24
  # The path supports 'wildcards', ie: path/**/i18n so you can load translations from multiple
27
25
  # combined apps each with their own <tt>i18n</tt> folder with translations.
28
- #
29
- # Note! when loading translations from multiple sources and the same translation key is used
30
- # in both files, the first loaded file takes precedence, ie: <tt>./i18n/en.yml</tt> takes
26
+ #
27
+ # Note! when loading translations from multiple sources and the same translation key is used
28
+ # in both files, the first loaded file takes precedence, ie: <tt>./i18n/en.yml</tt> takes
31
29
  # precedence over <tt>./apps/app1/i18n/en.yml</tt>
32
- #
30
+ #
33
31
  # == USAGE
34
- #
32
+ #
35
33
  # The i18n plugin depends upon simple YAML based translations files:
36
- #
34
+ #
37
35
  # # app/i18n/en.yml
38
- #
36
+ #
39
37
  # user:
40
38
  # edit: Edit user
41
39
  # name: User name is %1
42
40
  # count: !!pl
43
41
  # 1: There is 1 user
44
42
  # n: There are %1 users
45
- #
46
- #
43
+ #
44
+ #
47
45
  # and the <tt>:t</tt> instance method to output the translations:
48
- #
49
- #
46
+ #
47
+ #
50
48
  # t.user.edit #=> "Edit user"
51
49
  # t.user.name('John') #=> "User name is John"
52
50
  # t.user.count(5) #=> "There are 5 users"
53
- #
51
+ #
54
52
  # t.does.not.exist | 'default' #=> "default"
55
- #
56
- #
53
+ #
54
+ #
57
55
  # the <tt>:l</tt> instance method provides built-in localisations support:
58
- #
56
+ #
59
57
  # l Time.now #=> "03/01/2010 18:54"
60
58
  # l Time.now, :human #=> "now"
61
59
  # l Time.now, :full #=> "3rd of January, 2010 18:54"
62
- #
60
+ #
63
61
  # Both the +:t+ and +:l+ methods are available in the route and template (erb) scopes. ie:
64
- #
62
+ #
65
63
  # route do |r|
66
64
  # r.root do
67
65
  # t.welcome.message
68
66
  # end
69
67
  # end
70
- #
68
+ #
71
69
  # # app/views/layout.erb
72
70
  # <snip...>
73
71
  # <h1><%= t.welcome.message %></h1>
74
72
  # <snip...>
75
- #
76
- #
77
- #
73
+ #
74
+ #
75
+ #
78
76
  # Visit [R18n](https://github.com/ai/r18n/tree/master/r18n-core) for more information.
79
- #
80
- #
77
+ #
78
+ #
81
79
  # The i18n plugin also makes it easy to handle locales:
82
- #
83
- #
80
+ #
81
+ #
84
82
  # === <tt>:locale</tt> RequestMethod
85
- #
83
+ #
86
84
  # This request method makes it to handle translations based upon the :locale prefix on a URL,
87
85
  # ie: <tt>blog.com/de/posts</tt>, just use the following code:
88
- #
86
+ #
89
87
  # route do |r|
90
- #
88
+ #
91
89
  # r.locale do # or r.i18n_locale
92
- # r.is 'posts' do
90
+ # r.is 'posts' do
93
91
  # t.posts.header
94
92
  # end
95
93
  # end
96
- #
94
+ #
97
95
  # end
98
- #
99
- #
96
+ #
97
+ #
100
98
  # === <tt>:i18n_set_locale_from</tt> RequestMethod
101
- #
99
+ #
102
100
  # Obtains the locale from either ENV, HTTP (browser), Params or Session values
103
- #
104
- #
101
+ #
102
+ #
105
103
  # Naturally we can allow browsers to override the default locale within routes, like this:
106
- #
104
+ #
107
105
  # route do |r|
108
106
  # i18n_set_locale_from(:http) #=> set to the browser's default locale (en-US)
109
107
  # r.get '' do
110
108
  # t.hello #=> 'Howdy, I speak American English'
111
109
  # end
112
110
  # end
113
- #
111
+ #
114
112
  # The def
115
- #
116
- #
113
+ #
114
+ #
117
115
  # route do |r|
118
116
  # i18n_set_locale('de')
119
117
  # r.get 'in-german' do
120
118
  # t.hello #=> 'Guten tag, ich spreche deutsch'
121
119
  # end
122
120
  # end
123
- #
124
- #
125
- #
126
- #
121
+ #
122
+ #
123
+ #
124
+ #
127
125
  module RodaI18n
128
-
129
126
  # default options
130
127
  OPTS = {
131
128
  # set the default locale
132
- locale: 'en',
129
+ locale: 'en',
133
130
  # set the default fallback locale
134
- default_locale: 'en',
131
+ default_locale: 'en',
135
132
  # set the default translations.
136
- translations: nil
133
+ translations: nil
137
134
  }.freeze
138
-
139
-
135
+
140
136
  def self.configure(app, opts = OPTS)
141
- if app.opts[:i18n]
142
- opts = app.opts[:i18n][:orig_opts].merge(opts)
143
- else
144
- opts = OPTS.merge(opts)
145
- end
146
-
137
+ opts = if app.opts[:i18n]
138
+ app.opts[:i18n][:orig_opts].merge(opts)
139
+ else
140
+ OPTS.merge(opts)
141
+ end
142
+
147
143
  app.opts[:i18n] = opts.dup
148
144
  app.opts[:i18n][:orig_opts] = opts
149
145
  opts = app.opts[:i18n]
150
-
146
+
151
147
  # set the translations path to defaults if nil
152
148
  opts[:translations] = File.expand_path('i18n', app.opts[:root]) if opts[:translations].nil?
153
149
  ::R18n.default_places = opts[:translations]
154
-
155
- # default_locale is either 'en' or the set value, so reset :default_locale if
150
+
151
+ # default_locale is either 'en' or the set value, so reset :default_locale if
156
152
  # it is somehow nil or an empty string ' '
157
- if opts[:default_locale].nil? || opts[:default_locale] =~ /^\s*$/
158
- opts[:default_locale] = 'en'
159
- end
153
+ opts[:default_locale] = 'en' if opts[:default_locale].nil? || opts[:default_locale] =~ /^\s*$/
160
154
  ::R18n::I18n.default = opts[:default_locale]
161
-
162
- ::R18n.clear_cache! if ENV['RACK_ENV'] != 'production'
163
- i18n = R18n::I18n.new(
164
- opts[:locale],
155
+ # :nocov:
156
+ ::R18n.clear_cache! if ENV['RACK_ENV'] != 'production' # nocov for simple one-liner
157
+ # :nocov:
158
+ i18n = R18n::I18n.new(
159
+ opts[:locale],
165
160
  ::R18n.default_places,
166
- off_filters: :untranslated,
167
- on_filters: :untranslated_html
161
+ off_filters: :untranslated,
162
+ on_filters: :untranslated_html
168
163
  )
169
164
  ::R18n.set(i18n)
170
165
  end
171
-
166
+
172
167
  # methods used within Roda's route block
173
- #
168
+ #
174
169
  module RequestMethods
175
-
176
170
  # Obtains the locale from either ENV, HTTP (browser), Params or Session
177
171
  # values.
178
- #
172
+ #
179
173
  # route do |r|
180
174
  # # A): set from URL params ie: GET /posts?locale=de
181
175
  # r.i18n_set_locale_from(:params)
182
- #
176
+ #
183
177
  # /url?locale=de
184
178
  # <%= t.one %> #=> Ein
185
179
  # /url?locale=es
186
180
  # <%= t.one %> #=> Uno
187
- #
181
+ #
188
182
  # # B): set from session[:locale] (if present)
189
183
  # r.i18n_set_locale_from(:session)
190
- #
184
+ #
191
185
  # session[:locale] = 'de'
192
186
  # <%= t.one %> #=> Ein
193
187
  # session[:locale] = 'es'
194
188
  # <%= t.one %> #=> Uno
195
- #
189
+ #
196
190
  # # C): set from the browser's HTTP request locale
197
191
  # r.i18n_set_locale_from(:http)
198
- #
192
+ #
199
193
  # HTTP_ACCEPT_LANGUAGE = 'sv-se;q=1,es;q=0.8,en;q=0.6'
200
194
  # <%= t.one %> #=> Ett
201
- #
195
+ #
202
196
  # # D): set from the server ENV['LANG'] variable
203
197
  # r.i18n_set_locale_from(:ENV)
204
- #
198
+ #
205
199
  # ENV['LANG'] = 'en_US.UTF8'
206
200
  # <%= t.one %> #=> One
207
201
  # ENV['LANG'] = 'es'
208
202
  # <%= t.one %> #=> Uno
209
- #
210
- # r.is 'posts' do
203
+ #
204
+ # r.is 'posts' do
211
205
  # t.posts.header # use translations
212
206
  # end
213
207
  # end
214
- #
208
+ #
215
209
  def i18n_set_locale_from(type)
216
210
  case type.to_sym
217
211
  when :http
@@ -219,7 +213,10 @@ class Roda
219
213
  when :session
220
214
  loc = session[:locale] if session[:locale]
221
215
  when :params
216
+ # :nocov:
217
+ # loc will be nil, and caught after the `case` statement
222
218
  loc = scope.request.params['locale'] if scope.request.params['locale']
219
+ # :nocov:
223
220
  when :ENV
224
221
  # ENV['LANG']="en_US.UTF-8"
225
222
  loc = ENV['LANG'].split('.').first if ENV['LANG']
@@ -228,99 +225,102 @@ class Roda
228
225
  end
229
226
  # sanity check: set to default locale if not set above
230
227
  loc = ::R18n::I18n.default.to_s if loc.nil?
231
-
228
+
232
229
  i18n = ::R18n::I18n.new(
233
- loc,
230
+ loc,
234
231
  ::R18n.default_places,
235
- off_filters: :untranslated,
236
- on_filters: :untranslated_html
232
+ off_filters: :untranslated,
233
+ on_filters: :untranslated_html
237
234
  )
238
235
  ::R18n.set(i18n)
239
236
  end
240
-
237
+
241
238
  # Enables setting temporary :locale blocks within the routing block.
242
- #
239
+ #
243
240
  # route do |r|
244
- #
241
+ #
245
242
  # r.i18n_set_locale('de') do
246
243
  # # within this block the locale is DE (German)
247
244
  # end
248
- #
245
+ #
249
246
  # r.i18n_set_locale('es') do
250
247
  # # within this block the locale is ES (Spanish)
251
248
  # end
252
- #
249
+ #
253
250
  # end
254
- #
255
- def i18n_set_locale(locale, &blk)
256
- locale = ::R18n::I18n.default.to_s if locale.nil?
257
-
251
+ #
252
+ def i18n_set_locale(locale)
253
+ sanitized_locale = i18n_set_locale_with_fallback(locale)
254
+
258
255
  i18n = ::R18n::I18n.new(
259
- locale,
260
- ::R18n.default_places,
261
- off_filters: :untranslated,
262
- on_filters: :untranslated_html
256
+ sanitized_locale,
257
+ ::R18n.default_places,
258
+ off_filters: :untranslated,
259
+ on_filters: :untranslated_html
263
260
  )
264
261
  ::R18n.set(i18n)
265
262
  yield if block_given?
266
- # return # NB!! needed to enable routes below to work
267
263
  end
268
-
264
+
269
265
  # Match only paths that contain one available locale from the ::R18n.available_locales
270
266
  # list, otherwise skip it.
271
267
  #
272
268
  # This custom matcher allows us to have other routes below the r.locale .. declaration
273
269
  def _match_available_locales_only
274
- lambda do
275
- locale = remaining_path.split("/").reject(&:empty?).first.to_s
276
- if ::R18n.available_locales.map(&:code).map(&:downcase).include?(locale.downcase)
277
- @captures.push(locale)
278
- @remaining_path = remaining_path.sub("/#{locale}", "")
279
- end
280
- end
270
+ lambda do
271
+ locale = remaining_path.split('/').reject(&:empty?).first.to_s
272
+ if ::R18n.available_locales.map { |locale| locale.code.downcase }.include?(locale.downcase)
273
+ @captures.push(locale)
274
+ @remaining_path = remaining_path.sub("/#{locale}", '')
275
+ end
276
+ end
281
277
  end
282
-
278
+
283
279
  # Sets the locale based upon <tt>:locale</tt> prefixed routes
284
- #
280
+ #
285
281
  # route do |r|
286
282
  # r.locale do
287
283
  # # all routes are prefixed with '/:locale'
288
284
  # # ie: GET /de/posts => will use DE translations
289
285
  # # ie: GET /es/posts => will use ES translations
290
- # r.is 'posts' do
286
+ # r.is 'posts' do
291
287
  # t.posts.header # use translations or locales
292
288
  # end
293
289
  # end
294
290
  # r.get(:about) { erb(:about) }
295
291
  # end
296
292
  #
297
- def locale(opts = {}, &blk)
293
+ def locale(opts = {})
298
294
  on(_match_available_locales_only, opts) do |l|
299
295
  loc = l || Roda.opts[:locale]
300
296
  ::R18n.set(loc)
301
- yield loc if block_given?
297
+ # :nocov:
298
+ yield loc if block_given? # nothing will happen without a block
302
299
  return # NB!! needed to enable routes below to work
300
+ # :nocov:
303
301
  end
304
302
  end
305
- alias_method :i18n_locale, :locale
306
-
307
- end # /module RequestMethods
308
-
309
-
303
+ alias i18n_locale locale
304
+
305
+ # Sets a fallback for locale => I18n.default
306
+ def i18n_set_locale_with_fallback(locale)
307
+ return ::R18n::I18n.default.to_s if locale.nil?
308
+
309
+ locale
310
+ end
311
+ end
312
+
310
313
  module ClassMethods
311
-
312
314
  # Return the i18n options for this plugin.
313
315
  def i18n_opts
314
316
  opts[:i18n]
315
317
  end
316
-
317
- end # /module ClassMethods
318
-
319
-
318
+ end
319
+
320
320
  # defines method available within the views / routing block
321
321
  module InstanceMethods
322
322
  include ::R18n::Helpers
323
-
323
+
324
324
  def i18n_available_locales
325
325
  @available_locales = []
326
326
  ::R18n.available_locales.each do |l|
@@ -328,15 +328,13 @@ class Roda
328
328
  end
329
329
  @available_locales
330
330
  end
331
-
331
+
332
332
  def i18n_default_places
333
333
  ::R18n.default_places
334
334
  end
335
-
336
335
  end
337
-
338
- end # /module RodaI18n
339
-
336
+ end
337
+
340
338
  register_plugin(:i18n, RodaI18n)
341
- end # /module RodaPlugins
342
- end # /class Roda
339
+ end
340
+ end
data/roda-i18n.gemspec CHANGED
@@ -1,19 +1,20 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'roda/i18n/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
8
  spec.name = 'roda-i18n'
8
- spec.version = ::Roda::I18n::VERSION
9
+ spec.version = Roda::I18n::VERSION
9
10
  spec.authors = ['Kematzy']
10
11
  spec.email = ['kematzy@gmail.com']
11
-
12
+
12
13
  spec.summary = 'Roda Internationalisation plugin'
13
- spec.description = "The Roda-i18n plugin enables easy addition of internationalisation (i18n) and localisation support in Roda apps"
14
+ spec.description = 'The Roda-i18n plugin enables easy addition of internationalisation (i18n) and localisation support in Roda apps'
14
15
  spec.homepage = 'http://github.com/kematzy/roda-i18n'
15
16
  spec.license = 'MIT'
16
-
17
+
17
18
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
19
  # delete this section to allow pushing this gem to any host.
19
20
  # if spec.respond_to?(:metadata)
@@ -22,29 +23,25 @@ Gem::Specification.new do |spec|
22
23
  # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
24
  # end
24
25
 
25
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
- spec.bindir = "exe"
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features|.github|.devcontainer)/}) ||
28
+ f.match(/^(Dockerfile|.tool-versions|justfile|.rubocop)/)
29
+ end
30
+ spec.bindir = 'exe'
27
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
29
-
32
+ spec.require_paths = ['lib']
33
+
30
34
  spec.platform = Gem::Platform::RUBY
31
- spec.has_rdoc = true
32
- spec.extra_rdoc_files = ['README.md', "MIT-LICENSE"]
33
- spec.rdoc_options += ['--quiet', '--line-numbers', '--inline-source', '--title', 'Roda-i18n: internationalisation plugin', '--main', 'README.md']
34
-
35
- spec.add_runtime_dependency 'roda', '~> 3.7'
36
- spec.add_runtime_dependency 'tilt'
37
- spec.add_runtime_dependency 'r18n-core', '~> 3.0'
38
-
39
- spec.add_development_dependency 'bundler', "~> 1.10"
40
- spec.add_development_dependency 'rake', "~> 12.3"
41
- spec.add_development_dependency 'erubis'
42
- spec.add_development_dependency 'kramdown'
43
- spec.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0'
44
- spec.add_development_dependency 'minitest-hooks', '~> 1.1', '>= 1.1.0'
45
- spec.add_development_dependency 'minitest-rg'
46
- spec.add_development_dependency 'rack-test', '~> 1.0'
47
- spec.add_development_dependency 'nokogiri'
48
- spec.add_development_dependency 'simplecov'
49
-
35
+ spec.extra_rdoc_files = ['README.md', 'MIT-LICENSE']
36
+ spec.rdoc_options += ['--quiet', '--line-numbers', '--inline-source', '--title',
37
+ 'Roda-i18n: internationalisation plugin', '--main', 'README.md']
38
+
39
+ spec.required_ruby_version = '>= 3.0.0'
40
+
41
+ spec.add_dependency 'date'
42
+ spec.add_dependency 'r18n-core', '~> 5'
43
+ spec.add_dependency 'roda', '~> 3.8'
44
+ spec.add_dependency 'tilt'
45
+
46
+ spec.metadata['rubygems_mfa_required'] = 'true'
50
47
  end