roda-i18n 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5a3d6733ef6fdf42c06e7e28107e123ba746f0af
4
+ data.tar.gz: 517043eb03472e935a0ca625bd71cc07c53a8fe9
5
+ SHA512:
6
+ metadata.gz: 33080f5390f7fea5ec9b4f6c85fb405cbaf70d0b8ce187eca20917ad83d61b071140deb5b03c04653a04f3fcb5f3d9816219f04a808a8ef3bec618e381e801b9
7
+ data.tar.gz: 1e96099edd2e3ecc6868b67982edb185a9ce9ed5cf5cb9dacb0d63c1f12e52fc3328653a3320b775189ceeef3d26f0341204b05ca9607d2d4c1a4c00603c93cd
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2015 Kematzy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,309 @@
1
+ # Roda-i18n
2
+
3
+ Easily add Internationalisation (i18n) and localisation support for [Roda](http://roda.jeremyevans.net/) apps, based upon the [R18n](https://github.com/ai/r18n) gem.
4
+
5
+ Extensively tested and with 100% code test coverage.
6
+
7
+
8
+ ## Installation
9
+
10
+ To use this gem, just do
11
+
12
+ $ (sudo) gem install roda-i18n
13
+
14
+ or if you use Bundler
15
+
16
+ gem "roda-i18n"
17
+
18
+
19
+ <br>
20
+
21
+
22
+ ## Getting Started
23
+
24
+ To add internationalisation and localisation support to your app just add the following code snippet in your app.
25
+
26
+ plugin :i18n
27
+
28
+ By default the default locale is set to <tt>'en'</tt> and the translations directory is set to the <tt>'i18n'</tt> directory in the rooot of your app, ie: '<tt>/path/2/app/i18n</tt>'.
29
+
30
+
31
+ **IMPORTANT! Make sure you create the 'i18n' folder and add an 'en.yml' file with
32
+ at least one translation within it.**
33
+
34
+
35
+ <br>
36
+
37
+
38
+ ## Configuration
39
+
40
+ ### Overriding defaults during configuration
41
+
42
+ Both <tt>:locale</tt> and <tt>:translations</tt> can be configured (overridden) during plugin configuration:
43
+
44
+ plugin :i18n, :locale => ['de'], :translations => ['absolute/path/2/i18n']
45
+
46
+
47
+ **NOTE!**
48
+
49
+ 1. You must set <tt>opts[:root]</tt> in your app if you do not define the <tt>:translations</tt> path during plugin configuration.
50
+
51
+ 2. When overriding <tt>:translations</tt> the **any path(s) given must be absolute**.
52
+
53
+
54
+ #### Loading translations from multiple i18n directories
55
+
56
+ The <tt>:translations</tt> path supports 'wildcards', ie: <tt>path/**/i18n</tt> so you can load translations from multiple combined apps, each with their own <tt>i18n</tt> folder with translations.
57
+
58
+ **Please Note!**
59
+
60
+ When loading translations from multiple sources and the same translation key is available in multiple files of the same locale, then **the translations in the first loaded translation file takes precedence over subsequent loaded translations**.
61
+
62
+ * ie: translations in <tt>./i18n/en.yml</tt> takes precedence over translations in <tt>./apps/app1/i18n/en.yml</tt>
63
+
64
+
65
+ You can also set a list of preferred locales as an array ordered by priority.
66
+
67
+ plugin :i18n, :locale => ['es','fr','en']
68
+
69
+
70
+
71
+ <br>
72
+
73
+
74
+ ## USAGE
75
+
76
+ The **i18n** plugin depends upon simple YAML based translations files:
77
+
78
+ # app/i18n/en.yml
79
+
80
+ user:
81
+ edit: Edit user
82
+ name: User name is %1
83
+ count: !!pl
84
+ 1: There is 1 user
85
+ n: There are %1 users
86
+
87
+
88
+ ...and the **<tt>:t</tt>** instance method to output the translations:
89
+
90
+
91
+ t.user.edit #=> "Edit user"
92
+ t.user.name('John') #=> "User name is John"
93
+ t.user.count(5) #=> "There are 5 users"
94
+
95
+ t.does.not.exist | 'default' #=> "default"
96
+
97
+
98
+ ...and the **<tt>:l</tt>** (lowercase L) instance method provides built-in localisations support:
99
+
100
+
101
+ l Time.now #=> "03/01/2010 18:54"
102
+ l Time.now, :human #=> "now"
103
+ l Time.now, :full #=> "3rd of January, 2010 18:54"
104
+
105
+
106
+
107
+ Both the <tt>:t</tt> and <tt>:l</tt> methods are available within the route and template (erb) scopes. ie:
108
+
109
+
110
+ route do |r|
111
+ r.root do
112
+ t.welcome.message
113
+ end
114
+ end
115
+
116
+ # app/views/layout.erb
117
+ <snip...>
118
+ <h1><%= t.welcome.message %></h1>
119
+ <snip...>
120
+
121
+
122
+
123
+ Please visit [R18n](https://github.com/ai/r18n/tree/master/r18n-core) for more information about the R18n gem used to create the above.
124
+
125
+ <br>
126
+
127
+ ## Key Methods / Functionality
128
+
129
+
130
+ <br>
131
+
132
+
133
+ ### <tt>#locale(opts={},&blk)</tt> - (request method)
134
+
135
+ This request method makes it easy to handle translations based upon the **<tt>:locale</tt> prefix on a route / URL**. ie: <tt>blog.com/**de**/posts</tt>.
136
+
137
+
138
+ To enable this, just use the following code structure:
139
+
140
+ route do |r|
141
+
142
+ # all routes are prefixed with '/:locale'
143
+ # ie: GET /de/posts => will use DE translations
144
+ # ie: GET /es/posts => will use ES translations
145
+
146
+ r.locale do # also aliased as #i18n_locale
147
+ r.is 'posts' do
148
+ t.posts.header # use translations or locales
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+
155
+
156
+
157
+ **NOTE!** Any URL / request with a non-present or not supported locale will be given the **configured default locale** or the EN (English) default locale.
158
+
159
+
160
+ <br>
161
+
162
+
163
+ ### <tt>#i18n_set_locale_from(type)</tt> - (request method)
164
+
165
+ Obtains the locale from either ENV, HTTP (browser), Params or Session values.
166
+
167
+ route do |r|
168
+ # A): set from session[:locale] (if present)
169
+ r.i18n_set_locale_from(:session)
170
+
171
+ # B): set from URL params ie: GET /posts?locale=de
172
+ r.i18n_set_locale_from(:params)
173
+
174
+ # C): set from the browser's HTTP request locale
175
+ r.i18n_set_locale_from(:http)
176
+
177
+ # D): set from the server ENV['LANG'] variable
178
+ r.i18n_set_locale_from(:ENV)
179
+
180
+
181
+ r.is 'posts' do
182
+ t.posts.header # use translations
183
+ end
184
+ end
185
+
186
+
187
+ **NOTE!** defaults to the configured default locale, or English, if the given locale type is invalid.
188
+
189
+
190
+ <br>
191
+
192
+
193
+ ### <tt>#i18n_set_locale(locale, &blk)</tt> - (request method)
194
+
195
+ Enables overriding the default locale and setting a temporary :locale
196
+ within a route block.
197
+
198
+ route do |r|
199
+ # configured default locale
200
+
201
+ <snip...>
202
+
203
+ r.i18n_set_locale('de') do
204
+ # within this route block the locale is DE (German)
205
+ end
206
+
207
+ r.i18n_set_locale('es') do
208
+ # within this route block the locale is ES (Spanish)
209
+ end
210
+
211
+ end
212
+
213
+
214
+
215
+
216
+
217
+ ## InstanceMethods
218
+
219
+ ### `#t`
220
+
221
+ This is the main translation output method. (See examples above)
222
+
223
+
224
+ <br>
225
+
226
+
227
+ ### `#l`
228
+
229
+ Key localisation method. Handles dates etc. (See examples above)
230
+
231
+
232
+ <br>
233
+
234
+
235
+ ### `#i18n_available_locales`
236
+
237
+ Returns a two-dimensional array of available locales.
238
+
239
+ puts i18n_available_locales #=> [ ['en', 'English'], ...]
240
+
241
+
242
+
243
+
244
+ ### `#i18n_default_places`
245
+
246
+
247
+ <br>
248
+
249
+
250
+ ## Class Methods
251
+
252
+ ### <tt>#i18n_opts</tt>
253
+
254
+ Return the i18n options for this class as a Hash.
255
+
256
+
257
+ <br>
258
+
259
+
260
+ ## Ideas
261
+
262
+ A few ideas that may be outlandish, but possible?
263
+
264
+
265
+ ### Ability to load translations from multiple locations via an array.
266
+
267
+ plugin :i18n, :translations => ['app1/i18n', 'app2/i18n', 'app3/i18n']
268
+
269
+
270
+ > [Concept Reference](https://github.com/ai/r18n/tree/master/r18n-core#loaders)
271
+ >
272
+ > You can also set several loaders to merge translations from different sources:
273
+ >
274
+ > R18n.default_places = [MyLoader.new, DBLoader.new, 'path/to/yaml']
275
+ >
276
+
277
+
278
+ ### Sequel DBLoader for DB based translations support
279
+
280
+ Som form of built-in support for storing / loading translations from a Sequel based DB.
281
+
282
+
283
+ <br>
284
+
285
+
286
+ ----
287
+
288
+ ## TODOs
289
+
290
+ * I'm sure there's something, but right now the list is empty ;-)
291
+
292
+
293
+ ## Credits
294
+
295
+ * This plugin have been inspired by the `sinatra-i18n` gem available at [github/ai/r18n](http://github.com/ai/r18n) created by [Andrey Sitnik](http://github.com/ai).
296
+
297
+ * Testing code have been partly copied from [Forme](github.com/jeremyevans/forme).
298
+
299
+ * Inspiration and assistance by [Jeremy Evans](github.com/jeremyevans). Many thanks Jeremy!!
300
+
301
+
302
+ ## Licence
303
+
304
+ MIT
305
+
306
+ Copyright: 2015 Kematzy
307
+
308
+
309
+
@@ -0,0 +1,78 @@
1
+ require "rake"
2
+ require "rake/clean"
3
+
4
+ NAME = 'roda-i18n'
5
+ CLEAN.include ["#{NAME}-*.gem", "rdoc", "coverage", '**/*.rbc']
6
+
7
+ # Gem Packaging and Release
8
+
9
+ desc "Packages #{NAME}"
10
+ task :package=>[:clean] do |p|
11
+ sh %{gem build #{NAME}.gemspec}
12
+ end
13
+
14
+ desc "Upload #{NAME} gem to rubygems"
15
+ task :release=>[:package] do
16
+ sh %{gem push ./#{NAME}-#{VERS.call}.gem}
17
+ end
18
+
19
+ # ### RDoc
20
+ #
21
+ # RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Roda I18n']
22
+ #
23
+ # begin
24
+ # gem 'hanna-nouveau'
25
+ # RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
26
+ # rescue Gem::LoadError
27
+ # end
28
+ #
29
+ # rdoc_task_class = begin
30
+ # require "rdoc/task"
31
+ # RDoc::Task
32
+ # rescue LoadError
33
+ # require "rake/rdoctask"
34
+ # Rake::RDocTask
35
+ # end
36
+ #
37
+ # RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
38
+ #
39
+ # rdoc_task_class.new do |rdoc|
40
+ # rdoc.rdoc_dir = "rdoc"
41
+ # rdoc.options += RDOC_OPTS
42
+ # rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb"
43
+ # end
44
+
45
+ ### Specs
46
+
47
+ desc "Run specs"
48
+ task :spec do
49
+ sh "#{FileUtils::RUBY} -rubygems -I lib -e 'ARGV.each{|f| require f}' ./spec/*_spec.rb"
50
+ end
51
+ task :default => :spec
52
+
53
+ desc "Run specs with coverage"
54
+ task :coverage do
55
+ ENV['COVERAGE'] = '1'
56
+ Rake::Task['spec'].invoke
57
+ end
58
+
59
+ ### Other
60
+
61
+ # desc "Print #{NAME} version"
62
+ # task :version do
63
+ # puts VERS.call
64
+ # end
65
+
66
+ desc "Check syntax of all .rb files"
67
+ task :check_syntax do
68
+ Dir['**/*.rb'].each{|file| print `#{ENV['RUBY'] || :ruby} -c #{file} | fgrep -v "Syntax OK"`}
69
+ end
70
+
71
+ # desc "Start an IRB shell using the extension"
72
+ # task :irb do
73
+ # require 'rbconfig'
74
+ # ruby = ENV['RUBY'] || File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
75
+ # irb = ENV['IRB'] || File.join(RbConfig::CONFIG['bindir'], File.basename(ruby).sub('ruby', 'irb'))
76
+ # sh %{#{irb} -I lib -r forme}
77
+ # end
78
+
@@ -0,0 +1,276 @@
1
+ require 'r18n-core'
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ # The i18n plugin allows you to easily add internationalisation (i18n) and
6
+ # localisation support to your Roda app, by adding the following:
7
+ #
8
+ # plugin :i18n
9
+ #
10
+ # By default the default locale is set to <tt>'en'</tt> and the translations directory
11
+ # is set to <tt>'i18n'</tt> in the rooot of your app.
12
+ #
13
+ # Both <tt>:locale</tt> and <tt>:translations</tt> can be overridden during configuration:
14
+ #
15
+ # plugin :i18n, :locale => ['de'], :translations => ['absolute/path/2/i18n']
16
+ #
17
+ # Please note!
18
+ # 1) You must set <tt>opts[:root]</tt> in your app if you don't define the <tt>:translations</tt> path.
19
+ #
20
+ # 2) When overriding <tt>:translations</tt> the path given must be absolute.
21
+ #
22
+ # The path supports 'wildcards', ie: path/**/i18n so you can load translations from multiple
23
+ # combined apps each with their own <tt>i18n</tt> folder with translations.
24
+ #
25
+ # Note! when loading translations from multiple sources and the same translation key is used
26
+ # in both files, the first loaded file takes precedence, ie: <tt>./i18n/en.yml</tt> takes precedence over
27
+ # <tt>./apps/app1/i18n/en.yml</tt>
28
+ #
29
+ # == USAGE
30
+ #
31
+ # The i18n plugin depends upon simple YAML based translations files:
32
+ #
33
+ # # app/i18n/en.yml
34
+ #
35
+ # user:
36
+ # edit: Edit user
37
+ # name: User name is %1
38
+ # count: !!pl
39
+ # 1: There is 1 user
40
+ # n: There are %1 users
41
+ #
42
+ #
43
+ # and the <tt>:t</tt> instance method to output the translations:
44
+ #
45
+ #
46
+ # t.user.edit #=> "Edit user"
47
+ # t.user.name('John') #=> "User name is John"
48
+ # t.user.count(5) #=> "There are 5 users"
49
+ #
50
+ # t.does.not.exist | 'default' #=> "default"
51
+ #
52
+ #
53
+ # the <tt>:l</tt> instance method provides built-in localisations support:
54
+ #
55
+ # l Time.now #=> "03/01/2010 18:54"
56
+ # l Time.now, :human #=> "now"
57
+ # l Time.now, :full #=> "3rd of January, 2010 18:54"
58
+ #
59
+ # Both the <tt>:t</tt> and <tt>:l</tt> methods are available in the route and template (erb) scopes. ie:
60
+ #
61
+ # route do |r|
62
+ # r.root do
63
+ # t.welcome.message
64
+ # end
65
+ # end
66
+ #
67
+ # # app/views/layout.erb
68
+ # <snip...>
69
+ # <h1><%= t.welcome.message %></h1>
70
+ # <snip...>
71
+ #
72
+ #
73
+ #
74
+ # Visit [R18n](https://github.com/ai/r18n/tree/master/r18n-core) for more information.
75
+ #
76
+ #
77
+ # The i18n plugin also makes it easy to handle locales:
78
+ #
79
+ #
80
+ # === <tt>:locale</tt> RequestMethod
81
+ #
82
+ # This request method makes it to handle translations based upon the :locale prefix on a URL,
83
+ # ie: <tt>blog.com/de/posts</tt>, just use the following code:
84
+ #
85
+ # route do |r|
86
+ #
87
+ # r.locale do
88
+ # r.is 'posts' do
89
+ # t.posts.header
90
+ # end
91
+ # end
92
+ #
93
+ # end
94
+ #
95
+ #
96
+ # === <tt>:i18n_set_locale_from</tt> RequestMethod
97
+ #
98
+ # Obtains the locale from either ENV, HTTP (browser), Params or Session
99
+ # values
100
+ #
101
+ #
102
+ # Naturally we can allow browsers to override the default locale within routes, like this:
103
+ #
104
+ # route do |r|
105
+ # i18n_set_locale_from(:http) #=> set to the browser's default locale (en-US)
106
+ # r.get '' do
107
+ # t.hello #=> 'Howdy, I speak American English'
108
+ # end
109
+ # end
110
+ #
111
+ # The def
112
+ #
113
+ #
114
+ # route do |r|
115
+ # i18n_set_locale('de')
116
+ # r.get 'in-german' do
117
+ # t.hello #=> 'Guten tag, ich spreche deutsch'
118
+ # end
119
+ # end
120
+ #
121
+ #
122
+ #
123
+ #
124
+ module RodaI18n
125
+
126
+ def self.load_dependencies(app, opts={})
127
+ # app.plugin :render
128
+ # app.plugin :environments
129
+ end
130
+
131
+ def self.configure(app, opts={})
132
+ opts = app.opts[:i18n][:orig_opts].merge(opts) if app.opts[:i18n]
133
+ app.opts[:i18n] = opts.dup
134
+ app.opts[:i18n][:orig_opts] = opts
135
+ opts = app.opts[:i18n]
136
+
137
+ ::R18n.default_places = opts[:translations] || File.expand_path('i18n', app.opts[:root])
138
+
139
+ opts[:default_locale] = opts[:locale].is_a?(Array) ? opts[:locale].first : opts[:locale] || 'en'
140
+ ::R18n::I18n.default = opts[:default_locale]
141
+
142
+ ::R18n.clear_cache! if ENV['RACK_ENV'] != 'production'
143
+ i18n = R18n::I18n.new(opts[:locale], ::R18n.default_places,
144
+ off_filters: :untranslated,
145
+ on_filters: :untranslated_html)
146
+ ::R18n.set(i18n)
147
+ end
148
+
149
+
150
+ module RequestMethods
151
+
152
+ # Obtains the locale from either ENV, HTTP (browser), Params or Session
153
+ # values.
154
+ #
155
+ # route do |r|
156
+ # # A): set from URL params ie: GET /posts?locale=de
157
+ # r.i18n_set_locale_from(:params)
158
+ #
159
+ # # B): set from session[:locale] (if present)
160
+ # r.i18n_set_locale_from(:session)
161
+ #
162
+ # # C): set from the browser's HTTP request locale
163
+ # r.i18n_set_locale_from(:http)
164
+ #
165
+ # # D): set from the server ENV['LANG'] variable
166
+ # r.i18n_set_locale_from(:ENV)
167
+ #
168
+ #
169
+ # r.is 'posts' do
170
+ # t.posts.header # use translations
171
+ # end
172
+ # end
173
+ #
174
+ def i18n_set_locale_from(type)
175
+ case type.to_sym
176
+ when :http
177
+ _locale = ::R18n::I18n.parse_http(scope.request.env['HTTP_ACCEPT_LANGUAGE'])
178
+ when :session
179
+ _locale = session[:locale] if session[:locale]
180
+ when :params
181
+ _locale = scope.request.params['locale'] if scope.request.params['locale']
182
+ when :ENV
183
+ _locale = ENV['LANG'].split('.').first if ENV['LANG']
184
+ else
185
+ _locale = nil
186
+ end
187
+ # sanity check: set to default locale if not set above
188
+ _locale = ::R18n::I18n.default.to_s if _locale.nil?
189
+
190
+ _i18n = ::R18n::I18n.new(_locale, ::R18n.default_places,
191
+ off_filters: :untranslated,
192
+ on_filters: :untranslated_html)
193
+ ::R18n.set(_i18n)
194
+ end
195
+
196
+ # Enables setting temporary :locale.
197
+ #
198
+ # route do |r|
199
+ #
200
+ # r.i18n_set_locale('de') do
201
+ # # within this block the locale is DE (German)
202
+ # end
203
+ #
204
+ # r.i18n_set_locale('es') do
205
+ # # within this block the locale is ES (Spanish)
206
+ # end
207
+ #
208
+ # end
209
+ #
210
+ def i18n_set_locale(locale, &blk)
211
+ locale = ::R18n::I18n.default.to_s if locale.nil?
212
+
213
+ _i18n = ::R18n::I18n.new(locale, ::R18n.default_places,
214
+ off_filters: :untranslated,
215
+ on_filters: :untranslated_html)
216
+ ::R18n.set(_i18n)
217
+ yield
218
+ end
219
+
220
+ # Sets the locale based upon <tt>:locale</tt> prefixed routes
221
+ #
222
+ # route do |r|
223
+ # r.locale do
224
+ # # all routes are prefixed with '/:locale'
225
+ # # ie: GET /de/posts => will use DE translations
226
+ # # ie: GET /es/posts => will use ES translations
227
+ # r.is 'posts' do
228
+ # t.posts.header # use translations or locales
229
+ # end
230
+ # end
231
+ # end
232
+ #
233
+ def locale(opts={}, &blk)
234
+ on(':locale', opts) do |l|
235
+ _locale = l || self.class.opts[:locale]
236
+ session[:locale] = _locale unless session[:locale]
237
+ ::R18n.set(_locale)
238
+ yield
239
+ end
240
+ end
241
+ alias_method :i18n_locale, :locale
242
+
243
+
244
+ end #/module RequestMethods
245
+
246
+ module ClassMethods
247
+
248
+ # Return the i18n options for this class.
249
+ def i18n_opts
250
+ opts[:i18n]
251
+ end
252
+
253
+ end #/module ClassMethods
254
+
255
+ module InstanceMethods
256
+ include ::R18n::Helpers
257
+
258
+ def i18n_available_locales
259
+ @available_locales = []
260
+ ::R18n.available_locales.each do |l|
261
+ @available_locales << [l.code, l.title]
262
+ end
263
+ @available_locales
264
+ end
265
+
266
+ def i18n_default_places
267
+ ::R18n.default_places
268
+ end
269
+
270
+ end
271
+
272
+ end #/module RodaI18n
273
+
274
+ register_plugin(:i18n, RodaI18n)
275
+ end #/module RodaPlugins
276
+ end #/class Roda