patch 9.1.1064: not possible to use plural forms with gettext()
Problem: not possible to use plural forms with gettext() Solution: implement ngettext() Vim script function (Christ van Willegen) closes: #16561 Signed-off-by: Christ van Willegen <cvwillegen@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
b32800f7c5
commit
c078675ac7
2
Filelist
2
Filelist
@ -231,6 +231,8 @@ SRC_ALL = \
|
|||||||
src/testdir/silent.wav \
|
src/testdir/silent.wav \
|
||||||
src/testdir/popupbounce.vim \
|
src/testdir/popupbounce.vim \
|
||||||
src/testdir/crash/* \
|
src/testdir/crash/* \
|
||||||
|
src/testdir/ru_RU/LC_MESSAGES/Makefile \
|
||||||
|
src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.po \
|
||||||
src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.mo \
|
src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.mo \
|
||||||
src/proto.h \
|
src/proto.h \
|
||||||
src/protodef.h \
|
src/protodef.h \
|
||||||
|
@ -416,6 +416,8 @@ mkdir({name} [, {flags} [, {prot}]])
|
|||||||
mode([{expr}]) String current editing mode
|
mode([{expr}]) String current editing mode
|
||||||
mzeval({expr}) any evaluate |MzScheme| expression
|
mzeval({expr}) any evaluate |MzScheme| expression
|
||||||
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
|
nextnonblank({lnum}) Number line nr of non-blank line >= {lnum}
|
||||||
|
ngettext({single}, {plural}, {number}[, {domain}])
|
||||||
|
String translate text based on {number}
|
||||||
nr2char({expr} [, {utf8}]) String single char with ASCII/UTF-8 value {expr}
|
nr2char({expr} [, {utf8}]) String single char with ASCII/UTF-8 value {expr}
|
||||||
or({expr}, {expr}) Number bitwise OR
|
or({expr}, {expr}) Number bitwise OR
|
||||||
pathshorten({expr} [, {len}]) String shorten directory names in a path
|
pathshorten({expr} [, {len}]) String shorten directory names in a path
|
||||||
@ -7687,6 +7689,20 @@ nextnonblank({lnum}) *nextnonblank()*
|
|||||||
Return type: |Number|
|
Return type: |Number|
|
||||||
|
|
||||||
|
|
||||||
|
ngettext({single}, {plural}, {number}[, {domain}) *ngettext()*
|
||||||
|
Return a string that contains the correct value for a
|
||||||
|
message based on the rules for plural form(s) in
|
||||||
|
a language. Examples: >
|
||||||
|
ngettext("File", "Files", 2) # returns "Files"
|
||||||
|
<
|
||||||
|
Can be used as a |method|: >
|
||||||
|
1->ngettext("File", "Files") # returns "File"
|
||||||
|
<
|
||||||
|
See |gettext()| for information on the domain parameter.
|
||||||
|
|
||||||
|
Return type: |String|
|
||||||
|
|
||||||
|
|
||||||
nr2char({expr} [, {utf8}]) *nr2char()*
|
nr2char({expr} [, {utf8}]) *nr2char()*
|
||||||
Return a string with a single character, which has the number
|
Return a string with a single character, which has the number
|
||||||
value {expr}. Examples: >
|
value {expr}. Examples: >
|
||||||
|
@ -9260,6 +9260,7 @@ new-vimscript-8.2 version8.txt /*new-vimscript-8.2*
|
|||||||
new-virtedit version6.txt /*new-virtedit*
|
new-virtedit version6.txt /*new-virtedit*
|
||||||
news intro.txt /*news*
|
news intro.txt /*news*
|
||||||
nextnonblank() builtin.txt /*nextnonblank()*
|
nextnonblank() builtin.txt /*nextnonblank()*
|
||||||
|
ngettext() builtin.txt /*ngettext()*
|
||||||
no-eval-feature eval.txt /*no-eval-feature*
|
no-eval-feature eval.txt /*no-eval-feature*
|
||||||
no-type-checking eval.txt /*no-type-checking*
|
no-type-checking eval.txt /*no-type-checking*
|
||||||
no_buffers_menu gui.txt /*no_buffers_menu*
|
no_buffers_menu gui.txt /*no_buffers_menu*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
*usr_41.txt* For Vim version 9.1. Last change: 2025 Jan 16
|
*usr_41.txt* For Vim version 9.1. Last change: 2025 Feb 01
|
||||||
|
|
||||||
VIM USER MANUAL - by Bram Moolenaar
|
VIM USER MANUAL - by Bram Moolenaar
|
||||||
|
|
||||||
@ -801,6 +801,7 @@ String manipulation: *string-functions*
|
|||||||
trim() trim characters from a string
|
trim() trim characters from a string
|
||||||
bindtextdomain() set message lookup translation base path
|
bindtextdomain() set message lookup translation base path
|
||||||
gettext() lookup message translation
|
gettext() lookup message translation
|
||||||
|
ngettext() lookup single/plural message translation
|
||||||
str2blob() convert a list of strings into a blob
|
str2blob() convert a list of strings into a blob
|
||||||
blob2str() convert a blob into a list of strings
|
blob2str() convert a blob into a list of strings
|
||||||
|
|
||||||
|
@ -41660,6 +41660,7 @@ Functions: ~
|
|||||||
Channel or Blob variable
|
Channel or Blob variable
|
||||||
|matchbufline()| all the matches of a pattern in a buffer
|
|matchbufline()| all the matches of a pattern in a buffer
|
||||||
|matchstrlist()| all the matches of a pattern in a List of strings
|
|matchstrlist()| all the matches of a pattern in a List of strings
|
||||||
|
|ngettext()| lookup single/plural message translation
|
||||||
|popup_setbuf()| switch to a different buffer in a popup
|
|popup_setbuf()| switch to a different buffer in a popup
|
||||||
|str2blob()| convert a List of strings into a blob
|
|str2blob()| convert a List of strings into a blob
|
||||||
|
|
||||||
|
24
src/auto/configure
vendored
24
src/auto/configure
vendored
@ -15942,6 +15942,30 @@ then :
|
|||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_DGETTEXT 1" >>confdefs.h
|
printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_DGETTEXT 1" >>confdefs.h
|
||||||
|
|
||||||
|
else $as_nop
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dngettext" >&5
|
||||||
|
printf %s "checking for dngettext... " >&6; }
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||||
|
printf "%s\n" "no" >&6; }
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <libintl.h>
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
dngettext("DOMAIN", "Test single", "Test plural", 1);
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_link "$LINENO"
|
||||||
|
then :
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
|
printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_DNGETTEXT 1" >>confdefs.h
|
||||||
|
|
||||||
else $as_nop
|
else $as_nop
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||||
printf "%s\n" "no" >&6; }
|
printf "%s\n" "no" >&6; }
|
||||||
|
@ -430,6 +430,9 @@
|
|||||||
/* Define if there is a working dgettext(). */
|
/* Define if there is a working dgettext(). */
|
||||||
#undef HAVE_DGETTEXT
|
#undef HAVE_DGETTEXT
|
||||||
|
|
||||||
|
/* Define if there is a working dngettext(). */
|
||||||
|
#undef HAVE_DNGETTEXT
|
||||||
|
|
||||||
/* Define if _nl_msg_cat_cntr is present. */
|
/* Define if _nl_msg_cat_cntr is present. */
|
||||||
#undef HAVE_NL_MSG_CAT_CNTR
|
#undef HAVE_NL_MSG_CAT_CNTR
|
||||||
|
|
||||||
|
@ -4520,6 +4520,12 @@ if test "$enable_nls" = "yes"; then
|
|||||||
[#include <libintl.h>],
|
[#include <libintl.h>],
|
||||||
[dgettext("Test", "Test");])],
|
[dgettext("Test", "Test");])],
|
||||||
AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_DGETTEXT),
|
AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_DGETTEXT),
|
||||||
|
AC_MSG_CHECKING([for dngettext])
|
||||||
|
AC_MSG_RESULT([no]))
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <libintl.h>],
|
||||||
|
[dngettext("DOMAIN", "Test single", "Test plural", 1);])],
|
||||||
|
AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_DNGETTEXT),
|
||||||
AC_MSG_RESULT([no]))
|
AC_MSG_RESULT([no]))
|
||||||
dnl _nl_msg_cat_cntr is required for GNU gettext
|
dnl _nl_msg_cat_cntr is required for GNU gettext
|
||||||
AC_MSG_CHECKING([for _nl_msg_cat_cntr])
|
AC_MSG_CHECKING([for _nl_msg_cat_cntr])
|
||||||
|
@ -119,6 +119,7 @@ static void f_min(typval_T *argvars, typval_T *rettv);
|
|||||||
static void f_mzeval(typval_T *argvars, typval_T *rettv);
|
static void f_mzeval(typval_T *argvars, typval_T *rettv);
|
||||||
#endif
|
#endif
|
||||||
static void f_nextnonblank(typval_T *argvars, typval_T *rettv);
|
static void f_nextnonblank(typval_T *argvars, typval_T *rettv);
|
||||||
|
static void f_ngettext(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_nr2char(typval_T *argvars, typval_T *rettv);
|
static void f_nr2char(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_or(typval_T *argvars, typval_T *rettv);
|
static void f_or(typval_T *argvars, typval_T *rettv);
|
||||||
#ifdef FEAT_PERL
|
#ifdef FEAT_PERL
|
||||||
@ -2402,6 +2403,8 @@ static funcentry_T global_functions[] =
|
|||||||
},
|
},
|
||||||
{"nextnonblank", 1, 1, FEARG_1, arg1_lnum,
|
{"nextnonblank", 1, 1, FEARG_1, arg1_lnum,
|
||||||
ret_number, f_nextnonblank},
|
ret_number, f_nextnonblank},
|
||||||
|
{"ngettext", 3, 4, FEARG_3, arg4_string_string_number_string,
|
||||||
|
ret_string, f_ngettext},
|
||||||
{"nr2char", 1, 2, FEARG_1, arg2_number_bool,
|
{"nr2char", 1, 2, FEARG_1, arg2_number_bool,
|
||||||
ret_string, f_nr2char},
|
ret_string, f_nr2char},
|
||||||
{"or", 2, 2, FEARG_1, arg2_number,
|
{"or", 2, 2, FEARG_1, arg2_number,
|
||||||
@ -9358,6 +9361,51 @@ f_nextnonblank(typval_T *argvars, typval_T *rettv)
|
|||||||
rettv->vval.v_number = lnum;
|
rettv->vval.v_number = lnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "ngettext()" function
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
f_ngettext(typval_T *argvars, typval_T *rettv)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
||||||
|
char *prev = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (check_for_nonempty_string_arg(argvars, 0) == FAIL
|
||||||
|
|| check_for_nonempty_string_arg(argvars, 1) == FAIL
|
||||||
|
|| check_for_number_arg(argvars, 2) == FAIL
|
||||||
|
|| check_for_opt_string_arg(argvars, 3) == FAIL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rettv->v_type = VAR_STRING;
|
||||||
|
|
||||||
|
if (argvars[3].v_type == VAR_STRING &&
|
||||||
|
argvars[3].vval.v_string != NULL &&
|
||||||
|
*(argvars[3].vval.v_string) != NUL)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
||||||
|
prev = bind_textdomain_codeset((const char *)argvars[3].vval.v_string, (char *)p_enc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_DNGETTEXT)
|
||||||
|
rettv->vval.v_string = vim_strsave((char_u *)dngettext((const char *)argvars[3].vval.v_string, (const char *)argvars[0].vval.v_string, (const char *)argvars[1].vval.v_string, (int)argvars[2].vval.v_number));
|
||||||
|
#else
|
||||||
|
textdomain((const char *)argvars[3].vval.v_string);
|
||||||
|
rettv->vval.v_string = vim_strsave((char_u *)NGETTEXT((const char *)argvars[0].vval.v_string, (const char *)argvars[1].vval.v_string, argvars[2].vval.v_number));
|
||||||
|
textdomain(VIMPACKAGE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
|
||||||
|
if (prev != NULL)
|
||||||
|
bind_textdomain_codeset((const char *)argvars[3].vval.v_string, prev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rettv->vval.v_string = vim_strsave((char_u *)NGETTEXT((const char *)argvars[0].vval.v_string, (const char *)argvars[1].vval.v_string, argvars[2].vval.v_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "nr2char()" function
|
* "nr2char()" function
|
||||||
*/
|
*/
|
||||||
|
5
src/testdir/ru_RU/LC_MESSAGES/Makefile
Normal file
5
src/testdir/ru_RU/LC_MESSAGES/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
all: __PACKAGE__.mo
|
||||||
|
|
||||||
|
__PACKAGE__.mo: __PACKAGE__.po
|
||||||
|
# Create __PACKAGE__.mo.
|
||||||
|
OLD_PO_FILE_INPUT=yes msgfmt -o $@ $<
|
Binary file not shown.
33
src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.po
Normal file
33
src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.po
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-01-04 12:34+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||||
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
|
||||||
|
# #Restorer: выводится при анализе (профилировании) программы, функции и т. п.
|
||||||
|
# ~!: earlier
|
||||||
|
msgid "ERROR: "
|
||||||
|
msgstr "ОШИБКА: for __PACKAGE__"
|
||||||
|
|
||||||
|
# :!~ Restorer
|
||||||
|
#, c-format
|
||||||
|
msgid "%d buffer unloaded"
|
||||||
|
msgid_plural "%d buffers unloaded"
|
||||||
|
msgstr[0] "%d буфер удалён из памяти for __PACKAGE__"
|
||||||
|
msgstr[1] "%d буфера удалено из памяти for __PACKAGE__"
|
||||||
|
msgstr[2] "%d буферов удалено из памяти for __PACKAGE__"
|
@ -13,6 +13,9 @@ func Test_gettext()
|
|||||||
call assert_equal('xxxTESTxxx', gettext("xxxTESTxxx", "vim"))
|
call assert_equal('xxxTESTxxx', gettext("xxxTESTxxx", "vim"))
|
||||||
call assert_equal('xxxTESTxxx', gettext("xxxTESTxxx", "__PACKAGE__"))
|
call assert_equal('xxxTESTxxx', gettext("xxxTESTxxx", "__PACKAGE__"))
|
||||||
call assert_equal('ERROR: ', gettext("ERROR: ", "__PACKAGE__"))
|
call assert_equal('ERROR: ', gettext("ERROR: ", "__PACKAGE__"))
|
||||||
|
|
||||||
|
call assert_equal('ERROR: ', ngettext("ERROR: ", "ERROR: ", 1, "__PACKAGE__"))
|
||||||
|
call assert_equal('ERRORS: ', ngettext("ERROR: ", "ERRORS: ", 2, "__PACKAGE__"))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -14,7 +14,7 @@ func Test_gettext()
|
|||||||
|
|
||||||
try
|
try
|
||||||
language messages ru_RU
|
language messages ru_RU
|
||||||
call assert_equal('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: ', gettext("ERROR: ", "__PACKAGE__"))
|
call assert_equal('<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: for __PACKAGE__', gettext("ERROR: ", "__PACKAGE__"))
|
||||||
catch /^Vim\%((\a\+)\)\=:E197:/
|
catch /^Vim\%((\a\+)\)\=:E197:/
|
||||||
throw "Skipped: not possible to set locale to ru (missing?)"
|
throw "Skipped: not possible to set locale to ru (missing?)"
|
||||||
endtry
|
endtry
|
||||||
|
@ -14,7 +14,14 @@ func Test_gettext()
|
|||||||
|
|
||||||
try
|
try
|
||||||
language messages ru_RU
|
language messages ru_RU
|
||||||
call assert_equal('ОШИБКА: ', gettext("ERROR: ", "__PACKAGE__"))
|
call assert_equal('ОШИБКА: for __PACKAGE__', gettext("ERROR: ", "__PACKAGE__"))
|
||||||
|
|
||||||
|
call assert_equal('ОШИБКА: for __PACKAGE__', ngettext("ERROR: ", "ERRORS: ", 1, "__PACKAGE__"))
|
||||||
|
call assert_equal('ОШИБКА: for __PACKAGE__', ngettext("ERROR: ", "ERRORS: ", 2, "__PACKAGE__"))
|
||||||
|
|
||||||
|
call assert_equal('%d буфер удалён из памяти for __PACKAGE__', ngettext("%d buffer unloaded", "%d buffers unloaded", 1, "__PACKAGE__"))
|
||||||
|
call assert_equal('%d буфера удалено из памяти for __PACKAGE__', ngettext("%d buffer unloaded", "%d buffers unloaded", 2, "__PACKAGE__"))
|
||||||
|
call assert_equal('%d буферов удалено из памяти for __PACKAGE__', ngettext("%d buffer unloaded", "%d buffers unloaded", 5, "__PACKAGE__"))
|
||||||
catch /^Vim\%((\a\+)\)\=:E197:/
|
catch /^Vim\%((\a\+)\)\=:E197:/
|
||||||
throw "Skipped: not possible to set locale to ru (missing?)"
|
throw "Skipped: not possible to set locale to ru (missing?)"
|
||||||
endtry
|
endtry
|
||||||
@ -22,6 +29,13 @@ func Test_gettext()
|
|||||||
try
|
try
|
||||||
language messages en_GB.UTF-8
|
language messages en_GB.UTF-8
|
||||||
call assert_equal('ERROR: ', gettext("ERROR: ", "__PACKAGE__"))
|
call assert_equal('ERROR: ', gettext("ERROR: ", "__PACKAGE__"))
|
||||||
|
|
||||||
|
call assert_equal('ERROR: ', ngettext("ERROR: ", "ERRORS: ", 1, "__PACKAGE__"))
|
||||||
|
call assert_equal('ERRORS: ', ngettext("ERROR: ", "ERRORS: ", 2, "__PACKAGE__"))
|
||||||
|
|
||||||
|
call assert_equal('%d buffer unloaded', ngettext("%d buffer unloaded", "%d buffers unloaded", 1, "__PACKAGE__"))
|
||||||
|
call assert_equal('%d buffers unloaded', ngettext("%d buffer unloaded", "%d buffers unloaded", 2, "__PACKAGE__"))
|
||||||
|
call assert_equal('%d buffers unloaded', ngettext("%d buffer unloaded", "%d buffers unloaded", 5, "__PACKAGE__"))
|
||||||
catch /^Vim\%((\a\+)\)\=:E197:/
|
catch /^Vim\%((\a\+)\)\=:E197:/
|
||||||
throw "Skipped: not possible to set locale to en (missing?)"
|
throw "Skipped: not possible to set locale to en (missing?)"
|
||||||
endtry
|
endtry
|
||||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1064,
|
||||||
/**/
|
/**/
|
||||||
1063,
|
1063,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user