1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
|
" Vim ftplugin for PO file (GNU gettext) editing.
" Maintainer: Aleksandar Jelenak <ajelenak AT yahoo.com>
" Last Change: Tue, 12 Apr 2005 13:49:55 -0400
"
" *** Latest version: http://www.vim.org/scripts/script.php?script_id=695 ***
"
" DESCRIPTION
" This file is a Vim ftplugin for editing PO files (GNU gettext -- the GNU
" i18n and l10n system). It automates over a dozen frequent tasks that
" occur while editing files of this type.
"
" Key mappings
" Action (Insert mode) GUI Vim Vim
" ===============================================================
" Move to an untransl. string forward <S-F1> \m
" Move to an untransl. string backward <S-F2> \p
" Copy the msgid string to msgstr <S-F3> \c
" Delete the msgstr string <S-F4> \d
" Move to the next fuzzy translation <S-F5> \f
" Move to the previous fuzzy translation <S-F6> \b
" Label the translation fuzzy <S-F7> \z
" Remove the fuzzy label <S-F8> \r
" Show msgfmt statistics for the file(*) <S-F11> \s
" Browse through msgfmt errors for the file(*) <S-F12> \e
" Put the translator info in the header \t \t
" Put the lang. team info in the header \l \l
" ---------------------------------------------------------------
" (*) Only available on UNIX computers.
"
"
" Key mappings
" Action (Normal mode) GUI Vim Vim
" ===============================================================
" Move to an untransl. string forward <S-F1> \m
" Move to an untransl. string backward <S-F2> \p
" Move to the next fuzzy translation <S-F5> \f
" Move to the previous fuzzy translation <S-F6> \b
" Label the translation fuzzy <S-F7> \z
" Remove the fuzzy label <S-F8> \r
" Split-open the file under cursor gf gf
" Show msgfmt statistics for the file(*) <S-F11> \s
" Browse through msgfmt errors for the file(*) <S-F12> \e
" Put the translator info in the header \t \t
" Put the lang. team info in the header \l \l
" ---------------------------------------------------------------
" (*) Only available on UNIX computers.
"
" Remarks:
" - "S" in the above key mappings stands for the <Shift> key and "\" in
" fact means "<LocalLeader>" (:help <LocalLeader>), which is "\" by
" Vim's default.
" - Information about the translator and language team is supplied by two
" global variables: 'g:po_translator' and 'g:po_lang_team'. They should
" be defined in the ".vimrc" (UNIX) or "_vimrc" (Windows) file. If they
" are not defined, the default values (descriptive strings) are put
" instead.
" - Vim's "gf" Normal mode command is remapped (local to the PO buffer, of
" course). It will only function on lines starting with "#: ". Search
" for the file is performed in the directories specified by the 'path'
" option. The user can supply its own addition to this option via the
" 'g:po_path' global variable. Its default value for PO files can be
" found by typing ":set path?" from within a PO buffer. For the correct
" format please see ":help 'path'". Warning messages are printed if no
" or more than one file is found.
" - Vim's Quickfix mode (see ":help quickfix") is used for browsing
" through msgfmt-reported errors for the file. No MO file is created
" when running the msgfmt program since its output is directed to
" "/dev/null". The user can supply command-line arguments to the msgfmt
" program via the global variable 'g:po_msgfmt_args'. All arguments are
" allowed except the "-o" for output file. The default value is
" "-vv -c".
"
" But there's even more!
"
" Every time the PO file is saved, a PO-formatted time stamp is
" automatically added to the file header.
"
" INSTALLATION
" Put this file in a Vim ftplugin directory. On UNIX computers it is
" usually either "~/.vim/ftplugin" or "~/.vim/after/ftplugin". On Windows
" computers, the defaults are "$VIM\vimfiles\ftplugin" or
" "$VIM\vimfiles\after\ftplugin". For more information consult the Vim
" help, ":help 'ftplugin'" and ":help 'runtimepath'".
"
" REMOVAL
" Just delete the bloody file!
" Only do this when not done yet for this buffer.
if exists("b:did_po_mode_ftplugin") || exists("loaded_po_ftplugin")
finish
endif
let b:did_po_mode_ftplugin = 1
let loaded_po_ftplugin = 1
setlocal comments=
setlocal errorformat=%f:%l:\ %m
setlocal makeprg=msgfmt
let b:po_path = '.,..,../src,../src/*'
if exists("g:po_path")
let b:po_path = b:po_path . ',' . g:po_path
endif
exe "setlocal path=" . b:po_path
unlet b:po_path
" Check if GUI Vim is running.
if has("gui_running")
let gui = 1
else
let gui = 0
endif
" Move to the first untranslated msgstr string forward.
if !hasmapto('<Plug>NextTransFwd')
if gui
imap <buffer> <unique> <S-F1> <Plug>NextTransFwd
nmap <buffer> <unique> <S-F1> <Plug>NextTransFwd
else
imap <buffer> <unique> <LocalLeader>m <Plug>NextTransFwd
nmap <buffer> <unique> <LocalLeader>m <Plug>NextTransFwd
endif
endif
inoremap <buffer> <unique> <Plug>NextTransFwd <ESC>/^msgstr\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextTransFwd /^msgstr\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call histdel("/", -1)<CR><C-L>z.
" Move to the first untranslated msgstr string backward.
if !hasmapto('<Plug>NextTransBwd')
if gui
imap <buffer> <unique> <S-F2> <Plug>NextTransBwd
nmap <buffer> <unique> <S-F2> <Plug>NextTransBwd
else
imap <buffer> <unique> <LocalLeader>p <Plug>NextTransBwd
nmap <buffer> <unique> <LocalLeader>p <Plug>NextTransBwd
endif
endif
inoremap <buffer> <unique> <Plug>NextTransBwd <ESC>{?^msgstr\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextTransBwd {?^msgstr\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call histdel("/", -1)<CR><C-L>z.
" Copy original msgid string into msgstr string.
if !hasmapto('<Plug>CopyMsgid')
if gui
imap <buffer> <unique> <S-F3> <Plug>CopyMsgid
else
imap <buffer> <unique> <LocalLeader>c <Plug>CopyMsgid
endif
endif
inoremap <buffer> <unique> <Plug>CopyMsgid <ESC>}?^msgid<CR>:let @/=""<CR>:call histdel("/", -1)<CR>f"y/^msgstr<CR>/^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR>f""_d$pa
" Erase the translation string.
if !hasmapto('<Plug>DeleteTrans')
if gui
imap <buffer> <unique> <S-F4> <Plug>DeleteTrans
else
imap <buffer> <unique> <LocalLeader>d <Plug>DeleteTrans
endif
endif
inoremap <buffer> <unique> <Plug>DeleteTrans <ESC>}?^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR>f"lc}"<ESC>i
" Move to the first fuzzy translation forward.
if !hasmapto('<Plug>NextFuzzy')
if gui
imap <buffer> <unique> <S-F5> <Plug>NextFuzzy
nmap <buffer> <unique> <S-F5> <Plug>NextFuzzy
else
imap <buffer> <unique> <LocalLeader>f <Plug>NextFuzzy
nmap <buffer> <unique> <LocalLeader>f <Plug>NextFuzzy
endif
endif
inoremap <buffer> <unique> <Plug>NextFuzzy <ESC>/^#,\(.*,\)\=\s*fuzzy<CR>:let @/=""<CR>:call histdel("/", -1)<CR>/^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.$i
nnoremap <buffer> <unique> <Plug>NextFuzzy /^#,\(.*,\)\=\s*fuzzy<CR>:let @/=""<CR>:call histdel("/", -1)<CR>/^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR><C-L>z.$
" Move to the first fuzzy descriptor backward.
if !hasmapto('<Plug>PreviousFuzzy')
if gui
imap <buffer> <unique> <S-F6> <Plug>PreviousFuzzy
nmap <buffer> <unique> <S-F6> <Plug>PreviousFuzzy
else
imap <buffer> <unique> <LocalLeader>b <Plug>PreviousFuzzy
nmap <buffer> <unique> <LocalLeader>b <Plug>PreviousFuzzy
endif
endif
inoremap <buffer> <unique> <Plug>PreviousFuzzy <ESC>{?^#,\(.*,\)\=\s*fuzzy<CR>:let @/=""<CR>:call histdel("/", -1)<CR>/^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.$i
nnoremap <buffer> <unique> <Plug>PreviousFuzzy {?^#,\(.*,\)\=\s*fuzzy<CR>:let @/=""<CR>:call histdel("/", -1)<CR>/^msgstr<CR>:let @/=""<CR>:call histdel("/", -1)<CR><C-L>z.$
" Insert fuzzy description for the translation.
if !hasmapto('<Plug>InsertFuzzy')
if gui
imap <buffer> <unique> <S-F7> <Plug>InsertFuzzy
nmap <buffer> <unique> <S-F7> <Plug>InsertFuzzy
else
imap <buffer> <unique> <LocalLeader>z <Plug>InsertFuzzy
nmap <buffer> <unique> <LocalLeader>z <Plug>InsertFuzzy
endif
endif
inoremap <buffer> <unique> <Plug>InsertFuzzy <ESC>{vap:call <SID>InsertFuzzy()<CR>gv<ESC>}i
nnoremap <buffer> <unique> <Plug>InsertFuzzy {vap:call <SID>InsertFuzzy()<CR>gv<ESC>}
fu! <SID>InsertFuzzy() range
let n = a:firstline
while n <= a:lastline
let line = getline(n)
if line =~ '^#,.*fuzzy'
return
elseif line =~ '^#,'
call setline(n, substitute(line, '#,','#, fuzzy,', ""))
return
elseif line =~ '^msgid'
call append(n-1, '#, fuzzy')
return
endif
let n = n + 1
endwhile
endf
" Remove fuzzy description from the translation.
if !hasmapto('<Plug>RemoveFuzzy')
if gui
imap <buffer> <unique> <S-F8> <Plug>RemoveFuzzy
nmap <buffer> <unique> <S-F8> <Plug>RemoveFuzzy
else
imap <buffer> <unique> <LocalLeader>r <Plug>RemoveFuzzy
nmap <buffer> <unique> <LocalLeader>r <Plug>RemoveFuzzy
endif
endif
inoremap <buffer> <unique> <Plug>RemoveFuzzy <ESC>{vap:call <SID>RemoveFuzzy()<CR>i
nnoremap <buffer> <unique> <Plug>RemoveFuzzy {vap:call <SID>RemoveFuzzy()<CR>
fu! <SID>RemoveFuzzy()
let line = getline(".")
if line =~ '^#,\s*fuzzy$'
exe "normal! dd"
elseif line =~ '^#,\(.*,\)\=\s*fuzzy'
exe 's/,\s*fuzzy//'
endif
endf
" Show PO translation statistics. (Only available on UNIX computers for now.)
if has("unix")
if !hasmapto('<Plug>MsgfmtStats')
if gui
imap <buffer> <unique> <S-F11> <Plug>MsgfmtStats
nmap <buffer> <unique> <S-F11> <Plug>MsgfmtStats
else
imap <buffer> <unique> <LocalLeader>s <Plug>MsgfmtStats
nmap <buffer> <unique> <LocalLeader>s <Plug>MsgfmtStats
endif
endif
inoremap <buffer> <unique> <Plug>MsgfmtStats <ESC>:call <SID>Msgfmt('stats')<CR>
nnoremap <buffer> <unique> <Plug>MsgfmtStats :call <SID>Msgfmt('stats')<CR>
if !hasmapto('<Plug>MsgfmtTest')
if gui
imap <buffer> <unique> <S-F12> <Plug>MsgfmtTest
nmap <buffer> <unique> <S-F12> <Plug>MsgfmtTest
else
imap <buffer> <unique> <LocalLeader>e <Plug>MsgfmtTest
nmap <buffer> <unique> <LocalLeader>e <Plug>MsgfmtTest
endif
endif
inoremap <buffer> <unique> <Plug>MsgfmtTest <ESC>:call <SID>Msgfmt('test')<CR>
nnoremap <buffer> <unique> <Plug>MsgfmtTest :call <SID>Msgfmt('test')<CR>
fu! <SID>Msgfmt(action)
" Check if the file needs to be saved first.
exe "if &modified | w | endif"
if a:action == 'stats'
exe "!msgfmt --statistics -o /dev/null %"
elseif a:action == 'test'
if exists("g:po_msgfmt_args")
let args = g:po_msgfmt_args
else
let args = '-vv -c'
endif
exe "make! " . args . " -o /dev/null %"
copen
endif
endf
endif
" Add translator info in the file header.
if !hasmapto('<Plug>TranslatorInfo')
if gui
imap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
nmap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
else
imap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
nmap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
endif
endif
inoremap <buffer> <unique> <Plug>TranslatorInfo <ESC>:call <SID>AddHeaderInfo('person')<CR>i
nnoremap <buffer> <unique> <Plug>TranslatorInfo :call <SID>AddHeaderInfo('person')<CR>
" Add language team info in the file header.
if !hasmapto('<Plug>LangTeamInfo')
if gui
imap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
nmap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
else
imap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
nmap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
endif
endif
inoremap <buffer> <unique> <Plug>LangTeamInfo <ESC>:call <SID>AddHeaderInfo('team')<CR>i
nnoremap <buffer> <unique> <Plug>LangTeamInfo :call <SID>AddHeaderInfo('team')<CR>
fu! <SID>AddHeaderInfo(action)
if a:action == 'person'
let search_for = 'Last-Translator'
if exists("g:po_translator")
let add = g:po_translator
else
let add = 'YOUR NAME <E-MAIL@ADDRESS>'
endif
elseif a:action == 'team'
let search_for = 'Language-Team'
if exists("g:po_lang_team")
let add = g:po_lang_team
else
let add = 'LANGUAGE TEAM <E-MAIL@ADDRESS or HOME PAGE>'
endif
else
" Undefined action -- just do nothing.
return
endif
let search_for = '"' . search_for . ':'
let add = add . '\\n"'
normal! 1G
if search('^' . search_for)
silent! exe 's/^\(' . search_for . '\).*$/\1 ' . add
endif
call histdel("/", -1)
endf
" Write automagically PO-formatted time stamp every time the file is saved.
augroup PoFileTimestamp
au!
au BufWrite *.po,*.po.gz call <SID>PoFileTimestamp()
augroup END
fu! <SID>PoFileTimestamp()
" Prepare for cleanup at the end of this function.
let hist_search = histnr("/")
let old_report = 'set report='.&report
let &report = 100
let cursor_pos_cmd = line(".").'normal! '.virtcol(".").'|'
normal! H
let scrn_pos = line(".").'normal! zt'
" Put in time stamp.
normal! 1G
if search('^"PO-Revision-Date:')
silent! exe 's/^\("PO-Revision-Date:\).*$/\1 ' . strftime("%Y-%m-%d %H:%M%z") . '\\n"'
endif
" Cleanup and restore old cursor position.
while histnr("/") > hist_search && histnr("/") > 0
call histdel("/", -1)
endwhile
exe scrn_pos
exe cursor_pos_cmd
exe old_report
endf
" On "gf" Normal mode command, split window and open the file under the
" cursor.
if !hasmapto('<Plug>OpenSourceFile')
map <buffer> <unique> gf <Plug>OpenSourceFile
endif
noremap <buffer> <unique> <Plug>OpenSourceFile :call <SID>OpenSourceFile()<CR>
" This opens the file under the cursor in a split-window.
fu! <SID>OpenSourceFile()
" Check if we're at the right line. Return if not.
if getline(".") !~ '^#:\s\+' | return | endif
" Get the reference, check it, and return if it doesn't have the assumed
" format.
let ref = expand("<cWORD>")
if ref !~ ':\d\+$' | return | endif
" Split the reference into the file name and the line number parts.
let d = match(ref, ':')
let flnm = strpart(ref, 0, d)
let lnr = strpart(ref, d+1, 100)
" Start searching for the file in the directories specified with the 'path'
" option.
let ff = globpath(&path, flnm)
" Check what's been found. Report if no or more than one file found and
" return.
if ff == ''
echohl WarningMsg | echo "No file found in the path."
echohl None
exe "normal \<Esc>"
elseif match(ff, "\n") > 0
echohl WarningMsg | echo "More than one file found: " . ff . "\nAborting."
echohl None
exe "normal \<Esc>"
else
" Split the window and open the file at the correct line.
execute "silent sp +" . lnr . " " . ff
endif
endf
unlet gui
|