From 0b39c3fd4c5d1c8ebd2efa85fced7df5e17efd3b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 30 Aug 2020 15:52:10 +0200 Subject: [PATCH] patch 8.2.1544: cannot translate messages in a Vim script Problem: Cannot translate messages in a Vim script. Solution: Add gettext(). Try it out for a few messages in the options window. --- Filelist | 2 ++ runtime/doc/eval.txt | 14 +++++++++++ runtime/doc/usr_41.txt | 1 + runtime/optwin.vim | 16 ++++++------- src/evalfunc.c | 22 +++++++++++++++++ src/po/Makefile | 53 ++++++++++++++++++++++++++++++++++++----- src/po/README.txt | 10 +++++++- src/po/fixfilenames.vim | 13 ++++++++++ src/po/tojavascript.vim | 18 ++++++++++++++ src/version.c | 2 ++ 10 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 src/po/fixfilenames.vim create mode 100644 src/po/tojavascript.vim diff --git a/Filelist b/Filelist index 42eaa1d3edd707..cd1325f30e081b 100644 --- a/Filelist +++ b/Filelist @@ -1028,6 +1028,8 @@ LANG_SRC = \ src/po/README_mvc.txt \ src/po/check.vim \ src/po/cleanup.vim \ + src/po/tojavascript.vim \ + src/po/fixfilenames.vim \ src/po/Makefile \ src/po/Make_all.mak \ src/po/Make_cyg.mak \ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e0c84730380ea1..f1b69921964903 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2546,6 +2546,7 @@ gettabvar({nr}, {varname} [, {def}]) gettabwinvar({tabnr}, {winnr}, {name} [, {def}]) any {name} in {winnr} in tab page {tabnr} gettagstack([{nr}]) Dict get the tag stack of window {nr} +gettext({text}) String lookup translation of {text} getwininfo([{winid}]) List list of info about each window getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window getwinposx() Number X coord in pixels of the Vim window @@ -5827,6 +5828,19 @@ gettagstack([{nr}]) *gettagstack()* Can also be used as a |method|: > GetWinnr()->gettagstack() + +gettext({text}) *gettext()* + Translate {text} if possible. + This is mainly for use in the distributed Vim scripts. When + generating message translations the {text} is extracted by + xgettext, the translator can add the translated message in the + .po file and Vim will lookup the translation when gettext() is + called. + For {text} double quoted strings are preferred, because + xgettext does not understand escaping in single quoted + strings. + + getwininfo([{winid}]) *getwininfo()* Returns information about windows as a |List| with Dictionaries. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 10e53baf7233f7..81474c7f42c1f4 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -628,6 +628,7 @@ String manipulation: *string-functions* execute() execute an Ex command and get the output win_execute() like execute() but in a specified window trim() trim characters from a string + gettext() lookup message translation List manipulation: *list-functions* get() get an item without error for wrong index diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 5bb78cdb0a52bf..67601a84227676 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: Bram Moolenaar -" Last Change: 2020 Jun 10 +" Last Change: 2020 aug 30 " If there already is an option window, jump to that one. let buf = bufnr('option-window') @@ -145,13 +145,13 @@ exe $OPTWIN_CMD . ' new option-window' setlocal ts=15 tw=0 noro buftype=nofile " Insert help and a "set" command for each option. -call append(0, '" Each "set" line shows the current value of an option (on the left).') -call append(1, '" Hit on a "set" line to execute it.') -call append(2, '" A boolean option will be toggled.') -call append(3, '" For other options you can edit the value before hitting .') -call append(4, '" Hit on a help line to open a help window on this option.') -call append(5, '" Hit on an index line to jump there.') -call append(6, '" Hit on a "set" line to refresh it.') +call append(0, gettext('" Each "set" line shows the current value of an option (on the left).')) +call append(1, gettext('" Hit on a "set" line to execute it.')) +call append(2, gettext('" A boolean option will be toggled.')) +call append(3, gettext('" For other options you can edit the value before hitting .')) +call append(4, gettext('" Hit on a help line to open a help window on this option.')) +call append(5, gettext('" Hit on an index line to jump there.')) +call append(6, gettext('" Hit on a "set" line to refresh it.')) " These functions are called often below. Keep them fast! diff --git a/src/evalfunc.c b/src/evalfunc.c index 62bbb8888d52d4..fff7a89a9aa856 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -97,6 +97,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv); static void f_getreginfo(typval_T *argvars, typval_T *rettv); static void f_getregtype(typval_T *argvars, typval_T *rettv); static void f_gettagstack(typval_T *argvars, typval_T *rettv); +static void f_gettext(typval_T *argvars, typval_T *rettv); static void f_haslocaldir(typval_T *argvars, typval_T *rettv); static void f_hasmapto(typval_T *argvars, typval_T *rettv); static void f_hlID(typval_T *argvars, typval_T *rettv); @@ -667,6 +668,7 @@ static funcentry_T global_functions[] = {"gettabvar", 2, 3, FEARG_1, ret_any, f_gettabvar}, {"gettabwinvar", 3, 4, FEARG_1, ret_any, f_gettabwinvar}, {"gettagstack", 0, 1, FEARG_1, ret_dict_any, f_gettagstack}, + {"gettext", 1, 1, FEARG_1, ret_string, f_gettext}, {"getwininfo", 0, 1, FEARG_1, ret_list_dict_any, f_getwininfo}, {"getwinpos", 0, 1, FEARG_1, ret_list_number, f_getwinpos}, {"getwinposx", 0, 0, 0, ret_number, f_getwinposx}, @@ -3437,6 +3439,26 @@ f_gettagstack(typval_T *argvars, typval_T *rettv) get_tagstack(wp, rettv->vval.v_dict); } +/* + * "gettext()" function + */ + static void +f_gettext(typval_T *argvars, typval_T *rettv) +{ + if (argvars[0].v_type != VAR_STRING + || argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL) + { + semsg(_(e_invarg2), tv_get_string(&argvars[0])); + } + else + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave( + (char_u *)_(argvars[0].vval.v_string)); + } +} + // for VIM_VERSION_ defines #include "version.h" diff --git a/src/po/Makefile b/src/po/Makefile index cce14a94a3bbb6..8ef93e6eb0fc52 100644 --- a/src/po/Makefile +++ b/src/po/Makefile @@ -36,6 +36,7 @@ all: $(MOFILES) $(MOCONVERTED) $(MSGFMT_DESKTOP) check: $(CHECKFILES) +# installing for real install: $(MOFILES) $(MOCONVERTED) @$(MAKE) prefixcheck for lang in $(LANGUAGES); do \ @@ -61,6 +62,24 @@ uninstall: rm -f $(LOCALEDIR)/$$lang/LC_MESSAGES/$(PACKAGE).mo; \ done +# installing for local tryout into ../../runtime/lang +tryoutinstall: $(MOFILES) $(MOCONVERTED) + @$(MAKE) prefixcheck + for lang in $(LANGUAGES); do \ + dir=../../runtime/lang/$$lang/; \ + if test ! -x "$$dir"; then \ + mkdir $$dir; chmod 755 $$dir; \ + fi; \ + dir=../../runtime/lang/$$lang/LC_MESSAGES; \ + if test ! -x "$$dir"; then \ + mkdir $$dir; chmod 755 $$dir; \ + fi; \ + if test -r $$lang.mo; then \ + cp $$lang.mo $$dir/$(PACKAGE).mo; \ + chmod 644 $$dir/$(PACKAGE).mo; \ + fi; \ + done + converted: $(MOCONVERTED) # nl.po was added later, if it does not exist use a file with just a # in it @@ -158,12 +177,34 @@ distclean: clean checkclean: rm -f *.ck -$(PACKAGE).pot: ../*.c ../if_perl.xs ../GvimExt/gvimext.cpp ../globals.h ../if_py_both.h ../vim.h gvim.desktop.in vim.desktop.in - cd ..; $(XGETTEXT) --default-domain=$(PACKAGE) \ - --add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2 \ - *.c if_perl.xs GvimExt/gvimext.cpp globals.h if_py_both.h vim.h \ - po/gvim.desktop.in po/vim.desktop.in - mv -f ../$(PACKAGE).po $(PACKAGE).pot +PO_INPUTLIST = \ + ../*.c \ + ../if_perl.xs \ + ../GvimExt/gvimext.cpp \ + ../globals.h \ + ../if_py_both.h \ + ../vim.h \ + gvim.desktop.in \ + vim.desktop.in + +PO_VIM_INPUTLIST = \ + ../../runtime/optwin.vim + +PO_VIM_JSLIST = \ + optwin.js + +$(PACKAGE).pot: $(PO_INPUTLIST) $(PO_VIM_INPUTLIST) + # Convert the Vim scripts to (what looks like) Javascript + $(VIM) -u NONE --not-a-term -S tojavascript.vim $(PACKAGE).pot $(PO_VIM_INPUTLIST) + # create vim.pot + $(XGETTEXT) --default-domain=$(PACKAGE) --add-comments \ + --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2 \ + $(PO_INPUTLIST) $(PO_VIM_JSLIST) + mv -f $(PACKAGE).po $(PACKAGE).pot + # Fix Vim scripts names, so that "gf" works + $(VIM) -u NONE --not-a-term -S fixfilenames.vim $(PACKAGE).pot $(PO_VIM_INPUTLIST) + # Delete the temporary files + rm *.js vim.desktop: vim.desktop.in $(POFILES) echo $(LANGUAGES) | tr " " "\n" |sed -e '/\./d' | sort > LINGUAS diff --git a/src/po/README.txt b/src/po/README.txt index e60d1d21683ab5..68d267af17d36f 100644 --- a/src/po/README.txt +++ b/src/po/README.txt @@ -78,7 +78,8 @@ language. (2) Translate See the gettext documentation on how to do this. You can also find - examples in the other po files. + examples in the other po files. You can use "gF" on the file name to see + the context of the message. Search the po file for items that require translation: /fuzzy\|^msgstr ""\(\n"\)\@! @@ -123,6 +124,13 @@ language. Look out for syntax errors and fix them. +(6) Local tryout: + Vim normally picks up the .mo files from: + $VIMRUNTIME/lang/{lang}/LC_MESSAGES/vim.mo + To try out the messages with Vim use: + make tryoutinstall + And run Vim with $VIMRUNTIME set to ../runtime + USING GETTEXT WITHOUT ICONV diff --git a/src/po/fixfilenames.vim b/src/po/fixfilenames.vim new file mode 100644 index 00000000000000..65d448ce419c25 --- /dev/null +++ b/src/po/fixfilenames.vim @@ -0,0 +1,13 @@ +" Invoked with the name "vim.pot" and a list of Vim script names. +" Converts them to a .js file, stripping comments, so that xgettext works. + +set shortmess+=A + +for name in argv()[1:] + let jsname = fnamemodify(name, ":t:r") .. ".js" + exe "%s+" .. jsname .. "+" .. name .. "+" +endfor + +write +last +quit diff --git a/src/po/tojavascript.vim b/src/po/tojavascript.vim new file mode 100644 index 00000000000000..7868570be75b18 --- /dev/null +++ b/src/po/tojavascript.vim @@ -0,0 +1,18 @@ +" Invoked with the name "vim.pot" and a list of Vim script names. +" Converts them to a .js file, stripping comments, so that xgettext works. +" Javascript is used because, like Vim, it accepts both single and double +" quoted strings. + +set shortmess+=A + +for name in argv()[1:] + exe 'edit ' .. fnameescape(name) + + " Strip comments + g/^\s*"/s/.*// + + " Write as .js file, xgettext recognizes them + exe 'w! ' .. fnamemodify(name, ":t:r") .. ".js" +endfor + +quit diff --git a/src/version.c b/src/version.c index 8eaf4844772a8b..1fe5b26dfd232c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1544, /**/ 1543, /**/