blob: 7fad9d9fe5658adf982d23ed03ec910f2fe0cb64 [file] [log] [blame]
Bram Moolenaar054f14b2020-07-22 19:11:19 +02001/* 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 *
20get_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 *
63gettext_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
100is_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 *
110get_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 *
149get_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
179set_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
220init_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
268ex_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
400static char_u **locales = NULL; // Array of all available locales
401
402static 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 **
409find_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
507init_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
518free_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 *
535get_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 *
556get_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