xref: /vim-8.2.3635/src/po/check.vim (revision ba3ff539)
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
26  " remove '%' used for plural forms.
27  let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
28
29  " remove everything but % items.
30  return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
31endfunc
32
33" This only works when 'wrapscan' is not set.
34let s:save_wrapscan = &wrapscan
35set nowrapscan
36
37" Start at the first "msgid" line.
38let wsv = winsaveview()
391
40/^msgid\>
41
42" When an error is detected this is set to the line number.
43" Note: this is used in the Makefile.
44let error = 0
45
46while 1
47  if getline(line('.') - 1) !~ "no-c-format"
48    " go over the "msgid" and "msgid_plural" lines
49    let prevfromline = 'foobar'
50    while 1
51      let fromline = GetMline()
52      if prevfromline != 'foobar' && prevfromline != fromline
53	echomsg 'Mismatching % in line ' . (line('.') - 1)
54	echomsg 'msgid: ' . prevfromline
55	echomsg 'msgid ' . fromline
56	if error == 0
57	  let error = line('.')
58	endif
59      endif
60      if getline('.') !~ 'msgid_plural'
61	break
62      endif
63      let prevfromline = fromline
64    endwhile
65
66    if getline('.') !~ '^msgstr'
67      echomsg 'Missing "msgstr" in line ' . line('.')
68      if error == 0
69	let error = line('.')
70      endif
71    endif
72
73    " check all the 'msgstr' lines
74    while getline('.') =~ '^msgstr'
75      let toline = GetMline()
76      if fromline != toline
77	echomsg 'Mismatching % in line ' . (line('.') - 1)
78	echomsg 'msgid: ' . fromline
79	echomsg 'msgstr: ' . toline
80	if error == 0
81	  let error = line('.')
82	endif
83      endif
84      if line('.') == line('$')
85	break
86      endif
87    endwhile
88  endif
89
90  " Find next msgid.  Quit when there is no more.
91  let lnum = line('.')
92  silent! /^msgid\>
93  if line('.') == lnum
94    break
95  endif
96endwhile
97
98" Check that error code in msgid matches the one in msgstr.
99"
100" Examples of mismatches found with msgid "E123: ..."
101" - msgstr "E321: ..."    error code mismatch
102" - msgstr "W123: ..."    warning instead of error
103" - msgstr "E123 ..."     missing colon
104" - msgstr "..."          missing error code
105"
1061
107if search('msgid "\("\n"\)\?\([EW][0-9]\+:\).*\nmsgstr "\("\n"\)\?[^"]\@=\2\@!') > 0
108  echomsg 'Mismatching error/warning code in line ' . line('.')
109  if error == 0
110    let error = line('.')
111  endif
112endif
113
114func! CountNl(first, last)
115  let nl = 0
116  for lnum in range(a:first, a:last)
117    let nl += count(getline(lnum), "\n")
118  endfor
119  return nl
120endfunc
121
122" Check that the \n at the end of the msgid line is also present in the msgstr
123" line.  Skip over the header.
1241
125/^"MIME-Version:
126while 1
127  let lnum = search('^msgid\>')
128  if lnum <= 0
129    break
130  endif
131  let strlnum = search('^msgstr\>')
132  let end = search('^$')
133  if end <= 0
134    let end = line('$') + 1
135  endif
136  let origcount = CountNl(lnum, strlnum - 1)
137  let transcount = CountNl(strlnum, end - 1)
138  " Allow for a few more or less line breaks when there are 2 or more
139  if origcount != transcount && (origcount <= 2 || transcount <= 2)
140    echomsg 'Mismatching "\n" in line ' . line('.')
141    if error == 0
142      let error = lnum
143    endif
144  endif
145endwhile
146
147" Check that the file is well formed according to msgfmts understanding
148if executable("msgfmt")
149  let filename = expand("%")
150  " Newer msgfmt does not take OLD_PO_FILE_INPUT argument, must be in
151  " environment.
152  let $OLD_PO_FILE_INPUT = 'yes'
153  let a = system("msgfmt --statistics " . filename)
154  if v:shell_error != 0
155    let error = matchstr(a, filename.':\zs\d\+\ze:')+0
156    for line in split(a, '\n') | echomsg line | endfor
157  endif
158endif
159
160" Check that the plural form is properly initialized
1611
162let plural = search('^msgid_plural ', 'n')
163if (plural && search('^"Plural-Forms: ', 'n') == 0) || (plural && search('^msgstr\[0\] ".\+"', 'n') != plural + 1)
164  if search('^"Plural-Forms: ', 'n') == 0
165    echomsg "Missing Plural header"
166    if error == 0
167      let error = search('\(^"[A-Za-z-_]\+: .*\\n"\n\)\+\zs', 'n') - 1
168    endif
169  elseif error == 0
170    let error = plural
171  endif
172elseif !plural && search('^"Plural-Forms: ', 'n')
173  " We allow for a stray plural header, msginit adds one.
174endif
175
176" Check that 8bit encoding is used instead of 8-bit
177let cte = search('^"Content-Transfer-Encoding:\s\+8-bit', 'n')
178let ctc = search('^"Content-Type:.*;\s\+\<charset=[iI][sS][oO]_', 'n')
179let ctu = search('^"Content-Type:.*;\s\+\<charset=utf-8', 'n')
180if cte
181  echomsg "Content-Transfer-Encoding should be 8bit instead of 8-bit"
182  " TODO: make this an error
183  " if error == 0
184  "   let error = cte
185  " endif
186elseif ctc
187  echomsg "Content-Type charset should be 'ISO-...' instead of 'ISO_...'"
188  " TODO: make this an error
189  " if error == 0
190  "   let error = ct
191  " endif
192elseif ctu
193  echomsg "Content-Type charset should be 'UTF-8' instead of 'utf-8'"
194  " TODO: make this an error
195  " if error == 0
196  "   let error = ct
197  " endif
198endif
199
200
201if error == 0
202  " If all was OK restore the view.
203  call winrestview(wsv)
204  echomsg "OK"
205else
206  " Put the cursor on the line with the error.
207  exe error
208endif
209
210let &wrapscan = s:save_wrapscan
211unlet s:save_wrapscan
212
213endif
214