1import os
2import base64
3import contextlib
4import gettext
5import unittest
6
7from test import support
8
9
10# TODO:
11#  - Add new tests, for example for "dgettext"
12#  - Remove dummy tests, for example testing for single and double quotes
13#    has no sense, it would have if we were testing a parser (i.e. pygettext)
14#  - Tests should have only one assert.
15
16GNU_MO_DATA = b'''\
173hIElQAAAAAJAAAAHAAAAGQAAAAAAAAArAAAAAAAAACsAAAAFQAAAK0AAAAjAAAAwwAAAKEAAADn
18AAAAMAAAAIkBAAAHAAAAugEAABYAAADCAQAAHAAAANkBAAALAAAA9gEAAEIBAAACAgAAFgAAAEUD
19AAAeAAAAXAMAAKEAAAB7AwAAMgAAAB0EAAAFAAAAUAQAABsAAABWBAAAIQAAAHIEAAAJAAAAlAQA
20AABSYXltb25kIEx1eHVyeSBZYWNoLXQAVGhlcmUgaXMgJXMgZmlsZQBUaGVyZSBhcmUgJXMgZmls
21ZXMAVGhpcyBtb2R1bGUgcHJvdmlkZXMgaW50ZXJuYXRpb25hbGl6YXRpb24gYW5kIGxvY2FsaXph
22dGlvbgpzdXBwb3J0IGZvciB5b3VyIFB5dGhvbiBwcm9ncmFtcyBieSBwcm92aWRpbmcgYW4gaW50
23ZXJmYWNlIHRvIHRoZSBHTlUKZ2V0dGV4dCBtZXNzYWdlIGNhdGFsb2cgbGlicmFyeS4AV2l0aCBj
24b250ZXh0BFRoZXJlIGlzICVzIGZpbGUAVGhlcmUgYXJlICVzIGZpbGVzAG11bGx1c2sAbXkgY29u
25dGV4dARudWRnZSBudWRnZQBteSBvdGhlciBjb250ZXh0BG51ZGdlIG51ZGdlAG51ZGdlIG51ZGdl
26AFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDMtMDQtMTEgMTQ6
27MzItMDQwMApMYXN0LVRyYW5zbGF0b3I6IEouIERhdmlkIEliYW5leiA8ai1kYXZpZEBub29zLmZy
28PgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246
29IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4NTktMQpDb250ZW50
30LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CkdlbmVyYXRlZC1CeTogcHlnZXR0ZXh0LnB5IDEuMQpQ
31bHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0d29iYmxlciBNYW5n
32cm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFoeXIgY2ViaXZxcmYg
33dmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVnIHNiZSBsYmhlIENs
34Z3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1ciBUQUgKdHJnZ3Jr
35ZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4ASGF5ICVzIGZpY2hlcm8gKGNvbnRleHQpAEhheSAl
36cyBmaWNoZXJvcyAoY29udGV4dCkAYmFjb24Ad2luayB3aW5rIChpbiAibXkgY29udGV4dCIpAHdp
37bmsgd2luayAoaW4gIm15IG90aGVyIGNvbnRleHQiKQB3aW5rIHdpbmsA
38'''
39
40# This data contains an invalid major version number (5)
41# An unexpected major version number should be treated as an error when
42# parsing a .mo file
43
44GNU_MO_DATA_BAD_MAJOR_VERSION = b'''\
453hIElQAABQAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
46AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
47AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
48eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
49aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
50CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
51Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
52ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
53MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
54YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
55SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
56NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
57ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
58d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
59eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
60IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
61ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
62'''
63
64# This data contains an invalid minor version number (7)
65# An unexpected minor version number only indicates that some of the file's
66# contents may not be able to be read. It does not indicate an error.
67
68GNU_MO_DATA_BAD_MINOR_VERSION = b'''\
693hIElQcAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
70AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
71AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
72eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
73aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
74CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
75Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
76ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
77MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
78YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
79SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
80NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
81ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
82d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
83eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
84IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
85ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
86'''
87
88
89UMO_DATA = b'''\
903hIElQAAAAADAAAAHAAAADQAAAAAAAAAAAAAAAAAAABMAAAABAAAAE0AAAAQAAAAUgAAAA8BAABj
91AAAABAAAAHMBAAAWAAAAeAEAAABhYsOeAG15Y29udGV4dMOeBGFiw54AUHJvamVjdC1JZC1WZXJz
92aW9uOiAyLjAKUE8tUmV2aXNpb24tRGF0ZTogMjAwMy0wNC0xMSAxMjo0Mi0wNDAwCkxhc3QtVHJh
93bnNsYXRvcjogQmFycnkgQS4gV0Fyc2F3IDxiYXJyeUBweXRob24ub3JnPgpMYW5ndWFnZS1UZWFt
94OiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5
95cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzog
96N2JpdApHZW5lcmF0ZWQtQnk6IG1hbnVhbGx5CgDCpHl6AMKkeXogKGNvbnRleHQgdmVyc2lvbikA
97'''
98
99MMO_DATA = b'''\
1003hIElQAAAAABAAAAHAAAACQAAAADAAAALAAAAAAAAAA4AAAAeAEAADkAAAABAAAAAAAAAAAAAAAA
101UHJvamVjdC1JZC1WZXJzaW9uOiBObyBQcm9qZWN0IDAuMApQT1QtQ3JlYXRpb24tRGF0ZTogV2Vk
102IERlYyAxMSAwNzo0NDoxNSAyMDAyClBPLVJldmlzaW9uLURhdGU6IDIwMDItMDgtMTQgMDE6MTg6
103NTgrMDA6MDAKTGFzdC1UcmFuc2xhdG9yOiBKb2huIERvZSA8amRvZUBleGFtcGxlLmNvbT4KSmFu
104ZSBGb29iYXIgPGpmb29iYXJAZXhhbXBsZS5jb20+Ckxhbmd1YWdlLVRlYW06IHh4IDx4eEBleGFt
105cGxlLmNvbT4KTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy
106c2V0PWlzby04ODU5LTE1CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFi
107bGUKR2VuZXJhdGVkLUJ5OiBweWdldHRleHQucHkgMS4zCgA=
108'''
109
110LOCALEDIR = os.path.join('xx', 'LC_MESSAGES')
111MOFILE = os.path.join(LOCALEDIR, 'gettext.mo')
112MOFILE_BAD_MAJOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_major_version.mo')
113MOFILE_BAD_MINOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_minor_version.mo')
114UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo')
115MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo')
116
117
118class GettextBaseTest(unittest.TestCase):
119    def setUp(self):
120        if not os.path.isdir(LOCALEDIR):
121            os.makedirs(LOCALEDIR)
122        with open(MOFILE, 'wb') as fp:
123            fp.write(base64.decodebytes(GNU_MO_DATA))
124        with open(MOFILE_BAD_MAJOR_VERSION, 'wb') as fp:
125            fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAJOR_VERSION))
126        with open(MOFILE_BAD_MINOR_VERSION, 'wb') as fp:
127            fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MINOR_VERSION))
128        with open(UMOFILE, 'wb') as fp:
129            fp.write(base64.decodebytes(UMO_DATA))
130        with open(MMOFILE, 'wb') as fp:
131            fp.write(base64.decodebytes(MMO_DATA))
132        self.env = support.EnvironmentVarGuard()
133        self.env['LANGUAGE'] = 'xx'
134        gettext._translations.clear()
135
136    def tearDown(self):
137        self.env.__exit__()
138        del self.env
139        support.rmtree(os.path.split(LOCALEDIR)[0])
140
141GNU_MO_DATA_ISSUE_17898 = b'''\
1423hIElQAAAAABAAAAHAAAACQAAAAAAAAAAAAAAAAAAAAsAAAAggAAAC0AAAAAUGx1cmFsLUZvcm1z
143OiBucGx1cmFscz0yOyBwbHVyYWw9KG4gIT0gMSk7CiMtIy0jLSMtIyAgbWVzc2FnZXMucG8gKEVk
144WCBTdHVkaW8pICAjLSMtIy0jLSMKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVU
145Ri04CgA=
146'''
147
148class GettextTestCase1(GettextBaseTest):
149    def setUp(self):
150        GettextBaseTest.setUp(self)
151        self.localedir = os.curdir
152        self.mofile = MOFILE
153        gettext.install('gettext', self.localedir, names=['pgettext'])
154
155    def test_some_translations(self):
156        eq = self.assertEqual
157        # test some translations
158        eq(_('albatross'), 'albatross')
159        eq(_('mullusk'), 'bacon')
160        eq(_(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
161        eq(_(r'nudge nudge'), 'wink wink')
162
163    def test_some_translations_with_context(self):
164        eq = self.assertEqual
165        eq(pgettext('my context', 'nudge nudge'),
166           'wink wink (in "my context")')
167        eq(pgettext('my other context', 'nudge nudge'),
168           'wink wink (in "my other context")')
169
170    def test_double_quotes(self):
171        eq = self.assertEqual
172        # double quotes
173        eq(_("albatross"), 'albatross')
174        eq(_("mullusk"), 'bacon')
175        eq(_(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
176        eq(_(r"nudge nudge"), 'wink wink')
177
178    def test_triple_single_quotes(self):
179        eq = self.assertEqual
180        # triple single quotes
181        eq(_('''albatross'''), 'albatross')
182        eq(_('''mullusk'''), 'bacon')
183        eq(_(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
184        eq(_(r'''nudge nudge'''), 'wink wink')
185
186    def test_triple_double_quotes(self):
187        eq = self.assertEqual
188        # triple double quotes
189        eq(_("""albatross"""), 'albatross')
190        eq(_("""mullusk"""), 'bacon')
191        eq(_(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
192        eq(_(r"""nudge nudge"""), 'wink wink')
193
194    def test_multiline_strings(self):
195        eq = self.assertEqual
196        # multiline strings
197        eq(_('''This module provides internationalization and localization
198support for your Python programs by providing an interface to the GNU
199gettext message catalog library.'''),
200           '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
201fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
202trggrkg zrffntr pngnybt yvoenel.''')
203
204    def test_the_alternative_interface(self):
205        eq = self.assertEqual
206        # test the alternative interface
207        with open(self.mofile, 'rb') as fp:
208            t = gettext.GNUTranslations(fp)
209        # Install the translation object
210        t.install()
211        eq(_('nudge nudge'), 'wink wink')
212        # Try unicode return type
213        t.install()
214        eq(_('mullusk'), 'bacon')
215        # Test installation of other methods
216        import builtins
217        t.install(names=["gettext", "lgettext"])
218        eq(_, t.gettext)
219        eq(builtins.gettext, t.gettext)
220        eq(lgettext, t.lgettext)
221        del builtins.gettext
222        del builtins.lgettext
223
224
225class GettextTestCase2(GettextBaseTest):
226    def setUp(self):
227        GettextBaseTest.setUp(self)
228        self.localedir = os.curdir
229        # Set up the bindings
230        gettext.bindtextdomain('gettext', self.localedir)
231        gettext.textdomain('gettext')
232        # For convenience
233        self._ = gettext.gettext
234
235    def test_bindtextdomain(self):
236        self.assertEqual(gettext.bindtextdomain('gettext'), self.localedir)
237
238    def test_textdomain(self):
239        self.assertEqual(gettext.textdomain(), 'gettext')
240
241    def test_bad_major_version(self):
242        with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp:
243            with self.assertRaises(OSError) as cm:
244                gettext.GNUTranslations(fp)
245
246            exception = cm.exception
247            self.assertEqual(exception.errno, 0)
248            self.assertEqual(exception.strerror, "Bad version number 5")
249            self.assertEqual(exception.filename, MOFILE_BAD_MAJOR_VERSION)
250
251    def test_bad_minor_version(self):
252        with open(MOFILE_BAD_MINOR_VERSION, 'rb') as fp:
253            # Check that no error is thrown with a bad minor version number
254            gettext.GNUTranslations(fp)
255
256    def test_some_translations(self):
257        eq = self.assertEqual
258        # test some translations
259        eq(self._('albatross'), 'albatross')
260        eq(self._('mullusk'), 'bacon')
261        eq(self._(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove')
262        eq(self._(r'nudge nudge'), 'wink wink')
263
264    def test_some_translations_with_context(self):
265        eq = self.assertEqual
266        eq(gettext.pgettext('my context', 'nudge nudge'),
267           'wink wink (in "my context")')
268        eq(gettext.pgettext('my other context', 'nudge nudge'),
269           'wink wink (in "my other context")')
270
271    def test_some_translations_with_context_and_domain(self):
272        eq = self.assertEqual
273        eq(gettext.dpgettext('gettext', 'my context', 'nudge nudge'),
274           'wink wink (in "my context")')
275        eq(gettext.dpgettext('gettext', 'my other context', 'nudge nudge'),
276           'wink wink (in "my other context")')
277
278    def test_double_quotes(self):
279        eq = self.assertEqual
280        # double quotes
281        eq(self._("albatross"), 'albatross')
282        eq(self._("mullusk"), 'bacon')
283        eq(self._(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove')
284        eq(self._(r"nudge nudge"), 'wink wink')
285
286    def test_triple_single_quotes(self):
287        eq = self.assertEqual
288        # triple single quotes
289        eq(self._('''albatross'''), 'albatross')
290        eq(self._('''mullusk'''), 'bacon')
291        eq(self._(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove')
292        eq(self._(r'''nudge nudge'''), 'wink wink')
293
294    def test_triple_double_quotes(self):
295        eq = self.assertEqual
296        # triple double quotes
297        eq(self._("""albatross"""), 'albatross')
298        eq(self._("""mullusk"""), 'bacon')
299        eq(self._(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove')
300        eq(self._(r"""nudge nudge"""), 'wink wink')
301
302    def test_multiline_strings(self):
303        eq = self.assertEqual
304        # multiline strings
305        eq(self._('''This module provides internationalization and localization
306support for your Python programs by providing an interface to the GNU
307gettext message catalog library.'''),
308           '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba
309fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH
310trggrkg zrffntr pngnybt yvoenel.''')
311
312
313class PluralFormsTestCase(GettextBaseTest):
314    def setUp(self):
315        GettextBaseTest.setUp(self)
316        self.mofile = MOFILE
317
318    def test_plural_forms1(self):
319        eq = self.assertEqual
320        x = gettext.ngettext('There is %s file', 'There are %s files', 1)
321        eq(x, 'Hay %s fichero')
322        x = gettext.ngettext('There is %s file', 'There are %s files', 2)
323        eq(x, 'Hay %s ficheros')
324
325    def test_plural_context_forms1(self):
326        eq = self.assertEqual
327        x = gettext.npgettext('With context',
328                              'There is %s file', 'There are %s files', 1)
329        eq(x, 'Hay %s fichero (context)')
330        x = gettext.npgettext('With context',
331                              'There is %s file', 'There are %s files', 2)
332        eq(x, 'Hay %s ficheros (context)')
333
334    def test_plural_forms2(self):
335        eq = self.assertEqual
336        with open(self.mofile, 'rb') as fp:
337            t = gettext.GNUTranslations(fp)
338        x = t.ngettext('There is %s file', 'There are %s files', 1)
339        eq(x, 'Hay %s fichero')
340        x = t.ngettext('There is %s file', 'There are %s files', 2)
341        eq(x, 'Hay %s ficheros')
342
343    def test_plural_context_forms2(self):
344        eq = self.assertEqual
345        with open(self.mofile, 'rb') as fp:
346            t = gettext.GNUTranslations(fp)
347        x = t.npgettext('With context',
348                        'There is %s file', 'There are %s files', 1)
349        eq(x, 'Hay %s fichero (context)')
350        x = t.npgettext('With context',
351                        'There is %s file', 'There are %s files', 2)
352        eq(x, 'Hay %s ficheros (context)')
353
354    # Examples from http://www.gnu.org/software/gettext/manual/gettext.html
355
356    def test_ja(self):
357        eq = self.assertEqual
358        f = gettext.c2py('0')
359        s = ''.join([ str(f(x)) for x in range(200) ])
360        eq(s, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
361
362    def test_de(self):
363        eq = self.assertEqual
364        f = gettext.c2py('n != 1')
365        s = ''.join([ str(f(x)) for x in range(200) ])
366        eq(s, "10111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
367
368    def test_fr(self):
369        eq = self.assertEqual
370        f = gettext.c2py('n>1')
371        s = ''.join([ str(f(x)) for x in range(200) ])
372        eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
373
374    def test_lv(self):
375        eq = self.assertEqual
376        f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2')
377        s = ''.join([ str(f(x)) for x in range(200) ])
378        eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111")
379
380    def test_gd(self):
381        eq = self.assertEqual
382        f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2')
383        s = ''.join([ str(f(x)) for x in range(200) ])
384        eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
385
386    def test_gd2(self):
387        eq = self.assertEqual
388        # Tests the combination of parentheses and "?:"
389        f = gettext.c2py('n==1 ? 0 : (n==2 ? 1 : 2)')
390        s = ''.join([ str(f(x)) for x in range(200) ])
391        eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
392
393    def test_ro(self):
394        eq = self.assertEqual
395        f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2')
396        s = ''.join([ str(f(x)) for x in range(200) ])
397        eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222")
398
399    def test_lt(self):
400        eq = self.assertEqual
401        f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2')
402        s = ''.join([ str(f(x)) for x in range(200) ])
403        eq(s, "20111111112222222222201111111120111111112011111111201111111120111111112011111111201111111120111111112011111111222222222220111111112011111111201111111120111111112011111111201111111120111111112011111111")
404
405    def test_ru(self):
406        eq = self.assertEqual
407        f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
408        s = ''.join([ str(f(x)) for x in range(200) ])
409        eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222")
410
411    def test_cs(self):
412        eq = self.assertEqual
413        f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2')
414        s = ''.join([ str(f(x)) for x in range(200) ])
415        eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
416
417    def test_pl(self):
418        eq = self.assertEqual
419        f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2')
420        s = ''.join([ str(f(x)) for x in range(200) ])
421        eq(s, "20111222222222222222221112222222111222222211122222221112222222111222222211122222221112222222111222222211122222222222222222111222222211122222221112222222111222222211122222221112222222111222222211122222")
422
423    def test_sl(self):
424        eq = self.assertEqual
425        f = gettext.c2py('n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3')
426        s = ''.join([ str(f(x)) for x in range(200) ])
427        eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
428
429    def test_ar(self):
430        eq = self.assertEqual
431        f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5')
432        s = ''.join([ str(f(x)) for x in range(200) ])
433        eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
434
435    def test_security(self):
436        raises = self.assertRaises
437        # Test for a dangerous expression
438        raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)")
439        # issue28563
440        raises(ValueError, gettext.c2py, '"(eval(foo) && ""')
441        raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"')
442        # Maximum recursion depth exceeded during compilation
443        raises(ValueError, gettext.c2py, 'n+'*10000 + 'n')
444        self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101)
445        # MemoryError during compilation
446        raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100)
447        # Maximum recursion depth exceeded in C to Python translator
448        raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000)
449        self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1)
450
451    def test_chained_comparison(self):
452        # C doesn't chain comparison as Python so 2 == 2 == 2 gets different results
453        f = gettext.c2py('n == n == n')
454        self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
455        f = gettext.c2py('1 < n == n')
456        self.assertEqual(''.join(str(f(x)) for x in range(3)), '100')
457        f = gettext.c2py('n == n < 2')
458        self.assertEqual(''.join(str(f(x)) for x in range(3)), '010')
459        f = gettext.c2py('0 < n < 2')
460        self.assertEqual(''.join(str(f(x)) for x in range(3)), '111')
461
462    def test_decimal_number(self):
463        self.assertEqual(gettext.c2py('0123')(1), 123)
464
465    def test_invalid_syntax(self):
466        invalid_expressions = [
467            'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2',
468            'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n',
469        ]
470        for expr in invalid_expressions:
471            with self.assertRaises(ValueError):
472                gettext.c2py(expr)
473
474    def test_nested_condition_operator(self):
475        self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4)
476        self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2)
477        self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4)
478        self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1)
479
480    def test_division(self):
481        f = gettext.c2py('2/n*3')
482        self.assertEqual(f(1), 6)
483        self.assertEqual(f(2), 3)
484        self.assertEqual(f(3), 0)
485        self.assertEqual(f(-1), -6)
486        self.assertRaises(ZeroDivisionError, f, 0)
487
488    def test_plural_number(self):
489        f = gettext.c2py('n != 1')
490        self.assertEqual(f(1), 0)
491        self.assertEqual(f(2), 1)
492        with self.assertWarns(DeprecationWarning):
493            self.assertEqual(f(1.0), 0)
494        with self.assertWarns(DeprecationWarning):
495            self.assertEqual(f(2.0), 1)
496        with self.assertWarns(DeprecationWarning):
497            self.assertEqual(f(1.1), 1)
498        self.assertRaises(TypeError, f, '2')
499        self.assertRaises(TypeError, f, b'2')
500        self.assertRaises(TypeError, f, [])
501        self.assertRaises(TypeError, f, object())
502
503
504class LGettextTestCase(GettextBaseTest):
505    def setUp(self):
506        GettextBaseTest.setUp(self)
507        self.mofile = MOFILE
508
509    @contextlib.contextmanager
510    def assertDeprecated(self, name):
511        with self.assertWarnsRegex(DeprecationWarning,
512                                   fr'^{name}\(\) is deprecated'):
513            yield
514
515    def test_lgettext(self):
516        lgettext = gettext.lgettext
517        ldgettext = gettext.ldgettext
518        with self.assertDeprecated('lgettext'):
519            self.assertEqual(lgettext('mullusk'), b'bacon')
520        with self.assertDeprecated('lgettext'):
521            self.assertEqual(lgettext('spam'), b'spam')
522        with self.assertDeprecated('ldgettext'):
523            self.assertEqual(ldgettext('gettext', 'mullusk'), b'bacon')
524        with self.assertDeprecated('ldgettext'):
525            self.assertEqual(ldgettext('gettext', 'spam'), b'spam')
526
527    def test_lgettext_2(self):
528        with open(self.mofile, 'rb') as fp:
529            t = gettext.GNUTranslations(fp)
530        lgettext = t.lgettext
531        with self.assertDeprecated('lgettext'):
532            self.assertEqual(lgettext('mullusk'), b'bacon')
533        with self.assertDeprecated('lgettext'):
534            self.assertEqual(lgettext('spam'), b'spam')
535
536    def test_lgettext_bind_textdomain_codeset(self):
537        lgettext = gettext.lgettext
538        ldgettext = gettext.ldgettext
539        with self.assertDeprecated('bind_textdomain_codeset'):
540            saved_codeset = gettext.bind_textdomain_codeset('gettext')
541        try:
542            with self.assertDeprecated('bind_textdomain_codeset'):
543                gettext.bind_textdomain_codeset('gettext', 'utf-16')
544            with self.assertDeprecated('lgettext'):
545                self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
546            with self.assertDeprecated('lgettext'):
547                self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
548            with self.assertDeprecated('ldgettext'):
549                self.assertEqual(ldgettext('gettext', 'mullusk'), 'bacon'.encode('utf-16'))
550            with self.assertDeprecated('ldgettext'):
551                self.assertEqual(ldgettext('gettext', 'spam'), 'spam'.encode('utf-16'))
552        finally:
553            del gettext._localecodesets['gettext']
554            with self.assertDeprecated('bind_textdomain_codeset'):
555                gettext.bind_textdomain_codeset('gettext', saved_codeset)
556
557    def test_lgettext_output_encoding(self):
558        with open(self.mofile, 'rb') as fp:
559            t = gettext.GNUTranslations(fp)
560        lgettext = t.lgettext
561        with self.assertDeprecated('set_output_charset'):
562            t.set_output_charset('utf-16')
563        with self.assertDeprecated('lgettext'):
564            self.assertEqual(lgettext('mullusk'), 'bacon'.encode('utf-16'))
565        with self.assertDeprecated('lgettext'):
566            self.assertEqual(lgettext('spam'), 'spam'.encode('utf-16'))
567
568    def test_lngettext(self):
569        lngettext = gettext.lngettext
570        ldngettext = gettext.ldngettext
571        with self.assertDeprecated('lngettext'):
572            x = lngettext('There is %s file', 'There are %s files', 1)
573        self.assertEqual(x, b'Hay %s fichero')
574        with self.assertDeprecated('lngettext'):
575            x = lngettext('There is %s file', 'There are %s files', 2)
576        self.assertEqual(x, b'Hay %s ficheros')
577        with self.assertDeprecated('lngettext'):
578            x = lngettext('There is %s directory', 'There are %s directories', 1)
579        self.assertEqual(x, b'There is %s directory')
580        with self.assertDeprecated('lngettext'):
581            x = lngettext('There is %s directory', 'There are %s directories', 2)
582        self.assertEqual(x, b'There are %s directories')
583        with self.assertDeprecated('ldngettext'):
584            x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
585        self.assertEqual(x, b'Hay %s fichero')
586        with self.assertDeprecated('ldngettext'):
587            x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
588        self.assertEqual(x, b'Hay %s ficheros')
589        with self.assertDeprecated('ldngettext'):
590            x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
591        self.assertEqual(x, b'There is %s directory')
592        with self.assertDeprecated('ldngettext'):
593            x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
594        self.assertEqual(x, b'There are %s directories')
595
596    def test_lngettext_2(self):
597        with open(self.mofile, 'rb') as fp:
598            t = gettext.GNUTranslations(fp)
599        lngettext = t.lngettext
600        with self.assertDeprecated('lngettext'):
601            x = lngettext('There is %s file', 'There are %s files', 1)
602        self.assertEqual(x, b'Hay %s fichero')
603        with self.assertDeprecated('lngettext'):
604            x = lngettext('There is %s file', 'There are %s files', 2)
605        self.assertEqual(x, b'Hay %s ficheros')
606        with self.assertDeprecated('lngettext'):
607            x = lngettext('There is %s directory', 'There are %s directories', 1)
608        self.assertEqual(x, b'There is %s directory')
609        with self.assertDeprecated('lngettext'):
610            x = lngettext('There is %s directory', 'There are %s directories', 2)
611        self.assertEqual(x, b'There are %s directories')
612
613    def test_lngettext_bind_textdomain_codeset(self):
614        lngettext = gettext.lngettext
615        ldngettext = gettext.ldngettext
616        with self.assertDeprecated('bind_textdomain_codeset'):
617            saved_codeset = gettext.bind_textdomain_codeset('gettext')
618        try:
619            with self.assertDeprecated('bind_textdomain_codeset'):
620                gettext.bind_textdomain_codeset('gettext', 'utf-16')
621            with self.assertDeprecated('lngettext'):
622                x = lngettext('There is %s file', 'There are %s files', 1)
623            self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
624            with self.assertDeprecated('lngettext'):
625                x = lngettext('There is %s file', 'There are %s files', 2)
626            self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
627            with self.assertDeprecated('lngettext'):
628                x = lngettext('There is %s directory', 'There are %s directories', 1)
629            self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
630            with self.assertDeprecated('lngettext'):
631                x = lngettext('There is %s directory', 'There are %s directories', 2)
632            self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
633            with self.assertDeprecated('ldngettext'):
634                x = ldngettext('gettext', 'There is %s file', 'There are %s files', 1)
635            self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
636            with self.assertDeprecated('ldngettext'):
637                x = ldngettext('gettext', 'There is %s file', 'There are %s files', 2)
638            self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
639            with self.assertDeprecated('ldngettext'):
640                x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 1)
641            self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
642            with self.assertDeprecated('ldngettext'):
643                x = ldngettext('gettext', 'There is %s directory', 'There are %s directories', 2)
644            self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
645        finally:
646            del gettext._localecodesets['gettext']
647            with self.assertDeprecated('bind_textdomain_codeset'):
648                gettext.bind_textdomain_codeset('gettext', saved_codeset)
649
650    def test_lngettext_output_encoding(self):
651        with open(self.mofile, 'rb') as fp:
652            t = gettext.GNUTranslations(fp)
653        lngettext = t.lngettext
654        with self.assertDeprecated('set_output_charset'):
655            t.set_output_charset('utf-16')
656        with self.assertDeprecated('lngettext'):
657            x = lngettext('There is %s file', 'There are %s files', 1)
658        self.assertEqual(x, 'Hay %s fichero'.encode('utf-16'))
659        with self.assertDeprecated('lngettext'):
660            x = lngettext('There is %s file', 'There are %s files', 2)
661        self.assertEqual(x, 'Hay %s ficheros'.encode('utf-16'))
662        with self.assertDeprecated('lngettext'):
663            x = lngettext('There is %s directory', 'There are %s directories', 1)
664        self.assertEqual(x, 'There is %s directory'.encode('utf-16'))
665        with self.assertDeprecated('lngettext'):
666            x = lngettext('There is %s directory', 'There are %s directories', 2)
667        self.assertEqual(x, 'There are %s directories'.encode('utf-16'))
668
669    def test_output_encoding(self):
670        with open(self.mofile, 'rb') as fp:
671            t = gettext.GNUTranslations(fp)
672        with self.assertDeprecated('set_output_charset'):
673            t.set_output_charset('utf-16')
674        with self.assertDeprecated('output_charset'):
675            self.assertEqual(t.output_charset(), 'utf-16')
676
677
678class GNUTranslationParsingTest(GettextBaseTest):
679    def test_plural_form_error_issue17898(self):
680        with open(MOFILE, 'wb') as fp:
681            fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898))
682        with open(MOFILE, 'rb') as fp:
683            # If this runs cleanly, the bug is fixed.
684            t = gettext.GNUTranslations(fp)
685
686    def test_ignore_comments_in_headers_issue36239(self):
687        """Checks that comments like:
688
689            #-#-#-#-#  messages.po (EdX Studio)  #-#-#-#-#
690
691        are ignored.
692        """
693        with open(MOFILE, 'wb') as fp:
694            fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898))
695        with open(MOFILE, 'rb') as fp:
696            t = gettext.GNUTranslations(fp)
697            self.assertEqual(t.info()["plural-forms"], "nplurals=2; plural=(n != 1);")
698
699
700class UnicodeTranslationsTest(GettextBaseTest):
701    def setUp(self):
702        GettextBaseTest.setUp(self)
703        with open(UMOFILE, 'rb') as fp:
704            self.t = gettext.GNUTranslations(fp)
705        self._ = self.t.gettext
706        self.pgettext = self.t.pgettext
707
708    def test_unicode_msgid(self):
709        self.assertIsInstance(self._(''), str)
710
711    def test_unicode_msgstr(self):
712        self.assertEqual(self._('ab\xde'), '\xa4yz')
713
714    def test_unicode_context_msgstr(self):
715        t = self.pgettext('mycontext\xde', 'ab\xde')
716        self.assertTrue(isinstance(t, str))
717        self.assertEqual(t, '\xa4yz (context version)')
718
719
720class UnicodeTranslationsPluralTest(GettextBaseTest):
721    def setUp(self):
722        GettextBaseTest.setUp(self)
723        with open(MOFILE, 'rb') as fp:
724            self.t = gettext.GNUTranslations(fp)
725        self.ngettext = self.t.ngettext
726        self.npgettext = self.t.npgettext
727
728    def test_unicode_msgid(self):
729        unless = self.assertTrue
730        unless(isinstance(self.ngettext('', '', 1), str))
731        unless(isinstance(self.ngettext('', '', 2), str))
732
733    def test_unicode_context_msgid(self):
734        unless = self.assertTrue
735        unless(isinstance(self.npgettext('', '', '', 1), str))
736        unless(isinstance(self.npgettext('', '', '', 2), str))
737
738    def test_unicode_msgstr(self):
739        eq = self.assertEqual
740        unless = self.assertTrue
741        t = self.ngettext("There is %s file", "There are %s files", 1)
742        unless(isinstance(t, str))
743        eq(t, "Hay %s fichero")
744        unless(isinstance(t, str))
745        t = self.ngettext("There is %s file", "There are %s files", 5)
746        unless(isinstance(t, str))
747        eq(t, "Hay %s ficheros")
748
749    def test_unicode_msgstr_with_context(self):
750        eq = self.assertEqual
751        unless = self.assertTrue
752        t = self.npgettext("With context",
753                           "There is %s file", "There are %s files", 1)
754        unless(isinstance(t, str))
755        eq(t, "Hay %s fichero (context)")
756        t = self.npgettext("With context",
757                           "There is %s file", "There are %s files", 5)
758        unless(isinstance(t, str))
759        eq(t, "Hay %s ficheros (context)")
760
761
762class WeirdMetadataTest(GettextBaseTest):
763    def setUp(self):
764        GettextBaseTest.setUp(self)
765        with open(MMOFILE, 'rb') as fp:
766            try:
767                self.t = gettext.GNUTranslations(fp)
768            except:
769                self.tearDown()
770                raise
771
772    def test_weird_metadata(self):
773        info = self.t.info()
774        self.assertEqual(len(info), 9)
775        self.assertEqual(info['last-translator'],
776           'John Doe <jdoe@example.com>\nJane Foobar <jfoobar@example.com>')
777
778
779class DummyGNUTranslations(gettext.GNUTranslations):
780    def foo(self):
781        return 'foo'
782
783
784class GettextCacheTestCase(GettextBaseTest):
785    def test_cache(self):
786        self.localedir = os.curdir
787        self.mofile = MOFILE
788
789        self.assertEqual(len(gettext._translations), 0)
790
791        t = gettext.translation('gettext', self.localedir)
792
793        self.assertEqual(len(gettext._translations), 1)
794
795        t = gettext.translation('gettext', self.localedir,
796                                class_=DummyGNUTranslations)
797
798        self.assertEqual(len(gettext._translations), 2)
799        self.assertEqual(t.__class__, DummyGNUTranslations)
800
801        # Calling it again doesn't add to the cache
802
803        t = gettext.translation('gettext', self.localedir,
804                                class_=DummyGNUTranslations)
805
806        self.assertEqual(len(gettext._translations), 2)
807        self.assertEqual(t.__class__, DummyGNUTranslations)
808
809        # Test deprecated parameter codeset
810        with self.assertWarnsRegex(DeprecationWarning, 'parameter codeset'):
811            t = gettext.translation('gettext', self.localedir,
812                                    class_=DummyGNUTranslations,
813                                    codeset='utf-16')
814        self.assertEqual(len(gettext._translations), 2)
815        self.assertEqual(t.__class__, DummyGNUTranslations)
816        with self.assertWarns(DeprecationWarning):
817            self.assertEqual(t.output_charset(), 'utf-16')
818
819
820class MiscTestCase(unittest.TestCase):
821    def test__all__(self):
822        blacklist = {'c2py', 'ENOENT'}
823        support.check__all__(self, gettext, blacklist=blacklist)
824
825
826if __name__ == '__main__':
827    unittest.main()
828
829
830# For reference, here's the .po file used to created the GNU_MO_DATA above.
831#
832# The original version was automatically generated from the sources with
833# pygettext. Later it was manually modified to add plural forms support.
834
835b'''
836# Dummy translation for the Python test_gettext.py module.
837# Copyright (C) 2001 Python Software Foundation
838# Barry Warsaw <barry@python.org>, 2000.
839#
840msgid ""
841msgstr ""
842"Project-Id-Version: 2.0\n"
843"PO-Revision-Date: 2003-04-11 14:32-0400\n"
844"Last-Translator: J. David Ibanez <j-david@noos.fr>\n"
845"Language-Team: XX <python-dev@python.org>\n"
846"MIME-Version: 1.0\n"
847"Content-Type: text/plain; charset=iso-8859-1\n"
848"Content-Transfer-Encoding: 8bit\n"
849"Generated-By: pygettext.py 1.1\n"
850"Plural-Forms: nplurals=2; plural=n!=1;\n"
851
852#: test_gettext.py:19 test_gettext.py:25 test_gettext.py:31 test_gettext.py:37
853#: test_gettext.py:51 test_gettext.py:80 test_gettext.py:86 test_gettext.py:92
854#: test_gettext.py:98
855msgid "nudge nudge"
856msgstr "wink wink"
857
858msgctxt "my context"
859msgid "nudge nudge"
860msgstr "wink wink (in \"my context\")"
861
862msgctxt "my other context"
863msgid "nudge nudge"
864msgstr "wink wink (in \"my other context\")"
865
866#: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34
867#: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95
868msgid "albatross"
869msgstr ""
870
871#: test_gettext.py:18 test_gettext.py:24 test_gettext.py:30 test_gettext.py:36
872#: test_gettext.py:79 test_gettext.py:85 test_gettext.py:91 test_gettext.py:97
873msgid "Raymond Luxury Yach-t"
874msgstr "Throatwobbler Mangrove"
875
876#: test_gettext.py:17 test_gettext.py:23 test_gettext.py:29 test_gettext.py:35
877#: test_gettext.py:56 test_gettext.py:78 test_gettext.py:84 test_gettext.py:90
878#: test_gettext.py:96
879msgid "mullusk"
880msgstr "bacon"
881
882#: test_gettext.py:40 test_gettext.py:101
883msgid ""
884"This module provides internationalization and localization\n"
885"support for your Python programs by providing an interface to the GNU\n"
886"gettext message catalog library."
887msgstr ""
888"Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba\n"
889"fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH\n"
890"trggrkg zrffntr pngnybt yvoenel."
891
892# Manually added, as neither pygettext nor xgettext support plural forms
893# in Python.
894msgid "There is %s file"
895msgid_plural "There are %s files"
896msgstr[0] "Hay %s fichero"
897msgstr[1] "Hay %s ficheros"
898
899# Manually added, as neither pygettext nor xgettext support plural forms
900# and context in Python.
901msgctxt "With context"
902msgid "There is %s file"
903msgid_plural "There are %s files"
904msgstr[0] "Hay %s fichero (context)"
905msgstr[1] "Hay %s ficheros (context)"
906'''
907
908# Here's the second example po file example, used to generate the UMO_DATA
909# containing utf-8 encoded Unicode strings
910
911b'''
912# Dummy translation for the Python test_gettext.py module.
913# Copyright (C) 2001 Python Software Foundation
914# Barry Warsaw <barry@python.org>, 2000.
915#
916msgid ""
917msgstr ""
918"Project-Id-Version: 2.0\n"
919"PO-Revision-Date: 2003-04-11 12:42-0400\n"
920"Last-Translator: Barry A. WArsaw <barry@python.org>\n"
921"Language-Team: XX <python-dev@python.org>\n"
922"MIME-Version: 1.0\n"
923"Content-Type: text/plain; charset=utf-8\n"
924"Content-Transfer-Encoding: 7bit\n"
925"Generated-By: manually\n"
926
927#: nofile:0
928msgid "ab\xc3\x9e"
929msgstr "\xc2\xa4yz"
930
931#: nofile:1
932msgctxt "mycontext\xc3\x9e"
933msgid "ab\xc3\x9e"
934msgstr "\xc2\xa4yz (context version)"
935'''
936
937# Here's the third example po file, used to generate MMO_DATA
938
939b'''
940msgid ""
941msgstr ""
942"Project-Id-Version: No Project 0.0\n"
943"POT-Creation-Date: Wed Dec 11 07:44:15 2002\n"
944"PO-Revision-Date: 2002-08-14 01:18:58+00:00\n"
945"Last-Translator: John Doe <jdoe@example.com>\n"
946"Jane Foobar <jfoobar@example.com>\n"
947"Language-Team: xx <xx@example.com>\n"
948"MIME-Version: 1.0\n"
949"Content-Type: text/plain; charset=iso-8859-15\n"
950"Content-Transfer-Encoding: quoted-printable\n"
951"Generated-By: pygettext.py 1.3\n"
952'''
953
954#
955# messages.po, used for bug 17898
956#
957
958b'''
959# test file for http://bugs.python.org/issue17898
960msgid ""
961msgstr ""
962"Plural-Forms: nplurals=2; plural=(n != 1);\n"
963"#-#-#-#-#  messages.po (EdX Studio)  #-#-#-#-#\n"
964"Content-Type: text/plain; charset=UTF-8\n"
965'''
966