UNPKG

5.11 kBPlain TextView Raw
1import { en } from "make-plural";
2
3import { Dict, Pluralizer, MakePlural } from "./typing";
4import { I18n } from "./I18n";
5
6/**
7 * Creates a new pluralizer function based on [make-plural](https://github.com/eemeli/make-plural/tree/master/packages/plurals).
8 *
9 * @param {boolean} options.includeZero When `true`, will return `zero` as the
10 * first key for `0` pluralization.
11 * @param {boolean} options.ordinal When `true`, will return the scope based on
12 * make-plural's ordinal category.
13 * @param {MakePlural} options.pluralizer The make-plural function that will be
14 * wrapped.
15 * @return {Pluralizer} Returns a pluralizer that can be used by I18n.
16 */
17export function useMakePlural({
18 pluralizer,
19 includeZero = true,
20 ordinal = false,
21}: {
22 pluralizer: MakePlural;
23 includeZero?: boolean;
24 ordinal?: boolean;
25}): Pluralizer {
26 return function (_i18n: I18n, count: number) {
27 return [
28 includeZero && count === 0 ? "zero" : "",
29 pluralizer(count, ordinal),
30 ].filter(Boolean);
31 };
32}
33
34/**
35 * The default pluralizer.
36 *
37 * It's pretty straightforward:
38 *
39 * - when count is `0`, then the possible keys are
40 * either `zero` for `other`; this allows having translations like
41 * "You have no messages", defaulting to "You have 0 messages".
42 * - when count is `1`, then the key is `one`.
43 * - when greater than `1`, then the key is `other`.
44 *
45 * @type {Pluralizer}
46 *
47 * @param {I18n} _i18n The I18n's instance.
48 *
49 * @param {number} count The number that's being analyzed.
50 *
51 * @returns {string[]} The list of possible translation scopes.
52 */
53export const defaultPluralizer: Pluralizer = useMakePlural({
54 pluralizer: en,
55 includeZero: true,
56});
57
58/**
59 * This class enables registering different strategies for pluralization, as
60 * well as getting a pluralized translation.
61 *
62 * The default pluralization strategy is based on three counters:
63 *
64 * - `one`: returned when count is equal to absolute `1`.
65 * - `zero`: returned when count is equal to `0`. If this translation is not
66 * set, then it defaults to `other`.
67 * - `other`: returned when count is different than absolute `1`.
68 *
69 * When calling `i18n.translate` (or its alias), pluralization rules will be
70 * triggered whenever the translation options contain `count`.
71 *
72 * @example
73 * A JSON describing the pluralization rules.
74 *
75 * ```json
76 * {
77 * "en": {
78 * "inbox": {
79 * "zero": "You have no messages",
80 * "one": "You have one message",
81 * "other": "You have %{count} messages"
82 * }
83 * }
84 * }
85 * ```
86 *
87 * @example
88 * How to get pluralized translations.
89 *
90 * ```js
91 * i18n.t("inbox", {count: 0}); // returns "You have no messages"
92 * i18n.t("inbox", {count: 1}); // returns "You have on message"
93 * i18n.t("inbox", {count: 2}); // returns "You have 2 messages"
94 * ```
95 */
96export class Pluralization {
97 private i18n: I18n;
98 private registry: Dict;
99
100 constructor(i18n: I18n) {
101 this.i18n = i18n;
102 this.registry = {};
103
104 this.register("default", defaultPluralizer);
105 }
106
107 /**
108 * Register a new pluralizer strategy.
109 *
110 * You may want to support different pluralization rules based on the locales
111 * you have to support. If you do, make sure you submit a pull request at
112 * <https://github.com/fnando/i18n> so we can distribute it. For now only
113 * Russian is distributed.
114 *
115 * The pluralizer will receive the `I18n` instance, together with `count`.
116 * Is up to the pluralizer to return a list of possible keys given that count.
117 * The Russian pluralizer, for instance, returns `one`, `few`, `many`, `other`
118 * as possible pluralization keys.
119 *
120 * You can view a complete list of pluralization rules at
121 * [unicode.org](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).
122 *
123 * You can also leverage
124 * [make-plural](https://github.com/eemeli/make-plural/), rather than writing
125 * all your pluralization functions. For this, you must wrap make-plural's
126 * function by using `useMakePlural({ pluralizer, includeZero, ordinal })`:
127 *
128 * @example
129 * ```js
130 * import { ru } from "make-plural";
131 * import { useMakePlural } from "i18n-js";
132 *
133 * i18n.pluralization.register("ru", useMakePlural({ pluralizer: ru }));
134 * ```
135 *
136 * @param {string} locale The locale.
137 *
138 * @param {Pluralizer} pluralizer The pluralizer function.
139 *
140 * @returns {void}
141 */
142 public register(locale: string, pluralizer: Pluralizer): void {
143 this.registry[locale] = pluralizer;
144 }
145
146 /**
147 * Returns a list of possible pluralization keys.
148 * This is defined by a chain of pluralizers, going from locale set
149 * explicitly, then the locale set through `i18n.locale`, defaulting to
150 * `defaultPluralizer`.
151 *
152 * @param {string} locale The locale.
153 *
154 * @returns {Pluralizer} The pluralizer function.
155 */
156 public get(locale: string): Pluralizer {
157 return (
158 this.registry[locale] ||
159 this.registry[this.i18n.locale] ||
160 this.registry["default"]
161 );
162 }
163}