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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +40 -0
- data/CHANGELOG.md +40 -0
- data/README.md +139 -144
- data/Rakefile +38 -2
- data/benchmark/converter_benchmark.rb +96 -0
- data/config/locales/ar.yml +5 -1
- data/config/locales/be.yml +4 -0
- data/config/locales/bg.yml +4 -0
- data/config/locales/bn.yml +4 -0
- data/config/locales/cs.yml +6 -2
- data/config/locales/da.yml +4 -0
- data/config/locales/de.yml +8 -4
- data/config/locales/el.yml +4 -0
- data/config/locales/en.yml +4 -0
- data/config/locales/es.yml +4 -0
- data/config/locales/et.yml +9 -5
- data/config/locales/fa.yml +15 -11
- data/config/locales/fi.yml +8 -4
- data/config/locales/fr.yml +4 -0
- data/config/locales/gu.yml +4 -0
- data/config/locales/he.yml +6 -2
- data/config/locales/hi.yml +4 -0
- data/config/locales/hr.yml +8 -4
- data/config/locales/hu.yml +6 -2
- data/config/locales/id.yml +4 -0
- data/config/locales/it.yml +4 -0
- data/config/locales/ja.yml +10 -5
- data/config/locales/kn.yml +186 -10
- data/config/locales/ko.yml +184 -13
- data/config/locales/kz.yml +183 -17
- data/config/locales/lt.yml +184 -8
- data/config/locales/lv.yml +188 -12
- data/config/locales/ml.yml +189 -13
- data/config/locales/mr.yml +184 -8
- data/config/locales/ms.yml +184 -8
- data/config/locales/nb.yml +183 -17
- data/config/locales/nl.yml +184 -13
- data/config/locales/pa.yml +189 -13
- data/config/locales/pl.yml +184 -13
- data/config/locales/pt.yml +184 -13
- data/config/locales/ro.yml +183 -12
- data/config/locales/ru.yml +4 -0
- data/config/locales/sk.yml +184 -8
- data/config/locales/sl.yml +185 -9
- data/config/locales/sr.yml +186 -15
- data/config/locales/sv.yml +182 -13
- data/config/locales/sw.yml +184 -8
- data/config/locales/ta.yml +184 -8
- data/config/locales/te.yml +185 -9
- data/config/locales/th.yml +183 -12
- data/config/locales/tr.yml +183 -12
- data/config/locales/uk.yml +189 -18
- data/config/locales/ur.yml +187 -11
- data/config/locales/vi.yml +183 -12
- data/config/locales/zh.yml +191 -9
- data/docs/api.md +422 -0
- data/docs/api.ru.md +422 -0
- data/docs/limits.md +140 -0
- data/docs/limits.ru.md +140 -0
- data/docs/locale_development.md +241 -0
- data/docs/locale_development.ru.md +241 -0
- data/lib/num2words/converter.rb +144 -35
- data/lib/num2words/locales/ar.rb +130 -0
- data/lib/num2words/locales/be.rb +101 -0
- data/lib/num2words/locales/bg.rb +127 -0
- data/lib/num2words/locales/bn.rb +112 -0
- data/lib/num2words/locales/cs.rb +106 -0
- data/lib/num2words/locales/da.rb +97 -0
- data/lib/num2words/locales/de.rb +119 -0
- data/lib/num2words/locales/el.rb +143 -0
- data/lib/num2words/locales/en.rb +90 -0
- data/lib/num2words/locales/es.rb +185 -0
- data/lib/num2words/locales/et.rb +104 -0
- data/lib/num2words/locales/fa.rb +104 -0
- data/lib/num2words/locales/fi.rb +104 -0
- data/lib/num2words/locales/fr.rb +121 -0
- data/lib/num2words/locales/gu.rb +112 -0
- data/lib/num2words/locales/he.rb +130 -0
- data/lib/num2words/locales/hi.rb +112 -0
- data/lib/num2words/locales/hr.rb +117 -0
- data/lib/num2words/locales/hu.rb +111 -0
- data/lib/num2words/locales/id.rb +104 -0
- data/lib/num2words/locales/it.rb +142 -0
- data/lib/num2words/locales/ja.rb +126 -0
- data/lib/num2words/locales/kn.rb +122 -2
- data/lib/num2words/locales/ko.rb +158 -2
- data/lib/num2words/locales/kz.rb +137 -2
- data/lib/num2words/locales/lt.rb +146 -2
- data/lib/num2words/locales/lv.rb +148 -2
- data/lib/num2words/locales/ml.rb +142 -2
- data/lib/num2words/locales/mr.rb +142 -2
- data/lib/num2words/locales/ms.rb +134 -2
- data/lib/num2words/locales/nb.rb +153 -0
- data/lib/num2words/locales/nl.rb +142 -2
- data/lib/num2words/locales/pa.rb +122 -2
- data/lib/num2words/locales/pl.rb +149 -2
- data/lib/num2words/locales/pt.rb +164 -2
- data/lib/num2words/locales/ro.rb +165 -2
- data/lib/num2words/locales/ru.rb +100 -0
- data/lib/num2words/locales/sk.rb +124 -2
- data/lib/num2words/locales/sl.rb +154 -2
- data/lib/num2words/locales/sr.rb +141 -3
- data/lib/num2words/locales/sv.rb +141 -2
- data/lib/num2words/locales/sw.rb +133 -2
- data/lib/num2words/locales/ta.rb +142 -2
- data/lib/num2words/locales/te.rb +142 -2
- data/lib/num2words/locales/th.rb +133 -2
- data/lib/num2words/locales/tr.rb +141 -2
- data/lib/num2words/locales/uk.rb +134 -2
- data/lib/num2words/locales/ur.rb +130 -2
- data/lib/num2words/locales/vi.rb +142 -2
- data/lib/num2words/locales/zh.rb +172 -2
- data/lib/num2words/version.rb +1 -1
- data/num2words.gemspec +3 -2
- metadata +28 -6
- data/lib/num2words/locales/no.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 78f705a73f571176b4d920dfe220590d2bd217ea513fb817028a0ae2c22221cd
|
|
4
|
+
data.tar.gz: 1e8726b93feee82a3b87143ae8a25edf388d726f65ecf424ec713fcc4323ea08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 [](https://badge.fury.io/rb/num2words)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
68
|
-
# => "
|
|
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
|
|
100
|
+
"2024-08-21".to_words(:ru)
|
|
95
101
|
# => "двадцать первое августа две тысячи двадцать четвёртого года"
|
|
96
102
|
|
|
97
|
-
"2024-08-21".to_words(:ru,
|
|
103
|
+
"2024-08-21".to_words(:ru, date_case: :genitive)
|
|
98
104
|
# => "двадцать первого августа две тысячи двадцать четвёртого года"
|
|
99
105
|
|
|
100
|
-
"2024-08-21".to_words(:en
|
|
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
|
|
113
|
+
"14:35:42".to_words(:ru)
|
|
107
114
|
# => "четырнадцать часов тридцать пять минут сорок две секунды"
|
|
108
115
|
|
|
109
|
-
"14:35:42".to_words(:ru,
|
|
116
|
+
"14:35:42".to_words(:ru, short: true)
|
|
110
117
|
# => "четырнадцать часов тридцать пять минут"
|
|
111
118
|
|
|
112
|
-
"14:35:42".to_words(:en
|
|
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
|
|
132
|
-
-
|
|
133
|
-
- `
|
|
134
|
-
- `
|
|
135
|
-
- `
|
|
136
|
-
- `
|
|
137
|
-
- `
|
|
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
|
-
|
|
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
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
##
|
|
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
|
-
- [
|
|
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)
|
data/config/locales/ar.yml
CHANGED
|
@@ -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: [ "فن", "فنان", "فنات" ]
|
data/config/locales/be.yml
CHANGED
|
@@ -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: [ "фэнь", "фэні", "фэняў" ]
|