russian 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Yaroslav Markin
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.
@@ -0,0 +1,44 @@
1
+ h1. Russian
2
+
3
+ Russian language support for Ruby and Rails, using I18n library (also shipped in Rails 2.2+).
4
+
5
+ Поддержка русского языка для Ruby и Rails при помощи библиотеки I18n (также входит в состав Rails 2.2+)
6
+
7
+ h3. If you don't speak Russian
8
+
9
+ This code may still be useful for you. You can learn how to create custom backends for I18n and how to
10
+ provide support of "standalone" (as defined in "Unicode CLDR":http://unicode.org/cldr/) month names with I18n and Rails, and also how to add dead simple pluralization rules into your translation tables.
11
+
12
+ h1. Что это
13
+
14
+ Russian -- это библиотека для полноценной поддержки русского языка (форматирование даты и времени, плюрализация, локализация в целом) для Ruby и Ruby on Rails.
15
+
16
+ Для этого используется библиотека I18n, несколько хаков поверх нее и файлы переводов.
17
+
18
+ I18n входит в состав Ruby on Rails начиная с версии 2.2, но на момент публикации нормальная поддержка русского языка отсутствовала.
19
+
20
+ h1. Установка
21
+
22
+ TODO: пути на GitHub, публикация на RubyForge
23
+
24
+ * gem rubyforge
25
+ * gem github
26
+
27
+ h2. Ruby on Rails
28
+
29
+ * gem
30
+ * plugin github
31
+
32
+ h1. Использование
33
+
34
+ * Прозрачное: локаль по умолчанию, свой бекенд (недеструктивный)
35
+ * Хелперы Russian.*
36
+
37
+ h2. Ruby on Rails
38
+
39
+ * Стандартный язык для I18n
40
+ * Перегрузка хелперов
41
+
42
+ h1. Автор
43
+
44
+ Ярослав Маркин ("yaroslav@markin.net":mailto:yaroslav@markin.net)
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+ require 'rubygems/specification'
5
+ require 'date'
6
+
7
+ GEM = "russian"
8
+ GEM_VERSION = "0.0.1"
9
+ AUTHOR = "Yaroslav Markin"
10
+ EMAIL = "yaroslav@markin.net"
11
+ HOMEPAGE = "http://github.com/yaroslav/russian/"
12
+ SUMMARY = "Russian language support for Ruby and Rails"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = GEM
16
+ s.version = GEM_VERSION
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = true
19
+ s.extra_rdoc_files = ["README.textile", "LICENSE", 'TODO']
20
+ s.summary = SUMMARY
21
+ s.description = s.summary
22
+ s.author = AUTHOR
23
+ s.email = EMAIL
24
+ s.homepage = HOMEPAGE
25
+
26
+ # Uncomment this to add a dependency
27
+ # s.add_dependency "foo"
28
+
29
+ s.require_path = 'lib'
30
+ s.autorequire = GEM
31
+ s.files = %w(LICENSE README.textile Rakefile TODO init.rb) + Dir.glob("{lib,spec}/**/*")
32
+ end
33
+
34
+ Rake::GemPackageTask.new(spec) do |pkg|
35
+ pkg.gem_spec = spec
36
+ end
37
+
38
+ task :default => :spec
39
+ desc "Run specs"
40
+ Spec::Rake::SpecTask.new do |t|
41
+ t.spec_files = FileList['spec/**/*_spec.rb']
42
+ t.spec_opts = %w(-fs --color)
43
+ end
44
+
45
+ desc "install the gem locally"
46
+ task :install => [:package] do
47
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
48
+ end
49
+
50
+ desc "create a gemspec file"
51
+ task :make_spec do
52
+ File.open("#{GEM}.gemspec", "w") do |file|
53
+ file.puts spec.to_ruby
54
+ end
55
+ end
data/TODO ADDED
@@ -0,0 +1,11 @@
1
+ TODO
2
+ ====
3
+ * docs and README (english - brief, full russian doc)
4
+ * check Unicode CLDR to ensure all datetime formats are correct
5
+ * refactor Advanced backend localize method (looks ugly)
6
+
7
+ Questionable
8
+ ============
9
+ * integration spec coverage for actionview locale
10
+ * integration spec coverage for activerecord locale
11
+ * integration spec coverage for activesupport locale
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Rails plugin init
2
+ require 'russian'
3
+
@@ -0,0 +1,91 @@
1
+ $KCODE='u'
2
+
3
+ $:.push File.join(File.dirname(__FILE__), 'russian')
4
+ $:.push File.join(File.dirname(__FILE__), 'vendor', 'i18n', 'lib')
5
+
6
+ require 'i18n' unless defined?(I18n)
7
+
8
+ require 'backend/advanced'
9
+ require 'action_view_ext/helpers/date_helper'
10
+ require 'active_record_ext/custom_error_message'
11
+
12
+ module Russian
13
+ module VERSION
14
+ MAJOR = 0
15
+ MINOR = 0
16
+ TINY = 1
17
+
18
+ STRING = [MAJOR, MINOR, TINY].join('.')
19
+ end
20
+
21
+ # Russian locale
22
+ LOCALE = :'ru-RU'
23
+
24
+ class << self
25
+ # Russian locale
26
+ def locale
27
+ LOCALE
28
+ end
29
+
30
+ # Returns custom backend class for usage with Russian library
31
+ #
32
+ # See I18n::Backend
33
+ def i18n_backend_class
34
+ I18n::Backend::Advanced
35
+ end
36
+
37
+ # Init Russian i18n: set custom backend, set default locale to Russian locale, load all translations
38
+ # shipped with library.
39
+ def init_i18n
40
+ I18n.backend = Russian.i18n_backend_class.new
41
+ I18n.default_locale = LOCALE
42
+ locale_files.each { |file| I18n.backend.load_translations(file) }
43
+ end
44
+
45
+ # See I18n::translate
46
+ def translate(key, options = {})
47
+ I18n.translate(key, options.merge({ :locale => LOCALE }))
48
+ end
49
+ alias :t :translate
50
+
51
+ # See I18n::localize
52
+ def localize(object, options = {})
53
+ I18n.localize(object, options.merge({ :locale => LOCALE }))
54
+ end
55
+ alias :l :localize
56
+
57
+ # strftime() proxy with Russian localization
58
+ def strftime(object, format = :default)
59
+ localize(object, { :format => format })
60
+ end
61
+
62
+ # Simple pluralization proxy
63
+ #
64
+ # Usage:
65
+ # Russian.pluralize(1, "вещь", "вещи", "вещей")
66
+ def pluralize(n, *variants)
67
+ variants_hash = pluralization_variants_to_hash(*variants)
68
+ I18n.backend.send(:pluralize, LOCALE, variants_hash, n)
69
+ end
70
+
71
+ protected
72
+ # Returns all locale files shipped with library
73
+ def locale_files
74
+ Dir[File.join(File.dirname(__FILE__), "russian", "locale", "**/*")]
75
+ end
76
+
77
+ # Converts an array of pluralization variants (3 entries) to a Hash that can be used
78
+ # with I18n pluralization.
79
+ def pluralization_variants_to_hash(*variants)
80
+ raise ArgumentError, "Must have at least 3 variants for pluralization" if variants.size < 3
81
+ {
82
+ :one => variants[0],
83
+ :few => variants[1],
84
+ :many => variants[2],
85
+ :other => variants[1]
86
+ }
87
+ end
88
+ end
89
+ end
90
+
91
+ Russian.init_i18n
@@ -0,0 +1,90 @@
1
+ # Replaces Rails select_month helper and translated_month_names private method to provide
2
+ # "standalone month names" feature.
3
+ #
4
+ # It is now possible to use both abbreviated and full month names in two variants (if current locale provides them).
5
+ # All date helpers support <tt>:use_standalone_month_names</tt> key now, <tt>select_month</tt> helper sets
6
+ # it to true by default.
7
+ # Standalone month names are also used when <tt>:discard_day</tt> key is provided.
8
+ if defined?(ActionView::Helpers::DateTimeSelector) && ActionView::Helpers::DateTimeSelector.private_instance_methods.include?("translated_month_names")
9
+ module ActionView
10
+ module Helpers
11
+ module DateHelper
12
+ # Returns a select tag with options for each of the months January through December with the current month
13
+ # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
14
+ # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
15
+ # instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
16
+ # want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
17
+ # to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
18
+ # to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
19
+ # You can also choose if you want to use i18n standalone month names or default month names -- you can
20
+ # force standalone month names usage by using <tt>:use_standalone_month_names</tt> key.
21
+ # Override the field name using the <tt>:field_name</tt> option, 'month' by default.
22
+ #
23
+ # ==== Examples
24
+ # # Generates a select field for months that defaults to the current month that
25
+ # # will use keys like "January", "March".
26
+ # select_month(Date.today)
27
+ #
28
+ # # Generates a select field for months that defaults to the current month that
29
+ # # is named "start" rather than "month"
30
+ # select_month(Date.today, :field_name => 'start')
31
+ #
32
+ # # Generates a select field for months that defaults to the current month that
33
+ # # will use keys like "1", "3".
34
+ # select_month(Date.today, :use_month_numbers => true)
35
+ #
36
+ # # Generates a select field for months that defaults to the current month that
37
+ # # will use keys like "1 - January", "3 - March".
38
+ # select_month(Date.today, :add_month_numbers => true)
39
+ #
40
+ # # Generates a select field for months that defaults to the current month that
41
+ # # will use keys like "Jan", "Mar".
42
+ # select_month(Date.today, :use_short_month => true)
43
+ #
44
+ # # Generates a select field for months that defaults to the current month that
45
+ # # will use keys like "Januar", "Marts."
46
+ # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
47
+ #
48
+ def select_month(date, options = {}, html_options = {})
49
+ DateTimeSelector.new(date, options.merge(:use_standalone_month_names => true), html_options).select_month
50
+ end
51
+ end
52
+
53
+ class DateTimeSelector #:nodoc:
54
+ private
55
+ # Returns translated month names
56
+ # => [nil, "January", "February", "March",
57
+ # "April", "May", "June", "July",
58
+ # "August", "September", "October",
59
+ # "November", "December"]
60
+ #
61
+ # If :use_short_month option is set
62
+ # => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63
+ # "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
64
+ #
65
+ # Also looks up if <tt>:discard_day</tt> or <tt>:use_standalone_month_names</tt> option is set
66
+ # and uses i18n standalone month names if so.
67
+ def translated_month_names
68
+ begin
69
+ if @options[:use_short_month]
70
+ if (@options[:discard_day] || @options[:use_standalone_month_names]) && I18n.translate(:'date.standalone_abbr_month_names')
71
+ key = :'date.standalone_abbr_month_names'
72
+ else
73
+ key = :'date.abbr_month_names'
74
+ end
75
+ else
76
+ if (@options[:discard_day] || @options[:use_standalone_month_names]) && I18n.translate(:'date.standalone_month_names')
77
+ key = :'date.standalone_month_names'
78
+ else
79
+ key = :'date.month_names'
80
+ end
81
+ end
82
+
83
+ I18n.translate(key, :locale => @options[:locale])
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+ end # if defined?
@@ -0,0 +1,33 @@
1
+ if defined?(ActiveRecord::Errors)
2
+ # The following is taken from custom_error_message plugin by David Easley
3
+ # (http://rubyforge.org/projects/custom-err-msg/)
4
+ module ActiveRecord
5
+ class Errors
6
+
7
+ # Redefine the ActiveRecord::Errors::full_messages method:
8
+ # Returns all the full error messages in an array. 'Base' messages are handled as usual.
9
+ # Non-base messages are prefixed with the attribute name as usual UNLESS they begin with '^'
10
+ # in which case the attribute name is omitted.
11
+ # E.g. validates_acceptance_of :accepted_terms, :message => '^Please accept the terms of service'
12
+ def full_messages
13
+ full_messages = []
14
+
15
+ @errors.each_key do |attr|
16
+ @errors[attr].each do |msg|
17
+ next if msg.nil?
18
+
19
+ if attr == "base"
20
+ full_messages << msg
21
+ elsif msg =~ /^\^/
22
+ full_messages << msg[1..-1]
23
+ else
24
+ full_messages << @base.class.human_attribute_name(attr) + " " + msg
25
+ end
26
+ end
27
+ end
28
+
29
+ return full_messages
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,104 @@
1
+ module I18n
2
+ module Backend
3
+ # Advanced I18n backend.
4
+ #
5
+ # Extends <tt>Simple</tt> backend. Allows usage of <tt>standalone_*</tt> keys
6
+ # for DateTime localization and usage of user-defined <tt>Proc</tt> (lambda) pluralization
7
+ # methods in translation tables.
8
+ class Advanced < Simple
9
+ LOCALIZE_ABBR_MONTH_NAMES_MATCH = /(%d|%e)?(\s*)(%b)/
10
+ LOCALIZE_MONTH_NAMES_MATCH = /(%d|%e)?(\s*)(%B)/
11
+ LOCALIZE_STANDALONE_ABBR_DAY_NAMES_MATCH = /^%a/
12
+ LOCALIZE_STANDALONE_DAY_NAMES_MATCH = /^%A/
13
+
14
+ # Acts the same as +strftime+, but returns a localized version of the
15
+ # formatted date string. Takes a key from the date/time formats
16
+ # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
17
+ #
18
+ # Note that it differs from <tt>localize</tt> in <tt>Simple</tt> backend by checking for
19
+ # <tt>standalone_*</tt> month name/day name keys in translation and using them if available.
20
+ def localize(locale, object, format = :default)
21
+ raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
22
+
23
+ type = object.respond_to?(:sec) ? 'time' : 'date'
24
+ # TODO only translate these if format is a String?
25
+ formats = translate(locale, :"#{type}.formats")
26
+ format = formats[format.to_sym] if formats && formats[format.to_sym]
27
+ # TODO raise exception unless format found?
28
+ format = format.to_s.dup
29
+
30
+ # TODO only translate these if the format string is actually present
31
+ # TODO check which format strings are present, then bulk translate then, then replace them
32
+
33
+ if lookup(locale, :"date.standalone_abbr_day_names")
34
+ format.gsub!(LOCALIZE_STANDALONE_ABBR_DAY_NAMES_MATCH,
35
+ translate(locale, :"date.standalone_abbr_day_names")[object.wday])
36
+ end
37
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
38
+
39
+ if lookup(locale, :"date.standalone_day_names")
40
+ format.gsub!(LOCALIZE_STANDALONE_DAY_NAMES_MATCH,
41
+ translate(locale, :"date.standalone_day_names")[object.wday])
42
+ end
43
+ format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
44
+
45
+ if lookup(locale, :"date.standalone_abbr_month_names")
46
+ format.gsub!(LOCALIZE_ABBR_MONTH_NAMES_MATCH) do
47
+ $1 ? $1 + $2 + translate(locale, :"date.abbr_month_names")[object.mon] :
48
+ $2 + translate(locale, :"date.standalone_abbr_month_names")[object.mon]
49
+ end
50
+ else
51
+ format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
52
+ end
53
+
54
+ if lookup(locale, :"date.standalone_month_names")
55
+ format.gsub!(LOCALIZE_MONTH_NAMES_MATCH) do
56
+ $1 ? $1 + $2 + translate(locale, :"date.month_names")[object.mon] :
57
+ $2 + translate(locale, :"date.standalone_month_names")[object.mon]
58
+ end
59
+ else
60
+ format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
61
+ end
62
+
63
+ format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
64
+ object.strftime(format)
65
+ end
66
+
67
+ protected
68
+ # Picks a pluralization rule specified in translation tables for a language or
69
+ # uses default pluralization rules.
70
+ #
71
+ # This is how pluralization rules are defined in translation tables, English
72
+ # language for example:
73
+ #
74
+ # store_translations :'en-US', {
75
+ # :pluralize => lambda { |n| n == 1 ? :one : :other }
76
+ # }
77
+ #
78
+ # Rule must return a symbol to use with pluralization, it must be one of:
79
+ # :zero, :one, :two, :few, :many, :other
80
+ def pluralize(locale, entry, count)
81
+ return entry unless entry.is_a?(Hash) and count
82
+
83
+ key = :zero if count == 0 && entry.has_key?(:zero)
84
+ locale_pluralize = lookup(locale, :pluralize)
85
+ if locale_pluralize && locale_pluralize.respond_to?(:call)
86
+ key ||= locale_pluralize.call(count)
87
+ else
88
+ key ||= default_pluralizer(count)
89
+ end
90
+ raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
91
+
92
+ entry[key]
93
+ end
94
+
95
+ # Default pluralizer, used if pluralization rule is not defined in translations.
96
+ #
97
+ # Uses English pluralization rules -- it will pick the first translation if count is not equal to 1
98
+ # and the second translation if it is equal to 1.
99
+ def default_pluralizer(count)
100
+ count == 1 ? :one : :other
101
+ end
102
+ end
103
+ end
104
+ end