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