roda-i18n 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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