Package: vim-scripts
Version: 20091011
Severity: wishlist
Tags: patch

Please find attached another version [1] of po.vim, based on the original
but improved with new functions, bugfixes, and a
much more improved keymapping.

As a heavy vim+po.vim user, I find that the improved ftplugin does make
a big difference in terms of ease of use.  Please consider including it.

[1] http://www.vim.org/scripts/script.php?script_id=2530

Best regards,
-- 
Omar Campagne Polaino

vim-doc-es: <http://www.assembla.com/wiki/show/vim-doc-es>
" Vim ftplugin for PO file (GNU gettext) editing.
" This script is a fork version of Aleksandar Jelenak's script. Since the 
" last change by the original creator is long time ago and I failed to get in
" tough with him via email, I started this project by myself. Nearly 90% of
" the script is written by Aleksandar Jelenak. I fixed two bugs and add three
" other functions and remapped the keys. Thanks to Aleksandar Jelenak a lot.
" Original Creator:     Aleksandar Jelenak <ajelenak AT yahoo.com>
" Modifier: Grissiom <chaos.proton AT gmail.com>
" Last Change by Original Creator:      Tue, 12 Apr 2005 13:49:55 -0400
"
" *** Latest version of original script: 
http://www.vim.org/scripts/script.php?script_id=695 ***
" *** Latest version of modified verion:
" http://repo.or.cz/w/grissiom.projects.git?a=blob_plain;f=vim/ftplugin/po.vim 
***
"
" 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.
"
"                                                    
"     Action (Insert mode)                           Key mappings
"     =======================================================
"     Move to a string (transl. or untransl) forward      \m
"     Move to a string (transl. or untransl) backward     \M
"     Begain to comment this entry                        \C
"     Move to an untransl. string forward                 \u
"     Move to an untransl. string backward                \U
"     Copy the msgid string to msgstr                     \c
"     Delete the msgstr string                            \d
"     Move to the next fuzzy translation                  \f
"     Move to the previous fuzzy translation              \F
"     Label the translation fuzzy                         \z
"     Remove the fuzzy label                              \Z
"     Show msgfmt statistics for the file(*)              \s
"     Browse through msgfmt errors for the file(*)        \e
"     Put the translator info in the header               \t
"     Put the lang. team info in the header               \l
"     Format the whole file(wrap the lines etc.)          \W
"     -------------------------------------------------------
"     (*) Only available on UNIX computers.
"
"
"                                                      
"     Action (Normal mode)                           Key mappings
"     =======================================================
"     Move to a string (transl. or untransl) forward      \m
"     Move to a string (transl. or untransl) backward     \M
"     Begain to comment this entry                        \C
"     Move to an untransl. string forward                 \u
"     Move to an untransl. string backward                \U
"     Move to the next fuzzy translation                  \f
"     Move to the previous fuzzy translation              \F
"     Label the translation fuzzy                         \z
"     Remove the fuzzy label                              \Z
"     Split-open the file under cursor                    gf
"     Show msgfmt statistics for the file(*)              \s
"     Browse through msgfmt errors for the file(*)        \e
"     Put the translator info in the header               \t
"     Put the lang. team info in the header               \l
"     Format the whole file(wrap the lines etc.)          \W
"     -------------------------------------------------------
"     (*) 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".
"     - Format the whole file is actually using msgmerge a.po a.po -o a.po.
"       Since we must reload the file that has been modified, you could not
"       do any Undo once you run this function.
"
"     But there's even more!
"
"     Every time the PO file is saved, a PO-formatted time stamp is
"     automatically added to the file header. Note that if the file is marked
"     'fuzzy', i.e, there is a "#, fuzzy" in the head of the file, the time
"     stamp will not be updated.
"
" 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")
        finish
endif
let b:did_po_mode_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

" Move to a string (transl. or untransl) forward
if !hasmapto('<Plug>NextStringFWD')
        imap <buffer> <unique> <LocalLeader>m <Plug>NextStringFWD
        nmap <buffer> <unique> <LocalLeader>m <Plug>NextStringFWD
endif
inoremap <buffer> <unique> <Plug>NextStringFWD 
<ESC>/^msgstr\(\[\d\]\)\=<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextStringFWD /^msgstr\(\[\d\]\)\=<CR>:let 
@/=""<CR>:call histdel("/", -1)<CR><C-L>z.

" Move to a string (transl. or untransl) backward
if !hasmapto('<Plug>NextStringBWD')
        imap <buffer> <unique> <LocalLeader>M <Plug>NextStringBWD
        nmap <buffer> <unique> <LocalLeader>M <Plug>NextStringBWD
endif
inoremap <buffer> <unique> <Plug>NextStringBWD 
<ESC>{{/^msgstr\(\[\d\]\)\=<CR>:let @/=""<CR>:call histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextStringBWD {{/^msgstr\(\[\d\]\)\=<CR>:let 
@/=""<CR>:call histdel("/", -1)<CR><C-L>z.

" Begain to comment this entry
if !hasmapto('<Plug>Commententry')
        imap <buffer> <unique> <LocalLeader>C <Plug>Commententry
        nmap <buffer> <unique> <LocalLeader>C <Plug>Commententry
endif
inoremap <buffer> <unique> <Plug>Commententry <ESC>{o#<Space>
nnoremap <buffer> <unique> <Plug>Commententry {o#<Space>

" Move to the first untranslated msgstr string forward.
if !hasmapto('<Plug>NextTransFwd')
        imap <buffer> <unique> <LocalLeader>u <Plug>NextTransFwd
        nmap <buffer> <unique> <LocalLeader>u <Plug>NextTransFwd
endif
inoremap <buffer> <unique> <Plug>NextTransFwd 
<ESC>/^msgstr\(\[\d\]\)\=\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call 
histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextTransFwd 
/^msgstr\(\[\d\]\)\=\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call histdel("/", 
-1)<CR><C-L>z.f"

" Move to the first untranslated msgstr string backward.
if !hasmapto('<Plug>NextTransBwd')
        imap <buffer> <unique> <LocalLeader>U <Plug>NextTransBwd
        nmap <buffer> <unique> <LocalLeader>U <Plug>NextTransBwd
endif
" If the cursor is in a empty entry, it will not move. So move a step first.
fu! <SID>crline()
        if getline('.') =~ '^msgstr\s*""$'
                normal! {
        endif
endf
inoremap <buffer> <unique> <Plug>NextTransBwd <ESC>:call 
<SID>crline()<CR>?^msgstr\(\[\d\]\)\=\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call 
histdel("/", -1)<CR>z.f"a
nnoremap <buffer> <unique> <Plug>NextTransBwd :call 
<SID>crline()<CR>?^msgstr\(\[\d\]\)\=\s*""\(\n\n\\|\%$\)<CR>:let @/=""<CR>:call 
histdel("/", -1)<CR><C-L>z.f"

" Copy original msgid string into msgstr string.
if !hasmapto('<Plug>CopyMsgid')
        imap <buffer> <unique> <LocalLeader>c <Plug>CopyMsgid
        nmap <buffer> <unique> <LocalLeader>c <Plug>CopyMsgid
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
nnoremap <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$p

" Erase the translation string.
if !hasmapto('<Plug>DeleteTrans')
        imap <buffer> <unique> <LocalLeader>d <Plug>DeleteTrans
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')
        imap <buffer> <unique> <LocalLeader>f <Plug>NextFuzzy
        nmap <buffer> <unique> <LocalLeader>f <Plug>NextFuzzy
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')
        imap <buffer> <unique> <LocalLeader>F <Plug>PreviousFuzzy
        nmap <buffer> <unique> <LocalLeader>F <Plug>PreviousFuzzy
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')
        imap <buffer> <unique> <LocalLeader>z <Plug>InsertFuzzy
        nmap <buffer> <unique> <LocalLeader>z <Plug>InsertFuzzy
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
        let n = n + 1
        while n <= a:lastline
                let line = getline(n)
                if line =~ '^#,.*fuzzy'
                        return
                elseif line =~ '^#,'
                        call setline(n, substitute(line, '#,','#, fuzzy,', ""))
                        return
                elseif line !~ '^#'
                        call append(n-1, '#, fuzzy')
                        return
                endif
                let n = n + 1
        endwhile
endf

" Remove fuzzy description from the translation.
if !hasmapto('<Plug>RemoveFuzzy')
        imap <buffer> <unique> <LocalLeader>Z <Plug>RemoveFuzzy
        nmap <buffer> <unique> <LocalLeader>Z <Plug>RemoveFuzzy
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')
                imap <buffer> <unique> <LocalLeader>s <Plug>MsgfmtStats
                nmap <buffer> <unique> <LocalLeader>s <Plug>MsgfmtStats
        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')
                imap <buffer> <unique> <LocalLeader>e <Plug>MsgfmtTest
                nmap <buffer> <unique> <LocalLeader>e <Plug>MsgfmtTest
        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')
        imap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
        nmap <buffer> <unique> <LocalLeader>t <Plug>TranslatorInfo
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')
        imap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
        nmap <buffer> <unique> <LocalLeader>l <Plug>LangTeamInfo
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-m...@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-m...@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.
        " only update time stamp for unfuzzy translations
        normal! 1G
        /^msgid
        if (!search('^#, fuzzy$', 'b')) && 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

" Format the whole file with msgmerge(merge itself with itself)
" note after fomating the whole file you may lost your position.
if !hasmapto('fmt_whole_file')
        imap <buffer> <unique> <LocalLeader>W <Plug>fmt_whole_file
        nmap <buffer> <unique> <LocalLeader>W <Plug>fmt_whole_file
endif
inoremap <buffer> <unique> <Plug>fmt_whole_file <ESC>:call 
<SID>fmt_whole_file()<CR>a
nnoremap <buffer> <unique> <Plug>fmt_whole_file :call 
<SID>fmt_whole_file()<CR><CR>

fu! <SID>fmt_whole_file()
        let fn=bufname('%')
        " Check if the file needs to be saved first.
        exe "if &modified | w | endif"
        exe "!msgmerge ".fn." ".fn." "."-o ".fn
        exe "e!"

        " it seems that this method could change both the file in the disk and
        " the buffer. So it may not be so safe as the farmer method.
        "exe "!msgmerge % % -o %"
endf

Reply via email to