babelfish-ruby 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +137 -0
- data/Rakefile +25 -0
- data/babelfish.gemspec +27 -0
- data/lib/babelfish-ruby.rb +2 -0
- data/lib/babelfish.rb +289 -0
- data/lib/babelfish/phrase/compiler.rb +70 -0
- data/lib/babelfish/phrase/literal.rb +12 -0
- data/lib/babelfish/phrase/node.rb +14 -0
- data/lib/babelfish/phrase/parser.rb +173 -0
- data/lib/babelfish/phrase/parser_base.rb +73 -0
- data/lib/babelfish/phrase/plural_forms.rb +65 -0
- data/lib/babelfish/phrase/plural_forms_parser.rb +64 -0
- data/lib/babelfish/phrase/pluralizer.rb +409 -0
- data/lib/babelfish/phrase/string_to_compile.rb +8 -0
- data/lib/babelfish/phrase/variable.rb +12 -0
- data/lib/babelfish/version.rb +4 -0
- data/spec/lib/babelfish_spec.rb +105 -0
- data/spec/locales/test.en-US.yml +7 -0
- data/spec/locales/test.ru-RU.yml +9 -0
- data/spec/spec_helper.rb +21 -0
- metadata +147 -0
@@ -0,0 +1,409 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
class Babelfish
|
3
|
+
module Phrase
|
4
|
+
# Babelfish pluralizer.
|
5
|
+
module Pluralizer
|
6
|
+
|
7
|
+
@rules = {}
|
8
|
+
|
9
|
+
def self.add( locales, rule )
|
10
|
+
locales = [ locales ] unless locales.kind_of?(Array)
|
11
|
+
|
12
|
+
locales.each { |locale| @rules[locale] = rule }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find_rule( locale )
|
16
|
+
return @rules[locale] if @rules.has_key?(locale)
|
17
|
+
|
18
|
+
locale = @rules.keys.find do |loc|
|
19
|
+
/^#{Regexp.escape(loc)}[\-_]/.match(locale) ? loc : nil
|
20
|
+
end
|
21
|
+
locale = 'en' if locale.nil?
|
22
|
+
|
23
|
+
return @rules[locale]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.is_int( input )
|
27
|
+
return (0 == input % 1)
|
28
|
+
end
|
29
|
+
|
30
|
+
## PLURALIZATION RULES
|
31
|
+
## https://github.com/nodeca/babelfish/blob/master/lib/babelfish/pluralizer.js#L51
|
32
|
+
|
33
|
+
# Azerbaijani, Bambara, Burmese, Chinese, Dzongkha, Georgian, Hungarian, Igbo,
|
34
|
+
# Indonesian, Japanese, Javanese, Kabuverdianu, Kannada, Khmer, Korean,
|
35
|
+
# Koyraboro Senni, Lao, Makonde, Malay, Persian, Root, Sakha, Sango,
|
36
|
+
# Sichuan Yi, Thai, Tibetan, Tonga, Turkish, Vietnamese, Wolof, Yoruba
|
37
|
+
|
38
|
+
add(['az', 'bm', 'my', 'zh', 'dz', 'ka', 'hu', 'ig',
|
39
|
+
'id', 'ja', 'jv', 'kea', 'kn', 'km', 'ko',
|
40
|
+
'ses', 'lo', 'kde', 'ms', 'fa', 'root', 'sah', 'sg',
|
41
|
+
'ii', 'th', 'bo', 'to', 'tr', 'vi', 'wo', 'yo'
|
42
|
+
], lambda { |n|
|
43
|
+
return 0
|
44
|
+
});
|
45
|
+
|
46
|
+
# Manx
|
47
|
+
|
48
|
+
add(['gv'], lambda { |n|
|
49
|
+
m10, m20 = n % 10, n % 20
|
50
|
+
|
51
|
+
if (m10 == 1 || m10 == 2 || m20 == 0) && is_int(n)
|
52
|
+
return 0
|
53
|
+
end
|
54
|
+
|
55
|
+
return 1
|
56
|
+
});
|
57
|
+
|
58
|
+
|
59
|
+
# Central Morocco Tamazight
|
60
|
+
|
61
|
+
add(['tzm'], lambda { |n|
|
62
|
+
if n == 0 || n == 1 || (11 <= n && n <= 99 && is_int(n))
|
63
|
+
return 0
|
64
|
+
end
|
65
|
+
|
66
|
+
return 1
|
67
|
+
});
|
68
|
+
|
69
|
+
|
70
|
+
# Macedonian
|
71
|
+
|
72
|
+
add(['mk'], lambda { |n|
|
73
|
+
if (n % 10 == 1) && (n != 11) && is_int(n)
|
74
|
+
return 0
|
75
|
+
end
|
76
|
+
|
77
|
+
return 1
|
78
|
+
});
|
79
|
+
|
80
|
+
|
81
|
+
# Akan, Amharic, Bihari, Filipino, Gun, Hindi,
|
82
|
+
# Lingala, Malagasy, Northern Sotho, Tagalog, Tigrinya, Walloon
|
83
|
+
|
84
|
+
add(['ak', 'am', 'bh', 'fil', 'guw', 'hi',
|
85
|
+
'ln', 'mg', 'nso', 'tl', 'ti', 'wa'
|
86
|
+
], lambda { |n|
|
87
|
+
return (n == 0 || n == 1) ? 0 : 1;
|
88
|
+
});
|
89
|
+
|
90
|
+
|
91
|
+
# Afrikaans, Albanian, Basque, Bemba, Bengali, Bodo, Bulgarian, Catalan,
|
92
|
+
# Cherokee, Chiga, Danish, Divehi, Dutch, English, Esperanto, Estonian, Ewe,
|
93
|
+
# Faroese, Finnish, Friulian, Galician, Ganda, German, Greek, Gujarati, Hausa,
|
94
|
+
# Hawaiian, Hebrew, Icelandic, Italian, Kalaallisut, Kazakh, Kurdish,
|
95
|
+
# Luxembourgish, Malayalam, Marathi, Masai, Mongolian, Nahuatl, Nepali,
|
96
|
+
# Norwegian, Norwegian Bokmål, Norwegian Nynorsk, Nyankole, Oriya, Oromo,
|
97
|
+
# Papiamento, Pashto, Portuguese, Punjabi, Romansh, Saho, Samburu, Soga,
|
98
|
+
# Somali, Spanish, Swahili, Swedish, Swiss German, Syriac, Tamil, Telugu,
|
99
|
+
# Turkmen, Urdu, Walser, Western Frisian, Zulu
|
100
|
+
|
101
|
+
add(['af', 'sq', 'eu', 'bem', 'bn', 'brx', 'bg', 'ca',
|
102
|
+
'chr', 'cgg', 'da', 'dv', 'nl', 'en', 'eo', 'et', 'ee',
|
103
|
+
'fo', 'fi', 'fur', 'gl', 'lg', 'de', 'el', 'gu', 'ha',
|
104
|
+
'haw', 'he', 'is', 'it', 'kl', 'kk', 'ku',
|
105
|
+
'lb', 'ml', 'mr', 'mas', 'mn', 'nah', 'ne',
|
106
|
+
'no', 'nb', 'nn', 'nyn', 'or', 'om',
|
107
|
+
'pap', 'ps', 'pt', 'pa', 'rm', 'ssy', 'saq', 'xog',
|
108
|
+
'so', 'es', 'sw', 'sv', 'gsw', 'syr', 'ta', 'te',
|
109
|
+
'tk', 'ur', 'wae', 'fy', 'zu'
|
110
|
+
], lambda { |n|
|
111
|
+
return (1 == n) ? 0 : 1;
|
112
|
+
});
|
113
|
+
|
114
|
+
|
115
|
+
# Latvian
|
116
|
+
|
117
|
+
add(['lv'], lambda { |n|
|
118
|
+
if n == 0
|
119
|
+
return 0
|
120
|
+
end
|
121
|
+
|
122
|
+
if (n % 10 == 1) && (n % 100 != 11) && is_int(n)
|
123
|
+
return 1
|
124
|
+
end
|
125
|
+
|
126
|
+
return 2
|
127
|
+
});
|
128
|
+
|
129
|
+
|
130
|
+
# Colognian
|
131
|
+
|
132
|
+
add(['ksh'], lambda { |n|
|
133
|
+
return (n == 0) ? 0 : ((n == 1) ? 1 : 2);
|
134
|
+
});
|
135
|
+
|
136
|
+
|
137
|
+
# Cornish, Inari Sami, Inuktitut, Irish, Lule Sami, Northern Sami,
|
138
|
+
# Sami Language, Skolt Sami, Southern Sami
|
139
|
+
|
140
|
+
add(['kw', 'smn', 'iu', 'ga', 'smj', 'se',
|
141
|
+
'smi', 'sms', 'sma'
|
142
|
+
], lambda { |n|
|
143
|
+
return (n == 1) ? 0 : ((n == 2) ? 1 : 2);
|
144
|
+
});
|
145
|
+
|
146
|
+
|
147
|
+
# Belarusian, Bosnian, Croatian, Russian, Serbian, Serbo-Croatian, Ukrainian
|
148
|
+
|
149
|
+
add(['be', 'bs', 'hr', 'ru', 'sr', 'sh', 'uk'], lambda { |n|
|
150
|
+
m10, m100 = n % 10, n % 100
|
151
|
+
|
152
|
+
unless is_int(n)
|
153
|
+
return 3
|
154
|
+
end
|
155
|
+
|
156
|
+
# one → n mod 10 is 1 and n mod 100 is not 11;
|
157
|
+
if 1 == m10 && 11 != m100
|
158
|
+
return 0
|
159
|
+
end
|
160
|
+
|
161
|
+
# few → n mod 10 in 2..4 and n mod 100 not in 12..14;
|
162
|
+
if 2 <= m10 && m10 <= 4 && !(12 <= m100 && m100 <= 14)
|
163
|
+
return 1
|
164
|
+
end
|
165
|
+
|
166
|
+
## many → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
|
167
|
+
## if 0 === m10 || (5 <= m10 && m10 <= 9) || (11 <= m100 && m100 <= 14)
|
168
|
+
## return 2
|
169
|
+
##end
|
170
|
+
|
171
|
+
## other
|
172
|
+
## return 3
|
173
|
+
return 2
|
174
|
+
});
|
175
|
+
|
176
|
+
|
177
|
+
# Polish
|
178
|
+
|
179
|
+
add(['pl'], lambda { |n|
|
180
|
+
m10, m100 = n % 10, n % 100
|
181
|
+
|
182
|
+
unless is_int(n)
|
183
|
+
return 3
|
184
|
+
end
|
185
|
+
|
186
|
+
# one → n is 1;
|
187
|
+
if n == 1
|
188
|
+
return 0
|
189
|
+
end
|
190
|
+
|
191
|
+
# few → n mod 10 in 2..4 and n mod 100 not in 12..14;
|
192
|
+
if 2 <= m10 && m10 <= 4 && !(12 <= m100 && m100 <= 14)
|
193
|
+
return 1
|
194
|
+
end
|
195
|
+
|
196
|
+
# many → n is not 1 and n mod 10 in 0..1 or
|
197
|
+
# n mod 10 in 5..9 or n mod 100 in 12..14
|
198
|
+
# (all other except partials)
|
199
|
+
return 2
|
200
|
+
});
|
201
|
+
|
202
|
+
|
203
|
+
# Lithuanian
|
204
|
+
|
205
|
+
add(['lt'], lambda { |n|
|
206
|
+
m10, m100 = n % 10, n % 100
|
207
|
+
|
208
|
+
unless is_int(n)
|
209
|
+
return 2
|
210
|
+
end
|
211
|
+
|
212
|
+
# one → n mod 10 is 1 and n mod 100 not in 11..19
|
213
|
+
if m10 == 1 && !(11 <= m100 && m100 <= 19)
|
214
|
+
return 0
|
215
|
+
end
|
216
|
+
|
217
|
+
# few → n mod 10 in 2..9 and n mod 100 not in 11..19
|
218
|
+
if 2 <= m10 && m10 <= 9 && !(11 <= m100 && m100 <= 19)
|
219
|
+
return 1
|
220
|
+
end
|
221
|
+
|
222
|
+
# other
|
223
|
+
return 2
|
224
|
+
});
|
225
|
+
|
226
|
+
|
227
|
+
# Tachelhit
|
228
|
+
|
229
|
+
add(['shi'], lambda { |n|
|
230
|
+
return (0 <= n && n <= 1) ? 0 : ((is_int(n) && 2 <= n && n <= 10) ? 1 : 2);
|
231
|
+
});
|
232
|
+
|
233
|
+
|
234
|
+
# Moldavian, Romanian
|
235
|
+
|
236
|
+
add(['mo', 'ro'], lambda { |n|
|
237
|
+
m100 = n % 100;
|
238
|
+
|
239
|
+
unless is_int(n)
|
240
|
+
return 2
|
241
|
+
end
|
242
|
+
|
243
|
+
# one → n is 1
|
244
|
+
if n == 1
|
245
|
+
return 0
|
246
|
+
end
|
247
|
+
|
248
|
+
# few → n is 0 OR n is not 1 AND n mod 100 in 1..19
|
249
|
+
if n == 0 || (1 <= m100 && m100 <= 19)
|
250
|
+
return 1
|
251
|
+
end
|
252
|
+
|
253
|
+
# other
|
254
|
+
return 2
|
255
|
+
});
|
256
|
+
|
257
|
+
|
258
|
+
## Czech, Slovak
|
259
|
+
|
260
|
+
add(['cs', 'sk'], lambda { |n|
|
261
|
+
# one → n is 1
|
262
|
+
if n == 1
|
263
|
+
return 0
|
264
|
+
end
|
265
|
+
|
266
|
+
# few → n in 2..4
|
267
|
+
if n == 2 || n == 3 || n == 4
|
268
|
+
return 1
|
269
|
+
end
|
270
|
+
|
271
|
+
# other
|
272
|
+
return 2
|
273
|
+
});
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
# Slovenian
|
278
|
+
|
279
|
+
add(['sl'], lambda { |n|
|
280
|
+
m100 = n % 100;
|
281
|
+
|
282
|
+
unless is_int(n)
|
283
|
+
return 3
|
284
|
+
end
|
285
|
+
|
286
|
+
# one → n mod 100 is 1
|
287
|
+
if m100 == 1
|
288
|
+
return 0
|
289
|
+
end
|
290
|
+
|
291
|
+
# one → n mod 100 is 2
|
292
|
+
if m100 == 2
|
293
|
+
return 1
|
294
|
+
end
|
295
|
+
|
296
|
+
# one → n mod 100 in 3..4
|
297
|
+
if m100 == 3 || m100 == 4
|
298
|
+
return 2
|
299
|
+
end
|
300
|
+
|
301
|
+
# other
|
302
|
+
return 3
|
303
|
+
});
|
304
|
+
|
305
|
+
|
306
|
+
# Maltese
|
307
|
+
|
308
|
+
add(['mt'], lambda { |n|
|
309
|
+
m100 = n % 100;
|
310
|
+
|
311
|
+
unless is_int(n)
|
312
|
+
return 3
|
313
|
+
end
|
314
|
+
|
315
|
+
# one → n is 1
|
316
|
+
if n == 1
|
317
|
+
return 0
|
318
|
+
end
|
319
|
+
|
320
|
+
# few → n is 0 or n mod 100 in 2..10
|
321
|
+
if n == 0 || (2 <= m100 && m100 <= 10)
|
322
|
+
return 1
|
323
|
+
end
|
324
|
+
|
325
|
+
# many → n mod 100 in 11..19
|
326
|
+
if 11 <= m100 && m100 <= 19
|
327
|
+
return 2
|
328
|
+
end
|
329
|
+
|
330
|
+
# other
|
331
|
+
return 3
|
332
|
+
});
|
333
|
+
|
334
|
+
|
335
|
+
# Arabic
|
336
|
+
|
337
|
+
add(['ar'], lambda { |n|
|
338
|
+
m100 = n % 100;
|
339
|
+
|
340
|
+
unless is_int(n)
|
341
|
+
return 5
|
342
|
+
end
|
343
|
+
|
344
|
+
if n == 0
|
345
|
+
return 0
|
346
|
+
end
|
347
|
+
if n == 1
|
348
|
+
return 1
|
349
|
+
end
|
350
|
+
if n == 2
|
351
|
+
return 2
|
352
|
+
end
|
353
|
+
|
354
|
+
# few → n mod 100 in 3..10
|
355
|
+
if 3 <= m100 && m100 <= 10
|
356
|
+
return 3
|
357
|
+
end
|
358
|
+
|
359
|
+
# many → n mod 100 in 11..99
|
360
|
+
if 11 <= m100 && m100 <= 99
|
361
|
+
return 4
|
362
|
+
end
|
363
|
+
|
364
|
+
# other
|
365
|
+
return 5
|
366
|
+
});
|
367
|
+
|
368
|
+
|
369
|
+
# Breton, Welsh
|
370
|
+
|
371
|
+
add(['br', 'cy'], lambda { |n|
|
372
|
+
if n == 0
|
373
|
+
return 0
|
374
|
+
end
|
375
|
+
if n == 1
|
376
|
+
return 1
|
377
|
+
end
|
378
|
+
if n == 2
|
379
|
+
return 2
|
380
|
+
end
|
381
|
+
if n == 3
|
382
|
+
return 3
|
383
|
+
end
|
384
|
+
if n == 6
|
385
|
+
return 4
|
386
|
+
end
|
387
|
+
|
388
|
+
return 5
|
389
|
+
});
|
390
|
+
|
391
|
+
|
392
|
+
## FRACTIONAL PARTS - SPECIAL CASES
|
393
|
+
|
394
|
+
# French, Fulah, Kabyle
|
395
|
+
|
396
|
+
add(['fr', 'ff', 'kab'], lambda { |n|
|
397
|
+
return (0 <= n && n < 2) ? 0 : 1;
|
398
|
+
});
|
399
|
+
|
400
|
+
|
401
|
+
# Langi
|
402
|
+
|
403
|
+
add(['lag'], lambda { |n|
|
404
|
+
return (n == 0) ? 0 : ((0 < n && n < 2) ? 1 : 2);
|
405
|
+
});
|
406
|
+
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'babelfish'
|
3
|
+
|
4
|
+
describe "Babelfish" do
|
5
|
+
before :all do
|
6
|
+
@b = Babelfish.new({
|
7
|
+
dirs: [ File.expand_path('../locales', File.dirname(__FILE__)) ],
|
8
|
+
default_locale: 'en-US',
|
9
|
+
})
|
10
|
+
end
|
11
|
+
describe "en-US locale" do
|
12
|
+
before :all do
|
13
|
+
@b.locale = 'en-US'
|
14
|
+
end
|
15
|
+
|
16
|
+
describe :t do
|
17
|
+
it "should return placeholder if has no key" do
|
18
|
+
expect( @b.t("wow") ).to eq "[wow]"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should test dummy parameters" do
|
22
|
+
expect(@b.t('test.simple', { dummy: ' test script '})).to eq 'I am '
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should test dummy key" do
|
26
|
+
expect(@b.t('test.dummy_key', { who: 'test script'})).to eq '[test.dummy_key]'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should test simple var" do
|
30
|
+
expect(@b.t('test.simple', { who: 'test script'})).to eq 'I am test script'
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "test.plural.case1" do
|
34
|
+
it "for 2" do
|
35
|
+
expect(@b.t('test.case1.combine', { single: { test: { deep: 'example'} }, count: 10, test:2 })).to eq 'I have 10 nails for example for 2 tests'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "for 10" do
|
39
|
+
expect(@b.t('test.plural.case1', { test: 10 })).to eq 'I have 10 nails'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "for 1" do
|
43
|
+
expect(@b.t('test.plural.case1', { test: 1 })).to eq 'I have 1 nail'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "test.plural.case2" do
|
48
|
+
it "for 1" do
|
49
|
+
expect(@b.t('test.plural.case2', { test: 1 })).to eq 'I have 1 nail simple using'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "test.plural.case3" do
|
54
|
+
it "for 17" do
|
55
|
+
expect(@b.t('test.plural.case3', 17)).to eq 'I have 17 big nails'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe :has_any_value do
|
61
|
+
|
62
|
+
describe "test.plural.case1" do
|
63
|
+
it :exists do
|
64
|
+
expect(@b.has_any_value('test.case1.combine')).to eq true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "test.plural.case2" do
|
69
|
+
it :exists do
|
70
|
+
expect(@b.has_any_value('test.plural.case2')).to eq true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "test.plural.case3" do
|
75
|
+
it :exists do
|
76
|
+
expect(@b.has_any_value('test.plural.case3')).to eq true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "ru-RU locale" do
|
83
|
+
before :all do
|
84
|
+
@b.locale = 'ru-RU'
|
85
|
+
end
|
86
|
+
|
87
|
+
describe :t do
|
88
|
+
it "should return placeholder if has no key" do
|
89
|
+
expect( @b.t("wow") ).to eq "[wow]"
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "test.simple.plural.nails2" do
|
93
|
+
it "for 1" do
|
94
|
+
expect(@b.t('test.simple.plural.nails2', 1)).to eq 'У меня гвоздь упрощенная форма записи'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "test.simple.plural.nails3" do
|
99
|
+
it "for 17" do
|
100
|
+
expect(@b.t('test.simple.plural.nails3', { test: 17 })).to eq '17 у меня гвоздей'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|