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