russian 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,118 +1,153 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
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)
21
- module ActionView
3
+ module Russian
4
+ # Rails date helper integration with support for standalone month names.
5
+ #
6
+ # The patch teaches Rails date helpers to distinguish between common
7
+ # ("01 декабря") and standalone ("Декабрь") month names when the active
8
+ # locale provides both forms.
9
+ #
10
+ # For Russian, `select_month` always uses standalone month names. Other month
11
+ # rendering helpers switch to standalone names when `:discard_day` or
12
+ # `:use_standalone_month_names` is set. If split translations are unavailable,
13
+ # the implementation falls back to the standard Rails month-name keys.
14
+ #
15
+ #
16
+ # Интеграция с Rails date helper'ами для поддержки отдельностоящих
17
+ # названий месяцев.
18
+ #
19
+ # Патч учит Rails date helper'ы различать контекстные ("01 декабря") и
20
+ # отдельностоящие ("Декабрь") формы названий месяцев, если активная локаль
21
+ # предоставляет обе формы.
22
+ #
23
+ # Для русского языка `select_month` всегда использует отдельностоящие
24
+ # названия месяцев. Остальные helper'ы, рендерящие месяцы, переключаются на
25
+ # отдельностоящие формы, если указан `:discard_day` или
26
+ # `:use_standalone_month_names`. Если раздельных переводов нет, реализация
27
+ # откатывается к стандартным ключам Rails для названий месяцев.
28
+ module ActionViewExt
29
+ # Helper patches injected into Action View.
30
+ #
31
+ #
32
+ # Патчи хелперов, внедряемые в Action View.
22
33
  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
- # использования отдельностоящих имен месяцев, если текущий язык их поддерживает.
34
+ # @private
35
+ MONTH_NAME_KEYS = {
36
+ short: {
37
+ common: :"date.common_abbr_month_names",
38
+ standalone: :"date.standalone_abbr_month_names",
39
+ fallback: :"date.abbr_month_names"
40
+ },
41
+ long: {
42
+ common: :"date.common_month_names",
43
+ standalone: :"date.standalone_month_names",
44
+ fallback: :"date.month_names"
45
+ }
46
+ }.freeze
47
+
48
+ # Patch for `ActionView::Helpers::DateHelper`.
49
+ #
50
+ #
51
+ # Патч для `ActionView::Helpers::DateHelper`.
52
+ module DateHelperPatch
53
+ # Builds a `<select>` with month options and forces standalone month
54
+ # names for Russian.
38
55
  #
56
+ # The method keeps standard Rails month helper behavior and options,
57
+ # but injects `use_standalone_month_names: true` before delegating to
58
+ # Rails. This makes Russian month names render as standalone forms such
59
+ # as `Декабрь` instead of contextual forms such as `декабря`.
39
60
  #
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
61
  #
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')
62
+ # Формирует `<select>` со списком месяцев и принудительно использует
63
+ # отдельностоящие названия месяцев для русского языка.
48
64
  #
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)
65
+ # Метод сохраняет стандартное поведение и опции Rails month helper'а,
66
+ # но перед делегированием в Rails добавляет
67
+ # `use_standalone_month_names: true`. Благодаря этому для русского
68
+ # языка рендерятся отдельностоящие формы вроде `Декабрь`, а не
69
+ # контекстные формы вроде `декабря`.
52
70
  #
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)
71
+ # @param date [Date, Time, DateTime, Integer, nil] Selected month value.
72
+ # Выбранное значение месяца.
73
+ # @param options [Hash] Rails helper options.
74
+ # Опции Rails-хелпера.
75
+ # @option options [Boolean] :use_month_numbers Show month numbers instead of names.
76
+ # Показывать номера месяцев вместо названий.
77
+ # @option options [Boolean] :add_month_numbers Prefix month names with numbers.
78
+ # Добавлять номер месяца перед названием.
79
+ # @option options [Boolean] :use_short_month Use abbreviated month names.
80
+ # Использовать сокращенные названия месяцев.
81
+ # @option options [Array<String>] :use_month_names Custom month names array.
82
+ # Пользовательский массив названий месяцев.
83
+ # @option options [String, Symbol] :field_name Override the field name, `"month"` by default.
84
+ # Переопределить имя поля; по умолчанию используется `"month"`.
85
+ # @option options [Boolean] :use_standalone_month_names Request standalone month names explicitly.
86
+ # Явно запросить отдельностоящие названия месяцев.
87
+ # @param html_options [Hash] HTML options.
88
+ # HTML-опции.
89
+ # @return [String] HTML for the select tag.
90
+ # HTML для select-тега.
56
91
  #
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)
92
+ # @example Default Russian output
93
+ # select_month(Date.new(2024, 12, 1))
94
+ # # => uses standalone month names for :ru
60
95
  #
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 ...))
96
+ # @example Custom field name
97
+ # select_month(Date.new(2024, 12, 1), field_name: "start")
98
+ # # => overrides the generated field name
64
99
  #
100
+ # @example Abbreviated standalone month names
101
+ # select_month(Date.new(2024, 12, 1), use_short_month: true)
102
+ # # => uses abbreviated standalone month names
65
103
  def select_month(date, options = {}, html_options = {})
66
- DateTimeSelector.new(date, options.merge(:use_standalone_month_names => true), html_options).select_month
104
+ super(date, options.merge(use_standalone_month_names: true), html_options)
67
105
  end
68
106
  end
69
-
70
- class DateTimeSelector #:nodoc:
107
+
108
+ # @private
109
+ module DateTimeSelectorPatch
71
110
  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
- if @options[:use_short_month]
91
- if I18n.backend.send(:lookup, I18n.locale, :'date.common_abbr_month_names')
92
- if (@options[:discard_day] || @options[:use_standalone_month_names])
93
- key = :'date.standalone_abbr_month_names'
94
- else
95
- key = :'date.common_abbr_month_names'
96
- end
97
- else
98
- key = :'date.abbr_month_names'
99
- end
100
- else
101
- if I18n.backend.send(:lookup, I18n.locale, :'date.common_month_names')
102
- if (@options[:discard_day] || @options[:use_standalone_month_names])
103
- key = :'date.standalone_month_names'
104
- else
105
- key = :'date.common_month_names'
106
- end
107
- else
108
- key = :'date.month_names'
109
- end
110
- end
111
-
112
- I18n.translate(key, :locale => @options[:locale])
113
- end
114
-
111
+
112
+ # Returns translated month names for the current selector options.
113
+ #
114
+ # The method chooses between common and standalone month-name
115
+ # translations depending on `:discard_day`,
116
+ # `:use_standalone_month_names`, and `:use_short_month`. If split
117
+ # common/standalone translations are not defined for the current
118
+ # locale, it falls back to the standard Rails month-name keys.
119
+ #
120
+ #
121
+ # Возвращает переведенные названия месяцев для текущих
122
+ # опций selector'а.
123
+ #
124
+ # Метод выбирает между контекстными и отдельностоящими переводами
125
+ # названий месяцев в зависимости от `:discard_day`,
126
+ # `:use_standalone_month_names` и `:use_short_month`. Если для текущей
127
+ # локали не определены раздельные контекстные и отдельностоящие
128
+ # переводы, метод откатывается к стандартным ключам Rails.
129
+ #
130
+ # @return [Array<String, nil>] Month names array with `nil` at index `0`.
131
+ # Массив названий месяцев с `nil` на позиции `0`.
132
+ def translated_month_names
133
+ I18n.translate(translated_month_names_key, locale: month_names_locale)
134
+ end
135
+
136
+ def translated_month_names_key
137
+ month_name_keys = MONTH_NAME_KEYS.fetch(@options[:use_short_month] ? :short : :long)
138
+ return month_name_keys[:fallback] unless I18n.exists?(month_name_keys[:common], month_names_locale)
139
+
140
+ standalone_month_names? ? month_name_keys[:standalone] : month_name_keys[:common]
141
+ end
142
+
143
+ def standalone_month_names?
144
+ @options[:discard_day] || @options[:use_standalone_month_names]
145
+ end
146
+
147
+ def month_names_locale
148
+ @options[:locale] || I18n.locale
149
+ end
115
150
  end
116
151
  end
117
152
  end
118
- end # if defined?
153
+ end
@@ -1,70 +1,51 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- if defined?(ActiveModel::Errors)
4
- module ActiveModel
5
- class Errors
6
- # Redefine the ActiveModel::Errors.full_messages method:
7
- # Returns all the full error messages in an array. 'Base' messages are handled as usual.
8
- # Non-base messages are prefixed with the attribute name as usual UNLESS they begin with '^'
9
- # in which case the attribute name is omitted.
10
- # E.g. validates_acceptance_of :accepted_terms, :message => '^Please accept the terms of service'
3
+ module Russian
4
+ # Extensions for ActiveModel integration.
5
+ #
6
+ #
7
+ # Расширения для интеграции с ActiveModel.
8
+ module ActiveModelExt
9
+ # Patch for `ActiveModel::Error.full_message`.
10
+ # Validation messages prefixed with `^` suppress the humanized
11
+ # attribute name in the resulting full message.
12
+ #
13
+ #
14
+ # Патч для `ActiveModel::Error.full_message`.
15
+ # Сообщения валидации с префиксом `^` подавляют humanized-имя
16
+ # атрибута в итоговом полном сообщении.
17
+ module ErrorPatch
18
+ # Builds a full validation message and optionally suppresses the
19
+ # attribute name when the message starts with `^`.
11
20
  #
12
- # Переопределяет метод ActiveModel::Errors.full_messages. Сообщения об ошибках для атрибутов
13
- # теперь не имеют префикса с названием атрибута если в сообщении об ошибке первым символом указан "^".
14
21
  #
15
- # Так, например,
16
- #
17
- # validates_acceptance_of :accepted_terms, :message => 'нужно принять соглашение'
18
- #
19
- # даст сообщение
20
- #
21
- # Accepted terms нужно принять соглашение
22
- #
23
- # однако,
24
- #
25
- # validates_acceptance_of :accepted_terms, :message => '^Нужно принять соглашение'
26
- #
27
- # даст сообщение
28
- #
29
- # Нужно принять соглашение
22
+ # Строит полное сообщение валидации и при необходимости подавляет имя
23
+ # атрибута, если сообщение начинается с `^`.
30
24
  #
25
+ # @param attribute [String, Symbol] Attribute name.
26
+ # Имя атрибута.
27
+ # @param message [#to_s] Validation message.
28
+ # Сообщение валидации.
29
+ # @param base [Object] Model instance.
30
+ # Экземпляр модели.
31
+ # @return [String] Full validation message.
32
+ # Полное сообщение валидации.
31
33
  #
32
- # Returns all the full error messages in an array.
33
- #
34
- # class Company
35
- # validates_presence_of :name, :address, :email
36
- # validates_length_of :name, :in => 5..30
37
- # end
38
- #
39
- # company = Company.create(:address => '123 First St.')
40
- # company.errors.full_messages # =>
41
- # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
42
- def full_messages
43
- full_messages = []
34
+ # @example
35
+ # ActiveModel::Error.full_message(:agreement, "^Нужно согласиться с соглашением", record)
36
+ # # => "Нужно согласиться с соглашением"
37
+ def full_message(attribute, message, base)
38
+ return super unless suppress_attribute_name?(attribute, message)
44
39
 
45
- each do |attribute, messages|
46
- messages = Array.wrap(messages)
47
- next if messages.empty?
48
-
49
- if attribute == :base
50
- messages.each {|m| full_messages << m }
51
- else
52
- attr_name = attribute.to_s.gsub('.', '_').humanize
53
- attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
54
- options = { :attribute => attr_name, :default => "%{attribute} %{message}" }
40
+ message.to_s.delete_prefix("^")
41
+ end
55
42
 
56
- messages.each do |m|
57
- if m =~ /^\^/
58
- full_messages << m[1..-1]
59
- else
60
- full_messages << I18n.t(:"errors.format", options.merge(:message => m))
61
- end
62
- end
63
- end
64
- end
43
+ private
65
44
 
66
- full_messages
45
+ # @private
46
+ def suppress_attribute_name?(attribute, message)
47
+ attribute.to_s != "base" && message.to_s.start_with?("^")
67
48
  end
68
49
  end
69
50
  end
70
- end # if defined?
51
+ end