blob: f94070feb2409072dcddce125ab62c613be52221 [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')
Bram Moolenaar0b8cf272020-08-30 19:42:06 +020025 let idline = substitute(idline, "%%", '', 'g')
Bram Moolenaara5792f52005-11-23 21:25:05 +000026
Bram Moolenaar1c6136a2009-09-11 11:00:05 +000027 " remove '%' used for plural forms.
28 let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
29
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000030 " remove everything but % items.
31 return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
32endfunc
33
Bram Moolenaarec420592016-08-26 15:51:53 +020034" This only works when 'wrapscan' is not set.
Bram Moolenaara411e5d2010-08-04 15:47:08 +020035let s:save_wrapscan = &wrapscan
Bram Moolenaarec420592016-08-26 15:51:53 +020036set nowrapscan
Bram Moolenaara411e5d2010-08-04 15:47:08 +020037
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000038" Start at the first "msgid" line.
Bram Moolenaar7f937032017-07-19 14:34:42 +020039let wsv = winsaveview()
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000401
Bram Moolenaarec420592016-08-26 15:51:53 +020041/^msgid\>
42
43" When an error is detected this is set to the line number.
44" Note: this is used in the Makefile.
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000045let error = 0
46
47while 1
Bram Moolenaare53ec392019-11-16 18:49:50 +010048 let lnum = line('.')
49 if getline(lnum) =~ 'msgid "Text;.*;"'
50 if getline(lnum + 1) !~ '^msgstr "\([^;]\+;\)\+"'
51 echomsg 'Mismatching ; in line ' . (lnum + 1)
52 echomsg 'Did you forget the trailing semicolon?'
53 if error == 0
54 let error = lnum + 1
55 endif
56 endif
57 endif
58
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000059 if getline(line('.') - 1) !~ "no-c-format"
Bram Moolenaarec420592016-08-26 15:51:53 +020060 " go over the "msgid" and "msgid_plural" lines
61 let prevfromline = 'foobar'
62 while 1
63 let fromline = GetMline()
64 if prevfromline != 'foobar' && prevfromline != fromline
65 echomsg 'Mismatching % in line ' . (line('.') - 1)
66 echomsg 'msgid: ' . prevfromline
67 echomsg 'msgid ' . fromline
68 if error == 0
69 let error = line('.')
70 endif
71 endif
72 if getline('.') !~ 'msgid_plural'
73 break
74 endif
75 let prevfromline = fromline
76 endwhile
77
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000078 if getline('.') !~ '^msgstr'
Bram Moolenaarec420592016-08-26 15:51:53 +020079 echomsg 'Missing "msgstr" in line ' . line('.')
80 if error == 0
81 let error = line('.')
82 endif
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +000083 endif
Bram Moolenaarec420592016-08-26 15:51:53 +020084
85 " check all the 'msgstr' lines
86 while getline('.') =~ '^msgstr'
87 let toline = GetMline()
88 if fromline != toline
89 echomsg 'Mismatching % in line ' . (line('.') - 1)
90 echomsg 'msgid: ' . fromline
91 echomsg 'msgstr: ' . toline
92 if error == 0
93 let error = line('.')
94 endif
95 endif
96 if line('.') == line('$')
97 break
98 endif
99 endwhile
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000100 endif
101
Bram Moolenaarec420592016-08-26 15:51:53 +0200102 " Find next msgid. Quit when there is no more.
103 let lnum = line('.')
104 silent! /^msgid\>
105 if line('.') == lnum
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000106 break
107 endif
108endwhile
109
Bram Moolenaar595f51c2008-06-09 12:46:00 +0000110" Check that error code in msgid matches the one in msgstr.
111"
112" Examples of mismatches found with msgid "E123: ..."
113" - msgstr "E321: ..." error code mismatch
114" - msgstr "W123: ..." warning instead of error
115" - msgstr "E123 ..." missing colon
116" - msgstr "..." missing error code
117"
1181
119if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0
Bram Moolenaarec420592016-08-26 15:51:53 +0200120 echomsg 'Mismatching error/warning code in line ' . line('.')
121 if error == 0
122 let error = line('.')
123 endif
Bram Moolenaar595f51c2008-06-09 12:46:00 +0000124endif
125
Bram Moolenaar7f937032017-07-19 14:34:42 +0200126func! CountNl(first, last)
127 let nl = 0
128 for lnum in range(a:first, a:last)
Bram Moolenaar9966b212017-07-28 16:46:57 +0200129 let nl += count(getline(lnum), "\n")
Bram Moolenaar7f937032017-07-19 14:34:42 +0200130 endfor
131 return nl
132endfunc
133
Bram Moolenaar9966b212017-07-28 16:46:57 +0200134" Check that the \n at the end of the msgid line is also present in the msgstr
Bram Moolenaar7f937032017-07-19 14:34:42 +0200135" line. Skip over the header.
Bram Moolenaarb07bbb02018-04-30 15:45:17 +02001361
Bram Moolenaar7f937032017-07-19 14:34:42 +0200137/^"MIME-Version:
138while 1
139 let lnum = search('^msgid\>')
140 if lnum <= 0
141 break
142 endif
143 let strlnum = search('^msgstr\>')
144 let end = search('^$')
145 if end <= 0
146 let end = line('$') + 1
147 endif
148 let origcount = CountNl(lnum, strlnum - 1)
149 let transcount = CountNl(strlnum, end - 1)
150 " Allow for a few more or less line breaks when there are 2 or more
151 if origcount != transcount && (origcount <= 2 || transcount <= 2)
Bram Moolenaar9966b212017-07-28 16:46:57 +0200152 echomsg 'Mismatching "\n" in line ' . line('.')
Bram Moolenaar7f937032017-07-19 14:34:42 +0200153 if error == 0
154 let error = lnum
155 endif
156 endif
157endwhile
158
Bram Moolenaaraaef1ba2017-08-01 17:40:23 +0200159" Check that the file is well formed according to msgfmts understanding
160if executable("msgfmt")
161 let filename = expand("%")
Bram Moolenaar01164a62017-11-02 22:58:42 +0100162 " Newer msgfmt does not take OLD_PO_FILE_INPUT argument, must be in
163 " environment.
164 let $OLD_PO_FILE_INPUT = 'yes'
165 let a = system("msgfmt --statistics " . filename)
Bram Moolenaaraaef1ba2017-08-01 17:40:23 +0200166 if v:shell_error != 0
167 let error = matchstr(a, filename.':\zs\d\+\ze:')+0
168 for line in split(a, '\n') | echomsg line | endfor
169 endif
170endif
171
Bram Moolenaar9cfc7d82018-05-13 22:37:03 +0200172" Check that the plural form is properly initialized
1731
174let plural = search('^msgid_plural ', 'n')
175if (plural && search('^"Plural-Forms: ', 'n') == 0) || (plural && search('^msgstr\[0\] ".\+"', 'n') != plural + 1)
176 if search('^"Plural-Forms: ', 'n') == 0
177 echomsg "Missing Plural header"
178 if error == 0
179 let error = search('\(^"[A-Za-z-_]\+: .*\\n"\n\)\+\zs', 'n') - 1
180 endif
181 elseif error == 0
182 let error = plural
183 endif
184elseif !plural && search('^"Plural-Forms: ', 'n')
185 " We allow for a stray plural header, msginit adds one.
186endif
187
Bram Moolenaard1d037e2018-06-24 18:04:50 +0200188" Check that 8bit encoding is used instead of 8-bit
189let cte = search('^"Content-Transfer-Encoding:\s\+8-bit', 'n')
190let ctc = search('^"Content-Type:.*;\s\+\<charset=[iI][sS][oO]_', 'n')
191let ctu = search('^"Content-Type:.*;\s\+\<charset=utf-8', 'n')
192if cte
193 echomsg "Content-Transfer-Encoding should be 8bit instead of 8-bit"
194 " TODO: make this an error
195 " if error == 0
196 " let error = cte
197 " endif
198elseif ctc
199 echomsg "Content-Type charset should be 'ISO-...' instead of 'ISO_...'"
200 " TODO: make this an error
201 " if error == 0
202 " let error = ct
203 " endif
204elseif ctu
205 echomsg "Content-Type charset should be 'UTF-8' instead of 'utf-8'"
206 " TODO: make this an error
207 " if error == 0
208 " let error = ct
209 " endif
210endif
211
Bram Moolenaar9cfc7d82018-05-13 22:37:03 +0200212
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000213if error == 0
Bram Moolenaar7f937032017-07-19 14:34:42 +0200214 " If all was OK restore the view.
215 call winrestview(wsv)
Bram Moolenaarec420592016-08-26 15:51:53 +0200216 echomsg "OK"
217else
Bram Moolenaard1d037e2018-06-24 18:04:50 +0200218 " Put the cursor on the line with the error.
Bram Moolenaarec420592016-08-26 15:51:53 +0200219 exe error
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000220endif
221
Bram Moolenaara411e5d2010-08-04 15:47:08 +0200222let &wrapscan = s:save_wrapscan
223unlet s:save_wrapscan
224
Bram Moolenaar1d94f9b2005-08-04 21:29:45 +0000225endif