/******************************************************************** * COPYRIGHT: * Copyright (c) 1997-2003, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ /******************************************************************************** * * File CLOCTST.C * * Modification History: * Name Description * Madhu Katragadda Ported for C API ********************************************************************************* */ #include #include #include #include "unicode/utypes.h" #include "unicode/putil.h" #include "cloctst.h" #include "unicode/uloc.h" #include "unicode/uscript.h" #include "unicode/uchar.h" #include "unicode/ustring.h" #include "unicode/uset.h" #include "cintltst.h" #include "cstring.h" #include "unicode/ures.h" #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) #ifdef WIN32 #include "locmap.h" #endif static void TestNullDefault(void); static void VerifyTranslation(void); void PrintDataTable(); /*--------------------------------------------------- table of valid data --------------------------------------------------- */ #define LOCALE_SIZE 5 #define LOCALE_INFO_SIZE 23 static const char* rawData2[LOCALE_INFO_SIZE][LOCALE_SIZE] = { /* language code */ { "en", "fr", "hr", "el", "no" }, /* country code */ { "US", "FR", "HR", "GR", "NO" }, /* variant code */ { "", "", "", "", "NY" }, /* full name */ { "en_US", "fr_FR", "hr_HR", "el_GR", "no_NO_NY" }, /* ISO-3 language */ { "eng", "fra", "hrv", "ell", "nor" }, /* ISO-3 country */ { "USA", "FRA", "HRV", "GRC", "NOR" }, /* LCID (not currently public) */ { "409", "40c", "41a", "408", "814" }, /* display language (English) */ { "English", "French", "Croatian", "Greek", "Norwegian" }, /* display country (English) */ { "United States", "France", "Croatia", "Greece", "Norway" }, /* display variant (English) */ { "", "", "", "", "Nynorsk" }, /* display name (English) */ { "English (United States)", "French (France)", "Croatian (Croatia)", "Greek (Greece)", "Norwegian (Norway, Nynorsk)" }, /* display language (French) */ { "anglais", "fran\\u00E7ais", "croate", "grec", "norv\\u00E9gien" }, /* display country (French) */ { "\\u00C9tats-Unis", "France", "Croatie", "Gr\\u00E8ce", "Norv\\u00E8ge" }, /* display variant (French) */ { "", "", "", "", "Nynorsk" }, /* display name (French) */ { "anglais (\\u00C9tats-Unis)", "fran\\u00E7ais (France)", "croate (Croatie)", "grec (Gr\\u00E8ce)", "norv\\u00E9gien (Norv\\u00E8ge, Nynorsk)" }, /* display language (Croatian) */ { "", "", "hrvatski", "", "" }, /* display country (Croatian) */ { "", "", "Hrvatska", "", "" }, /* display variant (Croatian) */ { "", "", "", "", "Nynorsk" }, /* display name (Croatian) */ { "", "", "hrvatski (Hrvatska)", "", "" }, /* display language (Greek) */ { "\\u0391\\u03b3\\u03b3\\u03bb\\u03b9\\u03ba\\u03ac", "\\u0393\\u03b1\\u03bb\\u03bb\\u03b9\\u03ba\\u03ac", "\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03b9\\u03ba\\u03ac", "\\u03b5\\u03bb\\u03bb\\u03b7\\u03bd\\u03b9\\u03ba\\u03ac", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03b9\\u03ba\\u03ac" }, /* display country (Greek) */ { "\\u0397\\u03bd\\u03c9\\u03bc\\u03ad\\u03bd\\u03b5\\u03c2 \\u03a0\\u03bf\\u03bb\\u03b9\\u03c4\\u03b5\\u03af\\u03b5\\u03c2 \\u0391\\u03bc\\u03b5\\u03c1\\u03b9\\u03ba\\u03ae\\u03c2", "\\u0393\\u03b1\\u03bb\\u03bb\\u03af\\u03b1", "\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03af\\u03b1", "\\u0395\\u03bb\\u03bb\\u03ac\\u03b4\\u03b1", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03af\\u03b1" }, /* display variant (Greek) */ { "", "", "", "", "Nynorsk" }, /* display name (Greek) */ { "\\u0391\\u03b3\\u03b3\\u03bb\\u03b9\\u03ba\\u03ac (\\u0397\\u03bd\\u03c9\\u03bc\\u03ad\\u03bd\\u03b5\\u03c2 \\u03a0\\u03bf\\u03bb\\u03b9\\u03c4\\u03b5\\u03af\\u03b5\\u03c2 \\u0391\\u03bc\\u03b5\\u03c1\\u03b9\\u03ba\\u03ae\\u03c2)", "\\u0393\\u03b1\\u03bb\\u03bb\\u03b9\\u03ba\\u03ac (\\u0393\\u03b1\\u03bb\\u03bb\\u03af\\u03b1)", "\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03b9\\u03ba\\u03ac (\\u039a\\u03c1\\u03bf\\u03b1\\u03c4\\u03af\\u03b1)", "\\u03b5\\u03bb\\u03bb\\u03b7\\u03bd\\u03b9\\u03ba\\u03ac (\\u0395\\u03bb\\u03bb\\u03ac\\u03b4\\u03b1)", "\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03b9\\u03ba\\u03ac (\\u039d\\u03bf\\u03c1\\u03b2\\u03b7\\u03b3\\u03af\\u03b1, Nynorsk)" } }; static UChar*** dataTable=0; enum { ENGLISH = 0, FRENCH = 1, CROATIAN = 2, GREEK = 3, NORWEGIAN = 4 }; enum { LANG = 0, CTRY = 1, VAR = 2, NAME = 3, LANG3 = 4, CTRY3 = 5, LCID = 6, DLANG_EN = 7, DCTRY_EN = 8, DVAR_EN = 9, DNAME_EN = 10, DLANG_FR = 11, DCTRY_FR = 12, DVAR_FR = 13, DNAME_FR = 14, DLANG_HR = 15, DCTRY_HR = 16, DVAR_HR = 17, DNAME_HR = 18, DLANG_EL = 19, DCTRY_EL = 20, DVAR_EL = 21, DNAME_EL = 22 }; void addLocaleTest(TestNode** root); void addLocaleTest(TestNode** root) { addTest(root, &TestObsoleteNames, "tsutil/cloctst/TestObsoleteNames"); /* srl- move */ addTest(root, &TestBasicGetters, "tsutil/cloctst/TestBasicGetters"); addTest(root, &TestNullDefault, "tsutil/cloctst/TestNullDefault"); addTest(root, &TestPrefixes, "tsutil/cloctst/TestPrefixes"); addTest(root, &TestSimpleResourceInfo, "tsutil/cloctst/TestSimpleResourceInfo"); addTest(root, &TestDisplayNames, "tsutil/cloctst/TestDisplayNames"); addTest(root, &TestGetAvailableLocales, "tsutil/cloctst/TestGetAvailableLocales"); addTest(root, &TestDataDirectory, "tsutil/cloctst/TestDataDirectory"); addTest(root, &TestISOFunctions, "tsutil/cloctst/TestISOFunctions"); addTest(root, &TestISO3Fallback, "tsutil/cloctst/TestISO3Fallback"); addTest(root, &TestUninstalledISO3Names, "tsutil/cloctst/TestUninstalledISO3Names"); addTest(root, &TestSimpleDisplayNames, "tsutil/cloctst/TestSimpleDisplayNames"); addTest(root, &TestVariantParsing, "tsutil/cloctst/TestVariantParsing"); addTest(root, &TestLocaleStructure, "tsutil/cloctst/TestLocaleStructure"); addTest(root, &TestConsistentCountryInfo,"tsutil/cloctst/TestConsistentCountryInfo"); addTest(root, &VerifyTranslation, "tsutil/cloctst/VerifyTranslation"); } /* testing uloc(), uloc_getName(), uloc_getLanguage(), uloc_getVariant(), uloc_getCountry() */ static void TestBasicGetters() { int32_t i; int32_t cap; UErrorCode status = U_ZERO_ERROR; char *testLocale = 0; char *temp = 0, *name = 0; log_verbose("Testing Basic Getters\n"); for (i = 0; i < LOCALE_SIZE; i++) { testLocale=(char*)malloc(sizeof(char) * (strlen(rawData2[NAME][i])+1)); strcpy(testLocale,rawData2[NAME][i]); log_verbose("Testing %s .....\n", testLocale); cap=uloc_getLanguage(testLocale, NULL, 0, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)malloc(sizeof(char) * (cap+1)); uloc_getLanguage(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getLanguage %s\n", myErrorName(status)); } if (0 !=strcmp(temp,rawData2[LANG][i])) { log_err(" Language code mismatch: %s versus %s\n", temp, rawData2[LANG][i]); } cap=uloc_getCountry(testLocale, temp, cap, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)realloc(temp, sizeof(char) * (cap+1)); uloc_getCountry(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getCountry %s\n", myErrorName(status)); } if (0 != strcmp(temp, rawData2[CTRY][i])) { log_err(" Country code mismatch: %s versus %s\n", temp, rawData2[CTRY][i]); } cap=uloc_getVariant(testLocale, temp, cap, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; temp=(char*)realloc(temp, sizeof(char) * (cap+1)); uloc_getVariant(testLocale, temp, cap+1, &status); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getVariant %s\n", myErrorName(status)); } if (0 != strcmp(temp, rawData2[VAR][i])) { log_err("Variant code mismatch: %s versus %s\n", temp, rawData2[VAR][i]); } cap=uloc_getName(testLocale, NULL, 0, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; name=(char*)malloc(sizeof(char) * (cap+1)); uloc_getName(testLocale, name, cap+1, &status); } else if(status==U_ZERO_ERROR) { log_err("ERROR: in uloc_getName(%s,NULL,0,..), expected U_BUFFER_OVERFLOW_ERROR!\n", testLocale); } if(U_FAILURE(status)){ log_err("ERROR: in uloc_getName %s\n", myErrorName(status)); } if (0 != strcmp(name, rawData2[NAME][i])){ log_err(" Mismatch in getName: %s versus %s\n", name, rawData2[NAME][i]); } free(temp); free(name); free(testLocale); } } static void TestNullDefault() { UErrorCode status = U_ZERO_ERROR; char original[ULOC_FULLNAME_CAPACITY]; uprv_strcpy(original, uloc_getDefault()); uloc_setDefault("qq_BLA", &status); if (uprv_strcmp(uloc_getDefault(), "qq_BLA") != 0) { log_err(" Mismatch in uloc_setDefault: qq_BLA versus %s\n", uloc_getDefault()); } uloc_setDefault(NULL, &status); if (uprv_strcmp(uloc_getDefault(), original) != 0) { log_err(" uloc_setDefault(NULL, &status) didn't get the default locale back!\n"); } } /* Test the i- and x- and @ and . functionality */ #define PREFIXBUFSIZ 128 static void TestPrefixes() { int row = 0; int n; const char *loc; const char *testData[][5] = { {"sv", "FI", "AL", "sv-fi-al", "sv_FI_AL" }, {"en", "GB", "", "en-gb", "en_GB" }, {"i-hakka", "MT", "XEMXIJA", "i-hakka_MT_XEMXIJA", "i-hakka_MT_XEMXIJA"}, {"i-hakka", "CN", "", "i-hakka_CN", "i-hakka_CN"}, {"i-hakka", "MX", "", "I-hakka_MX", "i-hakka_MX"}, {"x-klingon", "US", "SANJOSE", "X-KLINGON_us_SANJOSE", "x-klingon_US_SANJOSE"}, {"mr", "", "", "mr.utf8", "mr"}, {"de", "TV", "", "de-tv.koi8r", "de_TV"}, {"x-piglatin", "ML", "", "x-piglatin_ML.MBE", "x-piglatin_ML"}, /* Multibyte English */ {"i-cherokee","US", "", "i-Cherokee_US.utf7", "i-cherokee_US"}, {"x-filfli", "MT", "FILFLA", "x-filfli_MT_FILFLA.gb-18030", "x-filfli_MT_FILFLA"}, {"no", "NO", "NY", "no-no-ny.utf32@B", "no_NO_NY"}, /* @ ignored unless variant is empty */ {"no", "NO", "B", "no-no.utf32@B", "no_NO_B" }, {"no", "", "NY", "no__ny", "no__NY" }, {"no", "", "NY", "no@ny", "no__NY" }, { "","","","",""} }; const char *testTitles[] = { "uloc_getLanguage()", "uloc_getCountry()", "uloc_getVariant()", "name", "uloc_getName()", "country3", "lcid" }; char buf[PREFIXBUFSIZ]; int32_t len; UErrorCode err; for(row=0;testData[row][0][0] != 0;row++) { loc = testData[row][NAME]; log_verbose("Test #%d: %s\n", row, loc); err = U_ZERO_ERROR; len=0; buf[0]=0; for(n=0;n<=(NAME+1);n++) { if(n==NAME) continue; for(len=0;len [%s] (length %d)\n", row, testTitles[n], loc, buf, len); if(len != (int32_t)strlen(buf)) { log_err("#%d: %s on %s: -> [%s] (length returned %d, actual %d!)\n", row, testTitles[n], loc, buf, len, strlen(buf)+1); } /* see if they smashed something */ if(buf[len+1] != '%') { log_err("#%d: %s on %s: -> [%s] - wrote [%X] out ofbounds!\n", row, testTitles[n], loc, buf, buf[len+1]); } if(strcmp(buf, testData[row][n])) { log_err("#%d: %s on %s: -> [%s] (expected '%s'!)\n", row, testTitles[n], loc, buf, testData[row][n]); } } } } } /* testing uloc_getISO3Language(), uloc_getISO3Country(), */ static void TestSimpleResourceInfo() { int32_t i; char* testLocale = 0; UChar* expected = 0; const char* temp; char temp2[20]; testLocale=(char*)malloc(sizeof(char) * 1); expected=(UChar*)malloc(sizeof(UChar) * 1); setUpDataTable(); log_verbose("Testing getISO3Language and getISO3Country\n"); for (i = 0; i < LOCALE_SIZE; i++) { testLocale=(char*)realloc(testLocale, sizeof(char) * (u_strlen(dataTable[NAME][i])+1)); u_austrcpy(testLocale, dataTable[NAME][i]); log_verbose("Testing %s ......\n", testLocale); temp=uloc_getISO3Language(testLocale); expected=(UChar*)realloc(expected, sizeof(UChar) * (strlen(temp) + 1)); u_uastrcpy(expected,temp); if (0 != u_strcmp(expected, dataTable[LANG3][i])) { log_err(" ISO-3 language code mismatch: %s versus %s\n", austrdup(expected), austrdup(dataTable[LANG3][i])); } temp=uloc_getISO3Country(testLocale); expected=(UChar*)realloc(expected, sizeof(UChar) * (strlen(temp) + 1)); u_uastrcpy(expected,temp); if (0 != u_strcmp(expected, dataTable[CTRY3][i])) { log_err(" ISO-3 Country code mismatch: %s versus %s\n", austrdup(expected), austrdup(dataTable[CTRY3][i])); } sprintf(temp2, "%x", uloc_getLCID(testLocale)); if (strcmp(temp2, rawData2[LCID][i]) != 0) { log_data_err("LCID mismatch: %s versus %s\n", temp2 , rawData2[LCID][i]); } } free(expected); free(testLocale); cleanUpDataTable(); } /* * Jitterbug 2439 -- markus 20030425 * * The lookup of display names must not fall back through the default * locale because that yields useless results. */ static void TestDisplayNames() { UChar buffer[100]; UErrorCode errorCode; int32_t length; log_verbose("Testing getDisplayName for different locales\n"); log_verbose(" In locale = en_US...\n"); doTestDisplayNames("en_US", DLANG_EN); log_verbose(" In locale = fr_FR....\n"); doTestDisplayNames("fr_FR", DLANG_FR); log_verbose(" In locale = hr_HR...\n"); doTestDisplayNames("hr_HR", DLANG_HR); log_verbose(" In locale = gr_EL..\n"); doTestDisplayNames("el_GR", DLANG_EL); /* test that the default locale has a display name for its own language */ errorCode=U_ZERO_ERROR; length=uloc_getDisplayLanguage(NULL, NULL, buffer, LENGTHOF(buffer), &errorCode); if(U_FAILURE(errorCode) || (length<=3 && buffer[0]<=0x7f)) { /* check <=3 to reject getting the language code as a display name */ log_err("unable to get a display string for the language of the default locale - %s\n", u_errorName(errorCode)); } /* test that we get the language code itself for an unknown language, and a default warning */ errorCode=U_ZERO_ERROR; length=uloc_getDisplayLanguage("qq", "rr", buffer, LENGTHOF(buffer), &errorCode); if(errorCode!=U_USING_DEFAULT_WARNING || length!=2 || buffer[0]!=0x71 || buffer[1]!=0x71) { log_err("error getting the display string for an unknown language - %s\n", u_errorName(errorCode)); } } /* test for uloc_getAvialable() and uloc_countAvilable()*/ static void TestGetAvailableLocales() { const char *locList; int32_t locCount,i; log_verbose("Testing the no of avialable locales\n"); locCount=uloc_countAvailable(); if (locCount == 0) log_data_err("countAvailable() returned an empty list!\n"); /* use something sensible w/o hardcoding the count */ else if(locCount < 0){ log_data_err("countAvailable() returned a wrong value!= %d\n", locCount); } else{ log_info("Number of locales returned = %d\n", locCount); } for(i=0;i subBundleSize) { minSize = subBundleSize; log_err("Arrays are different size with key \"%s\" in \"%s\" from root for locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } for (idx = 0; idx < minSize && sameArray; idx++) { if (subRootBundleArr[idx] != subBundleArr[idx]) { sameArray = FALSE; } if (strcmp(subBundleKey, "DateTimeElements") == 0 && (subBundleArr[idx] < 1 || 7 < subBundleArr[idx])) { log_err("Value out of range with key \"%s\" at index %d in \"%s\" for locale \"%s\"\n", subBundleKey, idx, ures_getKey(currentBundle), locale); } } /* Special exception es_US and DateTimeElements */ if (sameArray && !(strcmp(locale, "es_US") == 0 && strcmp(subBundleKey, "DateTimeElements") == 0)) { log_err("Arrays are the same with key \"%s\" in \"%s\" from root for locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } } else if (ures_getType(subBundle) == URES_ARRAY) { UResourceBundle *subSubBundle = ures_getByIndex(subBundle, 0, NULL, &errorCode); UResourceBundle *subSubRootBundle = ures_getByIndex(subRootBundle, 0, NULL, &errorCode); if (U_SUCCESS(errorCode) && (ures_getType(subSubBundle) == URES_ARRAY || ures_getType(subSubRootBundle) == URES_ARRAY)) { /* TODO: Properly check for 2D arrays and zoneStrings */ if (subBundleKey != NULL && strcmp(subBundleKey, "zoneStrings") == 0) { /* int32_t minSize = ures_getSize(subBundle); int32_t idx; for (idx = 0; idx < minSize; idx++) { UResourceBundle *subSubBundleAtIndex = ures_getByIndex(subBundle, idx, NULL, &errorCode); if (ures_getSize(subSubBundleAtIndex) != 6) { log_err("zoneStrings at index %d has wrong size for locale \"%s\". array size=%d\n", idx, locale, ures_getSize(subSubBundleAtIndex)); } ures_close(subSubBundleAtIndex); }*/ } else { /* Here is one of the recursive parts */ TestKeyInRootRecursive(subRootBundle, subBundle, locale); } } else { int32_t minSize = ures_getSize(subRootBundle); int32_t idx; UBool sameArray = TRUE; if (minSize > ures_getSize(subBundle)) { minSize = ures_getSize(subBundle); } if ((subBundleKey == NULL || (subBundleKey != NULL && strcmp(subBundleKey, "LocaleScript") != 0)) && ures_getSize(subRootBundle) != ures_getSize(subBundle)) { log_err("Different size array with key \"%s\" in \"%s\" from root for locale \"%s\"\n" "\troot array size=%d, locale array size=%d\n", subBundleKey, ures_getKey(currentBundle), locale, ures_getSize(subRootBundle), ures_getSize(subBundle)); } for (idx = 0; idx < minSize; idx++) { int32_t rootStrLen, localeStrLen; const UChar *rootStr = ures_getStringByIndex(subRootBundle,idx,&rootStrLen,&errorCode); const UChar *localeStr = ures_getStringByIndex(subBundle,idx,&localeStrLen,&errorCode); if (rootStr && localeStr && U_SUCCESS(errorCode)) { if (u_strcmp(rootStr, localeStr) != 0) { sameArray = FALSE; } } else { log_err("Got a NULL string with key \"%s\" in \"%s\" at index %d for root or locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), idx, locale); continue; } if (localeStr[0] == (UChar)0x20) { log_err("key \"%s\" at index %d in \"%s\" starts with a space in locale \"%s\"\n", subBundleKey, idx, ures_getKey(currentBundle), locale); } else if (localeStr[localeStrLen - 1] == (UChar)0x20) { log_err("key \"%s\" at index %d in \"%s\" ends with a space in locale \"%s\"\n", subBundleKey, idx, ures_getKey(currentBundle), locale); } else if (subBundleKey != NULL && strcmp(subBundleKey, "DateTimePatterns") == 0) { int32_t quoted = 0; const UChar *localeStrItr = localeStr; while (*localeStrItr) { if (*localeStrItr == (UChar)0x27 /* ' */) { quoted++; } else if ((quoted % 2) == 0) { /* Search for unquoted characters */ if (4 <= idx && idx <= 7 && (*localeStrItr == (UChar)0x6B /* k */ || *localeStrItr == (UChar)0x48 /* H */ || *localeStrItr == (UChar)0x6D /* m */ || *localeStrItr == (UChar)0x73 /* s */ || *localeStrItr == (UChar)0x53 /* S */ || *localeStrItr == (UChar)0x61 /* a */ || *localeStrItr == (UChar)0x68 /* h */ || *localeStrItr == (UChar)0x7A /* z */)) { log_err("key \"%s\" at index %d has time pattern chars in date for locale \"%s\"\n", subBundleKey, idx, locale); } else if (0 <= idx && idx <= 3 && (*localeStrItr == (UChar)0x47 /* G */ || *localeStrItr == (UChar)0x79 /* y */ || *localeStrItr == (UChar)0x4D /* M */ || *localeStrItr == (UChar)0x64 /* d */ || *localeStrItr == (UChar)0x45 /* E */ || *localeStrItr == (UChar)0x44 /* D */ || *localeStrItr == (UChar)0x46 /* F */ || *localeStrItr == (UChar)0x77 /* w */ || *localeStrItr == (UChar)0x57 /* W */)) { log_err("key \"%s\" at index %d has date pattern chars in time for locale \"%s\"\n", subBundleKey, idx, locale); } } localeStrItr++; } } else if (idx == 4 && subBundleKey != NULL && strcmp(subBundleKey, "NumberElements") == 0 && u_charDigitValue(localeStr[0]) != 0) { log_err("key \"%s\" at index %d has a non-zero based number for locale \"%s\"\n", subBundleKey, idx, locale); } } if (sameArray) { log_err("Arrays are the same with key \"%s\" in \"%s\" from root for locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } } ures_close(subSubBundle); ures_close(subSubRootBundle); } else if (ures_getType(subBundle) == URES_STRING) { int32_t len = 0; const UChar *string = ures_getString(subBundle, &len, &errorCode); if (U_FAILURE(errorCode) || string == NULL) { log_err("Can't open a string with key \"%s\" in \"%s\" for locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } else if (string[0] == (UChar)0x20) { log_err("key \"%s\" in \"%s\" starts with a space in locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } else if (string[len - 1] == (UChar)0x20) { log_err("key \"%s\" in \"%s\" ends with a space in locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } else if (strcmp(subBundleKey, "localPatternChars") == 0 && len != 20) { log_err("key \"%s\" has the wrong number of characters in locale \"%s\"\n", subBundleKey, locale); } /* No fallback was done. Check for duplicate data */ /* The ures_* API does not do fallback of sub-resource bundles, So we can't do this now. */ else if (strcmp(locale, "root") != 0 && errorCode == U_ZERO_ERROR) { const UChar *rootString = ures_getString(subRootBundle, &len, &errorCode); if (U_FAILURE(errorCode) || rootString == NULL) { log_err("Can't open a string with key \"%s\" in \"%s\" in root\n", ures_getKey(subRootBundle), ures_getKey(currentBundle)); continue; } else if (u_strcmp(string, rootString) == 0) { if (strcmp(locale, "de_CH") != 0 && strcmp(subBundleKey, "Countries") != 0) { log_err("Found duplicate data with key \"%s\" in \"%s\" in locale \"%s\"\n", ures_getKey(subRootBundle), ures_getKey(currentBundle), locale); } else { /* Ignore for now. */ /* Can be fixed if fallback through de locale was done. */ log_verbose("Skipping key %s in %s\n", subBundleKey, locale); } } } } else if (ures_getType(subBundle) == URES_TABLE) { /* Here is one of the recursive parts */ TestKeyInRootRecursive(subRootBundle, subBundle, locale); } else if (ures_getType(subBundle) == URES_BINARY || ures_getType(subBundle) == URES_INT) { /* Can't do anything to check it */ /* We'll assume it's all correct */ if (strcmp(subBundleKey, "LocaleID") != 0) { log_verbose("Skipping key \"%s\" in \"%s\" for locale \"%s\"\n", subBundleKey, ures_getKey(currentBundle), locale); } /* Testing for LocaleID is done in testLCID */ } else { log_err("Type %d for key \"%s\" in \"%s\" is unknown for locale \"%s\"\n", ures_getType(subBundle), subBundleKey, ures_getKey(currentBundle), locale); } ures_close(subRootBundle); ures_close(subBundle); } } #ifdef WIN32 static void testLCID(UResourceBundle *currentBundle, const char *localeName) { UErrorCode status = U_ZERO_ERROR; uint32_t lcid; uint32_t expectedLCID; char lcidStringC[64] = {0}; int32_t lcidStringLen = 0; const UChar *lcidString = NULL; UResourceBundle *localeID = ures_getByKey(currentBundle, "LocaleID", NULL, &status); expectedLCID = ures_getInt(localeID, &status); ures_close(localeID); if (U_FAILURE(status)) { log_err("ERROR: %s does not have a LocaleID (%s)\n", localeName, u_errorName(status)); return; } lcid = uprv_convertToLCID(localeName, &status); if (U_FAILURE(status)) { if (expectedLCID == 0) { log_verbose("INFO: %-5s does not have any LCID mapping\n", localeName); } else { log_err("ERROR: %-5s does not have an LCID mapping to 0x%.4X\n", localeName, expectedLCID); } return; } status = U_ZERO_ERROR; uprv_strcpy(lcidStringC, uprv_convertToPosix(expectedLCID, &status)); if (U_FAILURE(status)) { log_err("ERROR: %.4x does not have a POSIX mapping due to %s\n", expectedLCID, u_errorName(status)); } if(lcid != expectedLCID) { log_err("ERROR: %-5s wrongfully has 0x%.4x instead of 0x%.4x for LCID\n", localeName, expectedLCID, lcid); } if(strcmp(localeName, lcidStringC) != 0) { char langName[1024]; char langLCID[1024]; uloc_getLanguage(localeName, langName, sizeof(langName), &status); uloc_getLanguage(lcidStringC, langLCID, sizeof(langLCID), &status); if (expectedLCID == lcid && strcmp(langName, langLCID) == 0) { log_verbose("WARNING: %-5s resolves to %s (0x%.4x)\n", localeName, lcidStringC, lcid); } else if (expectedLCID == lcid) { log_err("ERROR: %-5s has 0x%.4x and the number resolves wrongfully to %s\n", localeName, expectedLCID, lcidStringC); } else { log_err("ERROR: %-5s has 0x%.4x and the number resolves wrongfully to %s. It should be 0x%x.\n", localeName, expectedLCID, lcidStringC, lcid); } } } #endif static void TestLocaleStructure(void) { UResourceBundle *root, *currentLocale; int32_t locCount = uloc_countAvailable(); int32_t locIndex; UErrorCode errorCode = U_ZERO_ERROR; const char *currLoc; /* TODO: Compare against parent's data too. This code can't handle fallbacks that some tools do already. */ /* char locName[ULOC_FULLNAME_CAPACITY]; char *locNamePtr; for (locIndex = 0; locIndex < locCount; locIndex++) { errorCode=U_ZERO_ERROR; strcpy(locName, uloc_getAvailable(locIndex)); locNamePtr = strrchr(locName, '_'); if (locNamePtr) { *locNamePtr = 0; } else { strcpy(locName, "root"); } root = ures_openDirect(NULL, locName, &errorCode); if(U_FAILURE(errorCode)) { log_err("Can't open %s\n", locName); continue; } */ if (locCount <= 1) { log_data_err("At least root needs to be installed\n"); } root = ures_openDirect(NULL, "root", &errorCode); if(U_FAILURE(errorCode)) { log_data_err("Can't open root\n"); return; } for (locIndex = 0; locIndex < locCount; locIndex++) { errorCode=U_ZERO_ERROR; currLoc = uloc_getAvailable(locIndex); currentLocale = ures_open(NULL, currLoc, &errorCode); if(errorCode != U_ZERO_ERROR) { if(U_SUCCESS(errorCode)) { /* It's installed, but there is no data. It's installed for the g18n white paper [grhoten] */ log_data_err("ERROR: Locale %-5s not installed, and it should be!\n", uloc_getAvailable(locIndex)); } else { log_err("%%%%%%% Unexpected error %d in %s %%%%%%%", u_errorName(errorCode), uloc_getAvailable(locIndex)); } ures_close(currentLocale); continue; } ures_getStringByKey(currentLocale, "Version", NULL, &errorCode); if(errorCode != U_ZERO_ERROR) { log_err("No version information is available for locale %s, and it should be!\n", currLoc); } else if (ures_getStringByKey(currentLocale, "Version", NULL, &errorCode)[0] == (UChar)(0x78)) { log_verbose("WARNING: The locale %s is experimental! It shouldn't be listed as an installed locale.\n", currLoc); } TestKeyInRootRecursive(root, currentLocale, currLoc); #ifdef WIN32 testLCID(currentLocale, currLoc); #endif ures_close(currentLocale); } ures_close(root); } static void compareArrays(const char *keyName, UResourceBundle *fromArray, const char *fromLocale, UResourceBundle *toArray, const char *toLocale, int32_t start, int32_t end) { int32_t fromSize = ures_getSize(fromArray); int32_t toSize = ures_getSize(fromArray); int32_t idx; UErrorCode errorCode = U_ZERO_ERROR; if (fromSize > toSize) { fromSize = toSize; log_err("Arrays are different size from \"%s\" to \"%s\"\n", fromLocale, toLocale); } for (idx = start; idx <= end; idx++) { const UChar *fromBundleStr = ures_getStringByIndex(fromArray, idx, NULL, &errorCode); const UChar *toBundleStr = ures_getStringByIndex(toArray, idx, NULL, &errorCode); if (fromBundleStr && toBundleStr && u_strcmp(fromBundleStr, toBundleStr) != 0) { log_err("Difference for %s at index %d from %s= \"%s\" to %s= \"%s\"\n", keyName, idx, fromLocale, austrdup(fromBundleStr), toLocale, austrdup(toBundleStr)); } } } static void compareConsistentCountryInfo(const char *fromLocale, const char *toLocale) { UErrorCode errorCode = U_ZERO_ERROR; UResourceBundle *fromDateTimeElements, *toDateTimeElements; UResourceBundle *fromArray, *toArray; UResourceBundle *fromLocaleBund = ures_open(NULL, fromLocale, &errorCode); UResourceBundle *toLocaleBund = ures_open(NULL, toLocale, &errorCode); if(U_FAILURE(errorCode)) { log_err("Can't open resource bundle %s or %s\n", fromLocale, toLocale); return; } fromDateTimeElements = ures_getByKey(fromLocaleBund, "DateTimeElements", NULL, &errorCode); toDateTimeElements = ures_getByKey(toLocaleBund, "DateTimeElements", NULL, &errorCode); if (strcmp(fromLocale, "ar_IN") != 0) { int32_t fromSize; int32_t toSize; int32_t idx; const int32_t *fromBundleArr = ures_getIntVector(fromDateTimeElements, &fromSize, &errorCode); const int32_t *toBundleArr = ures_getIntVector(toDateTimeElements, &toSize, &errorCode); if (fromSize > toSize) { fromSize = toSize; log_err("Arrays are different size with key \"DateTimeElements\" from \"%s\" to \"%s\"\n", fromLocale, toLocale); } for (idx = 0; idx < fromSize; idx++) { if (fromBundleArr[idx] != toBundleArr[idx]) { log_err("Difference with key \"DateTimeElements\" at index %d from \"%s\" to \"%s\"\n", idx, fromLocale, toLocale); } } } ures_close(fromDateTimeElements); ures_close(toDateTimeElements); fromArray = ures_getByKey(fromLocaleBund, "CurrencyElements", NULL, &errorCode); toArray = ures_getByKey(toLocaleBund, "CurrencyElements", NULL, &errorCode); if (strcmp(fromLocale, "en_CA") != 0) { /* The first one is probably localized. */ compareArrays("CurrencyElements", fromArray, fromLocale, toArray, toLocale, 1, 2); } ures_close(fromArray); ures_close(toArray); fromArray = ures_getByKey(fromLocaleBund, "NumberPatterns", NULL, &errorCode); toArray = ures_getByKey(toLocaleBund, "NumberPatterns", NULL, &errorCode); if (strcmp(fromLocale, "en_CA") != 0) { compareArrays("NumberPatterns", fromArray, fromLocale, toArray, toLocale, 0, 3); } ures_close(fromArray); ures_close(toArray); /* Difficult to test properly */ /* fromArray = ures_getByKey(fromLocaleBund, "DateTimePatterns", NULL, &errorCode); toArray = ures_getByKey(toLocaleBund, "DateTimePatterns", NULL, &errorCode); { compareArrays("DateTimePatterns", fromArray, fromLocale, toArray, toLocale); } ures_close(fromArray); ures_close(toArray);*/ fromArray = ures_getByKey(fromLocaleBund, "NumberElements", NULL, &errorCode); toArray = ures_getByKey(toLocaleBund, "NumberElements", NULL, &errorCode); if (strcmp(fromLocale, "en_CA") != 0) { compareArrays("NumberElements", fromArray, fromLocale, toArray, toLocale, 0, 3); /* Index 4 is a script based 0 */ compareArrays("NumberElements", fromArray, fromLocale, toArray, toLocale, 5, 10); } ures_close(fromArray); ures_close(toArray); ures_close(fromLocaleBund); ures_close(toLocaleBund); } static void TestConsistentCountryInfo(void) { /* UResourceBundle *fromLocale, *toLocale;*/ int32_t locCount = uloc_countAvailable(); int32_t fromLocIndex, toLocIndex; int32_t fromCountryLen, toCountryLen; char fromCountry[ULOC_FULLNAME_CAPACITY], toCountry[ULOC_FULLNAME_CAPACITY]; int32_t fromVariantLen, toVariantLen; char fromVariant[ULOC_FULLNAME_CAPACITY], toVariant[ULOC_FULLNAME_CAPACITY]; UErrorCode errorCode = U_ZERO_ERROR; for (fromLocIndex = 0; fromLocIndex < locCount; fromLocIndex++) { const char *fromLocale = uloc_getAvailable(fromLocIndex); errorCode=U_ZERO_ERROR; fromCountryLen = uloc_getCountry(fromLocale, fromCountry, ULOC_FULLNAME_CAPACITY, &errorCode); if (fromCountryLen <= 0) { /* Ignore countryless locales */ continue; } fromVariantLen = uloc_getVariant(fromLocale, fromVariant, ULOC_FULLNAME_CAPACITY, &errorCode); if (fromVariantLen > 0) { /* Most variants are ignorable like PREEURO, or collation variants. */ continue; } /* Start comparing only after the current index. Previous loop should have already compared fromLocIndex. */ for (toLocIndex = fromLocIndex + 1; toLocIndex < locCount; toLocIndex++) { const char *toLocale = uloc_getAvailable(toLocIndex); toCountryLen = uloc_getCountry(toLocale, toCountry, ULOC_FULLNAME_CAPACITY, &errorCode); if(U_FAILURE(errorCode)) { log_err("Unknown failure fromLocale=%s toLocale=%s errorCode=%s\n", fromLocale, toLocale, u_errorName(errorCode)); continue; } if (toCountryLen <= 0) { /* Ignore countryless locales */ continue; } toVariantLen = uloc_getVariant(toLocale, toVariant, ULOC_FULLNAME_CAPACITY, &errorCode); if (toVariantLen > 0) { /* Most variants are ignorable like PREEURO, or collation variants. */ /* They're a variant for a reason. */ continue; } if (strcmp(fromCountry, toCountry) == 0) { log_verbose("comparing fromLocale=%s toLocale=%s\n", fromLocale, toLocale); compareConsistentCountryInfo(fromLocale, toLocale); } } } } static int32_t findStringSetMismatch(const UChar *string, int32_t langSize, const UChar *exemplarCharacters, int32_t exemplarLen, UBool ignoreNumbers) { UErrorCode errorCode = U_ZERO_ERROR; USet *exemplarSet = uset_openPatternOptions(exemplarCharacters, exemplarLen, USET_CASE_INSENSITIVE, &errorCode); int32_t strIdx; if (U_FAILURE(errorCode)) { log_err("error uset_openPattern returned %s\n", u_errorName(errorCode)); return -1; } for (strIdx = 0; strIdx < langSize; strIdx++) { if (!uset_contains(exemplarSet, string[strIdx]) && string[strIdx] != 0x0020 && string[strIdx] != 0x002e && string[strIdx] != 0x002c && string[strIdx] != 0x002d) { if (!ignoreNumbers || (ignoreNumbers && (string[strIdx] < 0x30 || string[strIdx] > 0x39))) { return strIdx; } } } uset_close(exemplarSet); return -1; } static void VerifyTranslation(void) { UResourceBundle *root, *currentLocale; int32_t locCount = uloc_countAvailable(); int32_t locIndex; UErrorCode errorCode = U_ZERO_ERROR; int32_t exemplarLen; const UChar *exemplarCharacters; const char *currLoc; UScriptCode scripts[USCRIPT_CODE_LIMIT]; int32_t numScripts; int32_t idx; int32_t end; UResourceBundle *resArray; if (locCount <= 1) { log_data_err("At least root needs to be installed\n"); } root = ures_openDirect(NULL, "root", &errorCode); if(U_FAILURE(errorCode)) { log_data_err("Can't open root\n"); return; } for (locIndex = 0; locIndex < locCount; locIndex++) { errorCode=U_ZERO_ERROR; currLoc = uloc_getAvailable(locIndex); currentLocale = ures_open(NULL, currLoc, &errorCode); if(errorCode != U_ZERO_ERROR) { if(U_SUCCESS(errorCode)) { /* It's installed, but there is no data. It's installed for the g18n white paper [grhoten] */ log_data_err("ERROR: Locale %-5s not installed, and it should be!\n", uloc_getAvailable(locIndex)); } else { log_err("%%%%%%% Unexpected error %d in %s %%%%%%%", u_errorName(errorCode), uloc_getAvailable(locIndex)); } ures_close(currentLocale); continue; } exemplarCharacters = ures_getStringByKey(currentLocale, "ExemplarCharacters", &exemplarLen, &errorCode); if (U_FAILURE(errorCode)) { log_err("error ures_getStringByKey returned %s\n", u_errorName(errorCode)); } else if (QUICK && exemplarLen > 2048) { log_verbose("skipping test for %s\n", currLoc); } else { UChar langBuffer[128]; int32_t langSize; int32_t strIdx; langSize = uloc_getDisplayLanguage(currLoc, currLoc, langBuffer, sizeof(langBuffer)/sizeof(langBuffer[0]), &errorCode); if (U_FAILURE(errorCode)) { log_err("error uloc_getDisplayLanguage returned %s\n", u_errorName(errorCode)); } else { strIdx = findStringSetMismatch(langBuffer, langSize, exemplarCharacters, exemplarLen, FALSE); if (strIdx >= 0) { log_err("getDisplayLanguage(%s) at index %d returned characters not in the exemplar characters.\n", currLoc, strIdx); } } langSize = uloc_getDisplayCountry(currLoc, currLoc, langBuffer, sizeof(langBuffer)/sizeof(langBuffer[0]), &errorCode); if (U_FAILURE(errorCode)) { log_err("error uloc_getDisplayCountry returned %s\n", u_errorName(errorCode)); } else { strIdx = findStringSetMismatch(langBuffer, langSize, exemplarCharacters, exemplarLen, FALSE); if (strIdx >= 0) { log_err("getDisplayCountry(%s) at index %d returned characters not in the exemplar characters.\n", currLoc, strIdx); } } resArray = ures_getByKey(currentLocale, "DayNames", NULL, &errorCode); if (U_FAILURE(errorCode)) { log_err("error ures_getByKey returned %s\n", u_errorName(errorCode)); } if (QUICK) { end = 1; } else { end = ures_getSize(resArray); } for (idx = 0; idx < end; idx++) { const UChar *fromBundleStr = ures_getStringByIndex(resArray, idx, &langSize, &errorCode); if (U_FAILURE(errorCode)) { log_err("error ures_getStringByIndex(%d) returned %s\n", idx, u_errorName(errorCode)); continue; } strIdx = findStringSetMismatch(fromBundleStr, langSize, exemplarCharacters, exemplarLen, TRUE); if (strIdx >= 0) { log_err("getDayNames(%s, %d) at index %d returned characters not in the exemplar characters.\n", currLoc, idx, strIdx); } } ures_close(resArray); resArray = ures_getByKey(currentLocale, "MonthNames", NULL, &errorCode); if (U_FAILURE(errorCode)) { log_err("error ures_getByKey returned %s\n", u_errorName(errorCode)); } if (QUICK) { end = 1; } else { end = ures_getSize(resArray); } for (idx = 0; idx < end; idx++) { const UChar *fromBundleStr = ures_getStringByIndex(resArray, idx, &langSize, &errorCode); if (U_FAILURE(errorCode)) { log_err("error ures_getStringByIndex(%d) returned %s\n", idx, u_errorName(errorCode)); continue; } strIdx = findStringSetMismatch(fromBundleStr, langSize, exemplarCharacters, exemplarLen, TRUE); if (strIdx >= 0) { log_err("getMonthNames(%s, %d) at index %d returned characters not in the exemplar characters.\n", currLoc, idx, strIdx); } } ures_close(resArray); errorCode = U_ZERO_ERROR; numScripts = uscript_getCode(currLoc, scripts, sizeof(scripts)/sizeof(scripts[0]), &errorCode); if (numScripts == 0) { log_err("uscript_getCode(%s) doesn't work.\n", currLoc); } /* TODO: test that the scripts are a superset of exemplar characters. */ } ures_close(currentLocale); } ures_close(root); }