macOS system language detection.

It appears the system language detection doesn't work on [some versions
of?] macOS.  Fixed.

Strangely, querying the Cocoa API for the system locale on a system in
US with the preferred language set to Korean produces the invalid locale
"ko-US" instead of the expected "ko-KR".  This behavior of returning the
language with a invalid region qualifier for the language appears to
happen only with languages where macOS does not have regional variants
(this issue does not seem to occur with Canadian English, en-CA, for
example.)  A fuzzy matching locale function has been added to handle
this issue.
This commit is contained in:
Mark Kim 2022-02-06 20:15:40 -05:00
parent c2ab6d461c
commit c9e195549f
6 changed files with 96 additions and 4 deletions

View file

@ -47,6 +47,12 @@
#include <wctype.h>
#endif
#if defined(__APPLE__)
#include "macos.h"
#elif defined(__IOS__)
#include "ios.h"
#endif
/* Globals: */
@ -790,7 +796,7 @@ static void ctype_utf8(void)
}
/**
* For a given language, return its local, or exit with a usage error.
* For a given language, return its locale, or exit with a usage error.
*
* @param langstr Name of language (e.g., "german")
* @return Locale (e.g., "de_DE.UTF-8")
@ -811,6 +817,47 @@ static const char *language_to_locale(const char *langstr)
return NULL;
}
/**
* For a given locale, return the known locale that matches it closest, or exit
* with a usage error.
*
* @param inlocale Name of some locale (e.g., "ko_US")
* @return Known locale. (e.g., "ko_KR.UTF-8")
*/
static const char *locale_to_closest_locale(const char *inlocale)
{
const int numlocale = sizeof(language_to_locale_array) / sizeof(language_to_locale_array[0]);
const char* outlocale = NULL;
int outlocale_score = 0;
int i = 0;
int j = 0;
/* find the locale with the longest string match */
for (i=0; i<numlocale; i++)
{
const char* candidate = language_to_locale_array[i].locale;
for (j=0; j<strlen(inlocale) && j<strlen(candidate); j++)
{
if(inlocale[j] != candidate[j]) break;
}
if (j > outlocale_score)
{
outlocale = candidate;
outlocale_score = j;
}
}
/* locale must match at least three characters */
if (outlocale_score < 3)
{
outlocale = NULL;
}
return outlocale;
}
/**
* Set language ("langint" global) based on a given locale;
* will try a few ways of checking, is case-insensitive, etc.
@ -1225,7 +1272,13 @@ int setup_i18n(const char *restrict lang, const char *restrict locale, int * num
}
}
else
locale = "";
{
#if defined(__APPLE__)
locale = locale_to_closest_locale(apple_locale());
#else
locale = "";
#endif
}
if (lang)
locale = language_to_locale(lang);