Merge lp:~henninge/launchpad/bug-128324 into lp:launchpad
- bug-128324
- Merge into devel
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-128324 |
Merge into: | lp:launchpad |
Diff against target: |
588 lines 8 files modified
lib/lp/testing/factory.py (+2/-4) lib/lp/translations/doc/potmsgset.txt (+7/-69) lib/lp/translations/interfaces/potemplate.py (+15/-7) lib/lp/translations/model/potemplate.py (+14/-6) lib/lp/translations/tests/test_pofile.py (+48/-12) lib/lp/translations/tests/test_potmsgset.py (+95/-1) lib/lp/translations/tests/test_shared_potemplate.py (+11/-38) lib/lp/translations/tests/test_vpoexport.py (+0/-62) |
To merge this branch: | bzr merge lp:~henninge/launchpad/bug-128324 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | Approve | ||
Review via email: mp+13946@code.launchpad.net |
Commit message
Description of the change
Henning Eggers (henninge) wrote : | # |
Henning Eggers (henninge) wrote : | # |
I missed a test...
Directly related:
bin/test -vvt potmsgset.txt -t TestPOTMsgSetTr
Indirectly related:
bin/test -vvt TestTranslation
Jeroen T. Vermeulen (jtv) wrote : | # |
Looks good; I particularly like the tech debt payoff component in this branch.
It's not really explicit, but I take it the "half" of the bug that you're not solving here is the one where you mark credits as translated when you create a new POFile. If so, there really ought to be a separate bug about that. Could you see to it?
One nit: _launchpad_
Jeroen
Preview Diff
1 | === modified file 'lib/lp/testing/factory.py' | |||
2 | --- lib/lp/testing/factory.py 2009-10-23 13:51:40 +0000 | |||
3 | +++ lib/lp/testing/factory.py 2009-10-26 15:28:11 +0000 | |||
4 | @@ -1566,14 +1566,12 @@ | |||
5 | 1566 | create_sharing=create_sharing) | 1566 | create_sharing=create_sharing) |
6 | 1567 | 1567 | ||
7 | 1568 | def makePOTMsgSet(self, potemplate, singular=None, plural=None, | 1568 | def makePOTMsgSet(self, potemplate, singular=None, plural=None, |
9 | 1569 | context=None, sequence=None): | 1569 | context=None, sequence=0): |
10 | 1570 | """Make a new `POTMsgSet` in the given template.""" | 1570 | """Make a new `POTMsgSet` in the given template.""" |
11 | 1571 | if singular is None and plural is None: | 1571 | if singular is None and plural is None: |
12 | 1572 | singular = self.getUniqueString() | 1572 | singular = self.getUniqueString() |
13 | 1573 | potmsgset = potemplate.createMessageSetFromText( | 1573 | potmsgset = potemplate.createMessageSetFromText( |
17 | 1574 | singular, plural, context=context) | 1574 | singular, plural, context, sequence) |
15 | 1575 | if sequence is not None: | ||
16 | 1576 | potmsgset.setSequence(potemplate, sequence) | ||
18 | 1577 | naked_potmsgset = removeSecurityProxy(potmsgset) | 1575 | naked_potmsgset = removeSecurityProxy(potmsgset) |
19 | 1578 | naked_potmsgset.sync() | 1576 | naked_potmsgset.sync() |
20 | 1579 | return potmsgset | 1577 | return potmsgset |
21 | 1580 | 1578 | ||
22 | === modified file 'lib/lp/translations/doc/potmsgset.txt' | |||
23 | --- lib/lp/translations/doc/potmsgset.txt 2009-10-22 15:51:58 +0000 | |||
24 | +++ lib/lp/translations/doc/potmsgset.txt 2009-10-26 15:28:11 +0000 | |||
25 | @@ -34,79 +34,17 @@ | |||
26 | 34 | is_translation_credit indicates if the POTMsgSet is translation credits. The | 34 | is_translation_credit indicates if the POTMsgSet is translation credits. The |
27 | 35 | property translation_credit_type contains the type of translation credits. | 35 | property translation_credit_type contains the type of translation credits. |
28 | 36 | 36 | ||
34 | 37 | >>> def print_credits(credits): | 37 | >>> print potmsgset.is_translation_credit |
30 | 38 | ... print credits.is_translation_credit | ||
31 | 39 | ... print credits.translation_credits_type.title | ||
32 | 40 | |||
33 | 41 | >>> print_credits(factory.makePOTMsgSet(potemplate)) | ||
35 | 42 | False | 38 | False |
36 | 39 | >>> print potmsgset.translation_credits_type.title | ||
37 | 43 | Not a translation credits message | 40 | Not a translation credits message |
38 | 44 | 41 | ||
70 | 45 | >>> print_credits(factory.makePOTMsgSet( | 42 | >>> credits = factory.makePOTMsgSet( |
40 | 46 | ... potemplate, singular=u'translator-credits')) | ||
41 | 47 | True | ||
42 | 48 | Gnome credits message | ||
43 | 49 | |||
44 | 50 | >>> print_credits(factory.makePOTMsgSet( | ||
45 | 51 | ... potemplate, singular=u'translation-credits')) | ||
46 | 52 | True | ||
47 | 53 | Gnome credits message | ||
48 | 54 | |||
49 | 55 | >>> print_credits(factory.makePOTMsgSet( | ||
50 | 56 | ... potemplate, | ||
51 | 57 | ... singular=u'Your emails', context=u'EMAIL OF TRANSLATORS')) | ||
52 | 58 | True | ||
53 | 59 | KDE emails credits message | ||
54 | 60 | |||
55 | 61 | >>> print_credits(factory.makePOTMsgSet( | ||
56 | 62 | ... potemplate, singular=u'_: EMAIL OF TRANSLATORS\nYour emails')) | ||
57 | 63 | True | ||
58 | 64 | KDE emails credits message | ||
59 | 65 | |||
60 | 66 | |||
61 | 67 | == POTMsgSet.setTranslationCreditsToTranslated == | ||
62 | 68 | |||
63 | 69 | If a message is a translation credits message, its translations must be set | ||
64 | 70 | to some dummy value so that they are counted as "translated" and don't | ||
65 | 71 | mess up the statistics. | ||
66 | 72 | |||
67 | 73 | >>> eo_pofile = factory.makePOFile('eo', potemplate=potemplate) | ||
68 | 74 | >>> eo_language = eo_pofile.language | ||
69 | 75 | >>> gnome_credits = factory.makePOTMsgSet( | ||
71 | 76 | ... potemplate, singular=u'translator-credits') | 43 | ... potemplate, singular=u'translator-credits') |
105 | 77 | >>> current = gnome_credits.getCurrentTranslationMessage(potemplate, | 44 | >>> print credits.is_translation_credit |
106 | 78 | ... eo_language) | 45 | True |
107 | 79 | >>> print current | 46 | >>> print credits.translation_credits_type.title |
108 | 80 | None | 47 | Gnome credits message |
76 | 81 | >>> login("foo.bar@canonical.com") | ||
77 | 82 | >>> gnome_credits.setTranslationCreditsToTranslated(eo_pofile) | ||
78 | 83 | >>> current = gnome_credits.getCurrentTranslationMessage(potemplate, | ||
79 | 84 | ... eo_language) | ||
80 | 85 | >>> print current.msgstr0.translation | ||
81 | 86 | This is a dummy translation so that the credits are counted as translated. | ||
82 | 87 | |||
83 | 88 | An imported translation credits message will already contain a translation | ||
84 | 89 | and is therefore left untouched. | ||
85 | 90 | |||
86 | 91 | >>> from zope.security.proxy import removeSecurityProxy | ||
87 | 92 | >>> sr_pofile = removeSecurityProxy(factory.makePOFile('sr', potemplate=potemplate)) | ||
88 | 93 | >>> sr_language = sr_pofile.language | ||
89 | 94 | >>> sr_credits = factory.makeTranslationMessage( | ||
90 | 95 | ... potmsgset=gnome_credits, pofile=sr_pofile, is_imported=True, | ||
91 | 96 | ... translations=[u"Some translations credits for Serbian."]) | ||
92 | 97 | >>> gnome_credits.setTranslationCreditsToTranslated(sr_pofile) | ||
93 | 98 | >>> current = gnome_credits.getCurrentTranslationMessage(potemplate, | ||
94 | 99 | ... sr_language) | ||
95 | 100 | >>> print current.msgstr0.translation | ||
96 | 101 | Some translations credits for Serbian. | ||
97 | 102 | |||
98 | 103 | Normal messages will be left untouched, too. | ||
99 | 104 | |||
100 | 105 | >>> potmsgset.setTranslationCreditsToTranslated(sr_language) | ||
101 | 106 | >>> current = potmsgset.getCurrentTranslationMessage(potemplate, | ||
102 | 107 | ... sr_pofile) | ||
103 | 108 | >>> print current | ||
104 | 109 | None | ||
109 | 110 | 48 | ||
110 | 111 | 49 | ||
111 | 112 | == POTMsgSet.normalizeWhitespaces == | 50 | == POTMsgSet.normalizeWhitespaces == |
112 | 113 | 51 | ||
113 | === modified file 'lib/lp/translations/interfaces/potemplate.py' | |||
114 | --- lib/lp/translations/interfaces/potemplate.py 2009-10-23 21:50:23 +0000 | |||
115 | +++ lib/lp/translations/interfaces/potemplate.py 2009-10-26 15:28:11 +0000 | |||
116 | @@ -463,27 +463,35 @@ | |||
117 | 463 | """ | 463 | """ |
118 | 464 | 464 | ||
119 | 465 | def createPOTMsgSetFromMsgIDs(msgid_singular, msgid_plural=None, | 465 | def createPOTMsgSetFromMsgIDs(msgid_singular, msgid_plural=None, |
121 | 466 | context=None): | 466 | context=None, sequence=0): |
122 | 467 | """Creates a new template message in the database. | 467 | """Creates a new template message in the database. |
123 | 468 | 468 | ||
124 | 469 | :param msgid_singular: A reference to a singular msgid. | 469 | :param msgid_singular: A reference to a singular msgid. |
125 | 470 | :param msgid_plural: A reference to a plural msgid. Can be None | 470 | :param msgid_plural: A reference to a plural msgid. Can be None |
127 | 471 | if the message is not a plural message. | 471 | if the message is not a plural message. |
128 | 472 | :param context: A context for the template message differentiating | 472 | :param context: A context for the template message differentiating |
130 | 473 | it from other template messages with exactly the same `msgid`. | 473 | it from other template messages with exactly the same `msgid`. |
131 | 474 | :param sequence: The sequence number of this POTMsgSet within this | ||
132 | 475 | POTemplate. If 0, it is considered obsolete. | ||
133 | 474 | :return: The newly created message set. | 476 | :return: The newly created message set. |
134 | 475 | """ | 477 | """ |
135 | 476 | 478 | ||
137 | 477 | def createMessageSetFromText(singular_text, plural_text, context=None): | 479 | def createMessageSetFromText(singular_text, plural_text, |
138 | 480 | context=None, sequence=0): | ||
139 | 478 | """Creates a new template message in the database using strings. | 481 | """Creates a new template message in the database using strings. |
140 | 479 | 482 | ||
141 | 480 | Similar to createMessageSetFromMessageID, but takes text objects | 483 | Similar to createMessageSetFromMessageID, but takes text objects |
142 | 481 | (unicode or string) along with textual context, rather than a | 484 | (unicode or string) along with textual context, rather than a |
143 | 482 | message IDs. | 485 | message IDs. |
144 | 483 | 486 | ||
148 | 484 | For non-plural messages, plural_text should be None. | 487 | :param singular_text: The string for the singular msgid. |
149 | 485 | 488 | :param msgid_plural: The string for the plural msgid. Must be None | |
150 | 486 | Returns the newly created message set. | 489 | if the message is not a plural message. |
151 | 490 | :param context: A context for the template message differentiating | ||
152 | 491 | it from other template messages with exactly the same `msgid`. | ||
153 | 492 | :param sequence: The sequence number of this POTMsgSet within this | ||
154 | 493 | POTemplate. If 0, it is considered obsolete. | ||
155 | 494 | :return: The newly created message set. | ||
156 | 487 | """ | 495 | """ |
157 | 488 | 496 | ||
158 | 489 | def getOrCreateSharedPOTMsgSet(singular_text, plural_text, context=None): | 497 | def getOrCreateSharedPOTMsgSet(singular_text, plural_text, context=None): |
159 | 490 | 498 | ||
160 | === modified file 'lib/lp/translations/model/potemplate.py' | |||
161 | --- lib/lp/translations/model/potemplate.py 2009-10-23 21:50:23 +0000 | |||
162 | +++ lib/lp/translations/model/potemplate.py 2009-10-26 15:28:11 +0000 | |||
163 | @@ -799,9 +799,9 @@ | |||
164 | 799 | return DummyPOFile(self, language, variant=variant, owner=requester) | 799 | return DummyPOFile(self, language, variant=variant, owner=requester) |
165 | 800 | 800 | ||
166 | 801 | def createPOTMsgSetFromMsgIDs(self, msgid_singular, msgid_plural=None, | 801 | def createPOTMsgSetFromMsgIDs(self, msgid_singular, msgid_plural=None, |
168 | 802 | context=None): | 802 | context=None, sequence=0): |
169 | 803 | """See `IPOTemplate`.""" | 803 | """See `IPOTemplate`.""" |
171 | 804 | return POTMsgSet( | 804 | potmsgset = POTMsgSet( |
172 | 805 | context=context, | 805 | context=context, |
173 | 806 | msgid_singular=msgid_singular, | 806 | msgid_singular=msgid_singular, |
174 | 807 | msgid_plural=msgid_plural, | 807 | msgid_plural=msgid_plural, |
175 | @@ -812,6 +812,15 @@ | |||
176 | 812 | sourcecomment=None, | 812 | sourcecomment=None, |
177 | 813 | flagscomment=None) | 813 | flagscomment=None) |
178 | 814 | 814 | ||
179 | 815 | potmsgset.setSequence(self, sequence) | ||
180 | 816 | if potmsgset.is_translation_credit: | ||
181 | 817 | for language in self.languages(): | ||
182 | 818 | pofile = self.getPOFileByLang(language.code) | ||
183 | 819 | if pofile is not None: | ||
184 | 820 | potmsgset.setTranslationCreditsToTranslated(pofile) | ||
185 | 821 | |||
186 | 822 | return potmsgset | ||
187 | 823 | |||
188 | 815 | def getOrCreatePOMsgID(self, text): | 824 | def getOrCreatePOMsgID(self, text): |
189 | 816 | """Creates or returns existing POMsgID for given `text`.""" | 825 | """Creates or returns existing POMsgID for given `text`.""" |
190 | 817 | try: | 826 | try: |
191 | @@ -824,7 +833,7 @@ | |||
192 | 824 | return msgid | 833 | return msgid |
193 | 825 | 834 | ||
194 | 826 | def createMessageSetFromText(self, singular_text, plural_text, | 835 | def createMessageSetFromText(self, singular_text, plural_text, |
196 | 827 | context=None): | 836 | context=None, sequence=0): |
197 | 828 | """See `IPOTemplate`.""" | 837 | """See `IPOTemplate`.""" |
198 | 829 | 838 | ||
199 | 830 | msgid_singular = self.getOrCreatePOMsgID(singular_text) | 839 | msgid_singular = self.getOrCreatePOMsgID(singular_text) |
200 | @@ -837,7 +846,7 @@ | |||
201 | 837 | " primary msgid and context '%r'" % context) | 846 | " primary msgid and context '%r'" % context) |
202 | 838 | 847 | ||
203 | 839 | return self.createPOTMsgSetFromMsgIDs(msgid_singular, msgid_plural, | 848 | return self.createPOTMsgSetFromMsgIDs(msgid_singular, msgid_plural, |
205 | 840 | context) | 849 | context, sequence) |
206 | 841 | 850 | ||
207 | 842 | def getOrCreateSharedPOTMsgSet(self, singular_text, plural_text, | 851 | def getOrCreateSharedPOTMsgSet(self, singular_text, plural_text, |
208 | 843 | context=None): | 852 | context=None): |
209 | @@ -851,8 +860,7 @@ | |||
210 | 851 | context, sharing_templates=True) | 860 | context, sharing_templates=True) |
211 | 852 | if potmsgset is None: | 861 | if potmsgset is None: |
212 | 853 | potmsgset = self.createMessageSetFromText( | 862 | potmsgset = self.createMessageSetFromText( |
215 | 854 | singular_text, plural_text, context) | 863 | singular_text, plural_text, context, sequence=0) |
214 | 855 | potmsgset.setSequence(self, 0) | ||
216 | 856 | return potmsgset | 864 | return potmsgset |
217 | 857 | 865 | ||
218 | 858 | def importFromQueue(self, entry_to_import, logger=None, txn=None): | 866 | def importFromQueue(self, entry_to_import, logger=None, txn=None): |
219 | 859 | 867 | ||
220 | === modified file 'lib/lp/translations/tests/test_pofile.py' | |||
221 | --- lib/lp/translations/tests/test_pofile.py 2009-10-20 19:34:26 +0000 | |||
222 | +++ lib/lp/translations/tests/test_pofile.py 2009-10-26 15:28:11 +0000 | |||
223 | @@ -926,6 +926,14 @@ | |||
224 | 926 | self.credits_potmsgset = self.factory.makePOTMsgSet( | 926 | self.credits_potmsgset = self.factory.makePOTMsgSet( |
225 | 927 | potemplate=self.potemplate, singular=u'translator-credits') | 927 | potemplate=self.potemplate, singular=u'translator-credits') |
226 | 928 | 928 | ||
227 | 929 | def compose_launchpad_credits_text(self, imported_credits_text): | ||
228 | 930 | return u"%s\n\nLaunchpad Contributions:\n %s" % ( | ||
229 | 931 | imported_credits_text, | ||
230 | 932 | "\n ".join(["%s %s" % (person.displayname, | ||
231 | 933 | canonical_url(person)) | ||
232 | 934 | for person in self.pofile.contributors]) | ||
233 | 935 | ) | ||
234 | 936 | |||
235 | 929 | def test_prepareTranslationCredits_extending(self): | 937 | def test_prepareTranslationCredits_extending(self): |
236 | 930 | # This test ensures that continuous updates to the translation credits | 938 | # This test ensures that continuous updates to the translation credits |
237 | 931 | # don't result in duplicate entries. | 939 | # don't result in duplicate entries. |
238 | @@ -933,10 +941,6 @@ | |||
239 | 933 | person = self.factory.makePerson() | 941 | person = self.factory.makePerson() |
240 | 934 | 942 | ||
241 | 935 | imported_credits_text = u"Imported Contributor <name@project.org>" | 943 | imported_credits_text = u"Imported Contributor <name@project.org>" |
242 | 936 | launchpad_credits_text = ( | ||
243 | 937 | u"%s\n\nLaunchpad Contributions:\n %s %s" % ( | ||
244 | 938 | imported_credits_text, person.displayname, | ||
245 | 939 | canonical_url(person))) | ||
246 | 940 | 944 | ||
247 | 941 | # Import a translation credits message to 'translator-credits'. | 945 | # Import a translation credits message to 'translator-credits'. |
248 | 942 | self.factory.makeTranslationMessage( | 946 | self.factory.makeTranslationMessage( |
249 | @@ -954,7 +958,9 @@ | |||
250 | 954 | # The first translation credits export. | 958 | # The first translation credits export. |
251 | 955 | credits_text = self.pofile.prepareTranslationCredits( | 959 | credits_text = self.pofile.prepareTranslationCredits( |
252 | 956 | self.credits_potmsgset) | 960 | self.credits_potmsgset) |
254 | 957 | self.assertEquals(launchpad_credits_text, credits_text) | 961 | self.assertEquals( |
255 | 962 | self.compose_launchpad_credits_text(imported_credits_text), | ||
256 | 963 | credits_text) | ||
257 | 958 | 964 | ||
258 | 959 | # Now, re-import this generated message. | 965 | # Now, re-import this generated message. |
259 | 960 | self.factory.makeTranslationMessage( | 966 | self.factory.makeTranslationMessage( |
260 | @@ -965,7 +971,9 @@ | |||
261 | 965 | 971 | ||
262 | 966 | credits_text = self.pofile.prepareTranslationCredits( | 972 | credits_text = self.pofile.prepareTranslationCredits( |
263 | 967 | self.credits_potmsgset) | 973 | self.credits_potmsgset) |
265 | 968 | self.assertEquals(launchpad_credits_text, credits_text) | 974 | self.assertEquals( |
266 | 975 | self.compose_launchpad_credits_text(imported_credits_text), | ||
267 | 976 | credits_text) | ||
268 | 969 | 977 | ||
269 | 970 | 978 | ||
270 | 971 | class TestTranslationPOFilePOTMsgSetOrdering(TestCaseWithFactory): | 979 | class TestTranslationPOFilePOTMsgSetOrdering(TestCaseWithFactory): |
271 | @@ -1487,21 +1495,49 @@ | |||
272 | 1487 | 1495 | ||
273 | 1488 | layer = ZopelessDatabaseLayer | 1496 | layer = ZopelessDatabaseLayer |
274 | 1489 | 1497 | ||
275 | 1498 | # The sequence number 0 is put at the beginning of the data to verify that | ||
276 | 1499 | # it really gets sorted to the end. | ||
277 | 1500 | TEST_MESSAGES = [ | ||
278 | 1501 | {'msgid':'computer', 'string':'komputilo', 'sequence':0}, | ||
279 | 1502 | {'msgid':'mouse', 'string':'muso', 'sequence':0}, | ||
280 | 1503 | {'msgid':'Good morning', 'string':'Bonan matenon', 'sequence':2}, | ||
281 | 1504 | {'msgid':'Thank you', 'string':'Dankon', 'sequence':1}, | ||
282 | 1505 | ] | ||
283 | 1506 | EXPECTED_SEQUENCE = [1, 2, 0, 0] | ||
284 | 1507 | |||
285 | 1490 | def setUp(self): | 1508 | def setUp(self): |
286 | 1491 | # Create a POFile to calculate statistics on. | 1509 | # Create a POFile to calculate statistics on. |
287 | 1492 | super(TestPOFile, self).setUp() | 1510 | super(TestPOFile, self).setUp() |
289 | 1493 | self.pofile = self.factory.makePOFile('sr') | 1511 | self.pofile = self.factory.makePOFile('eo') |
290 | 1494 | self.potemplate = self.pofile.potemplate | 1512 | self.potemplate = self.pofile.potemplate |
291 | 1495 | 1513 | ||
292 | 1496 | # Create a single POTMsgSet that is used across all tests. | ||
293 | 1497 | self.potmsgset = self.factory.makePOTMsgSet(self.potemplate, | ||
294 | 1498 | sequence=1) | ||
295 | 1499 | |||
296 | 1500 | def test_makeTranslatableMessage(self): | 1514 | def test_makeTranslatableMessage(self): |
297 | 1501 | # TranslatableMessages can be created from the PO file | 1515 | # TranslatableMessages can be created from the PO file |
299 | 1502 | message = self.pofile.makeTranslatableMessage(self.potmsgset) | 1516 | potmsgset = self.factory.makePOTMsgSet(self.potemplate, |
300 | 1517 | sequence=1) | ||
301 | 1518 | message = self.pofile.makeTranslatableMessage(potmsgset) | ||
302 | 1503 | verifyObject(ITranslatableMessage, message) | 1519 | verifyObject(ITranslatableMessage, message) |
303 | 1504 | 1520 | ||
304 | 1521 | def _createMessageSet(self, testmsg): | ||
305 | 1522 | # Create a message set from the test data. | ||
306 | 1523 | pomsgset = self.factory.makePOTMsgSet( | ||
307 | 1524 | self.potemplate, testmsg['msgid'], sequence=testmsg['sequence']) | ||
308 | 1525 | pomsgset.updateTranslation( | ||
309 | 1526 | self.pofile, self.pofile.owner, | ||
310 | 1527 | {0:testmsg['string'],}, | ||
311 | 1528 | True, None, force_edition_rights=True) | ||
312 | 1529 | |||
313 | 1530 | def test_getTranslationRows_sequence(self): | ||
314 | 1531 | # Test for correct sorting of obsolete messages (where sequence=0). | ||
315 | 1532 | msgsets = [ | ||
316 | 1533 | self._createMessageSet(msg) for msg in self.TEST_MESSAGES] | ||
317 | 1534 | for rownum, row in enumerate( | ||
318 | 1535 | self.pofile.getTranslationRows()): | ||
319 | 1536 | self.failUnlessEqual( | ||
320 | 1537 | row.sequence, self.EXPECTED_SEQUENCE[rownum], | ||
321 | 1538 | "getTranslationRows does not sort obsolete messages " | ||
322 | 1539 | "(sequence=0) to the end of the file.") | ||
323 | 1540 | |||
324 | 1505 | 1541 | ||
325 | 1506 | def test_suite(): | 1542 | def test_suite(): |
326 | 1507 | return unittest.TestLoader().loadTestsFromName(__name__) | 1543 | return unittest.TestLoader().loadTestsFromName(__name__) |
327 | 1508 | 1544 | ||
328 | === modified file 'lib/lp/translations/tests/test_potmsgset.py' | |||
329 | --- lib/lp/translations/tests/test_potmsgset.py 2009-08-26 16:24:02 +0000 | |||
330 | +++ lib/lp/translations/tests/test_potmsgset.py 2009-10-26 15:28:11 +0000 | |||
331 | @@ -20,7 +20,7 @@ | |||
332 | 20 | from lp.registry.interfaces.person import IPersonSet | 20 | from lp.registry.interfaces.person import IPersonSet |
333 | 21 | from lp.services.worlddata.interfaces.language import ILanguageSet | 21 | from lp.services.worlddata.interfaces.language import ILanguageSet |
334 | 22 | from lp.translations.interfaces.potmsgset import ( | 22 | from lp.translations.interfaces.potmsgset import ( |
336 | 23 | POTMsgSetInIncompatibleTemplatesError) | 23 | POTMsgSetInIncompatibleTemplatesError, TranslationCreditsType) |
337 | 24 | from lp.translations.interfaces.translationfileformat import ( | 24 | from lp.translations.interfaces.translationfileformat import ( |
338 | 25 | TranslationFileFormat) | 25 | TranslationFileFormat) |
339 | 26 | from lp.translations.interfaces.translationmessage import TranslationConflict | 26 | from lp.translations.interfaces.translationmessage import TranslationConflict |
340 | @@ -1078,5 +1078,99 @@ | |||
341 | 1078 | self.assertFalse(tm2.is_imported) | 1078 | self.assertFalse(tm2.is_imported) |
342 | 1079 | 1079 | ||
343 | 1080 | 1080 | ||
344 | 1081 | class TestPOTMsgSetTranslationCredits(TestCaseWithFactory): | ||
345 | 1082 | """Test methods related to TranslationCredits.""" | ||
346 | 1083 | |||
347 | 1084 | layer = ZopelessDatabaseLayer | ||
348 | 1085 | |||
349 | 1086 | def setUp(self): | ||
350 | 1087 | super(TestPOTMsgSetTranslationCredits, self).setUp() | ||
351 | 1088 | self.potemplate = self.factory.makePOTemplate() | ||
352 | 1089 | |||
353 | 1090 | def test_creation_credits(self): | ||
354 | 1091 | # Upon creation of a translation credits message, | ||
355 | 1092 | # dummy translations are inserted for each POFile. | ||
356 | 1093 | eo_pofile = self.factory.makePOFile('eo', potemplate=self.potemplate) | ||
357 | 1094 | sr_pofile = self.factory.makePOFile('sr', potemplate=self.potemplate) | ||
358 | 1095 | |||
359 | 1096 | credits = self.factory.makePOTMsgSet( | ||
360 | 1097 | self.potemplate, u'translator-credits', sequence=1) | ||
361 | 1098 | |||
362 | 1099 | eo_translation = credits.getCurrentTranslationMessage( | ||
363 | 1100 | self.potemplate, eo_pofile.language) | ||
364 | 1101 | self.assertIsNot(None, eo_translation, | ||
365 | 1102 | "Translation credits are not translated upon creation.") | ||
366 | 1103 | |||
367 | 1104 | sr_translation = credits.getCurrentTranslationMessage( | ||
368 | 1105 | self.potemplate, sr_pofile.language) | ||
369 | 1106 | self.assertIsNot(None, sr_translation, | ||
370 | 1107 | "Translation credits are not translated upon " | ||
371 | 1108 | "creation in 2nd POFile.") | ||
372 | 1109 | |||
373 | 1110 | def test_creation_not_translated(self): | ||
374 | 1111 | # Normal messages do not receive a dummy translation. | ||
375 | 1112 | eo_pofile = self.factory.makePOFile('eo', potemplate=self.potemplate) | ||
376 | 1113 | |||
377 | 1114 | potmsgset = self.factory.makePOTMsgSet(self.potemplate, sequence=1) | ||
378 | 1115 | eo_translation = potmsgset.getCurrentTranslationMessage( | ||
379 | 1116 | self.potemplate, eo_pofile.language) | ||
380 | 1117 | self.assertIs(None, eo_translation) | ||
381 | 1118 | |||
382 | 1119 | def test_creation_not_imported(self): | ||
383 | 1120 | # Dummy translation for translation credits are not created as | ||
384 | 1121 | # imported and can therefore be overwritten by later imports. | ||
385 | 1122 | eo_pofile = self.factory.makePOFile('eo', potemplate=self.potemplate) | ||
386 | 1123 | imported_credits = u'Imported credits.' | ||
387 | 1124 | |||
388 | 1125 | credits = self.factory.makePOTMsgSet( | ||
389 | 1126 | self.potemplate, u'translator-credits', sequence=1) | ||
390 | 1127 | translation = self.factory.makeTranslationMessage(eo_pofile, credits, | ||
391 | 1128 | translations=[imported_credits], is_imported=True) | ||
392 | 1129 | |||
393 | 1130 | eo_translation = credits.getCurrentTranslationMessage( | ||
394 | 1131 | self.potemplate, eo_pofile.language) | ||
395 | 1132 | self.assertEqual(imported_credits, eo_translation.msgstr0.translation, | ||
396 | 1133 | "Imported translation credits do not replace dummy credits.") | ||
397 | 1134 | |||
398 | 1135 | def test_translation_credits_gnome(self): | ||
399 | 1136 | # Detect all known variations of Gnome translator credits. | ||
400 | 1137 | gnome_credits = [ | ||
401 | 1138 | u'translator-credits', | ||
402 | 1139 | u'translator_credits', | ||
403 | 1140 | u'translation-credits', | ||
404 | 1141 | ] | ||
405 | 1142 | for sequence, credits_string in enumerate(gnome_credits): | ||
406 | 1143 | credits = self.factory.makePOTMsgSet( | ||
407 | 1144 | self.potemplate, credits_string, sequence=sequence+1) | ||
408 | 1145 | self.assertTrue(credits.is_translation_credit) | ||
409 | 1146 | self.assertEqual(TranslationCreditsType.GNOME, | ||
410 | 1147 | credits.translation_credits_type) | ||
411 | 1148 | |||
412 | 1149 | def test_translation_credits_kde(self): | ||
413 | 1150 | # Detect all known variations of KDE translator credits. | ||
414 | 1151 | kde_credits = [ | ||
415 | 1152 | (u'Your emails', u'EMAIL OF TRANSLATORS', | ||
416 | 1153 | TranslationCreditsType.KDE_EMAILS), | ||
417 | 1154 | (u'Your names', u'NAME OF TRANSLATORS', | ||
418 | 1155 | TranslationCreditsType.KDE_NAMES), | ||
419 | 1156 | ] | ||
420 | 1157 | sequence = 0 | ||
421 | 1158 | for credits_string, context, credits_type in kde_credits: | ||
422 | 1159 | sequence += 1 | ||
423 | 1160 | credits = self.factory.makePOTMsgSet( | ||
424 | 1161 | self.potemplate, credits_string, | ||
425 | 1162 | context=context, sequence=sequence) | ||
426 | 1163 | self.assertTrue(credits.is_translation_credit) | ||
427 | 1164 | self.assertEqual(credits_type, credits.translation_credits_type) | ||
428 | 1165 | |||
429 | 1166 | # Old KDE style. | ||
430 | 1167 | sequence += 1 | ||
431 | 1168 | credits = self.factory.makePOTMsgSet( | ||
432 | 1169 | self.potemplate, u'_: %s\n%s' % (context, credits_string), | ||
433 | 1170 | sequence=sequence) | ||
434 | 1171 | self.assertTrue(credits.is_translation_credit) | ||
435 | 1172 | self.assertEqual(credits_type, credits.translation_credits_type) | ||
436 | 1173 | |||
437 | 1174 | |||
438 | 1081 | def test_suite(): | 1175 | def test_suite(): |
439 | 1082 | return unittest.TestLoader().loadTestsFromName(__name__) | 1176 | return unittest.TestLoader().loadTestsFromName(__name__) |
440 | 1083 | 1177 | ||
441 | === modified file 'lib/lp/translations/tests/test_shared_potemplate.py' | |||
442 | --- lib/lp/translations/tests/test_shared_potemplate.py 2009-07-17 00:26:05 +0000 | |||
443 | +++ lib/lp/translations/tests/test_shared_potemplate.py 2009-10-26 15:28:11 +0000 | |||
444 | @@ -53,32 +53,20 @@ | |||
445 | 53 | 53 | ||
446 | 54 | def test_getPOTMsgSetByMsgIDText(self): | 54 | def test_getPOTMsgSetByMsgIDText(self): |
447 | 55 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate, | 55 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate, |
458 | 56 | singular="Open file") | 56 | singular="Open file", |
459 | 57 | 57 | sequence=2) | |
460 | 58 | # It's still not present in the PO template. | 58 | |
461 | 59 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByMsgIDText( | 59 | # We can retrieve the potmsgset by its ID text. |
452 | 60 | "Open file") | ||
453 | 61 | self.assertEquals(read_potmsgset, None) | ||
454 | 62 | |||
455 | 63 | # To actually insert it into a POTemplate, it needs | ||
456 | 64 | # a sequence to be set. | ||
457 | 65 | potmsgset.setSequence(self.devel_potemplate, 2) | ||
462 | 66 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByMsgIDText( | 60 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByMsgIDText( |
463 | 67 | "Open file") | 61 | "Open file") |
464 | 68 | self.assertEquals(potmsgset, read_potmsgset) | 62 | self.assertEquals(potmsgset, read_potmsgset) |
465 | 69 | 63 | ||
466 | 70 | def test_getPOTMsgSetBySequence(self): | 64 | def test_getPOTMsgSetBySequence(self): |
467 | 71 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate) | ||
468 | 72 | sequence = self.factory.getUniqueInteger() | 65 | sequence = self.factory.getUniqueInteger() |
478 | 73 | 66 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate, | |
479 | 74 | # It's still not present in the PO template. | 67 | sequence=sequence) |
480 | 75 | read_potmsgset = self.devel_potemplate.getPOTMsgSetBySequence( | 68 | |
481 | 76 | sequence) | 69 | # We can retrieve the potmsgset by its sequence. |
473 | 77 | self.assertEquals(read_potmsgset, None) | ||
474 | 78 | |||
475 | 79 | # Now we set the appropriate sequence in a potemplate and see that | ||
476 | 80 | # it works. | ||
477 | 81 | potmsgset.setSequence(self.devel_potemplate, sequence) | ||
482 | 82 | read_potmsgset = self.devel_potemplate.getPOTMsgSetBySequence( | 70 | read_potmsgset = self.devel_potemplate.getPOTMsgSetBySequence( |
483 | 83 | sequence) | 71 | sequence) |
484 | 84 | self.assertEquals(potmsgset, read_potmsgset) | 72 | self.assertEquals(potmsgset, read_potmsgset) |
485 | @@ -89,16 +77,11 @@ | |||
486 | 89 | self.assertEquals(read_potmsgset, None) | 77 | self.assertEquals(read_potmsgset, None) |
487 | 90 | 78 | ||
488 | 91 | def test_getPOTMsgSetByID(self): | 79 | def test_getPOTMsgSetByID(self): |
490 | 92 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate) | 80 | potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate, |
491 | 81 | sequence=3) | ||
492 | 93 | id = potmsgset.id | 82 | id = potmsgset.id |
493 | 94 | 83 | ||
501 | 95 | # It's still not present in the PO template. | 84 | # We can retrieve the potmsgset by its ID. |
495 | 96 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByID(id) | ||
496 | 97 | self.assertEquals(read_potmsgset, None) | ||
497 | 98 | |||
498 | 99 | # Now we set the appropriate sequence in a potemplate and see that | ||
499 | 100 | # we can get it by ID. | ||
500 | 101 | potmsgset.setSequence(self.devel_potemplate, 3) | ||
502 | 102 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByID(id) | 85 | read_potmsgset = self.devel_potemplate.getPOTMsgSetByID(id) |
503 | 103 | self.assertEquals(potmsgset, read_potmsgset) | 86 | self.assertEquals(potmsgset, read_potmsgset) |
504 | 104 | 87 | ||
505 | @@ -122,16 +105,6 @@ | |||
506 | 122 | present_msgid_singular, present_msgid_plural, present_context) | 105 | present_msgid_singular, present_msgid_plural, present_context) |
507 | 123 | self.assertEquals(has_message_id, True) | 106 | self.assertEquals(has_message_id, True) |
508 | 124 | 107 | ||
509 | 125 | # A new POTMsgSet that is not part of the POTemplate cannot | ||
510 | 126 | # be gotten using hasMessageID on a POTemplate. | ||
511 | 127 | absent_potmsgset = self.factory.makePOTMsgSet(self.devel_potemplate) | ||
512 | 128 | absent_msgid_singular = absent_potmsgset.msgid_singular | ||
513 | 129 | absent_msgid_plural = absent_potmsgset.msgid_plural | ||
514 | 130 | absent_context = absent_potmsgset.msgid_plural | ||
515 | 131 | has_message_id = naked_potemplate.hasMessageID( | ||
516 | 132 | absent_msgid_singular, absent_msgid_plural, absent_context) | ||
517 | 133 | self.assertEquals(has_message_id, False) | ||
518 | 134 | |||
519 | 135 | def test_hasPluralMessage(self): | 108 | def test_hasPluralMessage(self): |
520 | 136 | naked_potemplate = removeSecurityProxy(self.devel_potemplate) | 109 | naked_potemplate = removeSecurityProxy(self.devel_potemplate) |
521 | 137 | 110 | ||
522 | 138 | 111 | ||
523 | === removed file 'lib/lp/translations/tests/test_vpoexport.py' | |||
524 | --- lib/lp/translations/tests/test_vpoexport.py 2009-07-22 11:52:40 +0000 | |||
525 | +++ lib/lp/translations/tests/test_vpoexport.py 1970-01-01 00:00:00 +0000 | |||
526 | @@ -1,62 +0,0 @@ | |||
527 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | ||
528 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
529 | 3 | |||
530 | 4 | """PO file export view tests.""" | ||
531 | 5 | |||
532 | 6 | __metaclass__ = type | ||
533 | 7 | |||
534 | 8 | import unittest | ||
535 | 9 | import transaction | ||
536 | 10 | |||
537 | 11 | from lp.testing.factory import LaunchpadObjectFactory | ||
538 | 12 | from canonical.testing import LaunchpadZopelessLayer | ||
539 | 13 | |||
540 | 14 | # The sequence number 0 is put at the beginning of the data to verify that | ||
541 | 15 | # it really gets sorted to the end. | ||
542 | 16 | TEST_MESSAGES = [ | ||
543 | 17 | {'msgid':'computer', 'string':'komputilo', 'sequence':0}, | ||
544 | 18 | {'msgid':'mouse', 'string':'muso', 'sequence':0}, | ||
545 | 19 | {'msgid':'Good morning', 'string':'Bonan matenon', 'sequence':2}, | ||
546 | 20 | {'msgid':'Thank you', 'string':'Dankon', 'sequence':1}, | ||
547 | 21 | ] | ||
548 | 22 | EXPECTED_SEQUENCE = [1, 2, 0, 0] | ||
549 | 23 | |||
550 | 24 | class VPOExportSetTestCase(unittest.TestCase): | ||
551 | 25 | """Test the PO file export view.""" | ||
552 | 26 | layer = LaunchpadZopelessLayer | ||
553 | 27 | |||
554 | 28 | def _createMessageSet(self, testmsg): | ||
555 | 29 | # Create a message set from the test data. | ||
556 | 30 | msgset = self.potemplate.createMessageSetFromText( | ||
557 | 31 | testmsg['msgid'], None) | ||
558 | 32 | msgset.setSequence(self.potemplate, testmsg['sequence']) | ||
559 | 33 | msgset.updateTranslation( | ||
560 | 34 | self.pofile, self.submitter_person, | ||
561 | 35 | {0:testmsg['string'],}, | ||
562 | 36 | True, None, force_edition_rights=True) | ||
563 | 37 | |||
564 | 38 | def setUp(self): | ||
565 | 39 | factory = LaunchpadObjectFactory() | ||
566 | 40 | |||
567 | 41 | # Create a PO file and fill with test data. | ||
568 | 42 | self.potemplate = factory.makePOTemplate() | ||
569 | 43 | self.pofile = factory.makePOFile('eo', self.potemplate) | ||
570 | 44 | self.submitter_person = factory.makePerson() | ||
571 | 45 | self.msgsets = [ | ||
572 | 46 | self._createMessageSet(msg) for msg in TEST_MESSAGES] | ||
573 | 47 | |||
574 | 48 | transaction.commit() | ||
575 | 49 | |||
576 | 50 | def test_getTranslationRows_sequence(self): | ||
577 | 51 | # Test for correct sorting of obsolete messages (where sequence=0). | ||
578 | 52 | for rownum, row in enumerate( | ||
579 | 53 | self.pofile.getTranslationRows()): | ||
580 | 54 | self.failUnlessEqual( | ||
581 | 55 | row.sequence, EXPECTED_SEQUENCE[rownum], | ||
582 | 56 | "VPOExportSet does not sort obsolete messages (sequence=0) " | ||
583 | 57 | "to the end of the file.") | ||
584 | 58 | |||
585 | 59 | def test_suite(): | ||
586 | 60 | suite = unittest.TestSuite() | ||
587 | 61 | suite.addTest(unittest.makeSuite(VPOExportSetTestCase)) | ||
588 | 62 | return suite |
= Details =
See bug 128324.
This branch only contains half the fix as proposed in the bug. It adds the creation of dummy translations to the creation of POTMsgSets. The question of where the creation of POTMsgSets actually happens lead to some more related changes so that it is best to land this part separately now.
A POTMsgSet is created by POTemplate. createPOTMsgSet FromMsgIDs( ) whose only call site is in POTemplate. createMessageSe tFromText( ). This used to create an "unbound" POTMsgSet that is not linked to a POTemplate by a TranslationTemp lateItem instance. This had to be done by a subsequent call to setSequence.
Having identified createPOTMsgSet FromMsgIDs as the place where POTMsgSets are born, I thought that might be a good place to put the "create dummy translations for all pofiles that belong to this potemplate". But wait, the POTMsgSet had not yet officially been linked to the POTemplate, so setting translations in its POFile seems premature.
The first obvious solution was to do it in setSequence but that seemed to be too much because that methods gets called a lot when re-importing templates where the order has changed.
So really the question is: Should unbound POTMsgSets exist at all? My answer is a clear: No, there is no point in having them. The logical solution is to set the sequence to 0 in createPOTMsgSet FromMsgIDs which the only production call site for createMessageSe tFromText had done anyway.
So it was only the makePOTMsgSet factory method that needed to be adjusted too and any test that depended on this missfeature. Luckily they were few.
== Implementation details == setTranslationC reditsToTransla ted separately does not make sense anymore because the factory method will already call it. So it is now tested as part of the creation test.
I decided to move some tests from the doctest to the unit tests for better coverage. Also, testing POTMsgSet.
One of the call sites for createMessageSe tFromText was in the old test_vpoexport.py whose content I could move to test_pofile.py completely, thereby removing this relict.
== Test ==
Run the new tests and those affected by the change. I ran all of lp.translations tests to find out which were affected by the test. (A full ec2 test run has taken place since then, too.)
bin/test -vvt potmsgset.txt -t TestTranslation Credits -t getTranslationR ows_sequence -t shared_potemplate
== QA/Demo ==
1. Download an existing potemplate that does not have translator-credits.
2. Add translator-credits to the template and import it again.
3. Filter for untranslated messages in any of the languages that this template has translations in.
4. The translator-credits message should not appear among the untranslated messages.
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: testing/ factory. py translations/ doc/potmsgset. txt translations/ interfaces/ potemplate. py translations/ model/potemplat e.py translations/ tests/test_ pofile. py translations/ tests/test_ potmsgset. py translations/ tests/test_ shared_ potemplate. py
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== Pyli...