1"""
2 babel.messages.plurals
3 ~~~~~~~~~~~~~~~~~~~~~~
4
5 Plural form definitions.
6
7 :copyright: (c) 2013-2025 by the Babel Team.
8 :license: BSD, see LICENSE for more details.
9"""
10from __future__ import annotations
11
12from babel.core import Locale, default_locale
13
14# XXX: remove this file, duplication with babel.plural
15
16
17LC_CTYPE: str | None = default_locale('LC_CTYPE')
18
19
20PLURALS: dict[str, tuple[int, str]] = {
21 # Afar
22 # 'aa': (),
23 # Abkhazian
24 # 'ab': (),
25 # Avestan
26 # 'ae': (),
27 # Afrikaans - From Pootle's PO's
28 'af': (2, '(n != 1)'),
29 # Akan
30 # 'ak': (),
31 # Amharic
32 # 'am': (),
33 # Aragonese
34 # 'an': (),
35 # Arabic - From Pootle's PO's
36 'ar': (6, '(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=0 && n%100<=2 ? 4 : 5)'),
37 # Assamese
38 # 'as': (),
39 # Avaric
40 # 'av': (),
41 # Aymara
42 # 'ay': (),
43 # Azerbaijani
44 # 'az': (),
45 # Bashkir
46 # 'ba': (),
47 # Belarusian
48 'be': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
49 # Bulgarian - From Pootle's PO's
50 'bg': (2, '(n != 1)'),
51 # Bihari
52 # 'bh': (),
53 # Bislama
54 # 'bi': (),
55 # Bambara
56 # 'bm': (),
57 # Bengali - From Pootle's PO's
58 'bn': (2, '(n != 1)'),
59 # Tibetan - as discussed in private with Andrew West
60 'bo': (1, '0'),
61 # Breton
62 'br': (
63 6,
64 '(n==1 ? 0 : n%10==1 && n%100!=11 && n%100!=71 && n%100!=91 ? 1 : n%10==2 && n%100!=12 && n%100!=72 && '
65 'n%100!=92 ? 2 : (n%10==3 || n%10==4 || n%10==9) && n%100!=13 && n%100!=14 && n%100!=19 && n%100!=73 && '
66 'n%100!=74 && n%100!=79 && n%100!=93 && n%100!=94 && n%100!=99 ? 3 : n%1000000==0 ? 4 : 5)',
67 ),
68 # Bosnian
69 'bs': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
70 # Catalan - From Pootle's PO's
71 'ca': (2, '(n != 1)'),
72 # Chechen
73 # 'ce': (),
74 # Chamorro
75 # 'ch': (),
76 # Corsican
77 # 'co': (),
78 # Cree
79 # 'cr': (),
80 # Czech
81 'cs': (3, '((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2)'),
82 # Church Slavic
83 # 'cu': (),
84 # Chuvash
85 'cv': (1, '0'),
86 # Welsh
87 'cy': (5, '(n==1 ? 1 : n==2 ? 2 : n==3 ? 3 : n==6 ? 4 : 0)'),
88 # Danish
89 'da': (2, '(n != 1)'),
90 # German
91 'de': (2, '(n != 1)'),
92 # Divehi
93 # 'dv': (),
94 # Dzongkha
95 'dz': (1, '0'),
96 # Greek
97 'el': (2, '(n != 1)'),
98 # English
99 'en': (2, '(n != 1)'),
100 # Esperanto
101 'eo': (2, '(n != 1)'),
102 # Spanish
103 'es': (2, '(n != 1)'),
104 # Estonian
105 'et': (2, '(n != 1)'),
106 # Basque - From Pootle's PO's
107 'eu': (2, '(n != 1)'),
108 # Persian - From Pootle's PO's
109 'fa': (1, '0'),
110 # Finnish
111 'fi': (2, '(n != 1)'),
112 # French
113 'fr': (2, '(n > 1)'),
114 # Friulian - From Pootle's PO's
115 'fur': (2, '(n > 1)'),
116 # Irish
117 'ga': (5, '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)'),
118 # Galician - From Pootle's PO's
119 'gl': (2, '(n != 1)'),
120 # Hausa - From Pootle's PO's
121 'ha': (2, '(n != 1)'),
122 # Hebrew
123 'he': (2, '(n != 1)'),
124 # Hindi - From Pootle's PO's
125 'hi': (2, '(n != 1)'),
126 # Croatian
127 'hr': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
128 # Hungarian
129 'hu': (1, '0'),
130 # Armenian - From Pootle's PO's
131 'hy': (1, '0'),
132 # Icelandic - From Pootle's PO's
133 'is': (2, '(n%10==1 && n%100!=11 ? 0 : 1)'),
134 # Italian
135 'it': (2, '(n != 1)'),
136 # Japanese
137 'ja': (1, '0'),
138 # Georgian - From Pootle's PO's
139 'ka': (1, '0'),
140 # Kongo - From Pootle's PO's
141 'kg': (2, '(n != 1)'),
142 # Khmer - From Pootle's PO's
143 'km': (1, '0'),
144 # Korean
145 'ko': (1, '0'),
146 # Kurdish - From Pootle's PO's
147 'ku': (2, '(n != 1)'),
148 # Lao - Another member of the Tai language family, like Thai.
149 'lo': (1, '0'),
150 # Lithuanian
151 'lt': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2)'),
152 # Latvian
153 'lv': (3, '(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2)'),
154 # Maltese - From Pootle's PO's
155 'mt': (4, '(n==1 ? 0 : n==0 || ( n%100>=1 && n%100<=10) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3)'),
156 # Norwegian Bokmål
157 'nb': (2, '(n != 1)'),
158 # Dutch
159 'nl': (2, '(n != 1)'),
160 # Norwegian Nynorsk
161 'nn': (2, '(n != 1)'),
162 # Norwegian
163 'no': (2, '(n != 1)'),
164 # Punjabi - From Pootle's PO's
165 'pa': (2, '(n != 1)'),
166 # Polish
167 'pl': (3, '(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
168 # Portuguese
169 'pt': (2, '(n != 1)'),
170 # Brazilian
171 'pt_BR': (2, '(n > 1)'),
172 # Romanian - From Pootle's PO's
173 'ro': (3, '(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2)'),
174 # Russian
175 'ru': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
176 # Slovak
177 'sk': (3, '((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2)'),
178 # Slovenian
179 'sl': (4, '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)'),
180 # Serbian - From Pootle's PO's
181 'sr': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
182 # Southern Sotho - From Pootle's PO's
183 'st': (2, '(n != 1)'),
184 # Swedish
185 'sv': (2, '(n != 1)'),
186 # Thai
187 'th': (1, '0'),
188 # Turkish
189 'tr': (1, '0'),
190 # Ukrainian
191 'uk': (3, '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)'),
192 # Venda - From Pootle's PO's
193 've': (2, '(n != 1)'),
194 # Vietnamese - From Pootle's PO's
195 'vi': (1, '0'),
196 # Xhosa - From Pootle's PO's
197 'xh': (2, '(n != 1)'),
198 # Chinese - From Pootle's PO's (modified)
199 'zh': (1, '0'),
200}
201
202
203DEFAULT_PLURAL: tuple[int, str] = (2, '(n != 1)')
204
205
206class _PluralTuple(tuple):
207 """A tuple with plural information."""
208
209 __slots__ = ()
210
211 @property
212 def num_plurals(self) -> int:
213 """The number of plurals used by the locale."""
214 return self[0]
215
216 @property
217 def plural_expr(self) -> str:
218 """The plural expression used by the locale."""
219 return self[1]
220
221 @property
222 def plural_forms(self) -> str:
223 """The plural expression used by the catalog or locale."""
224 return f'nplurals={self[0]}; plural={self[1]};'
225
226 def __str__(self) -> str:
227 return self.plural_forms
228
229
230def get_plural(locale: Locale | str | None = None) -> _PluralTuple:
231 """A tuple with the information catalogs need to perform proper
232 pluralization. The first item of the tuple is the number of plural
233 forms, the second the plural expression.
234
235 :param locale: the `Locale` object or locale identifier. Defaults to the system character type locale.
236
237 >>> get_plural(locale='en')
238 (2, '(n != 1)')
239 >>> get_plural(locale='ga')
240 (5, '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)')
241
242 The object returned is a special tuple with additional members:
243
244 >>> tup = get_plural("ja")
245 >>> tup.num_plurals
246 1
247 >>> tup.plural_expr
248 '0'
249 >>> tup.plural_forms
250 'nplurals=1; plural=0;'
251
252 Converting the tuple into a string prints the plural forms for a
253 gettext catalog:
254
255 >>> str(tup)
256 'nplurals=1; plural=0;'
257 """
258 locale = Locale.parse(locale or LC_CTYPE)
259 try:
260 tup = PLURALS[str(locale)]
261 except KeyError:
262 try:
263 tup = PLURALS[locale.language]
264 except KeyError:
265 tup = DEFAULT_PLURAL
266 return _PluralTuple(tup)