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 let a = system("msgfmt --statistics OLD_PO_FILE_INPUT=yes " . filename) 150 if v:shell_error != 0 151 let error = matchstr(a, filename.':\zs\d\+\ze:')+0 152 for line in split(a, '\n') | echomsg line | endfor 153 endif 154endif 155 156if error == 0 157 " If all was OK restore the view. 158 call winrestview(wsv) 159 echomsg "OK" 160else 161 exe error 162endif 163 164let &wrapscan = s:save_wrapscan 165unlet s:save_wrapscan 166 167endif 168