xref: /vim-8.2.3635/src/po/check.vim (revision 0b8cf278)
1" Vim script for checking .po files.
2"
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".
6
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
23  " remove '%', not used for formatting.
24  let idline = substitute(idline, "'%'", '', 'g')
25  let idline = substitute(idline, "%%", '', 'g')
26
27  " remove '%' used for plural forms.
28  let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
29
30  " remove everything but % items.
31  return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
32endfunc
33
34" This only works when 'wrapscan' is not set.
35let s:save_wrapscan = &wrapscan
36set nowrapscan
37
38" Start at the first "msgid" line.
39let wsv = winsaveview()
401
41/^msgid\>
42
43" When an error is detected this is set to the line number.
44" Note: this is used in the Makefile.
45let error = 0
46
47while 1
48  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
59  if getline(line('.') - 1) !~ "no-c-format"
60    " 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
78    if getline('.') !~ '^msgstr'
79      echomsg 'Missing "msgstr" in line ' . line('.')
80      if error == 0
81	let error = line('.')
82      endif
83    endif
84
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
100  endif
101
102  " Find next msgid.  Quit when there is no more.
103  let lnum = line('.')
104  silent! /^msgid\>
105  if line('.') == lnum
106    break
107  endif
108endwhile
109
110" 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
120  echomsg 'Mismatching error/warning code in line ' . line('.')
121  if error == 0
122    let error = line('.')
123  endif
124endif
125
126func! CountNl(first, last)
127  let nl = 0
128  for lnum in range(a:first, a:last)
129    let nl += count(getline(lnum), "\n")
130  endfor
131  return nl
132endfunc
133
134" Check that the \n at the end of the msgid line is also present in the msgstr
135" line.  Skip over the header.
1361
137/^"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)
152    echomsg 'Mismatching "\n" in line ' . line('.')
153    if error == 0
154      let error = lnum
155    endif
156  endif
157endwhile
158
159" Check that the file is well formed according to msgfmts understanding
160if executable("msgfmt")
161  let filename = expand("%")
162  " 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)
166  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
172" 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
188" 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
212
213if error == 0
214  " If all was OK restore the view.
215  call winrestview(wsv)
216  echomsg "OK"
217else
218  " Put the cursor on the line with the error.
219  exe error
220endif
221
222let &wrapscan = s:save_wrapscan
223unlet s:save_wrapscan
224
225endif
226