Codebase list i18nspector / 63e19a7
Imported Upstream version 0.10 Stuart Prescott 10 years ago
44 changed file(s) with 1312 addition(s) and 188 deletion(s). Raw diff Collapse all Expand all
1717 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1818 # SOFTWARE.
1919
20 PYTHON = python3
2021 PREFIX = /usr/local
2122 DESTDIR =
2223
4950
5051 .PHONY: test
5152 test:
52 python3 -c 'import nose; nose.main()' -v
53 $(PYTHON) -c 'import nose; nose.main()' -v
5354
5455 # vim:ts=4 sw=4 noet
2424 This normally indicates an error in the plural form expression.
2525 references =
2626 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
2736
2837 [boilerplate-in-language-team]
2938 severity = minor
98107 references =
99108 http://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
100109
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
101128 [date-from-future]
102129 severity = normal
103130 certainty = certain
105132 The date refers to the future. As such, it's extremely unlikely to be correct.
106133
107134 [duplicate-header-field]
108 severity = serious
109 certainty = certain
135 severity = minor
136 certainty = wild-guess
110137 description =
111138 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.
112199
113200 [duplicate-message-definition]
114201 severity = serious
130217 shouldn't be included in this field.
131218 references =
132219 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
133230
134231 [inconsistent-leading-newlines]
135232 severity = important
272369 severity = minor
273370 certainty = possible
274371 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
276373 address.
277374
278375 [language-variant-does-not-affect-translation]
0 export LC_ALL=C
1
02 rst2man = $(or $(shell which rst2man),rst2man.py)
13 exe = i18nspector
24
810 mv $(@).tmp $(@)
911
1012 $(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
1216 mv $(@).tmp $(@)
1317
1418 .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
043 i18nspector (0.9.2) unstable; urgency=low
144
245 * When emitting broken-encoding, don't output the whole file, but only the
00 .\" Man page generated from reStructuredText.
11 .
2 .TH I18NSPECTOR 1 "2013-06-08" "i18nspector 0.9.2" ""
2 .TH I18NSPECTOR 1 "2013-06-15" "i18nspector 0.10" ""
33 .SH NAME
44 i18nspector \- checking tool for gettext POT, PO and MO files
55 .
66 .nr rst2man-indent-level 0
77 .
8 .de1 rstReportMargin
8 .de rstReportMargin
99 \\$1 \\n[an-margin]
1010 level \\n[rst2man-indent-level]
1111 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
1414 \\n[rst2man-indent1]
1515 \\n[rst2man-indent2]
1616 ..
17 .de1 INDENT
17 .de INDENT
1818 .\" .rstReportMargin pre:
1919 . RS \\$1
2020 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
7676 .SS ancient\-date
7777 .sp
7878 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.
8080 .sp
8181 References:
8282 .INDENT 0.0
128128 important, possible
129129 .UNINDENT
130130 .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
131149 .SS boilerplate\-in\-language\-team
132150 .sp
133151 The Language\-Team header field contains xgettext boilerplate.
150168 .SS boilerplate\-in\-last\-translator
151169 .sp
152170 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.
154172 .sp
155173 References:
156174 .INDENT 0.0
204222 .UNINDENT
205223 .SS broken\-encoding
206224 .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
208226 Unicode.
209227 The usual cause of this is incorrect or missing encoding declaration.
210228 .sp
265283 important, possible
266284 .UNINDENT
267285 .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
268322 .SS date\-from\-future
269323 .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.
271325 .sp
272326 Severity, certainty:
273327 .INDENT 0.0
282336 Severity, certainty:
283337 .INDENT 0.0
284338 .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
285359 serious, certain
286360 .UNINDENT
287361 .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
288442 .SS duplicate\-message\-definition
289443 .sp
290444 This file contains multiple definitions of the same message.
297451 .UNINDENT
298452 .SS empty\-file
299453 .sp
300 This file doesn\(aqt contain any messages.
454 This file doesn't contain any messages.
301455 .sp
302456 Severity, certainty:
303457 .INDENT 0.0
308462 .SS encoding\-in\-language\-header\-field
309463 .sp
310464 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.
312466 .sp
313467 References:
314468 .INDENT 0.0
323477 minor, certain
324478 .UNINDENT
325479 .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
326499 .SS inconsistent\-leading\-newlines
327500 .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.
329502 Either all of them should start with a newline, or none of them should.
330503 .sp
331504 Severity, certainty:
336509 .UNINDENT
337510 .SS inconsistent\-number\-of\-plural\-forms
338511 .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
340513 forms declared in another message definition.
341514 .sp
342515 References:
356529 .UNINDENT
357530 .SS inconsistent\-trailing\-newlines
358531 .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.
360533 Either all of them should end with a newline, or none of them should.
361534 .sp
362535 Severity, certainty:
367540 .UNINDENT
368541 .SS incorrect\-number\-of\-plural\-forms
369542 .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
371544 forms declared in the header.
372545 .sp
373546 References:
387560 .UNINDENT
388561 .SS incorrect\-plural\-forms
389562 .sp
390 The Plural\-Forms declaration is incorrect, according to i18nspector\(aqs
563 The Plural\-Forms declaration is incorrect, according to i18nspector's
391564 linguistic data.
392565 .sp
393566 Severity, certainty:
398571 .UNINDENT
399572 .SS incorrect\-unused\-plural\-forms
400573 .sp
401 The Plural\-Forms declaration is incorrect, according to i18nspector\(aqs
574 The Plural\-Forms declaration is incorrect, according to i18nspector's
402575 linguistic data.
403576 (But there are no translated messages which use plural forms.)
404577 .sp
468641 .UNINDENT
469642 .SS invalid\-language
470643 .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
472645 language.
473646 .sp
474647 References:
541714 .UNINDENT
542715 .SS invalid\-mo\-file
543716 .sp
544 This file couldn\(aqt be parsed a MO file.
717 This file couldn't be parsed a MO file.
545718 .sp
546719 Severity, certainty:
547720 .INDENT 0.0
572745 .SS language\-disparity
573746 .sp
574747 Language of this file has been declared in multiple places, but the
575 declarations don\(aqt match.
748 declarations don't match.
576749 .sp
577750 Severity, certainty:
578751 .INDENT 0.0
582755 .UNINDENT
583756 .SS language\-team\-equal\-to\-last\-translator
584757 .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
586759 address.
587760 .sp
588761 Severity, certainty:
611784 .UNINDENT
612785 .SS no\-content\-transfer\-encoding\-header\-field
613786 .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
615788 \fB8bit\fP.
616789 .sp
617790 References:
631804 .UNINDENT
632805 .SS no\-content\-type\-header\-field
633806 .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
635808 \fBtext/plain; charset=\fP\fIencoding\fP.
636809 .sp
637810 Note that in the absence of encoding declaration, i18nspector assumes ASCII
654827 .UNINDENT
655828 .SS no\-date\-header\-field
656829 .sp
657 The date header field doesn\(aqt exist.
830 The date header field doesn't exist.
658831 .sp
659832 References:
660833 .INDENT 0.0
671844 .UNINDENT
672845 .SS no\-language\-header\-field
673846 .sp
674 The Language header field doesn\(aqt exist.
847 The Language header field doesn't exist.
675848 .sp
676849 References:
677850 .INDENT 0.0
707880 .UNINDENT
708881 .SS no\-last\-translator\-header\-field
709882 .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.
712885 .sp
713886 References:
714887 .INDENT 0.0
725898 .UNINDENT
726899 .SS no\-mime\-version\-header\-field
727900 .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.
729902 .sp
730903 References:
731904 .INDENT 0.0
742915 .UNINDENT
743916 .SS no\-package\-name\-in\-project\-id\-version
744917 .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.
746919 It should contain both the name and the version of the package.
747920 .sp
748921 References:
8331006 .UNINDENT
8341007 .SS no\-version\-in\-project\-id\-version
8351008 .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.
8371010 It should contain both the name and the version of the package.
8381011 .sp
8391012 References:
9251098 .UNINDENT
9261099 .SS syntax\-error\-in\-po\-file
9271100 .sp
928 This file couldn\(aqt be parsed a PO file.
1101 This file couldn't be parsed a PO file.
9291102 In some rare cases this is due to incorrect or missing encoding declaration.
9301103 .sp
9311104 References:
9751148 .UNINDENT
9761149 .SS unknown\-encoding
9771150 .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.
9791152 It might be a typo.
9801153 Absence of encoding information will prevent i18nspector from performing
9811154 further checks.
9881161 .UNINDENT
9891162 .SS unknown\-file\-type
9901163 .sp
991 File format of this file couldn\(aqt be recognized.
1164 File format of this file couldn't be recognized.
9921165 It might be a bug in i18nspector.
9931166 .sp
9941167 Severity, certainty:
10181191 .UNINDENT
10191192 .SS unknown\-poedit\-language
10201193 .sp
1021 Language declared in X\-Poedit\-Language couldn\(aqt be recognized.
1194 Language declared in X\-Poedit\-Language couldn't be recognized.
10221195 It might be a bug in i18nspector.
10231196 .sp
10241197 Severity, certainty:
66 ----------------------------------------------
77
88 :manual section: 1
9 :version: i18nspector 0.9.2
9 :version: i18nspector 0.10
1010 :date: |date|
1111
1212 Synopsis
3939 Severity, certainty:
4040
4141 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
4255
4356 boilerplate-in-language-team
4457 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
142155
143156 important, possible
144157
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
145184 date-from-future
146185 ~~~~~~~~~~~~~~~~
147186 The date refers to the future. As such, it's extremely unlikely to be correct.
156195
157196 Severity, certainty:
158197
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
160279
161280 duplicate-message-definition
162281 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186305 Severity, certainty:
187306
188307 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
189322
190323 inconsistent-leading-newlines
191324 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
384517
385518 language-team-equal-to-last-translator
386519 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
388521 address.
389522
390523 Severity, certainty:
4747 Make duplicate-header-field work for .mo files, too.
4848 * https://bitbucket.org/izi/polib/issue/36
4949
50 Check for initial stray lines in headers. (They are currently ignored due to
51 deficiencies in the polib parser.)
52
5053 Add support for configuration files. Once it's implemented:
5154 * Disable --debian by default.
5255 * Disable --pedantic by default.
6467
6568 Run ``msgfmt --check-format`` on PO files.
6669
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
6776 .. vim:ft=rst
4242 pass
4343
4444 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
5252 ).findall
5353
5454 class Checker(object):
162162 broken_encoding = True
163163 # check_headers() modifies the file metadata,
164164 # so it has to be the first check:
165 self.check_headers(file)
165 self.check_headers(file, is_template=is_template)
166166 language = self.check_language(file, is_template=is_template)
167167 self.check_plurals(file, is_template=is_template, language=language)
168168 encoding = self.check_mime(file, is_template=is_template, language=language)
175175
176176 def check_language(self, file, *, is_template):
177177 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:
179181 self.tag('no-language-header-field')
180182 return
181183 language = self.options.language
216218 language = None
217219 else:
218220 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
220229 if meta_language:
221230 try:
222231 meta_language = linginfo.parse_language(meta_language)
269278 poedit_language, tags.safestr('(X-Poedit-Language header field)')
270279 )
271280 if language is None:
272 if not orig_meta_language:
281 if not orig_meta_language and not duplicate_meta_language:
273282 self.tag('no-language-header-field')
274283 self.tag('unable-to-determine-language')
275284 return
276 if not orig_meta_language:
285 if not orig_meta_language and not duplicate_meta_language:
277286 self.tag('no-language-header-field', tags.safestr('Language:'), language)
278287 return language
279288
280289 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
281293 correct_plural_forms = None
282294 if language is not None:
283295 correct_plural_forms = language.get_plural_forms()
284296 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
287299 for message in file:
288300 if message.obsolete:
289301 continue
395407 self.tag('codomain-error-in-unused-plural-forms', message)
396408
397409 def check_mime(self, file, *, is_template, language):
410 # MIME-Version:
398411 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:
400415 if mime_version != '1.0':
401416 self.tag('invalid-mime-version', mime_version, '=>', '1.0')
402417 else:
403418 self.tag('no-mime-version-header-field', tags.safestr('MIME-Version: 1.0'))
419 # Content-Transfer-Encoding:
404420 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:
406424 if cte != '8bit':
407425 self.tag('invalid-content-transfer-encoding', cte, '=>', '8bit')
408426 else:
409427 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')
411435 encoding = None
412436 content_type_hint = 'text/plain; charset=<encoding>'
413437 if ct is not None:
418442 try:
419443 is_ascii_compatible = encinfo.is_ascii_compatible_encoding(encoding)
420444 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)
423448 else:
424449 self.tag('unknown-encoding', encoding)
425450 encoding = None
447472 self.tag('invalid-content-type', ct, '=>', content_type_hint)
448473 else:
449474 self.tag('invalid-content-type', ct, '=>', content_type_hint)
475 elif duplicate_ct:
476 self.tag('duplicate-header-field-content-type')
450477 else:
451478 self.tag('no-content-type-header-field', tags.safestr('Content-Type: ' + content_type_hint))
452479 return encoding
453480
454481 def check_dates(self, file, *, is_template):
455482 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
456486 date = file.metadata.get(field)
457487 if date is None:
458488 self.tag('no-date-header-field', field)
472502 self.tag('ancient-date', tags.safestr(field + ':'), date)
473503
474504 def check_project(self, file):
505 # Project-Id-Version:
475506 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')
476509 if project_id_version is None:
477510 self.tag('no-project-id-version-header-field')
478511 elif project_id_version in {'PACKAGE VERSION', 'PROJECT VERSION'}:
482515 self.tag('no-package-name-in-project-id-version', project_id_version)
483516 if not re.search(r'[0-9]', project_id_version):
484517 self.tag('no-version-in-project-id-version', project_id_version)
518 # Report-Msgid-Bugs-To:
485519 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:
487523 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)
490526 if '@' not in email_address:
491527 uri = urllib.parse.urlparse(report_msgid_bugs_to)
492528 if uri.scheme == '':
497533 self.tag('boilerplate-in-report-msgid-bugs-to', report_msgid_bugs_to)
498534
499535 def check_translator(self, file, *, is_template):
536 # Last-Translator:
500537 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:
502541 self.tag('no-last-translator-header-field')
503542 else:
504543 translator_name, translator_email = email.utils.parseaddr(translator)
509548 elif translator_email == 'EMAIL@ADDRESS':
510549 if not is_template:
511550 self.tag('boilerplate-in-last-translator', translator)
551 # Language-Team:
512552 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:
514556 self.tag('no-language-team-header-field')
515557 else:
516558 team_name, team_email = email.utils.parseaddr(team)
532574 if team_is_translator:
533575 self.tag('language-team-equal-to-last-translator', team, translator)
534576
535 def check_headers(self, file):
577 def check_headers(self, file, *, is_template):
536578 header_fields = frozenset(self.options.gettextinfo.header_fields)
537579 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
539583 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
540588 value, *strays = value.split('\n')
541589 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
543598 new_metadata[key] = value
544599 if key.startswith('X-'):
545600 continue
558613 try:
559614 duplicates = file.metadata.duplicates
560615 except AttributeError:
561 pass
616 duplicates = {}
562617 else:
563618 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)
565639 for value in values:
566640 value, *strays = value.split('\n')
567641 for stray in strays:
568642 self.tag('stray-header-line', stray)
643 new_metadata.duplicates = frozenset(duplicates)
569644 file.metadata = new_metadata
645 if file.metadata_is_fuzzy and not is_template:
646 self.tag('fuzzy-header-entry')
570647
571648 def check_messages(self, file, *, encoding):
572649 if encoding is None:
600677 set(find_unusual_characters(message.msgid_plural))
601678 )
602679 strings = [message.msgstr] + sorted(message.msgstr_plural.values())
680
681 conflict_marker = None
603682 for msgstr in strings:
604683 msgstr_uc = set(find_unusual_characters(msgstr))
605684 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)
614697 msgid_counter[message.msgid, message.msgctxt] += 1
615698 if msgid_counter[message.msgid, message.msgctxt] == 2:
616699 self.tag('duplicate-message-definition', message_repr(message))
3333 from . import tags
3434 from . import terminal
3535
36 __version__ = '0.9.2'
36 __version__ = '0.10'
3737
3838 def initialize_terminal():
3939 if sys.stdout.isatty():
5050 def tag(self, tagname, *extra):
5151 if tagname in self.options.ignore_tags:
5252 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 )
5459 s = tag.format(self.fake_path, *extra, color=True)
5560 print(s)
5661
8590 with open(os.devnull) as bitbucket:
8691 ipc.check_call(
8792 ['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...
8994 )
9095 options = copy_options(options,
9196 ignore_tags=ignore_tags,
2727 import contextlib
2828 import itertools
2929 import os
30 import subprocess as ipc
3130 import unicodedata
3231
32 from lib import iconv
3333 from lib import misc
3434
3535 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 )
4636
4737 def encode(input, errors='strict'):
4838 if not (parent._extra_encodings_installed > 0):
4939 # There doesn't seem to be a way to de-register a codec.
5040 # As a poor man's substitute, raise LookupError at encoding time.
5141 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)
6244
6345 def decode(input, errors='strict'):
6446 if not (parent._extra_encodings_installed > 0):
6547 # There doesn't seem to be a way to de-register a codec.
6648 # As a poor man's substitute, raise LookupError at decoding time.
6749 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)
8052
8153 def not_implemented(*args, **kwargs):
8254 raise NotImplementedError
9466 class EncodingInfo(object):
9567
9668 _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
10779 ], range(32, 127)))
10880 _interesting_ascii_str = _interesting_ascii_bytes.decode()
10981
4444 misc.check_sorted(fields)
4545 self.header_fields = frozenset(fields)
4646
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
4754 # ================
4855 # Plurals handling
4956 # ================
6774 (r'[?:]', None),
6875 (r'[()]', None),
6976 (r'[ \t]', None),
70 (r'.', '_'), # junk
77 (r'.', '_'), # junk
7178 ]
7279
7380 _plural_exp_token_re = '|'.join(
8289 if ctoken is not None:
8390 break
8491 if ctoken is not None:
85 if pytoken == '_': # junk
92 if pytoken == '_': # junk
8693 raise PluralExpressionSyntaxError(match.group(0))
8794 yield ' {} '.format(pytoken)
8895 else:
140147
141148 _parse_date = re.compile('''
142149 ^ \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
144151 ( \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
147154 \s*
148 ( [+-] [0-9]{2} ) # ZZ
155 ( [+-] [0-9]{2} ) # ZZ
149156 :?
150 ( [0-9]{2} ) # zz
157 ( [0-9]{2} ) # zz
151158 \s* $
152159 ''', re.VERBOSE).match
153160
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
265265
266266 def _visit_div(self, node, x, y):
267267 if y == (0, 0):
268 0 # division by zero
268 # division by zero
269269 return
270270 assert y[1] > 0
271271 return (
108108 raise FixingLanguageCodesFailed()
109109 elif cc != self.territory_code:
110110 # This shouldn't really happen, but better safe than sorry.
111 raise ValueError # <no-coverage>
111 raise ValueError # <no-coverage>
112112 # TODO: ll_CC could be still incorrect, even when both ll and CC are
113113 # correct.
114114 self.language_code = ll
130130 return True
131131
132132 def remove_encoding(self):
133 if self.encoding == None:
133 if self.encoding is None:
134134 return
135135 self.encoding = None
136136 return True
163163 return
164164 result = []
165165 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.
169169 ''.join(characters).encode(encoding)
170170 except UnicodeError:
171171 pass
177177 except UnicodeError as exc:
178178 result += [character]
179179 if exc.reason.startswith('iconv:'):
180 # Avoid further calls to iconv:
180 # Avoid further calls to iconv(1):
181181 break
182182 return result
183183
237237 if ll:
238238 try:
239239 self._primary_languages[ll]
240 except LookupError: # <no-coverage>
240 except LookupError: # <no-coverage>
241241 raise misc.DataIntegrityError
242242
243243 def _lookup_language_code(self, language):
99 pwd="$PWD"
1010 version="$1"
1111 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"
1313 export GZIP="-9 -n"
1414 mkdir -p "$sourceroot/$name-$version"
1515 if [ -d .hg ]
1919 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2020 # SOFTWARE.
2121
22 import io
2223 import os
2324 import sys
2425
5152 print()
5253
5354 def main():
55 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='UTF-8')
5456 datadir = os.path.join(basedir, 'data')
5557 taginfo = tags.TagInfo(datadir)
5658 output_tags_rst(taginfo)
4949 print('[{check}] {tag}'.format(check=check, tag=tag.name))
5050 sys.stdout.close()
5151 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)
5257
5358 if __name__ == '__main__':
5459 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
2222 import os
2323 import re
2424 import shlex
25 import signal
2526 import subprocess as ipc
2627 import sys
2728
101102
102103 # ----------------------------------------
103104
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
104132 def assert_emit_tags(path, etags, *, options=()):
105133 etags = list(etags)
106134 commandline = os.environ.get('I18NSPECTOR_COMMANDLINE')
112140 commandline += options
113141 commandline += [path]
114142 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))
117166 if stdout != etags:
118167 str_etags = [str(x) for x in etags]
119168 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."
33 [X] ancient-date
44 [X] arithmetic-error-in-plural-forms
55 [X] arithmetic-error-in-unused-plural-forms
6 [X] boilerplate-in-content-type
67 [X] boilerplate-in-language-team
78 [X] boilerplate-in-last-translator
89 [X] boilerplate-in-project-id-version
1011 [X] broken-encoding
1112 [X] codomain-error-in-plural-forms
1213 [X] codomain-error-in-unused-plural-forms
14 [X] conflict-marker-in-header-entry
15 [X] conflict-marker-in-translation
1316 [X] date-from-future
1417 [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
1528 [X] duplicate-message-definition
1629 [X] empty-file
1730 [X] encoding-in-language-header-field
31 [X] fuzzy-header-entry
1832 [X] inconsistent-leading-newlines
1933 [X] inconsistent-number-of-plural-forms
2034 [X] inconsistent-trailing-newlines
+0
-18
tests/blackbox_tests/duplicate-field.po less more
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
-18
tests/blackbox_tests/stray-header-line.po less more
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."
9393 def test_plural_exp_and(self):
9494 self._pe('n && 6', 7, 1)
9595 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
9797 self._pe('n && 0', 7, 0)
9898 self._pe('n && 0', 0, 0)
9999
100100 def test_plural_exp_or(self):
101101 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
103103 self._pe('n || 6', 0, 1)
104104 self._pe('n || 0', 7, 1)
105105 self._pe('n || 0', 0, 0)
135135 self._pe('n != 37', 42, 1)
136136
137137 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
144144
145145 def test_plural_exp_neg(self):
146146 self._pe('! n', 0, 1)
192192 else:
193193 assert_equal(new, expected)
194194
195
196195 def test_boilerplate(self):
197196 self._test('YEAR-MO-DA HO:MI+ZONE', None)
198197
397397 with E.extra_encodings():
398398 result = lang.get_unrepresentable_characters(encoding)
399399 assert_not_equal(result, [])
400 assert_equal(len(result), 1)
401400
402401 # vim:ts=4 sw=4 et
2525
2626 class test_escape:
2727
28 _esc = staticmethod(lib.tags._escape) # FIXME: _escape is private
28 _esc = staticmethod(lib.tags._escape) # FIXME: _escape is private
2929
3030 def _test(self, s, expected):
3131 assert_equal(