activesupport 2.3.5 → 2.3.6.pre
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- data/CHANGELOG +23 -0
- data/lib/active_support.rb +1 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +14 -3
- data/lib/active_support/core_ext/enumerable.rb +6 -0
- data/lib/active_support/core_ext/file/atomic.rb +2 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/object/metaclass.rb +6 -5
- data/lib/active_support/core_ext/object/singleton_class.rb +13 -0
- data/lib/active_support/core_ext/string.rb +0 -1
- data/lib/active_support/core_ext/string/output_safety.rb +148 -44
- data/lib/active_support/core_ext/time/calculations.rb +1 -1
- data/lib/active_support/inflector.rb +1 -1
- data/lib/active_support/json/backends/yajl.rb +40 -0
- data/lib/active_support/json/decoding.rb +16 -1
- data/lib/active_support/json/encoding.rb +11 -2
- data/lib/active_support/ordered_hash.rb +24 -1
- data/lib/active_support/vendor.rb +10 -2
- data/lib/active_support/vendor/i18n-0.3.3/CHANGELOG.textile +76 -0
- data/lib/active_support/vendor/{i18n-0.1.3 → i18n-0.3.3}/MIT-LICENSE +0 -0
- data/lib/active_support/vendor/i18n-0.3.3/README.textile +81 -0
- data/lib/active_support/vendor/i18n-0.3.3/Rakefile +24 -0
- data/lib/active_support/vendor/i18n-0.3.3/benchmark/example.yml +144 -0
- data/lib/active_support/vendor/i18n-0.3.3/benchmark/run.rb +71 -0
- data/lib/active_support/vendor/i18n-0.3.3/contributors.txt +17 -0
- data/lib/active_support/vendor/i18n-0.3.3/i18n.gemspec +165 -0
- data/lib/active_support/vendor/i18n-0.3.3/init.rb +1 -0
- data/lib/active_support/vendor/{i18n-0.1.3 → i18n-0.3.3}/lib/i18n.rb +99 -21
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend.rb +17 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record.rb +70 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/missing.rb +67 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/store_procs.rb +38 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/translation.rb +83 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/base.rb +259 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cache.rb +75 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb +44 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/chain.rb +74 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fallbacks.rb +52 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fast.rb +68 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb +75 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb +80 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb +119 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb +73 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/pluralization.rb +57 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/simple.rb +22 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/object/meta_class.rb +5 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/string/interpolate.rb +99 -0
- data/lib/active_support/vendor/{i18n-0.1.3 → i18n-0.3.3}/lib/i18n/exceptions.rb +14 -6
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/gettext.rb +25 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers.rb +5 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb +64 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale.rb +6 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb +98 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag.rb +28 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/parents.rb +24 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb +76 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/simple.rb +41 -0
- data/lib/active_support/vendor/i18n-0.3.3/lib/i18n/version.rb +3 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/all.rb +8 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/basics.rb +15 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/defaults.rb +40 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/interpolation.rb +92 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/link.rb +55 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date.rb +91 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date_time.rb +90 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/localization/procs.rb +54 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/localization/time.rb +84 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/lookup.rb +45 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/pluralization.rb +35 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/api/procs.rb +40 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/active_record_test.rb +29 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/all_features_test.rb +40 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/cascade_test.rb +31 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/chain_test.rb +26 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fallbacks_test.rb +33 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fast_test.rb +31 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/pluralization_test.rb +33 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/api/simple_test.rb +21 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record/missing_test.rb +60 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record_test.rb +52 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cache_test.rb +72 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cascade_test.rb +66 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/chain_test.rb +64 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fallbacks_test.rb +57 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fast_test.rb +50 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/helpers_test.rb +26 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/interpolation_compiler_test.rb +107 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/metadata_test.rb +67 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/pluralization_test.rb +43 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/simple_test.rb +77 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/core_ext/string/interpolate_test.rb +94 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/api_test.rb +201 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/backend_test.rb +91 -0
- data/lib/active_support/vendor/{i18n-0.1.3/test → i18n-0.3.3/test/cases}/i18n_exceptions_test.rb +8 -10
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_load_path_test.rb +23 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_test.rb +172 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/fallbacks_test.rb +126 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/rfc4646_test.rb +143 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/simple_test.rb +33 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/de.po +72 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.rb +3 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.yml +3 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/plurals.rb +113 -0
- data/lib/active_support/vendor/i18n-0.3.3/test/test_helper.rb +100 -0
- data/lib/active_support/vendor/i18n-0.3.3/vendor/po_parser.rb +329 -0
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/whiny_nil.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +23 -83
- data/lib/active_support/xml_mini/libxmlsax.rb +74 -0
- data/lib/active_support/xml_mini/nokogiri.rb +25 -22
- data/lib/active_support/xml_mini/nokogirisax.rb +73 -0
- metadata +108 -20
- data/lib/active_support/vendor/i18n-0.1.3/README.textile +0 -20
- data/lib/active_support/vendor/i18n-0.1.3/Rakefile +0 -5
- data/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec +0 -27
- data/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb +0 -214
- data/lib/active_support/vendor/i18n-0.1.3/test/all.rb +0 -5
- data/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +0 -124
- data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb +0 -1
- data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml +0 -3
- data/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +0 -567
@@ -0,0 +1,75 @@
|
|
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
|
+
# Also, in Ruby < 1.8.7 {}.hash != {}.hash
|
67
|
+
# (see http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7)
|
68
|
+
# If args.inspect does not work for you for some reason, patches are very welcome :)
|
69
|
+
hash = RUBY_VERSION >= "1.8.7" ? args.hash : args.inspect
|
70
|
+
keys = ['i18n', I18n.cache_namespace, hash]
|
71
|
+
keys.compact.join('-')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# EXPERIMENTAL
|
4
|
+
#
|
5
|
+
# The cascade module adds the ability to do cascading lookups to backends that
|
6
|
+
# are compatible to the Simple backend.
|
7
|
+
#
|
8
|
+
# By cascading lookups we mean that for any key that can not be found the
|
9
|
+
# Cascade module strips one segment off the scope part of the key and then
|
10
|
+
# tries to look up the key in that scope.
|
11
|
+
#
|
12
|
+
# E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then
|
13
|
+
# the segment :bar will be stripped off the scope part :"foo.bar" and the new
|
14
|
+
# scope :foo will be used to look up the key :baz. If that does not succeed
|
15
|
+
# then the remaining scope segment :foo will be omitted, too, and again the
|
16
|
+
# key :baz will be looked up (now with no scope).
|
17
|
+
#
|
18
|
+
# Defaults will only kick in after the cascading lookups haven't succeeded.
|
19
|
+
#
|
20
|
+
# This behavior is useful for libraries like ActiveRecord validations where
|
21
|
+
# the library wants to give users a bunch of more or less fine-grained options
|
22
|
+
# of scopes for a particular key.
|
23
|
+
#
|
24
|
+
# Thanks to Clemens Kofler for the initial idea and implementation! See
|
25
|
+
# http://github.com/clemens/i18n-cascading-backend
|
26
|
+
|
27
|
+
module I18n
|
28
|
+
@@fallbacks = nil
|
29
|
+
|
30
|
+
module Backend
|
31
|
+
module Cascade
|
32
|
+
def lookup(locale, key, scope = [], separator = nil)
|
33
|
+
return unless key
|
34
|
+
locale, *scope = I18n.send(:normalize_translation_keys, locale, key, scope, separator)
|
35
|
+
key = scope.pop
|
36
|
+
|
37
|
+
begin
|
38
|
+
result = super
|
39
|
+
return result unless result.nil?
|
40
|
+
end while scope.pop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,74 @@
|
|
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 < Simple
|
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, options = {})
|
31
|
+
backends.first.store_translations(locale, data, options = {})
|
32
|
+
end
|
33
|
+
|
34
|
+
def available_locales
|
35
|
+
backends.map { |backend| backend.available_locales }.flatten.uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
def translate(locale, key, options = {})
|
39
|
+
return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
|
40
|
+
|
41
|
+
default = options.delete(:default)
|
42
|
+
namespace = {}
|
43
|
+
backends.each do |backend|
|
44
|
+
begin
|
45
|
+
options.update(:default => default) if default and backend == backends.last
|
46
|
+
translation = backend.translate(locale, key, options)
|
47
|
+
if namespace_lookup?(translation, options)
|
48
|
+
namespace.update(translation)
|
49
|
+
elsif !translation.nil?
|
50
|
+
return translation
|
51
|
+
end
|
52
|
+
rescue MissingTranslationData
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return namespace unless namespace.empty?
|
56
|
+
raise(I18n::MissingTranslationData.new(locale, key, options))
|
57
|
+
end
|
58
|
+
|
59
|
+
def localize(locale, object, format = :default, options = {})
|
60
|
+
backends.each do |backend|
|
61
|
+
begin
|
62
|
+
result = backend.localize(locale, object, format, options) and return result
|
63
|
+
rescue MissingTranslationData
|
64
|
+
end
|
65
|
+
end and nil
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def namespace_lookup?(result, options)
|
70
|
+
result.is_a?(Hash) and not options.has_key?(:count)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# I18n locale fallbacks are useful when you want your application to use
|
4
|
+
# translations from other locales when translations for the current locale are
|
5
|
+
# missing. E.g. you might want to use :en translations when translations in
|
6
|
+
# your applications main locale :de are missing.
|
7
|
+
#
|
8
|
+
# To enable locale fallbacks you can simply include the Fallbacks module to
|
9
|
+
# the Simple backend - or whatever other backend you are using:
|
10
|
+
#
|
11
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
12
|
+
module I18n
|
13
|
+
@@fallbacks = nil
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
|
17
|
+
def fallbacks
|
18
|
+
@@fallbacks ||= I18n::Locale::Fallbacks.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
|
22
|
+
def fallbacks=(fallbacks)
|
23
|
+
@@fallbacks = fallbacks
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Backend
|
28
|
+
module Fallbacks
|
29
|
+
# Overwrites the Base backend translate method so that it will try each
|
30
|
+
# locale given by I18n.fallbacks for the given locale. E.g. for the
|
31
|
+
# locale :"de-DE" it might try the locales :"de-DE", :de and :en
|
32
|
+
# (depends on the fallbacks implementation) until it finds a result with
|
33
|
+
# the given options. If it does not find any result for any of the
|
34
|
+
# locales it will then raise a MissingTranslationData exception as
|
35
|
+
# usual.
|
36
|
+
#
|
37
|
+
# The default option takes precedence over fallback locales, i.e. it
|
38
|
+
# will first evaluate a given default option before falling back to
|
39
|
+
# another locale.
|
40
|
+
def translate(locale, key, options = {})
|
41
|
+
I18n.fallbacks[locale].each do |fallback|
|
42
|
+
begin
|
43
|
+
result = super(fallback, key, options)
|
44
|
+
return result unless result.nil?
|
45
|
+
rescue I18n::MissingTranslationData
|
46
|
+
end
|
47
|
+
end
|
48
|
+
raise(I18n::MissingTranslationData.new(locale, key, options))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The Fast module contains optimizations that can tremendously speed up the
|
4
|
+
# lookup process on the Simple backend. It works by flattening the nested
|
5
|
+
# translation hash to a flat hash (e.g. { :a => { :b => 'c' } } becomes
|
6
|
+
# { :'a.b' => 'c' }).
|
7
|
+
#
|
8
|
+
# To enable these optimizations you can simply include the Fast module to
|
9
|
+
# the Simple backend:
|
10
|
+
#
|
11
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Fast)
|
12
|
+
module I18n
|
13
|
+
module Backend
|
14
|
+
module Fast
|
15
|
+
SEPARATOR_ESCAPE_CHAR = "\001"
|
16
|
+
|
17
|
+
def reset_flattened_translations!
|
18
|
+
@flattened_translations = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def flattened_translations
|
22
|
+
@flattened_translations ||= flatten_translations(translations)
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge_translations(locale, data)
|
26
|
+
super
|
27
|
+
reset_flattened_translations!
|
28
|
+
end
|
29
|
+
|
30
|
+
def init_translations
|
31
|
+
super
|
32
|
+
reset_flattened_translations!
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
# flatten_hash({:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}})
|
37
|
+
# # => {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"}
|
38
|
+
def flatten_hash(h, nested_stack = [], flattened_h = {}, orig_h=h)
|
39
|
+
wind_keys(h, nil, true)
|
40
|
+
end
|
41
|
+
|
42
|
+
def flatten_translations(translations)
|
43
|
+
# don't flatten locale roots
|
44
|
+
translations.inject({}) do |flattened_h, (locale_name, locale_translations)|
|
45
|
+
flattened_h[locale_name] = flatten_hash(locale_translations)
|
46
|
+
flattened_h
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def lookup(locale, key, scope = nil, separator = nil)
|
51
|
+
return unless key
|
52
|
+
init_translations unless initialized?
|
53
|
+
|
54
|
+
if separator && I18n.default_separator != separator
|
55
|
+
key = cleanup_non_standard_separator(key, separator)
|
56
|
+
scope = Array(scope).map{|k| cleanup_non_standard_separator(k, separator)} if scope
|
57
|
+
end
|
58
|
+
|
59
|
+
key = (Array(scope) + [key]).join(I18n.default_separator) if scope
|
60
|
+
flattened_translations[locale.to_sym][key.to_sym]
|
61
|
+
end
|
62
|
+
|
63
|
+
def cleanup_non_standard_separator(key, user_separator)
|
64
|
+
escape_default_separator(key).tr(user_separator, I18n.default_separator)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,75 @@
|
|
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
|
+
unless key.nil? || key.empty?
|
45
|
+
key, value = normalize_pluralization(locale, key, value) if key.index("\000")
|
46
|
+
|
47
|
+
parts = key.split('|').reverse
|
48
|
+
normalized = parts.inject({}) do |normalized, part|
|
49
|
+
normalized = { part => normalized.empty? ? value : normalized }
|
50
|
+
end
|
51
|
+
|
52
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
53
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
54
|
+
result.merge!(normalized, &merger)
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def normalize_pluralization(locale, key, value)
|
61
|
+
# FIXME po_parser includes \000 chars that can not be turned into Symbols
|
62
|
+
key = key.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR).split(I18n::Gettext::PLURAL_SEPARATOR).first
|
63
|
+
|
64
|
+
keys = I18n::Gettext.plural_keys(locale)
|
65
|
+
values = value.split("\000")
|
66
|
+
raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size
|
67
|
+
|
68
|
+
result = {}
|
69
|
+
values.each_with_index { |value, ix| result[keys[ix]] = value }
|
70
|
+
[key, result]
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module I18n
|
2
|
+
module Backend
|
3
|
+
module Helpers
|
4
|
+
SEPARATOR_ESCAPE_CHAR = "\001"
|
5
|
+
|
6
|
+
# Return a new hash with all keys and nested keys converted to symbols.
|
7
|
+
def deep_symbolize_keys(hash)
|
8
|
+
hash.inject({}) { |result, (key, value)|
|
9
|
+
value = deep_symbolize_keys(value) if value.is_a?(Hash)
|
10
|
+
result[(key.to_sym rescue key) || key] = value
|
11
|
+
result
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Flatten keys for nested Hashes by chaining up keys using the separator
|
16
|
+
# >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
|
17
|
+
# => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
|
18
|
+
def wind_keys(hash, separator = nil, subtree = false, prev_key = nil, result = {}, orig_hash=hash)
|
19
|
+
separator ||= I18n.default_separator
|
20
|
+
|
21
|
+
hash.each_pair do |key, value|
|
22
|
+
key = escape_default_separator(key, separator)
|
23
|
+
curr_key = [prev_key, key].compact.join(separator).to_sym
|
24
|
+
|
25
|
+
if value.is_a?(Symbol)
|
26
|
+
value = hash_lookup(orig_hash, value, separator) ||
|
27
|
+
hash_lookup(hash, value, separator) || value
|
28
|
+
end
|
29
|
+
|
30
|
+
if value.is_a?(Hash)
|
31
|
+
result[curr_key] = value if subtree
|
32
|
+
wind_keys(value, separator, subtree, curr_key, result, orig_hash)
|
33
|
+
else
|
34
|
+
result[curr_key] = value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def escape_default_separator(key, separator=nil)
|
42
|
+
key.to_s.tr(separator || I18n.default_separator, SEPARATOR_ESCAPE_CHAR)
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash_lookup(hash, keys, separator = ".")
|
46
|
+
keys.to_s.split(separator).inject(hash) do |result, key|
|
47
|
+
key = key.to_sym
|
48
|
+
if result.respond_to?(:has_key?) and result.has_key?(key)
|
49
|
+
result[key]
|
50
|
+
else
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Expand keys chained by the the given separator through nested Hashes
|
57
|
+
# >> { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }.unwind
|
58
|
+
# => { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}
|
59
|
+
def unwind_keys(hash, separator = ".")
|
60
|
+
result = {}
|
61
|
+
hash.each do |key, value|
|
62
|
+
keys = key.to_s.split(separator)
|
63
|
+
curr = result
|
64
|
+
curr = curr[keys.shift] ||= {} while keys.size > 1
|
65
|
+
curr[keys.shift] = value
|
66
|
+
end
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
# # Flatten the given array once
|
71
|
+
# def flatten_once(array)
|
72
|
+
# result = []
|
73
|
+
# for element in array # a little faster than each
|
74
|
+
# result.push(*element)
|
75
|
+
# end
|
76
|
+
# result
|
77
|
+
# end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|