blob: 454a0536925380e4c50a235241058f1707a75532 [file] [log] [blame]
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +00001" Vim script for checking .po files.
2"
Bram Moolenaar595f51c2008-06-09 12:46:00 +00003" Go through the file and verify that:
4" - All %...s items in "msgid" are identical to the ones in "msgstr".
5" - An error or warning code in "msgid" matches the one in "msgstr".
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +00006
7if 1 " Only execute this if the eval feature is available.
8
9" Function to get a split line at the cursor.
10" Used for both msgid and msgstr lines.
11" Removes all text except % items and returns the result.
12func! GetMline()
13 let idline = substitute(getline('.'), '"\(.*\)"$', '\1', '')
14 while line('.') < line('$')
15 +
16 let line = getline('.')
17 if line[0] != '"'
18 break
19 endif
20 let idline .= substitute(line, '"\(.*\)"$', '\1', '')
21 endwhile
22
Bram Moolenaara5792f52005-11-23 21:25:05 +000023 " remove '%', not used for formatting.
24 let idline = substitute(idline, "'%'", '', 'g')
25
Bram Moolenaar1c6136a2009-09-11 11:00:05 +000026 " remove '%' used for plural forms.
27 let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
28
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000029 " remove everything but % items.
30 return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
31endfunc
32
Bram Moolenaarec420592016-08-26 15:51:53 +020033" This only works when 'wrapscan' is not set.
Bram Moolenaara411e5d2010-08-04 15:47:08 +020034let s:save_wrapscan = &wrapscan
Bram Moolenaarec420592016-08-26 15:51:53 +020035set nowrapscan
Bram Moolenaara411e5d2010-08-04 15:47:08 +020036
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000037" Start at the first "msgid" line.
Bram Moolenaar7f937032017-07-19 14:34:42 +020038let wsv = winsaveview()
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000391
Bram Moolenaarec420592016-08-26 15:51:53 +020040/^msgid\>
41
42" When an error is detected this is set to the line number.
43" Note: this is used in the Makefile.
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000044let error = 0
45
46while 1
Bram Moolenaare53ec392019-11-16 18:49:50 +010047 let lnum = line('.')
48 if getline(lnum) =~ 'msgid "Text;.*;"'
49 if getline(lnum + 1) !~ '^msgstr "\([^;]\+;\)\+"'
50 echomsg 'Mismatching ; in line ' . (lnum + 1)
51 echomsg 'Did you forget the trailing semicolon?'
52 if error == 0
53 let error = lnum + 1
54 endif
55 endif
56 endif
57
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000058 if getline(line('.') - 1) !~ "no-c-format"
Bram Moolenaarec420592016-08-26 15:51:53 +020059 " go over the "msgid" and "msgid_plural" lines
60 let prevfromline = 'foobar'
61 while 1
62 let fromline = GetMline()
63 if prevfromline != 'foobar' && prevfromline != fromline
64 echomsg 'Mismatching % in line ' . (line('.') - 1)
65 echomsg 'msgid: ' . prevfromline
66 echomsg 'msgid ' . fromline
67 if error == 0
68 let error = line('.')
69 endif
70 endif
71 if getline('.') !~ 'msgid_plural'
72 break
73 endif
74 let prevfromline = fromline
75 endwhile
76
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000077 if getline('.') !~ '^msgstr'
Bram Moolenaarec420592016-08-26 15:51:53 +020078 echomsg 'Missing "msgstr" in line ' . line('.')
79 if error == 0
80 let error = line('.')
81 endif
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000082 endif
Bram Moolenaarec420592016-08-26 15:51:53 +020083
84 " check all the 'msgstr' lines
85 while getline('.') =~ '^msgstr'
86 let toline = GetMline()
87 if fromline != toline
88 echomsg 'Mismatching % in line ' . (line('.') - 1)
89 echomsg 'msgid: ' . fromline
90 echomsg 'msgstr: ' . toline
91 if error == 0
92 let error = line('.')
93 endif
94 endif
95 if line('.') == line('$')
96 break
97 endif
98 endwhile
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000099 endif
100
Bram Moolenaarec420592016-08-26 15:51:53 +0200101 " Find next msgid. Quit when there is no more.
102 let lnum = line('.')
103 silent! /^msgid\>
104 if line('.') == lnum
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000105 break
106 endif
107endwhile
108
Bram Moolenaar595f51c2008-06-09 12:46:00 +0000109" Check that error code in msgid matches the one in msgstr.
110"
111" Examples of mismatches found with msgid "E123: ..."
112" - msgstr "E321: ..." error code mismatch
113" - msgstr "W123: ..." warning instead of error
114" - msgstr "E123 ..." missing colon
115" - msgstr "..." missing error code
116"
1171
118if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0
Bram Moolenaarec420592016-08-26 15:51:53 +0200119 echomsg 'Mismatching error/warning code in line ' . line('.')
120 if error == 0
121 let error = line('.')
122 endif
Bram Moolenaar595f51c2008-06-09 12:46:00 +0000123endif
124
Bram Moolenaar7f937032017-07-19 14:34:42 +0200125func! CountNl(first, last)
126 let nl = 0
127 for lnum in range(a:first, a:last)
Bram Moolenaar9966b212017-07-28 16:46:57 +0200128 let nl += count(getline(lnum), "\n")
Bram Moolenaar7f937032017-07-19 14:34:42 +0200129 endfor
130 return nl
131endfunc
132
Bram Moolenaar9966b212017-07-28 16:46:57 +0200133" Check that the \n at the end of the msgid line is also present in the msgstr
Bram Moolenaar7f937032017-07-19 14:34:42 +0200134" line. Skip over the header.
Bram Moolenaarb07bbb02018-04-30 15:45:17 +02001351
Bram Moolenaar7f937032017-07-19 14:34:42 +0200136/^"MIME-Version:
137while 1
138 let lnum = search('^msgid\>')
139 if lnum <= 0
140 break
141 endif
142 let strlnum = search('^msgstr\>')
143 let end = search('^$')
144 if end <= 0
145 let end = line('$') + 1
146 endif
147 let origcount = CountNl(lnum, strlnum - 1)
148 let transcount = CountNl(strlnum, end - 1)
149 " Allow for a few more or less line breaks when there are 2 or more
150 if origcount != transcount && (origcount <= 2 || transcount <= 2)
Bram Moolenaar9966b212017-07-28 16:46:57 +0200151 echomsg 'Mismatching "\n" in line ' . line('.')
Bram Moolenaar7f937032017-07-19 14:34:42 +0200152 if error == 0
153 let error = lnum
154 endif
155 endif
156endwhile
157
Bram Moolenaaraaef1ba2017-08-01 17:40:23 +0200158" Check that the file is well formed according to msgfmts understanding
159if executable("msgfmt")
160 let filename = expand("%")
Bram Moolenaar01164a62017-11-02 22:58:42 +0100161 " Newer msgfmt does not take OLD_PO_FILE_INPUT argument, must be in
162 " environment.
163 let $OLD_PO_FILE_INPUT = 'yes'
164 let a = system("msgfmt --statistics " . filename)
Bram Moolenaaraaef1ba2017-08-01 17:40:23 +0200165 if v:shell_error != 0
166 let error = matchstr(a, filename.':\zs\d\+\ze:')+0
167 for line in split(a, '\n') | echomsg line | endfor
168 endif
169endif
170
Bram Moolenaar9cfc7d82018-05-13 22:37:03 +0200171" Check that the plural form is properly initialized
1721
173let plural = search('^msgid_plural ', 'n')
174if (plural && search('^"Plural-Forms: ', 'n') == 0) || (plural && search('^msgstr\[0\] ".\+"', 'n') != plural + 1)
175 if search('^"Plural-Forms: ', 'n') == 0
176 echomsg "Missing Plural header"
177 if error == 0
178 let error = search('\(^"[A-Za-z-_]\+: .*\\n"\n\)\+\zs', 'n') - 1
179 endif
180 elseif error == 0
181 let error = plural
182 endif
183elseif !plural && search('^"Plural-Forms: ', 'n')
184 " We allow for a stray plural header, msginit adds one.
185endif
186
Bram Moolenaard1d037e2018-06-24 18:04:50 +0200187" Check that 8bit encoding is used instead of 8-bit
188let cte = search('^"Content-Transfer-Encoding:\s\+8-bit', 'n')
189let ctc = search('^"Content-Type:.*;\s\+\<charset=[iI][sS][oO]_', 'n')
190let ctu = search('^"Content-Type:.*;\s\+\<charset=utf-8', 'n')
191if cte
192 echomsg "Content-Transfer-Encoding should be 8bit instead of 8-bit"
193 " TODO: make this an error
194 " if error == 0
195 " let error = cte
196 " endif
197elseif ctc
198 echomsg "Content-Type charset should be 'ISO-...' instead of 'ISO_...'"
199 " TODO: make this an error
200 " if error == 0
201 " let error = ct
202 " endif
203elseif ctu
204 echomsg "Content-Type charset should be 'UTF-8' instead of 'utf-8'"
205 " TODO: make this an error
206 " if error == 0
207 " let error = ct
208 " endif
209endif
210
Bram Moolenaar9cfc7d82018-05-13 22:37:03 +0200211
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000212if error == 0
Bram Moolenaar7f937032017-07-19 14:34:42 +0200213 " If all was OK restore the view.
214 call winrestview(wsv)
Bram Moolenaarec420592016-08-26 15:51:53 +0200215 echomsg "OK"
216else
Bram Moolenaard1d037e2018-06-24 18:04:50 +0200217 " Put the cursor on the line with the error.
Bram Moolenaarec420592016-08-26 15:51:53 +0200218 exe error
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000219endif
220
Bram Moolenaara411e5d2010-08-04 15:47:08 +0200221let &wrapscan = s:save_wrapscan
222unlet s:save_wrapscan
223
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000224endif