001/*
002 * Copyright 2017 Product Mog LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.lokalized;
018
019import com.lokalized.Maps.MapEntry;
020
021import javax.annotation.Nonnull;
022import java.math.BigDecimal;
023import java.math.BigInteger;
024import java.text.Collator;
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.HashMap;
028import java.util.Locale;
029import java.util.Map;
030import java.util.Optional;
031import java.util.SortedMap;
032import java.util.SortedSet;
033import java.util.TreeSet;
034import java.util.function.Function;
035import java.util.stream.Collectors;
036
037import static com.lokalized.NumberUtils.equal;
038import static com.lokalized.NumberUtils.inRange;
039import static com.lokalized.NumberUtils.inSet;
040import static com.lokalized.NumberUtils.notEqual;
041import static com.lokalized.NumberUtils.notInSet;
042import static java.lang.String.format;
043import static java.util.Objects.requireNonNull;
044
045/**
046 * Language plural ordinality forms.
047 * <p>
048 * For example, English has four: {@code 1st, 2nd, 3rd, 4th}, while Swedish has two: {@code 1:a, 3:e}.
049 * <p>
050 * See the <a href="http://cldr.unicode.org/index/cldr-spec/plural-rules">Unicode Common Locale Data Repository</a>
051 * and its <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html">Language Plural Rules</a> for details.
052 * <p>
053 * Per the CLDR:
054 * <blockquote>
055 * These categories are only mnemonics -- the names don't necessarily imply the exact contents of the category.
056 * For example, for both English and French the number 1 has the category one (singular).
057 * <p>
058 * In English, every other number has a plural form, and is given the category other.
059 * French is similar, except that the number 0 also has the category one and not other or zero, because the form of
060 * units qualified by 0 is also singular.
061 * <p>
062 * This is worth emphasizing: A common mistake is to think that "one" is only for only the number 1.
063 * Instead, "one" is a category for any number that behaves like 1. So in some languages, for example,
064 * one → numbers that end in "1" (like 1, 21, 151) but that don't end in 11 (like "11, 111, 10311).
065 * </blockquote>
066 *
067 * @author <a href="https://revetkn.com">Mark Allen</a>
068 */
069public enum Ordinality implements LanguageForm {
070  /**
071   * Normally the form used with 0, if it is limited to numbers whose integer values end with 0.
072   * <p>
073   * For example: the Welsh {@code 0fed ci} means "{@code 0th dog}" in English.
074   */
075  ZERO,
076  /**
077   * The form used with 1.
078   * <p>
079   * For example: the Welsh {@code ci 1af} means {@code 1st dog} in English.
080   */
081  ONE,
082  /**
083   * Normally the form used with 2, if it is limited to numbers whose integer values end with 2.
084   * <p>
085   * For example: the Welsh {@code 2il gi} means {@code 2nd dog} in English.
086   */
087  TWO,
088  /**
089   * The form that falls between {@code TWO} and {@code MANY}.
090   * <p>
091   * For example: the Welsh {@code 3ydd ci} means {@code 3rd dog} in English.
092   */
093  FEW,
094  /**
095   * The form that falls between {@code FEW} and {@code OTHER}.
096   * <p>
097   * For example: the Welsh {@code 5ed ci} means {@code 5th dog} in English.
098   */
099  MANY,
100  /**
101   * General "catchall" form which comprises any cases not handled by the other forms.
102   * <p>
103   * For example: the Welsh {@code ci rhif 10} means {@code 10th dog} in English.
104   */
105  OTHER;
106
107  @Nonnull
108  private static final BigInteger BIG_INTEGER_0;
109  @Nonnull
110  private static final BigInteger BIG_INTEGER_1;
111  @Nonnull
112  private static final BigInteger BIG_INTEGER_2;
113  @Nonnull
114  private static final BigInteger BIG_INTEGER_3;
115  @Nonnull
116  private static final BigInteger BIG_INTEGER_4;
117  @Nonnull
118  private static final BigInteger BIG_INTEGER_5;
119  @Nonnull
120  private static final BigInteger BIG_INTEGER_6;
121  @Nonnull
122  private static final BigInteger BIG_INTEGER_7;
123  @Nonnull
124  private static final BigInteger BIG_INTEGER_8;
125  @Nonnull
126  private static final BigInteger BIG_INTEGER_10;
127  @Nonnull
128  private static final BigInteger BIG_INTEGER_11;
129  @Nonnull
130  private static final BigInteger BIG_INTEGER_12;
131  @Nonnull
132  private static final BigInteger BIG_INTEGER_17;
133  @Nonnull
134  private static final BigInteger BIG_INTEGER_18;
135  @Nonnull
136  private static final BigInteger BIG_INTEGER_20;
137  @Nonnull
138  private static final BigInteger BIG_INTEGER_40;
139  @Nonnull
140  private static final BigInteger BIG_INTEGER_50;
141  @Nonnull
142  private static final BigInteger BIG_INTEGER_60;
143  @Nonnull
144  private static final BigInteger BIG_INTEGER_70;
145  @Nonnull
146  private static final BigInteger BIG_INTEGER_80;
147  @Nonnull
148  private static final BigInteger BIG_INTEGER_90;
149  @Nonnull
150  private static final BigInteger BIG_INTEGER_100;
151  @Nonnull
152  private static final BigInteger BIG_INTEGER_200;
153  @Nonnull
154  private static final BigInteger BIG_INTEGER_300;
155  @Nonnull
156  private static final BigInteger BIG_INTEGER_400;
157  @Nonnull
158  private static final BigInteger BIG_INTEGER_500;
159  @Nonnull
160  private static final BigInteger BIG_INTEGER_600;
161  @Nonnull
162  private static final BigInteger BIG_INTEGER_700;
163  @Nonnull
164  private static final BigInteger BIG_INTEGER_800;
165  @Nonnull
166  private static final BigInteger BIG_INTEGER_900;
167  @Nonnull
168  private static final BigInteger BIG_INTEGER_1_000;
169
170  @Nonnull
171  private static final BigDecimal BIG_DECIMAL_0;
172  @Nonnull
173  private static final BigDecimal BIG_DECIMAL_1;
174  @Nonnull
175  private static final BigDecimal BIG_DECIMAL_2;
176  @Nonnull
177  private static final BigDecimal BIG_DECIMAL_3;
178  @Nonnull
179  private static final BigDecimal BIG_DECIMAL_4;
180  @Nonnull
181  private static final BigDecimal BIG_DECIMAL_5;
182  @Nonnull
183  private static final BigDecimal BIG_DECIMAL_6;
184  @Nonnull
185  private static final BigDecimal BIG_DECIMAL_7;
186  @Nonnull
187  private static final BigDecimal BIG_DECIMAL_8;
188  @Nonnull
189  private static final BigDecimal BIG_DECIMAL_9;
190  @Nonnull
191  private static final BigDecimal BIG_DECIMAL_10;
192  @Nonnull
193  private static final BigDecimal BIG_DECIMAL_11;
194  @Nonnull
195  private static final BigDecimal BIG_DECIMAL_12;
196  @Nonnull
197  private static final BigDecimal BIG_DECIMAL_13;
198  @Nonnull
199  private static final BigDecimal BIG_DECIMAL_14;
200  @Nonnull
201  private static final BigDecimal BIG_DECIMAL_80;
202  @Nonnull
203  private static final BigDecimal BIG_DECIMAL_100;
204  @Nonnull
205  private static final BigDecimal BIG_DECIMAL_800;
206
207  @Nonnull
208  static final Map<String, Ordinality> ORDINALITIES_BY_NAME;
209
210  static {
211    BIG_INTEGER_0 = BigInteger.ZERO;
212    BIG_INTEGER_1 = BigInteger.ONE;
213    BIG_INTEGER_2 = BigInteger.valueOf(2);
214    BIG_INTEGER_3 = BigInteger.valueOf(3);
215    BIG_INTEGER_4 = BigInteger.valueOf(4);
216    BIG_INTEGER_5 = BigInteger.valueOf(5);
217    BIG_INTEGER_6 = BigInteger.valueOf(6);
218    BIG_INTEGER_7 = BigInteger.valueOf(7);
219    BIG_INTEGER_8 = BigInteger.valueOf(8);
220    BIG_INTEGER_10 = BigInteger.TEN;
221    BIG_INTEGER_11 = BigInteger.valueOf(11);
222    BIG_INTEGER_12 = BigInteger.valueOf(12);
223    BIG_INTEGER_17 = BigInteger.valueOf(17);
224    BIG_INTEGER_18 = BigInteger.valueOf(18);
225    BIG_INTEGER_20 = BigInteger.valueOf(20);
226    BIG_INTEGER_40 = BigInteger.valueOf(40);
227    BIG_INTEGER_50 = BigInteger.valueOf(50);
228    BIG_INTEGER_60 = BigInteger.valueOf(60);
229    BIG_INTEGER_70 = BigInteger.valueOf(70);
230    BIG_INTEGER_80 = BigInteger.valueOf(80);
231    BIG_INTEGER_90 = BigInteger.valueOf(90);
232    BIG_INTEGER_100 = BigInteger.valueOf(100);
233    BIG_INTEGER_200 = BigInteger.valueOf(200);
234    BIG_INTEGER_300 = BigInteger.valueOf(300);
235    BIG_INTEGER_400 = BigInteger.valueOf(400);
236    BIG_INTEGER_500 = BigInteger.valueOf(500);
237    BIG_INTEGER_600 = BigInteger.valueOf(600);
238    BIG_INTEGER_700 = BigInteger.valueOf(700);
239    BIG_INTEGER_800 = BigInteger.valueOf(800);
240    BIG_INTEGER_900 = BigInteger.valueOf(900);
241    BIG_INTEGER_1_000 = BigInteger.valueOf(1_000);
242
243    BIG_DECIMAL_0 = BigDecimal.ZERO;
244    BIG_DECIMAL_1 = BigDecimal.ONE;
245    BIG_DECIMAL_2 = BigDecimal.valueOf(2);
246    BIG_DECIMAL_3 = BigDecimal.valueOf(3);
247    BIG_DECIMAL_4 = BigDecimal.valueOf(4);
248    BIG_DECIMAL_5 = BigDecimal.valueOf(5);
249    BIG_DECIMAL_6 = BigDecimal.valueOf(6);
250    BIG_DECIMAL_7 = BigDecimal.valueOf(7);
251    BIG_DECIMAL_8 = BigDecimal.valueOf(8);
252    BIG_DECIMAL_9 = BigDecimal.valueOf(9);
253    BIG_DECIMAL_10 = BigDecimal.TEN;
254    BIG_DECIMAL_11 = BigDecimal.valueOf(11);
255    BIG_DECIMAL_12 = BigDecimal.valueOf(12);
256    BIG_DECIMAL_13 = BigDecimal.valueOf(13);
257    BIG_DECIMAL_14 = BigDecimal.valueOf(14);
258    BIG_DECIMAL_80 = BigDecimal.valueOf(80);
259    BIG_DECIMAL_100 = BigDecimal.valueOf(100);
260    BIG_DECIMAL_800 = BigDecimal.valueOf(800);
261
262    ORDINALITIES_BY_NAME = Collections.unmodifiableMap(Arrays.stream(
263        Ordinality.values()).collect(Collectors.toMap(ordinality -> ordinality.name(), ordinality -> ordinality)));
264  }
265
266  /**
267   * Gets an appropriate plural ordinality for the given number and locale.
268   * <p>
269   * See <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html">http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html</a>
270   * for a cheat sheet.
271   *
272   * @param number the number that drives pluralization, not null
273   * @param locale the locale that drives pluralization, not null
274   * @return an appropriate plural ordinality, not null
275   * @throws UnsupportedLocaleException if the locale is not supported
276   */
277  @Nonnull
278  public static Ordinality forNumber(@Nonnull Number number, @Nonnull Locale locale) {
279    requireNonNull(number);
280    requireNonNull(locale);
281
282    BigDecimal numberAsBigDecimal = NumberUtils.toBigDecimal(number);
283
284    Optional<OrdinalityFamily> ordinalityFamily = OrdinalityFamily.ordinalityFamilyForLocale(locale);
285
286    // TODO: throwing an exception might not be the best solution here...need to think about it
287    if (!ordinalityFamily.isPresent())
288      throw new UnsupportedLocaleException(locale);
289
290    return ordinalityFamily.get().getOrdinalityFunction().apply(numberAsBigDecimal);
291  }
292
293  /**
294   * Gets the set of ordinalities supported for the given locale.
295   * <p>
296   * The empty set will be returned if the locale is not supported.
297   * <p>
298   * The set's values are sorted by the natural ordering of the {@link Ordinality} enumeration.
299   *
300   * @param locale the locale to use for lookup, not null
301   * @return the ordinalities supported by the given locale, not null
302   */
303  @Nonnull
304  public static SortedSet<Ordinality> supportedOrdinalitiesForLocale(@Nonnull Locale locale) {
305    requireNonNull(locale);
306
307    Optional<OrdinalityFamily> ordinalityFamily = OrdinalityFamily.ordinalityFamilyForLocale(locale);
308    return ordinalityFamily.isPresent() ? ordinalityFamily.get().getSupportedOrdinalities() : Collections.emptySortedSet();
309  }
310
311  /**
312   * Gets a mapping of ordinalities to example integer values for the given locale.
313   * <p>
314   * The empty map will be returned if the locale is not supported or if no example values are available.
315   * <p>
316   * The map's keys are sorted by the natural ordering of the {@link Ordinality} enumeration.
317   *
318   * @param locale the locale to use for lookup, not null
319   * @return a mapping of ordinalities to example integer values, not null
320   */
321  @Nonnull
322  public static SortedMap<Ordinality, Range<Integer>> exampleIntegerValuesForLocale(@Nonnull Locale locale) {
323    requireNonNull(locale);
324
325    Optional<OrdinalityFamily> ordinalityFamily = OrdinalityFamily.ordinalityFamilyForLocale(locale);
326    return ordinalityFamily.isPresent() ? ordinalityFamily.get().getExampleIntegerValuesByOrdinality() : Collections.emptySortedMap();
327  }
328
329  /**
330   * Gets the ISO 639 language codes for which ordinality operations are supported.
331   * <p>
332   * The set's values are ISO 639 codes and therefore sorted using English collation.
333   *
334   * @return the ISO 639 language codes for which ordinality operations are supported, not null
335   */
336  @Nonnull
337  public static SortedSet<String> getSupportedLanguageCodes() {
338    return OrdinalityFamily.getSupportedLanguageCodes();
339  }
340
341  /**
342   * Gets the mapping of ordinality names to values.
343   *
344   * @return the mapping of ordinality names to values, not null
345   */
346  @Nonnull
347  static Map<String, Ordinality> getOrdinalitiesByName() {
348    return ORDINALITIES_BY_NAME;
349  }
350
351  /**
352   * Plural ordinality forms grouped by language family.
353   * <p>
354   * Each family has a distinct ordinality calculation rule.
355   * <p>
356   * See <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html">http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html</a>
357   * for a cheat sheet.
358   */
359  enum OrdinalityFamily {
360    /**
361     * Languages Include:
362     * <p>
363     * <ul>
364     * <li>Afrikaans (af)</li>
365     * <li>Akan (ak) (no CLDR data available)</li>
366     * <li>Amharic (am)</li>
367     * <li>Arabic (ar)</li>
368     * <li>Najdi Arabic (ars) (no CLDR data available)</li>
369     * <li>Asu (asa) (no CLDR data available)</li>
370     * <li>Asturian (ast) (no CLDR data available)</li>
371     * <li>Bemba (bem) (no CLDR data available)</li>
372     * <li>Bena (bez) (no CLDR data available)</li>
373     * <li>Bulgarian (bg)</li>
374     * <li>Bihari (bh) (no CLDR data available)</li>
375     * <li>Bambara (bm) (no CLDR data available)</li>
376     * <li>Tibetan (bo) (no CLDR data available)</li>
377     * <li>Breton (br) (no CLDR data available)</li>
378     * <li>Bodo (brx) (no CLDR data available)</li>
379     * <li>Bosnian (bs)</li>
380     * <li>Chechen (ce)</li>
381     * <li>Chiga (cgg) (no CLDR data available)</li>
382     * <li>Cherokee (chr) (no CLDR data available)</li>
383     * <li>Central Kurdish (ckb) (no CLDR data available)</li>
384     * <li>Czech (cs)</li>
385     * <li>Danish (da)</li>
386     * <li>German (de)</li>
387     * <li>Lower Sorbian (dsb)</li>
388     * <li>Divehi (dv) (no CLDR data available)</li>
389     * <li>Dzongkha (dz) (no CLDR data available)</li>
390     * <li>Ewe (ee) (no CLDR data available)</li>
391     * <li>Greek (el)</li>
392     * <li>Esperanto (eo) (no CLDR data available)</li>
393     * <li>Spanish (es)</li>
394     * <li>Estonian (et)</li>
395     * <li>Basque (eu)</li>
396     * <li>Persian (fa)</li>
397     * <li>Fulah (ff) (no CLDR data available)</li>
398     * <li>Finnish (fi)</li>
399     * <li>Faroese (fo) (no CLDR data available)</li>
400     * <li>Friulian (fur) (no CLDR data available)</li>
401     * <li>Western Frisian (fy)</li>
402     * <li>Scottish Gaelic (gd) (no CLDR data available)</li>
403     * <li>Galician (gl)</li>
404     * <li>Swiss German (gsw)</li>
405     * <li>Gun (guw) (no CLDR data available)</li>
406     * <li>Manx (gv) (no CLDR data available)</li>
407     * <li>Hausa (ha) (no CLDR data available)</li>
408     * <li>Hawaiian (haw) (no CLDR data available)</li>
409     * <li>Hebrew (he)</li>
410     * <li>Croatian (hr)</li>
411     * <li>Upper Sorbian (hsb)</li>
412     * <li>Indonesian (id)</li>
413     * <li>Igbo (ig) (no CLDR data available)</li>
414     * <li>Sichuan Yi (ii) (no CLDR data available)</li>
415     * <li>Icelandic (is)</li>
416     * <li>Inuktitut (iu) (no CLDR data available)</li>
417     * <li>Japanese (ja)</li>
418     * <li>Lojban (jbo) (no CLDR data available)</li>
419     * <li>Ngomba (jgo) (no CLDR data available)</li>
420     * <li>Machame (jmc) (no CLDR data available)</li>
421     * <li>Javanese (jv) (no CLDR data available)</li>
422     * <li>Javanese (jw) (no CLDR data available)</li>
423     * <li>Kabyle (kab) (no CLDR data available)</li>
424     * <li>Jju (kaj) (no CLDR data available)</li>
425     * <li>Tyap (kcg) (no CLDR data available)</li>
426     * <li>Makonde (kde) (no CLDR data available)</li>
427     * <li>Kabuverdianu (kea) (no CLDR data available)</li>
428     * <li>Kako (kkj) (no CLDR data available)</li>
429     * <li>Greenlandic (kl) (no CLDR data available)</li>
430     * <li>Khmer (km)</li>
431     * <li>Kannada (kn)</li>
432     * <li>Korean (ko)</li>
433     * <li>Kashmiri (ks) (no CLDR data available)</li>
434     * <li>Shambala (ksb) (no CLDR data available)</li>
435     * <li>Colognian (ksh) (no CLDR data available)</li>
436     * <li>Kurdish (ku) (no CLDR data available)</li>
437     * <li>Cornish (kw) (no CLDR data available)</li>
438     * <li>Kirghiz (ky)</li>
439     * <li>Langi (lag) (no CLDR data available)</li>
440     * <li>Luxembourgish (lb) (no CLDR data available)</li>
441     * <li>Ganda (lg) (no CLDR data available)</li>
442     * <li>Lakota (lkt) (no CLDR data available)</li>
443     * <li>Lingala (ln) (no CLDR data available)</li>
444     * <li>Lithuanian (lt)</li>
445     * <li>Latvian (lv)</li>
446     * <li>Masai (mas) (no CLDR data available)</li>
447     * <li>Malagasy (mg) (no CLDR data available)</li>
448     * <li>Metaʼ (mgo) (no CLDR data available)</li>
449     * <li>Malayalam (ml)</li>
450     * <li>Mongolian (mn)</li>
451     * <li>Maltese (mt) (no CLDR data available)</li>
452     * <li>Burmese (my)</li>
453     * <li>Nahuatl (nah) (no CLDR data available)</li>
454     * <li>Nama (naq) (no CLDR data available)</li>
455     * <li>Norwegian Bokmål (nb)</li>
456     * <li>North Ndebele (nd) (no CLDR data available)</li>
457     * <li>Dutch (nl)</li>
458     * <li>Norwegian Nynorsk (nn) (no CLDR data available)</li>
459     * <li>Ngiemboon (nnh) (no CLDR data available)</li>
460     * <li>Norwegian (no) (no CLDR data available)</li>
461     * <li>N’Ko (nqo) (no CLDR data available)</li>
462     * <li>South Ndebele (nr) (no CLDR data available)</li>
463     * <li>Northern Sotho (nso) (no CLDR data available)</li>
464     * <li>Nyanja (ny) (no CLDR data available)</li>
465     * <li>Nyankole (nyn) (no CLDR data available)</li>
466     * <li>Oromo (om) (no CLDR data available)</li>
467     * <li>Odia (or) (no CLDR data available)</li>
468     * <li>Ossetian (os) (no CLDR data available)</li>
469     * <li>Punjabi (pa)</li>
470     * <li>Papiamento (pap) (no CLDR data available)</li>
471     * <li>Polish (pl)</li>
472     * <li>Prussian (prg)</li>
473     * <li>Pushto (ps) (no CLDR data available)</li>
474     * <li>Portuguese (pt)</li>
475     * <li>Romansh (rm) (no CLDR data available)</li>
476     * <li>Rombo (rof) (no CLDR data available)</li>
477     * <li>Root (root)</li>
478     * <li>Russian (ru)</li>
479     * <li>Rwa (rwk) (no CLDR data available)</li>
480     * <li>Sakha (sah) (no CLDR data available)</li>
481     * <li>Samburu (saq) (no CLDR data available)</li>
482     * <li>Southern Kurdish (sdh) (no CLDR data available)</li>
483     * <li>Northern Sami (se) (no CLDR data available)</li>
484     * <li>Sena (seh) (no CLDR data available)</li>
485     * <li>Koyraboro Senni (ses) (no CLDR data available)</li>
486     * <li>Sango (sg) (no CLDR data available)</li>
487     * <li>Serbo-Croatian (sh)</li>
488     * <li>Tachelhit (shi) (no CLDR data available)</li>
489     * <li>Sinhalese (si)</li>
490     * <li>Slovak (sk)</li>
491     * <li>Slovenian (sl)</li>
492     * <li>Southern Sami (sma) (no CLDR data available)</li>
493     * <li>Sami (smi) (no CLDR data available)</li>
494     * <li>Lule Sami (smj) (no CLDR data available)</li>
495     * <li>Inari Sami (smn) (no CLDR data available)</li>
496     * <li>Skolt Sami (sms) (no CLDR data available)</li>
497     * <li>Shona (sn) (no CLDR data available)</li>
498     * <li>Somali (so) (no CLDR data available)</li>
499     * <li>Serbian (sr)</li>
500     * <li>Swati (ss) (no CLDR data available)</li>
501     * <li>Saho (ssy) (no CLDR data available)</li>
502     * <li>Southern Sotho (st) (no CLDR data available)</li>
503     * <li>Swahili (sw)</li>
504     * <li>Syriac (syr) (no CLDR data available)</li>
505     * <li>Tamil (ta)</li>
506     * <li>Telugu (te)</li>
507     * <li>Teso (teo) (no CLDR data available)</li>
508     * <li>Thai (th)</li>
509     * <li>Tigrinya (ti) (no CLDR data available)</li>
510     * <li>Tigre (tig) (no CLDR data available)</li>
511     * <li>Turkmen (tk) (no CLDR data available)</li>
512     * <li>Tswana (tn) (no CLDR data available)</li>
513     * <li>Tongan (to) (no CLDR data available)</li>
514     * <li>Turkish (tr)</li>
515     * <li>Tsonga (ts) (no CLDR data available)</li>
516     * <li>Central Atlas Tamazight (tzm) (no CLDR data available)</li>
517     * <li>Uighur (ug) (no CLDR data available)</li>
518     * <li>Urdu (ur)</li>
519     * <li>Uzbek (uz)</li>
520     * <li>Venda (ve) (no CLDR data available)</li>
521     * <li>Volapük (vo) (no CLDR data available)</li>
522     * <li>Vunjo (vun) (no CLDR data available)</li>
523     * <li>Walloon (wa) (no CLDR data available)</li>
524     * <li>Walser (wae) (no CLDR data available)</li>
525     * <li>Wolof (wo) (no CLDR data available)</li>
526     * <li>Xhosa (xh) (no CLDR data available)</li>
527     * <li>Soga (xog) (no CLDR data available)</li>
528     * <li>Yiddish (yi) (no CLDR data available)</li>
529     * <li>Yoruba (yo) (no CLDR data available)</li>
530     * <li>Cantonese (yue)</li>
531     * <li>Mandarin Chinese (zh)</li>
532     * <li>Zulu (zu)</li>
533     * </ul>
534     */
535    FAMILY_1(
536        (n) -> {
537          // No ordinality rules for this family
538          return OTHER;
539        },
540        Sets.sortedSet(
541            OTHER
542        ),
543        Maps.sortedMap(
544            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 100, 1000, 10000, 100000, 1000000))
545        )
546    ),
547
548    /**
549     * Languages Include:
550     * <p>
551     * <ul>
552     * <li>Filipino (fil)</li>
553     * <li>French (fr)</li>
554     * <li>Irish (ga)</li>
555     * <li>Armenian (hy)</li>
556     * <li>Lao (lo)</li>
557     * <li>Moldovan (mo)</li>
558     * <li>Malay (ms)</li>
559     * <li>Romanian (ro)</li>
560     * <li>Tagalog (tl)</li>
561     * <li>Vietnamese (vi)</li>
562     * </ul>
563     */
564    FAMILY_2(
565        (n) -> {
566          // n = 1
567          if (equal(n, BIG_DECIMAL_1))
568            return ONE;
569
570          return OTHER;
571        },
572        Sets.sortedSet(
573            ONE,
574            OTHER
575        ),
576        Maps.sortedMap(
577            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
578            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 100, 1000, 10000, 100000, 1000000))
579        )
580    ),
581
582    /**
583     * Languages Include:
584     * <p>
585     * <ul>
586     * <li>Assamese (as)</li>
587     * <li>Bangla (bn)</li>
588     * </ul>
589     */
590    FAMILY_3(
591        (n) -> {
592          // n = 1,5,7,8,9,10
593          if (inSet(n, BIG_DECIMAL_1, BIG_DECIMAL_5, BIG_DECIMAL_7, BIG_DECIMAL_8, BIG_DECIMAL_9, BIG_DECIMAL_10))
594            return ONE;
595          // n = 2,3
596          if (inSet(n, BIG_DECIMAL_2, BIG_DECIMAL_3))
597            return TWO;
598          // n = 4
599          if (equal(n, BIG_DECIMAL_4))
600            return FEW;
601          // n = 6
602          if (equal(n, BIG_DECIMAL_6))
603            return MANY;
604
605          return OTHER;
606        },
607        Sets.sortedSet(
608            ONE,
609            TWO,
610            FEW,
611            MANY,
612            OTHER
613        ),
614        Maps.sortedMap(
615            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1, 5, 7, 8, 9, 10)),
616            MapEntry.of(Ordinality.TWO, Range.ofFiniteValues(2, 3)),
617            MapEntry.of(Ordinality.FEW, Range.ofFiniteValues(4)),
618            MapEntry.of(Ordinality.MANY, Range.ofFiniteValues(6)),
619            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 100, 1000, 10000, 100000, 1000000))
620        )
621    ),
622
623    /**
624     * Languages Include:
625     * <p>
626     * <ul>
627     * <li>Gujarati (gu)</li>
628     * <li>Hindi (hi)</li>
629     * </ul>
630     */
631    FAMILY_4(
632        (n) -> {
633          // n = 1
634          if (equal(n, BIG_DECIMAL_1))
635            return ONE;
636          // n = 2,3
637          if (inSet(n, BIG_DECIMAL_2, BIG_DECIMAL_3))
638            return TWO;
639          // n = 4
640          if (equal(n, BIG_DECIMAL_4))
641            return FEW;
642          // n = 6
643          if (equal(n, BIG_DECIMAL_6))
644            return MANY;
645
646          return OTHER;
647        },
648        Sets.sortedSet(
649            ONE,
650            TWO,
651            FEW,
652            MANY,
653            OTHER
654        ),
655        Maps.sortedMap(
656            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
657            MapEntry.of(Ordinality.TWO, Range.ofFiniteValues(2, 3)),
658            MapEntry.of(Ordinality.FEW, Range.ofFiniteValues(4)),
659            MapEntry.of(Ordinality.MANY, Range.ofFiniteValues(6)),
660            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 100, 1000, 10000, 100000, 1000000))
661        )
662    ),
663
664    /**
665     * Languages Include:
666     * <p>
667     * <ul>
668     * <li>Azeri (az)</li>
669     * </ul>
670     */
671    FAMILY_5(
672        (n) -> {
673          BigInteger i = NumberUtils.integerComponent(n);
674
675          // i % 10 = 1,2,5,7,8 or i % 100 = 20,50,70,80
676          if (inSet(i.mod(BIG_INTEGER_10), BIG_INTEGER_1, BIG_INTEGER_2, BIG_INTEGER_5, BIG_INTEGER_7, BIG_INTEGER_8)
677              || inSet(i.mod(BIG_INTEGER_100), BIG_INTEGER_20, BIG_INTEGER_50, BIG_INTEGER_70, BIG_INTEGER_80))
678            return ONE;
679          // i % 10 = 3,4 or i % 1000 = 100,200,300,400,500,600,700,800,900
680          if (inSet(i.mod(BIG_INTEGER_10), BIG_INTEGER_3, BIG_INTEGER_4)
681              || inSet(i.mod(BIG_INTEGER_1_000), BIG_INTEGER_100, BIG_INTEGER_200, BIG_INTEGER_300, BIG_INTEGER_400, BIG_INTEGER_500, BIG_INTEGER_600, BIG_INTEGER_700, BIG_INTEGER_800, BIG_INTEGER_900))
682            return FEW;
683          // i = 0 or i % 10 = 6 or i % 100 = 40,60,90
684          if (equal(i, BIG_INTEGER_0)
685              || equal(i.mod(BIG_INTEGER_10), BIG_INTEGER_6)
686              || inSet(i.mod(BIG_INTEGER_100), BIG_INTEGER_40, BIG_INTEGER_60, BIG_INTEGER_90))
687            return MANY;
688
689          return OTHER;
690        },
691        Sets.sortedSet(
692            ONE,
693            FEW,
694            MANY,
695            OTHER
696        ),
697        Maps.sortedMap(
698            MapEntry.of(Ordinality.ONE, Range.ofInfiniteValues(1, 2, 5, 7, 8, 11, 12, 15, 17, 18, 20, 21, 22, 25, 101, 1001)),
699            MapEntry.of(Ordinality.FEW, Range.ofInfiniteValues(3, 4, 13, 14, 23, 24, 33, 34, 43, 44, 53, 54, 63, 64, 73, 74, 100, 1003)),
700            MapEntry.of(Ordinality.MANY, Range.ofInfiniteValues(0, 6, 16, 26, 36, 40, 46, 56, 106, 1006)),
701            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(9, 10, 19, 29, 30, 39, 49, 59, 69, 79, 109, 1000, 10000, 100000, 1000000))
702        )
703    ),
704
705    /**
706     * Languages Include:
707     * <p>
708     * <ul>
709     * <li>Belarusian (be)</li>
710     * </ul>
711     */
712    FAMILY_6(
713        (n) -> {
714          // n % 10 = 2,3 and n % 100 != 12,13
715          if (inSet(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_2, BIG_DECIMAL_3)
716              && notInSet(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_12, BIG_DECIMAL_13))
717            return FEW;
718
719          return OTHER;
720        },
721        Sets.sortedSet(
722            FEW,
723            OTHER
724        ),
725        Maps.sortedMap(
726            MapEntry.of(Ordinality.FEW, Range.ofInfiniteValues(2, 3, 22, 23, 32, 33, 42, 43, 52, 53, 62, 63, 72, 73, 82, 83, 102, 1002)),
727            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 100, 1000, 10000, 100000, 1000000))
728        )
729    ),
730
731    /**
732     * Languages Include:
733     * <p>
734     * <ul>
735     * <li>Catalan (ca)</li>
736     * </ul>
737     */
738    FAMILY_7(
739        (n) -> {
740          // n = 1,3
741          if (inSet(n, BIG_DECIMAL_1, BIG_DECIMAL_3))
742            return ONE;
743          // n = 2
744          if (equal(n, BIG_DECIMAL_2))
745            return TWO;
746          // n = 4
747          if (equal(n, BIG_DECIMAL_4))
748            return FEW;
749
750          return OTHER;
751        },
752        Sets.sortedSet(
753            ONE,
754            TWO,
755            FEW,
756            OTHER
757        ),
758        Maps.sortedMap(
759            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1, 3)),
760            MapEntry.of(Ordinality.TWO, Range.ofFiniteValues(2)),
761            MapEntry.of(Ordinality.FEW, Range.ofFiniteValues(4)),
762            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 1000, 10000, 100000, 1000000))
763        )
764    ),
765
766    /**
767     * Languages Include:
768     * <p>
769     * <ul>
770     * <li>Welsh (cy)</li>
771     * </ul>
772     */
773    FAMILY_8(
774        (n) -> {
775          // n = 0,7,8,9
776          if (inSet(n, BIG_DECIMAL_0, BIG_DECIMAL_7, BIG_DECIMAL_8, BIG_DECIMAL_9))
777            return ZERO;
778          // n = 1
779          if (equal(n, BIG_DECIMAL_1))
780            return ONE;
781          // n = 2
782          if (equal(n, BIG_DECIMAL_2))
783            return TWO;
784          // n = 3,4
785          if (inSet(n, BIG_DECIMAL_3, BIG_DECIMAL_4))
786            return FEW;
787          // n = 5,6
788          if (inSet(n, BIG_DECIMAL_5, BIG_DECIMAL_6))
789            return MANY;
790
791          return OTHER;
792        },
793        Sets.sortedSet(
794            ZERO,
795            ONE,
796            TWO,
797            FEW,
798            MANY,
799            OTHER
800        ),
801        Maps.sortedMap(
802            MapEntry.of(Ordinality.ZERO, Range.ofFiniteValues(0, 7, 8, 9)),
803            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
804            MapEntry.of(Ordinality.TWO, Range.ofFiniteValues(2)),
805            MapEntry.of(Ordinality.FEW, Range.ofFiniteValues(3, 4)),
806            MapEntry.of(Ordinality.MANY, Range.ofFiniteValues(5, 6)),
807            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 100, 1000, 10000, 100000, 1000000))
808        )
809    ),
810
811    /**
812     * Languages Include:
813     * <p>
814     * <ul>
815     * <li>English (en)</li>
816     * </ul>
817     */
818    FAMILY_9(
819        (n) -> {
820          // n % 10 = 1 and n % 100 != 11
821          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_1) && notEqual(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_11))
822            return ONE;
823          // n % 10 = 2 and n % 100 != 12
824          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_2) && notEqual(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_12))
825            return TWO;
826          // n % 10 = 3 and n % 100 != 13
827          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_3) && notEqual(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_13))
828            return FEW;
829
830          return OTHER;
831        },
832        Sets.sortedSet(
833            ONE,
834            TWO,
835            FEW,
836            OTHER
837        ),
838        Maps.sortedMap(
839            MapEntry.of(Ordinality.ONE, Range.ofInfiniteValues(1, 21, 31, 41, 51, 61, 71, 81, 101, 1001)),
840            MapEntry.of(Ordinality.TWO, Range.ofInfiniteValues(2, 22, 32, 42, 52, 62, 72, 82, 102, 1002)),
841            MapEntry.of(Ordinality.FEW, Range.ofInfiniteValues(3, 23, 33, 43, 53, 63, 73, 83, 103, 1003)),
842            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 100, 1000, 10000, 100000, 1000000))
843        )
844    ),
845
846    /**
847     * Languages Include:
848     * <p>
849     * <ul>
850     * <li>Hungarian (hu)</li>
851     * </ul>
852     */
853    FAMILY_10(
854        (n) -> {
855          // n = 1,5
856          if (inSet(n, BIG_DECIMAL_1, BIG_DECIMAL_5))
857            return ONE;
858
859          return OTHER;
860        },
861        Sets.sortedSet(
862            ONE,
863            OTHER
864        ),
865        Maps.sortedMap(
866            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1, 5)),
867            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 100, 1000, 10000, 100000, 1000000))
868        )
869    ),
870
871    /**
872     * Languages Include:
873     * <p>
874     * <ul>
875     * <li>Italian (it)</li>
876     * </ul>
877     */
878    FAMILY_11(
879        (n) -> {
880          // n = 11,8,80,800
881          if (inSet(n, BIG_DECIMAL_11, BIG_DECIMAL_8, BIG_DECIMAL_80, BIG_DECIMAL_800))
882            return MANY;
883
884          return OTHER;
885        },
886        Sets.sortedSet(
887            MANY,
888            OTHER
889        ),
890        Maps.sortedMap(
891            MapEntry.of(Ordinality.MANY, Range.ofFiniteValues(8, 11, 80, 800)),
892            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 13, 14, 15, 16, 17, 100, 1000, 10000, 100000, 1000000))
893        )
894    ),
895
896    /**
897     * Languages Include:
898     * <p>
899     * <ul>
900     * <li>Georgian (ka)</li>
901     * </ul>
902     */
903    FAMILY_12(
904        (n) -> {
905          BigInteger i = NumberUtils.integerComponent(n);
906
907          // i = 1
908          if (equal(i, BIG_INTEGER_1))
909            return ONE;
910          // i = 0 or i % 100 = 2..20,40,60,80
911          if (equal(i, BIG_INTEGER_0) || inRange(i.mod(BIG_INTEGER_100), BIG_INTEGER_2, BIG_INTEGER_20) || inSet(i.mod(BIG_INTEGER_100), BIG_INTEGER_40, BIG_INTEGER_60, BIG_INTEGER_80))
912            return MANY;
913
914          return OTHER;
915        },
916        Sets.sortedSet(
917            ONE,
918            MANY,
919            OTHER
920        ),
921        Maps.sortedMap(
922            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
923            MapEntry.of(Ordinality.MANY, Range.ofInfiniteValues(0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 102, 1002)),
924            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 100, 1000, 10000, 100000, 1000000))
925        )
926    ),
927
928    /**
929     * Languages Include:
930     * <p>
931     * <ul>
932     * <li>Kazakh (kk)</li>
933     * </ul>
934     */
935    FAMILY_13(
936        (n) -> {
937          // n % 10 = 6 or n % 10 = 9 or n % 10 = 0 and n != 0
938          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_6)
939              || equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_9)
940              || (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_0) && notEqual(n, BIG_DECIMAL_0)))
941            return MANY;
942
943          return OTHER;
944        },
945        Sets.sortedSet(
946            MANY,
947            OTHER
948        ),
949        Maps.sortedMap(
950            MapEntry.of(Ordinality.MANY, Range.ofInfiniteValues(6, 9, 10, 16, 19, 20, 26, 29, 30, 36, 39, 40, 100, 1000, 10000, 100000, 1000000)),
951            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 1, 2, 3, 4, 5, 7, 8, 11, 12, 13, 14, 15, 17, 18, 21, 101, 1001))
952        )
953    ),
954
955    /**
956     * Languages Include:
957     * <p>
958     * <ul>
959     * <li>Macedonian (mk)</li>
960     * </ul>
961     */
962    FAMILY_14(
963        (n) -> {
964          BigInteger i = NumberUtils.integerComponent(n);
965
966          // i % 10 = 1 and i % 100 != 11
967          if (equal(i.mod(BIG_INTEGER_10), BIG_INTEGER_1) && notEqual(i.mod(BIG_INTEGER_100), BIG_INTEGER_11))
968            return ONE;
969          // i % 10 = 2 and i % 100 != 12
970          if (equal(i.mod(BIG_INTEGER_10), BIG_INTEGER_2) && notEqual(i.mod(BIG_INTEGER_100), BIG_INTEGER_12))
971            return TWO;
972          // i % 10 = 7,8 and i % 100 != 17,18
973          if (inSet(i.mod(BIG_INTEGER_10), BIG_INTEGER_7, BIG_INTEGER_8) && notInSet(i.mod(BIG_INTEGER_100), BIG_INTEGER_17, BIG_INTEGER_18))
974            return MANY;
975
976          return OTHER;
977        },
978        Sets.sortedSet(
979            ONE,
980            TWO,
981            MANY,
982            OTHER
983        ),
984        Maps.sortedMap(
985            MapEntry.of(Ordinality.ONE, Range.ofInfiniteValues(1, 21, 31, 41, 51, 61, 71, 81, 101, 1001)),
986            MapEntry.of(Ordinality.TWO, Range.ofInfiniteValues(2, 22, 32, 42, 52, 62, 72, 82, 102, 1002)),
987            MapEntry.of(Ordinality.MANY, Range.ofInfiniteValues(7, 8, 27, 28, 37, 38, 47, 48, 57, 58, 67, 68, 77, 78, 87, 88, 107, 1007)),
988            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 1000, 10000, 100000, 1000000))
989        )
990    ),
991
992    /**
993     * Languages Include:
994     * <p>
995     * <ul>
996     * <li>Marathi (mr)</li>
997     * </ul>
998     */
999    FAMILY_15(
1000        (n) -> {
1001          // n = 1
1002          if (equal(n, BIG_DECIMAL_1))
1003            return ONE;
1004          // n = 2,3
1005          if (inSet(n, BIG_DECIMAL_2, BIG_DECIMAL_3))
1006            return TWO;
1007          // n = 4
1008          if (equal(n, BIG_DECIMAL_4))
1009            return FEW;
1010
1011          return OTHER;
1012        },
1013        Sets.sortedSet(
1014            ONE,
1015            TWO,
1016            FEW,
1017            OTHER
1018        ),
1019        Maps.sortedMap(
1020            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
1021            MapEntry.of(Ordinality.TWO, Range.ofFiniteValues(2, 3)),
1022            MapEntry.of(Ordinality.FEW, Range.ofFiniteValues(4)),
1023            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 1000, 10000, 100000, 1000000))
1024        )
1025    ),
1026
1027    /**
1028     * Languages Include:
1029     * <p>
1030     * <ul>
1031     * <li>Nepali (ne)</li>
1032     * </ul>
1033     */
1034    FAMILY_16(
1035        (n) -> {
1036          // n = 1..4
1037          if (inRange(n, BIG_DECIMAL_1, BIG_DECIMAL_4))
1038            return ONE;
1039
1040          return OTHER;
1041        },
1042        Sets.sortedSet(
1043            ONE,
1044            OTHER
1045        ),
1046        Maps.sortedMap(
1047            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1, 2, 3, 4)),
1048            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 1000, 10000, 100000, 1000000))
1049        )
1050    ),
1051
1052    /**
1053     * Languages Include:
1054     * <p>
1055     * <ul>
1056     * <li>Albanian (sq)</li>
1057     * </ul>
1058     */
1059    FAMILY_17(
1060        (n) -> {
1061          // n = 1
1062          if (equal(n, BIG_DECIMAL_1))
1063            return ONE;
1064          // n % 10 = 4 and n % 100 != 14
1065          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_4) && notEqual(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_14))
1066            return MANY;
1067
1068          return OTHER;
1069        },
1070        Sets.sortedSet(
1071            ONE,
1072            MANY,
1073            OTHER
1074        ),
1075        Maps.sortedMap(
1076            MapEntry.of(Ordinality.ONE, Range.ofFiniteValues(1)),
1077            MapEntry.of(Ordinality.MANY, Range.ofInfiniteValues(4, 24, 34, 44, 54, 64, 74, 84, 104, 1004)),
1078            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 100, 1000, 10000, 100000, 1000000))
1079        )
1080    ),
1081
1082    /**
1083     * Languages Include:
1084     * <p>
1085     * <ul>
1086     * <li>Swedish (sv)</li>
1087     * </ul>
1088     */
1089    FAMILY_18(
1090        (n) -> {
1091          // n % 10 = 1,2 and n % 100 != 11,12
1092          if (inSet(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_1, BIG_DECIMAL_2) && notInSet(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_11, BIG_DECIMAL_12))
1093            return ONE;
1094
1095          return OTHER;
1096        },
1097        Sets.sortedSet(
1098            ONE,
1099            OTHER
1100        ),
1101        Maps.sortedMap(
1102            MapEntry.of(Ordinality.ONE, Range.ofInfiniteValues(1, 2, 21, 22, 31, 32, 41, 42, 51, 52, 61, 62, 71, 72, 81, 82, 101, 1001)),
1103            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 100, 1000, 10000, 100000, 1000000))
1104        )
1105    ),
1106
1107    /**
1108     * Languages Include:
1109     * <p>
1110     * <ul>
1111     * <li>Ukrainian (uk)</li>
1112     * </ul>
1113     */
1114    FAMILY_19(
1115        (n) -> {
1116          // n % 10 = 3 and n % 100 != 13
1117          if (equal(n.remainder(BIG_DECIMAL_10), BIG_DECIMAL_3) && notEqual(n.remainder(BIG_DECIMAL_100), BIG_DECIMAL_13))
1118            return FEW;
1119
1120          return OTHER;
1121        },
1122        Sets.sortedSet(
1123            FEW,
1124            OTHER
1125        ),
1126        Maps.sortedMap(
1127            MapEntry.of(Ordinality.FEW, Range.ofInfiniteValues(3, 23, 33, 43, 53, 63, 73, 83, 103, 1003)),
1128            MapEntry.of(Ordinality.OTHER, Range.ofInfiniteValues(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 100, 1000, 10000, 100000, 1000000))
1129        )
1130    );
1131
1132    @Nonnull
1133    private static final Map<String, OrdinalityFamily> ORDINALITY_FAMILIES_BY_LANGUAGE_CODE;
1134    @Nonnull
1135    private static final SortedSet<String> SUPPORTED_LANGUAGE_CODES;
1136
1137    @Nonnull
1138    private final Function<BigDecimal, Ordinality> ordinalityFunction;
1139    @Nonnull
1140    private final SortedSet<Ordinality> supportedOrdinalities;
1141    @Nonnull
1142    private final SortedMap<Ordinality, Range<Integer>> exampleIntegerValuesByOrdinality;
1143
1144    /**
1145     * Constructs an ordinality family.
1146     *
1147     * @param ordinalityFunction               the ordinality-determining function for this ordinality family, not null
1148     * @param supportedOrdinalities            the ordinalities supported by this family sorted by the natural ordering of {@link Ordinality}, not null
1149     * @param exampleIntegerValuesByOrdinality a mapping of ordinalities to example integer values for this ordinality family sorted by the natural ordering of {@link Ordinality}, not null
1150     */
1151    OrdinalityFamily(@Nonnull Function<BigDecimal, Ordinality> ordinalityFunction,
1152                     @Nonnull SortedSet<Ordinality> supportedOrdinalities,
1153                     @Nonnull SortedMap<Ordinality, Range<Integer>> exampleIntegerValuesByOrdinality) {
1154      requireNonNull(ordinalityFunction);
1155      requireNonNull(supportedOrdinalities);
1156      requireNonNull(exampleIntegerValuesByOrdinality);
1157
1158      this.ordinalityFunction = ordinalityFunction;
1159      this.supportedOrdinalities = supportedOrdinalities;
1160      this.exampleIntegerValuesByOrdinality = exampleIntegerValuesByOrdinality;
1161    }
1162
1163    static {
1164      ORDINALITY_FAMILIES_BY_LANGUAGE_CODE = Collections.unmodifiableMap(new HashMap<String, OrdinalityFamily>() {{
1165        put("af", OrdinalityFamily.FAMILY_1); // Afrikaans
1166        put("ak", OrdinalityFamily.FAMILY_1); // Akan (no CLDR data available)
1167        put("am", OrdinalityFamily.FAMILY_1); // Amharic
1168        put("ar", OrdinalityFamily.FAMILY_1); // Arabic
1169        put("ars", OrdinalityFamily.FAMILY_1); // Najdi Arabic (no CLDR data available)
1170        put("as", OrdinalityFamily.FAMILY_3); // Assamese
1171        put("asa", OrdinalityFamily.FAMILY_1); // Asu (no CLDR data available)
1172        put("ast", OrdinalityFamily.FAMILY_1); // Asturian (no CLDR data available)
1173        put("az", OrdinalityFamily.FAMILY_5); // Azeri
1174        put("be", OrdinalityFamily.FAMILY_6); // Belarusian
1175        put("bem", OrdinalityFamily.FAMILY_1); // Bemba (no CLDR data available)
1176        put("bez", OrdinalityFamily.FAMILY_1); // Bena (no CLDR data available)
1177        put("bg", OrdinalityFamily.FAMILY_1); // Bulgarian
1178        put("bh", OrdinalityFamily.FAMILY_1); // Bihari (no CLDR data available)
1179        put("bm", OrdinalityFamily.FAMILY_1); // Bambara (no CLDR data available)
1180        put("bn", OrdinalityFamily.FAMILY_3); // Bangla
1181        put("bo", OrdinalityFamily.FAMILY_1); // Tibetan (no CLDR data available)
1182        put("br", OrdinalityFamily.FAMILY_1); // Breton (no CLDR data available)
1183        put("brx", OrdinalityFamily.FAMILY_1); // Bodo (no CLDR data available)
1184        put("bs", OrdinalityFamily.FAMILY_1); // Bosnian
1185        put("ca", OrdinalityFamily.FAMILY_7); // Catalan
1186        put("ce", OrdinalityFamily.FAMILY_1); // Chechen
1187        put("cgg", OrdinalityFamily.FAMILY_1); // Chiga (no CLDR data available)
1188        put("chr", OrdinalityFamily.FAMILY_1); // Cherokee (no CLDR data available)
1189        put("ckb", OrdinalityFamily.FAMILY_1); // Central Kurdish (no CLDR data available)
1190        put("cs", OrdinalityFamily.FAMILY_1); // Czech
1191        put("cy", OrdinalityFamily.FAMILY_8); // Welsh
1192        put("da", OrdinalityFamily.FAMILY_1); // Danish
1193        put("de", OrdinalityFamily.FAMILY_1); // German
1194        put("dsb", OrdinalityFamily.FAMILY_1); // Lower Sorbian
1195        put("dv", OrdinalityFamily.FAMILY_1); // Divehi (no CLDR data available)
1196        put("dz", OrdinalityFamily.FAMILY_1); // Dzongkha (no CLDR data available)
1197        put("ee", OrdinalityFamily.FAMILY_1); // Ewe (no CLDR data available)
1198        put("el", OrdinalityFamily.FAMILY_1); // Greek
1199        put("en", OrdinalityFamily.FAMILY_9); // English
1200        put("eo", OrdinalityFamily.FAMILY_1); // Esperanto (no CLDR data available)
1201        put("es", OrdinalityFamily.FAMILY_1); // Spanish
1202        put("et", OrdinalityFamily.FAMILY_1); // Estonian
1203        put("eu", OrdinalityFamily.FAMILY_1); // Basque
1204        put("fa", OrdinalityFamily.FAMILY_1); // Persian
1205        put("ff", OrdinalityFamily.FAMILY_1); // Fulah (no CLDR data available)
1206        put("fi", OrdinalityFamily.FAMILY_1); // Finnish
1207        put("fil", OrdinalityFamily.FAMILY_2); // Filipino
1208        put("fo", OrdinalityFamily.FAMILY_1); // Faroese (no CLDR data available)
1209        put("fr", OrdinalityFamily.FAMILY_2); // French
1210        put("fur", OrdinalityFamily.FAMILY_1); // Friulian (no CLDR data available)
1211        put("fy", OrdinalityFamily.FAMILY_1); // Western Frisian
1212        put("ga", OrdinalityFamily.FAMILY_2); // Irish
1213        put("gd", OrdinalityFamily.FAMILY_1); // Scottish Gaelic (no CLDR data available)
1214        put("gl", OrdinalityFamily.FAMILY_1); // Galician
1215        put("gsw", OrdinalityFamily.FAMILY_1); // Swiss German
1216        put("gu", OrdinalityFamily.FAMILY_4); // Gujarati
1217        put("guw", OrdinalityFamily.FAMILY_1); // Gun (no CLDR data available)
1218        put("gv", OrdinalityFamily.FAMILY_1); // Manx (no CLDR data available)
1219        put("ha", OrdinalityFamily.FAMILY_1); // Hausa (no CLDR data available)
1220        put("haw", OrdinalityFamily.FAMILY_1); // Hawaiian (no CLDR data available)
1221        put("he", OrdinalityFamily.FAMILY_1); // Hebrew
1222        put("hi", OrdinalityFamily.FAMILY_4); // Hindi
1223        put("hr", OrdinalityFamily.FAMILY_1); // Croatian
1224        put("hsb", OrdinalityFamily.FAMILY_1); // Upper Sorbian
1225        put("hu", OrdinalityFamily.FAMILY_10); // Hungarian
1226        put("hy", OrdinalityFamily.FAMILY_2); // Armenian
1227        put("id", OrdinalityFamily.FAMILY_1); // Indonesian
1228        put("ig", OrdinalityFamily.FAMILY_1); // Igbo (no CLDR data available)
1229        put("ii", OrdinalityFamily.FAMILY_1); // Sichuan Yi (no CLDR data available)
1230        put("is", OrdinalityFamily.FAMILY_1); // Icelandic
1231        put("it", OrdinalityFamily.FAMILY_11); // Italian
1232        put("iu", OrdinalityFamily.FAMILY_1); // Inuktitut (no CLDR data available)
1233        put("ja", OrdinalityFamily.FAMILY_1); // Japanese
1234        put("jbo", OrdinalityFamily.FAMILY_1); // Lojban (no CLDR data available)
1235        put("jgo", OrdinalityFamily.FAMILY_1); // Ngomba (no CLDR data available)
1236        put("jmc", OrdinalityFamily.FAMILY_1); // Machame (no CLDR data available)
1237        put("jv", OrdinalityFamily.FAMILY_1); // Javanese (no CLDR data available)
1238        put("jw", OrdinalityFamily.FAMILY_1); // Javanese (no CLDR data available)
1239        put("ka", OrdinalityFamily.FAMILY_12); // Georgian
1240        put("kab", OrdinalityFamily.FAMILY_1); // Kabyle (no CLDR data available)
1241        put("kaj", OrdinalityFamily.FAMILY_1); // Jju (no CLDR data available)
1242        put("kcg", OrdinalityFamily.FAMILY_1); // Tyap (no CLDR data available)
1243        put("kde", OrdinalityFamily.FAMILY_1); // Makonde (no CLDR data available)
1244        put("kea", OrdinalityFamily.FAMILY_1); // Kabuverdianu (no CLDR data available)
1245        put("kk", OrdinalityFamily.FAMILY_13); // Kazakh
1246        put("kkj", OrdinalityFamily.FAMILY_1); // Kako (no CLDR data available)
1247        put("kl", OrdinalityFamily.FAMILY_1); // Greenlandic (no CLDR data available)
1248        put("km", OrdinalityFamily.FAMILY_1); // Khmer
1249        put("kn", OrdinalityFamily.FAMILY_1); // Kannada
1250        put("ko", OrdinalityFamily.FAMILY_1); // Korean
1251        put("ks", OrdinalityFamily.FAMILY_1); // Kashmiri (no CLDR data available)
1252        put("ksb", OrdinalityFamily.FAMILY_1); // Shambala (no CLDR data available)
1253        put("ksh", OrdinalityFamily.FAMILY_1); // Colognian (no CLDR data available)
1254        put("ku", OrdinalityFamily.FAMILY_1); // Kurdish (no CLDR data available)
1255        put("kw", OrdinalityFamily.FAMILY_1); // Cornish (no CLDR data available)
1256        put("ky", OrdinalityFamily.FAMILY_1); // Kirghiz
1257        put("lag", OrdinalityFamily.FAMILY_1); // Langi (no CLDR data available)
1258        put("lb", OrdinalityFamily.FAMILY_1); // Luxembourgish (no CLDR data available)
1259        put("lg", OrdinalityFamily.FAMILY_1); // Ganda (no CLDR data available)
1260        put("lkt", OrdinalityFamily.FAMILY_1); // Lakota (no CLDR data available)
1261        put("ln", OrdinalityFamily.FAMILY_1); // Lingala (no CLDR data available)
1262        put("lo", OrdinalityFamily.FAMILY_2); // Lao
1263        put("lt", OrdinalityFamily.FAMILY_1); // Lithuanian
1264        put("lv", OrdinalityFamily.FAMILY_1); // Latvian
1265        put("mas", OrdinalityFamily.FAMILY_1); // Masai (no CLDR data available)
1266        put("mg", OrdinalityFamily.FAMILY_1); // Malagasy (no CLDR data available)
1267        put("mgo", OrdinalityFamily.FAMILY_1); // Metaʼ (no CLDR data available)
1268        put("mk", OrdinalityFamily.FAMILY_14); // Macedonian
1269        put("ml", OrdinalityFamily.FAMILY_1); // Malayalam
1270        put("mn", OrdinalityFamily.FAMILY_1); // Mongolian
1271        put("mo", OrdinalityFamily.FAMILY_2); // Moldovan
1272        put("mr", OrdinalityFamily.FAMILY_15); // Marathi
1273        put("ms", OrdinalityFamily.FAMILY_2); // Malay
1274        put("mt", OrdinalityFamily.FAMILY_1); // Maltese (no CLDR data available)
1275        put("my", OrdinalityFamily.FAMILY_1); // Burmese
1276        put("nah", OrdinalityFamily.FAMILY_1); // Nahuatl (no CLDR data available)
1277        put("naq", OrdinalityFamily.FAMILY_1); // Nama (no CLDR data available)
1278        put("nb", OrdinalityFamily.FAMILY_1); // Norwegian Bokmål
1279        put("nd", OrdinalityFamily.FAMILY_1); // North Ndebele (no CLDR data available)
1280        put("ne", OrdinalityFamily.FAMILY_16); // Nepali
1281        put("nl", OrdinalityFamily.FAMILY_1); // Dutch
1282        put("nn", OrdinalityFamily.FAMILY_1); // Norwegian Nynorsk (no CLDR data available)
1283        put("nnh", OrdinalityFamily.FAMILY_1); // Ngiemboon (no CLDR data available)
1284        put("no", OrdinalityFamily.FAMILY_1); // Norwegian (no CLDR data available)
1285        put("nqo", OrdinalityFamily.FAMILY_1); // N’Ko (no CLDR data available)
1286        put("nr", OrdinalityFamily.FAMILY_1); // South Ndebele (no CLDR data available)
1287        put("nso", OrdinalityFamily.FAMILY_1); // Northern Sotho (no CLDR data available)
1288        put("ny", OrdinalityFamily.FAMILY_1); // Nyanja (no CLDR data available)
1289        put("nyn", OrdinalityFamily.FAMILY_1); // Nyankole (no CLDR data available)
1290        put("om", OrdinalityFamily.FAMILY_1); // Oromo (no CLDR data available)
1291        put("or", OrdinalityFamily.FAMILY_1); // Odia (no CLDR data available)
1292        put("os", OrdinalityFamily.FAMILY_1); // Ossetian (no CLDR data available)
1293        put("pa", OrdinalityFamily.FAMILY_1); // Punjabi
1294        put("pap", OrdinalityFamily.FAMILY_1); // Papiamento (no CLDR data available)
1295        put("pl", OrdinalityFamily.FAMILY_1); // Polish
1296        put("prg", OrdinalityFamily.FAMILY_1); // Prussian
1297        put("ps", OrdinalityFamily.FAMILY_1); // Pushto (no CLDR data available)
1298        put("pt", OrdinalityFamily.FAMILY_1); // Portuguese
1299        put("rm", OrdinalityFamily.FAMILY_1); // Romansh (no CLDR data available)
1300        put("ro", OrdinalityFamily.FAMILY_2); // Romanian
1301        put("rof", OrdinalityFamily.FAMILY_1); // Rombo (no CLDR data available)
1302        put("root", OrdinalityFamily.FAMILY_1); // Root
1303        put("ru", OrdinalityFamily.FAMILY_1); // Russian
1304        put("rwk", OrdinalityFamily.FAMILY_1); // Rwa (no CLDR data available)
1305        put("sah", OrdinalityFamily.FAMILY_1); // Sakha (no CLDR data available)
1306        put("saq", OrdinalityFamily.FAMILY_1); // Samburu (no CLDR data available)
1307        put("sdh", OrdinalityFamily.FAMILY_1); // Southern Kurdish (no CLDR data available)
1308        put("se", OrdinalityFamily.FAMILY_1); // Northern Sami (no CLDR data available)
1309        put("seh", OrdinalityFamily.FAMILY_1); // Sena (no CLDR data available)
1310        put("ses", OrdinalityFamily.FAMILY_1); // Koyraboro Senni (no CLDR data available)
1311        put("sg", OrdinalityFamily.FAMILY_1); // Sango (no CLDR data available)
1312        put("sh", OrdinalityFamily.FAMILY_1); // Serbo-Croatian
1313        put("shi", OrdinalityFamily.FAMILY_1); // Tachelhit (no CLDR data available)
1314        put("si", OrdinalityFamily.FAMILY_1); // Sinhalese
1315        put("sk", OrdinalityFamily.FAMILY_1); // Slovak
1316        put("sl", OrdinalityFamily.FAMILY_1); // Slovenian
1317        put("sma", OrdinalityFamily.FAMILY_1); // Southern Sami (no CLDR data available)
1318        put("smi", OrdinalityFamily.FAMILY_1); // Sami (no CLDR data available)
1319        put("smj", OrdinalityFamily.FAMILY_1); // Lule Sami (no CLDR data available)
1320        put("smn", OrdinalityFamily.FAMILY_1); // Inari Sami (no CLDR data available)
1321        put("sms", OrdinalityFamily.FAMILY_1); // Skolt Sami (no CLDR data available)
1322        put("sn", OrdinalityFamily.FAMILY_1); // Shona (no CLDR data available)
1323        put("so", OrdinalityFamily.FAMILY_1); // Somali (no CLDR data available)
1324        put("sq", OrdinalityFamily.FAMILY_17); // Albanian
1325        put("sr", OrdinalityFamily.FAMILY_1); // Serbian
1326        put("ss", OrdinalityFamily.FAMILY_1); // Swati (no CLDR data available)
1327        put("ssy", OrdinalityFamily.FAMILY_1); // Saho (no CLDR data available)
1328        put("st", OrdinalityFamily.FAMILY_1); // Southern Sotho (no CLDR data available)
1329        put("sv", OrdinalityFamily.FAMILY_18); // Swedish
1330        put("sw", OrdinalityFamily.FAMILY_1); // Swahili
1331        put("syr", OrdinalityFamily.FAMILY_1); // Syriac (no CLDR data available)
1332        put("ta", OrdinalityFamily.FAMILY_1); // Tamil
1333        put("te", OrdinalityFamily.FAMILY_1); // Telugu
1334        put("teo", OrdinalityFamily.FAMILY_1); // Teso (no CLDR data available)
1335        put("th", OrdinalityFamily.FAMILY_1); // Thai
1336        put("ti", OrdinalityFamily.FAMILY_1); // Tigrinya (no CLDR data available)
1337        put("tig", OrdinalityFamily.FAMILY_1); // Tigre (no CLDR data available)
1338        put("tk", OrdinalityFamily.FAMILY_1); // Turkmen (no CLDR data available)
1339        put("tl", OrdinalityFamily.FAMILY_2); // Tagalog
1340        put("tn", OrdinalityFamily.FAMILY_1); // Tswana (no CLDR data available)
1341        put("to", OrdinalityFamily.FAMILY_1); // Tongan (no CLDR data available)
1342        put("tr", OrdinalityFamily.FAMILY_1); // Turkish
1343        put("ts", OrdinalityFamily.FAMILY_1); // Tsonga (no CLDR data available)
1344        put("tzm", OrdinalityFamily.FAMILY_1); // Central Atlas Tamazight (no CLDR data available)
1345        put("ug", OrdinalityFamily.FAMILY_1); // Uighur (no CLDR data available)
1346        put("uk", OrdinalityFamily.FAMILY_19); // Ukrainian
1347        put("ur", OrdinalityFamily.FAMILY_1); // Urdu
1348        put("uz", OrdinalityFamily.FAMILY_1); // Uzbek
1349        put("ve", OrdinalityFamily.FAMILY_1); // Venda (no CLDR data available)
1350        put("vi", OrdinalityFamily.FAMILY_2); // Vietnamese
1351        put("vo", OrdinalityFamily.FAMILY_1); // Volapük (no CLDR data available)
1352        put("vun", OrdinalityFamily.FAMILY_1); // Vunjo (no CLDR data available)
1353        put("wa", OrdinalityFamily.FAMILY_1); // Walloon (no CLDR data available)
1354        put("wae", OrdinalityFamily.FAMILY_1); // Walser (no CLDR data available)
1355        put("wo", OrdinalityFamily.FAMILY_1); // Wolof (no CLDR data available)
1356        put("xh", OrdinalityFamily.FAMILY_1); // Xhosa (no CLDR data available)
1357        put("xog", OrdinalityFamily.FAMILY_1); // Soga (no CLDR data available)
1358        put("yi", OrdinalityFamily.FAMILY_1); // Yiddish (no CLDR data available)
1359        put("yo", OrdinalityFamily.FAMILY_1); // Yoruba (no CLDR data available)
1360        put("yue", OrdinalityFamily.FAMILY_1); // Cantonese
1361        put("zh", OrdinalityFamily.FAMILY_1); // Mandarin Chinese
1362        put("zu", OrdinalityFamily.FAMILY_1); // Zulu
1363      }});
1364
1365      // Language codes are in English - force collation for sorting
1366      SortedSet<String> supportedLanguageCodes = new TreeSet<>(Collator.getInstance(Locale.ENGLISH));
1367      supportedLanguageCodes.addAll(ORDINALITY_FAMILIES_BY_LANGUAGE_CODE.keySet());
1368
1369      SUPPORTED_LANGUAGE_CODES = Collections.unmodifiableSortedSet(supportedLanguageCodes);
1370    }
1371
1372    /**
1373     * Gets the ordinality-determining function for this ordinality family.
1374     * <p>
1375     * The function takes a numeric value as input and returns the appropriate ordinal form.
1376     * <p>
1377     * The function's input must not be null and its output is guaranteed non-null.
1378     *
1379     * @return the ordinality-determining function for this ordinality family, not null
1380     */
1381    @Nonnull
1382    public Function<BigDecimal, Ordinality> getOrdinalityFunction() {
1383      return ordinalityFunction;
1384    }
1385
1386    /**
1387     * Gets the ordinalities supported by this ordinality family.
1388     * <p>
1389     * There will always be at least one value - {@link Ordinality#OTHER} - in the set.
1390     * <p>
1391     * The set's values are sorted by the natural ordering of the {@link Ordinality} enumeration.
1392     *
1393     * @return the ordinalities supported by this ordinality family, not null
1394     */
1395    @Nonnull
1396    SortedSet<Ordinality> getSupportedOrdinalities() {
1397      return supportedOrdinalities;
1398    }
1399
1400    /**
1401     * Gets a mapping of ordinalities to example integer values for this ordinality family.
1402     * <p>
1403     * The map may be empty.
1404     * <p>
1405     * The map's keys are sorted by the natural ordering of the {@link Ordinality} enumeration.
1406     *
1407     * @return a mapping of ordinalities to example integer values, not null
1408     */
1409    @Nonnull
1410    SortedMap<Ordinality, Range<Integer>> getExampleIntegerValuesByOrdinality() {
1411      return exampleIntegerValuesByOrdinality;
1412    }
1413
1414    /**
1415     * Gets the ISO 639 language codes for which ordinality operations are supported.
1416     * <p>
1417     * The set's values are ISO 639 codes and therefore sorted using English collation.
1418     *
1419     * @return the ISO 639 language codes for which ordinality operations are supported, not null
1420     */
1421    @Nonnull
1422    static SortedSet<String> getSupportedLanguageCodes() {
1423      return SUPPORTED_LANGUAGE_CODES;
1424    }
1425
1426    /**
1427     * Gets an appropriate plural ordinality family for the given locale.
1428     *
1429     * @param locale the locale to check, not null
1430     * @return the appropriate plural ordinality family (if one exists) for the given locale, not null
1431     */
1432    @Nonnull
1433    static Optional<OrdinalityFamily> ordinalityFamilyForLocale(@Nonnull Locale locale) {
1434      requireNonNull(locale);
1435
1436      String language = LocaleUtils.normalizedLanguage(locale).orElse(null);
1437      String country = locale.getCountry();
1438
1439      OrdinalityFamily ordinalityFamily = null;
1440
1441      if (language != null && country != null)
1442        ordinalityFamily = ORDINALITY_FAMILIES_BY_LANGUAGE_CODE.get(format("%s-%s", language, country));
1443
1444      if (ordinalityFamily != null)
1445        return Optional.of(ordinalityFamily);
1446
1447      if (language != null)
1448        ordinalityFamily = ORDINALITY_FAMILIES_BY_LANGUAGE_CODE.get(language);
1449
1450      return Optional.ofNullable(ordinalityFamily);
1451    }
1452  }
1453}