Skip to content

Commit 2566a9f

Browse files
azc100chrisbra
authored andcommitted
check.vim: Improve po message checks
Signed-off-by: Antonio Giovanni Colombo <azc100@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent aa55185 commit 2566a9f

1 file changed

Lines changed: 109 additions & 41 deletions

File tree

src/po/check.vim

Lines changed: 109 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
" Vim script for checking .po files.
22
"
3-
" 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".
3+
" Goes through the xx.po file (more than once)
4+
" and verify various congruences
5+
" See the comments in the code
66

7-
" Last Update: 2025 Jul 10
7+
" Last Update: 2025 Jul 22
88

9-
if 1 " Only execute this if the eval feature is available.
9+
if 1" Only execute this if the eval feature is available.
1010

11-
" using line continuation
11+
" Using line continuation (set cpo to vim default value)
12+
let s:save_cpo = &cpo
1213
set cpo&vim
1314

15+
" This only works when 'wrapscan' is not set.
16+
let s:save_wrapscan = &wrapscan
17+
set nowrapscan
18+
1419
" Function to get a split line at the cursor.
1520
" Used for both msgid and msgstr lines.
1621
" Removes all text except % items and returns the result.
@@ -43,9 +48,15 @@ func! GetMline()
4348
return substitute(idline, '[^%]*\(%([1-9][0-9]*\$)\=[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
4449
endfunc
4550

46-
" This only works when 'wrapscan' is not set.
47-
let s:save_wrapscan = &wrapscan
48-
set nowrapscan
51+
func! CountNl(first, last)
52+
let nl = 0
53+
for lnum in range(a:first, a:last)
54+
let nl += count(getline(lnum), "\n")
55+
endfor
56+
return nl
57+
endfunc
58+
59+
" main
4960

5061
" Start at the first "msgid" line.
5162
let wsv = winsaveview()
@@ -57,63 +68,72 @@ keeppatterns /^msgid\>
5768
let error = 0
5869

5970
while 1
71+
" for each "msgid"
72+
73+
" check msgid "Text;editor;"
74+
" translation must have two ";" as well
6075
let lnum = line('.')
6176
if getline(lnum) =~ 'msgid "Text;.*;"'
62-
if getline(lnum + 1) !~ '^msgstr "\([^;]\+;\)\+"'
77+
if getline(lnum + 1) !~ '^msgstr "\([^;]\+;\)\+"$'
6378
echomsg 'Mismatching ; in line ' . (lnum + 1)
64-
echomsg 'Did you forget the trailing semicolon?'
79+
echomsg 'Wrong semicolon count'
6580
if error == 0
66-
let error = lnum + 1
81+
let error = lnum + 1
6782
endif
6883
endif
6984
endif
7085

86+
" check for equal number of % in msgid and msgstr
87+
" it is skipping the no-c-format strings
7188
if getline(line('.') - 1) !~ "no-c-format"
72-
" go over the "msgid" and "msgid_plural" lines
89+
" skip the "msgid_plural" lines
7390
let prevfromline = 'foobar'
7491
let plural = 0
7592
while 1
7693
if getline('.') =~ 'msgid_plural'
77-
let plural += 1
94+
let plural += 1
7895
endif
7996
let fromline = GetMline()
8097
if prevfromline != 'foobar' && prevfromline != fromline
81-
\ && (plural != 1
82-
\ || count(prevfromline, '%') + 1 != count(fromline, '%'))
83-
echomsg 'Mismatching % in line ' . (line('.') - 1)
84-
echomsg 'msgid: ' . prevfromline
85-
echomsg 'msgid: ' . fromline
86-
if error == 0
87-
let error = line('.')
88-
endif
98+
\ && (plural != 1
99+
\ || count(prevfromline, '%') + 1 != count(fromline, '%'))
100+
echomsg 'possibly mismatching % in line ' . (line('.') - 1)
101+
echomsg 'msgid: ' . prevfromline
102+
echomsg 'msgid: ' . fromline
103+
if error == 0
104+
let error = line('.')
105+
endif
89106
endif
90107
if getline('.') !~ 'msgid_plural'
91-
break
108+
break
92109
endif
93110
let prevfromline = fromline
94111
endwhile
95112

113+
" checks that for each 'msgid' there is a 'msgstr'
96114
if getline('.') !~ '^msgstr'
97115
echomsg 'Missing "msgstr" in line ' . line('.')
98116
if error == 0
99-
let error = line('.')
117+
let error = line('.')
100118
endif
101119
endif
102120

103-
" check all the 'msgstr' lines
121+
" check all the 'msgstr' lines have the same number of '%'
122+
" only the number of '%' is checked,
123+
" %d vs. %s or %d vs. %ld go undetected
104124
while getline('.') =~ '^msgstr'
105125
let toline = GetMline()
106126
if fromline != toline
107-
\ && (plural == 0 || count(fromline, '%') != count(toline, '%') + 1)
108-
echomsg 'Mismatching % in line ' . (line('.') - 1)
109-
echomsg 'msgid: ' . fromline
110-
echomsg 'msgstr: ' . toline
111-
if error == 0
112-
let error = line('.')
113-
endif
127+
\ && (plural == 0 || count(fromline, '%') != count(toline, '%') + 1)
128+
echomsg 'possibly mismatching % in line ' . (line('.') - 1)
129+
echomsg 'msgid: ' . fromline
130+
echomsg 'msgstr: ' . toline
131+
if error == 0
132+
let error = line('.')
133+
endif
114134
endif
115135
if line('.') == line('$')
116-
break
136+
break
117137
endif
118138
endwhile
119139
endif
@@ -142,14 +162,6 @@ if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!')
142162
endif
143163
endif
144164

145-
func! CountNl(first, last)
146-
let nl = 0
147-
for lnum in range(a:first, a:last)
148-
let nl += count(getline(lnum), "\n")
149-
endfor
150-
return nl
151-
endfunc
152-
153165
" Check that the \n at the end of the msgid line is also present in the msgstr
154166
" line. Skip over the header.
155167
1
@@ -175,6 +187,55 @@ while 1
175187
endif
176188
endwhile
177189

190+
" Check that the eventual continuation of 'msgstr' is well formed
191+
" final '""', '\n"', ' "' are OK
192+
" Beware, it can give false positives if the message is split
193+
" in the middle of a word
194+
1
195+
keeppatterns /^"MIME-Version:
196+
while 1
197+
let lnum = search('^msgid\>')
198+
if lnum <= 0
199+
break
200+
endif
201+
" "msgstr" goes from strlnum to end-1
202+
let strlnum = search('^msgstr\>')
203+
let end = search('^$')
204+
if end <= 0
205+
let end = line('$') + 1
206+
endif
207+
" only if there is a continuation line...
208+
if end > strlnum + 1
209+
let ilnum = strlnum
210+
while ilnum < end - 1
211+
let iltype = 0
212+
if getline( ilnum ) =~ "^msgid_plural"
213+
let iltype = 99
214+
endif
215+
if getline( ilnum ) =~ "^msgstr["
216+
let iltype = 98
217+
endif
218+
if getline( ilnum ) =~ "\"\""
219+
let iltype = 1
220+
endif
221+
if getline( ilnum ) =~ " \"$"
222+
let iltype = 2
223+
endif
224+
if getline( ilnum ) =~ "\\\\n\"$"
225+
let iltype = 3
226+
endif
227+
if iltype == 0
228+
echomsg 'Possibly incorrect final at line: ' . ilnum
229+
" TODO: make this an error
230+
" if error == 0
231+
" let error = ilnum
232+
" endif
233+
endif
234+
let ilnum += 1
235+
endwhile
236+
endif
237+
endwhile
238+
178239
" Check that the file is well formed according to msgfmts understanding
179240
if executable("msgfmt")
180241
let filename = expand("%")
@@ -257,7 +318,14 @@ else
257318
exe error
258319
endif
259320

321+
" restore original wrapscan
260322
let &wrapscan = s:save_wrapscan
261323
unlet s:save_wrapscan
262324

325+
" restore original cpo
326+
let &cpo = s:save_cpo
327+
unlet s:save_cpo
328+
263329
endif
330+
331+
" vim:sts=2:sw=2:et

0 commit comments

Comments
 (0)