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 maketitle();
394 }
395 }
396 }
397
398 static char_u **locales = NULL; // Array of all available locales
399
400 static int did_init_locales = FALSE;
401
402 /*
403 * Return an array of strings for all available locales + NULL for the
404 * last element. Return NULL in case of error.
405 */
406 static char_u **
find_locales(void)407 find_locales(void)
408 {
409 garray_T locales_ga;
410 char_u *loc;
411 char_u *locale_list;
412 # ifdef MSWIN
413 size_t len = 0;
414 # endif
415
416 // Find all available locales by running command "locale -a". If this
417 // doesn't work we won't have completion.
418 # ifndef MSWIN
419 locale_list = get_cmd_output((char_u *)"locale -a",
420 NULL, SHELL_SILENT, NULL);
421 # else
422 // Find all available locales by examining the directories in
423 // $VIMRUNTIME/lang/
424 {
425 int options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL;
426 expand_T xpc;
427 char_u *p;
428
429 ExpandInit(&xpc);
430 xpc.xp_context = EXPAND_DIRECTORIES;
431 locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*",
432 NULL, options, WILD_ALL);
433 ExpandCleanup(&xpc);
434 if (locale_list == NULL)
435 // Add a dummy input, that will be skipped lated but we need to
436 // have something in locale_list so that the C locale is added at
437 // the end.
438 locale_list = vim_strsave((char_u *)".\n");
439 p = locale_list;
440 // find the last directory delimiter
441 while (p != NULL && *p != NUL)
442 {
443 if (*p == '\n')
444 break;
445 if (*p == '\\')
446 len = p - locale_list;
447 p++;
448 }
449 }
450 # endif
451 if (locale_list == NULL)
452 return NULL;
453 ga_init2(&locales_ga, sizeof(char_u *), 20);
454
455 // Transform locale_list string where each locale is separated by "\n"
456 // into an array of locale strings.
457 loc = (char_u *)strtok((char *)locale_list, "\n");
458
459 while (loc != NULL)
460 {
461 int ignore = FALSE;
462
463 # ifdef MSWIN
464 if (len > 0)
465 loc += len + 1;
466 // skip locales with a dot (which indicates the charset)
467 if (vim_strchr(loc, '.') != NULL)
468 ignore = TRUE;
469 # endif
470 if (!ignore)
471 {
472 if (ga_grow(&locales_ga, 1) == FAIL)
473 break;
474
475 loc = vim_strsave(loc);
476 if (loc == NULL)
477 break;
478
479 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
480 }
481 loc = (char_u *)strtok(NULL, "\n");
482 }
483
484 # ifdef MSWIN
485 // Add the C locale
486 if (ga_grow(&locales_ga, 1) == OK)
487 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] =
488 vim_strsave((char_u *)"C");
489 # endif
490
491 vim_free(locale_list);
492 if (ga_grow(&locales_ga, 1) == FAIL)
493 {
494 ga_clear(&locales_ga);
495 return NULL;
496 }
497 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
498 return (char_u **)locales_ga.ga_data;
499 }
500
501 /*
502 * Lazy initialization of all available locales.
503 */
504 static void
init_locales(void)505 init_locales(void)
506 {
507 if (!did_init_locales)
508 {
509 did_init_locales = TRUE;
510 locales = find_locales();
511 }
512 }
513
514 # if defined(EXITFREE) || defined(PROTO)
515 void
free_locales(void)516 free_locales(void)
517 {
518 int i;
519 if (locales != NULL)
520 {
521 for (i = 0; locales[i] != NULL; i++)
522 vim_free(locales[i]);
523 VIM_CLEAR(locales);
524 }
525 }
526 # endif
527
528 /*
529 * Function given to ExpandGeneric() to obtain the possible arguments of the
530 * ":language" command.
531 */
532 char_u *
get_lang_arg(expand_T * xp UNUSED,int idx)533 get_lang_arg(expand_T *xp UNUSED, int idx)
534 {
535 if (idx == 0)
536 return (char_u *)"messages";
537 if (idx == 1)
538 return (char_u *)"ctype";
539 if (idx == 2)
540 return (char_u *)"time";
541 if (idx == 3)
542 return (char_u *)"collate";
543
544 init_locales();
545 if (locales == NULL)
546 return NULL;
547 return locales[idx - 4];
548 }
549
550 /*
551 * Function given to ExpandGeneric() to obtain the available locales.
552 */
553 char_u *
get_locales(expand_T * xp UNUSED,int idx)554 get_locales(expand_T *xp UNUSED, int idx)
555 {
556 init_locales();
557 if (locales == NULL)
558 return NULL;
559 return locales[idx];
560 }
561
562 #endif
563