russian 0.2.7 → 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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +203 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE +2 -2
  5. data/README.md +327 -0
  6. data/Rakefile +12 -50
  7. data/lib/russian/action_view_ext/helpers/date_helper.rb +136 -95
  8. data/lib/russian/active_model_ext/custom_error_message.rb +51 -0
  9. data/lib/russian/locale/actionview.yml +150 -83
  10. data/lib/russian/locale/activemodel.yml +57 -0
  11. data/lib/russian/locale/activerecord.yml +66 -61
  12. data/lib/russian/locale/activesupport.yml +5 -8
  13. data/lib/russian/locale/datetime.rb +49 -0
  14. data/lib/russian/locale/datetime.yml +90 -23
  15. data/lib/russian/locale/pluralization.rb +49 -0
  16. data/lib/russian/locale/transliterator.rb +18 -0
  17. data/lib/russian/russian_rails.rb +99 -0
  18. data/lib/russian/strptime.rb +166 -0
  19. data/lib/russian/transliteration.rb +87 -53
  20. data/lib/russian/version.rb +31 -0
  21. data/lib/russian.rb +382 -103
  22. data/russian.gemspec +43 -0
  23. data/sig/russian.rbs +46 -0
  24. metadata +197 -86
  25. data/CHANGELOG +0 -108
  26. data/README.textile +0 -298
  27. data/TODO +0 -10
  28. data/init.rb +0 -3
  29. data/lib/russian/active_record_ext/custom_error_message.rb +0 -163
  30. data/lib/russian/active_support_ext/parameterize.rb +0 -31
  31. data/lib/russian/backend/advanced.rb +0 -134
  32. data/lib/russian/locale/pluralize.rb +0 -25
  33. data/lib/vendor/i18n/MIT-LICENSE +0 -20
  34. data/lib/vendor/i18n/README.textile +0 -20
  35. data/lib/vendor/i18n/Rakefile +0 -5
  36. data/lib/vendor/i18n/i18n.gemspec +0 -27
  37. data/lib/vendor/i18n/lib/i18n/backend/simple.rb +0 -214
  38. data/lib/vendor/i18n/lib/i18n/exceptions.rb +0 -53
  39. data/lib/vendor/i18n/lib/i18n.rb +0 -199
  40. data/lib/vendor/i18n/test/all.rb +0 -5
  41. data/lib/vendor/i18n/test/i18n_exceptions_test.rb +0 -100
  42. data/lib/vendor/i18n/test/i18n_test.rb +0 -125
  43. data/lib/vendor/i18n/test/locale/en.rb +0 -1
  44. data/lib/vendor/i18n/test/locale/en.yml +0 -3
  45. data/lib/vendor/i18n/test/simple_backend_test.rb +0 -568
  46. data/lib/vendor/i18n_label/README.textile +0 -38
  47. data/lib/vendor/i18n_label/Rakefile +0 -11
  48. data/lib/vendor/i18n_label/init.rb +0 -1
  49. data/lib/vendor/i18n_label/install.rb +0 -1
  50. data/lib/vendor/i18n_label/lib/i18n_label.rb +0 -23
  51. data/lib/vendor/i18n_label/spec/i18n_label_spec.rb +0 -20
  52. data/lib/vendor/i18n_label/spec/spec_helper.rb +0 -10
  53. data/lib/vendor/i18n_label/tasks/i18n_label_tasks.rake +0 -4
  54. data/lib/vendor/i18n_label/uninstall.rb +0 -1
  55. data/spec/fixtures/en.yml +0 -4
  56. data/spec/fixtures/ru.yml +0 -4
  57. data/spec/i18n/locale/datetime_spec.rb +0 -99
  58. data/spec/i18n/locale/pluralization_spec.rb +0 -28
  59. data/spec/locale_spec.rb +0 -129
  60. data/spec/russian_spec.rb +0 -136
  61. data/spec/spec_helper.rb +0 -7
  62. data/spec/transliteration_spec.rb +0 -51
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
5
+
6
+ module Russian
7
+ # Localized `strptime` helpers for Russian month and weekday names.
8
+ #
9
+ # The module normalizes Russian textual date/time tokens to the English
10
+ # tokens expected by Ruby's native parsers, then delegates to
11
+ # `Date.strptime`, `Time.strptime`, or `DateTime.strptime`.
12
+ #
13
+ #
14
+ # Локализованные хелперы `strptime` для русских названий месяцев и дней
15
+ # недели.
16
+ #
17
+ # Модуль нормализует русские текстовые токены даты и времени к английским
18
+ # токенам, которые ожидают стандартные parser'ы Ruby, а затем делегирует
19
+ # работу в `Date.strptime`, `Time.strptime` или `DateTime.strptime`.
20
+ module Strptime
21
+ module_function
22
+
23
+ # Parses a localized Russian date string with `Date.strptime`.
24
+ #
25
+ #
26
+ # Разбирает локализованную русскую строку даты через `Date.strptime`.
27
+ #
28
+ # @param string [String] Localized date string.
29
+ # Локализованная строка даты.
30
+ # @param format [String] Optional `strptime` format string.
31
+ # Строка формата `strptime`.
32
+ # @return [Date] Parsed date.
33
+ # Разобранная дата.
34
+ def date_strptime(string, format = "%F")
35
+ Date.strptime(normalize_input(string, format), format, Date::ITALY)
36
+ end
37
+
38
+ # Parses a localized Russian date/time string with `Time.strptime`.
39
+ #
40
+ #
41
+ # Разбирает локализованную русскую строку даты и времени через
42
+ # `Time.strptime`.
43
+ #
44
+ # @param string [String] Localized date/time string.
45
+ # Локализованная строка даты и времени.
46
+ # @param format [String] `strptime` format string.
47
+ # Строка формата `strptime`.
48
+ # @param now [Time] Optional base time passed to `Time.strptime`.
49
+ # Базовое значение времени, передаваемое в `Time.strptime`.
50
+ # @return [Time] Parsed time.
51
+ # Разобранное время.
52
+ def time_strptime(string, format, now = Time.now)
53
+ Time.strptime(normalize_input(string, format), format, now)
54
+ end
55
+
56
+ # Parses a localized Russian date/time string with `DateTime.strptime`.
57
+ #
58
+ #
59
+ # Разбирает локализованную русскую строку даты и времени через
60
+ # `DateTime.strptime`.
61
+ #
62
+ # @param string [String] Localized date/time string.
63
+ # Локализованная строка даты и времени.
64
+ # @param format [String] Optional `strptime` format string.
65
+ # Строка формата `strptime`.
66
+ # @return [DateTime] Parsed datetime.
67
+ # Разобранный `DateTime`.
68
+ def datetime_strptime(string, format = "%FT%T%z")
69
+ DateTime.strptime(normalize_input(string, format), format, Date::ITALY)
70
+ end
71
+
72
+ # @private
73
+ def normalize_input(string, format)
74
+ return string unless string.is_a?(String) && format.is_a?(String)
75
+
76
+ replacements = localized_replacements(format)
77
+ return string if replacements.empty?
78
+
79
+ token_regexp = Regexp.new(
80
+ "(?<!\\p{L})(?:#{replacements.keys.sort_by(&:length).reverse.map do |token|
81
+ Regexp.escape(token)
82
+ end.join("|")})(?!\\p{L})",
83
+ Regexp::IGNORECASE
84
+ )
85
+
86
+ string.gsub(token_regexp) { |token| replacements.fetch(token.downcase) }
87
+ end
88
+
89
+ # @private
90
+ def localized_replacements(format)
91
+ directives = directives_in(format)
92
+ replacements = {}
93
+
94
+ replacements.merge!(localized_tokens(month_names_key(format), Date::MONTHNAMES)) if directives.include?("B")
95
+
96
+ if directives.include?("b")
97
+ replacements.merge!(localized_tokens(abbr_month_names_key(format), Date::ABBR_MONTHNAMES))
98
+ end
99
+
100
+ replacements.merge!(localized_tokens(day_names_key(format), Date::DAYNAMES)) if directives.include?("A")
101
+
102
+ replacements.merge!(localized_tokens(abbr_day_names_key(format), Date::ABBR_DAYNAMES)) if directives.include?("a")
103
+
104
+ replacements
105
+ end
106
+
107
+ # @private
108
+ def localized_tokens(key, english_names)
109
+ localized_names = I18n.t(key, locale: Russian::LOCALE).compact
110
+ english_names = english_names.compact
111
+
112
+ localized_names.zip(english_names).each_with_object({}) do |(localized_name, english_name), replacements|
113
+ replacements[localized_name.downcase] = english_name
114
+ end
115
+ end
116
+
117
+ # @private
118
+ def directives_in(format)
119
+ directives = []
120
+ index = 0
121
+
122
+ while index < format.length
123
+ if format[index] != "%"
124
+ index += 1
125
+ next
126
+ end
127
+
128
+ if format[index + 1] == "%"
129
+ index += 2
130
+ next
131
+ end
132
+
133
+ index += 1
134
+ index += 1 while index < format.length && Russian::STRPTIME_DIRECTIVE_MODIFIERS.include?(format[index])
135
+ index += 1 while index < format.length && format[index].match?(/\d/)
136
+
137
+ index += 1 if index < format.length && %w[E O].include?(format[index])
138
+
139
+ directives << format[index] if index < format.length
140
+ index += 1
141
+ end
142
+
143
+ directives
144
+ end
145
+
146
+ # @private
147
+ def month_names_key(format)
148
+ Russian::LOCALIZE_MONTH_NAMES_MATCH.match?(format) ? :"date.common_month_names" : :"date.standalone_month_names"
149
+ end
150
+
151
+ # @private
152
+ def abbr_month_names_key(format)
153
+ Russian::LOCALIZE_ABBR_MONTH_NAMES_MATCH.match?(format) ? :"date.common_abbr_month_names" : :"date.standalone_abbr_month_names"
154
+ end
155
+
156
+ # @private
157
+ def day_names_key(format)
158
+ Russian::LOCALIZE_STANDALONE_DAY_NAMES_MATCH.match?(format) ? :"date.standalone_day_names" : :"date.common_day_names"
159
+ end
160
+
161
+ # @private
162
+ def abbr_day_names_key(format)
163
+ Russian::LOCALIZE_STANDALONE_ABBR_DAY_NAMES_MATCH.match?(format) ? :"date.common_abbr_day_names" : :"date.standalone_abbr_day_names"
164
+ end
165
+ end
166
+ end
@@ -1,72 +1,106 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Russian
4
- # Russian transliteration
4
+ # Russian transliteration tables and helpers.
5
5
  #
6
- # Транслитерация для букв русского алфавита
6
+ # Transliteration is heavily based on the
7
+ # [rutils](https://github.com/julik/rutils) gem by
8
+ # Julian "julik" Tarkhanov and contributors, then cleaned up and
9
+ # adapted for this gem.
10
+ #
11
+ #
12
+ # Таблицы и хелперы для русской транслитерации.
13
+ #
14
+ # Транслитерация во многом основана на gem
15
+ # [rutils](https://github.com/julik/rutils) Юлика Тарханова и
16
+ # соавторов, а затем упрощена и адаптирована для этого gem'а.
17
+ #
18
+ # @example
19
+ # Russian::Transliteration.transliterate("Юлия")
20
+ # # => "Yuliya"
21
+ #
22
+ # Russian::Transliteration.transliterate("Н.П. Шерстяков")
23
+ # # => "N.P. Sherstyakov"
7
24
  module Transliteration
8
- extend self
9
-
10
- # Transliteration heavily based on rutils gem by Julian "julik" Tarkhanov and Co.
11
- # <http://rutils.rubyforge.org/>
12
- # Cleaned up and optimized.
25
+ module_function
13
26
 
27
+ # @private
14
28
  LOWER_SINGLE = {
15
- "і"=>"i","ґ"=>"g","ё"=>"yo","№"=>"#","є"=>"e",
16
- "ї"=>"yi","а"=>"a","б"=>"b",
17
- "в"=>"v","г"=>"g","д"=>"d","е"=>"e","ж"=>"zh",
18
- "з"=>"z","и"=>"i","й"=>"y","к"=>"k","л"=>"l",
19
- "м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r",
20
- "с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"h",
21
- "ц"=>"ts","ч"=>"ch","ш"=>"sh","щ"=>"sch","ъ"=>"'",
22
- "ы"=>"y","ь"=>"","э"=>"e","ю"=>"yu","я"=>"ya",
23
- }
29
+ "і" => "i", "ґ" => "g", "ё" => "yo", "№" => "#", "є" => "e",
30
+ "ї" => "yi", "а" => "a", "б" => "b",
31
+ "в" => "v", "г" => "g", "д" => "d", "е" => "e", "ж" => "zh",
32
+ "з" => "z", "и" => "i", "й" => "y", "к" => "k", "л" => "l",
33
+ "м" => "m", "н" => "n", "о" => "o", "п" => "p", "р" => "r",
34
+ "с" => "s", "т" => "t", "у" => "u", "ф" => "f", "х" => "h",
35
+ "ц" => "ts", "ч" => "ch", "ш" => "sh", "щ" => "sch", "ъ" => "'",
36
+ "ы" => "y", "ь" => "", "э" => "e", "ю" => "yu", "я" => "ya"
37
+ }.freeze
38
+ # @private
24
39
  LOWER_MULTI = {
25
- "ье"=>"ie",
26
- "ьё"=>"ie",
27
- }
40
+ "ье" => "ie",
41
+ "ьё" => "ie"
42
+ }.freeze
28
43
 
44
+ # @private
29
45
  UPPER_SINGLE = {
30
- "Ґ"=>"G","Ё"=>"YO","Є"=>"E","Ї"=>"YI","І"=>"I",
31
- "А"=>"A","Б"=>"B","В"=>"V","Г"=>"G",
32
- "Д"=>"D","Е"=>"E","Ж"=>"ZH","З"=>"Z","И"=>"I",
33
- "Й"=>"Y","К"=>"K","Л"=>"L","М"=>"M","Н"=>"N",
34
- "О"=>"O","П"=>"P","Р"=>"R","С"=>"S","Т"=>"T",
35
- "У"=>"U","Ф"=>"F","Х"=>"H","Ц"=>"TS","Ч"=>"CH",
36
- "Ш"=>"SH","Щ"=>"SCH","Ъ"=>"'","Ы"=>"Y","Ь"=>"",
37
- "Э"=>"E","Ю"=>"YU","Я"=>"YA",
38
- }
46
+ "Ґ" => "G", "Ё" => "YO", "Є" => "E", "Ї" => "YI", "І" => "I",
47
+ "А" => "A", "Б" => "B", "В" => "V", "Г" => "G",
48
+ "Д" => "D", "Е" => "E", "Ж" => "ZH", "З" => "Z", "И" => "I",
49
+ "Й" => "Y", "К" => "K", "Л" => "L", "М" => "M", "Н" => "N",
50
+ "О" => "O", "П" => "P", "Р" => "R", "С" => "S", "Т" => "T",
51
+ "У" => "U", "Ф" => "F", "Х" => "H", "Ц" => "TS", "Ч" => "CH",
52
+ "Ш" => "SH", "Щ" => "SCH", "Ъ" => "'", "Ы" => "Y", "Ь" => "",
53
+ "Э" => "E", "Ю" => "YU", "Я" => "YA"
54
+ }.freeze
55
+ # @private
39
56
  UPPER_MULTI = {
40
- "ЬЕ"=>"IE",
41
- "ЬЁ"=>"IE",
42
- }
57
+ "ЬЕ" => "IE",
58
+ "ЬЁ" => "IE"
59
+ }.freeze
43
60
 
44
- LOWER = (LOWER_SINGLE.merge(LOWER_MULTI)).freeze
45
- UPPER = (UPPER_SINGLE.merge(UPPER_MULTI)).freeze
46
- MULTI_KEYS = (LOWER_MULTI.merge(UPPER_MULTI)).keys.sort_by {|s| s.length}.reverse.freeze
61
+ # @private
62
+ LOWER = LOWER_SINGLE.merge(LOWER_MULTI).freeze
63
+ # @private
64
+ UPPER = UPPER_SINGLE.merge(UPPER_MULTI).freeze
65
+ # @private
66
+ TITLE = UPPER.transform_values(&:capitalize).freeze
67
+ # @private
68
+ MULTI_KEYS = LOWER_MULTI.merge(UPPER_MULTI).keys.sort_by(&:length).reverse.freeze
69
+ # @private
70
+ TOKEN_RE = /#{Regexp.union(MULTI_KEYS).source}|./m
47
71
 
48
- # Transliterate a string with russian characters
72
+ # Transliterates a string containing Cyrillic characters.
73
+ #
74
+ # The method preserves non-Cyrillic characters and follows the historical
75
+ # casing rules of the gem.
76
+ #
77
+ #
78
+ # Транслитерирует строку, содержащую кириллические символы.
79
+ #
80
+ # Метод сохраняет некириллические символы и следует историческим правилам
81
+ # gem'а для регистра.
82
+ #
83
+ # @param str [String] String to transliterate.
84
+ # Строка для транслитерации.
85
+ # @return [String] Transliteration result.
86
+ # Результат транслитерации.
49
87
  #
50
- # Возвращает строку, в которой все буквы русского алфавита заменены на похожую по звучанию латиницу
88
+ # @example
89
+ # Russian::Transliteration.transliterate("Привет, мир!")
90
+ # # => "Privet, mir!"
51
91
  def transliterate(str)
52
- chars = str.scan(%r{#{MULTI_KEYS.join '|'}|\w|.})
92
+ tokens = str.scan(TOKEN_RE)
53
93
 
54
- result = ""
94
+ tokens.each_with_index.map do |token, index|
95
+ lower = LOWER[token]
96
+ next lower if lower
55
97
 
56
- chars.each_with_index do |char, index|
57
- if UPPER.has_key?(char) && LOWER.has_key?(chars[index+1])
58
- # combined case
59
- result << UPPER[char].downcase.capitalize
60
- elsif UPPER.has_key?(char)
61
- result << UPPER[char]
62
- elsif LOWER.has_key?(char)
63
- result << LOWER[char]
64
- else
65
- result << char
66
- end
67
- end
98
+ upper = UPPER[token]
99
+ next TITLE[token] if upper && LOWER[tokens[index + 1]]
100
+ next upper if upper
68
101
 
69
- result
102
+ token
103
+ end.join
70
104
  end
71
105
  end
72
- end
106
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Russian
4
+ # Gem version constants.
5
+ #
6
+ # The version is split into major, minor, and tiny components, plus a string
7
+ # representation.
8
+ #
9
+ #
10
+ # Константы версии gem'а.
11
+ #
12
+ # Версия разбита на major, minor и tiny-компоненты, а также на строковое
13
+ # представление.
14
+ #
15
+ # @example
16
+ # Russian::VERSION::STRING
17
+ # # => "1.0.0"
18
+ module VERSION
19
+ MAJOR = 1
20
+ MINOR = 0
21
+ TINY = 0
22
+
23
+ # Full version string.
24
+ #
25
+ # Полная строка версии.
26
+ #
27
+ # @return [String] Full version string.
28
+ # Полная строка версии.
29
+ STRING = [MAJOR, MINOR, TINY].join(".")
30
+ end
31
+ end