xref: /vim-8.2.3635/src/po/check.vim (revision fb094e14)
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.
124/^"MIME-Version:
125while 1
126  let lnum = search('^msgid\>')
127  if lnum <= 0
128    break
129  endif
130  let strlnum = search('^msgstr\>')
131  let end = search('^$')
132  if end <= 0
133    let end = line('$') + 1
134  endif
135  let origcount = CountNl(lnum, strlnum - 1)
136  let transcount = CountNl(strlnum, end - 1)
137  " Allow for a few more or less line breaks when there are 2 or more
138  if origcount != transcount && (origcount <= 2 || transcount <= 2)
139    echomsg 'Mismatching "\n" in line ' . line('.')
140    if error == 0
141      let error = lnum
142    endif
143  endif
144endwhile
145
146" Check that the file is well formed according to msgfmts understanding
147if executable("msgfmt")
148  let filename = expand("%")
149  " Newer msgfmt does not take OLD_PO_FILE_INPUT argument, must be in
150  " environment.
151  let $OLD_PO_FILE_INPUT = 'yes'
152  let a = system("msgfmt --statistics " . filename)
153  if v:shell_error != 0
154    let error = matchstr(a, filename.':\zs\d\+\ze:')+0
155    for line in split(a, '\n') | echomsg line | endfor
156  endif
157endif
158
159if error == 0
160  " If all was OK restore the view.
161  call winrestview(wsv)
162  echomsg "OK"
163else
164  exe error
165endif
166
167let &wrapscan = s:save_wrapscan
168unlet s:save_wrapscan
169
170endif
171