theoooo-i18n 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.textile ADDED
@@ -0,0 +1,57 @@
1
+ h1. Changelog
2
+
3
+ h2. master
4
+
5
+ * (no changes)
6
+
7
+ h2. 0.2.0 (2009-07-12)
8
+
9
+ * "Allow using Ruby 1.9 syntax for string interpolation (API addition)":http://github.com/svenfuchs/i18n/commit/c6e0b06d512f2af57199a843a1d8a40241b32861
10
+ * "Allow configuring the default scope separator, allow to pass a custom scope separator(API addition)":http://github.com/svenfuchs/i18n/commit/5b75bfbc348061adc11e3790187a187275bfd471 (e.g. I18n.t(:'foo|bar', :separator => '|')
11
+ * "Pass :format option to #translate for #localize more useful lambda support":http://github.com/svenfuchs/i18n/commit/e277711b3c844fe7589b8d3f9af0f7d1b969a273
12
+ * "Refactor Simple backend #resolve to #default and #resolve for more consistency. Now allows to pass lambdas as defaults and re-resolve Symbols":http://github.com/svenfuchs/i18n/commit/8c4ce3d923ce5fa73e973fe28217e18165549aba
13
+ * "Add lambda support to #translate (API addition)":http://github.com/svenfuchs/i18n/commit/c90e62d8f7d3d5b78f34cfe328d871b58884f115
14
+ * "Add lambda support to #localize (API addition)":http://github.com/svenfuchs/i18n/commit/9d390afcf33f3f469bb95e6888147152f6cc7442
15
+
16
+ h2. 0.1.3 (2009-02-27)
17
+
18
+ * "Remove unnecessary string encoding handling in the i18n simple backend which made the backend break on Ruby 1.9":http://github.com/svenfuchs/i18n/commit/4c3a970783861a94f2e89f46714fb3434e4f4f8d
19
+
20
+ h2. 0.1.2 (2009-01-09)
21
+
22
+ * "added #available_locales (returns an array of locales for which translations are available)":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4
23
+ * "flatten load_path before using it so that a nested array of paths won't throw up":http://github.com/svenfuchs/i18n/commit/d473a068a2b90aba98135deb225d6eb6d8104d70
24
+
25
+ h2. 0.1.1 (2008-11-20)
26
+
27
+ * "Use :'en' as a default locale (in favor of :'en-US')":http://github.com/svenfuchs/i18n/commit/c4b10b246aecf7da78cb2568dd0d2ab7e6b8a230
28
+ * "Add #reload! to Simple backend":http://github.com/svenfuchs/i18n/commit/36dd2bd9973b9e1559728749a9daafa44693e964
29
+
30
+ h2. 0.1.0 (2008-10-25)
31
+
32
+ * "Fix Simple backend to distinguish false from nil values":http://github.com/svenfuchs/i18n/commit/39d9a47da14b5f3ba126af48923af8c30e135166
33
+ * "Add #load_path to public api, add initialize to simple backend and remove #load_translations from public api":http://github.com/svenfuchs/i18n/commit/c4c5649e6bc8f020f1aaf5a5470bde048e22c82d
34
+ * "Speed up Backend::Simple#interpolate":http://github.com/svenfuchs/i18n/commit/9e1ac6bf8833304e036323ec9932b9f33c468a35
35
+ * "Remove #populate and #store_translations from public API":http://github.com/svenfuchs/i18n/commit/f4e514a80be7feb509f66824ee311905e2940900
36
+ * "Use :other instead of :many as a plural key":http://github.com/svenfuchs/i18n/commit/0f8f20a2552bf6a2aa758d8fdd62a7154e4a1bf6
37
+ * "Use a class instead of a module for Simple backend":http://github.com/svenfuchs/i18n/commit/08f051aa61320c17debde24a83268bc74e33b995
38
+ * "Make Simple backend #interpolate deal with non-ASCII string encodings":http://github.com/svenfuchs/i18n/commit/d84a3f3f55543c084d5dc5d1fed613b8df148789
39
+ * "Fix default arrays of non-existant keys returning the default array":http://github.com/svenfuchs/i18n/commit/6c04ca86c87f97dc78f07c2a4023644e5ba8b839
40
+
41
+ h2. Initial implementation (June/July 2008)
42
+
43
+ Initial implementation by "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs based on previous discussion/consensus of the rails-i18n team (alphabetical order) and many others:
44
+
45
+ * "Matt Aimonetti":http://railsontherun.com
46
+ * "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs
47
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
48
+ * "Saimon Moore":http://saimonmoore.net
49
+ * "Stephan Soller":http://www.arkanis-development.de
50
+
51
+ h2. More information
52
+
53
+ * "Homepage":http://rails-i18n.org
54
+ * "Wiki":http://rails-i18n.org/wiki
55
+ * "Mailinglist":http://groups.google.com/group/rails-i18n
56
+ * "About the project/history":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized
57
+ * "Initial API Intro":http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 The Ruby I18n team
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,42 @@
1
+ h1. Ruby I18n
2
+
3
+ Ruby Internationalization and localization solution.
4
+
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
+ h2. Authors
30
+
31
+ * "Sven Fuchs":http://www.artweb-design.de
32
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
33
+ * "Stephan Soller":http://www.arkanis-development.de
34
+ * "Saimon Moore":http://saimonmoore.net
35
+ * "Matt Aimonetti":http://railsontherun.com
36
+
37
+ h2. License
38
+
39
+ MIT License. See the included MIT-LICENCE file.
40
+
41
+
42
+
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ task :default => [:test]
2
+
3
+ task :test do
4
+ ruby "test/all.rb"
5
+ end
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = "i18n"
11
+ s.rubyforge_project = "i18n"
12
+ s.summary = "New wave Internationalization support for Ruby"
13
+ s.email = "rails-i18n@googlegroups.com"
14
+ s.homepage = "http://rails-i18n.org"
15
+ s.description = "Add Internationalization support to your Ruby application."
16
+ s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore']
17
+ s.files = FileList["[A-Z]*", "{lib,test}/**/*"]
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
21
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,42 @@
1
+ require 'active_record'
2
+ require 'ruby2ruby'
3
+ require 'parse_tree'
4
+ require 'parse_tree_extensions'
5
+
6
+ class Translation < ActiveRecord::Base
7
+
8
+ attr_protected :proc
9
+ serialize :value
10
+
11
+ named_scope :locale, lambda {|locale|
12
+ { :conditions => {:locale => locale }}
13
+ }
14
+
15
+ named_scope :key, lambda { |key|
16
+ { :conditions => {:key => key} }
17
+ }
18
+
19
+ named_scope :keys, lambda { |key, separator|
20
+ separator ||= I18n.default_separator
21
+ { :conditions => "key LIKE '#{key}#{separator}%'" }
22
+ }
23
+
24
+ def value=(v)
25
+ case v
26
+ when Proc
27
+ write_attribute(:value, v.to_ruby)
28
+ write_attribute(:proc, true)
29
+ else
30
+ write_attribute(:value, v)
31
+ end
32
+ end
33
+
34
+ def value
35
+ if proc
36
+ Kernel.eval read_attribute( :value )
37
+ else
38
+ read_attribute( :value )
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,45 @@
1
+ require 'i18n/backend/base'
2
+ require 'i18n/backend/active_record/translation'
3
+ require 'i18n/hash'
4
+
5
+ module I18n
6
+ module Backend
7
+ class ActiveRecord < Base
8
+
9
+ def reload!
10
+ end
11
+
12
+ def store_translations(locale, data)
13
+ data.unwind.each{|key,v|
14
+ Translation.create(:locale => locale.to_s, :key => key, :value => v)
15
+ }
16
+ end
17
+
18
+ def available_locales
19
+ Translation.find(:all, :select => 'DISTINCT locale').map{|t| t.locale}
20
+ end
21
+
22
+ protected
23
+
24
+ def lookup(locale, key, scope = [], separator = I18n.default_separator)
25
+ return unless key
26
+ separator ||= "."
27
+ flat_key = (Array(scope) + Array(key)).join( separator )
28
+
29
+ result = Translation.locale(locale).key(flat_key).find(:first)
30
+ return result.value if result
31
+ results = Translation.locale(locale).keys(flat_key, separator)
32
+ if results.empty?
33
+ return nil
34
+ else
35
+ chop_range = (flat_key.size + separator.size)..-1
36
+ return results.inject({}){|hash,r|
37
+ hash[r.key.slice( chop_range )] = hash[r.value]
38
+ hash
39
+ }.wind
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ require 'i18n/backend/base'
2
+
3
+ # Stub class for the Simple backend. The actual implementation is provided by
4
+ # the backend Base class. This makes it easier to extend the Simple backend's
5
+ # behaviour by including modules. E.g.:
6
+ #
7
+ # module I18n::Backend::Pluralization
8
+ # def pluralize(*args)
9
+ # # extended pluralization logic
10
+ # super
11
+ # end
12
+ # end
13
+ #
14
+ # I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
15
+
16
+ module I18n
17
+ module Backend
18
+ class Simple < Base
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,59 @@
1
+ class KeyError < IndexError
2
+ def initialize(message = nil)
3
+ super(message || "key not found")
4
+ end
5
+ end unless defined?(KeyError)
6
+
7
+ module I18n
8
+ class ArgumentError < ::ArgumentError; end
9
+
10
+ class InvalidLocale < ArgumentError
11
+ attr_reader :locale
12
+ def initialize(locale)
13
+ @locale = locale
14
+ super "#{locale.inspect} is not a valid locale"
15
+ end
16
+ end
17
+
18
+ class MissingTranslationData < ArgumentError
19
+ attr_reader :locale, :key, :options
20
+ def initialize(locale, key, options)
21
+ @key, @locale, @options = key, locale, options
22
+ keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope])
23
+ keys << 'no key' if keys.size < 2
24
+ super "translation missing: #{keys.join(', ')}"
25
+ end
26
+ end
27
+
28
+ class InvalidPluralizationData < ArgumentError
29
+ attr_reader :entry, :count
30
+ def initialize(entry, count)
31
+ @entry, @count = entry, count
32
+ super "translation data #{entry.inspect} can not be used with :count => #{count}"
33
+ end
34
+ end
35
+
36
+ class MissingInterpolationArgument < ArgumentError
37
+ attr_reader :values, :string
38
+ def initialize(values, string)
39
+ @values, @string = values, string
40
+ super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
41
+ end
42
+ end
43
+
44
+ class ReservedInterpolationKey < ArgumentError
45
+ attr_reader :key, :string
46
+ def initialize(key, string)
47
+ @key, @string = key, string
48
+ super "reserved key #{key.inspect} used in #{string.inspect}"
49
+ end
50
+ end
51
+
52
+ class UnknownFileType < ArgumentError
53
+ attr_reader :type, :filename
54
+ def initialize(type, filename)
55
+ @type, @filename = type, filename
56
+ super "can not load translations from #{filename}, the file type #{type} is not known"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,93 @@
1
+ =begin
2
+ heavily based on Masao Mutoh's gettext String interpolation extension
3
+ http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
4
+ Copyright (C) 2005-2009 Masao Mutoh
5
+ You may redistribute it and/or modify it under the same license terms as Ruby.
6
+ =end
7
+
8
+ if RUBY_VERSION < '1.9'
9
+
10
+ # KeyError is raised by String#% when the string contains a named placeholder
11
+ # that is not contained in the given arguments hash. Ruby 1.9 includes and
12
+ # raises this exception natively. We define it to mimic Ruby 1.9's behaviour
13
+ # in Ruby 1.8.x
14
+
15
+ class KeyError < IndexError
16
+ def initialize(message = nil)
17
+ super(message || "key not found")
18
+ end
19
+ end unless defined?(KeyError)
20
+
21
+ # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
22
+ #
23
+ # String#% method which accept "named argument". The translator can know
24
+ # the meaning of the msgids using "named argument" instead of %s/%d style.
25
+
26
+ class String
27
+ # For older ruby versions, such as ruby-1.8.5
28
+ alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'}
29
+ alias :interpolate_without_ruby_19_syntax :% # :nodoc:
30
+
31
+ INTERPOLATION_PATTERN = Regexp.union(
32
+ /%%/,
33
+ /%\{(\w+)\}/, # matches placeholders like "%{foo}"
34
+ /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
35
+ )
36
+
37
+ # % uses self (i.e. the String) as a format specification and returns the
38
+ # result of applying it to the given arguments. In other words it interpolates
39
+ # the given arguments to the string according to the formats the string
40
+ # defines.
41
+ #
42
+ # There are three ways to use it:
43
+ #
44
+ # * Using a single argument or Array of arguments.
45
+ #
46
+ # This is the default behaviour of the String class. See Kernel#sprintf for
47
+ # more details about the format string.
48
+ #
49
+ # Example:
50
+ #
51
+ # "%d %s" % [1, "message"]
52
+ # # => "1 message"
53
+ #
54
+ # * Using a Hash as an argument and unformatted, named placeholders.
55
+ #
56
+ # When you pass a Hash as an argument and specify placeholders with %{foo}
57
+ # it will interpret the hash values as named arguments.
58
+ #
59
+ # Example:
60
+ #
61
+ # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
62
+ # # => "Masao Mutoh"
63
+ #
64
+ # * Using a Hash as an argument and formatted, named placeholders.
65
+ #
66
+ # When you pass a Hash as an argument and specify placeholders with %<foo>d
67
+ # it will interpret the hash values as named arguments and format the value
68
+ # according to the formatting instruction appended to the closing >.
69
+ #
70
+ # Example:
71
+ #
72
+ # "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
73
+ # # => "10, 43.3"
74
+ def %(args)
75
+ if args.kind_of?(Hash)
76
+ dup.gsub(INTERPOLATION_PATTERN) do |match|
77
+ if match == '%%'
78
+ '%'
79
+ else
80
+ key = ($1 || $2).to_sym
81
+ raise KeyError unless args.has_key?(key)
82
+ $3 ? sprintf("%#{$3}", args[key]) : args[key]
83
+ end
84
+ end
85
+ elsif self =~ INTERPOLATION_PATTERN
86
+ raise ArgumentError.new('one hash required')
87
+ else
88
+ result = gsub(/%([{<])/, '%%\1')
89
+ result.send :'interpolate_without_ruby_19_syntax', args
90
+ end
91
+ end
92
+ end
93
+ end
data/lib/i18n.rb ADDED
@@ -0,0 +1,269 @@
1
+ # Authors:: Matt Aimonetti (http://railsontherun.com/),
2
+ # Sven Fuchs (http://www.artweb-design.de),
3
+ # Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
4
+ # Saimon Moore (http://saimonmoore.net),
5
+ # Stephan Soller (http://www.arkanis-development.de/)
6
+ # Copyright:: Copyright (c) 2008 The Ruby i18n Team
7
+ # License:: MIT
8
+ require 'i18n/backend/simple'
9
+ require 'i18n/exceptions'
10
+ require 'i18n/string'
11
+ require 'i18n/hash'
12
+
13
+ module I18n
14
+ @@backend = nil
15
+ @@load_path = nil
16
+ @@default_locale = :en
17
+ @@default_separator = '.'
18
+ @@exception_handler = :default_exception_handler
19
+
20
+ class << self
21
+ # Returns the current backend. Defaults to +Backend::Simple+.
22
+ def backend
23
+ @@backend ||= Backend::Simple.new
24
+ end
25
+
26
+ # Sets the current backend. Used to set a custom backend.
27
+ def backend=(backend)
28
+ @@backend = backend
29
+ end
30
+
31
+ # Returns the current default locale. Defaults to :'en'
32
+ def default_locale
33
+ @@default_locale
34
+ end
35
+
36
+ # Sets the current default locale. Used to set a custom default locale.
37
+ def default_locale=(locale)
38
+ @@default_locale = locale.to_sym rescue nil
39
+ end
40
+
41
+ # Returns the current locale. Defaults to I18n.default_locale.
42
+ def locale
43
+ Thread.current[:locale] ||= default_locale
44
+ end
45
+
46
+ # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
47
+ def locale=(locale)
48
+ Thread.current[:locale] = locale.to_sym rescue nil
49
+ end
50
+
51
+ # Returns an array of locales for which translations are available.
52
+ # Unless you explicitely set the these through I18n.available_locales=
53
+ # the call will be delegated to the backend and memoized on the I18n module.
54
+ def available_locales
55
+ @@available_locales ||= backend.available_locales
56
+ end
57
+
58
+ # Sets the available locales.
59
+ def available_locales=(locales)
60
+ @@available_locales = locales
61
+ end
62
+
63
+ # Returns the current default scope separator. Defaults to '.'
64
+ def default_separator
65
+ @@default_separator
66
+ end
67
+
68
+ # Sets the current default scope separator.
69
+ def default_separator=(separator)
70
+ @@default_separator = separator
71
+ end
72
+
73
+ # Sets the exception handler.
74
+ def exception_handler=(exception_handler)
75
+ @@exception_handler = exception_handler
76
+ end
77
+
78
+ # Allow clients to register paths providing translation data sources. The
79
+ # backend defines acceptable sources.
80
+ #
81
+ # E.g. the provided SimpleBackend accepts a list of paths to translation
82
+ # files which are either named *.rb and contain plain Ruby Hashes or are
83
+ # named *.yml and contain YAML data. So for the SimpleBackend clients may
84
+ # register translation files like this:
85
+ # I18n.load_path << 'path/to/locale/en.yml'
86
+ def load_path
87
+ @@load_path ||= []
88
+ end
89
+
90
+ # Sets the load path instance. Custom implementations are expected to
91
+ # behave like a Ruby Array.
92
+ def load_path=(load_path)
93
+ @@load_path = load_path
94
+ end
95
+
96
+ # Tells the backend to reload translations. Used in situations like the
97
+ # Rails development environment. Backends can implement whatever strategy
98
+ # is useful.
99
+ def reload!
100
+ backend.reload!
101
+ end
102
+
103
+ # Translates, pluralizes and interpolates a given key using a given locale,
104
+ # scope, and default, as well as interpolation values.
105
+ #
106
+ # *LOOKUP*
107
+ #
108
+ # Translation data is organized as a nested hash using the upper-level keys
109
+ # as namespaces. <em>E.g.</em>, ActionView ships with the translation:
110
+ # <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
111
+ #
112
+ # Translations can be looked up at any level of this hash using the key argument
113
+ # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
114
+ # returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
115
+ #
116
+ # Key can be either a single key or a dot-separated key (both Strings and Symbols
117
+ # work). <em>E.g.</em>, the short format can be looked up using both:
118
+ # I18n.t 'date.formats.short'
119
+ # I18n.t :'date.formats.short'
120
+ #
121
+ # Scope can be either a single key, a dot-separated key or an array of keys
122
+ # or dot-separated keys. Keys and scopes can be combined freely. So these
123
+ # examples will all look up the same short date format:
124
+ # I18n.t 'date.formats.short'
125
+ # I18n.t 'formats.short', :scope => 'date'
126
+ # I18n.t 'short', :scope => 'date.formats'
127
+ # I18n.t 'short', :scope => %w(date formats)
128
+ #
129
+ # *INTERPOLATION*
130
+ #
131
+ # Translations can contain interpolation variables which will be replaced by
132
+ # values passed to #translate as part of the options hash, with the keys matching
133
+ # the interpolation variable names.
134
+ #
135
+ # <em>E.g.</em>, with a translation <tt>:foo => "foo {{bar}}"</tt> the option
136
+ # value for the key +bar+ will be interpolated into the translation:
137
+ # I18n.t :foo, :bar => 'baz' # => 'foo baz'
138
+ #
139
+ # *PLURALIZATION*
140
+ #
141
+ # Translation data can contain pluralized translations. Pluralized translations
142
+ # are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
143
+ #
144
+ # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
145
+ # pluralization rules. Other algorithms can be supported by custom backends.
146
+ #
147
+ # This returns the singular version of a pluralized translation:
148
+ # I18n.t :foo, :count => 1 # => 'Foo'
149
+ #
150
+ # These both return the plural version of a pluralized translation:
151
+ # I18n.t :foo, :count => 0 # => 'Foos'
152
+ # I18n.t :foo, :count => 2 # => 'Foos'
153
+ #
154
+ # The <tt>:count</tt> option can be used both for pluralization and interpolation.
155
+ # <em>E.g.</em>, with the translation
156
+ # <tt>:foo => ['{{count}} foo', '{{count}} foos']</tt>, count will
157
+ # be interpolated to the pluralized translation:
158
+ # I18n.t :foo, :count => 1 # => '1 foo'
159
+ #
160
+ # *DEFAULTS*
161
+ #
162
+ # This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
163
+ # I18n.t :foo, :default => 'default'
164
+ #
165
+ # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
166
+ # translation for <tt>:foo</tt> was found:
167
+ # I18n.t :foo, :default => :bar
168
+ #
169
+ # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
170
+ # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
171
+ # I18n.t :foo, :default => [:bar, 'default']
172
+ #
173
+ # *BULK LOOKUP*
174
+ #
175
+ # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
176
+ # I18n.t [:foo, :bar]
177
+ #
178
+ # Can be used with dot-separated nested keys:
179
+ # I18n.t [:'baz.foo', :'baz.bar']
180
+ #
181
+ # Which is the same as using a scope option:
182
+ # I18n.t [:foo, :bar], :scope => :baz
183
+ #
184
+ # *LAMBDAS*
185
+ #
186
+ # Both translations and defaults can be given as Ruby lambdas. Lambdas will be
187
+ # called and passed the key and options.
188
+ #
189
+ # E.g. assuming the key <tt>:salutation</tt> resolves to:
190
+ # lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" }
191
+ #
192
+ # Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
193
+ #
194
+ # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
195
+ # a cache layer is put in front of I18n.translate it will generate a cache key
196
+ # from the argument values passed to #translate. Therefor your lambdas should
197
+ # always return the same translations/values per unique combination of argument
198
+ # values.
199
+ def translate(*args)
200
+ options = args.last.is_a?(Hash) ? args.pop : {}
201
+ key = args.shift
202
+ locale = options.delete(:locale) || I18n.locale
203
+ backend.translate(locale, key, options)
204
+ rescue I18n::ArgumentError => exception
205
+ raise exception if options[:raise]
206
+ handle_exception(exception, locale, key, options)
207
+ end
208
+ alias :t :translate
209
+
210
+ def translate!(key, options = {})
211
+ translate(key, options.merge( :raise => true ))
212
+ end
213
+ alias :t! :translate!
214
+
215
+ # Localizes certain objects, such as dates and numbers to local formatting.
216
+ def localize(object, options = {})
217
+ locale = options[:locale] || I18n.locale
218
+ format = options[:format] || :default
219
+ backend.localize(locale, object, format)
220
+ end
221
+ alias :l :localize
222
+
223
+ protected
224
+
225
+ # Handles exceptions raised in the backend. All exceptions except for
226
+ # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
227
+ # was caught and the option :raise is not set the handler returns an error
228
+ # message string containing the key/scope.
229
+ def default_exception_handler(exception, locale, key, options)
230
+ return exception.message if MissingTranslationData === exception
231
+ raise exception
232
+ end
233
+
234
+ # Any exceptions thrown in translate will be sent to the @@exception_handler
235
+ # which can be a Symbol, a Proc or any other Object.
236
+ #
237
+ # If exception_handler is a Symbol then it will simply be sent to I18n as
238
+ # a method call. A Proc will simply be called. In any other case the
239
+ # method #call will be called on the exception_handler object.
240
+ #
241
+ # Examples:
242
+ #
243
+ # I18n.exception_handler = :default_exception_handler # this is the default
244
+ # I18n.default_exception_handler(exception, locale, key, options) # will be called like this
245
+ #
246
+ # I18n.exception_handler = lambda { |*args| ... } # a lambda
247
+ # I18n.exception_handler.call(exception, locale, key, options) # will be called like this
248
+ #
249
+ # I18n.exception_handler = I18nExceptionHandler.new # an object
250
+ # I18n.exception_handler.call(exception, locale, key, options) # will be called like this
251
+ def handle_exception(exception, locale, key, options)
252
+ case @@exception_handler
253
+ when Symbol
254
+ send(@@exception_handler, exception, locale, key, options)
255
+ else
256
+ @@exception_handler.call(exception, locale, key, options)
257
+ end
258
+ end
259
+
260
+ # Merges the given locale, key and scope into a single array of keys.
261
+ # Splits keys that contain dots into multiple keys. Makes sure all
262
+ # keys are Symbols.
263
+ def normalize_translation_keys(locale, key, scope, separator = nil)
264
+ keys = [locale] + Array(scope) + Array(key)
265
+ keys = keys.map { |k| k.to_s.split(separator || I18n.default_separator) }
266
+ keys.flatten.map { |k| k.to_sym }
267
+ end
268
+ end
269
+ end
data/test/all.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__) + '/**/*_test.rb'].each do |file|
2
+ require file
3
+ end