1 /* Choice of user interface language, and internationalization. */
2
3 /* -------------------------- Specification ---------------------------- */
4
5 #ifdef GNU_GETTEXT
6
7 /* Initializes the current interface language, according to the given
8 arguments, getting the defaults from environment variables. */
9 global void init_language (const char* argv_language, const char* argv_localedir, bool lisp_error_p);
10
11 /* Returns the translation of msgid according to the current interface
12 language. */
13 global const char * clgettext (const char * msgid);
14
15 #endif
16
17 /* Returns the translation of string according to the current interface
18 language. A string is returned.
19 can trigger GC */
20 global object CLSTEXT (const char*);
21
22 /* Returns the translated value of obj. obj is translated,
23 then READ-FROM-STRING is applied to the result.
24 can trigger GC */
25 global object CLOTEXT (const char*);
26
27
28 /* -------------------------- Implementation --------------------------- */
29
30 #ifdef GNU_GETTEXT
31
32 /* language, that is used for communication with the user: */
33 static enum {
34 language_english,
35 language_german,
36 language_french,
37 language_spanish,
38 language_dutch,
39 language_russian,
40 language_danish,
41 language_swedish
42 } language;
43
44 /* Initializes the language, given the language name. */
45 local bool init_language_from (const char* langname);
46
current_language_o(void)47 global object current_language_o (void) {
48 switch (language) {
49 case language_english: { return S(english); }
50 case language_german: { return S(german); }
51 case language_french: { return S(french); }
52 case language_spanish: { return S(spanish); }
53 case language_dutch: { return S(dutch); }
54 case language_russian: { return S(russian); }
55 case language_danish: { return S(danish); }
56 case language_swedish: { return S(swedish); }
57 default: NOTREACHED;
58 }
59 }
60
init_language_from(const char * langname)61 local bool init_language_from (const char* langname) {
62 if (NULL == langname) return false;
63 if (asciz_equal(langname,"ENGLISH") || asciz_equal(langname,"english")) {
64 language = language_english; return true;
65 }
66 if (asciz_equal(langname,"DEUTSCH") || asciz_equal(langname,"deutsch")
67 || asciz_equal(langname,"GERMAN") || asciz_equal(langname,"german")) {
68 language = language_german; return true;
69 }
70 if (asciz_equal(langname,"FRANCAIS") || asciz_equal(langname,"francais")
71 #ifndef ASCII_CHS
72 || asciz_equal(langname,"FRAN\307AIS") || asciz_equal(langname,"FRAN\303\207AIS") /* FRENCH */
73 || asciz_equal(langname,"fran\347ais") || asciz_equal(langname,"fran\303\247ais") /* french */
74 #endif
75 || asciz_equal(langname,"FRENCH") || asciz_equal(langname,"french")) {
76 language = language_french; return true;
77 }
78 if (asciz_equal(langname,"ESPANOL") || asciz_equal(langname,"espanol")
79 #ifndef ASCII_CHS
80 || asciz_equal(langname,"ESPA\321OL") || asciz_equal(langname,"ESPA\303\221OL") /* SPANISH */
81 || asciz_equal(langname,"espa\361ol") || asciz_equal(langname,"espa\303\261ol") /* spanish */
82 #endif
83 || asciz_equal(langname,"SPANISH") || asciz_equal(langname,"spanish")) {
84 language = language_spanish; return true;
85 }
86 if (asciz_equal(langname,"russian") || asciz_equal(langname,"RUSSIAN")
87 #ifndef ASCII_CHS
88 || asciz_equal(langname,"\320\240\320\243\320\241\320\241\320\232\320\230\320\231")
89 || asciz_equal(langname,"\321\200\321\203\321\201\321\201\320\272\320\270\320\271")
90 || asciz_equal(langname,"\240\243\241\241\232\230\231")
91 || asciz_equal(langname,"\200\203\201\201\272\270\271")
92 #endif
93 ) {
94 language = language_russian; return true;
95 }
96 if (asciz_equal(langname,"NEDERLANDS") || asciz_equal(langname,"nederlands")
97 || asciz_equal(langname,"DUTCH") || asciz_equal(langname,"dutch")) {
98 language = language_dutch; return true;
99 }
100 if (asciz_equal(langname,"DANSK") || asciz_equal(langname,"dansk")
101 || asciz_equal(langname,"DANISH") || asciz_equal(langname,"danish")) {
102 language = language_danish; return true;
103 }
104 if (asciz_equal(langname,"SVENSKA") || asciz_equal(langname,"svenska")
105 || asciz_equal(langname,"SWEDISH") || asciz_equal(langname,"swedish")) {
106 language = language_swedish; return true;
107 }
108 return false;
109 }
110
111 /* Initializes the language. */
init_language(const char * argv_language,const char * argv_localedir,bool lisp_error_p)112 global void init_language
113 (const char* argv_language, const char* argv_localedir, bool lisp_error_p) {
114 #define ANSIC_ERROR(f,a) if (lisp_error_p) ANSIC_error(); else { \
115 fprintf(stderr,GETTEXT("WARNING: %s/%s: %s.\n"),f,a,strerror(errno)); \
116 goto init_language_failure; \
117 }
118 #define MY_NOTREACHED if (lisp_error_p) NOTREACHED; else abort()
119 /* language is set with priorities in this order:
120 1. -L command line argument
121 2. environment-variable CLISP_LANGUAGE
122 3. environment-variable LANG
123 4. default: English */
124 if (argv_language == NULL) {
125 /* noop */
126 } else if (init_language_from(argv_language)
127 || init_language_from(getenv("CLISP_LANGUAGE"))) {
128 /* At this point we have chosen the language based upon the
129 command-line option or the clisp-specific environment variables. */
130 /* GNU gettext chooses the message catalog based upon:
131 1. environment variable LANGUAGE [only if dcgettext.c, not with
132 cat-compat.c],
133 2. environment variable LC_ALL,
134 3. environment variable LC_MESSAGES,
135 4. environment variable LANG.
136 We clobber LC_MESSAGES and unset the earlier two variables. */
137 var const char *locale1, *locale2;
138 switch (language) {
139 case language_english: locale1 = "en_US"; locale2 = "en_US.utf8"; break;
140 case language_german: locale1 = "de_DE"; locale2 = "de_DE.utf8"; break;
141 case language_french: locale1 = "fr_FR"; locale2 = "fr_FR.utf8"; break;
142 case language_spanish: locale1 = "es_ES"; locale2 = "es_ES.utf8"; break;
143 case language_dutch: locale1 = "nl_NL"; locale2 = "nl_NL.utf8"; break;
144 case language_russian: locale1 = "ru_RU"; locale2 = "ru_RU.utf8"; break;
145 case language_danish: locale1 = "da_DK"; locale2 = "da_DK.utf8"; break;
146 case language_swedish: locale1 = "sv_SE"; locale2 = "sv_SE.utf8"; break;
147 default: MY_NOTREACHED;
148 }
149 if (getenv("LANGUAGE") && unsetenv("LANGUAGE")) {
150 ANSIC_ERROR("unsetenv","LANGUAGE");
151 }
152 if (getenv("LC_ALL") && unsetenv("LC_ALL")) {
153 ANSIC_ERROR("unsetenv","LC_ALL");
154 }
155 if (NULL == setlocale(LC_MESSAGES,locale2)) {
156 if (NULL == setlocale(LC_MESSAGES,locale1)) {
157 if (lisp_error_p) {
158 pushSTACK(ascii_to_string(locale1));
159 pushSTACK(ascii_to_string(locale2));
160 pushSTACK(TheSubr(subr_self)->name);
161 error(error_condition,GETTEXT("~S: locales ~S and ~S are not installed on this system"));
162 } else {
163 fprintf(stderr,GETTEXT("locales %s and %s are not installed on this system\n"),locale1,locale2);
164 goto init_language_failure;
165 }
166 } else if (setenv("LC_MESSAGES",locale1,1)) {
167 ANSIC_ERROR("setenv/LC_MESSAGES",locale1);
168 }
169 } else if (setenv("LC_MESSAGES",locale2,1)) {
170 ANSIC_ERROR("setenv/LC_MESSAGES",locale2);
171 }
172 { /* Invalidate the gettext internal caches. */
173 char *td = textdomain(NULL);
174 if (NULL == td) {
175 ANSIC_ERROR("textdomain",NULL);
176 }
177 if (NULL == textdomain(td)) {
178 ANSIC_ERROR("textdomain",td);
179 }
180 }
181 } else if (lisp_error_p) {
182 pushSTACK(ascii_to_string(argv_language));
183 pushSTACK(TheSubr(subr_self)->name);
184 error(error_condition,GETTEXT("~S: invalid language ~S"));
185 } else {
186 fprintf(stderr,"invalid language %s.\n",argv_language);
187 goto init_language_failure;
188 }
189 /* At this point we have chosen the language based upon an
190 environment variable GNU gettext knows about.
191 argv_localedir=NULL usually means (setq *current-language* ...)
192 in which case reusing text domain from the original (or previous)
193 call to bindtextdomain() is a wise choice */
194 if (argv_localedir) { /* make sure that it exists and is a directory */
195 char truename[MAXPATHLEN];
196 switch (classify_namestring(argv_localedir,truename,NULL,NULL)) {
197 case FILE_KIND_FILE:
198 if (lisp_error_p) {
199 pushSTACK(ascii_to_string(truename));
200 pushSTACK(ascii_to_string(argv_localedir));
201 pushSTACK(TheSubr(subr_self)->name);
202 error(error_condition,GETTEXT("~S: ~S resolves to ~S which is a file, not a directory"));
203 } else {
204 fprintf(stderr,GETTEXT("%s resolves o %s which is a file, not a directory\n"),argv_localedir,truename);
205 goto init_language_failure;
206 }
207 MY_NOTREACHED;
208 case FILE_KIND_NONE: case FILE_KIND_BAD:
209 if (lisp_error_p) {
210 pushSTACK(ascii_to_string(argv_localedir));
211 pushSTACK(TheSubr(subr_self)->name);
212 error(error_condition,GETTEXT("~S: ~S does not exist"));
213 } else {
214 fprintf(stderr,GETTEXT("%s does not exist\n"),argv_localedir);
215 goto init_language_failure;
216 }
217 MY_NOTREACHED;
218 case FILE_KIND_DIR:
219 if (NULL == bindtextdomain("clisp",truename)) {
220 ANSIC_ERROR("bindtextdomain/clisp",truename);
221 }
222 if (NULL == bindtextdomain("clisplow",truename)) {
223 ANSIC_ERROR("bindtextdomain/clisplow",truename);
224 }
225 }
226 }
227 #ifdef ENABLE_UNICODE
228 if (NULL == bind_textdomain_codeset("clisp","UTF-8")) {
229 ANSIC_ERROR("bind_textdomain_codeset","UTF-8");
230 }
231 #endif
232 return;
233 init_language_failure:
234 fprintf(stderr,GETTEXT("WARNING: setting language to %s failed.\n"),argv_language);
235 #undef ANSIC_ERROR
236 #undef MY_NOTREACHED
237 }
238
clisp_gettext(const char * domain,const char * msgid)239 local const char * clisp_gettext (const char * domain, const char * msgid) {
240 var const char * translated_msg;
241 if (msgid[0] == '\0') {
242 /* If you ask gettext to translate the empty string, it returns
243 the catalog's header (containing meta information)! */
244 translated_msg = msgid;
245 } else {
246 begin_system_call();
247 translated_msg = dgettext(domain,msgid);
248 end_system_call();
249 }
250 return translated_msg;
251 }
252
253 /* High-level messages, which are converted to Lisp strings, are
254 stored in a separate catalog and returned in the UTF-8 encoding. */
clgettext(const char * msgid)255 modexp const char * clgettext (const char * msgid)
256 { return clisp_gettext("clisp", msgid); }
257
258 /* Low-level messages, which are output through fprintf(3), are
259 stored in a separate catalog and returned in locale encoding. */
clgettextl(const char * msgid)260 global const char * clgettextl (const char * msgid)
261 { return clisp_gettext("clisplow", msgid); }
262
263 #else
264 #define clgettext(m) m /* for CLSTEXT below */
265 #endif
266
267 /* FIXME: Don't hardwire ISO-8859-1. The catalog's character set is
268 given by the "Content-Type:" line in the meta information.
269 in anticipation of this fix, CLSTEXT is a function, not a macro */
CLSTEXT(const char * asciz)270 modexp maygc object CLSTEXT (const char* asciz) {
271 return asciz_to_string(clgettext(asciz),Symbol_value(S(utf_8)));
272 }
273
CLOTEXT(const char * asciz)274 global maygc object CLOTEXT (const char* asciz) {
275 dynamic_bind(S(packagestar),O(default_package)); /* bind *PACKAGE* */
276 pushSTACK(CLSTEXT(asciz)); funcall(L(read_from_string),1);
277 dynamic_unbind(S(packagestar));
278 return value1;
279 }
280