1import os 2import base64 3import gettext 4import unittest 5 6from test import support 7from test.support import os_helper 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 = os_helper.EnvironmentVarGuard() 133 self.env['LANGUAGE'] = 'xx' 134 gettext._translations.clear() 135 136 def tearDown(self): 137 self.env.__exit__() 138 del self.env 139 os_helper.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 neq = self.assertNotEqual 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", "ngettext"]) 219 eq(_, t.gettext) 220 eq(builtins.gettext, t.gettext) 221 eq(ngettext, t.ngettext) 222 neq(pgettext, t.pgettext) 223 del builtins.gettext 224 del builtins.ngettext 225 226 227class GettextTestCase2(GettextBaseTest): 228 def setUp(self): 229 GettextBaseTest.setUp(self) 230 self.localedir = os.curdir 231 # Set up the bindings 232 gettext.bindtextdomain('gettext', self.localedir) 233 gettext.textdomain('gettext') 234 # For convenience 235 self._ = gettext.gettext 236 237 def test_bindtextdomain(self): 238 self.assertEqual(gettext.bindtextdomain('gettext'), self.localedir) 239 240 def test_textdomain(self): 241 self.assertEqual(gettext.textdomain(), 'gettext') 242 243 def test_bad_major_version(self): 244 with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp: 245 with self.assertRaises(OSError) as cm: 246 gettext.GNUTranslations(fp) 247 248 exception = cm.exception 249 self.assertEqual(exception.errno, 0) 250 self.assertEqual(exception.strerror, "Bad version number 5") 251 self.assertEqual(exception.filename, MOFILE_BAD_MAJOR_VERSION) 252 253 def test_bad_minor_version(self): 254 with open(MOFILE_BAD_MINOR_VERSION, 'rb') as fp: 255 # Check that no error is thrown with a bad minor version number 256 gettext.GNUTranslations(fp) 257 258 def test_some_translations(self): 259 eq = self.assertEqual 260 # test some translations 261 eq(self._('albatross'), 'albatross') 262 eq(self._('mullusk'), 'bacon') 263 eq(self._(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove') 264 eq(self._(r'nudge nudge'), 'wink wink') 265 266 def test_some_translations_with_context(self): 267 eq = self.assertEqual 268 eq(gettext.pgettext('my context', 'nudge nudge'), 269 'wink wink (in "my context")') 270 eq(gettext.pgettext('my other context', 'nudge nudge'), 271 'wink wink (in "my other context")') 272 273 def test_some_translations_with_context_and_domain(self): 274 eq = self.assertEqual 275 eq(gettext.dpgettext('gettext', 'my context', 'nudge nudge'), 276 'wink wink (in "my context")') 277 eq(gettext.dpgettext('gettext', 'my other context', 'nudge nudge'), 278 'wink wink (in "my other context")') 279 280 def test_double_quotes(self): 281 eq = self.assertEqual 282 # double quotes 283 eq(self._("albatross"), 'albatross') 284 eq(self._("mullusk"), 'bacon') 285 eq(self._(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove') 286 eq(self._(r"nudge nudge"), 'wink wink') 287 288 def test_triple_single_quotes(self): 289 eq = self.assertEqual 290 # triple single quotes 291 eq(self._('''albatross'''), 'albatross') 292 eq(self._('''mullusk'''), 'bacon') 293 eq(self._(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove') 294 eq(self._(r'''nudge nudge'''), 'wink wink') 295 296 def test_triple_double_quotes(self): 297 eq = self.assertEqual 298 # triple double quotes 299 eq(self._("""albatross"""), 'albatross') 300 eq(self._("""mullusk"""), 'bacon') 301 eq(self._(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove') 302 eq(self._(r"""nudge nudge"""), 'wink wink') 303 304 def test_multiline_strings(self): 305 eq = self.assertEqual 306 # multiline strings 307 eq(self._('''This module provides internationalization and localization 308support for your Python programs by providing an interface to the GNU 309gettext message catalog library.'''), 310 '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba 311fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH 312trggrkg zrffntr pngnybt yvoenel.''') 313 314 315class PluralFormsTestCase(GettextBaseTest): 316 def setUp(self): 317 GettextBaseTest.setUp(self) 318 self.mofile = MOFILE 319 320 def test_plural_forms1(self): 321 eq = self.assertEqual 322 x = gettext.ngettext('There is %s file', 'There are %s files', 1) 323 eq(x, 'Hay %s fichero') 324 x = gettext.ngettext('There is %s file', 'There are %s files', 2) 325 eq(x, 'Hay %s ficheros') 326 327 def test_plural_context_forms1(self): 328 eq = self.assertEqual 329 x = gettext.npgettext('With context', 330 'There is %s file', 'There are %s files', 1) 331 eq(x, 'Hay %s fichero (context)') 332 x = gettext.npgettext('With context', 333 'There is %s file', 'There are %s files', 2) 334 eq(x, 'Hay %s ficheros (context)') 335 336 def test_plural_forms2(self): 337 eq = self.assertEqual 338 with open(self.mofile, 'rb') as fp: 339 t = gettext.GNUTranslations(fp) 340 x = t.ngettext('There is %s file', 'There are %s files', 1) 341 eq(x, 'Hay %s fichero') 342 x = t.ngettext('There is %s file', 'There are %s files', 2) 343 eq(x, 'Hay %s ficheros') 344 345 def test_plural_context_forms2(self): 346 eq = self.assertEqual 347 with open(self.mofile, 'rb') as fp: 348 t = gettext.GNUTranslations(fp) 349 x = t.npgettext('With context', 350 'There is %s file', 'There are %s files', 1) 351 eq(x, 'Hay %s fichero (context)') 352 x = t.npgettext('With context', 353 'There is %s file', 'There are %s files', 2) 354 eq(x, 'Hay %s ficheros (context)') 355 356 # Examples from http://www.gnu.org/software/gettext/manual/gettext.html 357 358 def test_ja(self): 359 eq = self.assertEqual 360 f = gettext.c2py('0') 361 s = ''.join([ str(f(x)) for x in range(200) ]) 362 eq(s, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 363 364 def test_de(self): 365 eq = self.assertEqual 366 f = gettext.c2py('n != 1') 367 s = ''.join([ str(f(x)) for x in range(200) ]) 368 eq(s, "10111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") 369 370 def test_fr(self): 371 eq = self.assertEqual 372 f = gettext.c2py('n>1') 373 s = ''.join([ str(f(x)) for x in range(200) ]) 374 eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") 375 376 def test_lv(self): 377 eq = self.assertEqual 378 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2') 379 s = ''.join([ str(f(x)) for x in range(200) ]) 380 eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111") 381 382 def test_gd(self): 383 eq = self.assertEqual 384 f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2') 385 s = ''.join([ str(f(x)) for x in range(200) ]) 386 eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 387 388 def test_gd2(self): 389 eq = self.assertEqual 390 # Tests the combination of parentheses and "?:" 391 f = gettext.c2py('n==1 ? 0 : (n==2 ? 1 : 2)') 392 s = ''.join([ str(f(x)) for x in range(200) ]) 393 eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 394 395 def test_ro(self): 396 eq = self.assertEqual 397 f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2') 398 s = ''.join([ str(f(x)) for x in range(200) ]) 399 eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222") 400 401 def test_lt(self): 402 eq = self.assertEqual 403 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2') 404 s = ''.join([ str(f(x)) for x in range(200) ]) 405 eq(s, "20111111112222222222201111111120111111112011111111201111111120111111112011111111201111111120111111112011111111222222222220111111112011111111201111111120111111112011111111201111111120111111112011111111") 406 407 def test_ru(self): 408 eq = self.assertEqual 409 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2') 410 s = ''.join([ str(f(x)) for x in range(200) ]) 411 eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222") 412 413 def test_cs(self): 414 eq = self.assertEqual 415 f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2') 416 s = ''.join([ str(f(x)) for x in range(200) ]) 417 eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 418 419 def test_pl(self): 420 eq = self.assertEqual 421 f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2') 422 s = ''.join([ str(f(x)) for x in range(200) ]) 423 eq(s, "20111222222222222222221112222222111222222211122222221112222222111222222211122222221112222222111222222211122222222222222222111222222211122222221112222222111222222211122222221112222222111222222211122222") 424 425 def test_sl(self): 426 eq = self.assertEqual 427 f = gettext.c2py('n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3') 428 s = ''.join([ str(f(x)) for x in range(200) ]) 429 eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") 430 431 def test_ar(self): 432 eq = self.assertEqual 433 f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5') 434 s = ''.join([ str(f(x)) for x in range(200) ]) 435 eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444") 436 437 def test_security(self): 438 raises = self.assertRaises 439 # Test for a dangerous expression 440 raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)") 441 # issue28563 442 raises(ValueError, gettext.c2py, '"(eval(foo) && ""') 443 raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"') 444 # Maximum recursion depth exceeded during compilation 445 raises(ValueError, gettext.c2py, 'n+'*10000 + 'n') 446 self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101) 447 # MemoryError during compilation 448 raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100) 449 # Maximum recursion depth exceeded in C to Python translator 450 raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000) 451 self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1) 452 453 def test_chained_comparison(self): 454 # C doesn't chain comparison as Python so 2 == 2 == 2 gets different results 455 f = gettext.c2py('n == n == n') 456 self.assertEqual(''.join(str(f(x)) for x in range(3)), '010') 457 f = gettext.c2py('1 < n == n') 458 self.assertEqual(''.join(str(f(x)) for x in range(3)), '100') 459 f = gettext.c2py('n == n < 2') 460 self.assertEqual(''.join(str(f(x)) for x in range(3)), '010') 461 f = gettext.c2py('0 < n < 2') 462 self.assertEqual(''.join(str(f(x)) for x in range(3)), '111') 463 464 def test_decimal_number(self): 465 self.assertEqual(gettext.c2py('0123')(1), 123) 466 467 def test_invalid_syntax(self): 468 invalid_expressions = [ 469 'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2', 470 'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n', 471 ] 472 for expr in invalid_expressions: 473 with self.assertRaises(ValueError): 474 gettext.c2py(expr) 475 476 def test_nested_condition_operator(self): 477 self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4) 478 self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2) 479 self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4) 480 self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1) 481 482 def test_division(self): 483 f = gettext.c2py('2/n*3') 484 self.assertEqual(f(1), 6) 485 self.assertEqual(f(2), 3) 486 self.assertEqual(f(3), 0) 487 self.assertEqual(f(-1), -6) 488 self.assertRaises(ZeroDivisionError, f, 0) 489 490 def test_plural_number(self): 491 f = gettext.c2py('n != 1') 492 self.assertEqual(f(1), 0) 493 self.assertEqual(f(2), 1) 494 with self.assertWarns(DeprecationWarning): 495 self.assertEqual(f(1.0), 0) 496 with self.assertWarns(DeprecationWarning): 497 self.assertEqual(f(2.0), 1) 498 with self.assertWarns(DeprecationWarning): 499 self.assertEqual(f(1.1), 1) 500 self.assertRaises(TypeError, f, '2') 501 self.assertRaises(TypeError, f, b'2') 502 self.assertRaises(TypeError, f, []) 503 self.assertRaises(TypeError, f, object()) 504 505 506class GNUTranslationParsingTest(GettextBaseTest): 507 def test_plural_form_error_issue17898(self): 508 with open(MOFILE, 'wb') as fp: 509 fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898)) 510 with open(MOFILE, 'rb') as fp: 511 # If this runs cleanly, the bug is fixed. 512 t = gettext.GNUTranslations(fp) 513 514 def test_ignore_comments_in_headers_issue36239(self): 515 """Checks that comments like: 516 517 #-#-#-#-# messages.po (EdX Studio) #-#-#-#-# 518 519 are ignored. 520 """ 521 with open(MOFILE, 'wb') as fp: 522 fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898)) 523 with open(MOFILE, 'rb') as fp: 524 t = gettext.GNUTranslations(fp) 525 self.assertEqual(t.info()["plural-forms"], "nplurals=2; plural=(n != 1);") 526 527 528class UnicodeTranslationsTest(GettextBaseTest): 529 def setUp(self): 530 GettextBaseTest.setUp(self) 531 with open(UMOFILE, 'rb') as fp: 532 self.t = gettext.GNUTranslations(fp) 533 self._ = self.t.gettext 534 self.pgettext = self.t.pgettext 535 536 def test_unicode_msgid(self): 537 self.assertIsInstance(self._(''), str) 538 539 def test_unicode_msgstr(self): 540 self.assertEqual(self._('ab\xde'), '\xa4yz') 541 542 def test_unicode_context_msgstr(self): 543 t = self.pgettext('mycontext\xde', 'ab\xde') 544 self.assertTrue(isinstance(t, str)) 545 self.assertEqual(t, '\xa4yz (context version)') 546 547 548class UnicodeTranslationsPluralTest(GettextBaseTest): 549 def setUp(self): 550 GettextBaseTest.setUp(self) 551 with open(MOFILE, 'rb') as fp: 552 self.t = gettext.GNUTranslations(fp) 553 self.ngettext = self.t.ngettext 554 self.npgettext = self.t.npgettext 555 556 def test_unicode_msgid(self): 557 unless = self.assertTrue 558 unless(isinstance(self.ngettext('', '', 1), str)) 559 unless(isinstance(self.ngettext('', '', 2), str)) 560 561 def test_unicode_context_msgid(self): 562 unless = self.assertTrue 563 unless(isinstance(self.npgettext('', '', '', 1), str)) 564 unless(isinstance(self.npgettext('', '', '', 2), str)) 565 566 def test_unicode_msgstr(self): 567 eq = self.assertEqual 568 unless = self.assertTrue 569 t = self.ngettext("There is %s file", "There are %s files", 1) 570 unless(isinstance(t, str)) 571 eq(t, "Hay %s fichero") 572 unless(isinstance(t, str)) 573 t = self.ngettext("There is %s file", "There are %s files", 5) 574 unless(isinstance(t, str)) 575 eq(t, "Hay %s ficheros") 576 577 def test_unicode_msgstr_with_context(self): 578 eq = self.assertEqual 579 unless = self.assertTrue 580 t = self.npgettext("With context", 581 "There is %s file", "There are %s files", 1) 582 unless(isinstance(t, str)) 583 eq(t, "Hay %s fichero (context)") 584 t = self.npgettext("With context", 585 "There is %s file", "There are %s files", 5) 586 unless(isinstance(t, str)) 587 eq(t, "Hay %s ficheros (context)") 588 589 590class WeirdMetadataTest(GettextBaseTest): 591 def setUp(self): 592 GettextBaseTest.setUp(self) 593 with open(MMOFILE, 'rb') as fp: 594 try: 595 self.t = gettext.GNUTranslations(fp) 596 except: 597 self.tearDown() 598 raise 599 600 def test_weird_metadata(self): 601 info = self.t.info() 602 self.assertEqual(len(info), 9) 603 self.assertEqual(info['last-translator'], 604 'John Doe <jdoe@example.com>\nJane Foobar <jfoobar@example.com>') 605 606 607class DummyGNUTranslations(gettext.GNUTranslations): 608 def foo(self): 609 return 'foo' 610 611 612class GettextCacheTestCase(GettextBaseTest): 613 def test_cache(self): 614 self.localedir = os.curdir 615 self.mofile = MOFILE 616 617 self.assertEqual(len(gettext._translations), 0) 618 619 t = gettext.translation('gettext', self.localedir) 620 621 self.assertEqual(len(gettext._translations), 1) 622 623 t = gettext.translation('gettext', self.localedir, 624 class_=DummyGNUTranslations) 625 626 self.assertEqual(len(gettext._translations), 2) 627 self.assertEqual(t.__class__, DummyGNUTranslations) 628 629 # Calling it again doesn't add to the cache 630 631 t = gettext.translation('gettext', self.localedir, 632 class_=DummyGNUTranslations) 633 634 self.assertEqual(len(gettext._translations), 2) 635 self.assertEqual(t.__class__, DummyGNUTranslations) 636 637 638class MiscTestCase(unittest.TestCase): 639 def test__all__(self): 640 support.check__all__(self, gettext, 641 not_exported={'c2py', 'ENOENT'}) 642 643 644if __name__ == '__main__': 645 unittest.main() 646 647 648# For reference, here's the .po file used to created the GNU_MO_DATA above. 649# 650# The original version was automatically generated from the sources with 651# pygettext. Later it was manually modified to add plural forms support. 652 653b''' 654# Dummy translation for the Python test_gettext.py module. 655# Copyright (C) 2001 Python Software Foundation 656# Barry Warsaw <barry@python.org>, 2000. 657# 658msgid "" 659msgstr "" 660"Project-Id-Version: 2.0\n" 661"PO-Revision-Date: 2003-04-11 14:32-0400\n" 662"Last-Translator: J. David Ibanez <j-david@noos.fr>\n" 663"Language-Team: XX <python-dev@python.org>\n" 664"MIME-Version: 1.0\n" 665"Content-Type: text/plain; charset=iso-8859-1\n" 666"Content-Transfer-Encoding: 8bit\n" 667"Generated-By: pygettext.py 1.1\n" 668"Plural-Forms: nplurals=2; plural=n!=1;\n" 669 670#: test_gettext.py:19 test_gettext.py:25 test_gettext.py:31 test_gettext.py:37 671#: test_gettext.py:51 test_gettext.py:80 test_gettext.py:86 test_gettext.py:92 672#: test_gettext.py:98 673msgid "nudge nudge" 674msgstr "wink wink" 675 676msgctxt "my context" 677msgid "nudge nudge" 678msgstr "wink wink (in \"my context\")" 679 680msgctxt "my other context" 681msgid "nudge nudge" 682msgstr "wink wink (in \"my other context\")" 683 684#: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34 685#: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95 686msgid "albatross" 687msgstr "" 688 689#: test_gettext.py:18 test_gettext.py:24 test_gettext.py:30 test_gettext.py:36 690#: test_gettext.py:79 test_gettext.py:85 test_gettext.py:91 test_gettext.py:97 691msgid "Raymond Luxury Yach-t" 692msgstr "Throatwobbler Mangrove" 693 694#: test_gettext.py:17 test_gettext.py:23 test_gettext.py:29 test_gettext.py:35 695#: test_gettext.py:56 test_gettext.py:78 test_gettext.py:84 test_gettext.py:90 696#: test_gettext.py:96 697msgid "mullusk" 698msgstr "bacon" 699 700#: test_gettext.py:40 test_gettext.py:101 701msgid "" 702"This module provides internationalization and localization\n" 703"support for your Python programs by providing an interface to the GNU\n" 704"gettext message catalog library." 705msgstr "" 706"Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba\n" 707"fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH\n" 708"trggrkg zrffntr pngnybt yvoenel." 709 710# Manually added, as neither pygettext nor xgettext support plural forms 711# in Python. 712msgid "There is %s file" 713msgid_plural "There are %s files" 714msgstr[0] "Hay %s fichero" 715msgstr[1] "Hay %s ficheros" 716 717# Manually added, as neither pygettext nor xgettext support plural forms 718# and context in Python. 719msgctxt "With context" 720msgid "There is %s file" 721msgid_plural "There are %s files" 722msgstr[0] "Hay %s fichero (context)" 723msgstr[1] "Hay %s ficheros (context)" 724''' 725 726# Here's the second example po file example, used to generate the UMO_DATA 727# containing utf-8 encoded Unicode strings 728 729b''' 730# Dummy translation for the Python test_gettext.py module. 731# Copyright (C) 2001 Python Software Foundation 732# Barry Warsaw <barry@python.org>, 2000. 733# 734msgid "" 735msgstr "" 736"Project-Id-Version: 2.0\n" 737"PO-Revision-Date: 2003-04-11 12:42-0400\n" 738"Last-Translator: Barry A. WArsaw <barry@python.org>\n" 739"Language-Team: XX <python-dev@python.org>\n" 740"MIME-Version: 1.0\n" 741"Content-Type: text/plain; charset=utf-8\n" 742"Content-Transfer-Encoding: 7bit\n" 743"Generated-By: manually\n" 744 745#: nofile:0 746msgid "ab\xc3\x9e" 747msgstr "\xc2\xa4yz" 748 749#: nofile:1 750msgctxt "mycontext\xc3\x9e" 751msgid "ab\xc3\x9e" 752msgstr "\xc2\xa4yz (context version)" 753''' 754 755# Here's the third example po file, used to generate MMO_DATA 756 757b''' 758msgid "" 759msgstr "" 760"Project-Id-Version: No Project 0.0\n" 761"POT-Creation-Date: Wed Dec 11 07:44:15 2002\n" 762"PO-Revision-Date: 2002-08-14 01:18:58+00:00\n" 763"Last-Translator: John Doe <jdoe@example.com>\n" 764"Jane Foobar <jfoobar@example.com>\n" 765"Language-Team: xx <xx@example.com>\n" 766"MIME-Version: 1.0\n" 767"Content-Type: text/plain; charset=iso-8859-15\n" 768"Content-Transfer-Encoding: quoted-printable\n" 769"Generated-By: pygettext.py 1.3\n" 770''' 771 772# 773# messages.po, used for bug 17898 774# 775 776b''' 777# test file for http://bugs.python.org/issue17898 778msgid "" 779msgstr "" 780"Plural-Forms: nplurals=2; plural=(n != 1);\n" 781"#-#-#-#-# messages.po (EdX Studio) #-#-#-#-#\n" 782"Content-Type: text/plain; charset=UTF-8\n" 783''' 784