num2words 0.2.0 → 0.3.1

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +40 -0
  3. data/CHANGELOG.md +40 -0
  4. data/README.md +139 -144
  5. data/Rakefile +38 -2
  6. data/benchmark/converter_benchmark.rb +96 -0
  7. data/config/locales/ar.yml +5 -1
  8. data/config/locales/be.yml +4 -0
  9. data/config/locales/bg.yml +4 -0
  10. data/config/locales/bn.yml +4 -0
  11. data/config/locales/cs.yml +6 -2
  12. data/config/locales/da.yml +4 -0
  13. data/config/locales/de.yml +8 -4
  14. data/config/locales/el.yml +4 -0
  15. data/config/locales/en.yml +4 -0
  16. data/config/locales/es.yml +4 -0
  17. data/config/locales/et.yml +9 -5
  18. data/config/locales/fa.yml +15 -11
  19. data/config/locales/fi.yml +8 -4
  20. data/config/locales/fr.yml +4 -0
  21. data/config/locales/gu.yml +4 -0
  22. data/config/locales/he.yml +6 -2
  23. data/config/locales/hi.yml +4 -0
  24. data/config/locales/hr.yml +8 -4
  25. data/config/locales/hu.yml +6 -2
  26. data/config/locales/id.yml +4 -0
  27. data/config/locales/it.yml +4 -0
  28. data/config/locales/ja.yml +10 -5
  29. data/config/locales/kn.yml +186 -10
  30. data/config/locales/ko.yml +184 -13
  31. data/config/locales/kz.yml +183 -17
  32. data/config/locales/lt.yml +184 -8
  33. data/config/locales/lv.yml +188 -12
  34. data/config/locales/ml.yml +189 -13
  35. data/config/locales/mr.yml +184 -8
  36. data/config/locales/ms.yml +184 -8
  37. data/config/locales/nb.yml +183 -17
  38. data/config/locales/nl.yml +184 -13
  39. data/config/locales/pa.yml +189 -13
  40. data/config/locales/pl.yml +184 -13
  41. data/config/locales/pt.yml +184 -13
  42. data/config/locales/ro.yml +183 -12
  43. data/config/locales/ru.yml +4 -0
  44. data/config/locales/sk.yml +184 -8
  45. data/config/locales/sl.yml +185 -9
  46. data/config/locales/sr.yml +186 -15
  47. data/config/locales/sv.yml +182 -13
  48. data/config/locales/sw.yml +184 -8
  49. data/config/locales/ta.yml +184 -8
  50. data/config/locales/te.yml +185 -9
  51. data/config/locales/th.yml +183 -12
  52. data/config/locales/tr.yml +183 -12
  53. data/config/locales/uk.yml +189 -18
  54. data/config/locales/ur.yml +187 -11
  55. data/config/locales/vi.yml +183 -12
  56. data/config/locales/zh.yml +191 -9
  57. data/docs/api.md +422 -0
  58. data/docs/api.ru.md +422 -0
  59. data/docs/limits.md +140 -0
  60. data/docs/limits.ru.md +140 -0
  61. data/docs/locale_development.md +241 -0
  62. data/docs/locale_development.ru.md +241 -0
  63. data/lib/num2words/converter.rb +144 -35
  64. data/lib/num2words/locales/ar.rb +130 -0
  65. data/lib/num2words/locales/be.rb +101 -0
  66. data/lib/num2words/locales/bg.rb +127 -0
  67. data/lib/num2words/locales/bn.rb +112 -0
  68. data/lib/num2words/locales/cs.rb +106 -0
  69. data/lib/num2words/locales/da.rb +97 -0
  70. data/lib/num2words/locales/de.rb +119 -0
  71. data/lib/num2words/locales/el.rb +143 -0
  72. data/lib/num2words/locales/en.rb +90 -0
  73. data/lib/num2words/locales/es.rb +185 -0
  74. data/lib/num2words/locales/et.rb +104 -0
  75. data/lib/num2words/locales/fa.rb +104 -0
  76. data/lib/num2words/locales/fi.rb +104 -0
  77. data/lib/num2words/locales/fr.rb +121 -0
  78. data/lib/num2words/locales/gu.rb +112 -0
  79. data/lib/num2words/locales/he.rb +130 -0
  80. data/lib/num2words/locales/hi.rb +112 -0
  81. data/lib/num2words/locales/hr.rb +117 -0
  82. data/lib/num2words/locales/hu.rb +111 -0
  83. data/lib/num2words/locales/id.rb +104 -0
  84. data/lib/num2words/locales/it.rb +142 -0
  85. data/lib/num2words/locales/ja.rb +126 -0
  86. data/lib/num2words/locales/kn.rb +122 -2
  87. data/lib/num2words/locales/ko.rb +158 -2
  88. data/lib/num2words/locales/kz.rb +137 -2
  89. data/lib/num2words/locales/lt.rb +146 -2
  90. data/lib/num2words/locales/lv.rb +148 -2
  91. data/lib/num2words/locales/ml.rb +142 -2
  92. data/lib/num2words/locales/mr.rb +142 -2
  93. data/lib/num2words/locales/ms.rb +134 -2
  94. data/lib/num2words/locales/nb.rb +153 -0
  95. data/lib/num2words/locales/nl.rb +142 -2
  96. data/lib/num2words/locales/pa.rb +122 -2
  97. data/lib/num2words/locales/pl.rb +149 -2
  98. data/lib/num2words/locales/pt.rb +164 -2
  99. data/lib/num2words/locales/ro.rb +165 -2
  100. data/lib/num2words/locales/ru.rb +100 -0
  101. data/lib/num2words/locales/sk.rb +124 -2
  102. data/lib/num2words/locales/sl.rb +154 -2
  103. data/lib/num2words/locales/sr.rb +141 -3
  104. data/lib/num2words/locales/sv.rb +141 -2
  105. data/lib/num2words/locales/sw.rb +133 -2
  106. data/lib/num2words/locales/ta.rb +142 -2
  107. data/lib/num2words/locales/te.rb +142 -2
  108. data/lib/num2words/locales/th.rb +133 -2
  109. data/lib/num2words/locales/tr.rb +141 -2
  110. data/lib/num2words/locales/uk.rb +134 -2
  111. data/lib/num2words/locales/ur.rb +130 -2
  112. data/lib/num2words/locales/vi.rb +142 -2
  113. data/lib/num2words/locales/zh.rb +172 -2
  114. data/lib/num2words/version.rb +1 -1
  115. data/num2words.gemspec +3 -2
  116. metadata +28 -6
  117. data/lib/num2words/locales/no.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbd7bda19d94f161e928ec326ce532c9c301746daaa371a041b859aa7262d4d9
4
- data.tar.gz: ec621319e9ce5de5657c01d87c9b91cb88da31516cd06c07629924558e5c6017
3
+ metadata.gz: 78f705a73f571176b4d920dfe220590d2bd217ea513fb817028a0ae2c22221cd
4
+ data.tar.gz: 1e8726b93feee82a3b87143ae8a25edf388d726f65ecf424ec713fcc4323ea08
5
5
  SHA512:
6
- metadata.gz: '092432a7d992343146c93337af17af5ea595ab06fff83793f81813ba55c7c4282b66e8bcc0598e82eba7db43e63131b1fba0f63332ede4984b10ef606e77698b'
7
- data.tar.gz: dfefff68054582dd62771c9fa87b2ff21996a73ae0d4b0b219b67178a06a8c393ac20ef5f8029bf5f32c77c2ef36bb66760d743c528e066f361ba8592f431013
6
+ metadata.gz: d8a078fef78bc1e26fc28d90c6604e6a3a09aa05e3b716df2faea6d7423bb91a4de1c907eaa7d7e9c85b0d3019fa98257f6b499113f1987d646c902680bcdd66
7
+ data.tar.gz: a87e663b01668bdf114cba5eb2c2f9f55b453b4aae314b5ceceac78047574e1fc5917a46d97fddaf60cea2a64333c2b1f0901694daed5aade3beff7d6682c1b9
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+ pull_request:
9
+
10
+ jobs:
11
+ test:
12
+ name: Ruby ${{ matrix.ruby }}
13
+ runs-on: ubuntu-22.04
14
+
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ ruby:
19
+ - "2.7"
20
+ - "3.0"
21
+ - "3.1"
22
+ - "3.2"
23
+ - "3.3"
24
+ - "3.4"
25
+
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@v4
29
+
30
+ - name: Set up Ruby
31
+ uses: ruby/setup-ruby@v1
32
+ with:
33
+ ruby-version: ${{ matrix.ruby }}
34
+ bundler-cache: true
35
+
36
+ - name: Run specs
37
+ run: bundle exec rspec
38
+
39
+ - name: Build gem
40
+ run: gem build num2words.gemspec
data/CHANGELOG.md CHANGED
@@ -11,6 +11,46 @@
11
11
 
12
12
  ---
13
13
 
14
+ ## [0.3.1] - 2026-07-04
15
+
16
+ ### Added
17
+ - Набор benchmark-проверок: `benchmark/converter_benchmark.rb` и `bundle exec rake benchmark`.
18
+ - Справочник API: `docs/api.md` и `docs/api.ru.md`.
19
+ - Документация по числовым лимитам: `docs/limits.md` и `docs/limits.ru.md`.
20
+ - Rake-задачи для разработки и релизной проверки:
21
+ - `bundle exec rake spec`
22
+ - `bundle exec rake build`
23
+ - `bundle exec rake release:check`
24
+ - `bundle exec rake locales`
25
+ - `bundle exec rake currencies`
26
+
27
+ ### Fixed
28
+ - Добавлена runtime-зависимость `bigdecimal` для совместимости с Ruby 3.4+.
29
+
30
+ ---
31
+
32
+ ## [0.3.0] - 2026-06-27
33
+
34
+ ### Added
35
+ - Полная актуализация всех локалей до единого набора возможностей: числа, дроби, даты, время, дата-время и валюты.
36
+ - Единый каталог из 32 валют для каждой локали:
37
+ `BDT`, `BGN`, `BRL`, `BYN`, `CNY`, `CZK`, `DKK`, `EUR`, `GBP`, `HUF`, `IDR`, `ILS`, `INR`, `IRR`, `JPY`, `KES`, `KRW`, `KZT`, `MYR`, `NOK`, `PKR`, `PLN`, `RON`, `RSD`, `RUB`, `SAR`, `SEK`, `THB`, `TRY`, `UAH`, `USD`, `VND`.
38
+ - Отдельные specs для локалей с проверками чисел, дробей, валют, дат и времени.
39
+ - Проверка `currency_catalog_spec.rb`, которая гарантирует одинаковый и уникальный набор валют во всех актуализированных локалях.
40
+
41
+ ### Changed
42
+ - Языковые правила и грамматические особенности перенесены в модули `Num2words::Locales::XX`.
43
+ - `Converter` оставлен для общего поведения и делегирует языковые различия локалям через locale hooks.
44
+ - README актуализирован под текущий набор возможностей и локалей.
45
+ - Описание gemspec обновлено: поддержка больше не ограничивается первичной русской локалью.
46
+
47
+ ### Fixed
48
+ - Устранены legacy-локали без `GRAMMAR`.
49
+ - Все локали теперь проходят smoke coverage без pending.
50
+ - Исправлены и унифицированы данные валют, дат, времени и дробей в локалях.
51
+
52
+ ---
53
+
14
54
  ## [0.2.0] - 2026-05-31
15
55
 
16
56
  ### Added
data/README.md CHANGED
@@ -1,29 +1,24 @@
1
1
  # num2words [![Gem Version](https://badge.fury.io/rb/num2words.svg)](https://badge.fury.io/rb/num2words)
2
2
 
3
- 📦 **num2words** — Ruby-гем для преобразования чисел в строковое представление (прописью).
3
+ Ruby-гем для преобразования чисел, дробей, денежных сумм, дат и времени в слова с учетом локальной грамматики.
4
4
 
5
5
  ---
6
6
 
7
- ## ✨ Основные возможности
8
-
9
- - Поддержка **чисел, времени, дат и валют**:
10
- - `Integer` -> `123.to_words(:ru)` -> `"сто двадцать три"`
11
- - `Float` -> `12.5.to_words(:en)` -> `"twelve and five tenths"`
12
- - `Time / DateTime` -> `"2024-08-21 14:35:42".to_words(:ru, :time)` -> `"четырнадцать часов тридцать пять минут сорок две секунды"`
13
- - `Float.to_currency` -> `12.5.to_currency(:ru)` -> `"двенадцать рублей пятьдесят копеек"`
14
- - Кастомизация вывода:
15
- - Краткая форма (`short: true`) -> `"четырнадцать часов тридцать пять минут"`
16
- - Форматы времени (`:hours_only`, `:hours_minutes`, `:hours_minutes_seconds`)
17
- - Выбор локали: `:ru`, `:en` (из коробки), легко расширить YAML-файлами
18
- - Расширение **Integer**, **Float**, **Date**, **Time**, **DateTime** удобными методами.
19
- - Локали инициализируются только при обращении к ним (`lazy loading`) через `lib/num2words/locales.rb`.
20
- - Унифицированный `Converter`, делегирующий работу нужной локали.
21
- - Настраиваемые правила разрядов чисел и валют через I18n YAML.
7
+ ## Возможности
22
8
 
9
+ - Числа: `Integer`, `Float`, числовые строки с точкой или запятой.
10
+ - Дроби: формальный стиль через `целых`/локальный аналог и разговорный `joiner: :and`.
11
+ - Decimal style: чтение дробной части по цифрам через `style: :decimal`.
12
+ - Валюты: единый каталог из 32 валют для каждой локали.
13
+ - Опции валют: `minor: :always | :nonzero | :never`.
14
+ - Даты, время и дата-время.
15
+ - Регистры вывода: `word_case: :upper | :downcase | :capitalize | :title`.
16
+ - Удобные методы для `Integer`, `Float`, `String`, `Date`, `Time`, `DateTime`.
17
+ - Интерактивная консоль `num2words-console`.
23
18
 
24
19
  ---
25
20
 
26
- ## 📦 Установка
21
+ ## Установка
27
22
 
28
23
  Добавьте в `Gemfile`:
29
24
 
@@ -39,36 +34,40 @@ gem install num2words
39
34
 
40
35
  ---
41
36
 
42
- ## 🚀 Быстрый старт
37
+ ## Быстрый старт
43
38
 
44
39
  ```ruby
45
40
  require "num2words"
46
41
  ```
47
42
 
48
- ### 🔢 Числа
43
+ ### Числа
44
+
49
45
  ```ruby
50
- # Integer
51
46
  123.to_words(:ru)
52
47
  # => "сто двадцать три"
53
48
 
54
49
  123.to_words(:en)
55
50
  # => "one hundred twenty three"
56
51
 
57
- # Float
58
- 45.67.to_words(:ru)
59
- # => "сорок пять целых шестьдесят семь сотых"
60
-
61
52
  "45,67".to_words(:ru)
62
53
  # => "сорок пять целых шестьдесят семь сотых"
63
54
 
64
55
  45.67.to_words(:ru, joiner: :and)
65
56
  # => "сорок пять и шестьдесят семь сотых"
66
57
 
67
- 45.67.to_words(:en)
68
- # => "forty five and sixty seven hundredths"
58
+ 12.12.to_words(:en, style: :decimal)
59
+ # => "twelve point one two"
60
+ ```
61
+
62
+ Универсальный вызов без core extensions:
63
+
64
+ ```ruby
65
+ Num2words.to_words("3,5", :ru)
66
+ # => "три целых пять десятых"
69
67
  ```
70
68
 
71
- ### 💰 Валюта
69
+ ### Валюта
70
+
72
71
  ```ruby
73
72
  21.05.to_currency(:ru)
74
73
  # => "двадцать один рубль пять копеек"
@@ -79,184 +78,180 @@ require "num2words"
79
78
  Num2words.to_currency(BigDecimal("21.05"), :ru)
80
79
  # => "двадцать один рубль пять копеек"
81
80
 
81
+ 12.50.to_currency(:ru, code: :USD)
82
+ # => "двенадцать долларов пятьдесят центов"
83
+
82
84
  1.to_currency(:ru, minor: :nonzero)
83
85
  # => "один рубль"
84
86
 
85
87
  1.25.to_currency(:ru, minor: :never)
86
88
  # => "один рубль"
87
-
88
- 12.5.to_currency(:en)
89
- # => "twelve dollars fifty cents"
90
89
  ```
91
90
 
92
- ### 📅 Дата
91
+ Поддерживаемые значения `minor:`:
92
+
93
+ - `:always` — всегда выводить младшую денежную единицу.
94
+ - `:nonzero` — выводить младшую единицу только когда она ненулевая.
95
+ - `:never` — не выводить младшую единицу.
96
+
97
+ ### Дата
98
+
93
99
  ```ruby
94
- "2024-08-21".to_words(:ru, :date)
100
+ "2024-08-21".to_words(:ru)
95
101
  # => "двадцать первое августа две тысячи двадцать четвёртого года"
96
102
 
97
- "2024-08-21".to_words(:ru, :date, date_case: :genitive)
103
+ "2024-08-21".to_words(:ru, date_case: :genitive)
98
104
  # => "двадцать первого августа две тысячи двадцать четвёртого года"
99
105
 
100
- "2024-08-21".to_words(:en, :date)
106
+ "2024-08-21".to_words(:en)
101
107
  # => "the twenty-first of August, two thousand twenty four"
102
108
  ```
103
109
 
104
- ### Время
110
+ ### Время
111
+
105
112
  ```ruby
106
- "14:35:42".to_words(:ru, :time)
113
+ "14:35:42".to_words(:ru)
107
114
  # => "четырнадцать часов тридцать пять минут сорок две секунды"
108
115
 
109
- "14:35:42".to_words(:ru, :time, short: true) # или короткая запись: "14:35:42".to_words(:ru, :time, true)
116
+ "14:35:42".to_words(:ru, short: true)
110
117
  # => "четырнадцать часов тридцать пять минут"
111
118
 
112
- "14:35:42".to_words(:en, :time)
119
+ "14:35:42".to_words(:en)
113
120
  # => "fourteen hours thirty five minutes forty two seconds"
114
121
  ```
115
122
 
116
- ### 🕓 Дата и время
123
+ ### Дата и время
117
124
 
118
125
  ```ruby
119
126
  "2024-08-21 14:35:42".to_words(:ru)
120
- # => "двадцать первое августа две тысячи двадцать четвёртого года, четырнадцать часов тридцать пять минут сорок две секунды"
121
-
122
- "2024-08-21 14:35:42".to_words(:ru, date_case: :genitive)
123
- # => "двадцать первого августа две тысячи двадцать четвёртого года, четырнадцать часов тридцать пять минут сорок две секунды"
127
+ # => "двадцать первое августа две тысячи двадцать четвёртого года, четырнадцать часов тридцать пять минут сорок две секунды"
124
128
 
125
129
  "2024-08-21 14:35:42".to_words(:en)
126
- # => "the twenty-first of August, two thousand twenty four at fourteen hours thirty five minutes forty two seconds"
130
+ # => "the twenty-first of August, two thousand twenty four at fourteen hours thirty five minutes forty two seconds"
127
131
  ```
128
132
 
129
- ### ⚙️ Опции
133
+ ---
134
+
135
+ ## Опции
130
136
 
131
- - `locale: :ru | :en` язык (по умолчанию берётся I18n.locale или :ru).
132
- - `:date, :time, :datetime` — формат преобразования для строк.
133
- - `short: true` — краткая форма для даты/времени.
134
- - `joiner: :and` — разговорный соединитель для дробей: `"ноль и пять десятых"` вместо `"ноль целых пять десятых"`.
135
- - `date_case: :genitive` — родительный падеж дня для дат в контексте “когда?”.
136
- - `Num2words.to_words(number, locale: :en)` универсальный способ вызова без расширения базовых классов.
137
- - `Num2words.to_currency(number)` — преобразование число в валюту.
137
+ - `locale: :ru` или второй аргумент `:ru` выбор локали.
138
+ - `style: :fraction | :decimal` — стиль дробной части.
139
+ - `joiner: :default | :and` — формальный или разговорный соединитель дробей.
140
+ - `date_case: :default | :genitive` — падеж дня для дат, если локаль это поддерживает.
141
+ - `short: true` — краткая форма времени или даты/времени.
142
+ - `word_case: :default | :upper | :downcase | :capitalize | :title` регистр результата.
143
+ - `minor: :always | :nonzero | :never` — вывод младшей денежной единицы.
144
+ - `code: :USD` — выбор валюты для `to_currency`.
138
145
 
139
- ### Консоль 💻
146
+ Полная справка по API:
140
147
 
141
- Num2words поддерживает интерактивную консоль для быстрого тестирования.
142
- Это удобно при работе с разными числами и языками.
148
+ - [English](docs/api.md)
149
+ - [Русский](docs/api.ru.md)
143
150
 
144
- #### Запуск консоли из репозитория
151
+ Ограничения числовых разрядов:
152
+
153
+ - [English](docs/limits.md)
154
+ - [Русский](docs/limits.ru.md)
155
+
156
+ ---
157
+
158
+ ## Консоль
159
+
160
+ Запуск из репозитория:
145
161
 
146
162
  ```bash
147
163
  bin/console
148
164
  ```
149
165
 
150
- #### Запуск консоли после установки гема
166
+ Запуск после установки гема:
151
167
 
152
168
  ```bash
153
169
  num2words-console
154
170
  ```
155
171
 
156
- После запуска появится приветственное сообщение:
172
+ В консоли доступны обычные вызовы:
157
173
 
158
- ```bash
159
- Добро пожаловать в консоль num2words!
160
- Можете использовать: Num2words.to_words(2025)
161
- -------------------------------------------------------------
174
+ ```ruby
175
+ Num2words.to_words(2025, :ru)
176
+ Num2words.to_currency("12.50", :en, code: :EUR)
177
+ "2024-08-21 14:35:42".to_words(:fr)
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Локали
183
+
184
+ Все локали поддерживают общий набор возможностей: числа, дроби, даты, время, дату-время и валюты. Грамматика и правила чтения чисел вынесены в соответствующие `Num2words::Locales::XX` модули.
185
+
186
+ Поддерживаемые локали:
187
+
188
+ ```text
189
+ ar be bg bn cs da de el en es et fa fi fr gu he hi hr hu id it ja kn ko
190
+ kz lt lv ml mr ms nb nl pa pl pt ro ru sk sl sr sv sw ta te th tr uk ur
191
+ vi zh
162
192
  ```
163
193
 
164
- 👉 Это позволяет проверять работу гема без написания отдельных скриптов.
194
+ Всего: 50 локалей.
195
+
196
+ ### Валюты
197
+
198
+ Каждая локаль содержит одинаковый каталог из 32 валют:
199
+
200
+ ```text
201
+ BDT BGN BRL BYN CNY CZK DKK EUR GBP HUF IDR ILS INR IRR JPY KES KRW
202
+ KZT MYR NOK PKR PLN RON RSD RUB SAR SEK THB TRY UAH USD VND
203
+ ```
165
204
 
166
205
  ---
167
206
 
168
- ## 🌍 Доступные локали (из коробки)
169
-
170
- | Язык | ISO-код | Валюта | Файл |
171
- |---------------|---------|-------------|--------|
172
- | Английский | en | USD ($) | en.yml |
173
- | Русский | ru | RUB (₽) | ru.yml |
174
- | Китайский | zh | CNY (¥) | zh.yml |
175
- | Испанский | es | EUR (€) | es.yml |
176
- | Французский | fr | EUR (€) | fr.yml |
177
- | Немецкий | de | EUR (€) | de.yml |
178
- | Итальянский | it | EUR (€) | it.yml |
179
- | Португальский | pt | EUR () | pt.yml |
180
- | Арабский | ar | SAR () | ar.yml |
181
- | Хинди | hi | INR (₹) | hi.yml |
182
- | Японский | ja | JPY (¥) | ja.yml |
183
- | Корейский | ko | KRW (₩) | ko.yml |
184
- | Турецкий | tr | TRY (₺) | tr.yml |
185
- | Нидерландский | nl | EUR (€) | nl.yml |
186
- | Польский | pl | PLN (zł) | pl.yml |
187
- | Украинский | uk | UAH (₴) | uk.yml |
188
- | Чешский | cs | CZK (Kč) | cs.yml |
189
- | Румынский | ro | RON (lei) | ro.yml |
190
- | Болгарский | bg | BGN (лв) | bg.yml |
191
- | Белорусский | be | BYN (Br) | be.yml |
192
- | Казахский | kz | KZT (₸) | kz.yml |
193
- | Греческий | el | EUR (€) | el.yml |
194
- | Иврит | he | ILS (₪) | he.yml |
195
- | Шведский | sv | SEK (kr) | sv.yml |
196
- | Норвежский | nb | NOK (kr) | nb.yml |
197
- | Датский | da | DKK (kr) | da.yml |
198
- | Финский | fi | EUR (€) | fi.yml |
199
- | Венгерский | hu | HUF (Ft) | hu.yml |
200
- | Тайский | th | THB (฿) | th.yml |
201
- | Вьетнамский | vi | VND (₫) | vi.yml |
202
- | Индонезийский | id | IDR (Rp) | id.yml |
203
- | Персидский | fa | IRR (﷼) | fa.yml |
204
- | Сербский | sr | RSD (дин) | sr.yml |
205
- | Хорватский | hr | EUR (€) | hr.yml |
206
- | Словацкий | sk | EUR (€) | sk.yml |
207
- | Словенский | sl | EUR (€) | sl.yml |
208
- | Литовский | lt | EUR (€) | lt.yml |
209
- | Латышский | lv | EUR (€) | lv.yml |
210
- | Эстонский | et | EUR (€) | et.yml |
211
- | Малайский | ms | MYR (RM) | ms.yml |
212
- | Бенгальский | bn | BDT (৳) | bn.yml |
213
- | Урду | ur | PKR (₨) | ur.yml |
214
- | Тамильский | ta | INR (₹) | ta.yml |
215
- | Телугу | te | INR (₹) | te.yml |
216
- | Малаялам | ml | INR (₹) | ml.yml |
217
- | Каннада | kn | INR (₹) | kn.yml |
218
- | Маратхи | mr | INR (₹) | mr.yml |
219
- | Гуджарати | gu | INR (₹) | gu.yml |
220
- | Панджаби | pa | INR (₹) | pa.yml |
221
- | Суахили | sw | KES (Sh) | sw.yml |
222
-
223
- Можно легко добавить новые:
224
-
225
- - YAML-файл: `config/locales/xx.yml`
226
- - Обёртка: `lib/num2words/locales/xx.rb`
227
-
228
- ### 🌍 Поддержка локалей (ru / en)
229
-
230
- | Возможность | ru | en |
231
- | ----------------------------- | --------- | ---------- |
232
- | 🔢 Числа (Integer) | ✔ | ✔ |
233
- | 🔢 Числа (Float) | ✔ | ✔ |
234
- | 💰 Валюта | ✔ (рубли) | ✔ (rubles) |
235
- | 💰 Валюта (short) | ✖ | ✖ |
236
- | 💱 Выбор валюты (USD/EUR/…) | ✖ | ✖ |
237
- | 📅 Дата | ✔ | ✔ |
238
- | ⏰ Время | ✔ | ✔ |
239
- | 🕓 Дата и время | ✔ | ✔ |
240
- | 📝 Краткая форма даты/времени | ✔ | ✔ |
207
+ ## Добавление локали
208
+
209
+ Новая локаль состоит из двух файлов:
210
+
211
+ - `config/locales/xx.yml` данные: числа, даты, время, валюты, грамматика.
212
+ - `lib/num2words/locales/xx.rb` языковые правила и hooks для converter.
213
+
214
+ После добавления локали стоит покрыть ее отдельным spec и добавить в `spec/currency_catalog_spec.rb`.
215
+
216
+ Подробная инструкция:
217
+
218
+ - [English](docs/locale_development.md)
219
+ - [Русский](docs/locale_development.ru.md)
241
220
 
242
221
  ---
243
222
 
244
- ## 🧪 Тестирование
223
+ ## Тестирование
245
224
 
246
225
  ```bash
247
226
  bundle exec rspec
248
227
  ```
249
228
 
250
- ## 📌 Roadmap
229
+ ## Rake tasks
230
+
231
+ ```bash
232
+ bundle exec rake spec
233
+ bundle exec rake build
234
+ bundle exec rake release:check
235
+ bundle exec rake locales
236
+ bundle exec rake currencies
237
+ bundle exec rake benchmark
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Roadmap
251
243
 
252
- - [x] 🇬🇧 Поддержка английского языка
253
- - [x] 🔠 Опция выбора регистра (строчные/Прописные)
254
- - [x] Краткая форма даты/времени
255
- - [ ] 💵 Поддержка других валют (USD, EUR и т.д)
256
- - [ ] 💰 Краткая форма валюты
244
+ - [x] Поддержка чисел для всех локалей.
245
+ - [x] Поддержка дробей для всех локалей.
246
+ - [x] Поддержка дат и времени для всех локалей.
247
+ - [x] Единый каталог из 32 валют для всех локалей.
248
+ - [x] Интерактивная консоль.
249
+ - [ ] Дополнительная экспертная вычитка грамматики носителями языка.
250
+ - [ ] Расширение списка валют.
251
+ - [ ] Настраиваемые форматы валютного вывода.
257
252
 
258
253
  ---
259
254
 
260
- ## 📜 Лицензия
255
+ ## Лицензия
261
256
 
262
257
  [MIT](LICENSE)
data/Rakefile CHANGED
@@ -2,9 +2,45 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
+ require "yaml"
5
6
 
6
- # таска для запуска тестов
7
7
  RSpec::Core::RakeTask.new(:spec)
8
8
 
9
- # по умолчанию запускаем тесты
9
+ task build: "build:clean"
10
+
11
+ namespace :build do
12
+ task :clean do
13
+ Dir.glob("num2words-*.gem").each { |file| File.delete(file) }
14
+ end
15
+ end
16
+
17
+ namespace :release do
18
+ desc "Run tests and build the gem"
19
+ task check: [:spec, :build]
20
+ end
21
+
22
+ desc "Print supported locale codes"
23
+ task :locales do
24
+ locales = Dir[File.expand_path("config/locales/*.yml", __dir__)]
25
+ .map { |file| File.basename(file, ".yml") }
26
+ .sort
27
+
28
+ puts locales.join(" ")
29
+ end
30
+
31
+ desc "Print supported currency codes"
32
+ task :currencies do
33
+ currencies = YAML.load_file(File.expand_path("config/locales/ru.yml", __dir__))
34
+ .dig("ru", "num2words", "currencies")
35
+ .keys
36
+ .sort
37
+
38
+ puts currencies.join(" ")
39
+ end
40
+
41
+ desc "Run converter benchmark"
42
+ task :benchmark do
43
+ sh "ruby benchmark/converter_benchmark.rb"
44
+ end
45
+
10
46
  task default: :spec
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require_relative "../lib/num2words"
5
+
6
+ DEFAULT_ITERATIONS = 100
7
+
8
+ def all_locales
9
+ Dir[File.expand_path("../config/locales/*.yml", __dir__)]
10
+ .map { |file| File.basename(file, ".yml").to_sym }
11
+ .sort
12
+ end
13
+
14
+ def selected_locales
15
+ requested = ENV.fetch("LOCALES", "").split(",").map(&:strip).reject(&:empty?).map(&:to_sym)
16
+ return all_locales if requested.empty?
17
+
18
+ unknown = requested - all_locales
19
+ abort "Unknown locales: #{unknown.join(', ')}" unless unknown.empty?
20
+
21
+ requested
22
+ end
23
+
24
+ def iterations
25
+ Integer(ENV.fetch("ITERATIONS", DEFAULT_ITERATIONS.to_s))
26
+ rescue ArgumentError
27
+ abort "ITERATIONS must be an integer"
28
+ end
29
+
30
+ SCENARIOS = {
31
+ integer: ->(locale) { Num2words.to_words(123_456_789, locale) },
32
+ decimal: ->(locale) { Num2words.to_words("12345.67", locale) },
33
+ date: ->(locale) { Num2words.to_words("2024-08-21", locale) },
34
+ time: ->(locale) { Num2words.to_words("14:35:42", locale) },
35
+ datetime: ->(locale) { Num2words.to_words("2024-08-21 14:35:42", locale) },
36
+ currency: ->(locale) { Num2words.to_currency("12345.67", locale) }
37
+ }.freeze
38
+
39
+ def measure_scenarios(locales, iterations_count)
40
+ SCENARIOS.map do |name, callable|
41
+ calls = locales.size * iterations_count
42
+ elapsed = Benchmark.realtime do
43
+ iterations_count.times do
44
+ locales.each { |locale| callable.call(locale) }
45
+ end
46
+ end
47
+
48
+ [name, calls, elapsed]
49
+ end
50
+ end
51
+
52
+ def measure_slowest_locales(locales, iterations_count)
53
+ locales.map do |locale|
54
+ calls = SCENARIOS.size * iterations_count
55
+ elapsed = Benchmark.realtime do
56
+ iterations_count.times do
57
+ SCENARIOS.each_value { |callable| callable.call(locale) }
58
+ end
59
+ end
60
+
61
+ [locale, calls, elapsed]
62
+ end.sort_by { |(_, _, elapsed)| -elapsed }.first(10)
63
+ end
64
+
65
+ def print_table(headers, rows)
66
+ widths = headers.each_with_index.map do |header, index|
67
+ ([header.length] + rows.map { |row| row[index].to_s.length }).max
68
+ end
69
+
70
+ puts headers.each_with_index.map { |header, index| header.ljust(widths[index]) }.join(" ")
71
+ puts widths.map { |width| "-" * width }.join(" ")
72
+ rows.each do |row|
73
+ puts row.each_with_index.map { |value, index| value.to_s.ljust(widths[index]) }.join(" ")
74
+ end
75
+ end
76
+
77
+ locales = selected_locales
78
+ iterations_count = iterations
79
+
80
+ puts "num2words benchmark"
81
+ puts "Locales: #{locales.join(', ')}"
82
+ puts "Iterations per locale: #{iterations_count}"
83
+ puts
84
+
85
+ scenario_rows = measure_scenarios(locales, iterations_count).map do |name, calls, elapsed|
86
+ [name, calls, format("%.4f", elapsed), format("%.4f", (elapsed / calls) * 1000)]
87
+ end
88
+
89
+ print_table(["Scenario", "Calls", "Total, s", "ms/call"], scenario_rows)
90
+ puts
91
+
92
+ slowest_rows = measure_slowest_locales(locales, iterations_count).map do |locale, calls, elapsed|
93
+ [locale, calls, format("%.4f", elapsed), format("%.4f", (elapsed / calls) * 1000)]
94
+ end
95
+
96
+ print_table(["Slowest locale", "Calls", "Total, s", "ms/call"], slowest_rows)
@@ -18,7 +18,7 @@ ar:
18
18
  conjunction: "فاصلة"
19
19
  default_fraction: "كسور"
20
20
  fractions:
21
- 10: ["عُشر", "أعشار", "أعشار"]
21
+ 10: ["عُشر", "عُشران", "أعشار"]
22
22
  100: ["مِئويّة", "مِئويّتان", "مِئويّات"]
23
23
  1000: ["ألفيّة", "ألفيّتان", "ألفيّات"]
24
24
  10000: ["عُشر الألف", "أعشار الألف", "أعشار الألف"]
@@ -89,6 +89,10 @@ ar:
89
89
  major_unit: [ "جنيه إسترليني", "جنيهان إسترلينيان", "جنيهات إسترلينية" ]
90
90
  minor_unit: [ "بنس", "بنسان", "بنسات" ]
91
91
  symbol: "£"
92
+ BRL:
93
+ major_unit: [ "ريال برازيلي", "ريالان برازيليان", "ريالات برازيلية" ]
94
+ minor_unit: [ "سنتافو", "سنتافوان", "سنتافوات" ]
95
+ symbol: "R$"
92
96
  CNY:
93
97
  major_unit: [ "يوان", "يوانان", "يوان" ]
94
98
  minor_unit: [ "فن", "فنان", "فنات" ]
@@ -88,6 +88,10 @@ be:
88
88
  major_unit: [ "фунт", "фунты", "фунтаў" ]
89
89
  minor_unit: [ "пенс", "пенсы", "пенсаў" ]
90
90
  symbol: "£"
91
+ BRL:
92
+ major_unit: [ "бразільскі рэал", "бразільскія рэалы", "бразільскіх рэалаў" ]
93
+ minor_unit: [ "сентава", "сентава", "сентава" ]
94
+ symbol: "R$"
91
95
  CNY:
92
96
  major_unit: [ "юань", "юані", "юаняў" ]
93
97
  minor_unit: [ "фэнь", "фэні", "фэняў" ]