Merge lp:~henninge/launchpad/bug-565294-devel into lp:launchpad

Proposed by Henning Eggers
Status: Merged
Approved by: Henning Eggers
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~henninge/launchpad/bug-565294-devel
Merge into: lp:launchpad
Diff against target: 763 lines (+219/-89)
2 files modified
lib/lp/translations/model/pofile.py (+42/-23)
lib/lp/translations/tests/test_pofile.py (+177/-66)
To merge this branch: bzr merge lp:~henninge/launchpad/bug-565294-devel
Reviewer Review Type Date Requested Status
Michael Nelson (community) code Approve
Review via email: mp+24020@code.launchpad.net

Commit message

When exporting PO files, use the plural information from the imported file in some cases instead of the information from the language database.

Description of the change

Merging already approved branch into devel instead of production-devel.
https://code.edge.launchpad.net/~henninge/launchpad/bug-565294-nplurals

No changes since last approval. ;-)

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/model/pofile.py'
2--- lib/lp/translations/model/pofile.py 2010-01-14 16:39:18 +0000
3+++ lib/lp/translations/model/pofile.py 2010-04-26 10:23:30 +0000
4@@ -34,7 +34,6 @@
5 from canonical.launchpad.webapp.interfaces import (
6 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE, MASTER_FLAVOR)
7 from canonical.launchpad.webapp.publisher import canonical_url
8-from canonical.librarian.interfaces import ILibrarianClient
9 from lp.registry.interfaces.person import validate_public_person
10 from lp.registry.model.person import Person
11 from lp.translations.utilities.rosettastats import RosettaStats
12@@ -792,7 +791,6 @@
13
14 # A POT set has "new" suggestions if there is a non current
15 # TranslationMessage newer than the current reviewed one.
16- store = Store.of(self)
17 query = (
18 """POTMsgSet.id IN (SELECT DISTINCT TranslationMessage.potmsgset
19 FROM TranslationMessage, TranslationTemplateItem, POTMsgSet
20@@ -1026,10 +1024,6 @@
21 entry_to_import = removeSecurityProxy(entry_to_import)
22
23 translation_importer = getUtility(ITranslationImporter)
24- librarian_client = getUtility(ILibrarianClient)
25-
26- import_file = librarian_client.getFileByAlias(
27- entry_to_import.content.id)
28
29 # While importing a file, there are two kinds of errors:
30 #
31@@ -1125,7 +1119,6 @@
32 # There were some errors with translations.
33 errorsdetails = ''
34 for error in errors:
35- pofile = error['pofile']
36 potmsgset = error['potmsgset']
37 pomessage = error['pomessage']
38 error_message = error['error-message']
39@@ -1706,6 +1699,47 @@
40
41 return self._pofile.language.code
42
43+ def _isWesternPluralForm(self, number, expression):
44+ # Western style is nplurals=2;plural=n!=1.
45+ if number != 2:
46+ return False
47+ if expression is None:
48+ return False
49+ # Normalize: Remove spaces.
50+ expression = expression.replace(' ', '')
51+ # Normalize: Remove enclosing brackets.
52+ expression = expression.strip('()')
53+ return expression in ('n!=1', '1!=n', 'n>1', '1<n')
54+
55+ def _updateHeaderPluralInfo(self, header):
56+ header_nplurals = header.number_plural_forms
57+ database_nplurals = self._pofile.language.pluralforms
58+ # These checks are here to catch cases where the plural information
59+ # from the header might be more acurate than what we have in the
60+ # database. This is usually the case when the number of plural forms
61+ # has grown but not if it's the standard western form.
62+ # See bug 565294
63+ if header_nplurals is not None and database_nplurals is not None:
64+ if header_nplurals > database_nplurals:
65+ is_western = self._isWesternPluralForm(
66+ header_nplurals, header.plural_form_expression)
67+ if not is_western:
68+ # Use existing information from the header.
69+ return
70+ if database_nplurals is None:
71+ # In all other cases we never use the plural info from the header.
72+ header.number_plural_forms = None
73+ header.plural_form_expression = None
74+ else:
75+ # We have pluralforms information for this language so we
76+ # update the header to be sure that we use the language
77+ # information from our database instead of using the one
78+ # that we got from upstream. We check this information so
79+ # we are sure it's valid.
80+ header.number_plural_forms = self._pofile.language.pluralforms
81+ header.plural_form_expression = (
82+ self._pofile.language.pluralexpression)
83+
84 @cachedproperty
85 def header(self):
86 """See `ITranslationFileData`."""
87@@ -1713,28 +1747,13 @@
88 translation_header = self._pofile.getHeader()
89 # Update default fields based on its values in the template header.
90 translation_header.updateFromTemplateHeader(template_header)
91- date_reviewed = None
92 translation_header.translation_revision_date = (
93 self._pofile.date_changed)
94
95 translation_header.comment = self._pofile.topcomment
96
97 if self._pofile.potemplate.hasPluralMessage():
98- number_plural_forms = None
99- plural_form_expression = None
100- if self._pofile.language.pluralforms is not None:
101- # We have pluralforms information for this language so we
102- # update the header to be sure that we use the language
103- # information from our database instead of use the one
104- # that we got from upstream. We check this information so
105- # we are sure it's valid.
106- number_plural_forms = self._pofile.language.pluralforms
107- plural_form_expression = (
108- self._pofile.language.pluralexpression)
109-
110- translation_header.number_plural_forms = number_plural_forms
111- translation_header.plural_form_expression = plural_form_expression
112-
113+ self._updateHeaderPluralInfo(translation_header)
114 if (self._pofile.lasttranslator is not None):
115 email = self._pofile.lasttranslator.safe_email_or_blank
116 if not email:
117
118=== modified file 'lib/lp/translations/tests/test_pofile.py'
119--- lib/lp/translations/tests/test_pofile.py 2010-01-12 21:29:03 +0000
120+++ lib/lp/translations/tests/test_pofile.py 2010-04-26 10:23:30 +0000
121@@ -7,7 +7,8 @@
122
123 from datetime import datetime, timedelta
124 import pytz
125-import unittest
126+from textwrap import dedent
127+from unittest import TestLoader
128
129 from zope.component import getAdapter, getUtility
130 from zope.security.proxy import removeSecurityProxy
131@@ -93,7 +94,7 @@
132 self.assertEquals(found_potmsgsets, [plural_potmsgset])
133
134 # Search translations as well.
135- translation = self.factory.makeTranslationMessage(
136+ self.factory.makeTranslationMessage(
137 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
138 translations=[u"One translation message"])
139 found_potmsgsets = list(
140@@ -101,7 +102,7 @@
141 self.assertEquals(found_potmsgsets, [potmsgset])
142
143 # Search matches all plural forms.
144- plural_translation = self.factory.makeTranslationMessage(
145+ self.factory.makeTranslationMessage(
146 pofile=self.devel_sr_pofile, potmsgset=plural_potmsgset,
147 translations=[u"One translation message",
148 u"Plural translation message",
149@@ -153,7 +154,7 @@
150 # If somebody else provides a translation, it's not added to the
151 # list of submitter's translations.
152 someone_else = self.factory.makePerson()
153- other_translation = self.factory.makeTranslationMessage(
154+ self.factory.makeTranslationMessage(
155 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
156 translations=[u"Another translation"],
157 translator=someone_else)
158@@ -166,7 +167,7 @@
159 # to the list of submitter's translations for *former* POFile.
160 self.devel_sr_latin_pofile = self.factory.makePOFile(
161 'sr', variant=u'latin', potemplate=self.devel_potemplate)
162- latin_translation = self.factory.makeTranslationMessage(
163+ self.factory.makeTranslationMessage(
164 pofile=self.devel_sr_latin_pofile, potmsgset=potmsgset,
165 translations=[u"Yet another translation"],
166 translator=submitter)
167@@ -194,7 +195,7 @@
168 self.assertEquals(found_translations, [])
169
170 # When a diverged translation is added, the potmsgset is returned.
171- translation = self.factory.makeTranslationMessage(
172+ self.factory.makeTranslationMessage(
173 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
174 translations=[u"Translation"])
175 found_translations = list(
176@@ -202,7 +203,7 @@
177 self.assertEquals(found_translations, [self.potmsgset])
178
179 # If diverged translation is empty, POTMsgSet is not listed.
180- translation = self.factory.makeTranslationMessage(
181+ self.factory.makeTranslationMessage(
182 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
183 translations=[u""])
184 found_translations = list(
185@@ -214,7 +215,7 @@
186 # translation for the POTMsgSet as well.
187
188 # We create a shared translation first.
189- shared_translation = self.factory.makeSharedTranslationMessage(
190+ self.factory.makeSharedTranslationMessage(
191 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
192 translations=[u"Shared translation"])
193
194@@ -224,7 +225,7 @@
195 self.assertEquals(found_translations, [self.potmsgset])
196
197 # When an empty diverged translation is added, nothing is listed.
198- translation = self.factory.makeTranslationMessage(
199+ self.factory.makeTranslationMessage(
200 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
201 translations=[u""])
202 found_translations = list(
203@@ -232,7 +233,7 @@
204 self.assertEquals(found_translations, [])
205
206 # If diverged translation is non-empty, POTMsgSet is listed.
207- translation = self.factory.makeTranslationMessage(
208+ self.factory.makeTranslationMessage(
209 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
210 translations=[u"Translation"])
211 found_translations = list(
212@@ -244,7 +245,7 @@
213 # empty shared translation for the POTMsgSet as well.
214
215 # We create an empty shared translation first.
216- shared_translation = self.factory.makeSharedTranslationMessage(
217+ self.factory.makeSharedTranslationMessage(
218 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
219 translations=[u""])
220
221@@ -255,7 +256,7 @@
222 self.assertEquals(found_translations, [])
223
224 # When an empty diverged translation is added, nothing is listed.
225- translation = self.factory.makeTranslationMessage(
226+ self.factory.makeTranslationMessage(
227 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
228 translations=[u""])
229 found_translations = list(
230@@ -263,7 +264,7 @@
231 self.assertEquals(found_translations, [])
232
233 # If diverged translation is non-empty, POTMsgSet is listed.
234- translation = self.factory.makeTranslationMessage(
235+ self.factory.makeTranslationMessage(
236 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
237 translations=[u"Translation"])
238 found_translations = list(
239@@ -275,7 +276,7 @@
240 # translated message.
241
242 # Add a diverged translation on the included POTMsgSet...
243- translation = self.factory.makeTranslationMessage(
244+ self.factory.makeTranslationMessage(
245 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
246 translations=[u"Diverged translation"])
247
248@@ -284,7 +285,7 @@
249 u"Translated text")
250 potmsgset.setSequence(self.devel_potemplate, 2)
251
252- shared_translation = self.factory.makeSharedTranslationMessage(
253+ self.factory.makeSharedTranslationMessage(
254 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
255 translations=[u"Shared translation"])
256
257@@ -303,7 +304,7 @@
258 self.assertEquals(found_translations, [self.potmsgset])
259
260 # When a diverged translation is added, the potmsgset is returned.
261- translation = self.factory.makeTranslationMessage(
262+ self.factory.makeTranslationMessage(
263 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
264 translations=[u"Translation"])
265 found_translations = list(
266@@ -311,7 +312,7 @@
267 self.assertEquals(found_translations, [])
268
269 # If diverged translation is empty, POTMsgSet is not listed.
270- translation = self.factory.makeTranslationMessage(
271+ self.factory.makeTranslationMessage(
272 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
273 translations=[u""])
274 found_translations = list(
275@@ -323,7 +324,7 @@
276 # translation for the POTMsgSet as well.
277
278 # We create a shared translation first.
279- shared_translation = self.factory.makeSharedTranslationMessage(
280+ self.factory.makeSharedTranslationMessage(
281 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
282 translations=[u"Shared translation"])
283
284@@ -333,7 +334,7 @@
285 self.assertEquals(found_translations, [])
286
287 # When an empty diverged translation is added, nothing is listed.
288- translation = self.factory.makeTranslationMessage(
289+ self.factory.makeTranslationMessage(
290 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
291 translations=[u""])
292 found_translations = list(
293@@ -341,7 +342,7 @@
294 self.assertEquals(found_translations, [self.potmsgset])
295
296 # If diverged translation is non-empty, POTMsgSet is listed.
297- translation = self.factory.makeTranslationMessage(
298+ self.factory.makeTranslationMessage(
299 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
300 translations=[u"Translation"])
301 found_translations = list(
302@@ -353,7 +354,7 @@
303 # empty shared translation for the POTMsgSet as well.
304
305 # We create an empty shared translation first.
306- shared_translation = self.factory.makeSharedTranslationMessage(
307+ self.factory.makeSharedTranslationMessage(
308 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
309 translations=[u""])
310
311@@ -364,7 +365,7 @@
312 self.assertEquals(found_translations, [self.potmsgset])
313
314 # When an empty diverged translation is added, nothing is listed.
315- translation = self.factory.makeTranslationMessage(
316+ self.factory.makeTranslationMessage(
317 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
318 translations=[u""])
319 found_translations = list(
320@@ -372,7 +373,7 @@
321 self.assertEquals(found_translations, [self.potmsgset])
322
323 # If diverged translation is non-empty, POTMsgSet is listed.
324- translation = self.factory.makeTranslationMessage(
325+ self.factory.makeTranslationMessage(
326 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
327 translations=[u"Translation"])
328 found_translations = list(
329@@ -384,7 +385,7 @@
330 # untranslated message.
331
332 # Add an empty translation to the included POTMsgSet...
333- translation = self.factory.makeTranslationMessage(
334+ self.factory.makeTranslationMessage(
335 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
336 translations=[u""])
337
338@@ -478,14 +479,14 @@
339
340 # A POTMsgSet has a shared, current translation created 5 days ago.
341 date_created = datetime.now(pytz.UTC)-timedelta(5)
342- translation = self.factory.makeSharedTranslationMessage(
343+ self.factory.makeSharedTranslationMessage(
344 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
345 translations=[u"Shared translation"], date_updated=date_created)
346
347 # And we also have a diverged translation created a day after shared
348 # current translation.
349 diverged_date = date_created + timedelta(1)
350- translation = self.factory.makeTranslationMessage(
351+ self.factory.makeTranslationMessage(
352 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
353 translations=[u"Old translation"], date_updated=diverged_date)
354
355@@ -536,7 +537,7 @@
356 # Test that multiple unreviewed POTMsgSets are returned.
357
358 # One POTMsgSet has no translations, but only a suggestion.
359- translation = self.factory.makeTranslationMessage(
360+ self.factory.makeTranslationMessage(
361 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
362 translations=[u"New suggestion"], suggestion=True)
363
364@@ -545,11 +546,11 @@
365 u"Translated text")
366 potmsgset.setSequence(self.devel_potemplate, 2)
367 date_created = datetime.now(pytz.UTC) - timedelta(5)
368- translation = self.factory.makeTranslationMessage(
369+ self.factory.makeTranslationMessage(
370 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
371 translations=[u"Translation"], date_updated=date_created)
372 suggestion_date = date_created + timedelta(1)
373- translation = self.factory.makeTranslationMessage(
374+ self.factory.makeTranslationMessage(
375 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
376 translations=[u"New suggestion"], suggestion=True,
377 date_updated=suggestion_date)
378@@ -562,12 +563,12 @@
379 def test_getPOTMsgSetWithNewSuggestions_distinct(self):
380 # Provide two suggestions on a single message and make sure
381 # a POTMsgSet is returned only once.
382- translation1 = self.factory.makeSharedTranslationMessage(
383+ self.factory.makeSharedTranslationMessage(
384 pofile=self.devel_sr_pofile,
385 potmsgset=self.potmsgset,
386 translations=["A suggestion"],
387 suggestion=True)
388- translation2 = self.factory.makeSharedTranslationMessage(
389+ self.factory.makeSharedTranslationMessage(
390 pofile=self.devel_sr_pofile,
391 potmsgset=self.potmsgset,
392 translations=["Another suggestion"],
393@@ -646,7 +647,6 @@
394
395 # But adding a diverged current and imported translation means
396 # that it's not changed anymore.
397- old_translation = translation
398 translation = self.factory.makeTranslationMessage(
399 pofile=self.devel_sr_pofile, potmsgset=self.potmsgset,
400 translations=[u"Diverged imported"], is_imported=True,
401@@ -753,42 +753,42 @@
402 # Second POTMsgSet is untranslated, but with a suggestion.
403 potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate)
404 potmsgset.setSequence(self.devel_potemplate, 2)
405- translation = self.factory.makeTranslationMessage(
406+ self.factory.makeTranslationMessage(
407 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
408 translations=[u"Unreviewed suggestion"], suggestion=True)
409
410 # Third POTMsgSet is translated, and with a suggestion.
411 potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate)
412 potmsgset.setSequence(self.devel_potemplate, 3)
413- translation = self.factory.makeTranslationMessage(
414+ self.factory.makeTranslationMessage(
415 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
416 translations=[u"Translation"], suggestion=False,
417 date_updated=datetime.now(pytz.UTC)-timedelta(1))
418- translation = self.factory.makeTranslationMessage(
419+ self.factory.makeTranslationMessage(
420 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
421 translations=[u"Another suggestion"], suggestion=True)
422
423 # Fourth POTMsgSet is translated in import.
424 potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate)
425 potmsgset.setSequence(self.devel_potemplate, 4)
426- translation = self.factory.makeTranslationMessage(
427+ self.factory.makeTranslationMessage(
428 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
429 translations=[u"Imported translation"], is_imported=True)
430
431 # Fifth POTMsgSet is translated in import, but changed in LP.
432 potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate)
433 potmsgset.setSequence(self.devel_potemplate, 5)
434- translation = self.factory.makeTranslationMessage(
435+ self.factory.makeTranslationMessage(
436 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
437 translations=[u"Imported translation"], is_imported=True)
438- translation = self.factory.makeTranslationMessage(
439+ self.factory.makeTranslationMessage(
440 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
441 translations=[u"LP translation"], is_imported=False)
442
443 # Sixth POTMsgSet is translated in LP only.
444 potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate)
445 potmsgset.setSequence(self.devel_potemplate, 6)
446- translation = self.factory.makeTranslationMessage(
447+ self.factory.makeTranslationMessage(
448 pofile=self.devel_sr_pofile, potmsgset=potmsgset,
449 translations=[u"New translation"], is_imported=False)
450
451@@ -810,7 +810,7 @@
452 def test_TranslationFileData_adapter(self):
453 # Test that exporting works correctly with shared and diverged
454 # messages.
455- shared_translation = self.factory.makeSharedTranslationMessage(
456+ self.factory.makeSharedTranslationMessage(
457 pofile=self.devel_sr_pofile,
458 potmsgset=self.potmsgset,
459 translations=["Shared translation"])
460@@ -827,7 +827,7 @@
461 "Shared translation")])
462
463 # When we add a diverged translation, only that is exported.
464- diverged_translation = self.factory.makeTranslationMessage(
465+ self.factory.makeTranslationMessage(
466 pofile=self.devel_sr_pofile,
467 potmsgset=self.potmsgset,
468 translations=["Diverged translation"],
469@@ -887,7 +887,7 @@
470 productseries=self.foo_stable, name="template-2")
471
472 self.assertEqual(None, potemplate_devel_1.getPOFileByLang('eo'))
473- pofile_devel = potemplate_devel_1.newPOFile('eo')
474+ potemplate_devel_1.newPOFile('eo')
475 self.assertEqual(None, potemplate_stable_2.getPOFileByLang('eo'))
476
477 def test_potemplate_creation(self):
478@@ -895,14 +895,14 @@
479 # all shared potemplates.
480 foo_other = self.factory.makeProductSeries(
481 name='other', product=self.foo)
482- other_potemplate = self.factory.makePOTemplate(
483+ self.factory.makePOTemplate(
484 productseries=foo_other, name="messages")
485 devel_potemplate = self.factory.makePOTemplate(
486 productseries=self.foo_devel, name="messages")
487 # These will automatically be shared across all sharing templates.
488 # They will also be created in the 'other' series.
489- pofile_devel_eo = devel_potemplate.newPOFile('eo')
490- pofile_devel_de = devel_potemplate.newPOFile('de')
491+ devel_potemplate.newPOFile('eo')
492+ devel_potemplate.newPOFile('de')
493
494 stable_potemplate = self.factory.makePOTemplate(
495 productseries=self.foo_stable, name="messages")
496@@ -1015,11 +1015,11 @@
497 def test_getPOTMsgSetTranslated_ordering(self):
498 # Translate both POTMsgSets in devel_sr_pofile, so
499 # they are returned with getPOTMsgSetTranslated() call.
500- translation1 = self.factory.makeSharedTranslationMessage(
501+ self.factory.makeSharedTranslationMessage(
502 pofile=self.devel_sr_pofile,
503 potmsgset=self.potmsgset1,
504 translations=["Shared translation"])
505- translation2 = self.factory.makeSharedTranslationMessage(
506+ self.factory.makeSharedTranslationMessage(
507 pofile=self.devel_sr_pofile,
508 potmsgset=self.potmsgset2,
509 translations=["Another shared translation"])
510@@ -1073,22 +1073,22 @@
511 def test_getPOTMsgSetChangedInLaunchpad_ordering(self):
512 # Suggest a translation on both POTMsgSets in devel_sr_pofile,
513 # so they are returned with getPOTMsgSetWithNewSuggestions() call.
514- imported1 = self.factory.makeSharedTranslationMessage(
515+ self.factory.makeSharedTranslationMessage(
516 pofile=self.devel_sr_pofile,
517 potmsgset=self.potmsgset1,
518 translations=["Imported"],
519 is_imported=True)
520- translation1 = self.factory.makeSharedTranslationMessage(
521+ self.factory.makeSharedTranslationMessage(
522 pofile=self.devel_sr_pofile,
523 potmsgset=self.potmsgset1,
524 translations=["Changed"],
525 is_imported=False)
526- imported2 = self.factory.makeSharedTranslationMessage(
527+ self.factory.makeSharedTranslationMessage(
528 pofile=self.devel_sr_pofile,
529 potmsgset=self.potmsgset2,
530 translations=["Another imported"],
531 is_imported=True)
532- translation2 = self.factory.makeSharedTranslationMessage(
533+ self.factory.makeSharedTranslationMessage(
534 pofile=self.devel_sr_pofile,
535 potmsgset=self.potmsgset2,
536 translations=["Another changed"],
537@@ -1186,11 +1186,11 @@
538 # This test will go away when potmsgset.sequence goes away.
539
540 # Give the method something to search for.
541- translation1 = self.factory.makeSharedTranslationMessage(
542+ self.factory.makeSharedTranslationMessage(
543 pofile=self.devel_sr_pofile,
544 potmsgset=self.potmsgset1,
545 translations=["Shared translation"])
546- translation2 = self.factory.makeSharedTranslationMessage(
547+ self.factory.makeSharedTranslationMessage(
548 pofile=self.devel_sr_pofile,
549 potmsgset=self.potmsgset2,
550 translations=["Another shared translation"])
551@@ -1410,7 +1410,7 @@
552 # All POFiles with translation credits messages are
553 # returned along with relevant POTMsgSets.
554 potemplate1 = self.factory.makePOTemplate()
555- credits_potmsgset = self.factory.makePOTMsgSet(
556+ self.factory.makePOTMsgSet(
557 potemplate1, singular=u'translator-credits', sequence=1)
558
559 sr_pofile = self.factory.makePOFile('sr', potemplate=potemplate1)
560@@ -1430,7 +1430,7 @@
561 # If another POTemplate has a translation credits message, it's
562 # returned as well.
563 potemplate2 = self.factory.makePOTemplate()
564- kde_credits_potmsgset = self.factory.makePOTMsgSet(
565+ self.factory.makePOTMsgSet(
566 potemplate2, singular=u'Your names',
567 context=u'NAME OF TRANSLATORS', sequence=1)
568 sr_kde_pofile = self.factory.makePOFile('sr', potemplate=potemplate2)
569@@ -1493,7 +1493,7 @@
570 def test_getPOFilesByPathAndOrigin_path_mismatch(self):
571 # getPOFilesByPathAndOrigin matches on POFile path.
572 template = self.factory.makePOTemplate()
573- pofile = template.newPOFile('ta')
574+ template.newPOFile('ta')
575
576 not_found = self.pofileset.getPOFilesByPathAndOrigin(
577 'tu.po', distroseries=template.distroseries,
578@@ -1613,7 +1613,7 @@
579 self.assertEquals(self.pofile.currentCount(), 0)
580
581 # Adding an imported translation increases currentCount().
582- imported = self.factory.makeTranslationMessage(
583+ self.factory.makeTranslationMessage(
584 pofile=self.pofile,
585 potmsgset=self.potmsgset,
586 translations=["Imported current"],
587@@ -1624,7 +1624,7 @@
588 # Adding a suggestion (i.e. unused translation)
589 # will not change the current count when there's
590 # already an imported message.
591- suggestion = self.factory.makeTranslationMessage(
592+ self.factory.makeTranslationMessage(
593 pofile=self.pofile,
594 potmsgset=self.potmsgset,
595 translations=["A suggestion"],
596@@ -1640,7 +1640,7 @@
597
598 # Adding a current translation for an untranslated
599 # message increases the count of new translations in LP.
600- current = self.factory.makeTranslationMessage(
601+ self.factory.makeTranslationMessage(
602 pofile=self.pofile,
603 potmsgset=self.potmsgset,
604 translations=["Current"])
605@@ -1651,12 +1651,12 @@
606 # If we get an 'imported' translation for what
607 # we already have as 'new', it's not considered 'new'
608 # anymore since it has been synced.
609- current = self.factory.makeTranslationMessage(
610+ self.factory.makeTranslationMessage(
611 pofile=self.pofile,
612 potmsgset=self.potmsgset,
613 translations=["Current"])
614 # Reimport it but with is_imported=True.
615- imported = self.factory.makeTranslationMessage(
616+ self.factory.makeTranslationMessage(
617 pofile=self.pofile,
618 potmsgset=self.potmsgset,
619 translations=["Current"],
620@@ -1669,12 +1669,12 @@
621 # If we change an 'imported' translation through
622 # Launchpad, it's still not considered 'new',
623 # but an 'update' instead.
624- imported = self.factory.makeTranslationMessage(
625+ self.factory.makeTranslationMessage(
626 pofile=self.pofile,
627 potmsgset=self.potmsgset,
628 translations=["Imported"],
629 is_imported=True)
630- update = self.factory.makeTranslationMessage(
631+ self.factory.makeTranslationMessage(
632 pofile=self.pofile,
633 potmsgset=self.potmsgset,
634 translations=["Changed"])
635@@ -1722,8 +1722,7 @@
636
637 def test_getTranslationRows_sequence(self):
638 # Test for correct sorting of obsolete messages (where sequence=0).
639- msgsets = [
640- self._createMessageSet(msg) for msg in self.TEST_MESSAGES]
641+ [self._createMessageSet(msg) for msg in self.TEST_MESSAGES]
642 for rownum, row in enumerate(
643 self.pofile.getTranslationRows()):
644 self.failUnlessEqual(
645@@ -1732,5 +1731,117 @@
646 "(sequence=0) to the end of the file.")
647
648
649+class TestPOFileToTranslationFileDataAdapter(TestCaseWithFactory):
650+ """Test POFile being adapted to IPOFileToTranslationFileData."""
651+
652+ layer = ZopelessDatabaseLayer
653+
654+ header = dedent("""
655+ Project-Id-Version: foo
656+ Report-Msgid-Bugs-To:
657+ POT-Creation-Date: 2007-07-09 03:39+0100
658+ PO-Revision-Date: 2001-09-09 01:46+0000
659+ Last-Translator: Kubla Kahn <kk@pleasure-dome.com>
660+ Language-Team: Serbian <sr@li.org>
661+ MIME-Version: 1.0
662+ Content-Type: text/plain; charset=UTF-8
663+ Content-Transfer-Encoding: 8bit
664+ Plural-Forms: %s""")
665+
666+ western_plural = "nplurals=2; plural=(n != 1)"
667+ other_2_plural = "nplurals=2; plural=(n > 0)"
668+ generic_plural = "nplurals=INTEGER; plural=EXPRESSION"
669+ serbian3_plural = ("nplurals=3; plural=(n%10==1 && n%100!=11 "
670+ "? 0 : n%10>=2 && n%10<=4 && (n% 100<10 || n%100>=20) "
671+ "? 1 : 2)")
672+ serbian4_plural = ("nplurals=4; plural=(n==1 ? 3 : (n%10==1 && n%100!=11 "
673+ "? 0 : n%10>=2 && n%10<=4 && (n% 100<10 || n%100>=20) "
674+ "? 1 : 2))")
675+ plural_template = "nplurals=%d; plural=%s"
676+
677+ def _makePOFileWithPlural(self, language_code):
678+ pofile = removeSecurityProxy(self.factory.makePOFile(language_code))
679+ self.factory.makePOTMsgSet(
680+ pofile.potemplate, singular=u"Foo", plural=u"Bar", sequence=1)
681+ return pofile
682+
683+ def test_header_pluralform_equal(self):
684+ # If the number of plural forms in the header is equal to that in the
685+ # language entry, use the data from the language entry.
686+ sr_pofile = self._makePOFileWithPlural('sr')
687+ sr_pofile.header = self.header % self.serbian3_plural
688+
689+ translation_file_data = getAdapter(
690+ sr_pofile, ITranslationFileData, 'all_messages')
691+ self.assertEqual(3, translation_file_data.header.number_plural_forms)
692+ # The expression from the header starts with a "(", the language entry
693+ # does not.
694+ self.assertEqual(
695+ u"n%10==1 && n%100!=11",
696+ translation_file_data.header.plural_form_expression[:20])
697+
698+ def test_header_pluralform_larger(self):
699+ # If the number of plural forms in the header is larger than in the
700+ # language entry, use the data from the header.
701+ sr_pofile = self._makePOFileWithPlural('sr')
702+ sr_pofile.header = self.header % self.serbian4_plural
703+
704+ translation_file_data = getAdapter(
705+ sr_pofile, ITranslationFileData, 'all_messages')
706+ self.assertEqual(4, translation_file_data.header.number_plural_forms)
707+ self.assertEqual(
708+ u"(n==1 ? 3 : (n%10==1",
709+ translation_file_data.header.plural_form_expression[:20])
710+
711+ def test_header_pluralform_larger_but_western(self):
712+ # If the plural form expression in the header is the standard western
713+ # expression, use the data from the language entry if present.
714+ # Use Japanese because it has only one plural form which is less
715+ # than the 2 the western style has.
716+ ja_pofile = self._makePOFileWithPlural('ja')
717+ # The expression comes in different forms.
718+ for expr in ('(n != 1)', '1 != n', 'n>1', '(1 < n)'):
719+ plural_info = self.plural_template % (2, expr)
720+ ja_pofile.header = self.header % plural_info
721+
722+ translation_file_data = getAdapter(
723+ ja_pofile, ITranslationFileData, 'all_messages')
724+ nplurals_expected = 1
725+ nplurals = translation_file_data.header.number_plural_forms
726+ self.assertEqual(
727+ nplurals_expected, nplurals,
728+ "%d != %d for '%s'" % (nplurals_expected, nplurals, expr))
729+ # The plural form expression for Japanese (or any other language
730+ # with just one form) is simply '0'.
731+ self.assertEqual(
732+ u"0", translation_file_data.header.plural_form_expression)
733+
734+ def test_header_pluralform_2_but_not_western(self):
735+ # If the plural form expression in the header reports two but is not
736+ # the standard western expression, use the data from the header.
737+ # Use Japanese because it has only one plural form which is less
738+ # than the 2 the western style has.
739+ ja_pofile = self._makePOFileWithPlural('ja')
740+ ja_pofile.header = self.header % self.other_2_plural
741+
742+ translation_file_data = getAdapter(
743+ ja_pofile, ITranslationFileData, 'all_messages')
744+ self.assertEqual(2, translation_file_data.header.number_plural_forms)
745+ self.assertEqual(
746+ u"(n > 0)", translation_file_data.header.plural_form_expression)
747+
748+ def test_header_pluralform_generic(self):
749+ # If the plural form expression in the header is a generic one (no
750+ # information), use the data from the language entry if present.
751+ ja_pofile = self._makePOFileWithPlural('ja')
752+ ja_pofile.header = self.header % self.generic_plural
753+
754+ translation_file_data = getAdapter(
755+ ja_pofile, ITranslationFileData, 'all_messages')
756+ self.assertEqual(1, translation_file_data.header.number_plural_forms)
757+ self.assertEqual(
758+ u"0", translation_file_data.header.plural_form_expression)
759+
760+
761 def test_suite():
762- return unittest.TestLoader().loadTestsFromName(__name__)
763+ return TestLoader().loadTestsFromName(__name__)