thedarkone-i18n 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.textile +57 -0
- data/README.textile +43 -9
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/lib/i18n.rb +87 -16
- data/lib/i18n/backend/base.rb +251 -0
- data/lib/i18n/backend/cache.rb +71 -0
- data/lib/i18n/backend/chain.rb +64 -0
- data/lib/i18n/backend/fallbacks.rb +53 -0
- data/lib/i18n/backend/fast.rb +53 -22
- data/lib/i18n/backend/fast/interpolation_compiler.rb +84 -0
- data/lib/i18n/backend/gettext.rb +65 -0
- data/lib/i18n/backend/lazy_reloading.rb +60 -0
- data/lib/i18n/backend/pluralization.rb +56 -0
- data/lib/i18n/backend/simple.rb +17 -240
- data/lib/i18n/exceptions.rb +13 -5
- data/lib/i18n/gettext.rb +25 -0
- data/lib/i18n/helpers/gettext.rb +35 -0
- data/lib/i18n/locale/fallbacks.rb +100 -0
- data/lib/i18n/locale/tag.rb +27 -0
- data/lib/i18n/locale/tag/parents.rb +24 -0
- data/lib/i18n/locale/tag/rfc4646.rb +78 -0
- data/lib/i18n/locale/tag/simple.rb +44 -0
- data/test/all.rb +5 -7
- data/test/api/basics.rb +15 -0
- data/test/api/interpolation.rb +85 -0
- data/test/api/lambda.rb +52 -0
- data/test/api/link.rb +47 -0
- data/test/api/localization/date.rb +65 -0
- data/test/api/localization/date_time.rb +63 -0
- data/test/api/localization/lambda.rb +26 -0
- data/test/api/localization/time.rb +63 -0
- data/test/api/pluralization.rb +37 -0
- data/test/api/translation.rb +51 -0
- data/test/backend/cache/cache_test.rb +57 -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 +79 -0
- data/test/backend/fallbacks/fallbacks_test.rb +29 -0
- data/test/backend/fast/all.rb +5 -0
- data/test/backend/fast/api_test.rb +91 -0
- data/test/backend/fast/interpolation_compiler_test.rb +84 -0
- data/test/backend/fast/lookup_test.rb +24 -0
- data/test/backend/fast/setup.rb +22 -0
- data/test/backend/fast/translations_test.rb +89 -0
- data/test/backend/lazy_reloading/reloading_test.rb +67 -0
- data/test/backend/pluralization/api_test.rb +81 -0
- data/test/backend/pluralization/pluralization_test.rb +39 -0
- data/test/backend/simple/all.rb +5 -0
- data/test/backend/simple/api_test.rb +90 -0
- data/test/backend/simple/lookup_test.rb +24 -0
- data/test/backend/simple/setup.rb +151 -0
- data/test/backend/simple/translations_test.rb +89 -0
- data/test/fixtures/locales/de.po +61 -0
- data/test/fixtures/locales/en.rb +3 -0
- data/test/fixtures/locales/en.yml +3 -0
- data/test/fixtures/locales/plurals.rb +112 -0
- data/test/gettext/api_test.rb +78 -0
- data/test/gettext/backend_test.rb +35 -0
- data/test/i18n_exceptions_test.rb +6 -25
- data/test/i18n_load_path_test.rb +23 -0
- data/test/i18n_test.rb +56 -18
- data/test/locale/fallbacks_test.rb +128 -0
- data/test/locale/tag/rfc4646_test.rb +147 -0
- data/test/locale/tag/simple_test.rb +35 -0
- data/test/test_helper.rb +72 -0
- data/test/with_options.rb +34 -0
- metadata +109 -19
- data/i18n.gemspec +0 -31
- data/lib/i18n/backend/fast/pluralization_compiler.rb +0 -50
- data/test/backend_test.rb +0 -633
- data/test/fast_backend_test.rb +0 -34
- data/test/locale/en.rb +0 -1
- data/test/locale/en.yml +0 -3
- data/test/pluralization_compiler_test.rb +0 -35
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This module allows you to easily cache all responses from the backend - thus
|
4
|
+
# speeding up the I18n aspects of your application quite a bit.
|
5
|
+
#
|
6
|
+
# To enable caching you can simply include the Cache module to the Simple
|
7
|
+
# backend - or whatever other backend you are using:
|
8
|
+
#
|
9
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Cache)
|
10
|
+
#
|
11
|
+
# You will also need to set a cache store implementation that you want to use:
|
12
|
+
#
|
13
|
+
# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
|
14
|
+
#
|
15
|
+
# You can use any cache implementation you want that provides the same API as
|
16
|
+
# ActiveSupport::Cache (only the methods #fetch and #write are being used).
|
17
|
+
#
|
18
|
+
# The cache_key implementation assumes that you only pass values to
|
19
|
+
# I18n.translate that return a valid key from #hash (see
|
20
|
+
# http://www.ruby-doc.org/core/classes/Object.html#M000337).
|
21
|
+
module I18n
|
22
|
+
class << self
|
23
|
+
@@cache_store = nil
|
24
|
+
@@cache_namespace = nil
|
25
|
+
|
26
|
+
def cache_store
|
27
|
+
@@cache_store
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_store=(store)
|
31
|
+
@@cache_store = store
|
32
|
+
end
|
33
|
+
|
34
|
+
def cache_namespace
|
35
|
+
@@cache_namespace
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_namespace=(namespace)
|
39
|
+
@@cache_namespace = namespace
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform_caching?
|
43
|
+
!cache_store.nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Backend
|
48
|
+
module Cache
|
49
|
+
def translate(*args)
|
50
|
+
I18n.perform_caching? ? fetch(*args) { super } : super
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def fetch(*args, &block)
|
56
|
+
result = I18n.cache_store.fetch(cache_key(*args), &block)
|
57
|
+
raise result if result.is_a?(Exception)
|
58
|
+
result
|
59
|
+
rescue MissingTranslationData => exception
|
60
|
+
I18n.cache_store.write(cache_key(*args), exception)
|
61
|
+
raise exception
|
62
|
+
end
|
63
|
+
|
64
|
+
def cache_key(*args)
|
65
|
+
# this assumes that only simple, native Ruby values are passed to I18n.translate
|
66
|
+
keys = ['i18n', I18n.cache_namespace, args.hash]
|
67
|
+
keys.compact.join('-')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Backend
|
5
|
+
# Backend that chains multiple other backends and checks each of them when
|
6
|
+
# a translation needs to be looked up. This is useful when you want to use
|
7
|
+
# standard translations with a Simple backend but store custom application
|
8
|
+
# translations in a database or other backends.
|
9
|
+
#
|
10
|
+
# To use the Chain backend instantiate it and set it to the I18n module.
|
11
|
+
# You can add chained backends through the initializer or backends
|
12
|
+
# accessor:
|
13
|
+
#
|
14
|
+
# # preserves the existing Simple backend set to I18n.backend
|
15
|
+
# I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
|
16
|
+
#
|
17
|
+
# The implementation assumes that all backends added to the Chain implement
|
18
|
+
# a lookup method with the same API as Simple backend does.
|
19
|
+
class Chain < Base
|
20
|
+
attr_accessor :backends
|
21
|
+
|
22
|
+
def initialize(*backends)
|
23
|
+
self.backends = backends
|
24
|
+
end
|
25
|
+
|
26
|
+
def reload!
|
27
|
+
backends.each { |backend| backend.reload! }
|
28
|
+
end
|
29
|
+
|
30
|
+
def store_translations(locale, data)
|
31
|
+
backends.first.store_translations(locale, data)
|
32
|
+
end
|
33
|
+
|
34
|
+
def available_locales
|
35
|
+
backends.map { |backend| backend.available_locales }.flatten.uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
def localize(locale, object, format = :default, options = {})
|
39
|
+
backends.each do |backend|
|
40
|
+
begin
|
41
|
+
result = backend.localize(locale, object, format, options) and return result
|
42
|
+
rescue MissingTranslationData
|
43
|
+
end
|
44
|
+
end or nil
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def lookup(locale, key, scope = [], separator = nil)
|
50
|
+
return unless key
|
51
|
+
result = {}
|
52
|
+
backends.each do |backend|
|
53
|
+
entry = backend.lookup(locale, key, scope, separator)
|
54
|
+
if entry.is_a?(Hash)
|
55
|
+
result.merge!(entry)
|
56
|
+
elsif !entry.nil?
|
57
|
+
return entry
|
58
|
+
end
|
59
|
+
end
|
60
|
+
result.empty? ? nil : result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'i18n/locale/fallbacks'
|
4
|
+
|
5
|
+
# I18n locale fallbacks are useful when you want your application to use
|
6
|
+
# translations from other locales when translations for the current locale are
|
7
|
+
# missing. E.g. you might want to use :en translations when translations in
|
8
|
+
# your applications main locale :de are missing.
|
9
|
+
#
|
10
|
+
# To enable locale fallbacks you can simply include the Fallbacks module to
|
11
|
+
# the Simple backend - or whatever other backend you are using:
|
12
|
+
#
|
13
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
14
|
+
module I18n
|
15
|
+
@@fallbacks = nil
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
|
19
|
+
def fallbacks
|
20
|
+
@@fallbacks ||= I18n::Locale::Fallbacks.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
|
24
|
+
def fallbacks=(fallbacks)
|
25
|
+
@@fallbacks = fallbacks
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Backend
|
30
|
+
module Fallbacks
|
31
|
+
# Overwrites the Base backend translate method so that it will try each
|
32
|
+
# locale given by I18n.fallbacks for the given locale. E.g. for the
|
33
|
+
# locale :"de-DE" it might try the locales :"de-DE", :de and :en
|
34
|
+
# (depends on the fallbacks implementation) until it finds a result with
|
35
|
+
# the given options. If it does not find any result for any of the
|
36
|
+
# locales it will then raise a MissingTranslationData exception as
|
37
|
+
# usual.
|
38
|
+
#
|
39
|
+
# The default option takes precedence over fallback locales, i.e. it
|
40
|
+
# will first evaluate a given default option before falling back to
|
41
|
+
# another locale.
|
42
|
+
def translate(locale, key, options = {})
|
43
|
+
I18n.fallbacks[locale].each do |fallback|
|
44
|
+
begin
|
45
|
+
result = super(fallback, key, options) and return result
|
46
|
+
rescue I18n::MissingTranslationData
|
47
|
+
end
|
48
|
+
end
|
49
|
+
raise(I18n::MissingTranslationData.new(locale, key, options))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/i18n/backend/fast.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
+
require 'i18n/backend/fast/interpolation_compiler'
|
2
|
+
|
1
3
|
module I18n
|
2
4
|
module Backend
|
3
|
-
class Fast <
|
4
|
-
|
5
|
-
# append any your custom pluralization keys to the constant
|
6
|
-
PLURALIZATION_KEYS = [:zero, :one, :other]
|
5
|
+
class Fast < Base
|
6
|
+
SEPARATOR_ESCAPE_CHAR = "\001"
|
7
7
|
|
8
8
|
def reset_flattened_translations!
|
9
9
|
@flattened_translations = nil
|
@@ -22,28 +22,45 @@ module I18n
|
|
22
22
|
super
|
23
23
|
reset_flattened_translations!
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
|
+
def translate(locale, key, opts = nil)
|
27
|
+
raise InvalidLocale.new(locale) unless locale
|
28
|
+
return key.map { |k| translate(locale, k, opts) } if key.is_a?(Array)
|
29
|
+
|
30
|
+
if opts
|
31
|
+
count = opts[:count]
|
32
|
+
scope = opts[:scope]
|
33
|
+
|
34
|
+
if entry = lookup(locale, key, scope, opts[:separator]) || ((default = opts.delete(:default)) && default(locale, key, default, opts))
|
35
|
+
entry = resolve(locale, key, entry, opts)
|
36
|
+
entry = pluralize(locale, entry, count) if count
|
37
|
+
entry = interpolate(locale, entry, opts)
|
38
|
+
entry
|
39
|
+
end
|
40
|
+
else
|
41
|
+
resolve(locale, key, lookup(locale, key), opts)
|
42
|
+
end || raise(I18n::MissingTranslationData.new(locale, key, opts))
|
43
|
+
end
|
44
|
+
|
26
45
|
protected
|
27
|
-
# {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}}
|
46
|
+
# flatten_hash({:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}})
|
47
|
+
# # => {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"}
|
28
48
|
def flatten_hash(h, nested_stack = [], flattened_h = {})
|
29
49
|
h.each_pair do |k, v|
|
30
|
-
new_nested_stack = nested_stack + [k]
|
31
|
-
|
32
|
-
if v.kind_of?(Hash)
|
33
|
-
# short circuit pluralization hashes
|
34
|
-
flattened_h[nested_stack_to_flat_key(new_nested_stack)] = v if (v.keys & PLURALIZATION_KEYS).any?
|
35
|
-
|
36
|
-
flatten_hash(v, new_nested_stack, flattened_h)
|
37
|
-
else
|
38
|
-
flattened_h[nested_stack_to_flat_key(new_nested_stack)] = PluralizationCompiler.compile_if_an_interpolation(v)
|
39
|
-
end
|
50
|
+
new_nested_stack = nested_stack + [escape_default_separator(k)]
|
51
|
+
flattened_h[nested_stack_to_flat_key(new_nested_stack)] = InterpolationCompiler.compile_if_an_interpolation(v)
|
52
|
+
flatten_hash(v, new_nested_stack, flattened_h) if v.kind_of?(Hash)
|
40
53
|
end
|
41
54
|
|
42
55
|
flattened_h
|
43
56
|
end
|
57
|
+
|
58
|
+
def escape_default_separator(key)
|
59
|
+
key.to_s.tr(I18n.default_separator, SEPARATOR_ESCAPE_CHAR)
|
60
|
+
end
|
44
61
|
|
45
62
|
def nested_stack_to_flat_key(nested_stack)
|
46
|
-
nested_stack.join(
|
63
|
+
nested_stack.join(I18n.default_separator).to_sym
|
47
64
|
end
|
48
65
|
|
49
66
|
def flatten_translations(translations)
|
@@ -53,14 +70,28 @@ module I18n
|
|
53
70
|
flattened_h
|
54
71
|
end
|
55
72
|
end
|
56
|
-
|
57
|
-
def interpolate(string, values)
|
58
|
-
string.respond_to?(:i18n_interpolate)
|
73
|
+
|
74
|
+
def interpolate(locale, string, values)
|
75
|
+
if string.respond_to?(:i18n_interpolate)
|
76
|
+
string.i18n_interpolate(values)
|
77
|
+
elsif values
|
78
|
+
super
|
79
|
+
else
|
80
|
+
string
|
81
|
+
end
|
59
82
|
end
|
60
83
|
|
61
|
-
def lookup(locale, key, scope = nil)
|
84
|
+
def lookup(locale, key, scope = nil, separator = nil)
|
62
85
|
init_translations unless @initialized
|
63
|
-
|
86
|
+
if separator
|
87
|
+
key = cleanup_non_standard_separator(key, separator)
|
88
|
+
scope = Array(scope).map{|k| cleanup_non_standard_separator(k, separator)} if scope
|
89
|
+
end
|
90
|
+
flattened_translations[locale.to_sym][(scope ? (Array(scope) + [key]).join(I18n.default_separator) : key).to_sym] rescue nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def cleanup_non_standard_separator(key, user_separator)
|
94
|
+
escape_default_separator(key).tr(user_separator, I18n.default_separator)
|
64
95
|
end
|
65
96
|
end
|
66
97
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module I18n
|
2
|
+
module Backend
|
3
|
+
class Fast < Base
|
4
|
+
module InterpolationCompiler
|
5
|
+
extend self
|
6
|
+
|
7
|
+
TOKENIZER = /(\\\{\{[^\}]+\}\}|\{\{[^\}]+\}\})/
|
8
|
+
INTERPOLATION_SYNTAX_PATTERN = /(\\)?(\{\{([^\}]+)\}\})/
|
9
|
+
|
10
|
+
def compile_if_an_interpolation(string)
|
11
|
+
if interpolated_str?(string)
|
12
|
+
string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__
|
13
|
+
def i18n_interpolate(v = {})
|
14
|
+
"#{compiled_interpolation_body(string)}"
|
15
|
+
end
|
16
|
+
RUBY_EVAL
|
17
|
+
end
|
18
|
+
|
19
|
+
string
|
20
|
+
end
|
21
|
+
|
22
|
+
def interpolated_str?(str)
|
23
|
+
str.kind_of?(String) && str =~ INTERPOLATION_SYNTAX_PATTERN
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
# tokenize("foo {{bar}} baz \\{{buz}}") # => ["foo ", "{{bar}}", " baz ", "\\{{buz}}"]
|
28
|
+
def tokenize(str)
|
29
|
+
str.split(TOKENIZER)
|
30
|
+
end
|
31
|
+
|
32
|
+
def compiled_interpolation_body(str)
|
33
|
+
tokenize(str).map do |token|
|
34
|
+
(matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token)
|
35
|
+
end.join
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_interpolation_token(interpolation, matchdata)
|
39
|
+
escaped, pattern, key = matchdata.values_at(1, 2, 3)
|
40
|
+
escaped ? pattern : compile_interpolation_token(key.to_sym)
|
41
|
+
end
|
42
|
+
|
43
|
+
def compile_interpolation_token(key)
|
44
|
+
"\#{#{interpolate_or_raise_missing(key)}}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def interpolate_or_raise_missing(key)
|
48
|
+
escaped_key = escape_key_sym(key)
|
49
|
+
Base::RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def interpolate_key(key)
|
53
|
+
[direct_key(key), nil_key(key), missing_key(key)].join('||')
|
54
|
+
end
|
55
|
+
|
56
|
+
def direct_key(key)
|
57
|
+
"((t = v[#{key}]) && t.respond_to?(:call) ? t.call : t)"
|
58
|
+
end
|
59
|
+
|
60
|
+
def nil_key(key)
|
61
|
+
"(v.has_key?(#{key}) && '')"
|
62
|
+
end
|
63
|
+
|
64
|
+
def missing_key(key)
|
65
|
+
"raise(MissingInterpolationArgument.new(#{key}, self))"
|
66
|
+
end
|
67
|
+
|
68
|
+
def reserved_key(key)
|
69
|
+
"raise(ReservedInterpolationKey.new(#{key}, self))"
|
70
|
+
end
|
71
|
+
|
72
|
+
def escape_plain_str(str)
|
73
|
+
str.gsub(/"|\\|#/) {|x| "\\#{x}"}
|
74
|
+
end
|
75
|
+
|
76
|
+
def escape_key_sym(key)
|
77
|
+
# rely on Ruby to do all the hard work :)
|
78
|
+
key.to_sym.inspect
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'i18n/gettext'
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../vendor/po_parser.rb')
|
5
|
+
|
6
|
+
# Experimental support for using Gettext po files to store translations.
|
7
|
+
#
|
8
|
+
# To use this you can simply include the module to the Simple backend - or
|
9
|
+
# whatever other backend you are using.
|
10
|
+
#
|
11
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Gettext)
|
12
|
+
#
|
13
|
+
# Now you should be able to include your Gettext translation (*.po) files to
|
14
|
+
# the I18n.load_path so they're loaded to the backend and you can use them as
|
15
|
+
# usual:
|
16
|
+
#
|
17
|
+
# I18n.load_path += Dir["path/to/locales/*.po"]
|
18
|
+
#
|
19
|
+
# Following the Gettext convention this implementation expects that your
|
20
|
+
# translation files are named by their locales. E.g. the file en.po would
|
21
|
+
# contain the translations for the English locale.
|
22
|
+
module I18n
|
23
|
+
module Backend
|
24
|
+
module Gettext
|
25
|
+
class PoData < Hash
|
26
|
+
def set_comment(msgid_or_sym, comment)
|
27
|
+
# ignore
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def load_po(filename)
|
33
|
+
locale = ::File.basename(filename, '.po').to_sym
|
34
|
+
data = normalize(locale, parse(filename))
|
35
|
+
{ locale => data }
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse(filename)
|
39
|
+
GetText::PoParser.new.parse(::File.read(filename), PoData.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
def normalize(locale, data)
|
43
|
+
data.inject({}) do |result, (key, value)|
|
44
|
+
key, value = normalize_pluralization(locale, key, value) if key.index("\000")
|
45
|
+
result[key] = value
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def normalize_pluralization(locale, key, value)
|
51
|
+
# FIXME po_parser includes \000 chars that can not be turned into Symbols
|
52
|
+
key = key.dup.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR)
|
53
|
+
|
54
|
+
keys = I18n::Gettext.plural_keys(locale)
|
55
|
+
values = value.split("\000")
|
56
|
+
raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size
|
57
|
+
|
58
|
+
result = {}
|
59
|
+
values.each_with_index { |value, ix| result[keys[ix]] = value }
|
60
|
+
[key, result]
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|