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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Locale Development
|
|
2
|
+
|
|
3
|
+
This guide describes how to add or update a locale in `num2words`.
|
|
4
|
+
|
|
5
|
+
Each locale has two main files:
|
|
6
|
+
|
|
7
|
+
- `config/locales/xx.yml` - locale data for numbers, grammar, dates, time and currencies.
|
|
8
|
+
- `lib/num2words/locales/xx.rb` - language-specific rules used by `Num2words::Converter`.
|
|
9
|
+
|
|
10
|
+
Use an existing completed locale as a reference. For simple European plural rules, `ru`, `uk`, `be`, `pl`, `cs` are useful examples. For languages with 4-digit or regional grouping, check `ja`, `ko`, `zh`, `hi`, `pa`, `ur`.
|
|
11
|
+
|
|
12
|
+
## YAML Structure
|
|
13
|
+
|
|
14
|
+
The YAML root must match the locale code:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
xx:
|
|
18
|
+
num2words:
|
|
19
|
+
ones_masc: []
|
|
20
|
+
ones_fem: []
|
|
21
|
+
teens: []
|
|
22
|
+
tens: []
|
|
23
|
+
hundreds: []
|
|
24
|
+
scales: []
|
|
25
|
+
|
|
26
|
+
grammar:
|
|
27
|
+
minus: ""
|
|
28
|
+
conjunction: ""
|
|
29
|
+
default_fraction: ""
|
|
30
|
+
|
|
31
|
+
fractions:
|
|
32
|
+
10: []
|
|
33
|
+
100: []
|
|
34
|
+
1000: []
|
|
35
|
+
10000: []
|
|
36
|
+
100000: []
|
|
37
|
+
1000000: []
|
|
38
|
+
10000000: []
|
|
39
|
+
100000000: []
|
|
40
|
+
1000000000: []
|
|
41
|
+
|
|
42
|
+
numbers:
|
|
43
|
+
ordinals:
|
|
44
|
+
nominative:
|
|
45
|
+
masculine: []
|
|
46
|
+
feminine: []
|
|
47
|
+
neuter: []
|
|
48
|
+
default:
|
|
49
|
+
masculine: []
|
|
50
|
+
|
|
51
|
+
datetime:
|
|
52
|
+
template: "%{date}, %{time}"
|
|
53
|
+
|
|
54
|
+
date:
|
|
55
|
+
template:
|
|
56
|
+
default: "%{day} %{month} %{year}"
|
|
57
|
+
nominative: "%{day} %{month} %{year}"
|
|
58
|
+
months:
|
|
59
|
+
default: []
|
|
60
|
+
nominative: []
|
|
61
|
+
|
|
62
|
+
time:
|
|
63
|
+
template:
|
|
64
|
+
hours_only: "%{hours}"
|
|
65
|
+
hours_minutes: "%{hours} %{minutes}"
|
|
66
|
+
hours_minutes_seconds: "%{hours} %{minutes} %{seconds}"
|
|
67
|
+
words:
|
|
68
|
+
hour: []
|
|
69
|
+
minute: []
|
|
70
|
+
second: []
|
|
71
|
+
|
|
72
|
+
warnings:
|
|
73
|
+
currency_not_available: "Currency %{currency} is not available for locale %{locale}."
|
|
74
|
+
|
|
75
|
+
currencies:
|
|
76
|
+
USD:
|
|
77
|
+
major_unit: []
|
|
78
|
+
minor_unit: []
|
|
79
|
+
symbol: "$"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Currency Catalog
|
|
83
|
+
|
|
84
|
+
Every completed locale must contain the same 32 currency codes:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
BDT BGN BRL BYN CNY CZK DKK EUR GBP HUF IDR ILS INR IRR JPY KES KRW
|
|
88
|
+
KZT MYR NOK PKR PLN RON RSD RUB SAR SEK THB TRY UAH USD VND
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Each currency entry must include:
|
|
92
|
+
|
|
93
|
+
- `major_unit` - array of unit forms.
|
|
94
|
+
- `minor_unit` - array of minor unit forms.
|
|
95
|
+
- `symbol` - display symbol.
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
USD:
|
|
101
|
+
major_unit: ["dollar", "dollars", "dollars"]
|
|
102
|
+
minor_unit: ["cent", "cents", "cents"]
|
|
103
|
+
symbol: "$"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If the language does not use plural forms, repeat the same value three times.
|
|
107
|
+
|
|
108
|
+
## Locale Module
|
|
109
|
+
|
|
110
|
+
Each Ruby locale module must register itself:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# frozen_string_literal: true
|
|
114
|
+
|
|
115
|
+
module Num2words
|
|
116
|
+
module Locales
|
|
117
|
+
module XX
|
|
118
|
+
ONES_MASC = I18n.t("num2words.ones_masc", locale: :xx)
|
|
119
|
+
ONES_FEM = I18n.t("num2words.ones_fem", locale: :xx)
|
|
120
|
+
TEENS = I18n.t("num2words.teens", locale: :xx)
|
|
121
|
+
TENS = I18n.t("num2words.tens", locale: :xx)
|
|
122
|
+
HUNDREDS = I18n.t("num2words.hundreds", locale: :xx)
|
|
123
|
+
SCALES = I18n.t("num2words.scales", locale: :xx)
|
|
124
|
+
|
|
125
|
+
FRACTIONS = I18n.t("num2words.fractions", locale: :xx)
|
|
126
|
+
GRAMMAR = I18n.t("num2words.grammar", locale: :xx)
|
|
127
|
+
|
|
128
|
+
DATE = I18n.t("num2words.date", locale: :xx)
|
|
129
|
+
DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :xx)
|
|
130
|
+
TIME = I18n.t("num2words.time", locale: :xx)
|
|
131
|
+
TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :xx)
|
|
132
|
+
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :xx)
|
|
133
|
+
|
|
134
|
+
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :xx)
|
|
135
|
+
|
|
136
|
+
module_function
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
register :xx, XX
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Common Hooks
|
|
145
|
+
|
|
146
|
+
`Converter` handles common flow and calls locale hooks when they exist.
|
|
147
|
+
|
|
148
|
+
Number hooks:
|
|
149
|
+
|
|
150
|
+
- `integer_to_words(number, feminine: false)` - fully custom integer conversion.
|
|
151
|
+
- `triple_to_words(number, scale_idx, feminine: false)` - conversion for a 0..999 group.
|
|
152
|
+
- `feminine_group?(scale_idx)` - whether a scale group uses feminine forms.
|
|
153
|
+
- `pluralize(number, singular, few, plural)` - local plural rule.
|
|
154
|
+
|
|
155
|
+
Fraction hooks:
|
|
156
|
+
|
|
157
|
+
- `minus_word`
|
|
158
|
+
- `fraction_joiner(joiner)`
|
|
159
|
+
- `default_fraction_word`
|
|
160
|
+
- `fraction_numerator_feminine?`
|
|
161
|
+
- `decimal_separator_word`
|
|
162
|
+
- `decimal_fraction_words(fraction_string)`
|
|
163
|
+
- `join_decimal_words(words)`
|
|
164
|
+
- `join_fraction_words(words)`
|
|
165
|
+
|
|
166
|
+
Date and time hooks:
|
|
167
|
+
|
|
168
|
+
- `date_day(day, format:, date_case:)`
|
|
169
|
+
- `date_year(year, format:)`
|
|
170
|
+
- `time_unit_feminine?(unit)`
|
|
171
|
+
- `time_number_words(number, unit:)`
|
|
172
|
+
- `join_time_words(number_words, unit_words)`
|
|
173
|
+
|
|
174
|
+
Currency hooks:
|
|
175
|
+
|
|
176
|
+
- `currency_major_feminine?(currency)`
|
|
177
|
+
- `currency_minor_feminine?(currency)`
|
|
178
|
+
- `currency_number_words(number, currency, unit:)`
|
|
179
|
+
- `join_currency_parts(parts)`
|
|
180
|
+
|
|
181
|
+
Use hooks only when the language needs them. If the common converter behavior is enough, keep the locale module smaller.
|
|
182
|
+
|
|
183
|
+
## Specs
|
|
184
|
+
|
|
185
|
+
Add a dedicated spec:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
spec/xx_to_words_spec.rb
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Minimum coverage should include:
|
|
192
|
+
|
|
193
|
+
- zero and basic integers;
|
|
194
|
+
- tens, hundreds and scale values;
|
|
195
|
+
- large compound numbers;
|
|
196
|
+
- negative numbers;
|
|
197
|
+
- string integer input;
|
|
198
|
+
- decimal string input with dot and comma;
|
|
199
|
+
- fraction style and `joiner: :and`;
|
|
200
|
+
- `word_case`;
|
|
201
|
+
- `to_currency` with default currency, another currency and `minor:` options;
|
|
202
|
+
- date, time and datetime.
|
|
203
|
+
|
|
204
|
+
Then add the locale code to:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
spec/currency_catalog_spec.rb
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The locale API smoke spec discovers YAML files automatically, so no manual update is needed there.
|
|
211
|
+
|
|
212
|
+
## Verification
|
|
213
|
+
|
|
214
|
+
Run targeted tests first:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
bundle exec rspec spec/xx_to_words_spec.rb spec/currency_catalog_spec.rb spec/locale_api_smoke_spec.rb
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Then run the full suite:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
bundle exec rspec
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Before release, also build the gem:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
gem build num2words.gemspec
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Practical Checklist
|
|
233
|
+
|
|
234
|
+
- YAML root matches the locale code.
|
|
235
|
+
- Locale module registers with `register :xx, XX`.
|
|
236
|
+
- `GRAMMAR`, `FRACTIONS`, `DATE`, `TIME`, `DATETIME_TEMPLATE` and `ORDINALS` constants exist.
|
|
237
|
+
- All 32 currencies exist and have `major_unit`, `minor_unit`, `symbol`.
|
|
238
|
+
- Default currency is the first currency in YAML.
|
|
239
|
+
- Dates and times work through `Num2words.to_words`.
|
|
240
|
+
- Currency works through `Num2words.to_currency`.
|
|
241
|
+
- Full test suite passes.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Разработка локалей
|
|
2
|
+
|
|
3
|
+
Эта инструкция описывает, как добавить новую локаль или актуализировать существующую в `num2words`.
|
|
4
|
+
|
|
5
|
+
Каждая локаль состоит из двух основных файлов:
|
|
6
|
+
|
|
7
|
+
- `config/locales/xx.yml` - данные локали: числа, грамматика, даты, время и валюты.
|
|
8
|
+
- `lib/num2words/locales/xx.rb` - языковые правила, которые использует `Num2words::Converter`.
|
|
9
|
+
|
|
10
|
+
В качестве примера лучше брать уже завершенные локали. Для языков с простыми европейскими правилами множественного числа подойдут `ru`, `uk`, `be`, `pl`, `cs`. Для языков с 4-значной или региональной группировкой смотри `ja`, `ko`, `zh`, `hi`, `pa`, `ur`.
|
|
11
|
+
|
|
12
|
+
## Структура YAML
|
|
13
|
+
|
|
14
|
+
Корневой ключ YAML должен совпадать с кодом локали:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
xx:
|
|
18
|
+
num2words:
|
|
19
|
+
ones_masc: []
|
|
20
|
+
ones_fem: []
|
|
21
|
+
teens: []
|
|
22
|
+
tens: []
|
|
23
|
+
hundreds: []
|
|
24
|
+
scales: []
|
|
25
|
+
|
|
26
|
+
grammar:
|
|
27
|
+
minus: ""
|
|
28
|
+
conjunction: ""
|
|
29
|
+
default_fraction: ""
|
|
30
|
+
|
|
31
|
+
fractions:
|
|
32
|
+
10: []
|
|
33
|
+
100: []
|
|
34
|
+
1000: []
|
|
35
|
+
10000: []
|
|
36
|
+
100000: []
|
|
37
|
+
1000000: []
|
|
38
|
+
10000000: []
|
|
39
|
+
100000000: []
|
|
40
|
+
1000000000: []
|
|
41
|
+
|
|
42
|
+
numbers:
|
|
43
|
+
ordinals:
|
|
44
|
+
nominative:
|
|
45
|
+
masculine: []
|
|
46
|
+
feminine: []
|
|
47
|
+
neuter: []
|
|
48
|
+
default:
|
|
49
|
+
masculine: []
|
|
50
|
+
|
|
51
|
+
datetime:
|
|
52
|
+
template: "%{date}, %{time}"
|
|
53
|
+
|
|
54
|
+
date:
|
|
55
|
+
template:
|
|
56
|
+
default: "%{day} %{month} %{year}"
|
|
57
|
+
nominative: "%{day} %{month} %{year}"
|
|
58
|
+
months:
|
|
59
|
+
default: []
|
|
60
|
+
nominative: []
|
|
61
|
+
|
|
62
|
+
time:
|
|
63
|
+
template:
|
|
64
|
+
hours_only: "%{hours}"
|
|
65
|
+
hours_minutes: "%{hours} %{minutes}"
|
|
66
|
+
hours_minutes_seconds: "%{hours} %{minutes} %{seconds}"
|
|
67
|
+
words:
|
|
68
|
+
hour: []
|
|
69
|
+
minute: []
|
|
70
|
+
second: []
|
|
71
|
+
|
|
72
|
+
warnings:
|
|
73
|
+
currency_not_available: "Currency %{currency} is not available for locale %{locale}."
|
|
74
|
+
|
|
75
|
+
currencies:
|
|
76
|
+
USD:
|
|
77
|
+
major_unit: []
|
|
78
|
+
minor_unit: []
|
|
79
|
+
symbol: "$"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Каталог валют
|
|
83
|
+
|
|
84
|
+
Каждая завершенная локаль должна содержать одинаковый набор из 32 валют:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
BDT BGN BRL BYN CNY CZK DKK EUR GBP HUF IDR ILS INR IRR JPY KES KRW
|
|
88
|
+
KZT MYR NOK PKR PLN RON RSD RUB SAR SEK THB TRY UAH USD VND
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Каждая валюта должна содержать:
|
|
92
|
+
|
|
93
|
+
- `major_unit` - формы основной денежной единицы.
|
|
94
|
+
- `minor_unit` - формы младшей денежной единицы.
|
|
95
|
+
- `symbol` - символ валюты.
|
|
96
|
+
|
|
97
|
+
Пример:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
USD:
|
|
101
|
+
major_unit: ["доллар", "доллара", "долларов"]
|
|
102
|
+
minor_unit: ["цент", "цента", "центов"]
|
|
103
|
+
symbol: "$"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Если в языке нет склонения по числу, повторить одно и то же значение три раза.
|
|
107
|
+
|
|
108
|
+
## Ruby-модуль локали
|
|
109
|
+
|
|
110
|
+
Каждый Ruby-модуль локали должен зарегистрировать себя:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# frozen_string_literal: true
|
|
114
|
+
|
|
115
|
+
module Num2words
|
|
116
|
+
module Locales
|
|
117
|
+
module XX
|
|
118
|
+
ONES_MASC = I18n.t("num2words.ones_masc", locale: :xx)
|
|
119
|
+
ONES_FEM = I18n.t("num2words.ones_fem", locale: :xx)
|
|
120
|
+
TEENS = I18n.t("num2words.teens", locale: :xx)
|
|
121
|
+
TENS = I18n.t("num2words.tens", locale: :xx)
|
|
122
|
+
HUNDREDS = I18n.t("num2words.hundreds", locale: :xx)
|
|
123
|
+
SCALES = I18n.t("num2words.scales", locale: :xx)
|
|
124
|
+
|
|
125
|
+
FRACTIONS = I18n.t("num2words.fractions", locale: :xx)
|
|
126
|
+
GRAMMAR = I18n.t("num2words.grammar", locale: :xx)
|
|
127
|
+
|
|
128
|
+
DATE = I18n.t("num2words.date", locale: :xx)
|
|
129
|
+
DATE_TEMPLATE = I18n.t("num2words.date.template", locale: :xx)
|
|
130
|
+
TIME = I18n.t("num2words.time", locale: :xx)
|
|
131
|
+
TIME_TEMPLATE = I18n.t("num2words.time.template", locale: :xx)
|
|
132
|
+
DATETIME_TEMPLATE = I18n.t("num2words.datetime.template", locale: :xx)
|
|
133
|
+
|
|
134
|
+
ORDINALS = I18n.t("num2words.numbers.ordinals", locale: :xx)
|
|
135
|
+
|
|
136
|
+
module_function
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
register :xx, XX
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Основные hooks
|
|
145
|
+
|
|
146
|
+
`Converter` отвечает за общий поток выполнения и вызывает методы локали, если они определены.
|
|
147
|
+
|
|
148
|
+
Hooks для чисел:
|
|
149
|
+
|
|
150
|
+
- `integer_to_words(number, feminine: false)` - полностью кастомное преобразование целых чисел.
|
|
151
|
+
- `triple_to_words(number, scale_idx, feminine: false)` - преобразование группы 0..999.
|
|
152
|
+
- `feminine_group?(scale_idx)` - нужно ли использовать женский род для группы разрядов.
|
|
153
|
+
- `pluralize(number, singular, few, plural)` - локальное правило множественного числа.
|
|
154
|
+
|
|
155
|
+
Hooks для дробей:
|
|
156
|
+
|
|
157
|
+
- `minus_word`
|
|
158
|
+
- `fraction_joiner(joiner)`
|
|
159
|
+
- `default_fraction_word`
|
|
160
|
+
- `fraction_numerator_feminine?`
|
|
161
|
+
- `decimal_separator_word`
|
|
162
|
+
- `decimal_fraction_words(fraction_string)`
|
|
163
|
+
- `join_decimal_words(words)`
|
|
164
|
+
- `join_fraction_words(words)`
|
|
165
|
+
|
|
166
|
+
Hooks для даты и времени:
|
|
167
|
+
|
|
168
|
+
- `date_day(day, format:, date_case:)`
|
|
169
|
+
- `date_year(year, format:)`
|
|
170
|
+
- `time_unit_feminine?(unit)`
|
|
171
|
+
- `time_number_words(number, unit:)`
|
|
172
|
+
- `join_time_words(number_words, unit_words)`
|
|
173
|
+
|
|
174
|
+
Hooks для валют:
|
|
175
|
+
|
|
176
|
+
- `currency_major_feminine?(currency)`
|
|
177
|
+
- `currency_minor_feminine?(currency)`
|
|
178
|
+
- `currency_number_words(number, currency, unit:)`
|
|
179
|
+
- `join_currency_parts(parts)`
|
|
180
|
+
|
|
181
|
+
Добавлять hook только если язык действительно требует отдельного поведения. Если общего поведения converter достаточно, модуль локали лучше оставить проще.
|
|
182
|
+
|
|
183
|
+
## Тесты
|
|
184
|
+
|
|
185
|
+
Для каждой локали нужен отдельный spec:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
spec/xx_to_words_spec.rb
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Минимальное покрытие:
|
|
192
|
+
|
|
193
|
+
- ноль и базовые целые числа;
|
|
194
|
+
- десятки, сотни и разряды;
|
|
195
|
+
- большие составные числа;
|
|
196
|
+
- отрицательные числа;
|
|
197
|
+
- целое число строкой;
|
|
198
|
+
- дробная строка с точкой и запятой;
|
|
199
|
+
- дроби и `joiner: :and`;
|
|
200
|
+
- `word_case`;
|
|
201
|
+
- `to_currency` с валютой по умолчанию, другой валютой и опциями `minor:`;
|
|
202
|
+
- дата, время и дата-время.
|
|
203
|
+
|
|
204
|
+
После добавления локали добавть ее код в:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
spec/currency_catalog_spec.rb
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`locale_api_smoke_spec` находит YAML-файлы автоматически, поэтому вручную обновлять его не нужно.
|
|
211
|
+
|
|
212
|
+
## Проверка
|
|
213
|
+
|
|
214
|
+
Сначала запустить целевые тесты:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
bundle exec rspec spec/xx_to_words_spec.rb spec/currency_catalog_spec.rb spec/locale_api_smoke_spec.rb
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Потом полный набор:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
bundle exec rspec
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Перед релизом также проверить сборку гема:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
gem build num2words.gemspec
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Практический чеклист
|
|
233
|
+
|
|
234
|
+
- Корневой ключ YAML совпадает с кодом локали.
|
|
235
|
+
- Ruby-модуль зарегистрирован через `register :xx, XX`.
|
|
236
|
+
- Есть константы `GRAMMAR`, `FRACTIONS`, `DATE`, `TIME`, `DATETIME_TEMPLATE`, `ORDINALS`.
|
|
237
|
+
- Все 32 валюты есть и содержат `major_unit`, `minor_unit`, `symbol`.
|
|
238
|
+
- Валюта по умолчанию стоит первой в YAML.
|
|
239
|
+
- Даты и время работают через `Num2words.to_words`.
|
|
240
|
+
- Валюты работают через `Num2words.to_currency`.
|
|
241
|
+
- Полный набор тестов проходит.
|