The R Project SVN R

Rev

Rev 87916 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 87916 Rev 87928
Line 1... Line 1...
1
/* Determine the user's language preferences.
1
/* Determine the user's language preferences.
2
   Copyright (C) 2004-2007, 2018-2024 Free Software Foundation, Inc.
2
   Copyright (C) 2004-2006 Free Software Foundation, Inc.
3
 
3
 
4
   This program is free software: you can redistribute it and/or modify
4
   This program is free software; you can redistribute it and/or modify it
5
   it under the terms of the GNU Lesser General Public License as published by
5
   under the terms of the GNU Library General Public License as published
6
   the Free Software Foundation; either version 2.1 of the License, or
6
   by the Free Software Foundation; either version 2, or (at your option)
7
   (at your option) any later version.
7
   any later version.
8
 
8
 
9
   This program is distributed in the hope that it will be useful,
9
   This program is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
   GNU Lesser General Public License for more details.
12
   Library General Public License for more details.
13
 
13
 
14
   You should have received a copy of the GNU Lesser General Public License
14
   You should have received a copy of the GNU Library General Public
15
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
15
   License along with this program; if not, write to the Free Software
-
 
16
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-
 
17
   USA.  */
16
 
18
 
17
/* Written by Bruno Haible <bruno@clisp.org>.
19
/* Written by Bruno Haible <bruno@clisp.org>.  */
18
   Win32 code originally by Michele Cicciotti <hackbunny@reactos.com>.  */
-
 
19
 
20
 
20
#ifdef HAVE_CONFIG_H
21
#ifdef HAVE_CONFIG_H
21
# include <config.h>
22
# include <config.h>
22
#endif
23
#endif
23
 
24
 
24
/* Specification.  */
-
 
25
#include "gettextP.h"
-
 
26
 
-
 
27
#include <stdlib.h>
25
#include <stdlib.h>
28
 
26
 
29
#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
27
#if HAVE_CFPREFERENCESCOPYAPPVALUE
30
# include <string.h>
28
# include <string.h>
31
# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES
-
 
32
#  include <CoreFoundation/CFLocale.h>
-
 
33
# elif HAVE_CFPREFERENCESCOPYAPPVALUE
-
 
34
#  include <CoreFoundation/CFPreferences.h>
29
# include <CoreFoundation/CFPreferences.h>
35
# endif
-
 
36
# include <CoreFoundation/CFPropertyList.h>
30
# include <CoreFoundation/CFPropertyList.h>
37
# include <CoreFoundation/CFArray.h>
31
# include <CoreFoundation/CFArray.h>
38
# include <CoreFoundation/CFString.h>
32
# include <CoreFoundation/CFString.h>
39
extern void gl_locale_name_canonicalize (char *name);
33
extern void _nl_locale_name_canonicalize (char *name);
40
#endif
-
 
41
 
-
 
42
#if defined _WIN32
-
 
43
# define WIN32_NATIVE
-
 
44
#endif
-
 
45
 
-
 
46
#ifdef WIN32_NATIVE
-
 
47
# define WIN32_LEAN_AND_MEAN
-
 
48
# include <windows.h>
-
 
49
 
-
 
50
# ifndef MUI_LANGUAGE_NAME
-
 
51
# define MUI_LANGUAGE_NAME 8
-
 
52
# endif
-
 
53
# ifndef STATUS_BUFFER_OVERFLOW
-
 
54
# define STATUS_BUFFER_OVERFLOW 0x80000005
-
 
55
# endif
-
 
56
 
-
 
57
extern void gl_locale_name_canonicalize (char *name);
-
 
58
extern const char *gl_locale_name_from_win32_LANGID (LANGID langid);
-
 
59
extern const char *gl_locale_name_from_win32_LCID (LCID lcid);
-
 
60
 
-
 
61
/* Get the preferences list through the MUI APIs. This works on Windows Vista
-
 
62
   and newer.  */
-
 
63
static const char *
-
 
64
_nl_language_preferences_win32_mui (HMODULE kernel32)
-
 
65
{
-
 
66
  /* BOOL GetUserPreferredUILanguages (DWORD dwFlags,
-
 
67
                                       PULONG pulNumLanguages,
-
 
68
                                       PWSTR pwszLanguagesBuffer,
-
 
69
                                       PULONG pcchLanguagesBuffer);  */
-
 
70
  typedef BOOL (WINAPI *GetUserPreferredUILanguages_func) (DWORD, PULONG, PWSTR, PULONG);
-
 
71
  GetUserPreferredUILanguages_func p_GetUserPreferredUILanguages;
-
 
72
 
-
 
73
  p_GetUserPreferredUILanguages =
-
 
74
   (GetUserPreferredUILanguages_func)
-
 
75
   GetProcAddress (kernel32, "GetUserPreferredUILanguages");
-
 
76
  if (p_GetUserPreferredUILanguages != NULL)
-
 
77
    {
-
 
78
      ULONG num_languages;
-
 
79
      ULONG bufsize;
-
 
80
      BOOL ret;
-
 
81
 
-
 
82
      bufsize = 0;
-
 
83
      ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
-
 
84
                                           &num_languages,
-
 
85
                                           NULL, &bufsize);
-
 
86
      if ((ret || GetLastError () == STATUS_BUFFER_OVERFLOW)
-
 
87
          && bufsize > 0)
-
 
88
        {
-
 
89
          WCHAR *buffer = (WCHAR *) malloc (bufsize * sizeof (WCHAR));
-
 
90
          if (buffer != NULL)
-
 
91
            {
-
 
92
              ret = p_GetUserPreferredUILanguages (MUI_LANGUAGE_NAME,
-
 
93
                                                   &num_languages,
-
 
94
                                                   buffer, &bufsize);
-
 
95
              if (ret)
-
 
96
                {
-
 
97
                  /* Convert the list from NUL-delimited WCHAR[] Win32 locale
-
 
98
                     names to colon-delimited char[] Unix locale names.
-
 
99
                     We assume that all these locale names are in ASCII,
-
 
100
                     nonempty and contain no colons.  */
-
 
101
                  char *languages =
-
 
102
                    (char *) malloc (bufsize + num_languages * 10 + 1);
-
 
103
                  if (languages != NULL)
-
 
104
                    {
-
 
105
                      const WCHAR *p = buffer;
-
 
106
                      char *q = languages;
-
 
107
                      ULONG i;
-
 
108
                      for (i = 0; i < num_languages; i++)
-
 
109
                        {
-
 
110
                          char *q1;
-
 
111
                          char *q2;
-
 
112
 
-
 
113
                          q1 = q;
-
 
114
                          if (i > 0)
-
 
115
                            *q++ = ':';
-
 
116
                          q2 = q;
-
 
117
                          for (; *p != (WCHAR)'\0'; p++)
-
 
118
                            {
-
 
119
                              if ((unsigned char) *p != *p || *p == ':')
-
 
120
                                {
-
 
121
                                  /* A non-ASCII character or a colon inside
-
 
122
                                     the Win32 locale name! Punt.  */
-
 
123
                                  q = q1;
-
 
124
                                  break;
-
 
125
                                }
-
 
126
                              *q++ = (unsigned char) *p;
-
 
127
                            }
-
 
128
                          if (q == q1)
-
 
129
                            /* An unexpected Win32 locale name occurred.  */
-
 
130
                            break;
-
 
131
                          *q = '\0';
-
 
132
                          gl_locale_name_canonicalize (q2);
-
 
133
                          q = q2 + strlen (q2);
-
 
134
                          p++;
-
 
135
                        }
-
 
136
                      *q = '\0';
-
 
137
                      if (q > languages)
-
 
138
                        {
-
 
139
                          free (buffer);
-
 
140
                          return languages;
-
 
141
                        }
-
 
142
                      free (languages);
-
 
143
                    }
-
 
144
                }
-
 
145
              free (buffer);
-
 
146
            }
-
 
147
        }
-
 
148
    }
-
 
149
  return NULL;
-
 
150
}
-
 
151
 
-
 
152
/* Get a preference.  This works on Windows ME and newer.  */
-
 
153
static const char *
-
 
154
_nl_language_preferences_win32_ME (HMODULE kernel32)
-
 
155
{
-
 
156
  /* LANGID GetUserDefaultUILanguage (void);  */
-
 
157
  typedef LANGID (WINAPI *GetUserDefaultUILanguage_func) (void);
-
 
158
  GetUserDefaultUILanguage_func p_GetUserDefaultUILanguage;
-
 
159
 
-
 
160
  p_GetUserDefaultUILanguage =
-
 
161
   (GetUserDefaultUILanguage_func)
-
 
162
   GetProcAddress (kernel32, "GetUserDefaultUILanguage");
-
 
163
  if (p_GetUserDefaultUILanguage != NULL)
-
 
164
    return gl_locale_name_from_win32_LANGID (p_GetUserDefaultUILanguage ());
-
 
165
  return NULL;
-
 
166
}
-
 
167
 
-
 
168
/* Get a preference.  This works on Windows 95 and newer.  */
-
 
169
static const char *
-
 
170
_nl_language_preferences_win32_95 ()
-
 
171
{
-
 
172
  HKEY desktop_resource_locale_key;
-
 
173
 
-
 
174
  if (RegOpenKeyExA (HKEY_CURRENT_USER,
-
 
175
                     "Control Panel\\Desktop\\ResourceLocale",
-
 
176
                     0, KEY_QUERY_VALUE, &desktop_resource_locale_key)
-
 
177
      == NO_ERROR)
-
 
178
    {
-
 
179
      DWORD type;
-
 
180
      BYTE data[8 + 1];
-
 
181
      DWORD data_size = sizeof (data);
-
 
182
      DWORD ret;
-
 
183
 
-
 
184
      ret = RegQueryValueExA (desktop_resource_locale_key, NULL, NULL,
-
 
185
                              &type, data, &data_size);
-
 
186
      RegCloseKey (desktop_resource_locale_key);
-
 
187
 
-
 
188
      if (ret == NO_ERROR)
-
 
189
        {
-
 
190
          /* We expect a string, at most 8 bytes long, that parses as a
-
 
191
             hexadecimal number.  */
-
 
192
          if (type == REG_SZ
-
 
193
              && data_size <= sizeof (data)
-
 
194
              && (data_size < sizeof (data)
-
 
195
                  || data[sizeof (data) - 1] == '\0'))
-
 
196
            {
-
 
197
              LCID lcid;
-
 
198
              char *endp;
-
 
199
              /* Ensure it's NUL terminated.  */
-
 
200
              if (data_size < sizeof (data))
-
 
201
                data[data_size] = '\0';
-
 
202
              /* Parse it as a hexadecimal number.  */
-
 
203
              lcid = strtoul ((char *) data, &endp, 16);
-
 
204
              if (endp > (char *) data && *endp == '\0')
-
 
205
                return gl_locale_name_from_win32_LCID (lcid);
-
 
206
            }
-
 
207
        }
-
 
208
    }
-
 
209
  return NULL;
-
 
210
}
-
 
211
 
-
 
212
/* Get the system's preference.  This can be used as a fallback.  */
-
 
213
static BOOL CALLBACK
-
 
214
ret_first_language (HMODULE h, LPCSTR type, LPCSTR name, WORD lang, LONG_PTR param)
-
 
215
{
-
 
216
  *(const char **)param = gl_locale_name_from_win32_LANGID (lang);
-
 
217
  return FALSE;
-
 
218
}
-
 
219
static const char *
-
 
220
_nl_language_preferences_win32_system (HMODULE kernel32)
-
 
221
{
-
 
222
  const char *languages = NULL;
-
 
223
  /* Ignore the warning on mingw here. mingw has a wrong definition of the last
-
 
224
     parameter type of ENUMRESLANGPROC.  */
-
 
225
  EnumResourceLanguages (kernel32, RT_VERSION, MAKEINTRESOURCE (1),
-
 
226
                         ret_first_language, (LONG_PTR)&languages);
-
 
227
  return languages;
-
 
228
}
-
 
229
 
-
 
230
#endif
34
#endif
231
 
35
 
232
/* Determine the user's language preferences, as a colon separated list of
36
/* Determine the user's language preferences, as a colon separated list of
233
   locale names in XPG syntax
37
   locale names in XPG syntax
234
     language[_territory][.codeset][@modifier]
38
     language[_territory][.codeset][@modifier]
Line 237... Line 41...
237
   already taken into account by the caller.  */
41
   already taken into account by the caller.  */
238
 
42
 
239
const char *
43
const char *
240
_nl_language_preferences_default (void)
44
_nl_language_preferences_default (void)
241
{
45
{
242
#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
-
 
243
  /* MacOS X 10.4 or newer */
46
#if HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.2 or newer */
244
  {
47
  {
245
    /* Cache the preferences list, since CoreFoundation calls are expensive.  */
48
    /* Cache the preferences list, since CoreFoundation calls are expensive.  */
246
    static const char *cached_languages;
49
    static const char *cached_languages;
247
    static int cache_initialized;
50
    static int cache_initialized;
248
 
51
 
249
    if (!cache_initialized)
52
    if (!cache_initialized)
250
      {
53
      {
251
# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES /* MacOS X 10.5 or newer */
54
	CFTypeRef preferences =
252
        CFArrayRef prefArray = CFLocaleCopyPreferredLanguages ();
55
	  CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
253
# elif HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.4 or newer */
56
				     kCFPreferencesCurrentApplication);
254
        CFTypeRef preferences =
57
	if (preferences != NULL
255
          CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"),
58
	    && CFGetTypeID (preferences) == CFArrayGetTypeID ())
256
                                     kCFPreferencesCurrentApplication);
59
	  {
257
        if (preferences != NULL
60
	    CFArrayRef prefArray = (CFArrayRef)preferences;
258
            && CFGetTypeID (preferences) == CFArrayGetTypeID ())
61
	    long n = CFArrayGetCount (prefArray);
259
          {
62
	    char buf[256];
260
            CFArrayRef prefArray = (CFArrayRef)preferences;
63
	    size_t size = 0;
261
# endif
64
	    int i;
262
 
65
 
263
            int n = (int) CFArrayGetCount (prefArray);
66
	    for (i = 0; i < n; i++)
264
            char buf[256];
67
	      {
265
            char buf2[256];
68
		CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i);
266
            size_t size = 0;
69
		if (element != NULL
267
            int i;
70
		    && CFGetTypeID (element) == CFStringGetTypeID ()
268
 
71
		    && CFStringGetCString ((CFStringRef)element,
269
            for (i = 0; i < n; i++)
72
					   buf, sizeof (buf),
270
              {
73
					   kCFStringEncodingASCII))
271
                CFTypeRef element = CFArrayGetValueAtIndex (prefArray, i);
74
		  {
272
                if (element != NULL
75
		    _nl_locale_name_canonicalize (buf);
273
                    && CFGetTypeID (element) == CFStringGetTypeID ()
76
		    size += strlen (buf) + 1;
274
                    && CFStringGetCString ((CFStringRef)element,
77
		    /* Most GNU programs use msgids in English and don't ship
275
                                           buf, sizeof (buf),
78
		       an en.mo message catalog.  Therefore when we see "en"
276
                                           kCFStringEncodingASCII))
79
		       in the preferences list, arrange for gettext() to
277
                  {
80
		       return the msgid, and ignore all further elements of
278
                    strcpy (buf2, buf);
81
		       the preferences list.  */
279
                    gl_locale_name_canonicalize (buf);
82
		    if (strcmp (buf, "en") == 0)
280
                    size += strlen (buf) + 1;
83
		      break;
281
                    /* Mac OS X 10.12 or newer returns an array of elements of
84
		  }
282
                       the form "ll-CC" or "ll-Scrp-CC" where ll is a language
85
		else
283
                       code, CC is a country code, and Scrp (optional) is a
86
		  break;
284
                       script code.
87
	      }
285
                       gl_locale_name_canonicalize converts this to "ll_CC" or
88
	    if (size > 0)
286
                       "ll_Scrp_CC".
89
	      {
287
                       Sometimes ll and CC are unrelated, i.e. there is no
90
		char *languages = (char *) malloc (size);
288
                       translation for "ll_CC" but one for "ll".
91
 
289
                       Similarly, in the case with a script, sometimes there is
92
		if (languages != NULL)
290
                       no translation for "ll_Scrp_CC" but one for "ll_Scrp"
93
		  {
291
                       (after proper canonicalization).
94
		    char *p = languages;
292
                       Therefore, in the result, we return "ll_CC" followed
95
 
293
                       by "ll", or similarly for the case with a script.  */
96
		    for (i = 0; i < n; i++)
294
                    {
97
		      {
295
                      char *last_minus = strrchr (buf2, '-');
98
			CFTypeRef element =
296
                      if (last_minus != NULL)
99
			  CFArrayGetValueAtIndex (prefArray, i);
297
                        {
100
			if (element != NULL
298
                          *last_minus = '\0';
101
		            && CFGetTypeID (element) == CFStringGetTypeID ()
299
                          gl_locale_name_canonicalize (buf2);
102
			    && CFStringGetCString ((CFStringRef)element,
300
                          size += strlen (buf2) + 1;
103
						   buf, sizeof (buf),
301
                        }
104
						   kCFStringEncodingASCII))
302
                    }
105
			  {
303
                    /* Most GNU programs use msgids in English and don't ship
106
			    _nl_locale_name_canonicalize (buf);
304
                       an en.mo message catalog.  Therefore when we see "en" or
107
			    strcpy (p, buf);
305
                       "en-CC" in the preferences list, arrange for gettext()
108
			    p += strlen (buf);
306
                       to return the msgid, and ignore all further elements of
109
			    *p++ = ':';
307
                       the preferences list.  */
110
			    if (strcmp (buf, "en") == 0)
308
                    if (buf[0] == 'e' && buf[1] == 'n'
111
			      break;
309
                        && (buf[2] == '\0' || buf[2] == '_'))
112
			  }
310
                      break;
113
			else
311
                  }
114
			  break;
312
                else
115
		      }
313
                  break;
116
		    *--p = '\0';
314
              }
117
 
315
            if (size > 0)
118
		    cached_languages = languages;
316
              {
119
		  }
317
                char *languages = (char *) malloc (size);
120
	      }
318
 
121
	  }
319
                if (languages != NULL)
122
	cache_initialized = 1;
320
                  {
-
 
321
                    char *p = languages;
-
 
322
 
-
 
323
                    for (i = 0; i < n; i++)
-
 
324
                      {
-
 
325
                        CFTypeRef element =
-
 
326
                          CFArrayGetValueAtIndex (prefArray, i);
-
 
327
                        if (element != NULL
-
 
328
                            && CFGetTypeID (element) == CFStringGetTypeID ()
-
 
329
                            && CFStringGetCString ((CFStringRef)element,
-
 
330
                                                   buf, sizeof (buf),
-
 
331
                                                   kCFStringEncodingASCII))
-
 
332
                          {
-
 
333
                            strcpy (buf2, buf);
-
 
334
                            gl_locale_name_canonicalize (buf);
-
 
335
                            strcpy (p, buf);
-
 
336
                            p += strlen (buf);
-
 
337
                            *p++ = ':';
-
 
338
                            {
-
 
339
                              char *last_minus = strrchr (buf2, '-');
-
 
340
                              if (last_minus != NULL)
-
 
341
                                {
-
 
342
                                  *last_minus = '\0';
-
 
343
                                  gl_locale_name_canonicalize (buf2);
-
 
344
                                  strcpy (p, buf2);
-
 
345
                                  p += strlen (buf2);
-
 
346
                                  *p++ = ':';
-
 
347
                                }
-
 
348
                            }
-
 
349
                            if (buf[0] == 'e' && buf[1] == 'n'
-
 
350
                                 && (buf[2] == '\0' || buf[2] == '_'))
-
 
351
                              break;
-
 
352
                          }
-
 
353
                        else
-
 
354
                          break;
-
 
355
                      }
-
 
356
                    *--p = '\0';
-
 
357
 
-
 
358
                    cached_languages = languages;
-
 
359
                  }
-
 
360
              }
-
 
361
 
-
 
362
# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES /* MacOS X 10.5 or newer */
-
 
363
        CFRelease (prefArray);
-
 
364
# elif HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.4 or newer */
-
 
365
          }
-
 
366
# endif
-
 
367
        cache_initialized = 1;
-
 
368
      }
-
 
369
    if (cached_languages != NULL)
-
 
370
      return cached_languages;
-
 
371
  }
-
 
372
#endif
-
 
373
 
-
 
374
#ifdef WIN32_NATIVE
-
 
375
  {
-
 
376
    /* Cache the preferences list, since computing it is expensive.  */
-
 
377
    static const char *cached_languages;
-
 
378
    static int cache_initialized;
-
 
379
 
-
 
380
    /* Activate the new code only when the GETTEXT_MUI environment variable is
-
 
381
       set, for the time being, since the new code is not well tested.  */
-
 
382
    if (!cache_initialized && getenv ("GETTEXT_MUI") != NULL)
-
 
383
      {
-
 
384
        const char *languages = NULL;
-
 
385
        HMODULE kernel32 = GetModuleHandle ("kernel32");
-
 
386
 
-
 
387
        if (kernel32 != NULL)
-
 
388
          languages = _nl_language_preferences_win32_mui (kernel32);
-
 
389
 
-
 
390
        if (languages == NULL && kernel32 != NULL)
-
 
391
          languages = _nl_language_preferences_win32_ME (kernel32);
-
 
392
 
-
 
393
        if (languages == NULL)
-
 
394
          languages = _nl_language_preferences_win32_95 ();
-
 
395
 
-
 
396
        if (languages == NULL && kernel32 != NULL)
-
 
397
          languages = _nl_language_preferences_win32_system (kernel32);
-
 
398
 
-
 
399
        cached_languages = languages;
-
 
400
        cache_initialized = 1;
-
 
401
      }
123
      }
402
    if (cached_languages != NULL)
124
    if (cached_languages != NULL)
403
      return cached_languages;
125
      return cached_languages;
404
  }
126
  }
405
#endif
127
#endif