synergy_russian 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/CHANGELOG +113 -0
  2. data/LICENSE +20 -0
  3. data/README.textile +272 -0
  4. data/Rakefile +55 -0
  5. data/TODO +10 -0
  6. data/init.rb +3 -0
  7. data/lib/russian.rb +118 -0
  8. data/lib/russian/action_view_ext/helpers/date_helper.rb +112 -0
  9. data/lib/russian/active_record_ext/custom_error_message.rb +163 -0
  10. data/lib/russian/active_support_ext/parameterize.rb +31 -0
  11. data/lib/russian/backend/advanced.rb +134 -0
  12. data/lib/russian/locale/actionview.yml +162 -0
  13. data/lib/russian/locale/activerecord.yml +95 -0
  14. data/lib/russian/locale/activesupport.yml +16 -0
  15. data/lib/russian/locale/datetime.yml +49 -0
  16. data/lib/russian/locale/pluralize.rb +25 -0
  17. data/lib/russian/transliteration.rb +72 -0
  18. data/lib/vendor/i18n/MIT-LICENSE +20 -0
  19. data/lib/vendor/i18n/README.textile +20 -0
  20. data/lib/vendor/i18n/Rakefile +5 -0
  21. data/lib/vendor/i18n/i18n.gemspec +27 -0
  22. data/lib/vendor/i18n/lib/i18n.rb +199 -0
  23. data/lib/vendor/i18n/lib/i18n/backend/simple.rb +214 -0
  24. data/lib/vendor/i18n/lib/i18n/exceptions.rb +53 -0
  25. data/lib/vendor/i18n/test/all.rb +5 -0
  26. data/lib/vendor/i18n/test/i18n_exceptions_test.rb +100 -0
  27. data/lib/vendor/i18n/test/i18n_test.rb +125 -0
  28. data/lib/vendor/i18n/test/locale/en.rb +1 -0
  29. data/lib/vendor/i18n/test/locale/en.yml +3 -0
  30. data/lib/vendor/i18n/test/simple_backend_test.rb +568 -0
  31. data/spec/fixtures/en.yml +4 -0
  32. data/spec/fixtures/ru.yml +4 -0
  33. data/spec/i18n/locale/datetime_spec.rb +99 -0
  34. data/spec/i18n/locale/pluralization_spec.rb +28 -0
  35. data/spec/locale_spec.rb +129 -0
  36. data/spec/russian_spec.rb +136 -0
  37. data/spec/spec_helper.rb +7 -0
  38. data/spec/transliteration_spec.rb +51 -0
  39. metadata +102 -0
@@ -0,0 +1,20 @@
1
+ h1. Ruby I18n gem
2
+
3
+ I18n and localization solution for Ruby.
4
+
5
+ For information please refer to http://rails-i18n.org
6
+
7
+ h2. Authors
8
+
9
+ * "Matt Aimonetti":http://railsontherun.com
10
+ * "Sven Fuchs":http://www.artweb-design.de
11
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
12
+ * "Saimon Moore":http://saimonmoore.net
13
+ * "Stephan Soller":http://www.arkanis-development.de
14
+
15
+ h2. License
16
+
17
+ MIT License. See the included MIT-LICENCE file.
18
+
19
+
20
+
@@ -0,0 +1,5 @@
1
+ task :default => [:test]
2
+
3
+ task :test do
4
+ ruby "test/all.rb"
5
+ end
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "i18n"
3
+ s.version = "0.1.3"
4
+ s.date = "2009-01-09"
5
+ s.summary = "Internationalization support for Ruby"
6
+ s.email = "rails-i18n@googlegroups.com"
7
+ s.homepage = "http://rails-i18n.org"
8
+ s.description = "Add Internationalization support to your Ruby application."
9
+ s.has_rdoc = false
10
+ s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore']
11
+ s.files = [
12
+ 'i18n.gemspec',
13
+ 'lib/i18n/backend/simple.rb',
14
+ 'lib/i18n/exceptions.rb',
15
+ 'lib/i18n.rb',
16
+ 'MIT-LICENSE',
17
+ 'README.textile'
18
+ ]
19
+ s.test_files = [
20
+ 'test/all.rb',
21
+ 'test/i18n_exceptions_test.rb',
22
+ 'test/i18n_test.rb',
23
+ 'test/locale/en.rb',
24
+ 'test/locale/en.yml',
25
+ 'test/simple_backend_test.rb'
26
+ ]
27
+ end
@@ -0,0 +1,199 @@
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
+
11
+ module I18n
12
+ @@backend = nil
13
+ @@load_path = nil
14
+ @@default_locale = :'en'
15
+ @@exception_handler = :default_exception_handler
16
+
17
+ class << self
18
+ # Returns the current backend. Defaults to +Backend::Simple+.
19
+ def backend
20
+ @@backend ||= Backend::Simple.new
21
+ end
22
+
23
+ # Sets the current backend. Used to set a custom backend.
24
+ def backend=(backend)
25
+ @@backend = backend
26
+ end
27
+
28
+ # Returns the current default locale. Defaults to :'en'
29
+ def default_locale
30
+ @@default_locale
31
+ end
32
+
33
+ # Sets the current default locale. Used to set a custom default locale.
34
+ def default_locale=(locale)
35
+ @@default_locale = locale
36
+ end
37
+
38
+ # Returns the current locale. Defaults to I18n.default_locale.
39
+ def locale
40
+ Thread.current[:locale] ||= default_locale
41
+ end
42
+
43
+ # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
44
+ def locale=(locale)
45
+ Thread.current[:locale] = locale
46
+ end
47
+
48
+ # Returns an array of locales for which translations are available
49
+ def available_locales
50
+ backend.available_locales
51
+ end
52
+
53
+ # Sets the exception handler.
54
+ def exception_handler=(exception_handler)
55
+ @@exception_handler = exception_handler
56
+ end
57
+
58
+ # Allow clients to register paths providing translation data sources. The
59
+ # backend defines acceptable sources.
60
+ #
61
+ # E.g. the provided SimpleBackend accepts a list of paths to translation
62
+ # files which are either named *.rb and contain plain Ruby Hashes or are
63
+ # named *.yml and contain YAML data. So for the SimpleBackend clients may
64
+ # register translation files like this:
65
+ # I18n.load_path << 'path/to/locale/en.yml'
66
+ def load_path
67
+ @@load_path ||= []
68
+ end
69
+
70
+ # Sets the load path instance. Custom implementations are expected to
71
+ # behave like a Ruby Array.
72
+ def load_path=(load_path)
73
+ @@load_path = load_path
74
+ end
75
+
76
+ # Tells the backend to reload translations. Used in situations like the
77
+ # Rails development environment. Backends can implement whatever strategy
78
+ # is useful.
79
+ def reload!
80
+ backend.reload!
81
+ end
82
+
83
+ # Translates, pluralizes and interpolates a given key using a given locale,
84
+ # scope, and default, as well as interpolation values.
85
+ #
86
+ # *LOOKUP*
87
+ #
88
+ # Translation data is organized as a nested hash using the upper-level keys
89
+ # as namespaces. <em>E.g.</em>, ActionView ships with the translation:
90
+ # <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
91
+ #
92
+ # Translations can be looked up at any level of this hash using the key argument
93
+ # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
94
+ # returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
95
+ #
96
+ # Key can be either a single key or a dot-separated key (both Strings and Symbols
97
+ # work). <em>E.g.</em>, the short format can be looked up using both:
98
+ # I18n.t 'date.formats.short'
99
+ # I18n.t :'date.formats.short'
100
+ #
101
+ # Scope can be either a single key, a dot-separated key or an array of keys
102
+ # or dot-separated keys. Keys and scopes can be combined freely. So these
103
+ # examples will all look up the same short date format:
104
+ # I18n.t 'date.formats.short'
105
+ # I18n.t 'formats.short', :scope => 'date'
106
+ # I18n.t 'short', :scope => 'date.formats'
107
+ # I18n.t 'short', :scope => %w(date formats)
108
+ #
109
+ # *INTERPOLATION*
110
+ #
111
+ # Translations can contain interpolation variables which will be replaced by
112
+ # values passed to #translate as part of the options hash, with the keys matching
113
+ # the interpolation variable names.
114
+ #
115
+ # <em>E.g.</em>, with a translation <tt>:foo => "foo {{bar}}"</tt> the option
116
+ # value for the key +bar+ will be interpolated into the translation:
117
+ # I18n.t :foo, :bar => 'baz' # => 'foo baz'
118
+ #
119
+ # *PLURALIZATION*
120
+ #
121
+ # Translation data can contain pluralized translations. Pluralized translations
122
+ # are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
123
+ #
124
+ # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
125
+ # pluralization rules. Other algorithms can be supported by custom backends.
126
+ #
127
+ # This returns the singular version of a pluralized translation:
128
+ # I18n.t :foo, :count => 1 # => 'Foo'
129
+ #
130
+ # These both return the plural version of a pluralized translation:
131
+ # I18n.t :foo, :count => 0 # => 'Foos'
132
+ # I18n.t :foo, :count => 2 # => 'Foos'
133
+ #
134
+ # The <tt>:count</tt> option can be used both for pluralization and interpolation.
135
+ # <em>E.g.</em>, with the translation
136
+ # <tt>:foo => ['{{count}} foo', '{{count}} foos']</tt>, count will
137
+ # be interpolated to the pluralized translation:
138
+ # I18n.t :foo, :count => 1 # => '1 foo'
139
+ #
140
+ # *DEFAULTS*
141
+ #
142
+ # This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
143
+ # I18n.t :foo, :default => 'default'
144
+ #
145
+ # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
146
+ # translation for <tt>:foo</tt> was found:
147
+ # I18n.t :foo, :default => :bar
148
+ #
149
+ # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
150
+ # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
151
+ # I18n.t :foo, :default => [:bar, 'default']
152
+ #
153
+ # <b>BULK LOOKUP</b>
154
+ #
155
+ # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
156
+ # I18n.t [:foo, :bar]
157
+ #
158
+ # Can be used with dot-separated nested keys:
159
+ # I18n.t [:'baz.foo', :'baz.bar']
160
+ #
161
+ # Which is the same as using a scope option:
162
+ # I18n.t [:foo, :bar], :scope => :baz
163
+ def translate(key, options = {})
164
+ locale = options.delete(:locale) || I18n.locale
165
+ backend.translate(locale, key, options)
166
+ rescue I18n::ArgumentError => e
167
+ raise e if options[:raise]
168
+ send(@@exception_handler, e, locale, key, options)
169
+ end
170
+ alias :t :translate
171
+
172
+ # Localizes certain objects, such as dates and numbers to local formatting.
173
+ def localize(object, options = {})
174
+ locale = options[:locale] || I18n.locale
175
+ format = options[:format] || :default
176
+ backend.localize(locale, object, format)
177
+ end
178
+ alias :l :localize
179
+
180
+ protected
181
+ # Handles exceptions raised in the backend. All exceptions except for
182
+ # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
183
+ # was caught and the option :raise is not set the handler returns an error
184
+ # message string containing the key/scope.
185
+ def default_exception_handler(exception, locale, key, options)
186
+ return exception.message if MissingTranslationData === exception
187
+ raise exception
188
+ end
189
+
190
+ # Merges the given locale, key and scope into a single array of keys.
191
+ # Splits keys that contain dots into multiple keys. Makes sure all
192
+ # keys are Symbols.
193
+ def normalize_translation_keys(locale, key, scope)
194
+ keys = [locale] + Array(scope) + [key]
195
+ keys = keys.map { |k| k.to_s.split(/\./) }
196
+ keys.flatten.map { |k| k.to_sym }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,214 @@
1
+ require 'yaml'
2
+
3
+ module I18n
4
+ module Backend
5
+ class Simple
6
+ INTERPOLATION_RESERVED_KEYS = %w(scope default)
7
+ MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
8
+
9
+ # Accepts a list of paths to translation files. Loads translations from
10
+ # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
11
+ # for details.
12
+ def load_translations(*filenames)
13
+ filenames.each { |filename| load_file(filename) }
14
+ end
15
+
16
+ # Stores translations for the given locale in memory.
17
+ # This uses a deep merge for the translations hash, so existing
18
+ # translations will be overwritten by new ones only at the deepest
19
+ # level of the hash.
20
+ def store_translations(locale, data)
21
+ merge_translations(locale, data)
22
+ end
23
+
24
+ def translate(locale, key, options = {})
25
+ raise InvalidLocale.new(locale) if locale.nil?
26
+ return key.map { |k| translate(locale, k, options) } if key.is_a? Array
27
+
28
+ reserved = :scope, :default
29
+ count, scope, default = options.values_at(:count, *reserved)
30
+ options.delete(:default)
31
+ values = options.reject { |name, value| reserved.include?(name) }
32
+
33
+ entry = lookup(locale, key, scope)
34
+ if entry.nil?
35
+ entry = default(locale, default, options)
36
+ if entry.nil?
37
+ raise(I18n::MissingTranslationData.new(locale, key, options))
38
+ end
39
+ end
40
+ entry = pluralize(locale, entry, count)
41
+ entry = interpolate(locale, entry, values)
42
+ entry
43
+ end
44
+
45
+ # Acts the same as +strftime+, but returns a localized version of the
46
+ # formatted date string. Takes a key from the date/time formats
47
+ # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
48
+ def localize(locale, object, format = :default)
49
+ raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
50
+
51
+ type = object.respond_to?(:sec) ? 'time' : 'date'
52
+ # TODO only translate these if format is a String?
53
+ formats = translate(locale, :"#{type}.formats")
54
+ format = formats[format.to_sym] if formats && formats[format.to_sym]
55
+ # TODO raise exception unless format found?
56
+ format = format.to_s.dup
57
+
58
+ # TODO only translate these if the format string is actually present
59
+ # TODO check which format strings are present, then bulk translate then, then replace them
60
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
61
+ format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
62
+ format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
63
+ format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
64
+ format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
65
+ object.strftime(format)
66
+ end
67
+
68
+ def initialized?
69
+ @initialized ||= false
70
+ end
71
+
72
+ # Returns an array of locales for which translations are available
73
+ def available_locales
74
+ init_translations unless initialized?
75
+ translations.keys
76
+ end
77
+
78
+ def reload!
79
+ @initialized = false
80
+ @translations = nil
81
+ end
82
+
83
+ protected
84
+ def init_translations
85
+ load_translations(*I18n.load_path.flatten)
86
+ @initialized = true
87
+ end
88
+
89
+ def translations
90
+ @translations ||= {}
91
+ end
92
+
93
+ # Looks up a translation from the translations hash. Returns nil if
94
+ # eiher key is nil, or locale, scope or key do not exist as a key in the
95
+ # nested translations hash. Splits keys or scopes containing dots
96
+ # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
97
+ # <tt>%w(currency format)</tt>.
98
+ def lookup(locale, key, scope = [])
99
+ return unless key
100
+ init_translations unless initialized?
101
+ keys = I18n.send(:normalize_translation_keys, locale, key, scope)
102
+ keys.inject(translations) do |result, k|
103
+ if (x = result[k.to_sym]).nil?
104
+ return nil
105
+ else
106
+ x
107
+ end
108
+ end
109
+ end
110
+
111
+ # Evaluates a default translation.
112
+ # If the given default is a String it is used literally. If it is a Symbol
113
+ # it will be translated with the given options. If it is an Array the first
114
+ # translation yielded will be returned.
115
+ #
116
+ # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
117
+ # <tt>translate(locale, :foo)</tt> does not yield a result.
118
+ def default(locale, default, options = {})
119
+ case default
120
+ when String then default
121
+ when Symbol then translate locale, default, options
122
+ when Array then default.each do |obj|
123
+ result = default(locale, obj, options.dup) and return result
124
+ end and nil
125
+ end
126
+ rescue MissingTranslationData
127
+ nil
128
+ end
129
+
130
+ # Picks a translation from an array according to English pluralization
131
+ # rules. It will pick the first translation if count is not equal to 1
132
+ # and the second translation if it is equal to 1. Other backends can
133
+ # implement more flexible or complex pluralization rules.
134
+ def pluralize(locale, entry, count)
135
+ return entry unless entry.is_a?(Hash) and count
136
+ # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash)
137
+ key = :zero if count == 0 && entry.has_key?(:zero)
138
+ key ||= count == 1 ? :one : :other
139
+ raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
140
+ entry[key]
141
+ end
142
+
143
+ # Interpolates values into a given string.
144
+ #
145
+ # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
146
+ # # => "file test.txt opened by {{user}}"
147
+ #
148
+ # Note that you have to double escape the <tt>\\</tt> when you want to escape
149
+ # the <tt>{{...}}</tt> key in a string (once for the string and once for the
150
+ # interpolation).
151
+ def interpolate(locale, string, values = {})
152
+ return string unless string.is_a?(String)
153
+
154
+ string.gsub(MATCH) do
155
+ escaped, pattern, key = $1, $2, $2.to_sym
156
+
157
+ if escaped
158
+ pattern
159
+ elsif INTERPOLATION_RESERVED_KEYS.include?(pattern)
160
+ raise ReservedInterpolationKey.new(pattern, string)
161
+ elsif !values.include?(key)
162
+ raise MissingInterpolationArgument.new(pattern, string)
163
+ else
164
+ values[key].to_s
165
+ end
166
+ end
167
+ end
168
+
169
+ # Loads a single translations file by delegating to #load_rb or
170
+ # #load_yml depending on the file extension and directly merges the
171
+ # data to the existing translations. Raises I18n::UnknownFileType
172
+ # for all other file extensions.
173
+ def load_file(filename)
174
+ type = File.extname(filename).tr('.', '').downcase
175
+ raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
176
+ data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
177
+ data.each { |locale, d| merge_translations(locale, d) }
178
+ end
179
+
180
+ # Loads a plain Ruby translations file. eval'ing the file must yield
181
+ # a Hash containing translation data with locales as toplevel keys.
182
+ def load_rb(filename)
183
+ eval(IO.read(filename), binding, filename)
184
+ end
185
+
186
+ # Loads a YAML translations file. The data must have locales as
187
+ # toplevel keys.
188
+ def load_yml(filename)
189
+ YAML::load(IO.read(filename))
190
+ end
191
+
192
+ # Deep merges the given translations hash with the existing translations
193
+ # for the given locale
194
+ def merge_translations(locale, data)
195
+ locale = locale.to_sym
196
+ translations[locale] ||= {}
197
+ data = deep_symbolize_keys(data)
198
+
199
+ # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
200
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
201
+ translations[locale].merge!(data, &merger)
202
+ end
203
+
204
+ # Return a new hash with all keys and nested keys converted to symbols.
205
+ def deep_symbolize_keys(hash)
206
+ hash.inject({}) { |result, (key, value)|
207
+ value = deep_symbolize_keys(value) if value.is_a? Hash
208
+ result[(key.to_sym rescue key) || key] = value
209
+ result
210
+ }
211
+ end
212
+ end
213
+ end
214
+ end