synergy_russian 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +113 -0
- data/LICENSE +20 -0
- data/README.textile +272 -0
- data/Rakefile +55 -0
- data/TODO +10 -0
- data/init.rb +3 -0
- data/lib/russian.rb +118 -0
- data/lib/russian/action_view_ext/helpers/date_helper.rb +112 -0
- data/lib/russian/active_record_ext/custom_error_message.rb +163 -0
- data/lib/russian/active_support_ext/parameterize.rb +31 -0
- data/lib/russian/backend/advanced.rb +134 -0
- data/lib/russian/locale/actionview.yml +162 -0
- data/lib/russian/locale/activerecord.yml +95 -0
- data/lib/russian/locale/activesupport.yml +16 -0
- data/lib/russian/locale/datetime.yml +49 -0
- data/lib/russian/locale/pluralize.rb +25 -0
- data/lib/russian/transliteration.rb +72 -0
- data/lib/vendor/i18n/MIT-LICENSE +20 -0
- data/lib/vendor/i18n/README.textile +20 -0
- data/lib/vendor/i18n/Rakefile +5 -0
- data/lib/vendor/i18n/i18n.gemspec +27 -0
- data/lib/vendor/i18n/lib/i18n.rb +199 -0
- data/lib/vendor/i18n/lib/i18n/backend/simple.rb +214 -0
- data/lib/vendor/i18n/lib/i18n/exceptions.rb +53 -0
- data/lib/vendor/i18n/test/all.rb +5 -0
- data/lib/vendor/i18n/test/i18n_exceptions_test.rb +100 -0
- data/lib/vendor/i18n/test/i18n_test.rb +125 -0
- data/lib/vendor/i18n/test/locale/en.rb +1 -0
- data/lib/vendor/i18n/test/locale/en.yml +3 -0
- data/lib/vendor/i18n/test/simple_backend_test.rb +568 -0
- data/spec/fixtures/en.yml +4 -0
- data/spec/fixtures/ru.yml +4 -0
- data/spec/i18n/locale/datetime_spec.rb +99 -0
- data/spec/i18n/locale/pluralization_spec.rb +28 -0
- data/spec/locale_spec.rb +129 -0
- data/spec/russian_spec.rb +136 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/transliteration_spec.rb +51 -0
- 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
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
|