xref: /vim-8.2.3635/src/locale.c (revision 054f14bb)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * locale.c: functions for language/locale configuration
12  */
13 
14 #include "vim.h"
15 
16 #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
17 	&& (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
18 # define HAVE_GET_LOCALE_VAL
19     static char_u *
get_locale_val(int what)20 get_locale_val(int what)
21 {
22     char_u	*loc;
23 
24     // Obtain the locale value from the libraries.
25     loc = (char_u *)setlocale(what, NULL);
26 
27 # ifdef MSWIN
28     if (loc != NULL)
29     {
30 	char_u	*p;
31 
32 	// setocale() returns something like "LC_COLLATE=<name>;LC_..." when
33 	// one of the values (e.g., LC_CTYPE) differs.
34 	p = vim_strchr(loc, '=');
35 	if (p != NULL)
36 	{
37 	    loc = ++p;
38 	    while (*p != NUL)	// remove trailing newline
39 	    {
40 		if (*p < ' ' || *p == ';')
41 		{
42 		    *p = NUL;
43 		    break;
44 		}
45 		++p;
46 	    }
47 	}
48     }
49 # endif
50 
51     return loc;
52 }
53 #endif
54 
55 
56 #ifdef MSWIN
57 /*
58  * On MS-Windows locale names are strings like "German_Germany.1252", but
59  * gettext expects "de".  Try to translate one into another here for a few
60  * supported languages.
61  */
62     static char_u *
gettext_lang(char_u * name)63 gettext_lang(char_u *name)
64 {
65     int		i;
66     static char *(mtable[]) = {
67 			"afrikaans",	"af",
68 			"czech",	"cs",
69 			"dutch",	"nl",
70 			"german",	"de",
71 			"english_united kingdom", "en_GB",
72 			"spanish",	"es",
73 			"french",	"fr",
74 			"italian",	"it",
75 			"japanese",	"ja",
76 			"korean",	"ko",
77 			"norwegian",	"no",
78 			"polish",	"pl",
79 			"russian",	"ru",
80 			"slovak",	"sk",
81 			"swedish",	"sv",
82 			"ukrainian",	"uk",
83 			"chinese_china", "zh_CN",
84 			"chinese_taiwan", "zh_TW",
85 			NULL};
86 
87     for (i = 0; mtable[i] != NULL; i += 2)
88 	if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
89 	    return (char_u *)mtable[i + 1];
90     return name;
91 }
92 #endif
93 
94 #if defined(FEAT_MULTI_LANG) || defined(PROTO)
95 /*
96  * Return TRUE when "lang" starts with a valid language name.
97  * Rejects NULL, empty string, "C", "C.UTF-8" and others.
98  */
99     static int
is_valid_mess_lang(char_u * lang)100 is_valid_mess_lang(char_u *lang)
101 {
102     return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
103 }
104 
105 /*
106  * Obtain the current messages language.  Used to set the default for
107  * 'helplang'.  May return NULL or an empty string.
108  */
109     char_u *
get_mess_lang(void)110 get_mess_lang(void)
111 {
112     char_u *p;
113 
114 # ifdef HAVE_GET_LOCALE_VAL
115 #  if defined(LC_MESSAGES)
116     p = get_locale_val(LC_MESSAGES);
117 #  else
118     // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
119     // may be set to the LCID number.  LC_COLLATE is the best guess, LC_TIME
120     // and LC_MONETARY may be set differently for a Japanese working in the
121     // US.
122     p = get_locale_val(LC_COLLATE);
123 #  endif
124 # else
125     p = mch_getenv((char_u *)"LC_ALL");
126     if (!is_valid_mess_lang(p))
127     {
128 	p = mch_getenv((char_u *)"LC_MESSAGES");
129 	if (!is_valid_mess_lang(p))
130 	    p = mch_getenv((char_u *)"LANG");
131     }
132 # endif
133 # ifdef MSWIN
134     p = gettext_lang(p);
135 # endif
136     return is_valid_mess_lang(p) ? p : NULL;
137 }
138 #endif
139 
140 // Complicated #if; matches with where get_mess_env() is used below.
141 #if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
142 	    && defined(LC_MESSAGES))) \
143 	|| ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
144 		&& !defined(LC_MESSAGES))
145 /*
146  * Get the language used for messages from the environment.
147  */
148     static char_u *
get_mess_env(void)149 get_mess_env(void)
150 {
151     char_u	*p;
152 
153     p = mch_getenv((char_u *)"LC_ALL");
154     if (p == NULL || *p == NUL)
155     {
156 	p = mch_getenv((char_u *)"LC_MESSAGES");
157 	if (p == NULL || *p == NUL)
158 	{
159 	    p = mch_getenv((char_u *)"LANG");
160 	    if (p != NULL && VIM_ISDIGIT(*p))
161 		p = NULL;		// ignore something like "1043"
162 # ifdef HAVE_GET_LOCALE_VAL
163 	    if (p == NULL || *p == NUL)
164 		p = get_locale_val(LC_CTYPE);
165 # endif
166 	}
167     }
168     return p;
169 }
170 #endif
171 
172 #if defined(FEAT_EVAL) || defined(PROTO)
173 
174 /*
175  * Set the "v:lang" variable according to the current locale setting.
176  * Also do "v:lc_time"and "v:ctype".
177  */
178     void
set_lang_var(void)179 set_lang_var(void)
180 {
181     char_u	*loc;
182 
183 # ifdef HAVE_GET_LOCALE_VAL
184     loc = get_locale_val(LC_CTYPE);
185 # else
186     // setlocale() not supported: use the default value
187     loc = (char_u *)"C";
188 # endif
189     set_vim_var_string(VV_CTYPE, loc, -1);
190 
191     // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
192     // back to LC_CTYPE if it's empty.
193 # if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
194     loc = get_locale_val(LC_MESSAGES);
195 # else
196     loc = get_mess_env();
197 # endif
198     set_vim_var_string(VV_LANG, loc, -1);
199 
200 # ifdef HAVE_GET_LOCALE_VAL
201     loc = get_locale_val(LC_TIME);
202 # endif
203     set_vim_var_string(VV_LC_TIME, loc, -1);
204 
205 # ifdef HAVE_GET_LOCALE_VAL
206     loc = get_locale_val(LC_COLLATE);
207 # else
208     // setlocale() not supported: use the default value
209     loc = (char_u *)"C";
210 # endif
211     set_vim_var_string(VV_COLLATE, loc, -1);
212 }
213 #endif
214 
215 #if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
216 /*
217  * Setup to use the current locale (for ctype() and many other things).
218  */
219     void
init_locale(void)220 init_locale(void)
221 {
222     setlocale(LC_ALL, "");
223 
224 # ifdef FEAT_GUI_GTK
225     // Tell Gtk not to change our locale settings.
226     gtk_disable_setlocale();
227 # endif
228 # if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
229     // Make sure strtod() uses a decimal point, not a comma.
230     setlocale(LC_NUMERIC, "C");
231 # endif
232 
233 # ifdef MSWIN
234     // Apparently MS-Windows printf() may cause a crash when we give it 8-bit
235     // text while it's expecting text in the current locale.  This call avoids
236     // that.
237     setlocale(LC_CTYPE, "C");
238 # endif
239 
240 # ifdef FEAT_GETTEXT
241     {
242 	int	mustfree = FALSE;
243 	char_u	*p;
244 
245 #  ifdef DYNAMIC_GETTEXT
246 	// Initialize the gettext library
247 	dyn_libintl_init();
248 #  endif
249 	// expand_env() doesn't work yet, because g_chartab[] is not
250 	// initialized yet, call vim_getenv() directly
251 	p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
252 	if (p != NULL && *p != NUL)
253 	{
254 	    vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
255 	    bindtextdomain(VIMPACKAGE, (char *)NameBuff);
256 	}
257 	if (mustfree)
258 	    vim_free(p);
259 	textdomain(VIMPACKAGE);
260     }
261 # endif
262 }
263 
264 /*
265  * ":language":  Set the language (locale).
266  */
267     void
ex_language(exarg_T * eap)268 ex_language(exarg_T *eap)
269 {
270     char	*loc;
271     char_u	*p;
272     char_u	*name;
273     int		what = LC_ALL;
274     char	*whatstr = "";
275 # ifdef LC_MESSAGES
276 #  define VIM_LC_MESSAGES LC_MESSAGES
277 # else
278 #  define VIM_LC_MESSAGES 6789
279 # endif
280 
281     name = eap->arg;
282 
283     // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
284     // Allow abbreviation, but require at least 3 characters to avoid
285     // confusion with a two letter language name "me" or "ct".
286     p = skiptowhite(eap->arg);
287     if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
288     {
289 	if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
290 	{
291 	    what = VIM_LC_MESSAGES;
292 	    name = skipwhite(p);
293 	    whatstr = "messages ";
294 	}
295 	else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
296 	{
297 	    what = LC_CTYPE;
298 	    name = skipwhite(p);
299 	    whatstr = "ctype ";
300 	}
301 	else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
302 	{
303 	    what = LC_TIME;
304 	    name = skipwhite(p);
305 	    whatstr = "time ";
306 	}
307 	else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0)
308 	{
309 	    what = LC_COLLATE;
310 	    name = skipwhite(p);
311 	    whatstr = "collate ";
312 	}
313     }
314 
315     if (*name == NUL)
316     {
317 # ifndef LC_MESSAGES
318 	if (what == VIM_LC_MESSAGES)
319 	    p = get_mess_env();
320 	else
321 # endif
322 	    p = (char_u *)setlocale(what, NULL);
323 	if (p == NULL || *p == NUL)
324 	    p = (char_u *)"Unknown";
325 	smsg(_("Current %slanguage: \"%s\""), whatstr, p);
326     }
327     else
328     {
329 # ifndef LC_MESSAGES
330 	if (what == VIM_LC_MESSAGES)
331 	    loc = "";
332 	else
333 # endif
334 	{
335 	    loc = setlocale(what, (char *)name);
336 # if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
337 	    // Make sure strtod() uses a decimal point, not a comma.
338 	    setlocale(LC_NUMERIC, "C");
339 # endif
340 	}
341 	if (loc == NULL)
342 	    semsg(_("E197: Cannot set language to \"%s\""), name);
343 	else
344 	{
345 # ifdef HAVE_NL_MSG_CAT_CNTR
346 	    // Need to do this for GNU gettext, otherwise cached translations
347 	    // will be used again.
348 	    extern int _nl_msg_cat_cntr;
349 
350 	    ++_nl_msg_cat_cntr;
351 # endif
352 	    // Reset $LC_ALL, otherwise it would overrule everything.
353 	    vim_setenv((char_u *)"LC_ALL", (char_u *)"");
354 
355 	    if (what != LC_TIME && what != LC_COLLATE)
356 	    {
357 		// Tell gettext() what to translate to.  It apparently doesn't
358 		// use the currently effective locale.  Also do this when
359 		// FEAT_GETTEXT isn't defined, so that shell commands use this
360 		// value.
361 		if (what == LC_ALL)
362 		{
363 		    vim_setenv((char_u *)"LANG", name);
364 
365 		    // Clear $LANGUAGE because GNU gettext uses it.
366 		    vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
367 # ifdef MSWIN
368 		    // Apparently MS-Windows printf() may cause a crash when
369 		    // we give it 8-bit text while it's expecting text in the
370 		    // current locale.  This call avoids that.
371 		    setlocale(LC_CTYPE, "C");
372 # endif
373 		}
374 		if (what != LC_CTYPE)
375 		{
376 		    char_u	*mname;
377 # ifdef MSWIN
378 		    mname = gettext_lang(name);
379 # else
380 		    mname = name;
381 # endif
382 		    vim_setenv((char_u *)"LC_MESSAGES", mname);
383 # ifdef FEAT_MULTI_LANG
384 		    set_helplang_default(mname);
385 # endif
386 		}
387 	    }
388 
389 # ifdef FEAT_EVAL
390 	    // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
391 	    set_lang_var();
392 # endif
393 # ifdef FEAT_TITLE
394 	    maketitle();
395 # endif
396 	}
397     }
398 }
399 
400 static char_u	**locales = NULL;	// Array of all available locales
401 
402 static int	did_init_locales = FALSE;
403 
404 /*
405  * Return an array of strings for all available locales + NULL for the
406  * last element.  Return NULL in case of error.
407  */
408     static char_u **
find_locales(void)409 find_locales(void)
410 {
411     garray_T	locales_ga;
412     char_u	*loc;
413     char_u	*locale_list;
414 # ifdef MSWIN
415     size_t	len = 0;
416 # endif
417 
418     // Find all available locales by running command "locale -a".  If this
419     // doesn't work we won't have completion.
420 # ifndef MSWIN
421     locale_list = get_cmd_output((char_u *)"locale -a",
422 						    NULL, SHELL_SILENT, NULL);
423 # else
424     // Find all available locales by examining the directories in
425     // $VIMRUNTIME/lang/
426     {
427 	int		options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL;
428 	expand_T	xpc;
429 	char_u		*p;
430 
431 	ExpandInit(&xpc);
432 	xpc.xp_context = EXPAND_DIRECTORIES;
433 	locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*",
434 						      NULL, options, WILD_ALL);
435 	ExpandCleanup(&xpc);
436 	if (locale_list == NULL)
437 	    // Add a dummy input, that will be skipped lated but we need to
438 	    // have something in locale_list so that the C locale is added at
439 	    // the end.
440 	    locale_list = vim_strsave((char_u *)".\n");
441 	p = locale_list;
442 	// find the last directory delimiter
443 	while (p != NULL && *p != NUL)
444 	{
445 	    if (*p == '\n')
446 		break;
447 	    if (*p == '\\')
448 		len = p - locale_list;
449 	    p++;
450 	}
451     }
452 # endif
453     if (locale_list == NULL)
454 	return NULL;
455     ga_init2(&locales_ga, sizeof(char_u *), 20);
456 
457     // Transform locale_list string where each locale is separated by "\n"
458     // into an array of locale strings.
459     loc = (char_u *)strtok((char *)locale_list, "\n");
460 
461     while (loc != NULL)
462     {
463 	int ignore = FALSE;
464 
465 # ifdef MSWIN
466 	if (len > 0)
467 	    loc += len + 1;
468 	// skip locales with a dot (which indicates the charset)
469 	if (vim_strchr(loc, '.') != NULL)
470 	    ignore = TRUE;
471 # endif
472 	if (!ignore)
473 	{
474 	    if (ga_grow(&locales_ga, 1) == FAIL)
475 		break;
476 
477 	    loc = vim_strsave(loc);
478 	    if (loc == NULL)
479 		break;
480 
481 	    ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
482 	}
483 	loc = (char_u *)strtok(NULL, "\n");
484     }
485 
486 # ifdef MSWIN
487     // Add the C locale
488     if (ga_grow(&locales_ga, 1) == OK)
489 	((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] =
490 						    vim_strsave((char_u *)"C");
491 # endif
492 
493     vim_free(locale_list);
494     if (ga_grow(&locales_ga, 1) == FAIL)
495     {
496 	ga_clear(&locales_ga);
497 	return NULL;
498     }
499     ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
500     return (char_u **)locales_ga.ga_data;
501 }
502 
503 /*
504  * Lazy initialization of all available locales.
505  */
506     static void
init_locales(void)507 init_locales(void)
508 {
509     if (!did_init_locales)
510     {
511 	did_init_locales = TRUE;
512 	locales = find_locales();
513     }
514 }
515 
516 # if defined(EXITFREE) || defined(PROTO)
517     void
free_locales(void)518 free_locales(void)
519 {
520     int			i;
521     if (locales != NULL)
522     {
523 	for (i = 0; locales[i] != NULL; i++)
524 	    vim_free(locales[i]);
525 	VIM_CLEAR(locales);
526     }
527 }
528 # endif
529 
530 /*
531  * Function given to ExpandGeneric() to obtain the possible arguments of the
532  * ":language" command.
533  */
534     char_u *
get_lang_arg(expand_T * xp UNUSED,int idx)535 get_lang_arg(expand_T *xp UNUSED, int idx)
536 {
537     if (idx == 0)
538 	return (char_u *)"messages";
539     if (idx == 1)
540 	return (char_u *)"ctype";
541     if (idx == 2)
542 	return (char_u *)"time";
543     if (idx == 3)
544 	return (char_u *)"collate";
545 
546     init_locales();
547     if (locales == NULL)
548 	return NULL;
549     return locales[idx - 4];
550 }
551 
552 /*
553  * Function given to ExpandGeneric() to obtain the available locales.
554  */
555     char_u *
get_locales(expand_T * xp UNUSED,int idx)556 get_locales(expand_T *xp UNUSED, int idx)
557 {
558     init_locales();
559     if (locales == NULL)
560 	return NULL;
561     return locales[idx];
562 }
563 
564 #endif
565