1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2007-2011 Edgewall Software
6
# This software is licensed as described in the file COPYING, which
7
# you should have received as part of this distribution. The terms
8
# are also available at http://babel.edgewall.org/wiki/License.
10
# This software consists of voluntary contributions made by many
11
# individuals. For the exact contribution history, see the revision
12
# history and logs, available at http://babel.edgewall.org/log/.
18
from babel.dates import format_datetime, UTC
19
from babel.messages import catalog
20
from babel.util import FixedOffsetTimezone
23
class MessageTestCase(unittest.TestCase):
25
def test_python_format(self):
26
assert catalog.PYTHON_FORMAT.search('foo %d bar')
27
assert catalog.PYTHON_FORMAT.search('foo %s bar')
28
assert catalog.PYTHON_FORMAT.search('foo %r bar')
29
assert catalog.PYTHON_FORMAT.search('foo %(name).1f')
30
assert catalog.PYTHON_FORMAT.search('foo %(name)3.3f')
31
assert catalog.PYTHON_FORMAT.search('foo %(name)3f')
32
assert catalog.PYTHON_FORMAT.search('foo %(name)06d')
33
assert catalog.PYTHON_FORMAT.search('foo %(name)Li')
34
assert catalog.PYTHON_FORMAT.search('foo %(name)#d')
35
assert catalog.PYTHON_FORMAT.search('foo %(name)-4.4hs')
36
assert catalog.PYTHON_FORMAT.search('foo %(name)*.3f')
37
assert catalog.PYTHON_FORMAT.search('foo %(name).*f')
38
assert catalog.PYTHON_FORMAT.search('foo %(name)3.*f')
39
assert catalog.PYTHON_FORMAT.search('foo %(name)*.*f')
40
assert catalog.PYTHON_FORMAT.search('foo %()s')
42
def test_translator_comments(self):
43
mess = catalog.Message('foo', user_comments=['Comment About `foo`'])
44
self.assertEqual(mess.user_comments, ['Comment About `foo`'])
45
mess = catalog.Message('foo',
46
auto_comments=['Comment 1 About `foo`',
47
'Comment 2 About `foo`'])
48
self.assertEqual(mess.auto_comments, ['Comment 1 About `foo`',
49
'Comment 2 About `foo`'])
51
def test_clone_message_object(self):
52
msg = catalog.Message('foo', locations=[('foo.py', 42)])
54
clone.locations.append(('bar.py', 42))
55
self.assertEqual(msg.locations, [('foo.py', 42)])
56
msg.flags.add('fuzzy')
57
assert not clone.fuzzy and msg.fuzzy
60
class CatalogTestCase(unittest.TestCase):
61
def test_add_returns_message_instance(self):
62
cat = catalog.Catalog()
63
message = cat.add('foo')
64
self.assertEquals('foo', message.id)
66
def test_two_messages_with_same_singular(self):
67
cat = catalog.Catalog()
69
cat.add(('foo', 'foos'))
70
self.assertEqual(1, len(cat))
72
def test_duplicate_auto_comment(self):
73
cat = catalog.Catalog()
74
cat.add('foo', auto_comments=['A comment'])
75
cat.add('foo', auto_comments=['A comment', 'Another comment'])
76
self.assertEqual(['A comment', 'Another comment'],
77
cat['foo'].auto_comments)
79
def test_duplicate_user_comment(self):
80
cat = catalog.Catalog()
81
cat.add('foo', user_comments=['A comment'])
82
cat.add('foo', user_comments=['A comment', 'Another comment'])
83
self.assertEqual(['A comment', 'Another comment'],
84
cat['foo'].user_comments)
86
def test_duplicate_location(self):
87
cat = catalog.Catalog()
88
cat.add('foo', locations=[('foo.py', 1)])
89
cat.add('foo', locations=[('foo.py', 1)])
90
self.assertEqual([('foo.py', 1)], cat['foo'].locations)
92
def test_update_message_changed_to_plural(self):
93
cat = catalog.Catalog()
94
cat.add(u'foo', u'Voh')
95
tmpl = catalog.Catalog()
96
tmpl.add((u'foo', u'foos'))
98
self.assertEqual((u'Voh', ''), cat['foo'].string)
99
assert cat['foo'].fuzzy
101
def test_update_message_changed_to_simple(self):
102
cat = catalog.Catalog()
103
cat.add((u'foo' u'foos'), (u'Voh', u'Vöhs'))
104
tmpl = catalog.Catalog()
107
self.assertEqual(u'Voh', cat['foo'].string)
108
assert cat['foo'].fuzzy
110
def test_update_message_updates_comments(self):
111
cat = catalog.Catalog()
112
cat[u'foo'] = catalog.Message('foo', locations=[('main.py', 5)])
113
self.assertEqual(cat[u'foo'].auto_comments, [])
114
self.assertEqual(cat[u'foo'].user_comments, [])
115
# Update cat[u'foo'] with a new location and a comment
116
cat[u'foo'] = catalog.Message('foo', locations=[('main.py', 7)],
117
user_comments=['Foo Bar comment 1'])
118
self.assertEqual(cat[u'foo'].user_comments, ['Foo Bar comment 1'])
119
# now add yet another location with another comment
120
cat[u'foo'] = catalog.Message('foo', locations=[('main.py', 9)],
121
auto_comments=['Foo Bar comment 2'])
122
self.assertEqual(cat[u'foo'].auto_comments, ['Foo Bar comment 2'])
124
def test_update_fuzzy_matching_with_case_change(self):
125
cat = catalog.Catalog()
126
cat.add('foo', 'Voh')
127
cat.add('bar', 'Bahr')
128
tmpl = catalog.Catalog()
131
self.assertEqual(1, len(cat.obsolete))
132
assert 'foo' not in cat
134
self.assertEqual('Voh', cat['Foo'].string)
135
self.assertEqual(True, cat['Foo'].fuzzy)
137
def test_update_fuzzy_matching_with_char_change(self):
138
cat = catalog.Catalog()
140
cat.add('bar', 'Bahr')
141
tmpl = catalog.Catalog()
144
self.assertEqual(1, len(cat.obsolete))
145
assert 'fo' not in cat
147
self.assertEqual('Voh', cat['foo'].string)
148
self.assertEqual(True, cat['foo'].fuzzy)
150
def test_update_fuzzy_matching_no_msgstr(self):
151
cat = catalog.Catalog()
153
tmpl = catalog.Catalog()
160
self.assertEqual('', cat['fo'].string)
161
self.assertEqual(False, cat['fo'].fuzzy)
162
self.assertEqual(None, cat['foo'].string)
163
self.assertEqual(False, cat['foo'].fuzzy)
165
def test_update_fuzzy_matching_with_new_context(self):
166
cat = catalog.Catalog()
167
cat.add('foo', 'Voh')
168
cat.add('bar', 'Bahr')
169
tmpl = catalog.Catalog()
170
tmpl.add('Foo', context='Menu')
172
self.assertEqual(1, len(cat.obsolete))
173
assert 'foo' not in cat
175
message = cat.get('Foo', 'Menu')
176
self.assertEqual('Voh', message.string)
177
self.assertEqual(True, message.fuzzy)
178
self.assertEqual('Menu', message.context)
180
def test_update_fuzzy_matching_with_changed_context(self):
181
cat = catalog.Catalog()
182
cat.add('foo', 'Voh', context='Menu|File')
183
cat.add('bar', 'Bahr', context='Menu|File')
184
tmpl = catalog.Catalog()
185
tmpl.add('Foo', context='Menu|Edit')
187
self.assertEqual(1, len(cat.obsolete))
188
assert cat.get('Foo', 'Menu|File') is None
190
message = cat.get('Foo', 'Menu|Edit')
191
self.assertEqual('Voh', message.string)
192
self.assertEqual(True, message.fuzzy)
193
self.assertEqual('Menu|Edit', message.context)
195
def test_update_fuzzy_matching_no_cascading(self):
196
cat = catalog.Catalog()
198
cat.add('foo', 'Vohe')
199
tmpl = catalog.Catalog()
207
self.assertEqual('Voh', cat['fo'].string)
208
self.assertEqual(False, cat['fo'].fuzzy)
209
self.assertEqual('Vohe', cat['foo'].string)
210
self.assertEqual(False, cat['foo'].fuzzy)
211
self.assertEqual('Vohe', cat['fooo'].string)
212
self.assertEqual(True, cat['fooo'].fuzzy)
214
def test_update_without_fuzzy_matching(self):
215
cat = catalog.Catalog()
217
cat.add('bar', 'Bahr')
218
tmpl = catalog.Catalog()
220
cat.update(tmpl, no_fuzzy_matching=True)
221
self.assertEqual(2, len(cat.obsolete))
223
def test_fuzzy_matching_regarding_plurals(self):
224
cat = catalog.Catalog()
225
cat.add(('foo', 'foh'), ('foo', 'foh'))
229
self.assertEqual(True, ru['foo'].fuzzy)
232
ru['foo'].string = ('foh', 'fohh', 'fohhh')
234
self.assertEqual(False, ru['foo'].fuzzy)
236
def test_update_no_template_mutation(self):
237
tmpl = catalog.Catalog()
239
cat1 = catalog.Catalog()
240
cat1.add('foo', 'Voh')
242
cat2 = catalog.Catalog()
245
self.assertEqual(None, cat2['foo'].string)
246
self.assertEqual(False, cat2['foo'].fuzzy)
248
def test_update_po_updates_pot_creation_date(self):
249
template = catalog.Catalog()
250
localized_catalog = copy.deepcopy(template)
251
localized_catalog.locale = 'de_DE'
252
self.assertNotEqual(template.mime_headers,
253
localized_catalog.mime_headers)
254
self.assertEqual(template.creation_date,
255
localized_catalog.creation_date)
256
template.creation_date = datetime.datetime.now() - \
257
datetime.timedelta(minutes=5)
258
localized_catalog.update(template)
259
self.assertEqual(template.creation_date,
260
localized_catalog.creation_date)
262
def test_update_po_keeps_po_revision_date(self):
263
template = catalog.Catalog()
264
localized_catalog = copy.deepcopy(template)
265
localized_catalog.locale = 'de_DE'
266
fake_rev_date = datetime.datetime.now() - datetime.timedelta(days=5)
267
localized_catalog.revision_date = fake_rev_date
268
self.assertNotEqual(template.mime_headers,
269
localized_catalog.mime_headers)
270
self.assertEqual(template.creation_date,
271
localized_catalog.creation_date)
272
template.creation_date = datetime.datetime.now() - \
273
datetime.timedelta(minutes=5)
274
localized_catalog.update(template)
275
self.assertEqual(localized_catalog.revision_date, fake_rev_date)
277
def test_stores_datetime_correctly(self):
278
localized = catalog.Catalog()
279
localized.locale = 'de_DE'
280
localized[''] = catalog.Message('',
281
"POT-Creation-Date: 2009-03-09 15:47-0700\n" +
282
"PO-Revision-Date: 2009-03-09 15:47-0700\n")
283
for key, value in localized.mime_headers:
284
if key in ('POT-Creation-Date', 'PO-Revision-Date'):
285
self.assertEqual(value, '2009-03-09 15:47-0700')
287
def test_mime_headers_contain_same_information_as_attributes(self):
288
cat = catalog.Catalog()
289
cat[''] = catalog.Message('',
290
"Last-Translator: Foo Bar <foo.bar@example.com>\n" +
291
"Language-Team: de <de@example.com>\n" +
292
"POT-Creation-Date: 2009-03-01 11:20+0200\n" +
293
"PO-Revision-Date: 2009-03-09 15:47-0700\n")
294
self.assertEqual(None, cat.locale)
295
mime_headers = dict(cat.mime_headers)
297
self.assertEqual('Foo Bar <foo.bar@example.com>', cat.last_translator)
298
self.assertEqual('Foo Bar <foo.bar@example.com>',
299
mime_headers['Last-Translator'])
301
self.assertEqual('de <de@example.com>', cat.language_team)
302
self.assertEqual('de <de@example.com>', mime_headers['Language-Team'])
304
dt = datetime.datetime(2009, 3, 9, 15, 47, tzinfo=FixedOffsetTimezone(-7 * 60))
305
self.assertEqual(dt, cat.revision_date)
306
formatted_dt = format_datetime(dt, 'yyyy-MM-dd HH:mmZ', locale='en')
307
self.assertEqual(formatted_dt, mime_headers['PO-Revision-Date'])
310
def test_message_fuzzy():
311
assert not catalog.Message('foo').fuzzy
312
msg = catalog.Message('foo', 'foo', flags=['fuzzy'])
314
assert msg.id == 'foo'
316
def test_message_pluralizable():
317
assert not catalog.Message('foo').pluralizable
318
assert catalog.Message(('foo', 'bar')).pluralizable
320
def test_message_python_format():
321
assert catalog.Message('foo %(name)s bar').python_format
322
assert catalog.Message(('foo %(name)s', 'foo %(name)s')).python_format
326
cat = catalog.Catalog(project='Foobar', version='1.0',
327
copyright_holder='Foo Company')
328
assert cat.header_comment == (
329
'# Translations template for Foobar.\n'
330
'# Copyright (C) %(year)d Foo Company\n'
331
'# This file is distributed under the same '
332
'license as the Foobar project.\n'
333
'# FIRST AUTHOR <EMAIL@ADDRESS>, %(year)d.\n'
334
'#') % {'year': datetime.date.today().year}
336
cat = catalog.Catalog(project='Foobar', version='1.0',
337
copyright_holder='Foo Company')
338
cat.header_comment = (
339
'# The POT for my really cool PROJECT project.\n'
340
'# Copyright (C) 1990-2003 ORGANIZATION\n'
341
'# This file is distributed under the same license as the PROJECT\n'
344
assert cat.header_comment == (
345
'# The POT for my really cool Foobar project.\n'
346
'# Copyright (C) 1990-2003 Foo Company\n'
347
'# This file is distributed under the same license as the Foobar\n'
352
def test_catalog_mime_headers():
353
created = datetime.datetime(1990, 4, 1, 15, 30, tzinfo=UTC)
354
cat = catalog.Catalog(project='Foobar', version='1.0',
355
creation_date=created)
356
assert cat.mime_headers == [
357
('Project-Id-Version', 'Foobar 1.0'),
358
('Report-Msgid-Bugs-To', 'EMAIL@ADDRESS'),
359
('POT-Creation-Date', '1990-04-01 15:30+0000'),
360
('PO-Revision-Date', 'YEAR-MO-DA HO:MI+ZONE'),
361
('Last-Translator', 'FULL NAME <EMAIL@ADDRESS>'),
362
('Language-Team', 'LANGUAGE <LL@li.org>'),
363
('MIME-Version', '1.0'),
364
('Content-Type', 'text/plain; charset=utf-8'),
365
('Content-Transfer-Encoding', '8bit'),
366
('Generated-By', 'Babel %s\n' % catalog.VERSION),
370
def test_catalog_mime_headers_set_locale():
371
created = datetime.datetime(1990, 4, 1, 15, 30, tzinfo=UTC)
372
revised = datetime.datetime(1990, 8, 3, 12, 0, tzinfo=UTC)
373
cat = catalog.Catalog(locale='de_DE', project='Foobar', version='1.0',
374
creation_date=created, revision_date=revised,
375
last_translator='John Doe <jd@example.com>',
376
language_team='de_DE <de@example.com>')
377
assert cat.mime_headers == [
378
('Project-Id-Version', 'Foobar 1.0'),
379
('Report-Msgid-Bugs-To', 'EMAIL@ADDRESS'),
380
('POT-Creation-Date', '1990-04-01 15:30+0000'),
381
('PO-Revision-Date', '1990-08-03 12:00+0000'),
382
('Last-Translator', 'John Doe <jd@example.com>'),
383
('Language-Team', 'de_DE <de@example.com>'),
384
('Plural-Forms', 'nplurals=2; plural=(n != 1)'),
385
('MIME-Version', '1.0'),
386
('Content-Type', 'text/plain; charset=utf-8'),
387
('Content-Transfer-Encoding', '8bit'),
388
('Generated-By', 'Babel %s\n' % catalog.VERSION),
392
def test_catalog_num_plurals():
393
assert catalog.Catalog(locale='en').num_plurals == 2
394
assert catalog.Catalog(locale='ga').num_plurals == 3
397
def test_catalog_plural_expr():
398
assert catalog.Catalog(locale='en').plural_expr == '(n != 1)'
399
assert (catalog.Catalog(locale='ga').plural_expr
400
== '(n==1 ? 0 : n==2 ? 1 : 2)')
403
def test_catalog_plural_forms():
404
assert (catalog.Catalog(locale='en').plural_forms
405
== 'nplurals=2; plural=(n != 1)')
406
assert (catalog.Catalog(locale='pt_BR').plural_forms
407
== 'nplurals=2; plural=(n > 1)')
410
def test_catalog_setitem():
411
cat = catalog.Catalog()
412
cat[u'foo'] = catalog.Message(u'foo')
413
assert cat[u'foo'].id == 'foo'
415
cat = catalog.Catalog()
416
cat[u'foo'] = catalog.Message(u'foo', locations=[('main.py', 1)])
417
assert cat[u'foo'].locations == [('main.py', 1)]
418
cat[u'foo'] = catalog.Message(u'foo', locations=[('utils.py', 5)])
419
assert cat[u'foo'].locations == [('main.py', 1), ('utils.py', 5)]
422
def test_catalog_add():
423
cat = catalog.Catalog()
424
foo = cat.add(u'foo')
425
assert foo.id == 'foo'
426
assert cat[u'foo'] is foo
429
def test_catalog_update():
430
template = catalog.Catalog()
431
template.add('green', locations=[('main.py', 99)])
432
template.add('blue', locations=[('main.py', 100)])
433
template.add(('salad', 'salads'), locations=[('util.py', 42)])
434
cat = catalog.Catalog(locale='de_DE')
435
cat.add('blue', u'blau', locations=[('main.py', 98)])
436
cat.add('head', u'Kopf', locations=[('util.py', 33)])
437
cat.add(('salad', 'salads'), (u'Salat', u'Salate'),
438
locations=[('util.py', 38)])
445
assert msg1.locations == [('main.py', 99)]
448
assert msg2.string == u'blau'
449
assert msg2.locations == [('main.py', 100)]
452
assert msg3.string == (u'Salat', u'Salate')
453
assert msg3.locations == [('util.py', 42)]
455
assert not 'head' in cat
456
assert list(cat.obsolete.values())[0].id == 'head'