Imported Upstream version 0.10
Stuart Prescott
10 years ago
17 | 17 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
18 | 18 | # SOFTWARE. |
19 | 19 | |
20 | PYTHON = python3 | |
20 | 21 | PREFIX = /usr/local |
21 | 22 | DESTDIR = |
22 | 23 | |
49 | 50 | |
50 | 51 | .PHONY: test |
51 | 52 | test: |
52 | python3 -c 'import nose; nose.main()' -v | |
53 | $(PYTHON) -c 'import nose; nose.main()' -v | |
53 | 54 | |
54 | 55 | # vim:ts=4 sw=4 noet |
24 | 24 | This normally indicates an error in the plural form expression. |
25 | 25 | references = |
26 | 26 | http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html |
27 | ||
28 | [boilerplate-in-content-type] | |
29 | severity = important | |
30 | certainty = certain | |
31 | description = | |
32 | The Content-Type header field contains xgettext boilerplate. | |
33 | It should be in the form ``text/plain; charset=``\ *encoding*. | |
34 | references = | |
35 | http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html | |
27 | 36 | |
28 | 37 | [boilerplate-in-language-team] |
29 | 38 | severity = minor |
98 | 107 | references = |
99 | 108 | http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html |
100 | 109 | |
110 | [conflict-marker-in-header-entry] | |
111 | severity = serious | |
112 | certainty = certain | |
113 | description = | |
114 | The header contains a conflict marker (**#-#-#-#-#** *…* **#-#-#-#-#**). | |
115 | The conflict will have to be resolved manually. | |
116 | references = | |
117 | http://www.gnu.org/software/gettext/manual/html_node/Creating-Compendia.html#Creating-Compendia | |
118 | ||
119 | [conflict-marker-in-translation] | |
120 | severity = serious | |
121 | certainty = possible | |
122 | description = | |
123 | One of the translated messages appear to contain a conflict marker (**#-#-#-#-#** *…* **#-#-#-#-#**). | |
124 | The conflict will have to be resolved manually. | |
125 | references = | |
126 | http://www.gnu.org/software/gettext/manual/html_node/Creating-Compendia.html#Creating-Compendia | |
127 | ||
101 | 128 | [date-from-future] |
102 | 129 | severity = normal |
103 | 130 | certainty = certain |
105 | 132 | The date refers to the future. As such, it's extremely unlikely to be correct. |
106 | 133 | |
107 | 134 | [duplicate-header-field] |
108 | severity = serious | |
109 | certainty = certain | |
135 | severity = minor | |
136 | certainty = wild-guess | |
110 | 137 | description = |
111 | 138 | This file contains multiple header fields of the same name. |
139 | ||
140 | [duplicate-header-field-content-transfer-encoding] | |
141 | severity = pedantic | |
142 | certainty = certain | |
143 | description = | |
144 | This file contains multiple Content-Transfer-Encoding header fields. | |
145 | ||
146 | [duplicate-header-field-content-type] | |
147 | severity = serious | |
148 | certainty = certain | |
149 | description = | |
150 | This file contains multiple Content-Type header fields. | |
151 | ||
152 | [duplicate-header-field-date] | |
153 | severity = normal | |
154 | certainty = certain | |
155 | description = | |
156 | This file contains multiple date header fields of the same name. | |
157 | ||
158 | [duplicate-header-field-language] | |
159 | severity = important | |
160 | certainty = certain | |
161 | description = | |
162 | This file contains multiple Language header fields. | |
163 | ||
164 | [duplicate-header-field-language-team] | |
165 | severity = normal | |
166 | certainty = certain | |
167 | description = | |
168 | This file contains multiple Language-Team header fields. | |
169 | ||
170 | [duplicate-header-field-last-translator] | |
171 | severity = normal | |
172 | certainty = certain | |
173 | description = | |
174 | This file contains multiple Last-Translator header fields. | |
175 | ||
176 | [duplicate-header-field-mime-version] | |
177 | severity = pedantic | |
178 | certainty = certain | |
179 | description = | |
180 | This file contains multiple MIME-Version header fields. | |
181 | ||
182 | [duplicate-header-field-plural-forms] | |
183 | severity = serious | |
184 | certainty = certain | |
185 | description = | |
186 | This file contains multiple Plural-Forms header fields. | |
187 | ||
188 | [duplicate-header-field-project-id-version] | |
189 | severity = minor | |
190 | certainty = certain | |
191 | description = | |
192 | This file contains multiple Project-Id-Version header fields. | |
193 | ||
194 | [duplicate-header-field-report-msgid-bugs-to] | |
195 | severity = normal | |
196 | certainty = certain | |
197 | description = | |
198 | This file contains multiple Report-Msgid-Bugs-To header fields. | |
112 | 199 | |
113 | 200 | [duplicate-message-definition] |
114 | 201 | severity = serious |
130 | 217 | shouldn't be included in this field. |
131 | 218 | references = |
132 | 219 | http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html |
220 | ||
221 | [fuzzy-header-entry] | |
222 | severity = pedantic | |
223 | certainty = certain | |
224 | description = | |
225 | The header entry is marked as fuzzy. | |
226 | To increase operability with very old (<< 0.11) **msgfmt**\ (1) versions, | |
227 | which didn't support fuzzy header entries, it shouldn't be marked as such. | |
228 | references = | |
229 | http://git.savannah.gnu.org/cgit/gettext.git/tree/NEWS?id=7808a2b730c4#n44 | |
133 | 230 | |
134 | 231 | [inconsistent-leading-newlines] |
135 | 232 | severity = important |
272 | 369 | severity = minor |
273 | 370 | certainty = possible |
274 | 371 | description = |
275 | Language-Team and Last-Translator header fields contains the same e-mail | |
372 | Language-Team and Last-Translator header fields contain the same e-mail | |
276 | 373 | address. |
277 | 374 | |
278 | 375 | [language-variant-does-not-affect-translation] |
0 | export LC_ALL=C | |
1 | ||
0 | 2 | rst2man = $(or $(shell which rst2man),rst2man.py) |
1 | 3 | exe = i18nspector |
2 | 4 | |
8 | 10 | mv $(@).tmp $(@) |
9 | 11 | |
10 | 12 | $(exe).1: $(exe).txt tags.txt |
11 | $(rst2man) < $(<) > $(@).tmp | |
13 | $(rst2man) --input-encoding=UTF-8:strict < $(<) > $(@).tmp | |
14 | sed -i -e 's/^[.]de1/.de/' $(@).tmp # work-around for http://bugs.debian.org/710678 | |
15 | sed -i -e "s/\([a-z]\)\\\\(aq\([a-z]\)/\1'\2/g" $(@).tmp # prefer ' to \(aq when used as an apostrophe | |
12 | 16 | mv $(@).tmp $(@) |
13 | 17 | |
14 | 18 | .PHONY: clean |
0 | i18nspector (0.10) unstable; urgency=low | |
1 | ||
2 | * Summary of tag changes: | |
3 | + Added: | |
4 | - boilerplate-in-content-type | |
5 | - conflict-marker-in-header-entry | |
6 | - conflict-marker-in-translation | |
7 | - duplicate-header-field-content-transfer-encoding | |
8 | - duplicate-header-field-content-type | |
9 | - duplicate-header-field-date | |
10 | - duplicate-header-field-language | |
11 | - duplicate-header-field-language-team | |
12 | - duplicate-header-field-last-translator | |
13 | - duplicate-header-field-mime-version | |
14 | - duplicate-header-field-plural-forms | |
15 | - duplicate-header-field-project-id-version | |
16 | - duplicate-header-field-report-msgid-bugs-to | |
17 | - fuzzy-header-entry | |
18 | ||
19 | * Check for boilerplate (“charset=CHARSET”) in the Content-Type header | |
20 | field. | |
21 | * Check header field name syntax. | |
22 | * Overhaul duplicate header field detection. | |
23 | + Emit duplicate-header-field only for non-standard fields. Downgrade | |
24 | duplicate-header-field to minor/wild-guess. | |
25 | + Emit duplicate-header-field-$NAME for standard fields. | |
26 | + Don't trust values of standard header fields if duplicates exist. | |
27 | * Check for conflict markers (“#-#-#-#-# … #-#-#-#-#”). | |
28 | * Check for fuzzy header entries. | |
29 | * Fix a typo in the language-team-equal-to-last-translator description. | |
30 | * Post-process the manual page, so that it can be more easily translated by | |
31 | po4a. | |
32 | * If iconv(3) is available in the C standard library, use it to implement | |
33 | encodings that are not implemented in the Python standard library. | |
34 | * Don't pass -s to iconv(1); it makes GNU iconv quieten errors, and other | |
35 | implementations don't have this option at all. | |
36 | * Improve the test suite: | |
37 | + Add new tests. | |
38 | + Make exception messages raised when a subprocess fails more readable. | |
39 | + Make it possible to use a custom Python interpreter for “make test”. | |
40 | ||
41 | -- Jakub Wilk <jwilk@jwilk.net> Sat, 15 Jun 2013 17:37:22 +0200 | |
42 | ||
0 | 43 | i18nspector (0.9.2) unstable; urgency=low |
1 | 44 | |
2 | 45 | * When emitting broken-encoding, don't output the whole file, but only the |
0 | 0 | .\" Man page generated from reStructuredText. |
1 | 1 | . |
2 | .TH I18NSPECTOR 1 "2013-06-08" "i18nspector 0.9.2" "" | |
2 | .TH I18NSPECTOR 1 "2013-06-15" "i18nspector 0.10" "" | |
3 | 3 | .SH NAME |
4 | 4 | i18nspector \- checking tool for gettext POT, PO and MO files |
5 | 5 | . |
6 | 6 | .nr rst2man-indent-level 0 |
7 | 7 | . |
8 | .de1 rstReportMargin | |
8 | .de rstReportMargin | |
9 | 9 | \\$1 \\n[an-margin] |
10 | 10 | level \\n[rst2man-indent-level] |
11 | 11 | level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] |
14 | 14 | \\n[rst2man-indent1] |
15 | 15 | \\n[rst2man-indent2] |
16 | 16 | .. |
17 | .de1 INDENT | |
17 | .de INDENT | |
18 | 18 | .\" .rstReportMargin pre: |
19 | 19 | . RS \\$1 |
20 | 20 | . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] |
76 | 76 | .SS ancient\-date |
77 | 77 | .sp |
78 | 78 | The date refers to the time before the first GNU gettext release. As such, |
79 | it\(aqs extremely unlikely to be correct. | |
79 | it's extremely unlikely to be correct. | |
80 | 80 | .sp |
81 | 81 | References: |
82 | 82 | .INDENT 0.0 |
128 | 128 | important, possible |
129 | 129 | .UNINDENT |
130 | 130 | .UNINDENT |
131 | .SS boilerplate\-in\-content\-type | |
132 | .sp | |
133 | The Content\-Type header field contains xgettext boilerplate. | |
134 | It should be in the form \fBtext/plain; charset=\fP\fIencoding\fP. | |
135 | .sp | |
136 | References: | |
137 | .INDENT 0.0 | |
138 | .INDENT 3.5 | |
139 | \fI\%http://www.gnu.org/software/gettext/manual/html_node/Header\-Entry.html\fP | |
140 | .UNINDENT | |
141 | .UNINDENT | |
142 | .sp | |
143 | Severity, certainty: | |
144 | .INDENT 0.0 | |
145 | .INDENT 3.5 | |
146 | important, certain | |
147 | .UNINDENT | |
148 | .UNINDENT | |
131 | 149 | .SS boilerplate\-in\-language\-team |
132 | 150 | .sp |
133 | 151 | The Language\-Team header field contains xgettext boilerplate. |
150 | 168 | .SS boilerplate\-in\-last\-translator |
151 | 169 | .sp |
152 | 170 | The Last\-Translator header field contains xgettext boilerplate. |
153 | It should contain the last translator\(aqs name and email address. | |
171 | It should contain the last translator's name and email address. | |
154 | 172 | .sp |
155 | 173 | References: |
156 | 174 | .INDENT 0.0 |
204 | 222 | .UNINDENT |
205 | 223 | .SS broken\-encoding |
206 | 224 | .sp |
207 | Header fields and messages contained by this file couldn\(aqt be decoded to | |
225 | Header fields and messages contained by this file couldn't be decoded to | |
208 | 226 | Unicode. |
209 | 227 | The usual cause of this is incorrect or missing encoding declaration. |
210 | 228 | .sp |
265 | 283 | important, possible |
266 | 284 | .UNINDENT |
267 | 285 | .UNINDENT |
286 | .SS conflict\-marker\-in\-header\-entry | |
287 | .sp | |
288 | The header contains a conflict marker (\fB#\-#\-#\-#\-#\fP \fI…\fP \fB#\-#\-#\-#\-#\fP). | |
289 | The conflict will have to be resolved manually. | |
290 | .sp | |
291 | References: | |
292 | .INDENT 0.0 | |
293 | .INDENT 3.5 | |
294 | \fI\%http://www.gnu.org/software/gettext/manual/html_node/Creating\-Compendia.html#Creating\-Compendia\fP | |
295 | .UNINDENT | |
296 | .UNINDENT | |
297 | .sp | |
298 | Severity, certainty: | |
299 | .INDENT 0.0 | |
300 | .INDENT 3.5 | |
301 | serious, certain | |
302 | .UNINDENT | |
303 | .UNINDENT | |
304 | .SS conflict\-marker\-in\-translation | |
305 | .sp | |
306 | One of the translated messages appear to contain a conflict marker (\fB#\-#\-#\-#\-#\fP \fI…\fP \fB#\-#\-#\-#\-#\fP). | |
307 | The conflict will have to be resolved manually. | |
308 | .sp | |
309 | References: | |
310 | .INDENT 0.0 | |
311 | .INDENT 3.5 | |
312 | \fI\%http://www.gnu.org/software/gettext/manual/html_node/Creating\-Compendia.html#Creating\-Compendia\fP | |
313 | .UNINDENT | |
314 | .UNINDENT | |
315 | .sp | |
316 | Severity, certainty: | |
317 | .INDENT 0.0 | |
318 | .INDENT 3.5 | |
319 | serious, possible | |
320 | .UNINDENT | |
321 | .UNINDENT | |
268 | 322 | .SS date\-from\-future |
269 | 323 | .sp |
270 | The date refers to the future. As such, it\(aqs extremely unlikely to be correct. | |
324 | The date refers to the future. As such, it's extremely unlikely to be correct. | |
271 | 325 | .sp |
272 | 326 | Severity, certainty: |
273 | 327 | .INDENT 0.0 |
282 | 336 | Severity, certainty: |
283 | 337 | .INDENT 0.0 |
284 | 338 | .INDENT 3.5 |
339 | minor, wild\-guess | |
340 | .UNINDENT | |
341 | .UNINDENT | |
342 | .SS duplicate\-header\-field\-content\-transfer\-encoding | |
343 | .sp | |
344 | This file contains multiple Content\-Transfer\-Encoding header fields. | |
345 | .sp | |
346 | Severity, certainty: | |
347 | .INDENT 0.0 | |
348 | .INDENT 3.5 | |
349 | pedantic, certain | |
350 | .UNINDENT | |
351 | .UNINDENT | |
352 | .SS duplicate\-header\-field\-content\-type | |
353 | .sp | |
354 | This file contains multiple Content\-Type header fields. | |
355 | .sp | |
356 | Severity, certainty: | |
357 | .INDENT 0.0 | |
358 | .INDENT 3.5 | |
285 | 359 | serious, certain |
286 | 360 | .UNINDENT |
287 | 361 | .UNINDENT |
362 | .SS duplicate\-header\-field\-date | |
363 | .sp | |
364 | This file contains multiple date header fields of the same name. | |
365 | .sp | |
366 | Severity, certainty: | |
367 | .INDENT 0.0 | |
368 | .INDENT 3.5 | |
369 | normal, certain | |
370 | .UNINDENT | |
371 | .UNINDENT | |
372 | .SS duplicate\-header\-field\-language | |
373 | .sp | |
374 | This file contains multiple Language header fields. | |
375 | .sp | |
376 | Severity, certainty: | |
377 | .INDENT 0.0 | |
378 | .INDENT 3.5 | |
379 | important, certain | |
380 | .UNINDENT | |
381 | .UNINDENT | |
382 | .SS duplicate\-header\-field\-language\-team | |
383 | .sp | |
384 | This file contains multiple Language\-Team header fields. | |
385 | .sp | |
386 | Severity, certainty: | |
387 | .INDENT 0.0 | |
388 | .INDENT 3.5 | |
389 | normal, certain | |
390 | .UNINDENT | |
391 | .UNINDENT | |
392 | .SS duplicate\-header\-field\-last\-translator | |
393 | .sp | |
394 | This file contains multiple Last\-Translator header fields. | |
395 | .sp | |
396 | Severity, certainty: | |
397 | .INDENT 0.0 | |
398 | .INDENT 3.5 | |
399 | normal, certain | |
400 | .UNINDENT | |
401 | .UNINDENT | |
402 | .SS duplicate\-header\-field\-mime\-version | |
403 | .sp | |
404 | This file contains multiple MIME\-Version header fields. | |
405 | .sp | |
406 | Severity, certainty: | |
407 | .INDENT 0.0 | |
408 | .INDENT 3.5 | |
409 | pedantic, certain | |
410 | .UNINDENT | |
411 | .UNINDENT | |
412 | .SS duplicate\-header\-field\-plural\-forms | |
413 | .sp | |
414 | This file contains multiple Plural\-Forms header fields. | |
415 | .sp | |
416 | Severity, certainty: | |
417 | .INDENT 0.0 | |
418 | .INDENT 3.5 | |
419 | serious, certain | |
420 | .UNINDENT | |
421 | .UNINDENT | |
422 | .SS duplicate\-header\-field\-project\-id\-version | |
423 | .sp | |
424 | This file contains multiple Project\-Id\-Version header fields. | |
425 | .sp | |
426 | Severity, certainty: | |
427 | .INDENT 0.0 | |
428 | .INDENT 3.5 | |
429 | minor, certain | |
430 | .UNINDENT | |
431 | .UNINDENT | |
432 | .SS duplicate\-header\-field\-report\-msgid\-bugs\-to | |
433 | .sp | |
434 | This file contains multiple Report\-Msgid\-Bugs\-To header fields. | |
435 | .sp | |
436 | Severity, certainty: | |
437 | .INDENT 0.0 | |
438 | .INDENT 3.5 | |
439 | normal, certain | |
440 | .UNINDENT | |
441 | .UNINDENT | |
288 | 442 | .SS duplicate\-message\-definition |
289 | 443 | .sp |
290 | 444 | This file contains multiple definitions of the same message. |
297 | 451 | .UNINDENT |
298 | 452 | .SS empty\-file |
299 | 453 | .sp |
300 | This file doesn\(aqt contain any messages. | |
454 | This file doesn't contain any messages. | |
301 | 455 | .sp |
302 | 456 | Severity, certainty: |
303 | 457 | .INDENT 0.0 |
308 | 462 | .SS encoding\-in\-language\-header\-field |
309 | 463 | .sp |
310 | 464 | The language header field contains encoding declaration. Such information |
311 | shouldn\(aqt be included in this field. | |
465 | shouldn't be included in this field. | |
312 | 466 | .sp |
313 | 467 | References: |
314 | 468 | .INDENT 0.0 |
323 | 477 | minor, certain |
324 | 478 | .UNINDENT |
325 | 479 | .UNINDENT |
480 | .SS fuzzy\-header\-entry | |
481 | .sp | |
482 | The header entry is marked as fuzzy. | |
483 | To increase operability with very old (<< 0.11) \fBmsgfmt\fP(1) versions, | |
484 | which didn't support fuzzy header entries, it shouldn't be marked as such. | |
485 | .sp | |
486 | References: | |
487 | .INDENT 0.0 | |
488 | .INDENT 3.5 | |
489 | \fI\%http://git.savannah.gnu.org/cgit/gettext.git/tree/NEWS?id=7808a2b730c4#n44\fP | |
490 | .UNINDENT | |
491 | .UNINDENT | |
492 | .sp | |
493 | Severity, certainty: | |
494 | .INDENT 0.0 | |
495 | .INDENT 3.5 | |
496 | pedantic, certain | |
497 | .UNINDENT | |
498 | .UNINDENT | |
326 | 499 | .SS inconsistent\-leading\-newlines |
327 | 500 | .sp |
328 | Some strings in an entry start with a newline, but some don\(aqt. | |
501 | Some strings in an entry start with a newline, but some don't. | |
329 | 502 | Either all of them should start with a newline, or none of them should. |
330 | 503 | .sp |
331 | 504 | Severity, certainty: |
336 | 509 | .UNINDENT |
337 | 510 | .SS inconsistent\-number\-of\-plural\-forms |
338 | 511 | .sp |
339 | Number of plural forms in a message definition doesn\(aqt match number of plural | |
512 | Number of plural forms in a message definition doesn't match number of plural | |
340 | 513 | forms declared in another message definition. |
341 | 514 | .sp |
342 | 515 | References: |
356 | 529 | .UNINDENT |
357 | 530 | .SS inconsistent\-trailing\-newlines |
358 | 531 | .sp |
359 | Some strings in an entry end with a newline, but some don\(aqt. | |
532 | Some strings in an entry end with a newline, but some don't. | |
360 | 533 | Either all of them should end with a newline, or none of them should. |
361 | 534 | .sp |
362 | 535 | Severity, certainty: |
367 | 540 | .UNINDENT |
368 | 541 | .SS incorrect\-number\-of\-plural\-forms |
369 | 542 | .sp |
370 | Number of plural forms in a message definition doesn\(aqt match number of plural | |
543 | Number of plural forms in a message definition doesn't match number of plural | |
371 | 544 | forms declared in the header. |
372 | 545 | .sp |
373 | 546 | References: |
387 | 560 | .UNINDENT |
388 | 561 | .SS incorrect\-plural\-forms |
389 | 562 | .sp |
390 | The Plural\-Forms declaration is incorrect, according to i18nspector\(aqs | |
563 | The Plural\-Forms declaration is incorrect, according to i18nspector's | |
391 | 564 | linguistic data. |
392 | 565 | .sp |
393 | 566 | Severity, certainty: |
398 | 571 | .UNINDENT |
399 | 572 | .SS incorrect\-unused\-plural\-forms |
400 | 573 | .sp |
401 | The Plural\-Forms declaration is incorrect, according to i18nspector\(aqs | |
574 | The Plural\-Forms declaration is incorrect, according to i18nspector's | |
402 | 575 | linguistic data. |
403 | 576 | (But there are no translated messages which use plural forms.) |
404 | 577 | .sp |
468 | 641 | .UNINDENT |
469 | 642 | .SS invalid\-language |
470 | 643 | .sp |
471 | The Language header field couldn\(aqt be parsed, or it contains an unknown | |
644 | The Language header field couldn't be parsed, or it contains an unknown | |
472 | 645 | language. |
473 | 646 | .sp |
474 | 647 | References: |
541 | 714 | .UNINDENT |
542 | 715 | .SS invalid\-mo\-file |
543 | 716 | .sp |
544 | This file couldn\(aqt be parsed a MO file. | |
717 | This file couldn't be parsed a MO file. | |
545 | 718 | .sp |
546 | 719 | Severity, certainty: |
547 | 720 | .INDENT 0.0 |
572 | 745 | .SS language\-disparity |
573 | 746 | .sp |
574 | 747 | Language of this file has been declared in multiple places, but the |
575 | declarations don\(aqt match. | |
748 | declarations don't match. | |
576 | 749 | .sp |
577 | 750 | Severity, certainty: |
578 | 751 | .INDENT 0.0 |
582 | 755 | .UNINDENT |
583 | 756 | .SS language\-team\-equal\-to\-last\-translator |
584 | 757 | .sp |
585 | Language\-Team and Last\-Translator header fields contains the same e\-mail | |
758 | Language\-Team and Last\-Translator header fields contain the same e\-mail | |
586 | 759 | address. |
587 | 760 | .sp |
588 | 761 | Severity, certainty: |
611 | 784 | .UNINDENT |
612 | 785 | .SS no\-content\-transfer\-encoding\-header\-field |
613 | 786 | .sp |
614 | The Content\-Transfer\-Encoding header field doesn\(aqt exist. It should be set to | |
787 | The Content\-Transfer\-Encoding header field doesn't exist. It should be set to | |
615 | 788 | \fB8bit\fP. |
616 | 789 | .sp |
617 | 790 | References: |
631 | 804 | .UNINDENT |
632 | 805 | .SS no\-content\-type\-header\-field |
633 | 806 | .sp |
634 | The Content\-Type header field doesn\(aqt exist. It should be set to | |
807 | The Content\-Type header field doesn't exist. It should be set to | |
635 | 808 | \fBtext/plain; charset=\fP\fIencoding\fP. |
636 | 809 | .sp |
637 | 810 | Note that in the absence of encoding declaration, i18nspector assumes ASCII |
654 | 827 | .UNINDENT |
655 | 828 | .SS no\-date\-header\-field |
656 | 829 | .sp |
657 | The date header field doesn\(aqt exist. | |
830 | The date header field doesn't exist. | |
658 | 831 | .sp |
659 | 832 | References: |
660 | 833 | .INDENT 0.0 |
671 | 844 | .UNINDENT |
672 | 845 | .SS no\-language\-header\-field |
673 | 846 | .sp |
674 | The Language header field doesn\(aqt exist. | |
847 | The Language header field doesn't exist. | |
675 | 848 | .sp |
676 | 849 | References: |
677 | 850 | .INDENT 0.0 |
707 | 880 | .UNINDENT |
708 | 881 | .SS no\-last\-translator\-header\-field |
709 | 882 | .sp |
710 | The Last\-Translator header field doesn\(aqt exist. | |
711 | It should contain the last translator\(aqs name and email address. | |
883 | The Last\-Translator header field doesn't exist. | |
884 | It should contain the last translator's name and email address. | |
712 | 885 | .sp |
713 | 886 | References: |
714 | 887 | .INDENT 0.0 |
725 | 898 | .UNINDENT |
726 | 899 | .SS no\-mime\-version\-header\-field |
727 | 900 | .sp |
728 | The MIME\-Version header field does\(aqt exist. It should be to set to \fB1.0\fP. | |
901 | The MIME\-Version header field does't exist. It should be to set to \fB1.0\fP. | |
729 | 902 | .sp |
730 | 903 | References: |
731 | 904 | .INDENT 0.0 |
742 | 915 | .UNINDENT |
743 | 916 | .SS no\-package\-name\-in\-project\-id\-version |
744 | 917 | .sp |
745 | The Project\-Id\-Version header field doesn\(aqt appear to contain any name. | |
918 | The Project\-Id\-Version header field doesn't appear to contain any name. | |
746 | 919 | It should contain both the name and the version of the package. |
747 | 920 | .sp |
748 | 921 | References: |
833 | 1006 | .UNINDENT |
834 | 1007 | .SS no\-version\-in\-project\-id\-version |
835 | 1008 | .sp |
836 | The Project\-Id\-Version header field doesn\(aqt appear to contain any version. | |
1009 | The Project\-Id\-Version header field doesn't appear to contain any version. | |
837 | 1010 | It should contain both the name and the version of the package. |
838 | 1011 | .sp |
839 | 1012 | References: |
925 | 1098 | .UNINDENT |
926 | 1099 | .SS syntax\-error\-in\-po\-file |
927 | 1100 | .sp |
928 | This file couldn\(aqt be parsed a PO file. | |
1101 | This file couldn't be parsed a PO file. | |
929 | 1102 | In some rare cases this is due to incorrect or missing encoding declaration. |
930 | 1103 | .sp |
931 | 1104 | References: |
975 | 1148 | .UNINDENT |
976 | 1149 | .SS unknown\-encoding |
977 | 1150 | .sp |
978 | This file declares an encoding that couldn\(aqt be recognized by i18nspector. | |
1151 | This file declares an encoding that couldn't be recognized by i18nspector. | |
979 | 1152 | It might be a typo. |
980 | 1153 | Absence of encoding information will prevent i18nspector from performing |
981 | 1154 | further checks. |
988 | 1161 | .UNINDENT |
989 | 1162 | .SS unknown\-file\-type |
990 | 1163 | .sp |
991 | File format of this file couldn\(aqt be recognized. | |
1164 | File format of this file couldn't be recognized. | |
992 | 1165 | It might be a bug in i18nspector. |
993 | 1166 | .sp |
994 | 1167 | Severity, certainty: |
1018 | 1191 | .UNINDENT |
1019 | 1192 | .SS unknown\-poedit\-language |
1020 | 1193 | .sp |
1021 | Language declared in X\-Poedit\-Language couldn\(aqt be recognized. | |
1194 | Language declared in X\-Poedit\-Language couldn't be recognized. | |
1022 | 1195 | It might be a bug in i18nspector. |
1023 | 1196 | .sp |
1024 | 1197 | Severity, certainty: |
6 | 6 | ---------------------------------------------- |
7 | 7 | |
8 | 8 | :manual section: 1 |
9 | :version: i18nspector 0.9.2 | |
9 | :version: i18nspector 0.10 | |
10 | 10 | :date: |date| |
11 | 11 | |
12 | 12 | Synopsis |
39 | 39 | Severity, certainty: |
40 | 40 | |
41 | 41 | important, possible |
42 | ||
43 | boilerplate-in-content-type | |
44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
45 | The Content-Type header field contains xgettext boilerplate. | |
46 | It should be in the form ``text/plain; charset=``\ *encoding*. | |
47 | ||
48 | References: | |
49 | ||
50 | http://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html | |
51 | ||
52 | Severity, certainty: | |
53 | ||
54 | important, certain | |
42 | 55 | |
43 | 56 | boilerplate-in-language-team |
44 | 57 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
142 | 155 | |
143 | 156 | important, possible |
144 | 157 | |
158 | conflict-marker-in-header-entry | |
159 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
160 | The header contains a conflict marker (**#-#-#-#-#** *…* **#-#-#-#-#**). | |
161 | The conflict will have to be resolved manually. | |
162 | ||
163 | References: | |
164 | ||
165 | http://www.gnu.org/software/gettext/manual/html_node/Creating-Compendia.html#Creating-Compendia | |
166 | ||
167 | Severity, certainty: | |
168 | ||
169 | serious, certain | |
170 | ||
171 | conflict-marker-in-translation | |
172 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
173 | One of the translated messages appear to contain a conflict marker (**#-#-#-#-#** *…* **#-#-#-#-#**). | |
174 | The conflict will have to be resolved manually. | |
175 | ||
176 | References: | |
177 | ||
178 | http://www.gnu.org/software/gettext/manual/html_node/Creating-Compendia.html#Creating-Compendia | |
179 | ||
180 | Severity, certainty: | |
181 | ||
182 | serious, possible | |
183 | ||
145 | 184 | date-from-future |
146 | 185 | ~~~~~~~~~~~~~~~~ |
147 | 186 | The date refers to the future. As such, it's extremely unlikely to be correct. |
156 | 195 | |
157 | 196 | Severity, certainty: |
158 | 197 | |
159 | serious, certain | |
198 | minor, wild-guess | |
199 | ||
200 | duplicate-header-field-content-transfer-encoding | |
201 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
202 | This file contains multiple Content-Transfer-Encoding header fields. | |
203 | ||
204 | Severity, certainty: | |
205 | ||
206 | pedantic, certain | |
207 | ||
208 | duplicate-header-field-content-type | |
209 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
210 | This file contains multiple Content-Type header fields. | |
211 | ||
212 | Severity, certainty: | |
213 | ||
214 | serious, certain | |
215 | ||
216 | duplicate-header-field-date | |
217 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
218 | This file contains multiple date header fields of the same name. | |
219 | ||
220 | Severity, certainty: | |
221 | ||
222 | normal, certain | |
223 | ||
224 | duplicate-header-field-language | |
225 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
226 | This file contains multiple Language header fields. | |
227 | ||
228 | Severity, certainty: | |
229 | ||
230 | important, certain | |
231 | ||
232 | duplicate-header-field-language-team | |
233 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
234 | This file contains multiple Language-Team header fields. | |
235 | ||
236 | Severity, certainty: | |
237 | ||
238 | normal, certain | |
239 | ||
240 | duplicate-header-field-last-translator | |
241 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
242 | This file contains multiple Last-Translator header fields. | |
243 | ||
244 | Severity, certainty: | |
245 | ||
246 | normal, certain | |
247 | ||
248 | duplicate-header-field-mime-version | |
249 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
250 | This file contains multiple MIME-Version header fields. | |
251 | ||
252 | Severity, certainty: | |
253 | ||
254 | pedantic, certain | |
255 | ||
256 | duplicate-header-field-plural-forms | |
257 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
258 | This file contains multiple Plural-Forms header fields. | |
259 | ||
260 | Severity, certainty: | |
261 | ||
262 | serious, certain | |
263 | ||
264 | duplicate-header-field-project-id-version | |
265 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
266 | This file contains multiple Project-Id-Version header fields. | |
267 | ||
268 | Severity, certainty: | |
269 | ||
270 | minor, certain | |
271 | ||
272 | duplicate-header-field-report-msgid-bugs-to | |
273 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
274 | This file contains multiple Report-Msgid-Bugs-To header fields. | |
275 | ||
276 | Severity, certainty: | |
277 | ||
278 | normal, certain | |
160 | 279 | |
161 | 280 | duplicate-message-definition |
162 | 281 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
186 | 305 | Severity, certainty: |
187 | 306 | |
188 | 307 | minor, certain |
308 | ||
309 | fuzzy-header-entry | |
310 | ~~~~~~~~~~~~~~~~~~ | |
311 | The header entry is marked as fuzzy. | |
312 | To increase operability with very old (<< 0.11) **msgfmt**\ (1) versions, | |
313 | which didn't support fuzzy header entries, it shouldn't be marked as such. | |
314 | ||
315 | References: | |
316 | ||
317 | http://git.savannah.gnu.org/cgit/gettext.git/tree/NEWS?id=7808a2b730c4#n44 | |
318 | ||
319 | Severity, certainty: | |
320 | ||
321 | pedantic, certain | |
189 | 322 | |
190 | 323 | inconsistent-leading-newlines |
191 | 324 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
384 | 517 | |
385 | 518 | language-team-equal-to-last-translator |
386 | 519 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
387 | Language-Team and Last-Translator header fields contains the same e-mail | |
520 | Language-Team and Last-Translator header fields contain the same e-mail | |
388 | 521 | address. |
389 | 522 | |
390 | 523 | Severity, certainty: |
47 | 47 | Make duplicate-header-field work for .mo files, too. |
48 | 48 | * https://bitbucket.org/izi/polib/issue/36 |
49 | 49 | |
50 | Check for initial stray lines in headers. (They are currently ignored due to | |
51 | deficiencies in the polib parser.) | |
52 | ||
50 | 53 | Add support for configuration files. Once it's implemented: |
51 | 54 | * Disable --debian by default. |
52 | 55 | * Disable --pedantic by default. |
64 | 67 | |
65 | 68 | Run ``msgfmt --check-format`` on PO files. |
66 | 69 | |
70 | Reimplement 8-bit codecs without iconv(1)/iconv(3). | |
71 | ||
72 | Check for hardcoded Language in POT files. | |
73 | ||
74 | Add option for printing tags descriptions. | |
75 | ||
67 | 76 | .. vim:ft=rst |
42 | 42 | pass |
43 | 43 | |
44 | 44 | find_unusual_characters = re.compile( |
45 | r'[\x00-\x08\x0b-\x1a\x1c-\x1f]' # C0 except TAB, LF, ESC | |
46 | r'|\x1b(?!\[)' # ESC, except when followed by [ | |
47 | r'|\x7f' # DEL | |
48 | r'|[\x80-\x9f]' # C1 | |
49 | '|\ufffd' # REPLACEMENT CHARACTER | |
50 | '|[\ufffe\uffff]' # non-characters | |
51 | r'|(?<=\w)\xbf' # INVERTED QUESTION MARK but only directly after a letter | |
45 | r'[\x00-\x08\x0b-\x1a\x1c-\x1f]' # C0 except TAB, LF, ESC | |
46 | r'|\x1b(?!\[)' # ESC, except when followed by [ | |
47 | r'|\x7f' # DEL | |
48 | r'|[\x80-\x9f]' # C1 | |
49 | '|\ufffd' # REPLACEMENT CHARACTER | |
50 | '|[\ufffe\uffff]' # non-characters | |
51 | r'|(?<=\w)\xbf' # INVERTED QUESTION MARK but only directly after a letter | |
52 | 52 | ).findall |
53 | 53 | |
54 | 54 | class Checker(object): |
162 | 162 | broken_encoding = True |
163 | 163 | # check_headers() modifies the file metadata, |
164 | 164 | # so it has to be the first check: |
165 | self.check_headers(file) | |
165 | self.check_headers(file, is_template=is_template) | |
166 | 166 | language = self.check_language(file, is_template=is_template) |
167 | 167 | self.check_plurals(file, is_template=is_template, language=language) |
168 | 168 | encoding = self.check_mime(file, is_template=is_template, language=language) |
175 | 175 | |
176 | 176 | def check_language(self, file, *, is_template): |
177 | 177 | if is_template: |
178 | if not 'Language' in file.metadata: | |
178 | if 'Language' in file.metadata.duplicates: | |
179 | self.tag('duplicate-header-field-language') | |
180 | elif not 'Language' in file.metadata: | |
179 | 181 | self.tag('no-language-header-field') |
180 | 182 | return |
181 | 183 | language = self.options.language |
216 | 218 | language = None |
217 | 219 | else: |
218 | 220 | language_source = 'pathname' |
219 | meta_language = orig_meta_language = file.metadata.get('Language') | |
221 | if 'Language' in file.metadata.duplicates: | |
222 | self.tag('duplicate-header-field-language') | |
223 | duplicate_meta_language = True | |
224 | meta_language = None | |
225 | else: | |
226 | duplicate_meta_language = False | |
227 | meta_language = file.metadata.get('Language') | |
228 | orig_meta_language = meta_language | |
220 | 229 | if meta_language: |
221 | 230 | try: |
222 | 231 | meta_language = linginfo.parse_language(meta_language) |
269 | 278 | poedit_language, tags.safestr('(X-Poedit-Language header field)') |
270 | 279 | ) |
271 | 280 | if language is None: |
272 | if not orig_meta_language: | |
281 | if not orig_meta_language and not duplicate_meta_language: | |
273 | 282 | self.tag('no-language-header-field') |
274 | 283 | self.tag('unable-to-determine-language') |
275 | 284 | return |
276 | if not orig_meta_language: | |
285 | if not orig_meta_language and not duplicate_meta_language: | |
277 | 286 | self.tag('no-language-header-field', tags.safestr('Language:'), language) |
278 | 287 | return language |
279 | 288 | |
280 | 289 | def check_plurals(self, file, *, is_template, language): |
290 | if 'Plural-Forms' in file.metadata.duplicates: | |
291 | self.tag('duplicate-header-field-plural-forms') | |
292 | return | |
281 | 293 | correct_plural_forms = None |
282 | 294 | if language is not None: |
283 | 295 | correct_plural_forms = language.get_plural_forms() |
284 | 296 | plural_forms = file.metadata.get('Plural-Forms') |
285 | has_plurals = False # messages with plural forms (translated or not)? | |
286 | expected_nplurals = {} # number of plurals in _translated_ messages | |
297 | has_plurals = False # messages with plural forms (translated or not)? | |
298 | expected_nplurals = {} # number of plurals in _translated_ messages | |
287 | 299 | for message in file: |
288 | 300 | if message.obsolete: |
289 | 301 | continue |
395 | 407 | self.tag('codomain-error-in-unused-plural-forms', message) |
396 | 408 | |
397 | 409 | def check_mime(self, file, *, is_template, language): |
410 | # MIME-Version: | |
398 | 411 | mime_version = file.metadata.get('MIME-Version') |
399 | if mime_version is not None: | |
412 | if 'MIME-Version' in file.metadata.duplicates: | |
413 | self.tag('duplicate-header-field-mime-version') | |
414 | elif mime_version is not None: | |
400 | 415 | if mime_version != '1.0': |
401 | 416 | self.tag('invalid-mime-version', mime_version, '=>', '1.0') |
402 | 417 | else: |
403 | 418 | self.tag('no-mime-version-header-field', tags.safestr('MIME-Version: 1.0')) |
419 | # Content-Transfer-Encoding: | |
404 | 420 | cte = file.metadata.get('Content-Transfer-Encoding') |
405 | if cte is not None: | |
421 | if 'Content-Transfer-Encoding' in file.metadata.duplicates: | |
422 | self.tag('duplicate-header-field-content-transfer-encoding') | |
423 | elif cte is not None: | |
406 | 424 | if cte != '8bit': |
407 | 425 | self.tag('invalid-content-transfer-encoding', cte, '=>', '8bit') |
408 | 426 | else: |
409 | 427 | self.tag('no-content-transfer-encoding-header-field', tags.safestr('Content-Transfer-Encoding: 8bit')) |
410 | ct = file.metadata.get('Content-Type') | |
428 | # Content-Type: | |
429 | if 'Content-Type' in file.metadata.duplicates: | |
430 | duplicate_ct = True | |
431 | ct = None | |
432 | else: | |
433 | duplicate_ct = False | |
434 | ct = file.metadata.get('Content-Type') | |
411 | 435 | encoding = None |
412 | 436 | content_type_hint = 'text/plain; charset=<encoding>' |
413 | 437 | if ct is not None: |
418 | 442 | try: |
419 | 443 | is_ascii_compatible = encinfo.is_ascii_compatible_encoding(encoding) |
420 | 444 | except LookupError: |
421 | if is_template and (encoding == 'CHARSET'): | |
422 | pass | |
445 | if encoding == 'CHARSET': | |
446 | if not is_template: | |
447 | self.tag('boilerplate-in-content-type', ct) | |
423 | 448 | else: |
424 | 449 | self.tag('unknown-encoding', encoding) |
425 | 450 | encoding = None |
447 | 472 | self.tag('invalid-content-type', ct, '=>', content_type_hint) |
448 | 473 | else: |
449 | 474 | self.tag('invalid-content-type', ct, '=>', content_type_hint) |
475 | elif duplicate_ct: | |
476 | self.tag('duplicate-header-field-content-type') | |
450 | 477 | else: |
451 | 478 | self.tag('no-content-type-header-field', tags.safestr('Content-Type: ' + content_type_hint)) |
452 | 479 | return encoding |
453 | 480 | |
454 | 481 | def check_dates(self, file, *, is_template): |
455 | 482 | for field in 'POT-Creation-Date', 'PO-Revision-Date': |
483 | if field in file.metadata.duplicates: | |
484 | self.tag('duplicate-header-field-date', field) | |
485 | continue | |
456 | 486 | date = file.metadata.get(field) |
457 | 487 | if date is None: |
458 | 488 | self.tag('no-date-header-field', field) |
472 | 502 | self.tag('ancient-date', tags.safestr(field + ':'), date) |
473 | 503 | |
474 | 504 | def check_project(self, file): |
505 | # Project-Id-Version: | |
475 | 506 | project_id_version = file.metadata.get('Project-Id-Version') |
507 | if 'Project-Id-Version' in file.metadata.duplicates: | |
508 | self.tag('duplicate-header-field-project-id-version') | |
476 | 509 | if project_id_version is None: |
477 | 510 | self.tag('no-project-id-version-header-field') |
478 | 511 | elif project_id_version in {'PACKAGE VERSION', 'PROJECT VERSION'}: |
482 | 515 | self.tag('no-package-name-in-project-id-version', project_id_version) |
483 | 516 | if not re.search(r'[0-9]', project_id_version): |
484 | 517 | self.tag('no-version-in-project-id-version', project_id_version) |
518 | # Report-Msgid-Bugs-To: | |
485 | 519 | report_msgid_bugs_to = file.metadata.get('Report-Msgid-Bugs-To') |
486 | if not report_msgid_bugs_to: | |
520 | if 'Report-Msgid-Bugs-To' in file.metadata.duplicates: | |
521 | self.tag('duplicate-header-field-report-msgid-bugs-to') | |
522 | elif not report_msgid_bugs_to: | |
487 | 523 | self.tag('no-report-msgid-bugs-to-header-field') |
488 | else: | |
489 | real_name, email_address = email.utils.parseaddr(report_msgid_bugs_to) | |
524 | elif report_msgid_bugs_to: | |
525 | real_name, email_address = email.utils.parseaddr(report_msgid_bugs_to) | |
490 | 526 | if '@' not in email_address: |
491 | 527 | uri = urllib.parse.urlparse(report_msgid_bugs_to) |
492 | 528 | if uri.scheme == '': |
497 | 533 | self.tag('boilerplate-in-report-msgid-bugs-to', report_msgid_bugs_to) |
498 | 534 | |
499 | 535 | def check_translator(self, file, *, is_template): |
536 | # Last-Translator: | |
500 | 537 | translator = file.metadata.get('Last-Translator') |
501 | if translator is None: | |
538 | if 'Last-Translator' in file.metadata.duplicates: | |
539 | self.tag('duplicate-header-field-last-translator') | |
540 | elif translator is None: | |
502 | 541 | self.tag('no-last-translator-header-field') |
503 | 542 | else: |
504 | 543 | translator_name, translator_email = email.utils.parseaddr(translator) |
509 | 548 | elif translator_email == 'EMAIL@ADDRESS': |
510 | 549 | if not is_template: |
511 | 550 | self.tag('boilerplate-in-last-translator', translator) |
551 | # Language-Team: | |
512 | 552 | team = file.metadata.get('Language-Team') |
513 | if team is None: | |
553 | if 'Language-Team' in file.metadata.duplicates: | |
554 | self.tag('duplicate-header-field-language-team') | |
555 | elif team is None: | |
514 | 556 | self.tag('no-language-team-header-field') |
515 | 557 | else: |
516 | 558 | team_name, team_email = email.utils.parseaddr(team) |
532 | 574 | if team_is_translator: |
533 | 575 | self.tag('language-team-equal-to-last-translator', team, translator) |
534 | 576 | |
535 | def check_headers(self, file): | |
577 | def check_headers(self, file, *, is_template): | |
536 | 578 | header_fields = frozenset(self.options.gettextinfo.header_fields) |
537 | 579 | header_fields_lc = {str.lower(s): s for s in header_fields} |
538 | new_metadata = {} | |
580 | class userdict(dict): pass | |
581 | new_metadata = userdict() | |
582 | seen_conflict_marker = False | |
539 | 583 | for key, value in sorted(file.metadata.items()): |
584 | if not gettext.is_valid_field_name(key): | |
585 | line = key + (':' if value else '') | |
586 | self.tag('stray-header-line', line) | |
587 | key = None | |
540 | 588 | value, *strays = value.split('\n') |
541 | 589 | for stray in strays: |
542 | self.tag('stray-header-line', stray) | |
590 | if gettext.search_for_conflict_marker(stray): | |
591 | if not seen_conflict_marker: | |
592 | self.tag('conflict-marker-in-header-entry', stray) | |
593 | seen_conflict_marker = True | |
594 | else: | |
595 | self.tag('stray-header-line', stray) | |
596 | if key is None: | |
597 | continue | |
543 | 598 | new_metadata[key] = value |
544 | 599 | if key.startswith('X-'): |
545 | 600 | continue |
558 | 613 | try: |
559 | 614 | duplicates = file.metadata.duplicates |
560 | 615 | except AttributeError: |
561 | pass | |
616 | duplicates = {} | |
562 | 617 | else: |
563 | 618 | for key, values in sorted(duplicates.items()): |
564 | self.tag('duplicate-header-field', key) | |
619 | if not gettext.is_valid_field_name(key): | |
620 | line = key + (':' if any(values) else '') | |
621 | self.tag('stray-header-line', key + ':') | |
622 | elif key in { | |
623 | 'Content-Transfer-Encoding', | |
624 | 'Content-Type', | |
625 | 'Language', | |
626 | 'Language-Team', | |
627 | 'Last-Translator', | |
628 | 'MIME-Version', | |
629 | 'PO-Revision-Date', | |
630 | 'POT-Creation-Date', | |
631 | 'Plural-Forms', | |
632 | 'Project-Id-Version', | |
633 | 'Report-Msgid-Bugs-To' | |
634 | }: | |
635 | # These will be handled elsewhere. | |
636 | pass | |
637 | else: | |
638 | self.tag('duplicate-header-field', key) | |
565 | 639 | for value in values: |
566 | 640 | value, *strays = value.split('\n') |
567 | 641 | for stray in strays: |
568 | 642 | self.tag('stray-header-line', stray) |
643 | new_metadata.duplicates = frozenset(duplicates) | |
569 | 644 | file.metadata = new_metadata |
645 | if file.metadata_is_fuzzy and not is_template: | |
646 | self.tag('fuzzy-header-entry') | |
570 | 647 | |
571 | 648 | def check_messages(self, file, *, encoding): |
572 | 649 | if encoding is None: |
600 | 677 | set(find_unusual_characters(message.msgid_plural)) |
601 | 678 | ) |
602 | 679 | strings = [message.msgstr] + sorted(message.msgstr_plural.values()) |
680 | ||
681 | conflict_marker = None | |
603 | 682 | for msgstr in strings: |
604 | 683 | msgstr_uc = set(find_unusual_characters(msgstr)) |
605 | 684 | uc = msgstr_uc - msgid_uc - found_unusual_characters |
606 | if not uc: | |
607 | continue | |
608 | names = ', '.join( | |
609 | 'U+{:04X} {}'.format(ord(ch), encinfo.get_character_name(ch)) | |
610 | for ch in sorted(uc) | |
611 | ) | |
612 | self.tag('unusual-character-in-translation', tags.safestr(names + ':'), msgstr) | |
613 | found_unusual_characters |= uc | |
685 | if uc: | |
686 | names = ', '.join( | |
687 | 'U+{:04X} {}'.format(ord(ch), encinfo.get_character_name(ch)) | |
688 | for ch in sorted(uc) | |
689 | ) | |
690 | self.tag('unusual-character-in-translation', tags.safestr(names + ':'), msgstr) | |
691 | found_unusual_characters |= uc | |
692 | if conflict_marker is None: | |
693 | conflict_marker = gettext.search_for_conflict_marker(msgstr) | |
694 | if conflict_marker is not None: | |
695 | conflict_marker = conflict_marker.group(0) | |
696 | self.tag('conflict-marker-in-translation', message_repr(message), conflict_marker) | |
614 | 697 | msgid_counter[message.msgid, message.msgctxt] += 1 |
615 | 698 | if msgid_counter[message.msgid, message.msgctxt] == 2: |
616 | 699 | self.tag('duplicate-message-definition', message_repr(message)) |
33 | 33 | from . import tags |
34 | 34 | from . import terminal |
35 | 35 | |
36 | __version__ = '0.9.2' | |
36 | __version__ = '0.10' | |
37 | 37 | |
38 | 38 | def initialize_terminal(): |
39 | 39 | if sys.stdout.isatty(): |
50 | 50 | def tag(self, tagname, *extra): |
51 | 51 | if tagname in self.options.ignore_tags: |
52 | 52 | return |
53 | tag = self.options.taginfo[tagname] | |
53 | try: | |
54 | tag = self.options.taginfo[tagname] | |
55 | except KeyError: | |
56 | raise misc.DataIntegrityError( | |
57 | 'attempted to emit an unknown tag: {tag!r}'.format(tag=tagname) | |
58 | ) | |
54 | 59 | s = tag.format(self.fake_path, *extra, color=True) |
55 | 60 | print(s) |
56 | 61 | |
85 | 90 | with open(os.devnull) as bitbucket: |
86 | 91 | ipc.check_call( |
87 | 92 | ['dpkg-source', '--no-copy', '--no-check', '-x', filename, real_root], |
88 | stdout=bitbucket # dpkg-source would be noisy without this... | |
93 | stdout=bitbucket # dpkg-source would be noisy without this... | |
89 | 94 | ) |
90 | 95 | options = copy_options(options, |
91 | 96 | ignore_tags=ignore_tags, |
27 | 27 | import contextlib |
28 | 28 | import itertools |
29 | 29 | import os |
30 | import subprocess as ipc | |
31 | 30 | import unicodedata |
32 | 31 | |
32 | from lib import iconv | |
33 | 33 | from lib import misc |
34 | 34 | |
35 | 35 | def iconv_encoding(encoding, *, parent): |
36 | ||
37 | # FIXME: This implementation is SLOW. | |
38 | ||
39 | def popen(*args): | |
40 | def set_lc_all_c(): | |
41 | os.environ['LC_ALL'] = 'C' # <no-coverage> | |
42 | return ipc.Popen(args, | |
43 | stdin=ipc.PIPE, stdout=ipc.PIPE, stderr=ipc.PIPE, | |
44 | preexec_fn=set_lc_all_c, | |
45 | ) | |
46 | 36 | |
47 | 37 | def encode(input, errors='strict'): |
48 | 38 | if not (parent._extra_encodings_installed > 0): |
49 | 39 | # There doesn't seem to be a way to de-register a codec. |
50 | 40 | # As a poor man's substitute, raise LookupError at encoding time. |
51 | 41 | raise LookupError('unknown encoding: ' + encoding) |
52 | if len(input) == 0: | |
53 | return b'', 0 | |
54 | if errors != 'strict': | |
55 | raise NotImplementedError | |
56 | child = popen('iconv', '-s', '-f', 'UTF-8', '-t', encoding) | |
57 | (stdout, stderr) = child.communicate(input.encode('UTF-8')) | |
58 | if stderr != b'': | |
59 | stderr = stderr.decode('ASCII', 'replace') | |
60 | raise UnicodeEncodeError(encoding, input, 0, len(input), stderr) | |
61 | return stdout, len(input) | |
42 | output = iconv.encode(input, encoding=encoding, errors=errors) | |
43 | return output, len(input) | |
62 | 44 | |
63 | 45 | def decode(input, errors='strict'): |
64 | 46 | if not (parent._extra_encodings_installed > 0): |
65 | 47 | # There doesn't seem to be a way to de-register a codec. |
66 | 48 | # As a poor man's substitute, raise LookupError at decoding time. |
67 | 49 | raise LookupError('unknown encoding: ' + encoding) |
68 | if len(input) == 0: | |
69 | # This actually never happens. Python optimizes out empty-string | |
70 | # decoding. | |
71 | return '', 0 # <no-coverage> | |
72 | if errors != 'strict': | |
73 | raise NotImplementedError | |
74 | child = popen('iconv', '-s', '-f', encoding, '-t', 'UTF-8') | |
75 | (stdout, stderr) = child.communicate(input) | |
76 | if stderr != b'': | |
77 | stderr = stderr.decode('ASCII', 'replace') | |
78 | raise UnicodeDecodeError(encoding, input, 0, len(input), stderr) | |
79 | return stdout.decode('UTF-8'), len(input) | |
50 | output = iconv.decode(bytes(input), encoding=encoding, errors=errors) | |
51 | return output, len(input) | |
80 | 52 | |
81 | 53 | def not_implemented(*args, **kwargs): |
82 | 54 | raise NotImplementedError |
94 | 66 | class EncodingInfo(object): |
95 | 67 | |
96 | 68 | _interesting_ascii_bytes = bytes(itertools.chain([ |
97 | 0, # NUL | |
98 | 4, # EOT | |
99 | 7, # BEL | |
100 | 8, # BS | |
101 | 9, # HT | |
102 | 10, # LF | |
103 | 11, # VT | |
104 | 12, # FF | |
105 | 13, # CR | |
106 | 27, # ESC | |
69 | 0, # NUL | |
70 | 4, # EOT | |
71 | 7, # BEL | |
72 | 8, # BS | |
73 | 9, # HT | |
74 | 10, # LF | |
75 | 11, # VT | |
76 | 12, # FF | |
77 | 13, # CR | |
78 | 27, # ESC | |
107 | 79 | ], range(32, 127))) |
108 | 80 | _interesting_ascii_str = _interesting_ascii_bytes.decode() |
109 | 81 |
44 | 44 | misc.check_sorted(fields) |
45 | 45 | self.header_fields = frozenset(fields) |
46 | 46 | |
47 | is_valid_field_name = re.compile(r'^[\x21-\x39\x3b-\x7e]+$').match | |
48 | # http://tools.ietf.org/html/rfc5322#section-3.6.8 | |
49 | ||
50 | search_for_conflict_marker = re.compile(r'^#-#-#-#-# .+ #-#-#-#-#$', re.MULTILINE).search | |
51 | # gettext-tools/src/msgl-cat.c | |
52 | # http://www.gnu.org/software/gettext/manual/html_node/Creating-Compendia.html#Creating-Compendia | |
53 | ||
47 | 54 | # ================ |
48 | 55 | # Plurals handling |
49 | 56 | # ================ |
67 | 74 | (r'[?:]', None), |
68 | 75 | (r'[()]', None), |
69 | 76 | (r'[ \t]', None), |
70 | (r'.', '_'), # junk | |
77 | (r'.', '_'), # junk | |
71 | 78 | ] |
72 | 79 | |
73 | 80 | _plural_exp_token_re = '|'.join( |
82 | 89 | if ctoken is not None: |
83 | 90 | break |
84 | 91 | if ctoken is not None: |
85 | if pytoken == '_': # junk | |
92 | if pytoken == '_': # junk | |
86 | 93 | raise PluralExpressionSyntaxError(match.group(0)) |
87 | 94 | yield ' {} '.format(pytoken) |
88 | 95 | else: |
140 | 147 | |
141 | 148 | _parse_date = re.compile(''' |
142 | 149 | ^ \s* |
143 | ( [0-9]{4}-[0-9]{2}-[0-9]{2} ) # YYYY-MM-DD | |
150 | ( [0-9]{4}-[0-9]{2}-[0-9]{2} ) # YYYY-MM-DD | |
144 | 151 | ( \s+ ) |
145 | ( [0-9]{2}:[0-9]{2} ) # hh:mm | |
146 | (?: : [0-9]{2} )? # ss | |
152 | ( [0-9]{2}:[0-9]{2} ) # hh:mm | |
153 | (?: : [0-9]{2} )? # ss | |
147 | 154 | \s* |
148 | ( [+-] [0-9]{2} ) # ZZ | |
155 | ( [+-] [0-9]{2} ) # ZZ | |
149 | 156 | :? |
150 | ( [0-9]{2} ) # zz | |
157 | ( [0-9]{2} ) # zz | |
151 | 158 | \s* $ |
152 | 159 | ''', re.VERBOSE).match |
153 | 160 |
0 | #!/usr/bin/python3 | |
1 | ||
2 | # Copyright © 2012, 2013 Jakub Wilk <jwilk@jwilk.net> | |
3 | # | |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | # of this software and associated documentation files (the “Software”), to deal | |
6 | # in the Software without restriction, including without limitation the rights | |
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the Software is | |
9 | # furnished to do so, subject to the following conditions: | |
10 | # | |
11 | # The above copyright notice and this permission notice shall be included in | |
12 | # all copies or substantial portions of the Software. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | # SOFTWARE. | |
21 | ||
22 | ''' | |
23 | string encoding and decoding using iconv(3), with a fallback to iconv(1) | |
24 | ''' | |
25 | ||
26 | import ctypes | |
27 | import errno | |
28 | import os | |
29 | import subprocess as ipc | |
30 | import re | |
31 | import sys | |
32 | ||
33 | default_encoding = sys.getdefaultencoding() | |
34 | ||
35 | _boring_iconv_stderr = re.compile('\nTry .+ for more information[.]$') | |
36 | ||
37 | _libc = ctypes.CDLL(None, use_errno=True) | |
38 | try: | |
39 | _iconv_open = _libc.iconv_open | |
40 | _iconv_close = _libc.iconv_close | |
41 | _iconv = _libc.iconv | |
42 | except AttributeError: | |
43 | _iconv = _iconv_open = _iconv_close = None | |
44 | else: | |
45 | _iconv_open.argtypes = [ctypes.c_char_p, ctypes.c_char_p] | |
46 | _iconv_open.restype = ctypes.c_void_p | |
47 | _iconv_close.argtypes = [ctypes.c_void_p] | |
48 | _iconv_close.restype = ctypes.c_int | |
49 | _iconv.argtypes = ( | |
50 | [ctypes.c_void_p] + | |
51 | [ctypes.POINTER(ctypes.POINTER(ctypes.c_char)), ctypes.POINTER(ctypes.c_size_t)] * 2 | |
52 | ) | |
53 | _iconv.restype = ctypes.c_size_t | |
54 | ||
55 | def _popen(*args): | |
56 | def set_lc_all_c(): | |
57 | os.environ['LC_ALL'] = 'C' # <no-coverage> | |
58 | return ipc.Popen(args, | |
59 | stdin=ipc.PIPE, stdout=ipc.PIPE, stderr=ipc.PIPE, | |
60 | preexec_fn=set_lc_all_c, | |
61 | ) | |
62 | ||
63 | def encode(input: str, encoding=default_encoding, errors='strict'): | |
64 | if not isinstance(input, str): | |
65 | raise TypeError('input must be str, not {tp}'.format(tp=type(input).__name__)) | |
66 | if not isinstance(encoding, str): | |
67 | raise TypeError('encoding must be str, not {tp}'.format(tp=type(encoding).__name__)) | |
68 | if not isinstance(errors, str): | |
69 | raise TypeError('errors must be str, not {tp}'.format(tp=type(errors).__name__)) | |
70 | if len(input) == 0: | |
71 | return b'' | |
72 | if errors != 'strict': | |
73 | raise NotImplementedError('error handler {e!r} is not implemented'.format(e=errors)) | |
74 | return _encode(input, encoding=encoding) | |
75 | ||
76 | def _encode_dl(input: str, *, encoding): | |
77 | if sys.maxunicode < (1 << 16): | |
78 | uencoding = 'UTF-16LE' | |
79 | uwidth = 2 | |
80 | else: | |
81 | uencoding = 'UTF-32LE' | |
82 | uwidth = 4 | |
83 | binput = bytes(input, encoding=uencoding) | |
84 | assert len(binput) == len(input) * uwidth | |
85 | cd = _iconv_open(bytes(encoding, 'ASCII'), bytes(uencoding, 'ASCII')) | |
86 | assert isinstance(cd, int) | |
87 | if cd == ctypes.c_void_p(-1).value: | |
88 | rc = ctypes.get_errno() | |
89 | raise OSError(rc, os.strerror(rc)) | |
90 | try: | |
91 | c_input = ctypes.c_char_p(binput) | |
92 | output_len = len(input) | |
93 | while True: | |
94 | inbuf = ctypes.cast(c_input, ctypes.POINTER(ctypes.c_char)) | |
95 | inbytesleft = ctypes.c_size_t(len(binput)) | |
96 | assert inbytesleft.value == len(binput) # no overflow | |
97 | outbuf = ctypes.create_string_buffer(output_len) | |
98 | outbytesleft = ctypes.c_size_t(output_len) | |
99 | assert outbytesleft.value == output_len # no overflow | |
100 | rc = _iconv(cd, *map(ctypes.byref, [ | |
101 | ctypes.cast(inbuf, ctypes.POINTER(ctypes.c_char)), inbytesleft, | |
102 | ctypes.cast(outbuf, ctypes.POINTER(ctypes.c_char)), outbytesleft, | |
103 | ])) | |
104 | if rc == ctypes.c_size_t(-1).value: | |
105 | rc = ctypes.get_errno() | |
106 | if rc == errno.E2BIG: | |
107 | output_len *= 2 | |
108 | continue | |
109 | elif rc in {errno.EILSEQ, errno.EINVAL}: | |
110 | begin = len(input) - inbytesleft.value // uwidth | |
111 | raise UnicodeEncodeError( | |
112 | encoding, | |
113 | input, | |
114 | begin, begin + 1, | |
115 | os.strerror(errno.EILSEQ), | |
116 | ) | |
117 | raise OSError(rc, os.strerror(rc)) | |
118 | assert inbytesleft.value == 0, '{n} bytes left'.format(n=inbytesleft.value) | |
119 | output_len -= outbytesleft.value | |
120 | return outbuf[:output_len] | |
121 | finally: | |
122 | rc = _iconv_close(cd) | |
123 | if rc != 0: | |
124 | rc = ctypes.get_errno() | |
125 | raise OSError(rc, os.strerror(rc)) | |
126 | ||
127 | def _encode_cli(input, *, encoding): | |
128 | child = _popen('iconv', '-f', 'UTF-8', '-t', encoding) | |
129 | (stdout, stderr) = child.communicate(input.encode('UTF-8')) | |
130 | if stderr != b'': | |
131 | stderr = stderr.decode('ASCII', 'replace') | |
132 | stderr = _boring_iconv_stderr.sub('', stderr) | |
133 | raise UnicodeEncodeError(encoding, | |
134 | input, # .object | |
135 | 0, # .begin | |
136 | len(input), # .end | |
137 | stderr.strip() # .reason | |
138 | ) | |
139 | return stdout | |
140 | ||
141 | _encode = _encode_dl if _iconv is not None else _encode_cli | |
142 | ||
143 | def decode(input: bytes, encoding=default_encoding, errors='strict'): | |
144 | if not isinstance(input, bytes): | |
145 | raise TypeError('input must be bytes, not {tp}'.format(tp=type(input).__name__)) | |
146 | if not isinstance(encoding, str): | |
147 | raise TypeError('encoding must be str, not {tp}'.format(tp=type(encoding).__name__)) | |
148 | if not isinstance(errors, str): | |
149 | raise TypeError('errors must be str, not {tp}'.format(tp=type(errors).__name__)) | |
150 | if len(input) == 0: | |
151 | return '' | |
152 | if errors != 'strict': | |
153 | raise NotImplementedError('error handler {e!r} is not implemented'.format(e=errors)) | |
154 | return _decode(input, encoding=encoding) | |
155 | ||
156 | def _decode_dl(input: bytes, *, encoding): | |
157 | cd = _iconv_open(b'WCHAR_T', bytes(encoding, 'ASCII')) | |
158 | assert isinstance(cd, int) | |
159 | if cd == ctypes.c_void_p(-1).value: | |
160 | rc = ctypes.get_errno() | |
161 | raise OSError(rc, os.strerror(rc)) | |
162 | try: | |
163 | c_input = ctypes.c_char_p(input) | |
164 | output_len = len(input) | |
165 | while True: | |
166 | inbuf = ctypes.cast(c_input, ctypes.POINTER(ctypes.c_char)) | |
167 | inbytesleft = ctypes.c_size_t(len(input)) | |
168 | assert inbytesleft.value == len(input) # no overflow | |
169 | outbuf = ctypes.create_unicode_buffer(output_len) | |
170 | outbytesleft = ctypes.c_size_t(output_len) # no overflow | |
171 | assert outbytesleft.value == output_len | |
172 | rc = _iconv(cd, *map(ctypes.byref, [ | |
173 | ctypes.cast(inbuf, ctypes.POINTER(ctypes.c_char)), inbytesleft, | |
174 | ctypes.cast(outbuf, ctypes.POINTER(ctypes.c_char)), outbytesleft, | |
175 | ])) | |
176 | if rc == ctypes.c_size_t(-1).value: | |
177 | rc = ctypes.get_errno() | |
178 | if rc == errno.E2BIG: | |
179 | output_len *= 2 | |
180 | continue | |
181 | elif rc in {errno.EILSEQ, errno.EINVAL}: | |
182 | begin = len(input) - inbytesleft.value | |
183 | for end in range(begin + 1, len(input)): | |
184 | # Assume that the encoding can be synchronized on ASCII characters. | |
185 | # That's not necessarily true for _every_ encoding, but oh well. | |
186 | if input[end] < 0x80: | |
187 | break | |
188 | else: | |
189 | end = len(input) | |
190 | raise UnicodeDecodeError( | |
191 | encoding, | |
192 | input, | |
193 | begin, end, | |
194 | os.strerror(errno.EILSEQ), | |
195 | ) | |
196 | raise OSError(rc, os.strerror(rc)) | |
197 | assert inbytesleft.value == 0, '{n} bytes left'.format(n=inbytesleft.value) | |
198 | output_len -= outbytesleft.value | |
199 | assert output_len % ctypes.sizeof(ctypes.c_wchar) == 0 | |
200 | unicode_output_len = output_len // ctypes.sizeof(ctypes.c_wchar) | |
201 | return outbuf[:unicode_output_len] | |
202 | finally: | |
203 | rc = _iconv_close(cd) | |
204 | if rc != 0: | |
205 | rc = ctypes.get_errno() | |
206 | raise OSError(rc, os.strerror(rc)) | |
207 | ||
208 | def _decode_cli(input, *, encoding): | |
209 | child = _popen('iconv', '-f', encoding, '-t', 'UTF-8') | |
210 | (stdout, stderr) = child.communicate(input) | |
211 | if stderr != b'': | |
212 | stderr = stderr.decode('ASCII', 'replace') | |
213 | stderr = _boring_iconv_stderr.sub('', stderr) | |
214 | raise UnicodeDecodeError(encoding, | |
215 | input, # .object | |
216 | 0, # .begin | |
217 | len(input), # .end | |
218 | stderr.strip() # .reason | |
219 | ) | |
220 | return stdout.decode('UTF-8') | |
221 | ||
222 | _decode = _decode_dl if _iconv is not None else _decode_cli | |
223 | ||
224 | __all__ = ['encode', 'decode'] | |
225 | ||
226 | # vim:ts=4 sw=4 et |
265 | 265 | |
266 | 266 | def _visit_div(self, node, x, y): |
267 | 267 | if y == (0, 0): |
268 | 0 # division by zero | |
268 | # division by zero | |
269 | 269 | return |
270 | 270 | assert y[1] > 0 |
271 | 271 | return ( |
108 | 108 | raise FixingLanguageCodesFailed() |
109 | 109 | elif cc != self.territory_code: |
110 | 110 | # This shouldn't really happen, but better safe than sorry. |
111 | raise ValueError # <no-coverage> | |
111 | raise ValueError # <no-coverage> | |
112 | 112 | # TODO: ll_CC could be still incorrect, even when both ll and CC are |
113 | 113 | # correct. |
114 | 114 | self.language_code = ll |
130 | 130 | return True |
131 | 131 | |
132 | 132 | def remove_encoding(self): |
133 | if self.encoding == None: | |
133 | if self.encoding is None: | |
134 | 134 | return |
135 | 135 | self.encoding = None |
136 | 136 | return True |
163 | 163 | return |
164 | 164 | result = [] |
165 | 165 | try: |
166 | # iconv-based encodings have huge overhead per encode() call. To | |
167 | # reduce number of such calls, optimize the common case of all | |
168 | # characters being representable. | |
166 | # If iconv(1) is used to implement an encoding, there's a huge | |
167 | # overhead per encode() call. To reduce number of such calls, | |
168 | # optimize the common case of all characters being representable. | |
169 | 169 | ''.join(characters).encode(encoding) |
170 | 170 | except UnicodeError: |
171 | 171 | pass |
177 | 177 | except UnicodeError as exc: |
178 | 178 | result += [character] |
179 | 179 | if exc.reason.startswith('iconv:'): |
180 | # Avoid further calls to iconv: | |
180 | # Avoid further calls to iconv(1): | |
181 | 181 | break |
182 | 182 | return result |
183 | 183 | |
237 | 237 | if ll: |
238 | 238 | try: |
239 | 239 | self._primary_languages[ll] |
240 | except LookupError: # <no-coverage> | |
240 | except LookupError: # <no-coverage> | |
241 | 241 | raise misc.DataIntegrityError |
242 | 242 | |
243 | 243 | def _lookup_language_code(self, language): |
9 | 9 | pwd="$PWD" |
10 | 10 | version="$1" |
11 | 11 | sourceroot=$(mktemp -d -t "$name-source-XXXXXX") |
12 | export TAR_OPTIONS="--owner root --group root --mode a+rX" | |
12 | export TAR_OPTIONS="--owner root --group root --mode a+rX --format ustar" | |
13 | 13 | export GZIP="-9 -n" |
14 | 14 | mkdir -p "$sourceroot/$name-$version" |
15 | 15 | if [ -d .hg ] |
19 | 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
20 | 20 | # SOFTWARE. |
21 | 21 | |
22 | import io | |
22 | 23 | import os |
23 | 24 | import sys |
24 | 25 | |
51 | 52 | print() |
52 | 53 | |
53 | 54 | def main(): |
55 | sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='UTF-8') | |
54 | 56 | datadir = os.path.join(basedir, 'data') |
55 | 57 | taginfo = tags.TagInfo(datadir) |
56 | 58 | output_tags_rst(taginfo) |
49 | 49 | print('[{check}] {tag}'.format(check=check, tag=tag.name)) |
50 | 50 | sys.stdout.close() |
51 | 51 | os.rename(path + '.tmp', path) |
52 | rc = 0 | |
53 | for tag in sorted(set(coverage) - set(t.name for t in taginfo)): | |
54 | print('update-coverage: error: uknown tag {tag}'.format(tag=tag), file=sys.stderr) | |
55 | rc = 1 | |
56 | sys.exit(rc) | |
52 | 57 | |
53 | 58 | if __name__ == '__main__': |
54 | 59 | main() |
0 | import sys | |
1 | ||
2 | if sys.version_info < (3, 0): | |
3 | raise ImportError('Python >= 3 is required') | |
4 | ||
5 | # vim:ts=4 sw=4 et |
22 | 22 | import os |
23 | 23 | import re |
24 | 24 | import shlex |
25 | import signal | |
25 | 26 | import subprocess as ipc |
26 | 27 | import sys |
27 | 28 | |
101 | 102 | |
102 | 103 | # ---------------------------------------- |
103 | 104 | |
105 | def _get_signal_names(): | |
106 | data = dict( | |
107 | (name, getattr(signal, name)) | |
108 | for name in dir(signal) | |
109 | if re.compile('^SIG[A-Z0-9]*$').match(name) | |
110 | ) | |
111 | try: | |
112 | if data['SIGABRT'] == data['SIGIOT']: | |
113 | del data['SIGIOT'] | |
114 | except KeyError: | |
115 | pass | |
116 | try: | |
117 | if data['SIGCHLD'] == data['SIGCLD']: | |
118 | del data['SIGCLD'] | |
119 | except KeyError: | |
120 | pass | |
121 | for name, n in data.items(): | |
122 | yield n, name | |
123 | ||
124 | _signal_names = dict(_get_signal_names()) | |
125 | ||
126 | def get_signal_name(n): | |
127 | try: | |
128 | return _signal_names[n] | |
129 | except KeyError: | |
130 | return str(n) | |
131 | ||
104 | 132 | def assert_emit_tags(path, etags, *, options=()): |
105 | 133 | etags = list(etags) |
106 | 134 | commandline = os.environ.get('I18NSPECTOR_COMMANDLINE') |
112 | 140 | commandline += options |
113 | 141 | commandline += [path] |
114 | 142 | fixed_env = dict(os.environ, LC_ALL='C') |
115 | stdout = ipc.check_output(commandline, env=fixed_env) | |
116 | stdout = stdout.decode('ASCII').splitlines() | |
143 | child = ipc.Popen(commandline, stdout=ipc.PIPE, stderr=ipc.PIPE, env=fixed_env) | |
144 | stdout, stderr = ( | |
145 | s.decode().splitlines() | |
146 | for s in child.communicate() | |
147 | ) | |
148 | rc = child.poll() | |
149 | if rc != 0: | |
150 | if rc < 0: | |
151 | message = ['command was interrupted by signal {sig}'.format(sig=get_signal_name(-rc))] | |
152 | else: | |
153 | message = ['command exited with status {rc}'.format(rc=rc)] | |
154 | message += [''] | |
155 | if stdout: | |
156 | message += ['stdout:'] | |
157 | message += ['| ' + s for s in stdout] + [''] | |
158 | else: | |
159 | message += ['stdout: (empty)'] | |
160 | if stderr: | |
161 | message += ['stderr:'] | |
162 | message += ['| ' + s for s in stderr] | |
163 | else: | |
164 | message += ['stderr: (empty)'] | |
165 | raise AssertionError('\n'.join(message)) | |
117 | 166 | if stdout != etags: |
118 | 167 | str_etags = [str(x) for x in etags] |
119 | 168 | message = ['Tags differ:', ''] |
0 | # W: boilerplate-in-content-type 'text/plain; charset=CHARSET' | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=CHARSET\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | ||
15 | msgid "A quick brown fox jumps over the lazy dog." | |
16 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # E: conflict-marker-in-header-entry '#-#-#-#-# la.po (Gizmo Enhancer 1.0) #-#-#-#-#' | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | "#-#-#-#-# la.po (Gizmo Enhancer 1.0) #-#-#-#-#\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # E: conflict-marker-in-translation msgid 'A quick brown fox jumps over the lazy dog.' '#-#-#-#-# la.po (Gizmo Enhancer 1.0) #-#-#-#-#' | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | ||
15 | msgid "A quick brown fox jumps over the lazy dog." | |
16 | msgstr "" | |
17 | "#-#-#-#-# la.po (Gizmo Enhancer 1.0) #-#-#-#-#\n" | |
18 | "Sic fugiens, dux, zelotypos, quam Karus haberis.\n" | |
19 | "#-#-#-#-# pl.po (Gizmo Enhancer 1.0) #-#-#-#-#\n" | |
20 | "Mężny bądź, chroń pułk twój i sześć flag." |
3 | 3 | [X] ancient-date |
4 | 4 | [X] arithmetic-error-in-plural-forms |
5 | 5 | [X] arithmetic-error-in-unused-plural-forms |
6 | [X] boilerplate-in-content-type | |
6 | 7 | [X] boilerplate-in-language-team |
7 | 8 | [X] boilerplate-in-last-translator |
8 | 9 | [X] boilerplate-in-project-id-version |
10 | 11 | [X] broken-encoding |
11 | 12 | [X] codomain-error-in-plural-forms |
12 | 13 | [X] codomain-error-in-unused-plural-forms |
14 | [X] conflict-marker-in-header-entry | |
15 | [X] conflict-marker-in-translation | |
13 | 16 | [X] date-from-future |
14 | 17 | [X] duplicate-header-field |
18 | [X] duplicate-header-field-content-transfer-encoding | |
19 | [X] duplicate-header-field-content-type | |
20 | [X] duplicate-header-field-date | |
21 | [X] duplicate-header-field-language | |
22 | [X] duplicate-header-field-language-team | |
23 | [X] duplicate-header-field-last-translator | |
24 | [X] duplicate-header-field-mime-version | |
25 | [X] duplicate-header-field-plural-forms | |
26 | [X] duplicate-header-field-project-id-version | |
27 | [X] duplicate-header-field-report-msgid-bugs-to | |
15 | 28 | [X] duplicate-message-definition |
16 | 29 | [X] empty-file |
17 | 30 | [X] encoding-in-language-header-field |
31 | [X] fuzzy-header-entry | |
18 | 32 | [X] inconsistent-leading-newlines |
19 | 33 | [X] inconsistent-number-of-plural-forms |
20 | 34 | [X] inconsistent-trailing-newlines |
0 | # E: duplicate-header-field Language | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # P: duplicate-header-field-content-transfer-encoding | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # E: duplicate-header-field-content-type | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-language-team | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-language | |
1 | # I: unable-to-determine-language | |
2 | ||
3 | msgid "" | |
4 | msgstr "" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "Language: la\n" | |
13 | "MIME-Version: 1.0\n" | |
14 | "Content-Type: text/plain; charset=UTF-8\n" | |
15 | "Content-Transfer-Encoding: 8bit\n" | |
16 | ||
17 | msgid "A quick brown fox jumps over the lazy dog." | |
18 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-last-translator | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # P: duplicate-header-field-mime-version | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # E: duplicate-header-field-plural-forms | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Polish <pl@li.org>\n" | |
10 | "Language: pl\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" | |
15 | "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" | |
16 | ||
17 | msgid "%d quick brown fox jumps over the lazy dog." | |
18 | msgid_plural "%d quick brown foxes jump over the lazy dog." | |
19 | msgstr[0] "Mężny bądź, chroń pułk twój i %d flagę." | |
20 | msgstr[1] "Mężny bądź, chroń pułk twój i %d flagi." | |
21 | msgstr[2] "Mężny bądź, chroń pułk twój i %d flag." |
0 | # W: duplicate-header-field-date PO-Revision-Date | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-date POT-Creation-Date | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-project-id-version | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: duplicate-header-field-report-msgid-bugs-to | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # I: duplicate-header-field Generated-By | |
1 | ||
2 | msgid "" | |
3 | msgstr "" | |
4 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
5 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
6 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
7 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
8 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
9 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
10 | "Language: la\n" | |
11 | "MIME-Version: 1.0\n" | |
12 | "Content-Type: text/plain; charset=UTF-8\n" | |
13 | "Content-Transfer-Encoding: 8bit\n" | |
14 | "Generated-By: VIM 7.3\n" | |
15 | "Generated-By: VIM 7.3\n" | |
16 | ||
17 | msgid "A quick brown fox jumps over the lazy dog." | |
18 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # P: fuzzy-header-entry | |
1 | ||
2 | #, fuzzy | |
3 | msgid "" | |
4 | msgstr "" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: stray-header-line 'MIME-Version 1.0' | |
1 | # P: no-mime-version-header-field MIME-Version: 1.0 | |
2 | ||
3 | msgid "" | |
4 | msgstr "" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: stray-header-line 'POT-Creation-Date 2012-11-01 14:' | |
1 | # W: no-date-header-field POT-Creation-Date | |
2 | ||
3 | msgid "" | |
4 | msgstr "" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version: 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
0 | # W: stray-header-line 'MIME-Version 1.0' | |
1 | # P: no-mime-version-header-field MIME-Version: 1.0 | |
2 | ||
3 | msgid "" | |
4 | msgstr "" | |
5 | "Project-Id-Version: Gizmo Enhancer 1.0\n" | |
6 | "Report-Msgid-Bugs-To: gizmoenhancer@jwilk.net\n" | |
7 | "POT-Creation-Date: 2012-11-01 14:42+0100\n" | |
8 | "PO-Revision-Date: 2012-11-01 14:42+0100\n" | |
9 | "Last-Translator: Jakub Wilk <jwilk@jwilk.net>\n" | |
10 | "Language-Team: Latin Strike Force <la@li.org>\n" | |
11 | "Language: la\n" | |
12 | "MIME-Version 1.0\n" | |
13 | "Content-Type: text/plain; charset=UTF-8\n" | |
14 | "Content-Transfer-Encoding: 8bit\n" | |
15 | ||
16 | msgid "A quick brown fox jumps over the lazy dog." | |
17 | msgstr "Sic fugiens, dux, zelotypos, quam Karus haberis." |
93 | 93 | def test_plural_exp_and(self): |
94 | 94 | self._pe('n && 6', 7, 1) |
95 | 95 | self._pe('n && 6', 0, 0) |
96 | self._pe('n && (6 / 0)', 0, 0) # no ZeroDivisionError | |
96 | self._pe('n && (6 / 0)', 0, 0) # no ZeroDivisionError | |
97 | 97 | self._pe('n && 0', 7, 0) |
98 | 98 | self._pe('n && 0', 0, 0) |
99 | 99 | |
100 | 100 | def test_plural_exp_or(self): |
101 | 101 | self._pe('n || 6', 7, 1) |
102 | self._pe('n || (6 / 0)', 7, 1) # no ZeroDivisionError | |
102 | self._pe('n || (6 / 0)', 7, 1) # no ZeroDivisionError | |
103 | 103 | self._pe('n || 6', 0, 1) |
104 | 104 | self._pe('n || 0', 7, 1) |
105 | 105 | self._pe('n || 0', 0, 0) |
135 | 135 | self._pe('n != 37', 42, 1) |
136 | 136 | |
137 | 137 | def test_plural_exp_multi_compare(self): |
138 | self._pe('1 < n == 3 <= 4', 1, 0) # False in Python | |
139 | self._pe('1 < n == 3 <= 4', 2, 1) # False in Python | |
140 | self._pe('1 < n == 3 <= 4', 3, 1) # True in Python | |
141 | self._pe('1 < n == 3 <= 4', 4, 1) # False in Python | |
142 | self._pe('2 == 2 == n', 2, 0) # True in Python | |
143 | self._pe('2 == 2 == n', 1, 1) # False in Python | |
138 | self._pe('1 < n == 3 <= 4', 1, 0) # False in Python | |
139 | self._pe('1 < n == 3 <= 4', 2, 1) # False in Python | |
140 | self._pe('1 < n == 3 <= 4', 3, 1) # True in Python | |
141 | self._pe('1 < n == 3 <= 4', 4, 1) # False in Python | |
142 | self._pe('2 == 2 == n', 2, 0) # True in Python | |
143 | self._pe('2 == 2 == n', 1, 1) # False in Python | |
144 | 144 | |
145 | 145 | def test_plural_exp_neg(self): |
146 | 146 | self._pe('! n', 0, 1) |
192 | 192 | else: |
193 | 193 | assert_equal(new, expected) |
194 | 194 | |
195 | ||
196 | 195 | def test_boilerplate(self): |
197 | 196 | self._test('YEAR-MO-DA HO:MI+ZONE', None) |
198 | 197 |