i18n 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of i18n might be problematic. Click here for more details.
- data/README.textile +44 -9
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/lib/i18n.rb +60 -15
- data/lib/i18n/backend.rb +14 -0
- data/lib/i18n/backend/active_record.rb +69 -0
- data/lib/i18n/backend/active_record/store_procs.rb +37 -0
- data/lib/i18n/backend/active_record/translation.rb +82 -0
- data/lib/i18n/backend/active_record_missing.rb +55 -0
- data/lib/i18n/backend/base.rb +235 -0
- data/lib/i18n/backend/cache.rb +71 -0
- data/lib/i18n/backend/chain.rb +74 -0
- data/lib/i18n/backend/fallbacks.rb +51 -0
- data/lib/i18n/backend/gettext.rb +75 -0
- data/lib/i18n/backend/helpers.rb +53 -0
- data/lib/i18n/backend/metadata.rb +73 -0
- data/lib/i18n/backend/pluralization.rb +57 -0
- data/lib/i18n/backend/simple.rb +15 -227
- data/lib/i18n/core_ext/object/meta_class.rb +5 -0
- data/lib/i18n/{string.rb → core_ext/string/interpolate.rb} +2 -0
- data/lib/i18n/exceptions.rb +2 -0
- data/lib/i18n/gettext.rb +25 -0
- data/lib/i18n/helpers.rb +5 -0
- data/lib/i18n/helpers/gettext.rb +64 -0
- data/lib/i18n/locale.rb +6 -0
- data/lib/i18n/locale/fallbacks.rb +98 -0
- data/lib/i18n/locale/tag.rb +28 -0
- data/lib/i18n/locale/tag/parents.rb +24 -0
- data/lib/i18n/locale/tag/rfc4646.rb +76 -0
- data/lib/i18n/locale/tag/simple.rb +41 -0
- data/test/all.rb +7 -2
- data/test/api/basics.rb +3 -1
- data/test/api/interpolation.rb +35 -4
- data/test/api/lambda.rb +5 -3
- data/test/api/link.rb +4 -2
- data/test/api/localization/date.rb +2 -0
- data/test/api/localization/date_time.rb +3 -1
- data/test/api/localization/lambda.rb +4 -2
- data/test/api/localization/time.rb +3 -1
- data/test/api/pluralization.rb +12 -15
- data/test/api/translation.rb +5 -3
- data/test/backend/active_record/active_record_test.rb +40 -0
- data/test/backend/active_record/all.rb +3 -0
- data/test/backend/active_record/api_test.rb +54 -0
- data/test/backend/active_record/setup.rb +166 -0
- data/test/backend/active_record_missing/active_record_missing_test.rb +63 -0
- data/test/backend/all/api_test.rb +88 -0
- data/test/backend/cache/cache_test.rb +69 -0
- data/test/backend/chain/api_test.rb +80 -0
- data/test/backend/chain/chain_test.rb +64 -0
- data/test/backend/fallbacks/api_test.rb +84 -0
- data/test/backend/fallbacks/fallbacks_test.rb +57 -0
- data/test/backend/metadata/metadata_test.rb +65 -0
- data/test/backend/pluralization/api_test.rb +86 -0
- data/test/backend/pluralization/pluralization_test.rb +43 -0
- data/test/backend/simple/all.rb +2 -0
- data/test/backend/simple/api_test.rb +27 -20
- data/test/backend/simple/helpers_test.rb +26 -0
- data/test/backend/simple/lookup_test.rb +2 -1
- data/test/backend/simple/{setup/localization.rb → setup.rb} +29 -11
- data/test/backend/simple/translations_test.rb +1 -6
- data/test/{string_test.rb → core_ext/string/interpolate_test.rb} +4 -2
- data/test/fixtures/locales/de.po +67 -0
- data/test/fixtures/locales/en.rb +2 -0
- data/test/fixtures/locales/plurals.rb +113 -0
- data/test/gettext/api_test.rb +204 -0
- data/test/gettext/backend_test.rb +84 -0
- data/test/i18n_exceptions_test.rb +3 -1
- data/test/i18n_load_path_test.rb +8 -1
- data/test/i18n_test.rb +30 -7
- data/test/locale/fallbacks_test.rb +128 -0
- data/test/locale/tag/rfc4646_test.rb +145 -0
- data/test/locale/tag/simple_test.rb +35 -0
- data/test/test_helper.rb +11 -5
- data/test/with_options.rb +2 -0
- metadata +75 -11
- data/test/backend/simple/setup/base.rb +0 -21
data/README.textile
CHANGED
@@ -1,20 +1,55 @@
|
|
1
|
-
h1. Ruby I18n
|
1
|
+
h1. Ruby I18n
|
2
2
|
|
3
|
-
|
3
|
+
Ruby Internationalization and localization solution.
|
4
4
|
|
5
|
-
|
5
|
+
Features:
|
6
|
+
|
7
|
+
* translation and localization
|
8
|
+
* interpolation of values to translations (Ruby 1.9 compatible syntax)
|
9
|
+
* pluralization (CLDR compatible)
|
10
|
+
* flexible defaults
|
11
|
+
* bulk lookup
|
12
|
+
* lambdas as translation data
|
13
|
+
* custom key/scope separator
|
14
|
+
* custom exception handlers
|
15
|
+
* extensible architecture with a swappable backend
|
16
|
+
|
17
|
+
Experimental, pluggable features:
|
18
|
+
|
19
|
+
* lambda pluralizers stored as translation data
|
20
|
+
* RFC4647 compliant locale fallbacks (with optional RFC4646 locale validation)
|
21
|
+
* backend cache
|
22
|
+
|
23
|
+
For more information and lots of resources see: "http://rails-i18n.org/wiki":http://rails-i18n.org/wiki
|
24
|
+
|
25
|
+
h2. Install
|
26
|
+
|
27
|
+
gem install i18n
|
28
|
+
|
29
|
+
h3. Install on Rails 2.3.4 (deprecated)
|
30
|
+
|
31
|
+
Rails 2.3.4 will not accept i18n gems > 0.1.3. There is an unpacked gem inside of active_support/lib/vendor which gets loaded unless gem 'i18n', '~> 0.1.3'. This requirement is relaxed in http://github.com/rails/rails/commit/6da036538334fd459156ab2789c9fae5512be5d2
|
32
|
+
|
33
|
+
The following is needed to get the new i18n gem inside vendor/plugins loaded properly.
|
34
|
+
|
35
|
+
def reload_i18n!
|
36
|
+
raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4"
|
37
|
+
|
38
|
+
$:.grep(/i18n/).each { |path| $:.delete(path) }
|
39
|
+
I18n::Backend.send :remove_const, "Simple"
|
40
|
+
$: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
Then you can `reload_i18n!` inside an i18n initializer.
|
6
44
|
|
7
45
|
h2. Authors
|
8
46
|
|
9
|
-
* "Matt Aimonetti":http://railsontherun.com
|
10
47
|
* "Sven Fuchs":http://www.artweb-design.de
|
11
48
|
* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
|
12
|
-
* "Saimon Moore":http://saimonmoore.net
|
13
49
|
* "Stephan Soller":http://www.arkanis-development.de
|
50
|
+
* "Saimon Moore":http://saimonmoore.net
|
51
|
+
* "Matt Aimonetti":http://railsontherun.com
|
14
52
|
|
15
53
|
h2. License
|
16
54
|
|
17
|
-
MIT License. See the included MIT-LICENCE file.
|
18
|
-
|
19
|
-
|
20
|
-
|
55
|
+
MIT License. See the included MIT-LICENCE file.
|
data/Rakefile
CHANGED
@@ -8,6 +8,7 @@ begin
|
|
8
8
|
require 'jeweler'
|
9
9
|
Jeweler::Tasks.new do |s|
|
10
10
|
s.name = "i18n"
|
11
|
+
s.rubyforge_project = "i18n"
|
11
12
|
s.summary = "New wave Internationalization support for Ruby"
|
12
13
|
s.email = "rails-i18n@googlegroups.com"
|
13
14
|
s.homepage = "http://rails-i18n.org"
|
@@ -17,4 +18,4 @@ begin
|
|
17
18
|
end
|
18
19
|
rescue LoadError
|
19
20
|
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
-
end
|
21
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/i18n.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Authors:: Sven Fuchs (http://www.artweb-design.de),
|
3
4
|
# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
|
5
|
+
# Stephan Soller (http://www.arkanis-development.de/),
|
4
6
|
# Saimon Moore (http://saimonmoore.net),
|
5
|
-
#
|
7
|
+
# Matt Aimonetti (http://railsontherun.com/)
|
6
8
|
# Copyright:: Copyright (c) 2008 The Ruby i18n Team
|
7
9
|
# License:: MIT
|
8
|
-
require 'i18n/backend/simple'
|
9
10
|
require 'i18n/exceptions'
|
10
|
-
require 'i18n/string'
|
11
|
+
require 'i18n/core_ext/string/interpolate'
|
11
12
|
|
12
13
|
module I18n
|
14
|
+
autoload :Backend, 'i18n/backend'
|
15
|
+
autoload :Helpers, 'i18n/helpers'
|
16
|
+
autoload :Locale, 'i18n/locale'
|
17
|
+
|
13
18
|
@@backend = nil
|
14
19
|
@@load_path = nil
|
15
|
-
@@default_locale = :
|
20
|
+
@@default_locale = :en
|
16
21
|
@@default_separator = '.'
|
17
22
|
@@exception_handler = :default_exception_handler
|
18
23
|
|
@@ -34,7 +39,7 @@ module I18n
|
|
34
39
|
|
35
40
|
# Sets the current default locale. Used to set a custom default locale.
|
36
41
|
def default_locale=(locale)
|
37
|
-
@@default_locale = locale
|
42
|
+
@@default_locale = locale.to_sym rescue nil
|
38
43
|
end
|
39
44
|
|
40
45
|
# Returns the current locale. Defaults to I18n.default_locale.
|
@@ -44,12 +49,19 @@ module I18n
|
|
44
49
|
|
45
50
|
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
|
46
51
|
def locale=(locale)
|
47
|
-
Thread.current[:locale] = locale
|
52
|
+
Thread.current[:locale] = locale.to_sym rescue nil
|
48
53
|
end
|
49
54
|
|
50
|
-
# Returns an array of locales for which translations are available
|
55
|
+
# Returns an array of locales for which translations are available.
|
56
|
+
# Unless you explicitely set the these through I18n.available_locales=
|
57
|
+
# the call will be delegated to the backend and memoized on the I18n module.
|
51
58
|
def available_locales
|
52
|
-
backend.available_locales
|
59
|
+
@@available_locales ||= backend.available_locales
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets the available locales.
|
63
|
+
def available_locales=(locales)
|
64
|
+
@@available_locales = locales
|
53
65
|
end
|
54
66
|
|
55
67
|
# Returns the current default scope separator. Defaults to '.'
|
@@ -176,13 +188,13 @@ module I18n
|
|
176
188
|
# *LAMBDAS*
|
177
189
|
#
|
178
190
|
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
|
179
|
-
# called and passed the key and options.
|
191
|
+
# called and passed the key and options.
|
180
192
|
#
|
181
193
|
# E.g. assuming the key <tt>:salutation</tt> resolves to:
|
182
194
|
# lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" }
|
183
195
|
#
|
184
196
|
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
|
185
|
-
#
|
197
|
+
#
|
186
198
|
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
|
187
199
|
# a cache layer is put in front of I18n.translate it will generate a cache key
|
188
200
|
# from the argument values passed to #translate. Therefor your lambdas should
|
@@ -192,13 +204,19 @@ module I18n
|
|
192
204
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
193
205
|
key = args.shift
|
194
206
|
locale = options.delete(:locale) || I18n.locale
|
207
|
+
raises = options.delete(:raise)
|
195
208
|
backend.translate(locale, key, options)
|
196
|
-
rescue I18n::ArgumentError =>
|
197
|
-
raise
|
198
|
-
|
209
|
+
rescue I18n::ArgumentError => exception
|
210
|
+
raise exception if raises
|
211
|
+
handle_exception(exception, locale, key, options)
|
199
212
|
end
|
200
213
|
alias :t :translate
|
201
214
|
|
215
|
+
def translate!(key, options = {})
|
216
|
+
translate(key, options.merge( :raise => true ))
|
217
|
+
end
|
218
|
+
alias :t! :translate!
|
219
|
+
|
202
220
|
# Localizes certain objects, such as dates and numbers to local formatting.
|
203
221
|
def localize(object, options = {})
|
204
222
|
locale = options[:locale] || I18n.locale
|
@@ -208,6 +226,7 @@ module I18n
|
|
208
226
|
alias :l :localize
|
209
227
|
|
210
228
|
protected
|
229
|
+
|
211
230
|
# Handles exceptions raised in the backend. All exceptions except for
|
212
231
|
# MissingTranslationData exceptions are re-raised. When a MissingTranslationData
|
213
232
|
# was caught and the option :raise is not set the handler returns an error
|
@@ -217,6 +236,32 @@ module I18n
|
|
217
236
|
raise exception
|
218
237
|
end
|
219
238
|
|
239
|
+
# Any exceptions thrown in translate will be sent to the @@exception_handler
|
240
|
+
# which can be a Symbol, a Proc or any other Object.
|
241
|
+
#
|
242
|
+
# If exception_handler is a Symbol then it will simply be sent to I18n as
|
243
|
+
# a method call. A Proc will simply be called. In any other case the
|
244
|
+
# method #call will be called on the exception_handler object.
|
245
|
+
#
|
246
|
+
# Examples:
|
247
|
+
#
|
248
|
+
# I18n.exception_handler = :default_exception_handler # this is the default
|
249
|
+
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
|
250
|
+
#
|
251
|
+
# I18n.exception_handler = lambda { |*args| ... } # a lambda
|
252
|
+
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
|
253
|
+
#
|
254
|
+
# I18n.exception_handler = I18nExceptionHandler.new # an object
|
255
|
+
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
|
256
|
+
def handle_exception(exception, locale, key, options)
|
257
|
+
case @@exception_handler
|
258
|
+
when Symbol
|
259
|
+
send(@@exception_handler, exception, locale, key, options)
|
260
|
+
else
|
261
|
+
@@exception_handler.call(exception, locale, key, options)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
220
265
|
# Merges the given locale, key and scope into a single array of keys.
|
221
266
|
# Splits keys that contain dots into multiple keys. Makes sure all
|
222
267
|
# keys are Symbols.
|
data/lib/i18n/backend.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module I18n
|
2
|
+
module Backend
|
3
|
+
autoload :ActiveRecord, 'i18n/backend/active_record'
|
4
|
+
autoload :Base, 'i18n/backend/base'
|
5
|
+
autoload :Cache, 'i18n/backend/cache'
|
6
|
+
autoload :Chain, 'i18n/backend/chain'
|
7
|
+
autoload :Fallbacks, 'i18n/backend/fallbacks'
|
8
|
+
autoload :Gettext, 'i18n/backend/gettext'
|
9
|
+
autoload :Helpers, 'i18n/backend/helpers'
|
10
|
+
autoload :Metadata, 'i18n/backend/metadata'
|
11
|
+
autoload :Pluralization, 'i18n/backend/pluralization'
|
12
|
+
autoload :Simple, 'i18n/backend/simple'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'i18n/backend/base'
|
2
|
+
require 'i18n/backend/active_record/translation'
|
3
|
+
|
4
|
+
#
|
5
|
+
# This backend reads translations from a Translations table in environment database. Note that the database
|
6
|
+
# will not automatically be prepopulated with missing keys. You can achieve this effect with the ActiveRecordMissing backend,
|
7
|
+
# as the following example shows:
|
8
|
+
#
|
9
|
+
# I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18.backend, I18n::Backend::ActiveRecordMissing.new)
|
10
|
+
#
|
11
|
+
module I18n
|
12
|
+
module Backend
|
13
|
+
class ActiveRecord
|
14
|
+
autoload :StoreProcs, 'i18n/backend/active_record/store_procs'
|
15
|
+
autoload :Translation, 'i18n/backend/active_record/translation'
|
16
|
+
|
17
|
+
include Base
|
18
|
+
|
19
|
+
def reload!
|
20
|
+
end
|
21
|
+
|
22
|
+
def store_translations(locale, data)
|
23
|
+
separator = I18n.default_separator # TODO allow to pass as an option?
|
24
|
+
wind_keys(data).each do |key, v|
|
25
|
+
Translation.locale(locale).lookup(expand_keys(key, separator), separator).delete_all
|
26
|
+
Translation.create(:locale => locale.to_s, :key => key, :value => v)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def available_locales
|
31
|
+
begin
|
32
|
+
Translation.available_locales
|
33
|
+
rescue ::ActiveRecord::StatementInvalid
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def lookup(locale, key, scope = [], separator = nil)
|
41
|
+
return unless key
|
42
|
+
|
43
|
+
separator ||= I18n.default_separator
|
44
|
+
key = (Array(scope) + Array(key)).join(separator)
|
45
|
+
|
46
|
+
result = Translation.locale(locale).lookup(key, separator).all
|
47
|
+
if result.empty?
|
48
|
+
return nil
|
49
|
+
elsif result.first.key == key
|
50
|
+
return result.first.value
|
51
|
+
else
|
52
|
+
chop_range = (key.size + separator.size)..-1
|
53
|
+
result = result.inject({}) do |hash, r|
|
54
|
+
hash[r.key.slice(chop_range)] = r.value
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
deep_symbolize_keys(unwind_keys(result))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
|
62
|
+
def expand_keys(key, separator = I18n.default_separator)
|
63
|
+
key.to_s.split(separator).inject([]) do |keys, key|
|
64
|
+
keys << [keys.last, key].compact.join(separator)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# This module is intended to be mixed into the ActiveRecord backend to allow
|
2
|
+
# storing Ruby Procs as translation values in the database.
|
3
|
+
#
|
4
|
+
# I18n.backend = I18n::Backend::ActiveRecord.new
|
5
|
+
# I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs)
|
6
|
+
#
|
7
|
+
# The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor
|
8
|
+
# was extracted from the original backend.
|
9
|
+
#
|
10
|
+
# ParseTree is not compatible with Ruby 1.9.
|
11
|
+
module I18n
|
12
|
+
module Backend
|
13
|
+
class ActiveRecord
|
14
|
+
module StoreProcs
|
15
|
+
unless RUBY_VERSION >= '1.9'
|
16
|
+
class << self
|
17
|
+
def included(target)
|
18
|
+
require 'ruby2ruby'
|
19
|
+
require 'parse_tree'
|
20
|
+
require 'parse_tree_extensions'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def value=(v)
|
25
|
+
case v
|
26
|
+
when Proc
|
27
|
+
write_attribute(:value, v.to_ruby)
|
28
|
+
write_attribute(:is_proc, true)
|
29
|
+
else
|
30
|
+
write_attribute(:value, v)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Backend
|
5
|
+
# ActiveRecord model used to store actual translations to the database.
|
6
|
+
#
|
7
|
+
# This model expects a table like the following to be already set up in
|
8
|
+
# your the database:
|
9
|
+
#
|
10
|
+
# create_table :translations do |t|
|
11
|
+
# t.string :locale
|
12
|
+
# t.string :key
|
13
|
+
# t.string :value
|
14
|
+
# t.boolean :is_proc, :default => false
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# This model supports to named scopes :locale and :lookup. The :locale
|
18
|
+
# scope simply adds a condition for a given locale:
|
19
|
+
#
|
20
|
+
# I18n::Backend::ActiveRecord::Translation.locale(:en).all
|
21
|
+
# # => all translation records that belong to the :en locale
|
22
|
+
#
|
23
|
+
# The :lookup scope adds a condition for looking up all translations
|
24
|
+
# that either start with the given keys (joined by an optionally given
|
25
|
+
# separator or I18n.default_separator) or that exactly have this key.
|
26
|
+
#
|
27
|
+
# # with translations present for :"foo.bar" and :"foo.baz"
|
28
|
+
# I18n::Backend::ActiveRecord::Translation.lookup(:foo)
|
29
|
+
# # => an array with both translation records :"foo.bar" and :"foo.baz"
|
30
|
+
#
|
31
|
+
# I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar])
|
32
|
+
# I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar")
|
33
|
+
# # => an array with the translation record :"foo.bar"
|
34
|
+
#
|
35
|
+
# When the StoreProcs module was mixed into this model then Procs will
|
36
|
+
# be stored to the database as Ruby code and evaluated when :value is
|
37
|
+
# called.
|
38
|
+
#
|
39
|
+
# Translation = I18n::Backend::ActiveRecord::Translation
|
40
|
+
# Translation.create \
|
41
|
+
# :locale => 'en'
|
42
|
+
# :key => 'foo'
|
43
|
+
# :value => lambda { |key, options| 'FOO' }
|
44
|
+
# Translation.find_by_locale_and_key('en', 'foo').value
|
45
|
+
# # => 'FOO'
|
46
|
+
class ActiveRecord
|
47
|
+
class Translation < ::ActiveRecord::Base
|
48
|
+
set_table_name 'translations'
|
49
|
+
attr_protected :is_proc, :interpolations
|
50
|
+
|
51
|
+
serialize :value
|
52
|
+
serialize :interpolations, Array
|
53
|
+
|
54
|
+
named_scope :locale, lambda { |locale|
|
55
|
+
{ :conditions => { :locale => locale.to_s } }
|
56
|
+
}
|
57
|
+
|
58
|
+
named_scope :lookup, lambda { |keys, *separator|
|
59
|
+
keys = Array(keys).map! { |key| key.to_s }
|
60
|
+
separator ||= I18n.default_separator
|
61
|
+
{ :conditions => ["`key` IN (?) OR `key` LIKE '#{keys.last}#{separator}%'", keys] }
|
62
|
+
}
|
63
|
+
|
64
|
+
def self.available_locales
|
65
|
+
Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale }
|
66
|
+
end
|
67
|
+
|
68
|
+
def interpolates?(key)
|
69
|
+
self.interpolations.include?(key) if self.interpolations
|
70
|
+
end
|
71
|
+
|
72
|
+
def value
|
73
|
+
if is_proc
|
74
|
+
Kernel.eval read_attribute(:value)
|
75
|
+
else
|
76
|
+
read_attribute(:value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# This extension stores untranslated keys as translation stubs in the database. This is useful if you have a web based
|
3
|
+
# translation tool. It will populate with untranslated keys as the application is being used. A translator can then go
|
4
|
+
# through these. Example usage:
|
5
|
+
#
|
6
|
+
# I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecordMissing)
|
7
|
+
# I18n.backend = I18nChainBackend.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
|
8
|
+
#
|
9
|
+
# Stubs for pluralizations will also be created for each key defined in i18n.plural_keys. Eg:
|
10
|
+
#
|
11
|
+
# en:
|
12
|
+
# i18n:
|
13
|
+
# plural_keys: [:zero, :one, :other]
|
14
|
+
#
|
15
|
+
# pl:
|
16
|
+
# i18n:
|
17
|
+
# plural_keys: [:zero, :one, :few, :other]
|
18
|
+
#
|
19
|
+
# This extension can also persist interpolation keys in Translation#interpolations. This is useful for translators
|
20
|
+
# who cannot infer possible interpolations from the keys, as they can with other solutions such as gettext or globalize.
|
21
|
+
#
|
22
|
+
module I18n
|
23
|
+
module Backend
|
24
|
+
module ActiveRecordMissing
|
25
|
+
|
26
|
+
def store_default_translations(locale, key, options = {})
|
27
|
+
count, scope, default, separator = options.values_at(:count, *Base::RESERVED_KEYS)
|
28
|
+
separator ||= I18n.default_separator
|
29
|
+
keys = I18n.send(:normalize_translation_keys, locale, key, scope, separator)[1..-1]
|
30
|
+
key = keys.join(separator || I18n.default_separator)
|
31
|
+
|
32
|
+
unless ActiveRecord::Translation.locale(locale).lookup(key, separator).exists?
|
33
|
+
interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys
|
34
|
+
keys = count ? I18n.t('i18n.plural_keys', :locale => locale).map { |k| [key, k].join(separator) } : [key]
|
35
|
+
keys.each { |key| store_default_translation(locale, key, interpolations) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def store_default_translation(locale, key, interpolations)
|
40
|
+
translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key
|
41
|
+
translation.interpolations = interpolations
|
42
|
+
translation.save
|
43
|
+
end
|
44
|
+
|
45
|
+
def translate(locale, key, options = {})
|
46
|
+
super
|
47
|
+
|
48
|
+
rescue I18n::MissingTranslationData => e
|
49
|
+
self.store_default_translations(locale, key, options)
|
50
|
+
|
51
|
+
raise e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|