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
data/TODO ADDED
@@ -0,0 +1,10 @@
1
+ TODO
2
+ ====
3
+ * RDoc
4
+ * check Unicode CLDR to ensure all datetime formats are correct
5
+ * refactor Advanced backend localize method (looks ugly)
6
+ * refactor DateTimeSelector#translated_month_names
7
+
8
+ Questionable
9
+ ============
10
+ * integration specs for rails hacks
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Rails plugin init
2
+ require 'russian'
3
+
data/lib/russian.rb ADDED
@@ -0,0 +1,118 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ if RUBY_VERSION < "1.9"
4
+ $KCODE = 'u'
5
+ end
6
+
7
+ $:.push File.join(File.dirname(__FILE__), 'russian')
8
+ require 'transliteration'
9
+
10
+ # I18n require
11
+ unless defined?(I18n)
12
+ $:.push File.join(File.dirname(__FILE__), 'vendor', 'i18n', 'lib')
13
+ require 'i18n'
14
+ end
15
+ # Advanced backend
16
+ require 'backend/advanced'
17
+
18
+ # Rails hacks
19
+ require 'active_record_ext/custom_error_message' if defined?(ActiveRecord)
20
+ if defined?(ActionView::Helpers)
21
+ require 'action_view_ext/helpers/date_helper'
22
+ end
23
+ require 'active_support_ext/parameterize' if defined?(ActiveSupport::Inflector)
24
+
25
+ module Russian
26
+ extend self
27
+
28
+ module VERSION
29
+ MAJOR = 0
30
+ MINOR = 2
31
+ TINY = 7
32
+
33
+ STRING = [MAJOR, MINOR, TINY].join('.')
34
+ end
35
+
36
+ # Russian locale
37
+ LOCALE = :'ru'
38
+
39
+ # Russian locale
40
+ def locale
41
+ LOCALE
42
+ end
43
+
44
+ # Returns custom backend class for usage with Russian library
45
+ #
46
+ # See I18n::Backend
47
+ def i18n_backend_class
48
+ I18n::Backend::Advanced
49
+ end
50
+
51
+ # Init Russian i18n: set custom backend, set default locale to Russian locale, load all translations
52
+ # shipped with library.
53
+ def init_i18n
54
+ I18n.backend = Russian.i18n_backend_class.new
55
+ I18n.default_locale = LOCALE
56
+ I18n.load_path.unshift(*locale_files)
57
+ end
58
+
59
+ # See I18n::translate
60
+ def translate(key, options = {})
61
+ I18n.translate(key, options.merge({ :locale => LOCALE }))
62
+ end
63
+ alias :t :translate
64
+
65
+ # See I18n::localize
66
+ def localize(object, options = {})
67
+ I18n.localize(object, options.merge({ :locale => LOCALE }))
68
+ end
69
+ alias :l :localize
70
+
71
+ # strftime() proxy with Russian localization
72
+ def strftime(object, format = :default)
73
+ localize(object, { :format => format })
74
+ end
75
+
76
+ # Simple pluralization proxy
77
+ #
78
+ # Usage:
79
+ # Russian.pluralize(1, "вещь", "вещи", "вещей")
80
+ # Russian.pluralize(3.14, "вещь", "вещи", "вещей", "вещи")
81
+ def pluralize(n, *variants)
82
+ raise ArgumentError, "Must have a Numeric as a first parameter" unless n.is_a?(Numeric)
83
+ raise ArgumentError, "Must have at least 3 variants for pluralization" if variants.size < 3
84
+ raise ArgumentError, "Must have at least 4 variants for pluralization" if (variants.size < 4 && n != n.round)
85
+ variants_hash = pluralization_variants_to_hash(*variants)
86
+ I18n.backend.send(:pluralize, LOCALE, variants_hash, n)
87
+ end
88
+ alias :p :pluralize
89
+
90
+ # Transliteration for russian language
91
+ #
92
+ # Usage:
93
+ # Russian.translit("рубин")
94
+ # Russian.transliterate("рубин")
95
+ def transliterate(str)
96
+ Russian::Transliteration.transliterate(str)
97
+ end
98
+ alias :translit :transliterate
99
+
100
+ protected
101
+ # Returns all locale files shipped with library
102
+ def locale_files
103
+ Dir[File.join(File.dirname(__FILE__), "russian", "locale", "**/*")]
104
+ end
105
+
106
+ # Converts an array of pluralization variants to a Hash that can be used
107
+ # with I18n pluralization.
108
+ def pluralization_variants_to_hash(*variants)
109
+ {
110
+ :one => variants[0],
111
+ :few => variants[1],
112
+ :many => variants[2],
113
+ :other => variants[3]
114
+ }
115
+ end
116
+ end
117
+
118
+ Russian.init_i18n
@@ -0,0 +1,112 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ # Заменяет хелпер Rails <tt>select_month</tt> и метод <tt>translated_month_names</tt>
4
+ # для поддержки функционала "отдельностоящих имен месяцев".
5
+ #
6
+ # Теперь можно использовать и полные, и сокращенные название месяцев в двух вариантах -- контекстном
7
+ # (по умолчанию) и отдельностоящем (если в текущем языке есть соответствующие переводы).
8
+ # Теперь хелперы поддерживают ключ <tt>:use_standalone_month_names</tt>, хелпер <tt>select_month</tt>
9
+ # устанавливает его по умолчанию.
10
+ # Отдельностоящие имена месяцев также используютс когда указан ключ <tt>:discard_day</tt>.
11
+ #
12
+ #
13
+ # Replaces Rails <tt>select_month</tt> helper and <tt>translated_month_names</tt> private method to provide
14
+ # "standalone month names" feature.
15
+ #
16
+ # It is now possible to use both abbreviated and full month names in two variants (if current locale provides them).
17
+ # All date helpers support <tt>:use_standalone_month_names</tt> key now, <tt>select_month</tt> helper sets
18
+ # it to true by default.
19
+ # Standalone month names are also used when <tt>:discard_day</tt> key is provided.
20
+ if defined?(ActionView::Helpers::DateTimeSelector) && ActionView::Helpers::DateTimeSelector.private_instance_methods.map(&:to_sym).include?(:translated_month_names)
21
+ module ActionView
22
+ module Helpers
23
+ module DateHelper
24
+ # Returns a select tag with options for each of the months January through December with the current month
25
+ # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
26
+ # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
27
+ # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
28
+ # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
29
+ # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
30
+ # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
31
+ # You can also choose if you want to use i18n standalone month names or default month names -- you can
32
+ # force standalone month names usage by using <tt>:use_standalone_month_names</tt> key.
33
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
34
+ #
35
+ #
36
+ # Также поддерживается ключ <tt>:use_standalone_month_names</tt> для явного указания о необходимости
37
+ # использования отдельностоящих имен месяцев, если текущий язык их поддерживает.
38
+ #
39
+ #
40
+ # ==== Examples
41
+ # # Generates a select field for months that defaults to the current month that
42
+ # # will use keys like "January", "March".
43
+ # select_month(Date.today)
44
+ #
45
+ # # Generates a select field for months that defaults to the current month that
46
+ # # is named "start" rather than "month"
47
+ # select_month(Date.today, :field_name => 'start')
48
+ #
49
+ # # Generates a select field for months that defaults to the current month that
50
+ # # will use keys like "1", "3".
51
+ # select_month(Date.today, :use_month_numbers => true)
52
+ #
53
+ # # Generates a select field for months that defaults to the current month that
54
+ # # will use keys like "1 - January", "3 - March".
55
+ # select_month(Date.today, :add_month_numbers => true)
56
+ #
57
+ # # Generates a select field for months that defaults to the current month that
58
+ # # will use keys like "Jan", "Mar".
59
+ # select_month(Date.today, :use_short_month => true)
60
+ #
61
+ # # Generates a select field for months that defaults to the current month that
62
+ # # will use keys like "Januar", "Marts."
63
+ # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
64
+ #
65
+ def select_month(date, options = {}, html_options = {})
66
+ DateTimeSelector.new(date, options.merge(:use_standalone_month_names => true), html_options).select_month
67
+ end
68
+ end
69
+
70
+ class DateTimeSelector #:nodoc:
71
+ private
72
+ # Returns translated month names
73
+ # => [nil, "January", "February", "March",
74
+ # "April", "May", "June", "July",
75
+ # "August", "September", "October",
76
+ # "November", "December"]
77
+ #
78
+ # If :use_short_month option is set
79
+ # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
80
+ # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
81
+ #
82
+ # Also looks up if <tt>:discard_day</tt> or <tt>:use_standalone_month_names</tt> option is set
83
+ # and uses i18n standalone month names if so.
84
+ #
85
+ #
86
+ # Также в зависимости от использования ключей <tt>:discard_day</tt> или <tt>:use_standalone_month_names</tt>
87
+ # убеждается, есть ли соотвествующие переводы в текущем языке и использует "отдельностоящие" названия
88
+ # месяцев по необходимости
89
+ def translated_month_names
90
+ begin
91
+ if @options[:use_short_month]
92
+ if (@options[:discard_day] || @options[:use_standalone_month_names]) && I18n.translate(:'date.standalone_abbr_month_names')
93
+ key = :'date.standalone_abbr_month_names'
94
+ else
95
+ key = :'date.abbr_month_names'
96
+ end
97
+ else
98
+ if (@options[:discard_day] || @options[:use_standalone_month_names]) && I18n.translate(:'date.standalone_month_names')
99
+ key = :'date.standalone_month_names'
100
+ else
101
+ key = :'date.month_names'
102
+ end
103
+ end
104
+
105
+ I18n.translate(key, :locale => @options[:locale])
106
+ end
107
+ end
108
+
109
+ end
110
+ end
111
+ end
112
+ end # if defined?
@@ -0,0 +1,163 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ if defined?(ActiveRecord::Error) # Rails 2.3.4+
4
+ module ActiveRecord
5
+ class Error
6
+ protected
7
+ if instance_methods.map(&:to_sym).include?(:default_options) # Rails 2.3.5+
8
+ # Redefine the ActiveRecord::Error::generate_full_message method:
9
+ # Returns all the full error messages in an array. 'Base' messages are handled as usual.
10
+ # Non-base messages are prefixed with the attribute name as usual UNLESS they begin with '^'
11
+ # in which case the attribute name is omitted.
12
+ # E.g. validates_acceptance_of :accepted_terms, :message => '^Please accept the terms of service'
13
+ #
14
+ #
15
+ # Переопределяет метод ActiveRecord::Error::generate_full_message. Сообщения об ошибках для атрибутов
16
+ # теперь не имеют префикса с названием атрибута если в сообщении об ошибке первым символом указан "^".
17
+ #
18
+ # Так, например,
19
+ #
20
+ # validates_acceptance_of :accepted_terms, :message => 'нужно принять соглашение'
21
+ #
22
+ # даст сообщение
23
+ #
24
+ # Accepted terms нужно принять соглашение
25
+ #
26
+ # однако,
27
+ #
28
+ # validates_acceptance_of :accepted_terms, :message => '^Нужно принять соглашение'
29
+ #
30
+ # даст сообщение
31
+ #
32
+ # Нужно принять соглашение
33
+ def generate_full_message(options = {})
34
+ keys = [
35
+ :"full_messages.#{@message}",
36
+ :'full_messages.format',
37
+ '%{attribute} %{message}'
38
+ ]
39
+
40
+ if self.message.is_a?(String) && self.message =~ /^\^/
41
+ ActiveSupport::Deprecation.warn("Using '^' hack for ActiveRecord error messages has been deprecated. Please use errors.full_messages.format I18n key for formatting")
42
+
43
+ options[:full_message] = self.message[1..-1]
44
+
45
+ keys = [
46
+ :"full_messages.#{@message}",
47
+ '%{full_message}'
48
+ ]
49
+ else
50
+ keys = [
51
+ :"full_messages.#{@message}",
52
+ :'full_messages.format',
53
+ '%{attribute} %{message}'
54
+ ]
55
+ end
56
+
57
+ options.merge!(:default => keys, :message => self.message)
58
+
59
+ I18n.translate(keys.shift, options)
60
+ end
61
+ else # Rails 2.3.4
62
+ # Redefine the ActiveRecord::Error::generate_full_message method:
63
+ # Returns all the full error messages in an array. 'Base' messages are handled as usual.
64
+ # Non-base messages are prefixed with the attribute name as usual UNLESS they begin with '^'
65
+ # in which case the attribute name is omitted.
66
+ # E.g. validates_acceptance_of :accepted_terms, :message => '^Please accept the terms of service'
67
+ #
68
+ #
69
+ # Переопределяет метод ActiveRecord::Error::generate_full_message. Сообщения об ошибках для атрибутов
70
+ # теперь не имеют префикса с названием атрибута если в сообщении об ошибке первым символом указан "^".
71
+ #
72
+ # Так, например,
73
+ #
74
+ # validates_acceptance_of :accepted_terms, :message => 'нужно принять соглашение'
75
+ #
76
+ # даст сообщение
77
+ #
78
+ # Accepted terms нужно принять соглашение
79
+ #
80
+ # однако,
81
+ #
82
+ # validates_acceptance_of :accepted_terms, :message => '^Нужно принять соглашение'
83
+ #
84
+ # даст сообщение
85
+ #
86
+ # Нужно принять соглашение
87
+ def generate_full_message(message, options = {})
88
+ options.reverse_merge! :message => self.message,
89
+ :model => @base.class.human_name,
90
+ :attribute => @base.class.human_attribute_name(attribute.to_s),
91
+ :value => value
92
+
93
+ key = :"full_messages.#{@message}"
94
+ defaults = [:'full_messages.format', '%{attribute} %{message}']
95
+
96
+ if options[:message].is_a?(String) && options[:message] =~ /^\^/
97
+ ActiveSupport::Deprecation.warn("Using '^' hack for ActiveRecord error messages has been deprecated. Please use errors.full_messages.format I18n key for formatting")
98
+
99
+ options[:full_message] = options[:message][1..-1]
100
+ defaults = [:"full_messages.#{@message}.format", '%{full_message}']
101
+ end
102
+
103
+ I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors]))
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ else # Rails 2.3.3-
110
+ module ActiveRecord
111
+ class Errors
112
+ # DEPRECATED as of Rails 2.3.4
113
+ #
114
+ # The following is taken from custom_error_message plugin by David Easley
115
+ # (http://rubyforge.org/projects/custom-err-msg/)
116
+ #
117
+ # Redefine the ActiveRecord::Errors::full_messages method:
118
+ # Returns all the full error messages in an array. 'Base' messages are handled as usual.
119
+ # Non-base messages are prefixed with the attribute name as usual UNLESS they begin with '^'
120
+ # in which case the attribute name is omitted.
121
+ # E.g. validates_acceptance_of :accepted_terms, :message => '^Please accept the terms of service'
122
+ #
123
+ #
124
+ # Переопределяет метод ActiveRecord::Errors::full_messages. Сообщения об ошибках для атрибутов
125
+ # теперь не имеют префикса с названием атрибута если в сообщении об ошибке первым символом указан "^".
126
+ #
127
+ # Так, например,
128
+ #
129
+ # validates_acceptance_of :accepted_terms, :message => 'нужно принять соглашение'
130
+ #
131
+ # даст сообщение
132
+ #
133
+ # Accepted terms нужно принять соглашение
134
+ #
135
+ # однако,
136
+ #
137
+ # validates_acceptance_of :accepted_terms, :message => '^Нужно принять соглашение'
138
+ #
139
+ # даст сообщение
140
+ #
141
+ # Нужно принять соглашение
142
+ def full_messages
143
+ full_messages = []
144
+
145
+ @errors.each_key do |attr|
146
+ @errors[attr].each do |msg|
147
+ next if msg.nil?
148
+
149
+ if attr == "base"
150
+ full_messages << msg
151
+ elsif msg =~ /^\^/
152
+ full_messages << msg[1..-1]
153
+ else
154
+ full_messages << @base.class.human_attribute_name(attr) + " " + msg
155
+ end
156
+ end
157
+ end
158
+
159
+ return full_messages
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module ActiveSupport
4
+ module Inflector
5
+ # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
6
+ # Transliterates all russian characters in string first, then passes it to ActiveSupport Inflector.
7
+ #
8
+ # Заменяет все спецсимволы в строке так, что результат может использоваться как часть "красивого" URL.
9
+ # От стандартного ActiveSupport Inflector отличается тем, что сначала производится транслитерация
10
+ # букв русского алфавита.
11
+ #
12
+ # ==== Examples
13
+ #
14
+ # class Person
15
+ # def to_param
16
+ # "#{id}-#{name.parameterize}"
17
+ # end
18
+ # end
19
+ #
20
+ # @person = Person.find(1)
21
+ # # => #<Person id: 1, name: "Дональд Кнут">
22
+ #
23
+ # <%= link_to(@person.name, person_path %>
24
+ # # => <a href="/person/1-donald-knut">Дональд Кнут</a>
25
+ def parameterize_with_russian(string, sep = '-')
26
+ parameterize_without_russian(Russian::transliterate(string), sep)
27
+ end
28
+ alias_method :parameterize_without_russian, :parameterize
29
+ alias_method :parameterize, :parameterize_with_russian
30
+ end
31
+ end
@@ -0,0 +1,134 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module I18n
4
+ module Backend
5
+ # "Продвинутый" бекэнд для I18n.
6
+ #
7
+ # Наследует Simple бекэнд и полностью с ним совместим. Добаляет поддержку
8
+ # для отдельностоящих/контекстных названий дней недели и месяцев.
9
+ # Также позволяет каждому языку использовать собственные правила плюрализации,
10
+ # объявленные как Proc (<tt>lambda</tt>).
11
+ #
12
+ #
13
+ # Advanced I18n backend.
14
+ #
15
+ # Extends Simple backend. Allows usage of "standalone" keys
16
+ # for DateTime localization and usage of user-defined Proc (lambda) pluralization
17
+ # methods in translation tables.
18
+ class Advanced < Simple
19
+ LOCALIZE_ABBR_MONTH_NAMES_MATCH = /(%d|%e)(.*)(%b)/
20
+ LOCALIZE_MONTH_NAMES_MATCH = /(%d|%e)(.*)(%B)/
21
+ LOCALIZE_STANDALONE_ABBR_DAY_NAMES_MATCH = /^%a/
22
+ LOCALIZE_STANDALONE_DAY_NAMES_MATCH = /^%A/
23
+
24
+ # Acts the same as +strftime+, but returns a localized version of the
25
+ # formatted date string. Takes a key from the date/time formats
26
+ # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
27
+ #
28
+ #
29
+ # Метод отличается от <tt>localize</tt> в Simple бекэнде поддержкой
30
+ # отдельностоящих/контекстных названий дней недели и месяцев.
31
+ #
32
+ #
33
+ # Note that it differs from <tt>localize</tt> in Simple< backend by checking for
34
+ # "standalone" month name/day name keys in translation and using them if available.
35
+ #
36
+ # <tt>options</tt> parameter added for i18n-0.3 compliance.
37
+ def localize(locale, object, format = :default, options = nil)
38
+ raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
39
+
40
+ type = object.respond_to?(:sec) ? 'time' : 'date'
41
+ # TODO only translate these if format is a String?
42
+ formats = translate(locale, :"#{type}.formats")
43
+ format = formats[format.to_sym] if formats && formats[format.to_sym]
44
+ # TODO raise exception unless format found?
45
+ format = format.to_s.dup
46
+
47
+ # TODO only translate these if the format string is actually present
48
+ # TODO check which format strings are present, then bulk translate then, then replace them
49
+
50
+ if lookup(locale, :"date.standalone_abbr_day_names")
51
+ format.gsub!(LOCALIZE_STANDALONE_ABBR_DAY_NAMES_MATCH,
52
+ translate(locale, :"date.standalone_abbr_day_names")[object.wday])
53
+ end
54
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
55
+
56
+ if lookup(locale, :"date.standalone_day_names")
57
+ format.gsub!(LOCALIZE_STANDALONE_DAY_NAMES_MATCH,
58
+ translate(locale, :"date.standalone_day_names")[object.wday])
59
+ end
60
+ format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
61
+
62
+ if lookup(locale, :"date.standalone_abbr_month_names")
63
+ format.gsub!(LOCALIZE_ABBR_MONTH_NAMES_MATCH) do
64
+ $1 + $2 + translate(locale, :"date.abbr_month_names")[object.mon]
65
+ end
66
+ format.gsub!(/%b/, translate(locale, :"date.standalone_abbr_month_names")[object.mon])
67
+ else
68
+ format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
69
+ end
70
+
71
+ if lookup(locale, :"date.standalone_month_names")
72
+ format.gsub!(LOCALIZE_MONTH_NAMES_MATCH) do
73
+ $1 + $2 + translate(locale, :"date.month_names")[object.mon]
74
+ end
75
+ format.gsub!(/%B/, translate(locale, :"date.standalone_month_names")[object.mon])
76
+ else
77
+ format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
78
+ end
79
+
80
+ format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
81
+ object.strftime(format)
82
+ end
83
+
84
+ protected
85
+ # Использует правила плюрализации из таблицы переводов для языка (если присутствуют),
86
+ # иначе использует правило плюрализации по умолчанию (английский язык).
87
+ #
88
+ # Пример задания правила в таблице переводов:
89
+ #
90
+ # store_translations :'en', {
91
+ # :pluralize => lambda { |n| n == 1 ? :one : :other }
92
+ # }
93
+ #
94
+ # Правило должно возвращать один из символов для таблицы переводов:
95
+ # :zero, :one, :two, :few, :many, :other
96
+ #
97
+ #
98
+ # Picks a pluralization rule specified in translation tables for a language or
99
+ # uses default pluralization rules.
100
+ #
101
+ # This is how pluralization rules are defined in translation tables, English
102
+ # language for example:
103
+ #
104
+ # store_translations :'en', {
105
+ # :pluralize => lambda { |n| n == 1 ? :one : :other }
106
+ # }
107
+ #
108
+ # Rule must return a symbol to use with pluralization, it must be one of:
109
+ # :zero, :one, :two, :few, :many, :other
110
+ def pluralize(locale, entry, count)
111
+ return entry unless entry.is_a?(Hash) and count
112
+
113
+ key = :zero if count == 0 && entry.has_key?(:zero)
114
+ locale_pluralize = lookup(locale, :pluralize)
115
+ if locale_pluralize && locale_pluralize.respond_to?(:call)
116
+ key ||= locale_pluralize.call(count)
117
+ else
118
+ key ||= default_pluralizer(count)
119
+ end
120
+ raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
121
+
122
+ entry[key]
123
+ end
124
+
125
+ # Default pluralizer, used if pluralization rule is not defined in translations.
126
+ #
127
+ # Uses English pluralization rules -- it will pick the first translation if count is not equal to 1
128
+ # and the second translation if it is equal to 1.
129
+ def default_pluralizer(count)
130
+ count == 1 ? :one : :other
131
+ end
132
+ end
133
+ end
134
+ end