xref: /vim-8.2.3635/src/po/check.vim (revision ed37d9b3)
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  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
58  if getline(line('.') - 1) !~ "no-c-format"
59    " 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
77    if getline('.') !~ '^msgstr'
78      echomsg 'Missing "msgstr" in line ' . line('.')
79      if error == 0
80	let error = line('.')
81      endif
82    endif
83
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
99  endif
100
101  " Find next msgid.  Quit when there is no more.
102  let lnum = line('.')
103  silent! /^msgid\>
104  if line('.') == lnum
105    break
106  endif
107endwhile
108
109" 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
119  echomsg 'Mismatching error/warning code in line ' . line('.')
120  if error == 0
121    let error = line('.')
122  endif
123endif
124
125func! CountNl(first, last)
126  let nl = 0
127  for lnum in range(a:first, a:last)
128    let nl += count(getline(lnum), "\n")
129  endfor
130  return nl
131endfunc
132
133" Check that the \n at the end of the msgid line is also present in the msgstr
134" line.  Skip over the header.
1351
136/^"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)
151    echomsg 'Mismatching "\n" in line ' . line('.')
152    if error == 0
153      let error = lnum
154    endif
155  endif
156endwhile
157
158" Check that the file is well formed according to msgfmts understanding
159if executable("msgfmt")
160  let filename = expand("%")
161  " 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)
165  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
171" 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
187" 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
211
212if error == 0
213  " If all was OK restore the view.
214  call winrestview(wsv)
215  echomsg "OK"
216else
217  " Put the cursor on the line with the error.
218  exe error
219endif
220
221let &wrapscan = s:save_wrapscan
222unlet s:save_wrapscan
223
224endif
225