russian 0.2.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG +24 -0
  2. data/Gemfile +9 -0
  3. data/LICENSE +1 -1
  4. data/README.textile +136 -139
  5. data/Rakefile +4 -49
  6. data/TODO +0 -6
  7. data/lib/russian.rb +18 -39
  8. data/lib/russian/action_view_ext/helpers/date_helper.rb +15 -9
  9. data/lib/russian/active_model_ext/custom_error_message.rb +70 -0
  10. data/lib/russian/locale/actionview.yml +100 -50
  11. data/lib/russian/locale/activemodel.yml +50 -0
  12. data/lib/russian/locale/activerecord.yml +24 -24
  13. data/lib/russian/locale/datetime.rb +39 -0
  14. data/lib/russian/locale/datetime.yml +9 -5
  15. data/lib/russian/locale/pluralization.rb +28 -0
  16. data/lib/russian/locale/transliterator.rb +17 -0
  17. data/lib/russian/russian_rails.rb +8 -0
  18. data/lib/russian/version.rb +9 -0
  19. data/russian.gemspec +26 -0
  20. data/spec/locale_spec.rb +5 -87
  21. data/spec/russian_spec.rb +5 -23
  22. metadata +70 -69
  23. data/init.rb +0 -3
  24. data/lib/russian/active_record_ext/custom_error_message.rb +0 -163
  25. data/lib/russian/active_support_ext/parameterize.rb +0 -31
  26. data/lib/russian/backend/advanced.rb +0 -134
  27. data/lib/russian/locale/pluralize.rb +0 -25
  28. data/lib/vendor/i18n/MIT-LICENSE +0 -20
  29. data/lib/vendor/i18n/README.textile +0 -20
  30. data/lib/vendor/i18n/Rakefile +0 -5
  31. data/lib/vendor/i18n/i18n.gemspec +0 -27
  32. data/lib/vendor/i18n/lib/i18n.rb +0 -199
  33. data/lib/vendor/i18n/lib/i18n/backend/simple.rb +0 -214
  34. data/lib/vendor/i18n/lib/i18n/exceptions.rb +0 -53
  35. data/lib/vendor/i18n/test/all.rb +0 -5
  36. data/lib/vendor/i18n/test/i18n_exceptions_test.rb +0 -100
  37. data/lib/vendor/i18n/test/i18n_test.rb +0 -125
  38. data/lib/vendor/i18n/test/locale/en.rb +0 -1
  39. data/lib/vendor/i18n/test/locale/en.yml +0 -3
  40. data/lib/vendor/i18n/test/simple_backend_test.rb +0 -568
  41. data/lib/vendor/i18n_label/README.textile +0 -38
  42. data/lib/vendor/i18n_label/Rakefile +0 -11
  43. data/lib/vendor/i18n_label/init.rb +0 -1
  44. data/lib/vendor/i18n_label/install.rb +0 -1
  45. data/lib/vendor/i18n_label/lib/i18n_label.rb +0 -23
  46. data/lib/vendor/i18n_label/spec/i18n_label_spec.rb +0 -20
  47. data/lib/vendor/i18n_label/spec/spec_helper.rb +0 -10
  48. data/lib/vendor/i18n_label/tasks/i18n_label_tasks.rake +0 -4
  49. data/lib/vendor/i18n_label/uninstall.rb +0 -1
@@ -1,214 +0,0 @@
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
@@ -1,53 +0,0 @@
1
- module I18n
2
- class ArgumentError < ::ArgumentError; end
3
-
4
- class InvalidLocale < ArgumentError
5
- attr_reader :locale
6
- def initialize(locale)
7
- @locale = locale
8
- super "#{locale.inspect} is not a valid locale"
9
- end
10
- end
11
-
12
- class MissingTranslationData < ArgumentError
13
- attr_reader :locale, :key, :options
14
- def initialize(locale, key, options)
15
- @key, @locale, @options = key, locale, options
16
- keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope])
17
- keys << 'no key' if keys.size < 2
18
- super "translation missing: #{keys.join(', ')}"
19
- end
20
- end
21
-
22
- class InvalidPluralizationData < ArgumentError
23
- attr_reader :entry, :count
24
- def initialize(entry, count)
25
- @entry, @count = entry, count
26
- super "translation data #{entry.inspect} can not be used with :count => #{count}"
27
- end
28
- end
29
-
30
- class MissingInterpolationArgument < ArgumentError
31
- attr_reader :key, :string
32
- def initialize(key, string)
33
- @key, @string = key, string
34
- super "interpolation argument #{key} missing in #{string.inspect}"
35
- end
36
- end
37
-
38
- class ReservedInterpolationKey < ArgumentError
39
- attr_reader :key, :string
40
- def initialize(key, string)
41
- @key, @string = key, string
42
- super "reserved key #{key.inspect} used in #{string.inspect}"
43
- end
44
- end
45
-
46
- class UnknownFileType < ArgumentError
47
- attr_reader :type, :filename
48
- def initialize(type, filename)
49
- @type, @filename = type, filename
50
- super "can not load translations from #{filename}, the file type #{type} is not known"
51
- end
52
- end
53
- end
@@ -1,5 +0,0 @@
1
- dir = File.dirname(__FILE__)
2
- require dir + '/i18n_test.rb'
3
- require dir + '/simple_backend_test.rb'
4
- require dir + '/i18n_exceptions_test.rb'
5
- # *require* dir + '/custom_backend_test.rb'
@@ -1,100 +0,0 @@
1
- $:.unshift "lib"
2
-
3
- require 'rubygems'
4
- require 'test/unit'
5
- require 'mocha'
6
- require 'i18n'
7
- require 'active_support'
8
-
9
- class I18nExceptionsTest < Test::Unit::TestCase
10
- def test_invalid_locale_stores_locale
11
- force_invalid_locale
12
- rescue I18n::ArgumentError => e
13
- assert_nil e.locale
14
- end
15
-
16
- def test_invalid_locale_message
17
- force_invalid_locale
18
- rescue I18n::ArgumentError => e
19
- assert_equal 'nil is not a valid locale', e.message
20
- end
21
-
22
- def test_missing_translation_data_stores_locale_key_and_options
23
- force_missing_translation_data
24
- rescue I18n::ArgumentError => e
25
- options = {:scope => :bar}
26
- assert_equal 'de', e.locale
27
- assert_equal :foo, e.key
28
- assert_equal options, e.options
29
- end
30
-
31
- def test_missing_translation_data_message
32
- force_missing_translation_data
33
- rescue I18n::ArgumentError => e
34
- assert_equal 'translation missing: de, bar, foo', e.message
35
- end
36
-
37
- def test_invalid_pluralization_data_stores_entry_and_count
38
- force_invalid_pluralization_data
39
- rescue I18n::ArgumentError => e
40
- assert_equal [:bar], e.entry
41
- assert_equal 1, e.count
42
- end
43
-
44
- def test_invalid_pluralization_data_message
45
- force_invalid_pluralization_data
46
- rescue I18n::ArgumentError => e
47
- assert_equal 'translation data [:bar] can not be used with :count => 1', e.message
48
- end
49
-
50
- def test_missing_interpolation_argument_stores_key_and_string
51
- force_missing_interpolation_argument
52
- rescue I18n::ArgumentError => e
53
- assert_equal 'bar', e.key
54
- assert_equal "{{bar}}", e.string
55
- end
56
-
57
- def test_missing_interpolation_argument_message
58
- force_missing_interpolation_argument
59
- rescue I18n::ArgumentError => e
60
- assert_equal 'interpolation argument bar missing in "{{bar}}"', e.message
61
- end
62
-
63
- def test_reserved_interpolation_key_stores_key_and_string
64
- force_reserved_interpolation_key
65
- rescue I18n::ArgumentError => e
66
- assert_equal 'scope', e.key
67
- assert_equal "{{scope}}", e.string
68
- end
69
-
70
- def test_reserved_interpolation_key_message
71
- force_reserved_interpolation_key
72
- rescue I18n::ArgumentError => e
73
- assert_equal 'reserved key "scope" used in "{{scope}}"', e.message
74
- end
75
-
76
- private
77
- def force_invalid_locale
78
- I18n.backend.translate nil, :foo
79
- end
80
-
81
- def force_missing_translation_data
82
- I18n.backend.store_translations 'de', :bar => nil
83
- I18n.backend.translate 'de', :foo, :scope => :bar
84
- end
85
-
86
- def force_invalid_pluralization_data
87
- I18n.backend.store_translations 'de', :foo => [:bar]
88
- I18n.backend.translate 'de', :foo, :count => 1
89
- end
90
-
91
- def force_missing_interpolation_argument
92
- I18n.backend.store_translations 'de', :foo => "{{bar}}"
93
- I18n.backend.translate 'de', :foo, :baz => 'baz'
94
- end
95
-
96
- def force_reserved_interpolation_key
97
- I18n.backend.store_translations 'de', :foo => "{{scope}}"
98
- I18n.backend.translate 'de', :foo, :baz => 'baz'
99
- end
100
- end
@@ -1,125 +0,0 @@
1
- $:.unshift "lib"
2
-
3
- require 'rubygems'
4
- require 'test/unit'
5
- require 'mocha'
6
- require 'i18n'
7
- require 'active_support'
8
-
9
- class I18nTest < Test::Unit::TestCase
10
- def setup
11
- I18n.backend.store_translations :'en', {
12
- :currency => {
13
- :format => {
14
- :separator => '.',
15
- :delimiter => ',',
16
- }
17
- }
18
- }
19
- end
20
-
21
- def test_uses_simple_backend_set_by_default
22
- assert I18n.backend.is_a?(I18n::Backend::Simple)
23
- end
24
-
25
- def test_can_set_backend
26
- assert_nothing_raised{ I18n.backend = self }
27
- assert_equal self, I18n.backend
28
- I18n.backend = I18n::Backend::Simple.new
29
- end
30
-
31
- def test_uses_en_us_as_default_locale_by_default
32
- assert_equal 'en', I18n.default_locale
33
- end
34
-
35
- def test_can_set_default_locale
36
- assert_nothing_raised{ I18n.default_locale = 'de' }
37
- assert_equal 'de', I18n.default_locale
38
- I18n.default_locale = 'en'
39
- end
40
-
41
- def test_uses_default_locale_as_locale_by_default
42
- assert_equal I18n.default_locale, I18n.locale
43
- end
44
-
45
- def test_can_set_locale_to_thread_current
46
- assert_nothing_raised{ I18n.locale = 'de' }
47
- assert_equal 'de', I18n.locale
48
- assert_equal 'de', Thread.current[:locale]
49
- I18n.locale = 'en'
50
- end
51
-
52
- def test_can_set_exception_handler
53
- assert_nothing_raised{ I18n.exception_handler = :custom_exception_handler }
54
- I18n.exception_handler = :default_exception_handler # revert it
55
- end
56
-
57
- def test_uses_custom_exception_handler
58
- I18n.exception_handler = :custom_exception_handler
59
- I18n.expects(:custom_exception_handler)
60
- I18n.translate :bogus
61
- I18n.exception_handler = :default_exception_handler # revert it
62
- end
63
-
64
- def test_delegates_translate_to_backend
65
- I18n.backend.expects(:translate).with 'de', :foo, {}
66
- I18n.translate :foo, :locale => 'de'
67
- end
68
-
69
- def test_delegates_localize_to_backend
70
- I18n.backend.expects(:localize).with 'de', :whatever, :default
71
- I18n.localize :whatever, :locale => 'de'
72
- end
73
-
74
- def test_translate_given_no_locale_uses_i18n_locale
75
- I18n.backend.expects(:translate).with 'en', :foo, {}
76
- I18n.translate :foo
77
- end
78
-
79
- def test_translate_on_nested_symbol_keys_works
80
- assert_equal ".", I18n.t(:'currency.format.separator')
81
- end
82
-
83
- def test_translate_with_nested_string_keys_works
84
- assert_equal ".", I18n.t('currency.format.separator')
85
- end
86
-
87
- def test_translate_with_array_as_scope_works
88
- assert_equal ".", I18n.t(:separator, :scope => ['currency.format'])
89
- end
90
-
91
- def test_translate_with_array_containing_dot_separated_strings_as_scope_works
92
- assert_equal ".", I18n.t(:separator, :scope => ['currency.format'])
93
- end
94
-
95
- def test_translate_with_key_array_and_dot_separated_scope_works
96
- assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format')
97
- end
98
-
99
- def test_translate_with_dot_separated_key_array_and_scope_works
100
- assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency')
101
- end
102
-
103
- def test_translate_with_options_using_scope_works
104
- I18n.backend.expects(:translate).with('de', :precision, :scope => :"currency.format")
105
- I18n.with_options :locale => 'de', :scope => :'currency.format' do |locale|
106
- locale.t :precision
107
- end
108
- end
109
-
110
- # def test_translate_given_no_args_raises_missing_translation_data
111
- # assert_equal "translation missing: en, no key", I18n.t
112
- # end
113
-
114
- def test_translate_given_a_bogus_key_raises_missing_translation_data
115
- assert_equal "translation missing: en, bogus", I18n.t(:bogus)
116
- end
117
-
118
- def test_localize_nil_raises_argument_error
119
- assert_raises(I18n::ArgumentError) { I18n.l nil }
120
- end
121
-
122
- def test_localize_object_raises_argument_error
123
- assert_raises(I18n::ArgumentError) { I18n.l Object.new }
124
- end
125
- end