summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-07-22 19:11:19 +0200
committerBram Moolenaar <Bram@vim.org>2020-07-22 19:11:19 +0200
commit054f14bbe58fece17f1a74ca63f0b37518f0b4de (patch)
tree14d9accc3dbe2a22a9345257e0bb514eb8f183b9
parente7e4838f25ac39177f3c3150ee53af8d6e8a0f28 (diff)
downloadvim-git-054f14bbe58fece17f1a74ca63f0b37518f0b4de.tar.gz
patch 8.2.1269: language and locale code spread outv8.2.1269
Problem: Language and locale code spread out. Solution: Move relevant code to src/locale.c. (Yegappan Lakshmanan, closes #6509)
-rw-r--r--Filelist2
-rw-r--r--src/Make_cyg_ming.mak1
-rw-r--r--src/Make_morph.mak1
-rw-r--r--src/Make_mvc.mak4
-rw-r--r--src/Make_vms.mms6
-rw-r--r--src/Makefile10
-rw-r--r--src/README.md1
-rw-r--r--src/ex_cmds2.c502
-rw-r--r--src/locale.c564
-rw-r--r--src/main.c53
-rw-r--r--src/proto.h1
-rw-r--r--src/proto/ex_cmds2.pro6
-rw-r--r--src/proto/locale.pro9
-rw-r--r--src/version.c2
14 files changed, 601 insertions, 561 deletions
diff --git a/Filelist b/Filelist
index 77fabbebf..1e22554d4 100644
--- a/Filelist
+++ b/Filelist
@@ -76,6 +76,7 @@ SRC_ALL = \
src/json_test.c \
src/kword_test.c \
src/list.c \
+ src/locale.c \
src/keymap.h \
src/macros.h \
src/main.c \
@@ -247,6 +248,7 @@ SRC_ALL = \
src/proto/insexpand.pro \
src/proto/json.pro \
src/proto/list.pro \
+ src/proto/locale.pro \
src/proto/main.pro \
src/proto/map.pro \
src/proto/mark.pro \
diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak
index 3e8bb04e0..dedc98d79 100644
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -751,6 +751,7 @@ OBJ = \
$(OUTDIR)/insexpand.o \
$(OUTDIR)/json.o \
$(OUTDIR)/list.o \
+ $(OUTDIR)/locale.o \
$(OUTDIR)/main.o \
$(OUTDIR)/map.o \
$(OUTDIR)/mark.o \
diff --git a/src/Make_morph.mak b/src/Make_morph.mak
index 6fc50ed1f..d293829ae 100644
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -70,6 +70,7 @@ SRC = arabic.c \
insexpand.c \
json.c \
list.c \
+ locale.c \
main.c \
map.c \
mark.c \
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index 65d530f35..3b72001a8 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -773,6 +773,7 @@ OBJ = \
$(OUTDIR)\insexpand.obj \
$(OUTDIR)\json.obj \
$(OUTDIR)\list.obj \
+ $(OUTDIR)\locale.obj \
$(OUTDIR)\main.obj \
$(OUTDIR)\map.obj \
$(OUTDIR)\mark.obj \
@@ -1669,6 +1670,8 @@ $(OUTDIR)/json.obj: $(OUTDIR) json.c $(INCL)
$(OUTDIR)/list.obj: $(OUTDIR) list.c $(INCL)
+$(OUTDIR)/locale.obj: $(OUTDIR) locale.c $(INCL)
+
$(OUTDIR)/main.obj: $(OUTDIR) main.c $(INCL) $(CUI_INCL)
$(OUTDIR)/map.obj: $(OUTDIR) map.c $(INCL)
@@ -1939,6 +1942,7 @@ proto.h: \
proto/insexpand.pro \
proto/json.pro \
proto/list.pro \
+ proto/locale.pro \
proto/main.pro \
proto/map.pro \
proto/mark.pro \
diff --git a/src/Make_vms.mms b/src/Make_vms.mms
index 0d80c0617..83b31f297 100644
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -345,6 +345,7 @@ SRC = \
insexpand.c \
json.c \
list.c \
+ locale.c \
main.c \
map.c \
mark.c \
@@ -460,6 +461,7 @@ OBJ = \
insexpand.obj \
json.obj \
list.obj \
+ locale.obj \
main.obj \
map.obj \
mark.obj \
@@ -865,6 +867,10 @@ list.obj : list.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \
globals.h
+locale.obj : locale.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
+ beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \
+ globals.h
main.obj : main.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
[.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \
diff --git a/src/Makefile b/src/Makefile
index bac4e1273..8ac7dc58b 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1647,6 +1647,7 @@ BASIC_SRC = \
insexpand.c \
json.c \
list.c \
+ locale.c \
main.c \
map.c \
mark.c \
@@ -1798,6 +1799,7 @@ OBJ_COMMON = \
objects/indent.o \
objects/insexpand.o \
objects/list.o \
+ objects/locale.o \
objects/map.o \
objects/mark.o \
objects/match.o \
@@ -1973,6 +1975,7 @@ PRO_AUTO = \
insexpand.pro \
json.pro \
list.pro \
+ locale.pro \
main.pro \
map.pro \
mark.pro \
@@ -3378,6 +3381,9 @@ objects/kword_test.o: kword_test.c
objects/list.o: list.c
$(CCC) -o $@ list.c
+objects/locale.o: locale.c
+ $(CCC) -o $@ locale.c
+
objects/main.o: main.c
$(CCC) -o $@ main.c
@@ -3968,6 +3974,10 @@ objects/list.o: list.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
proto.h globals.h
+objects/locale.o: locale.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+ proto.h globals.h
objects/main.o: main.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
diff --git a/src/README.md b/src/README.md
index f499c4cdb..b4d9bdd89 100644
--- a/src/README.md
+++ b/src/README.md
@@ -52,6 +52,7 @@ help.c | vim help related functions
highlight.c | syntax highlighting
indent.c | text indentation
insexpand.c | Insert mode completion
+locale.c | locale/language handling
map.c | mapping and abbreviations
mark.c | marks
match.c | highlight matching
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index f0b5bfd45..738bdb11d 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -996,505 +996,3 @@ ex_checktime(exarg_T *eap)
}
no_check_timestamps = save_no_check_timestamps;
}
-
-#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
- && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
-# define HAVE_GET_LOCALE_VAL
- static char_u *
-get_locale_val(int what)
-{
- char_u *loc;
-
- // Obtain the locale value from the libraries.
- loc = (char_u *)setlocale(what, NULL);
-
-# ifdef MSWIN
- if (loc != NULL)
- {
- char_u *p;
-
- // setocale() returns something like "LC_COLLATE=<name>;LC_..." when
- // one of the values (e.g., LC_CTYPE) differs.
- p = vim_strchr(loc, '=');
- if (p != NULL)
- {
- loc = ++p;
- while (*p != NUL) // remove trailing newline
- {
- if (*p < ' ' || *p == ';')
- {
- *p = NUL;
- break;
- }
- ++p;
- }
- }
- }
-# endif
-
- return loc;
-}
-#endif
-
-
-#ifdef MSWIN
-/*
- * On MS-Windows locale names are strings like "German_Germany.1252", but
- * gettext expects "de". Try to translate one into another here for a few
- * supported languages.
- */
- static char_u *
-gettext_lang(char_u *name)
-{
- int i;
- static char *(mtable[]) = {
- "afrikaans", "af",
- "czech", "cs",
- "dutch", "nl",
- "german", "de",
- "english_united kingdom", "en_GB",
- "spanish", "es",
- "french", "fr",
- "italian", "it",
- "japanese", "ja",
- "korean", "ko",
- "norwegian", "no",
- "polish", "pl",
- "russian", "ru",
- "slovak", "sk",
- "swedish", "sv",
- "ukrainian", "uk",
- "chinese_china", "zh_CN",
- "chinese_taiwan", "zh_TW",
- NULL};
-
- for (i = 0; mtable[i] != NULL; i += 2)
- if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
- return (char_u *)mtable[i + 1];
- return name;
-}
-#endif
-
-#if defined(FEAT_MULTI_LANG) || defined(PROTO)
-/*
- * Return TRUE when "lang" starts with a valid language name.
- * Rejects NULL, empty string, "C", "C.UTF-8" and others.
- */
- static int
-is_valid_mess_lang(char_u *lang)
-{
- return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
-}
-
-/*
- * Obtain the current messages language. Used to set the default for
- * 'helplang'. May return NULL or an empty string.
- */
- char_u *
-get_mess_lang(void)
-{
- char_u *p;
-
-# ifdef HAVE_GET_LOCALE_VAL
-# if defined(LC_MESSAGES)
- p = get_locale_val(LC_MESSAGES);
-# else
- // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
- // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
- // and LC_MONETARY may be set differently for a Japanese working in the
- // US.
- p = get_locale_val(LC_COLLATE);
-# endif
-# else
- p = mch_getenv((char_u *)"LC_ALL");
- if (!is_valid_mess_lang(p))
- {
- p = mch_getenv((char_u *)"LC_MESSAGES");
- if (!is_valid_mess_lang(p))
- p = mch_getenv((char_u *)"LANG");
- }
-# endif
-# ifdef MSWIN
- p = gettext_lang(p);
-# endif
- return is_valid_mess_lang(p) ? p : NULL;
-}
-#endif
-
-// Complicated #if; matches with where get_mess_env() is used below.
-#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
- && defined(LC_MESSAGES))) \
- || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
- && !defined(LC_MESSAGES))
-/*
- * Get the language used for messages from the environment.
- */
- static char_u *
-get_mess_env(void)
-{
- char_u *p;
-
- p = mch_getenv((char_u *)"LC_ALL");
- if (p == NULL || *p == NUL)
- {
- p = mch_getenv((char_u *)"LC_MESSAGES");
- if (p == NULL || *p == NUL)
- {
- p = mch_getenv((char_u *)"LANG");
- if (p != NULL && VIM_ISDIGIT(*p))
- p = NULL; // ignore something like "1043"
-# ifdef HAVE_GET_LOCALE_VAL
- if (p == NULL || *p == NUL)
- p = get_locale_val(LC_CTYPE);
-# endif
- }
- }
- return p;
-}
-#endif
-
-#if defined(FEAT_EVAL) || defined(PROTO)
-
-/*
- * Set the "v:lang" variable according to the current locale setting.
- * Also do "v:lc_time"and "v:ctype".
- */
- void
-set_lang_var(void)
-{
- char_u *loc;
-
-# ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_CTYPE);
-# else
- // setlocale() not supported: use the default value
- loc = (char_u *)"C";
-# endif
- set_vim_var_string(VV_CTYPE, loc, -1);
-
- // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
- // back to LC_CTYPE if it's empty.
-# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
- loc = get_locale_val(LC_MESSAGES);
-# else
- loc = get_mess_env();
-# endif
- set_vim_var_string(VV_LANG, loc, -1);
-
-# ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_TIME);
-# endif
- set_vim_var_string(VV_LC_TIME, loc, -1);
-
-# ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_COLLATE);
-# else
- // setlocale() not supported: use the default value
- loc = (char_u *)"C";
-# endif
- set_vim_var_string(VV_COLLATE, loc, -1);
-}
-#endif
-
-#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
-/*
- * ":language": Set the language (locale).
- */
- void
-ex_language(exarg_T *eap)
-{
- char *loc;
- char_u *p;
- char_u *name;
- int what = LC_ALL;
- char *whatstr = "";
-# ifdef LC_MESSAGES
-# define VIM_LC_MESSAGES LC_MESSAGES
-# else
-# define VIM_LC_MESSAGES 6789
-# endif
-
- name = eap->arg;
-
- // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
- // Allow abbreviation, but require at least 3 characters to avoid
- // confusion with a two letter language name "me" or "ct".
- p = skiptowhite(eap->arg);
- if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
- {
- if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
- {
- what = VIM_LC_MESSAGES;
- name = skipwhite(p);
- whatstr = "messages ";
- }
- else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
- {
- what = LC_CTYPE;
- name = skipwhite(p);
- whatstr = "ctype ";
- }
- else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
- {
- what = LC_TIME;
- name = skipwhite(p);
- whatstr = "time ";
- }
- else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0)
- {
- what = LC_COLLATE;
- name = skipwhite(p);
- whatstr = "collate ";
- }
- }
-
- if (*name == NUL)
- {
-# ifndef LC_MESSAGES
- if (what == VIM_LC_MESSAGES)
- p = get_mess_env();
- else
-# endif
- p = (char_u *)setlocale(what, NULL);
- if (p == NULL || *p == NUL)
- p = (char_u *)"Unknown";
- smsg(_("Current %slanguage: \"%s\""), whatstr, p);
- }
- else
- {
-# ifndef LC_MESSAGES
- if (what == VIM_LC_MESSAGES)
- loc = "";
- else
-# endif
- {
- loc = setlocale(what, (char *)name);
-# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
- }
- if (loc == NULL)
- semsg(_("E197: Cannot set language to \"%s\""), name);
- else
- {
-# ifdef HAVE_NL_MSG_CAT_CNTR
- // Need to do this for GNU gettext, otherwise cached translations
- // will be used again.
- extern int _nl_msg_cat_cntr;
-
- ++_nl_msg_cat_cntr;
-# endif
- // Reset $LC_ALL, otherwise it would overrule everything.
- vim_setenv((char_u *)"LC_ALL", (char_u *)"");
-
- if (what != LC_TIME && what != LC_COLLATE)
- {
- // Tell gettext() what to translate to. It apparently doesn't
- // use the currently effective locale. Also do this when
- // FEAT_GETTEXT isn't defined, so that shell commands use this
- // value.
- if (what == LC_ALL)
- {
- vim_setenv((char_u *)"LANG", name);
-
- // Clear $LANGUAGE because GNU gettext uses it.
- vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
-# ifdef MSWIN
- // Apparently MS-Windows printf() may cause a crash when
- // we give it 8-bit text while it's expecting text in the
- // current locale. This call avoids that.
- setlocale(LC_CTYPE, "C");
-# endif
- }
- if (what != LC_CTYPE)
- {
- char_u *mname;
-# ifdef MSWIN
- mname = gettext_lang(name);
-# else
- mname = name;
-# endif
- vim_setenv((char_u *)"LC_MESSAGES", mname);
-# ifdef FEAT_MULTI_LANG
- set_helplang_default(mname);
-# endif
- }
- }
-
-# ifdef FEAT_EVAL
- // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
- set_lang_var();
-# endif
-# ifdef FEAT_TITLE
- maketitle();
-# endif
- }
- }
-}
-
-static char_u **locales = NULL; // Array of all available locales
-
-static int did_init_locales = FALSE;
-
-/*
- * Return an array of strings for all available locales + NULL for the
- * last element. Return NULL in case of error.
- */
- static char_u **
-find_locales(void)
-{
- garray_T locales_ga;
- char_u *loc;
- char_u *locale_list;
-# ifdef MSWIN
- size_t len = 0;
-# endif
-
- // Find all available locales by running command "locale -a". If this
- // doesn't work we won't have completion.
-# ifndef MSWIN
- locale_list = get_cmd_output((char_u *)"locale -a",
- NULL, SHELL_SILENT, NULL);
-# else
- // Find all available locales by examining the directories in
- // $VIMRUNTIME/lang/
- {
- int options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL;
- expand_T xpc;
- char_u *p;
-
- ExpandInit(&xpc);
- xpc.xp_context = EXPAND_DIRECTORIES;
- locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*",
- NULL, options, WILD_ALL);
- ExpandCleanup(&xpc);
- if (locale_list == NULL)
- // Add a dummy input, that will be skipped lated but we need to
- // have something in locale_list so that the C locale is added at
- // the end.
- locale_list = vim_strsave((char_u *)".\n");
- p = locale_list;
- // find the last directory delimiter
- while (p != NULL && *p != NUL)
- {
- if (*p == '\n')
- break;
- if (*p == '\\')
- len = p - locale_list;
- p++;
- }
- }
-# endif
- if (locale_list == NULL)
- return NULL;
- ga_init2(&locales_ga, sizeof(char_u *), 20);
-
- // Transform locale_list string where each locale is separated by "\n"
- // into an array of locale strings.
- loc = (char_u *)strtok((char *)locale_list, "\n");
-
- while (loc != NULL)
- {
- int ignore = FALSE;
-
-# ifdef MSWIN
- if (len > 0)
- loc += len + 1;
- // skip locales with a dot (which indicates the charset)
- if (vim_strchr(loc, '.') != NULL)
- ignore = TRUE;
-# endif
- if (!ignore)
- {
- if (ga_grow(&locales_ga, 1) == FAIL)
- break;
-
- loc = vim_strsave(loc);
- if (loc == NULL)
- break;
-
- ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
- }
- loc = (char_u *)strtok(NULL, "\n");
- }
-
-# ifdef MSWIN
- // Add the C locale
- if (ga_grow(&locales_ga, 1) == OK)
- ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] =
- vim_strsave((char_u *)"C");
-# endif
-
- vim_free(locale_list);
- if (ga_grow(&locales_ga, 1) == FAIL)
- {
- ga_clear(&locales_ga);
- return NULL;
- }
- ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
- return (char_u **)locales_ga.ga_data;
-}
-
-/*
- * Lazy initialization of all available locales.
- */
- static void
-init_locales(void)
-{
- if (!did_init_locales)
- {
- did_init_locales = TRUE;
- locales = find_locales();
- }
-}
-
-# if defined(EXITFREE) || defined(PROTO)
- void
-free_locales(void)
-{
- int i;
- if (locales != NULL)
- {
- for (i = 0; locales[i] != NULL; i++)
- vim_free(locales[i]);
- VIM_CLEAR(locales);
- }
-}
-# endif
-
-/*
- * Function given to ExpandGeneric() to obtain the possible arguments of the
- * ":language" command.
- */
- char_u *
-get_lang_arg(expand_T *xp UNUSED, int idx)
-{
- if (idx == 0)
- return (char_u *)"messages";
- if (idx == 1)
- return (char_u *)"ctype";
- if (idx == 2)
- return (char_u *)"time";
- if (idx == 3)
- return (char_u *)"collate";
-
- init_locales();
- if (locales == NULL)
- return NULL;
- return locales[idx - 4];
-}
-
-/*
- * Function given to ExpandGeneric() to obtain the available locales.
- */
- char_u *
-get_locales(expand_T *xp UNUSED, int idx)
-{
- init_locales();
- if (locales == NULL)
- return NULL;
- return locales[idx];
-}
-
-#endif
diff --git a/src/locale.c b/src/locale.c
new file mode 100644
index 000000000..7fad9d9fe
--- /dev/null
+++ b/src/locale.c
@@ -0,0 +1,564 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * locale.c: functions for language/locale configuration
+ */
+
+#include "vim.h"
+
+#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
+ && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
+# define HAVE_GET_LOCALE_VAL
+ static char_u *
+get_locale_val(int what)
+{
+ char_u *loc;
+
+ // Obtain the locale value from the libraries.
+ loc = (char_u *)setlocale(what, NULL);
+
+# ifdef MSWIN
+ if (loc != NULL)
+ {
+ char_u *p;
+
+ // setocale() returns something like "LC_COLLATE=<name>;LC_..." when
+ // one of the values (e.g., LC_CTYPE) differs.
+ p = vim_strchr(loc, '=');
+ if (p != NULL)
+ {
+ loc = ++p;
+ while (*p != NUL) // remove trailing newline
+ {
+ if (*p < ' ' || *p == ';')
+ {
+ *p = NUL;
+ break;
+ }
+ ++p;
+ }
+ }
+ }
+# endif
+
+ return loc;
+}
+#endif
+
+
+#ifdef MSWIN
+/*
+ * On MS-Windows locale names are strings like "German_Germany.1252", but
+ * gettext expects "de". Try to translate one into another here for a few
+ * supported languages.
+ */
+ static char_u *
+gettext_lang(char_u *name)
+{
+ int i;
+ static char *(mtable[]) = {
+ "afrikaans", "af",
+ "czech", "cs",
+ "dutch", "nl",
+ "german", "de",
+ "english_united kingdom", "en_GB",
+ "spanish", "es",
+ "french", "fr",
+ "italian", "it",
+ "japanese", "ja",
+ "korean", "ko",
+ "norwegian", "no",
+ "polish", "pl",
+ "russian", "ru",
+ "slovak", "sk",
+ "swedish", "sv",
+ "ukrainian", "uk",
+ "chinese_china", "zh_CN",
+ "chinese_taiwan", "zh_TW",
+ NULL};
+
+ for (i = 0; mtable[i] != NULL; i += 2)
+ if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
+ return (char_u *)mtable[i + 1];
+ return name;
+}
+#endif
+
+#if defined(FEAT_MULTI_LANG) || defined(PROTO)
+/*
+ * Return TRUE when "lang" starts with a valid language name.
+ * Rejects NULL, empty string, "C", "C.UTF-8" and others.
+ */
+ static int
+is_valid_mess_lang(char_u *lang)
+{
+ return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
+}
+
+/*
+ * Obtain the current messages language. Used to set the default for
+ * 'helplang'. May return NULL or an empty string.
+ */
+ char_u *
+get_mess_lang(void)
+{
+ char_u *p;
+
+# ifdef HAVE_GET_LOCALE_VAL
+# if defined(LC_MESSAGES)
+ p = get_locale_val(LC_MESSAGES);
+# else
+ // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
+ // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
+ // and LC_MONETARY may be set differently for a Japanese working in the
+ // US.
+ p = get_locale_val(LC_COLLATE);
+# endif
+# else
+ p = mch_getenv((char_u *)"LC_ALL");
+ if (!is_valid_mess_lang(p))
+ {
+ p = mch_getenv((char_u *)"LC_MESSAGES");
+ if (!is_valid_mess_lang(p))
+ p = mch_getenv((char_u *)"LANG");
+ }
+# endif
+# ifdef MSWIN
+ p = gettext_lang(p);
+# endif
+ return is_valid_mess_lang(p) ? p : NULL;
+}
+#endif
+
+// Complicated #if; matches with where get_mess_env() is used below.
+#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
+ && defined(LC_MESSAGES))) \
+ || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
+ && !defined(LC_MESSAGES))
+/*
+ * Get the language used for messages from the environment.
+ */
+ static char_u *
+get_mess_env(void)
+{
+ char_u *p;
+
+ p = mch_getenv((char_u *)"LC_ALL");
+ if (p == NULL || *p == NUL)
+ {
+ p = mch_getenv((char_u *)"LC_MESSAGES");
+ if (p == NULL || *p == NUL)
+ {
+ p = mch_getenv((char_u *)"LANG");
+ if (p != NULL && VIM_ISDIGIT(*p))
+ p = NULL; // ignore something like "1043"
+# ifdef HAVE_GET_LOCALE_VAL
+ if (p == NULL || *p == NUL)
+ p = get_locale_val(LC_CTYPE);
+# endif
+ }
+ }
+ return p;
+}
+#endif
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+
+/*
+ * Set the "v:lang" variable according to the current locale setting.
+ * Also do "v:lc_time"and "v:ctype".
+ */
+ void
+set_lang_var(void)
+{
+ char_u *loc;
+
+# ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_CTYPE);
+# else
+ // setlocale() not supported: use the default value
+ loc = (char_u *)"C";
+# endif
+ set_vim_var_string(VV_CTYPE, loc, -1);
+
+ // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
+ // back to LC_CTYPE if it's empty.
+# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
+ loc = get_locale_val(LC_MESSAGES);
+# else
+ loc = get_mess_env();
+# endif
+ set_vim_var_string(VV_LANG, loc, -1);
+
+# ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_TIME);
+# endif
+ set_vim_var_string(VV_LC_TIME, loc, -1);
+
+# ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_COLLATE);
+# else
+ // setlocale() not supported: use the default value
+ loc = (char_u *)"C";
+# endif
+ set_vim_var_string(VV_COLLATE, loc, -1);
+}
+#endif
+
+#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
+/*
+ * Setup to use the current locale (for ctype() and many other things).
+ */
+ void
+init_locale(void)
+{
+ setlocale(LC_ALL, "");
+
+# ifdef FEAT_GUI_GTK
+ // Tell Gtk not to change our locale settings.
+ gtk_disable_setlocale();
+# endif
+# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+
+# ifdef MSWIN
+ // Apparently MS-Windows printf() may cause a crash when we give it 8-bit
+ // text while it's expecting text in the current locale. This call avoids
+ // that.
+ setlocale(LC_CTYPE, "C");
+# endif
+
+# ifdef FEAT_GETTEXT
+ {
+ int mustfree = FALSE;
+ char_u *p;
+
+# ifdef DYNAMIC_GETTEXT
+ // Initialize the gettext library
+ dyn_libintl_init();
+# endif
+ // expand_env() doesn't work yet, because g_chartab[] is not
+ // initialized yet, call vim_getenv() directly
+ p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
+ if (p != NULL && *p != NUL)
+ {
+ vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
+ bindtextdomain(VIMPACKAGE, (char *)NameBuff);
+ }
+ if (mustfree)
+ vim_free(p);
+ textdomain(VIMPACKAGE);
+ }
+# endif
+}
+
+/*
+ * ":language": Set the language (locale).
+ */
+ void
+ex_language(exarg_T *eap)
+{
+ char *loc;
+ char_u *p;
+ char_u *name;
+ int what = LC_ALL;
+ char *whatstr = "";
+# ifdef LC_MESSAGES
+# define VIM_LC_MESSAGES LC_MESSAGES
+# else
+# define VIM_LC_MESSAGES 6789
+# endif
+
+ name = eap->arg;
+
+ // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
+ // Allow abbreviation, but require at least 3 characters to avoid
+ // confusion with a two letter language name "me" or "ct".
+ p = skiptowhite(eap->arg);
+ if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
+ {
+ if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
+ {
+ what = VIM_LC_MESSAGES;
+ name = skipwhite(p);
+ whatstr = "messages ";
+ }
+ else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
+ {
+ what = LC_CTYPE;
+ name = skipwhite(p);
+ whatstr = "ctype ";
+ }
+ else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
+ {
+ what = LC_TIME;
+ name = skipwhite(p);
+ whatstr = "time ";
+ }
+ else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0)
+ {
+ what = LC_COLLATE;
+ name = skipwhite(p);
+ whatstr = "collate ";
+ }
+ }
+
+ if (*name == NUL)
+ {
+# ifndef LC_MESSAGES
+ if (what == VIM_LC_MESSAGES)
+ p = get_mess_env();
+ else
+# endif
+ p = (char_u *)setlocale(what, NULL);
+ if (p == NULL || *p == NUL)
+ p = (char_u *)"Unknown";
+ smsg(_("Current %slanguage: \"%s\""), whatstr, p);
+ }
+ else
+ {
+# ifndef LC_MESSAGES
+ if (what == VIM_LC_MESSAGES)
+ loc = "";
+ else
+# endif
+ {
+ loc = setlocale(what, (char *)name);
+# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+ }
+ if (loc == NULL)
+ semsg(_("E197: Cannot set language to \"%s\""), name);
+ else
+ {
+# ifdef HAVE_NL_MSG_CAT_CNTR
+ // Need to do this for GNU gettext, otherwise cached translations
+ // will be used again.
+ extern int _nl_msg_cat_cntr;
+
+ ++_nl_msg_cat_cntr;
+# endif
+ // Reset $LC_ALL, otherwise it would overrule everything.
+ vim_setenv((char_u *)"LC_ALL", (char_u *)"");
+
+ if (what != LC_TIME && what != LC_COLLATE)
+ {
+ // Tell gettext() what to translate to. It apparently doesn't
+ // use the currently effective locale. Also do this when
+ // FEAT_GETTEXT isn't defined, so that shell commands use this
+ // value.
+ if (what == LC_ALL)
+ {
+ vim_setenv((char_u *)"LANG", name);
+
+ // Clear $LANGUAGE because GNU gettext uses it.
+ vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
+# ifdef MSWIN
+ // Apparently MS-Windows printf() may cause a crash when
+ // we give it 8-bit text while it's expecting text in the
+ // current locale. This call avoids that.
+ setlocale(LC_CTYPE, "C");
+# endif
+ }
+ if (what != LC_CTYPE)
+ {
+ char_u *mname;
+# ifdef MSWIN
+ mname = gettext_lang(name);
+# else
+ mname = name;
+# endif
+ vim_setenv((char_u *)"LC_MESSAGES", mname);
+# ifdef FEAT_MULTI_LANG
+ set_helplang_default(mname);
+# endif
+ }
+ }
+
+# ifdef FEAT_EVAL
+ // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
+ set_lang_var();
+# endif
+# ifdef FEAT_TITLE
+ maketitle();
+# endif
+ }
+ }
+}
+
+static char_u **locales = NULL; // Array of all available locales
+
+static int did_init_locales = FALSE;
+
+/*
+ * Return an array of strings for all available locales + NULL for the
+ * last element. Return NULL in case of error.
+ */
+ static char_u **
+find_locales(void)
+{
+ garray_T locales_ga;
+ char_u *loc;
+ char_u *locale_list;
+# ifdef MSWIN
+ size_t len = 0;
+# endif
+
+ // Find all available locales by running command "locale -a". If this
+ // doesn't work we won't have completion.
+# ifndef MSWIN
+ locale_list = get_cmd_output((char_u *)"locale -a",
+ NULL, SHELL_SILENT, NULL);
+# else
+ // Find all available locales by examining the directories in
+ // $VIMRUNTIME/lang/
+ {
+ int options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL;
+ expand_T xpc;
+ char_u *p;
+
+ ExpandInit(&xpc);
+ xpc.xp_context = EXPAND_DIRECTORIES;
+ locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*",
+ NULL, options, WILD_ALL);
+ ExpandCleanup(&xpc);
+ if (locale_list == NULL)
+ // Add a dummy input, that will be skipped lated but we need to
+ // have something in locale_list so that the C locale is added at
+ // the end.
+ locale_list = vim_strsave((char_u *)".\n");
+ p = locale_list;
+ // find the last directory delimiter
+ while (p != NULL && *p != NUL)
+ {
+ if (*p == '\n')
+ break;
+ if (*p == '\\')
+ len = p - locale_list;
+ p++;
+ }
+ }
+# endif
+ if (locale_list == NULL)
+ return NULL;
+ ga_init2(&locales_ga, sizeof(char_u *), 20);
+
+ // Transform locale_list string where each locale is separated by "\n"
+ // into an array of locale strings.
+ loc = (char_u *)strtok((char *)locale_list, "\n");
+
+ while (loc != NULL)
+ {
+ int ignore = FALSE;
+
+# ifdef MSWIN
+ if (len > 0)
+ loc += len + 1;
+ // skip locales with a dot (which indicates the charset)
+ if (vim_strchr(loc, '.') != NULL)
+ ignore = TRUE;
+# endif
+ if (!ignore)
+ {
+ if (ga_grow(&locales_ga, 1) == FAIL)
+ break;
+
+ loc = vim_strsave(loc);
+ if (loc == NULL)
+ break;
+
+ ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
+ }
+ loc = (char_u *)strtok(NULL, "\n");
+ }
+
+# ifdef MSWIN
+ // Add the C locale
+ if (ga_grow(&locales_ga, 1) == OK)
+ ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] =
+ vim_strsave((char_u *)"C");
+# endif
+
+ vim_free(locale_list);
+ if (ga_grow(&locales_ga, 1) == FAIL)
+ {
+ ga_clear(&locales_ga);
+ return NULL;
+ }
+ ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
+ return (char_u **)locales_ga.ga_data;
+}
+
+/*
+ * Lazy initialization of all available locales.
+ */
+ static void
+init_locales(void)
+{
+ if (!did_init_locales)
+ {
+ did_init_locales = TRUE;
+ locales = find_locales();
+ }
+}
+
+# if defined(EXITFREE) || defined(PROTO)
+ void
+free_locales(void)
+{
+ int i;
+ if (locales != NULL)
+ {
+ for (i = 0; locales[i] != NULL; i++)
+ vim_free(locales[i]);
+ VIM_CLEAR(locales);
+ }
+}
+# endif
+
+/*
+ * Function given to ExpandGeneric() to obtain the possible arguments of the
+ * ":language" command.
+ */
+ char_u *
+get_lang_arg(expand_T *xp UNUSED, int idx)
+{
+ if (idx == 0)
+ return (char_u *)"messages";
+ if (idx == 1)
+ return (char_u *)"ctype";
+ if (idx == 2)
+ return (char_u *)"time";
+ if (idx == 3)
+ return (char_u *)"collate";
+
+ init_locales();
+ if (locales == NULL)
+ return NULL;
+ return locales[idx - 4];
+}
+
+/*
+ * Function given to ExpandGeneric() to obtain the available locales.
+ */
+ char_u *
+get_locales(expand_T *xp UNUSED, int idx)
+{
+ init_locales();
+ if (locales == NULL)
+ return NULL;
+ return locales[idx];
+}
+
+#endif
diff --git a/src/main.c b/src/main.c
index 39e67e998..86156b728 100644
--- a/src/main.c
+++ b/src/main.c
@@ -34,9 +34,6 @@
static int file_owned(char *fname);
#endif
static void mainerr(int, char_u *);
-# if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
-static void init_locale(void);
-# endif
static void early_arg_scan(mparm_T *parmp);
#ifndef NO_VIM_MAIN
static void usage(void);
@@ -1716,56 +1713,6 @@ getout(int exitval)
mch_exit(exitval);
}
-#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
-/*
- * Setup to use the current locale (for ctype() and many other things).
- */
- static void
-init_locale(void)
-{
- setlocale(LC_ALL, "");
-
-# ifdef FEAT_GUI_GTK
- // Tell Gtk not to change our locale settings.
- gtk_disable_setlocale();
-# endif
-# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-
-# ifdef MSWIN
- // Apparently MS-Windows printf() may cause a crash when we give it 8-bit
- // text while it's expecting text in the current locale. This call avoids
- // that.
- setlocale(LC_CTYPE, "C");
-# endif
-
-# ifdef FEAT_GETTEXT
- {
- int mustfree = FALSE;
- char_u *p;
-
-# ifdef DYNAMIC_GETTEXT
- // Initialize the gettext library
- dyn_libintl_init();
-# endif
- // expand_env() doesn't work yet, because g_chartab[] is not
- // initialized yet, call vim_getenv() directly
- p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
- if (p != NULL && *p != NUL)
- {
- vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
- bindtextdomain(VIMPACKAGE, (char *)NameBuff);
- }
- if (mustfree)
- vim_free(p);
- textdomain(VIMPACKAGE);
- }
-# endif
-}
-#endif
-
/*
* Get the name of the display, before gui_prepare() removes it from
* argv[]. Used for the xterm-clipboard display.
diff --git a/src/proto.h b/src/proto.h
index e3a25f988..95fd81ac9 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -101,6 +101,7 @@ extern int _stricoll(char *a, char *b);
# include "insexpand.pro"
# include "json.pro"
# include "list.pro"
+# include "locale.pro"
# include "blob.pro"
# include "main.pro"
# include "map.pro"
diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro
index b9d1929f7..127e01807 100644
--- a/src/proto/ex_cmds2.pro
+++ b/src/proto/ex_cmds2.pro
@@ -15,10 +15,4 @@ void ex_pyxfile(exarg_T *eap);
void ex_pyx(exarg_T *eap);
void ex_pyxdo(exarg_T *eap);
void ex_checktime(exarg_T *eap);
-char_u *get_mess_lang(void);
-void set_lang_var(void);
-void ex_language(exarg_T *eap);
-void free_locales(void);
-char_u *get_lang_arg(expand_T *xp, int idx);
-char_u *get_locales(expand_T *xp, int idx);
/* vim: set ft=c : */
diff --git a/src/proto/locale.pro b/src/proto/locale.pro
new file mode 100644
index 000000000..151e68a1b
--- /dev/null
+++ b/src/proto/locale.pro
@@ -0,0 +1,9 @@
+/* locale.c */
+char_u *get_mess_lang(void);
+void set_lang_var(void);
+void init_locale(void);
+void ex_language(exarg_T *eap);
+void free_locales(void);
+char_u *get_lang_arg(expand_T *xp, int idx);
+char_u *get_locales(expand_T *xp, int idx);
+/* vim: set ft=c : */
diff --git a/src/version.c b/src/version.c
index 1ab604036..cf6432720 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1269,
+/**/
1268,
/**/
1267,