mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
NSUserDefaults: Retrieve native UI languages from Windows and Android System APIs (#426)
* Create NSString Win32Additions Category * NSUserDefaults: Fetch Windows UI language information * NSUserDefaults: Add winnls include * Android Native UI Language * NSUserDefaults: Replace incorrect separator on Windows * NSProcessInfo: BCP-47 Note * GSConfig: Bump MinGW WINVER to Vista * NSString+Win32Additions: Do not add array to arp twice * NSUserDefaults: Increase default length
This commit is contained in:
parent
726777bf6e
commit
81b3c721bc
6 changed files with 229 additions and 4 deletions
|
@ -304,7 +304,7 @@ typedef struct {
|
|||
|
||||
# if defined(__MINGW__)
|
||||
# include <w32api.h>
|
||||
# define GS_WINVER Windows2000
|
||||
# define GS_WINVER WindowsVista
|
||||
# elif defined(_MSC_VER)
|
||||
# include <WinSDKVer.h>
|
||||
# define GS_WINVER _WIN32_WINNT_WIN10
|
||||
|
|
|
@ -1605,7 +1605,45 @@ GSInitializeProcessAndroid(JNIEnv *env, jobject context)
|
|||
}
|
||||
}
|
||||
|
||||
// get current time zone
|
||||
char *localeList = NULL;
|
||||
#if __ANDROID_API__ >= 24
|
||||
// get locales ordered by user preference
|
||||
jclass localeListCls = (*env)->FindClass(env, "android/os/LocaleList");
|
||||
jmethodID localeListGetDefaultMethod = (*env)->GetStaticMethodID(env, localeListCls, "getDefault", "()Landroid/os/LocaleList;");
|
||||
jobject localeListObj = (*env)->CallStaticObjectMethod(env, localeListCls, localeListGetDefaultMethod);
|
||||
|
||||
if (localeCls) {
|
||||
// Retrieve string representation of the locale list
|
||||
jmethodID localeListToLanguageTagsMethod = (*env)->GetMethodID(env, localeListCls, "toLanguageTags", "()Ljava/lang/String;");
|
||||
jstring localeListJava = (*env)->CallObjectMethod(env, localeListObj, localeListToLanguageTagsMethod);
|
||||
|
||||
if (localeListJava) {
|
||||
const char *localeListOrig = (*env)->GetStringUTFChars(env, localeIdJava, NULL);
|
||||
|
||||
// Some devices return with it enclosed in []'s so check if both exists before
|
||||
// removing to ensure it is formatted correctly
|
||||
if (localeListOrig[0] == '[' && localeListOrig[strlen(localeListOrig) - 1] == ']') {
|
||||
localeList = strdup(localeListOrig + 1);
|
||||
localeList[strlen(localeList) - 1] = '\0';
|
||||
} else {
|
||||
localeList = strdup(localeListOrig);
|
||||
}
|
||||
|
||||
// NOTE: This is an IETF BCP 47 language tag and may not correspond exactly tocorrespond ll-CC format
|
||||
// e.g. gsw-u-sd-chzh is a valid BCP 47 language tag, but uses an ISO 639-3 subtag to classify the language.
|
||||
// There is no easy fix to this, as we use ISO 639-2 subtags internally.
|
||||
for (int i = 0; localeList[i]; i++) {
|
||||
if (localeList[i] == '-') {
|
||||
localeList[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, localeListJava, localeListOrig);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
jclass timezoneCls = (*env)->FindClass(env, "java/util/TimeZone");
|
||||
jmethodID timezoneDefaultMethod = (*env)->GetStaticMethodID(env, timezoneCls, "getDefault", "()Ljava/util/TimeZone;");
|
||||
jmethodID timezoneIdMethod = (*env)->GetMethodID(env, timezoneCls, "getID", "()Ljava/lang/String;");
|
||||
|
@ -1613,20 +1651,28 @@ GSInitializeProcessAndroid(JNIEnv *env, jobject context)
|
|||
jstring timezoneIdJava = (*env)->CallObjectMethod(env, timezoneObj, timezoneIdMethod);
|
||||
const char *timezoneId = (*env)->GetStringUTFChars(env, timezoneIdJava, NULL);
|
||||
|
||||
char *localeListValue = "";
|
||||
if (localeList) {
|
||||
localeListValue = localeList;
|
||||
}
|
||||
|
||||
// initialize process with these options
|
||||
char *argv[] = {
|
||||
arg0,
|
||||
"-Locale", localeId,
|
||||
"-Local Time Zone", (char *)timezoneId,
|
||||
"-GSAndroidLocaleList", localeListValue,
|
||||
"-GSLogSyslog", "YES" // use syslog (available via logcat) instead of stdout/stderr (not available on Android)
|
||||
};
|
||||
|
||||
GSInitializeProcessAndroidWithArgs(env, context, sizeof(argv)/sizeof(char *), argv, NULL);
|
||||
|
||||
free(arg0);
|
||||
free(localeId);
|
||||
free(localeList);
|
||||
(*env)->ReleaseStringUTFChars(env, packageCodePathJava, packageCodePath);
|
||||
(*env)->ReleaseStringUTFChars(env, packageNameJava, packageName);
|
||||
(*env)->ReleaseStringUTFChars(env, localeIdJava, localeId);
|
||||
(*env)->ReleaseStringUTFChars(env, localeIdJava, localeIdOrig);
|
||||
(*env)->ReleaseStringUTFChars(env, timezoneIdJava, timezoneId);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,10 @@
|
|||
#import "GNUstepBase/NSString+GNUstepBase.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#import "win32/NSString+Win32Additions.h"
|
||||
|
||||
#include <winnls.h>
|
||||
|
||||
/* Fake interface to avoid compiler warnings
|
||||
*/
|
||||
@interface NSUserDefaultsWin32 : NSUserDefaults
|
||||
|
@ -355,6 +359,94 @@ systemLanguages()
|
|||
{
|
||||
NSMutableArray *names = [NSMutableArray arrayWithCapacity: 10];
|
||||
|
||||
#ifdef WIN32
|
||||
NSEnumerator *enumerator;
|
||||
NSArray *languages;
|
||||
NSString *locale;
|
||||
BOOL ret;
|
||||
|
||||
unsigned long numberOfLanguages = 0;
|
||||
unsigned long length = 7;
|
||||
unsigned long factor = sizeof(wchar_t);
|
||||
wchar_t *buffer = malloc(length * factor);
|
||||
if (!buffer)
|
||||
{
|
||||
return names;
|
||||
}
|
||||
|
||||
/* Returns a wchar_t list of languages in the form ll-CC, where ll is the
|
||||
* two-letter language code, and CC is the two-letter country code.
|
||||
*/
|
||||
ret = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages,
|
||||
buffer, &length);
|
||||
if (!ret)
|
||||
{
|
||||
length = 0;
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
|
||||
GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages,
|
||||
NULL, &length)) {
|
||||
wchar_t *oldBuffer = buffer;
|
||||
buffer = realloc(buffer, length * factor);
|
||||
if (!buffer)
|
||||
{
|
||||
free(oldBuffer);
|
||||
return names;
|
||||
}
|
||||
|
||||
ret = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, buffer, &length);
|
||||
if (!ret)
|
||||
{
|
||||
free(buffer);
|
||||
return names;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
languages = [NSString arrayFromWCharList:buffer length:length];
|
||||
enumerator = [languages objectEnumerator];
|
||||
free(buffer);
|
||||
|
||||
while (nil != (locale = [enumerator nextObject]))
|
||||
{
|
||||
/* Replace "-" Separator with "_" */
|
||||
locale = [locale stringByReplacingOccurrencesOfString:@"-" withString:@"_"];
|
||||
[names addObjectsFromArray: GSLanguagesFromLocale(locale)];
|
||||
}
|
||||
#elif defined(__ANDROID__)
|
||||
// When running on Android, the process must be correctly initialized
|
||||
// with GSInitializeProcessAndroid (See NSProcessInfo).
|
||||
//
|
||||
// If the minimum API level is 24 or higher, the user-prefered locales
|
||||
// are retrieved from the Android system and passed as GSAndroidLocaleList
|
||||
// process argument
|
||||
NSArray *args = [[NSProcessInfo processInfo] arguments];
|
||||
NSEnumerator *enumerator = [args objectEnumerator];
|
||||
NSString *key = nil;
|
||||
NSString *localeList = nil;
|
||||
|
||||
[enumerator nextObject]; // Skip process name.
|
||||
while (nil != (key = [enumerator nextObject]))
|
||||
{
|
||||
if ([key isEqualToString:@"-GSAndroidLocaleList"])
|
||||
{
|
||||
localeList = [enumerator nextObject];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The locale list is a comma-separated list of locales of form ll-CC
|
||||
if (localeList != nil)
|
||||
{
|
||||
NSString *locale;
|
||||
NSArray *locales = [localeList componentsSeparatedByString: @","];
|
||||
|
||||
enumerator = [locales objectEnumerator];
|
||||
while (nil != (locale = [enumerator nextObject]))
|
||||
{
|
||||
[names addObjectsFromArray: GSLanguagesFromLocale(locale)];
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Add the languages listed in the LANGUAGE environment variable
|
||||
// (a non-POSIX GNU extension)
|
||||
{
|
||||
|
@ -371,7 +463,7 @@ systemLanguages()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If LANGUAGES did not yield any languages, try LC_MESSAGES
|
||||
|
||||
if ([names count] == 0)
|
||||
|
@ -383,6 +475,7 @@ systemLanguages()
|
|||
[names addObjectsFromArray: GSLanguagesFromLocale(locale)];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return names;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ win32_OBJC_FILES =\
|
|||
NSMessagePortNameServer.m \
|
||||
NSStream.m \
|
||||
NSUserDefaults.m \
|
||||
NSString+Win32Additions.m\
|
||||
|
||||
-include Makefile.preamble
|
||||
|
||||
|
|
40
Source/win32/NSString+Win32Additions.h
Normal file
40
Source/win32/NSString+Win32Additions.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/** Category for converting Windows Strings
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Hugo Melder <hugo@algoriddim.com>
|
||||
Created: July 2024
|
||||
|
||||
This file is part of the GNUstep Base Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110 USA.
|
||||
*/
|
||||
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
/**
|
||||
* Converts a wchar_t list to an array of strings.
|
||||
* The list is NULL-delimited and terminated by two NULL (wchar_t) characters.
|
||||
*
|
||||
* The encoding is Unicode (UTF-16LE).
|
||||
*/
|
||||
@interface NSString (Win32Additions)
|
||||
|
||||
+ (GS_GENERIC_CLASS(NSArray, NSString *) *) arrayFromWCharList: (wchar_t *)list
|
||||
length: (unsigned long)length;
|
||||
|
||||
@end
|
45
Source/win32/NSString+Win32Additions.m
Normal file
45
Source/win32/NSString+Win32Additions.m
Normal file
|
@ -0,0 +1,45 @@
|
|||
/** Category for converting Windows Strings
|
||||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Hugo Melder <hugo@algoriddim.com>
|
||||
Created: July 2024
|
||||
|
||||
This file is part of the GNUstep Base Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110 USA.
|
||||
*/
|
||||
|
||||
#import "NSString+Win32Additions.h"
|
||||
|
||||
@implementation NSString (Win32Additions)
|
||||
|
||||
+ (GS_GENERIC_CLASS(NSArray, NSString *) *) arrayFromWCharList: (wchar_t *)list
|
||||
length: (unsigned long)length
|
||||
{
|
||||
NSString *string;
|
||||
GS_GENERIC_CLASS(NSArray, NSString *) * array;
|
||||
|
||||
string = [[NSString alloc] initWithBytes: list
|
||||
length: (length - 2) * sizeof(wchar_t)
|
||||
encoding: NSUTF16LittleEndianStringEncoding];
|
||||
|
||||
array = [string componentsSeparatedByString: @"\0"];
|
||||
RELEASE(string);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in a new issue