05d9fa9e57
X-SVN-Rev: 5711
1288 lines
36 KiB
C
1288 lines
36 KiB
C
/*
|
|
**********************************************************************
|
|
* Copyright (C) 1997-2001, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
**********************************************************************
|
|
*
|
|
* File ULOC.CPP
|
|
*
|
|
* Modification History:
|
|
*
|
|
* Date Name Description
|
|
* 04/01/97 aliu Creation.
|
|
* 08/21/98 stephen JDK 1.2 sync
|
|
* 12/08/98 rtg New Locale implementation and C API
|
|
* 03/15/99 damiba overhaul.
|
|
* 04/06/99 stephen changed setDefault() to realloc and copy
|
|
* 06/14/99 stephen Changed calls to ures_open for new params
|
|
* 07/21/99 stephen Modified setDefault() to propagate to C++
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
POSIX's locale format, from putil.c: [no spaces]
|
|
|
|
ll [ _CC ] [ . MM ] [ @ VV]
|
|
|
|
l = lang, C = ctry, M = charmap, V = variant
|
|
*/
|
|
|
|
|
|
#include "unicode/uloc.h"
|
|
|
|
#include "unicode/utypes.h"
|
|
#include "uresimp.h"
|
|
#include "umutex.h"
|
|
#include "cstring.h"
|
|
#include "unicode/ustring.h"
|
|
#include "cmemory.h"
|
|
#include "ucln_cmn.h"
|
|
|
|
/****************************************************************************
|
|
Global variable and type definitions
|
|
*****************************************************************************/
|
|
|
|
/* Locale stuff from locid.cpp */
|
|
U_CFUNC void locale_set_default(const char *id);
|
|
U_CFUNC const char *locale_get_default(void);
|
|
|
|
/* These strings describe the resources we attempt to load from
|
|
the locale ResourceBundle data file.*/
|
|
static const char _kLocaleID[] = "LocaleID";
|
|
static const char _kLanguages[] = "Languages";
|
|
static const char _kCountries[] = "Countries";
|
|
static const char _kIndexLocaleName[] = "index";
|
|
static const char _kIndexTag[] = "InstalledLocales";
|
|
|
|
#if 0
|
|
/* We don't use these resources currently */
|
|
static const char* _kLocaleString = "LocaleString";
|
|
static const char* _kShortLanguage = "ShortLanguage";
|
|
static const char* _kShortCountry = "ShortCountry";
|
|
#endif
|
|
|
|
#define TEMPBUFSIZE 8
|
|
|
|
/*Some static strings needed in the getDisplay* functions*/
|
|
static const UChar openParen[] = { 0x20, 0x28, 0}; /* " (" */
|
|
static const UChar comma[] = { 0x2C, 0x20, 0}; /* ", " */
|
|
static const UChar closeParen[] = { 0x29, 0}; /* ")" */
|
|
|
|
static char** _installedLocales = NULL;
|
|
static int32_t _installedLocalesCount = 0;
|
|
|
|
|
|
static const char * const _languages[] = {
|
|
"aa","ab","af","am","ar","as","ay","az",
|
|
"ba","be","bg","bh","bi","bn","bo","br",
|
|
"ca","co","cs","cy","da","de","dz",
|
|
"el","en","eo","es","et","eu","fa","fi","fj","fo","fr","fy",
|
|
"ga","gd","gl","gn","gu","ha","he","hi","hr","hu","hy",
|
|
"ia","id","ie","ik","in","is","it","iu","iw",
|
|
"ja","ji","jw","ka","kk","kl","km","kn","ko","ks","ku","ky",
|
|
"la","ln","lo","lt","lv",
|
|
"mg","mi","mk","ml","mn","mo","mr","ms","mt","my",
|
|
"na","ne","nl","no","oc","om","or",
|
|
"pa","pl","ps","pt","qu","rm","rn","ro","ru","rw",
|
|
"sa","sd","sg","sh","si","sk","sl","sm","sn","so","sq","sr","ss","st","su","sv","sw",
|
|
"ta","te","tg","th","ti","tk","tl","tn","to","tr","ts","tt","tw",
|
|
"ug","uk","ur","uz","vi","vo","wo","xh","yi","yo","za","zh","zu",
|
|
NULL
|
|
};
|
|
/* This list MUST be in sorted order, and MUST contain only two-letter codes! */
|
|
|
|
static const char * const _languages3[] = {
|
|
"aar","abk","afr","amh","ara","asm","aym","aze",
|
|
"bak","bel","bul","bih","bis","ben","bod","bre",
|
|
"cat","cos","ces","cym","dan","deu","dzo",
|
|
"ell","eng","epo","spa","est","eus","fas","fin","fij","fao","fra","fry",
|
|
"gai","gdh","glg","grn","guj","hau","heb","hin","hrv","hun","hye",
|
|
"ina","ind","ile","ipk","ind","isl","ita","iku","heb",
|
|
"jpn","yid","jaw","kat","kaz","kal","khm","kan","kor","kas","kur","kir",
|
|
"lat","lin","lao","lit","lav",
|
|
"mlg","mri","mkd","mal","mon","mol","mar","msa","mlt","mya",
|
|
"nau","nep","nld","nor","oci","orm","ori",
|
|
"pan","pol","pus","por","que","roh","run","ron","rus","kin",
|
|
"san","snd","sag","srp","sin","slk","slv","smo","sna","som","sqi","srp","ssw","sot","sun","swe","swa",
|
|
"tat","tel","tgk","tha","tir","tuk","tgl","tsn","ton","tur","tsn","tat","twi",
|
|
"uig","ukr","urd","uzb","vie","vol","wol","xho","yid","yor","zha","zho","zul",
|
|
NULL
|
|
};
|
|
/* This list MUST contain a three-letter code for every two-letter code in the
|
|
list above, and they MUST ne in the same order (i.e., the same language must
|
|
be in the same place in both lists)! */
|
|
|
|
static const char * const _countries[] = {
|
|
"AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ",
|
|
"BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ",
|
|
"CA","CC","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ",
|
|
"DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET",
|
|
"FI","FJ","FK","FM","FO","FR","FX",
|
|
"GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY",
|
|
"HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT",
|
|
"JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ",
|
|
"LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY",
|
|
"MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ",
|
|
"NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM",
|
|
"PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PT","PW","PY","QA","RE","RO","RU","RW",
|
|
"SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ",
|
|
"TC","TD","TF","TG","TH","TJ","TK","TM","TN","TO","TP","TR","TT","TV","TW","TZ",
|
|
"UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU",
|
|
"WF","WS","YE","YT","YU","ZA","ZM","ZR","ZW",
|
|
NULL
|
|
};
|
|
/* This list MUST be in sorted order, and MUST contain only two-letter codes! */
|
|
|
|
static const char * const _countries3[] = {
|
|
"AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","ATA","ARG","ASM","AUT","AUS","ABW","AZE",
|
|
"BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL","BRA","BHS","BTN","BVT","BWA","BLR","BLZ",
|
|
"CAN","CCK","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV","CXR","CYP","CZE",
|
|
"DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH",
|
|
"FIN","FJI","FLK","FSM","FRO","FRA","FXX",
|
|
"GAB","GBR","GRD","GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","SGS","GTM","GUM","GNB","GUY",
|
|
"HKG","HMD","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IOT","IRQ","IRN","ISL","ITA",
|
|
"JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT","CYM","KAZ",
|
|
"LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU","LUX","LVA","LBY",
|
|
"MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS",
|
|
"MDV","MWI","MEX","MYS","MOZ",
|
|
"NAM","NCL","NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN",
|
|
"PAN","PER","PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PRT","PLW","PRY","QAT","REU","ROM","RUS","RWA",
|
|
"SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ",
|
|
"TCA","TCD","ATF","TGO","THA","TJK","TKL","TKM","TUN","TON","TMP","TUR","TTO","TUV","TWN","TZA",
|
|
"UKR","UGA","UMI","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT",
|
|
"WLF","WSM","YEM","MYT","YUG","ZAF","ZMB","ZAR","ZWE",
|
|
NULL
|
|
};
|
|
/* This list MUST contain a three-letter code for every two-letter code in
|
|
the above list, and they MUST be listed in the same order! */
|
|
|
|
/*******************************************************************************
|
|
Implementation function definitions
|
|
*******************************************************************************/
|
|
|
|
/*Lazy evaluated the list of installed locales*/
|
|
static void _lazyEvaluate_installedLocales(void);
|
|
|
|
/*returns TRUE if a is an ID separator FALSE otherwise*/
|
|
#define _isIDSeparator(a) (a == '_' || a == '-')
|
|
|
|
#define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
|
|
/*returns TRUE if one of the special prefixes is here (s=string)
|
|
'x-' or 'i-' */
|
|
#define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1]))
|
|
|
|
/* Dot terminates it because of POSIX form where dot precedes the codepage
|
|
* except for variant
|
|
*/
|
|
#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
API function definitions
|
|
*******************************************************************************/
|
|
|
|
|
|
/*Works like strchr with '_' pr '-'*/
|
|
static const char* _findCharSeparator(const char* string)
|
|
{
|
|
if (string != NULL) {
|
|
/*Keeps iterating until an ID separator is found*/
|
|
while (*string) {
|
|
if (_isIDSeparator(*string)) {
|
|
return string;
|
|
}
|
|
string++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int16_t _findIndex(const char* const* list, const char* key)
|
|
{
|
|
const char* const* anchor = list;
|
|
|
|
while (*list)
|
|
{
|
|
if (uprv_strcmp(key, *list) == 0)
|
|
{
|
|
return (int16_t)(list - anchor);
|
|
}
|
|
list++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const char* uloc_getDefault()
|
|
{
|
|
return locale_get_default();
|
|
}
|
|
|
|
void uloc_setDefault(const char* newDefaultLocale,
|
|
UErrorCode* err)
|
|
{
|
|
if (U_FAILURE(*err))
|
|
return;
|
|
/* the error code isn't currently used for anything by this function*/
|
|
|
|
if (newDefaultLocale == NULL)
|
|
{
|
|
newDefaultLocale = uprv_getDefaultLocaleID();
|
|
}
|
|
|
|
/* propagate change to C++ */
|
|
locale_set_default(newDefaultLocale);
|
|
}
|
|
|
|
|
|
int32_t uloc_getParent(const char* localeID,
|
|
char* parent,
|
|
int32_t parentCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
int i=0;
|
|
int offset = 0;
|
|
int count = 0;
|
|
|
|
if (U_FAILURE(*err))
|
|
return 0;
|
|
|
|
if (localeID == NULL)
|
|
localeID = uloc_getDefault();
|
|
|
|
|
|
while (localeID[offset]&&(count < 2))
|
|
{
|
|
if (_isIDSeparator(localeID[offset++]))
|
|
count++;
|
|
}
|
|
|
|
/*finds the second IDSeparator*/
|
|
while (offset && !_isIDSeparator(localeID[offset]))
|
|
{
|
|
offset--;
|
|
}
|
|
|
|
|
|
/*Loop updates i to the size of the parent
|
|
but only copies into the buffer as much as the buffer can bare*/
|
|
while (i < offset)
|
|
{
|
|
if (parentCapacity > i)
|
|
parent[i] = localeID[i];
|
|
i++;
|
|
}
|
|
|
|
/*Sets the error code on case of need*/
|
|
if (i >= parentCapacity )
|
|
{
|
|
*err = U_BUFFER_OVERFLOW_ERROR;
|
|
}
|
|
|
|
if (parentCapacity>0)
|
|
parent[uprv_min(i,parentCapacity-1)] = '\0';
|
|
|
|
return i+1;
|
|
}
|
|
|
|
int32_t
|
|
uloc_getLanguage(const char* localeID,
|
|
char* language,
|
|
int32_t languageCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
int i=0;
|
|
|
|
|
|
if (U_FAILURE(*err))
|
|
return 0;
|
|
|
|
if (localeID == NULL)
|
|
localeID = uloc_getDefault();
|
|
|
|
/* If it starts with i- or x- */
|
|
if(_isIDPrefix(localeID))
|
|
{
|
|
if(languageCapacity > i)
|
|
{
|
|
language[i] = (char)uprv_tolower(*localeID);
|
|
}
|
|
i++;
|
|
localeID++;
|
|
|
|
if(languageCapacity > i)
|
|
{
|
|
language[i] = '-';
|
|
}
|
|
i++;
|
|
localeID++;
|
|
}
|
|
|
|
/*Loop updates i to the size of the language
|
|
but only copies into the buffer as much as the buffer can bare*/
|
|
while (!_isTerminator(*localeID) && !_isIDSeparator(*localeID))
|
|
{
|
|
if (languageCapacity > i)
|
|
language[i] = (char)uprv_tolower(*localeID);
|
|
i++;
|
|
localeID++;
|
|
}
|
|
|
|
if (i >= languageCapacity )
|
|
{
|
|
*err = U_BUFFER_OVERFLOW_ERROR;
|
|
}
|
|
|
|
if (languageCapacity > 0)
|
|
{
|
|
language[uprv_min(i,languageCapacity-1)] = '\0';
|
|
}
|
|
|
|
return i+1;
|
|
}
|
|
|
|
|
|
int32_t uloc_getCountry(const char* localeID,
|
|
char* country,
|
|
int32_t countryCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
int i=0;
|
|
|
|
if (U_FAILURE(*err))
|
|
return 0;
|
|
if (localeID == NULL)
|
|
localeID = uloc_getDefault();
|
|
|
|
|
|
/* skip over i- or x- */
|
|
if(_isIDPrefix(localeID))
|
|
{
|
|
localeID += 2;
|
|
}
|
|
|
|
localeID = _findCharSeparator(localeID);
|
|
|
|
/*Loop updates i to the size of the language
|
|
but only copies into the buffer as much as the buffer can bare*/
|
|
if (localeID)
|
|
{
|
|
++localeID;
|
|
while (!_isTerminator(*localeID) && !_isIDSeparator(*localeID))
|
|
{
|
|
if (countryCapacity > i)
|
|
country[i] = (char)uprv_toupper(*localeID);
|
|
i++;
|
|
localeID++;
|
|
}
|
|
}
|
|
|
|
if (i >= countryCapacity )
|
|
{
|
|
*err = U_BUFFER_OVERFLOW_ERROR;
|
|
}
|
|
|
|
if (countryCapacity > 0) {
|
|
country[uprv_min(i,countryCapacity-1)] = '\0';
|
|
}
|
|
return i+1;
|
|
}
|
|
|
|
int32_t uloc_getVariant(const char* localeID,
|
|
char* variant,
|
|
int32_t variantCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
int i=0;
|
|
const char *p = localeID;
|
|
|
|
if (U_FAILURE(*err))
|
|
return 0;
|
|
if (localeID == NULL)
|
|
{
|
|
localeID = uloc_getDefault();
|
|
}
|
|
|
|
/* skip over i- or x- */
|
|
if(_isIDPrefix(localeID))
|
|
{
|
|
localeID += 2;
|
|
}
|
|
|
|
localeID = _findCharSeparator(localeID);
|
|
if (localeID)
|
|
{
|
|
localeID = _findCharSeparator(++localeID);
|
|
}
|
|
|
|
if (localeID)
|
|
{
|
|
++localeID;
|
|
/*Loop updates i to the size of the language
|
|
but only copies into the buffer as much as the buffer can bear*/
|
|
while (!_isTerminator(*localeID))
|
|
{
|
|
if (variantCapacity > i)
|
|
variant[i] = (char)uprv_toupper(*localeID);
|
|
i++;
|
|
localeID++;
|
|
}
|
|
}
|
|
|
|
/* But wait, there's more!
|
|
**IFF** no variant was otherwise found, take one from @...
|
|
*/
|
|
if ( (i == 0) && /* Found nothing (zero chars copied) */
|
|
(localeID = uprv_strrchr(p, '@')) != NULL)
|
|
{
|
|
localeID++; /* point after the @ */
|
|
/* Note that we will stop at a period if the user accidentally
|
|
put a period after the @ sign */
|
|
|
|
/* repeat above copying loop */
|
|
while (!_isTerminator(*localeID))
|
|
{
|
|
if (variantCapacity > i)
|
|
variant[i] = (char)uprv_toupper(*localeID);
|
|
i++;
|
|
localeID++;
|
|
}
|
|
}
|
|
|
|
if (i >= variantCapacity )
|
|
{
|
|
*err = U_BUFFER_OVERFLOW_ERROR;
|
|
}
|
|
|
|
if (variantCapacity>0) {
|
|
variant[uprv_min(i,variantCapacity-1)] = '\0';
|
|
}
|
|
return i+1;
|
|
}
|
|
|
|
int32_t uloc_getName(const char* localeID,
|
|
char* name,
|
|
int32_t nameCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
int i= 0; /* total required size */
|
|
int n= 0; /* How much has been copied currently */
|
|
int varSze = 0; /* How big the variant is */
|
|
int cntSze = 0; /* How big the country is */
|
|
|
|
UErrorCode int_err = U_ZERO_ERROR;
|
|
|
|
if (U_FAILURE(*err)) return 0;
|
|
|
|
/*First we preflight the components in order to ensure a valid return value*/
|
|
if (localeID == NULL) localeID = uloc_getDefault();
|
|
|
|
cntSze = uloc_getCountry(localeID,
|
|
NULL ,
|
|
0,
|
|
&int_err);
|
|
int_err = U_ZERO_ERROR;
|
|
varSze = uloc_getVariant(localeID,
|
|
NULL ,
|
|
0,
|
|
&int_err);
|
|
|
|
int_err = U_ZERO_ERROR;
|
|
i = uloc_getLanguage(localeID,
|
|
NULL,
|
|
0,
|
|
&int_err);
|
|
|
|
/*Adjust for the zero terminators*/
|
|
--varSze;
|
|
--cntSze;
|
|
/* i is still languagesize+1 for the terminator */
|
|
|
|
/* Add space for underscores */
|
|
if (varSze)
|
|
{
|
|
i+= 2; /* if theres a variant, it will ALWAYS contain two underscores. */
|
|
}
|
|
else if (cntSze)
|
|
{
|
|
i++; /* Otherwise - only language _ country. */
|
|
}
|
|
|
|
/* Update i (total req'd size) */
|
|
i += cntSze + varSze;
|
|
|
|
if(nameCapacity) /* If size is zero, skip the actual copy */
|
|
{
|
|
/* Now, the real copying */
|
|
int_err = U_ZERO_ERROR;
|
|
|
|
uloc_getLanguage(localeID,
|
|
name,
|
|
nameCapacity /* -(n=0) */,
|
|
&int_err);
|
|
|
|
n += uprv_strlen(name);
|
|
|
|
/*We fill in the users buffer*/
|
|
if ((n<nameCapacity) && cntSze)
|
|
{
|
|
if(U_SUCCESS(int_err))
|
|
{
|
|
name[n++] = '_';
|
|
}
|
|
|
|
uloc_getCountry(localeID,
|
|
name + n,
|
|
nameCapacity - n,
|
|
&int_err);
|
|
n += cntSze;
|
|
|
|
if (varSze && (n<nameCapacity))
|
|
{
|
|
if(U_SUCCESS(int_err))
|
|
{
|
|
name[n++] = '_';
|
|
}
|
|
|
|
uloc_getVariant(localeID,
|
|
name + n,
|
|
nameCapacity - n,
|
|
&int_err);
|
|
}
|
|
|
|
}
|
|
else if((n<nameCapacity) && varSze)
|
|
{
|
|
if (U_SUCCESS(int_err))
|
|
{
|
|
name[n++] = '_';
|
|
if(n<nameCapacity)
|
|
name[n++] = '_';
|
|
}
|
|
|
|
uloc_getVariant(localeID,
|
|
name + n,
|
|
nameCapacity - n,
|
|
&int_err);
|
|
}
|
|
|
|
/* Tie it off */
|
|
name[uprv_min(i,nameCapacity-1)] = '\0';
|
|
} /* end (if nameCapacity > 0) */
|
|
|
|
*err = int_err;
|
|
|
|
return i;
|
|
}
|
|
|
|
const char* uloc_getISO3Language(const char* localeID)
|
|
{
|
|
int16_t offset;
|
|
char lang[TEMPBUFSIZE];
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
|
|
if (localeID == NULL)
|
|
{
|
|
localeID = uloc_getDefault();
|
|
}
|
|
uloc_getLanguage(localeID, lang, TEMPBUFSIZE, &err);
|
|
if (U_FAILURE(err))
|
|
return "";
|
|
offset = _findIndex(_languages, lang);
|
|
if (offset < 0)
|
|
return "";
|
|
return _languages3[offset];
|
|
}
|
|
|
|
const char* uloc_getISO3Country(const char* localeID)
|
|
{
|
|
int16_t offset;
|
|
char cntry[TEMPBUFSIZE];
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
|
|
if (localeID == NULL)
|
|
{
|
|
localeID = uloc_getDefault();
|
|
}
|
|
uloc_getCountry(localeID, cntry, TEMPBUFSIZE, &err);
|
|
if (U_FAILURE(err))
|
|
return "";
|
|
offset = _findIndex(_countries, cntry);
|
|
if (offset < 0)
|
|
return "";
|
|
|
|
return _countries3[offset];
|
|
}
|
|
|
|
uint32_t uloc_getLCID(const char* localeID)
|
|
{
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
char temp[30];
|
|
const UChar* lcid = NULL;
|
|
int32_t lcidLen = 0;
|
|
uint32_t result = 0;
|
|
UResourceBundle* bundle = ures_open(NULL, localeID, &err);
|
|
|
|
if (U_SUCCESS(err))
|
|
{
|
|
lcid = ures_getStringByKey(bundle, _kLocaleID, &lcidLen, &err);
|
|
ures_close(bundle);
|
|
if (U_FAILURE(err) || !lcid || lcidLen == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
u_austrcpy(temp, lcid);
|
|
result = (uint32_t)T_CString_stringToInteger(temp, 16);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int32_t uloc_getDisplayLanguage(const char* locale,
|
|
const char* inLocale,
|
|
UChar* language,
|
|
int32_t languageCapacity,
|
|
UErrorCode* status)
|
|
{
|
|
const UChar* result = NULL;
|
|
int32_t resultLen = 0;
|
|
int langBufSize;
|
|
char inLanguageBuffer[TEMPBUFSIZE];
|
|
char inLocaleBuffer[TEMPBUFSIZE];
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
UResourceBundle* bundle;
|
|
UBool isDefaultLocale = FALSE;
|
|
UBool done = FALSE;
|
|
|
|
if (U_FAILURE(*status))
|
|
return 0;
|
|
|
|
if (inLocale == NULL)
|
|
{
|
|
inLocale = uloc_getDefault();
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
/*truncates the fallback mechanism if we start out with a defaultLocale*/
|
|
|
|
if (locale == NULL)
|
|
locale = uloc_getDefault();
|
|
|
|
/*extracts the language*/
|
|
langBufSize = uloc_getLanguage(locale,
|
|
inLanguageBuffer,
|
|
TEMPBUFSIZE,
|
|
&err);
|
|
|
|
|
|
|
|
/*We need to implement a fallback mechanism here because we are getting keys out of a
|
|
tagged array, there is no capability of doing this with fallback through the resource
|
|
bundle API*/
|
|
|
|
if (langBufSize > 1)
|
|
{
|
|
do
|
|
{
|
|
/*
|
|
If we are at the root locale ("")
|
|
The first time we fall back to the full default locale
|
|
As we iterate down the latter, if we hit the root locale ("")
|
|
we pass it to the resource bundle api so it checks default.txt
|
|
*/
|
|
|
|
if (inLocale[0] == '\0')
|
|
{
|
|
if (!isDefaultLocale)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
inLocale = uloc_getDefault();
|
|
}
|
|
else {
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
bundle = ures_open(NULL, inLocale, &err);
|
|
|
|
if (U_SUCCESS(err))
|
|
{
|
|
const UChar* temp = NULL;
|
|
UResourceBundle* langBundle;
|
|
|
|
err = U_ZERO_ERROR;
|
|
langBundle = ures_getByKey(bundle, _kLanguages, NULL, &err);
|
|
if (U_SUCCESS(err))
|
|
{
|
|
temp = ures_getStringByKey(langBundle,
|
|
inLanguageBuffer,
|
|
&resultLen,
|
|
&err);
|
|
resultLen++;
|
|
if (U_SUCCESS(err))
|
|
result = temp;
|
|
ures_close(langBundle);
|
|
}
|
|
ures_close(bundle);
|
|
}
|
|
|
|
|
|
err = U_ZERO_ERROR;
|
|
|
|
/*Iterates down the Locale ID*/
|
|
|
|
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
|
|
inLocale = inLocaleBuffer;
|
|
|
|
} while ((result == NULL) && !done);
|
|
}
|
|
|
|
|
|
if (result)
|
|
{
|
|
if (resultLen > languageCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (languageCapacity >= 1)
|
|
{
|
|
u_strncpy(language, result, languageCapacity-1);
|
|
language[languageCapacity-1] = (UChar)0x0000;
|
|
}
|
|
}
|
|
else {
|
|
u_strcpy(language, result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*Falls back to ISO Name*/
|
|
resultLen = langBufSize;
|
|
if (resultLen > languageCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (languageCapacity >= 1)
|
|
{
|
|
|
|
language[languageCapacity-1] = (UChar)0x0000;
|
|
u_uastrncpy(language, inLanguageBuffer, languageCapacity-1);
|
|
}
|
|
}
|
|
else u_uastrcpy(language, inLanguageBuffer);
|
|
}
|
|
return resultLen;
|
|
}
|
|
|
|
int32_t uloc_getDisplayCountry(const char* locale,
|
|
const char* inLocale,
|
|
UChar* country,
|
|
int32_t countryCapacity,
|
|
UErrorCode* status)
|
|
{
|
|
/* NULL may be used to specify the default */
|
|
const UChar* result = NULL;
|
|
int32_t resultLen = 0;
|
|
int cntryBufSize;
|
|
char inCountryBuffer[TEMPBUFSIZE];
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
UResourceBundle* bundle = NULL;
|
|
char inLocaleBuffer[TEMPBUFSIZE];
|
|
UBool isDefaultLocale = FALSE;
|
|
UBool done = FALSE;
|
|
|
|
if (U_FAILURE(*status))
|
|
return 0;
|
|
|
|
if (inLocale == NULL)
|
|
{
|
|
inLocale = uloc_getDefault();
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
/*truncates the fallback mechanism if we start out with a defaultLocale*/
|
|
|
|
if (locale == NULL) {
|
|
locale = uloc_getDefault();
|
|
}
|
|
|
|
/*extracts the country*/
|
|
cntryBufSize = uloc_getCountry(locale, inCountryBuffer, TEMPBUFSIZE, &err);
|
|
|
|
|
|
if (cntryBufSize > 1)
|
|
{
|
|
/*
|
|
We need to implement a fallback mechanism here because we are getting keys out of a
|
|
tagged array, there is no capability of doing this with fallback through the resource
|
|
bundle API
|
|
*/
|
|
do
|
|
{
|
|
/*
|
|
If we are at the root locale ("")
|
|
The first time we fall back to the full default locale
|
|
As we iterate down the latter, if we hit the root locale ("")
|
|
we pass it to the resource bundle api so it checks default.txt
|
|
*/
|
|
|
|
if (inLocale[0] == '\0')
|
|
{
|
|
if (!isDefaultLocale)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
inLocale = uloc_getDefault();
|
|
}
|
|
else
|
|
done = TRUE;
|
|
}
|
|
|
|
|
|
bundle = ures_open(NULL, inLocale, &err);
|
|
|
|
if (U_SUCCESS(err))
|
|
{
|
|
const UChar* temp;
|
|
UResourceBundle* countryBundle;
|
|
|
|
err = U_ZERO_ERROR;
|
|
countryBundle = ures_getByKey(bundle, _kCountries, NULL, &err);
|
|
if (U_SUCCESS(err))
|
|
{
|
|
temp = ures_getStringByKey(countryBundle,
|
|
inCountryBuffer,
|
|
&resultLen,
|
|
&err);
|
|
resultLen++;
|
|
if (U_SUCCESS(err))
|
|
result = temp;
|
|
ures_close(countryBundle);
|
|
}
|
|
ures_close(bundle);
|
|
}
|
|
|
|
err = U_ZERO_ERROR;
|
|
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
|
|
|
|
inLocale = inLocaleBuffer;
|
|
} while ((result == NULL) && !done);
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
if (resultLen > countryCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (countryCapacity >= 1)
|
|
{
|
|
country[countryCapacity-1] = (UChar)0x0000;
|
|
u_strncpy(country, result, countryCapacity-1);
|
|
}
|
|
}
|
|
else u_strcpy(country, result);
|
|
}
|
|
else
|
|
{
|
|
/*Falls back to ISO Name*/
|
|
resultLen = cntryBufSize;
|
|
if (resultLen > countryCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (countryCapacity >= 1)
|
|
{
|
|
u_uastrncpy(country, inCountryBuffer, countryCapacity-1);
|
|
country[countryCapacity-1] = (UChar)0x0000;
|
|
}
|
|
}
|
|
else u_uastrcpy(country, inCountryBuffer);
|
|
}
|
|
|
|
return resultLen;
|
|
}
|
|
|
|
int32_t uloc_getDisplayVariant(const char* locale,
|
|
const char* inLocale,
|
|
UChar* variant,
|
|
int32_t variantCapacity,
|
|
UErrorCode* status)
|
|
{
|
|
const UChar* result = NULL;
|
|
int32_t resultLen = 0;
|
|
int varBufSize;
|
|
char inVariantBuffer[TEMPBUFSIZE];
|
|
char* inVariant = inVariantBuffer;
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
UResourceBundle* bundle;
|
|
char inLocaleBuffer[TEMPBUFSIZE];
|
|
UBool isDefaultLocale = FALSE;
|
|
char inVariantTagBuffer[TEMPBUFSIZE+2];
|
|
char* inVariantTag = inVariantTagBuffer;
|
|
UBool done = FALSE;
|
|
|
|
if (U_FAILURE(*status))
|
|
return 0;
|
|
|
|
inVariantTagBuffer[0] = '\0';
|
|
|
|
if (inLocale == NULL)
|
|
{
|
|
inLocale = uloc_getDefault();
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
}
|
|
/*truncates the fallback mechanism if we start out with a defaultLocale*/
|
|
|
|
if (locale == NULL)
|
|
locale = uloc_getDefault();
|
|
|
|
/*extracts the variant*/
|
|
varBufSize = uloc_getVariant(locale, inVariant, TEMPBUFSIZE, &err);
|
|
|
|
if (varBufSize > 1)
|
|
{
|
|
/*In case the variant is longer than our stack buffers*/
|
|
if (err == U_BUFFER_OVERFLOW_ERROR)
|
|
{
|
|
inVariant = (char*)uprv_malloc(varBufSize*sizeof(char)+1);
|
|
if (inVariant == NULL)
|
|
goto NO_MEMORY;
|
|
inVariantTag = (char*)uprv_malloc(varBufSize*sizeof(char)+uprv_strlen("%%")+1);
|
|
if (inVariantTag == NULL)
|
|
{
|
|
uprv_free(inVariant);
|
|
goto NO_MEMORY;
|
|
}
|
|
err = U_ZERO_ERROR;
|
|
uloc_getVariant(locale, inVariant, varBufSize, &err);
|
|
}
|
|
|
|
uprv_strcpy(inVariantTag,"%%");
|
|
uprv_strcat(inVariantTag, inVariant);
|
|
|
|
/*We need to implement a fallback mechanism here because we are getting keys out of a
|
|
tagged array, there is no capability of doing this with fallback through the resource
|
|
bundle API*/
|
|
do {
|
|
/*
|
|
If we are at the root locale ("")
|
|
The first time we fall back to the full default locale
|
|
As we iterate down the latter, if we hit the root locale ("")
|
|
we pass it to the resource bundle api so it checks default.txt
|
|
*/
|
|
|
|
if (inLocale[0] == '\0')
|
|
{
|
|
if (!isDefaultLocale)
|
|
{
|
|
isDefaultLocale = TRUE;
|
|
inLocale = uloc_getDefault();
|
|
}
|
|
else
|
|
done = TRUE;
|
|
}
|
|
|
|
|
|
bundle = ures_open(NULL, inLocale, &err);
|
|
|
|
if (U_SUCCESS(err))
|
|
{
|
|
const UChar* temp;
|
|
|
|
temp = ures_getStringByKey(bundle,
|
|
inVariantTag,
|
|
&resultLen,
|
|
&err);
|
|
resultLen++;
|
|
if (U_SUCCESS(err))
|
|
result = temp;
|
|
ures_close(bundle);
|
|
}
|
|
|
|
err = U_ZERO_ERROR;
|
|
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
|
|
|
|
inLocale = inLocaleBuffer;
|
|
} while ((result == NULL) && !done);
|
|
}
|
|
|
|
|
|
if (result)
|
|
{
|
|
if (resultLen > variantCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (variantCapacity >= 1)
|
|
{
|
|
variant[variantCapacity-1] = (UChar)0x0000;
|
|
u_strncpy(variant, result, variantCapacity-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u_strcpy(variant, result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*Falls back to user's Name*/
|
|
resultLen = varBufSize;
|
|
if (resultLen > variantCapacity)
|
|
{
|
|
*status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
if (variantCapacity >= 1)
|
|
{
|
|
u_uastrncpy(variant, inVariant, variantCapacity-1);
|
|
variant[variantCapacity-1] = (UChar)0x0000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u_uastrcpy(variant, inVariant);
|
|
}
|
|
}
|
|
|
|
/*Clean up memory*/
|
|
if (inVariant != inVariantBuffer)
|
|
{
|
|
uprv_free(inVariant);
|
|
uprv_free(inVariantTag);
|
|
}
|
|
return resultLen;
|
|
|
|
NO_MEMORY:
|
|
*status = U_MEMORY_ALLOCATION_ERROR;
|
|
return 0;
|
|
}
|
|
|
|
int32_t uloc_getDisplayName(const char* locale,
|
|
const char* inLocale,
|
|
UChar* result,
|
|
int32_t nameCapacity,
|
|
UErrorCode* err)
|
|
{
|
|
UErrorCode int_err = U_ZERO_ERROR;
|
|
int i = 0;
|
|
int cntSze, varSze;
|
|
UBool has_lang = TRUE;
|
|
int result_size;
|
|
|
|
int_err = U_ZERO_ERROR;
|
|
|
|
/*Preflights all the components*/
|
|
cntSze = uloc_getDisplayCountry(locale,
|
|
inLocale,
|
|
NULL ,
|
|
0,
|
|
&int_err);
|
|
int_err = U_ZERO_ERROR;
|
|
varSze = uloc_getDisplayVariant(locale,
|
|
inLocale,
|
|
NULL ,
|
|
0,
|
|
&int_err);
|
|
|
|
int_err = U_ZERO_ERROR;
|
|
i = uloc_getDisplayLanguage(locale,
|
|
inLocale,
|
|
NULL,
|
|
0,
|
|
&int_err);
|
|
/*Decrement duplicative zero-terminators*/
|
|
--varSze;
|
|
--cntSze;
|
|
|
|
/*Logic below adjusts pre-flight information with additional characters "(", ",", " ", ")"
|
|
when neeed be*/
|
|
if ((i-1 == 0) && (varSze == 0)) /*No language field*/
|
|
{
|
|
has_lang = FALSE;
|
|
i = cntSze+1;
|
|
}
|
|
else if (cntSze)
|
|
{
|
|
if (varSze)
|
|
{
|
|
i += cntSze + varSze + 5;
|
|
}
|
|
else
|
|
{
|
|
i += cntSze + 3;
|
|
}
|
|
}
|
|
|
|
int_err = U_ZERO_ERROR;
|
|
|
|
result_size = uloc_getDisplayLanguage(locale,
|
|
inLocale,
|
|
result,
|
|
nameCapacity,
|
|
&int_err) - 1;
|
|
|
|
if (U_SUCCESS(int_err)&&cntSze)
|
|
{
|
|
if (U_SUCCESS(int_err))
|
|
{
|
|
if (has_lang)
|
|
{
|
|
u_strcat(result, openParen);
|
|
result_size += 2;
|
|
}
|
|
|
|
result_size += uloc_getDisplayCountry(locale,
|
|
inLocale,
|
|
result + result_size,
|
|
nameCapacity - result_size,
|
|
&int_err) - 1;
|
|
}
|
|
|
|
if (varSze)
|
|
{
|
|
if (U_SUCCESS(int_err))
|
|
{
|
|
u_strcat(result, comma);
|
|
result_size += 2;
|
|
|
|
result_size += uloc_getDisplayVariant(locale,
|
|
inLocale,
|
|
result + result_size,
|
|
nameCapacity - result_size,
|
|
&int_err) - 1;
|
|
}
|
|
}
|
|
|
|
if (U_SUCCESS(int_err)&&has_lang)
|
|
{
|
|
u_strcat(result, closeParen);
|
|
}
|
|
}
|
|
|
|
*err = int_err;
|
|
|
|
return i;
|
|
}
|
|
|
|
const char*
|
|
uloc_getAvailable(int32_t offset)
|
|
{
|
|
|
|
if (_installedLocales == NULL)
|
|
_lazyEvaluate_installedLocales();
|
|
|
|
if (offset > _installedLocalesCount)
|
|
return NULL;
|
|
else
|
|
return _installedLocales[offset];
|
|
|
|
}
|
|
|
|
int32_t uloc_countAvailable()
|
|
{
|
|
if (_installedLocales == NULL)
|
|
_lazyEvaluate_installedLocales();
|
|
|
|
return _installedLocalesCount;
|
|
}
|
|
|
|
UBool uloc_cleanup(void) {
|
|
char ** temp;
|
|
int32_t localeCount;
|
|
int32_t i;
|
|
|
|
if (_installedLocales) {
|
|
temp = _installedLocales;
|
|
_installedLocales = NULL;
|
|
|
|
localeCount = _installedLocalesCount;
|
|
_installedLocalesCount = 0;
|
|
|
|
for (i = 0; i < localeCount; i++) {
|
|
uprv_free(temp[i]);
|
|
}
|
|
uprv_free(temp);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void _lazyEvaluate_installedLocales()
|
|
{
|
|
UResourceBundle *index = NULL;
|
|
UResourceBundle installed;
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
const UChar *lname;
|
|
char ** temp;
|
|
int32_t i = 0;
|
|
int32_t len = 0;
|
|
int32_t localeCount;
|
|
|
|
ures_setIsStackObject(&installed, TRUE);
|
|
index = ures_open(NULL, _kIndexLocaleName, &status);
|
|
ures_getByKey(index, _kIndexTag, &installed, &status);
|
|
|
|
if(U_SUCCESS(status)) {
|
|
localeCount = ures_getSize(&installed);
|
|
temp = (char **) uprv_malloc(sizeof(char*) * (localeCount+1));
|
|
|
|
ures_resetIterator(&installed);
|
|
while(ures_hasNext(&installed)) {
|
|
lname = ures_getNextString(&installed, &len, NULL, &status);
|
|
temp[i] = (char*) uprv_malloc(sizeof(char) * (len + 1));
|
|
|
|
u_UCharsToChars(lname, temp[i], len);
|
|
temp[i][len] = 0; /* Terminate the string */
|
|
i++;
|
|
}
|
|
|
|
umtx_lock(NULL);
|
|
if (_installedLocales == NULL)
|
|
{
|
|
_installedLocales = temp;
|
|
_installedLocalesCount = localeCount;
|
|
} else {
|
|
for (i = 0; i < localeCount; i++) {
|
|
uprv_free(temp[i]);
|
|
}
|
|
uprv_free(temp);
|
|
}
|
|
umtx_unlock(NULL);
|
|
|
|
ures_close(&installed);
|
|
}
|
|
ures_close(index);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of all language codes defined in ISO 639. This is a pointer
|
|
* to an array of pointers to arrays of char. All of these pointers are owned
|
|
* by ICU-- do not delete them, and do not write through them. The array is
|
|
* terminated with a null pointer.
|
|
*/
|
|
const char* const* uloc_getISOLanguages()
|
|
{
|
|
return _languages;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of all 2-letter country codes defined in ISO 639. This is a
|
|
* pointer to an array of pointers to arrays of char. All of these pointers are
|
|
* owned by ICU-- do not delete them, and do not write through them. The array is
|
|
* terminated with a null pointer.
|
|
*/
|
|
const char* const* uloc_getISOCountries()
|
|
{
|
|
return _countries;
|
|
}
|