1 /* 2 * Copyright 2003-2010, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Oliver Tappe, zooey@hirschkaefer.de 8 */ 9 10 11 #include <LocaleRoster.h> 12 13 #include <set> 14 15 #include <assert.h> 16 #include <syslog.h> 17 18 #include <Autolock.h> 19 #include <AppFileInfo.h> 20 #include <Catalog.h> 21 #include <Collator.h> 22 #include <Country.h> 23 #include <DefaultCatalog.h> 24 #include <Directory.h> 25 #include <Entry.h> 26 #include <File.h> 27 #include <Language.h> 28 #include <Locale.h> 29 #include <MutableLocaleRoster.h> 30 #include <Node.h> 31 #include <Path.h> 32 #include <String.h> 33 #include <TimeZone.h> 34 35 #include <ICUWrapper.h> 36 37 // ICU includes 38 #include <unicode/locid.h> 39 #include <unicode/timezone.h> 40 41 42 using BPrivate::CatalogAddOnInfo; 43 44 45 /* 46 * several attributes/resource-IDs used within the Locale Kit: 47 */ 48 const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; 49 // name of catalog language, lives in every catalog file 50 const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; 51 // catalog signature, lives in every catalog file 52 const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; 53 // catalog fingerprint, may live in catalog file 54 55 const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG"; 56 // attribute which contains flattened data of embedded catalog 57 // this may live in an app- or add-on-file 58 int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA; 59 // a unique value used to identify the resource (=> embedded CAtalog DAta) 60 // which contains flattened data of embedded catalog. 61 // this may live in an app- or add-on-file 62 63 64 BLocaleRoster::BLocaleRoster() 65 { 66 } 67 68 69 BLocaleRoster::~BLocaleRoster() 70 { 71 } 72 73 74 status_t 75 BLocaleRoster::Refresh() 76 { 77 return gRosterData.Refresh(); 78 } 79 80 81 status_t 82 BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const 83 { 84 if (!timezone) 85 return B_BAD_VALUE; 86 87 BAutolock lock(gRosterData.fLock); 88 if (!lock.IsLocked()) 89 return B_ERROR; 90 91 *timezone = gRosterData.fDefaultTimeZone; 92 93 return B_OK; 94 } 95 96 97 status_t 98 BLocaleRoster::GetLanguage(const char* languageCode, 99 BLanguage** _language) const 100 { 101 if (_language == NULL || languageCode == NULL || languageCode[0] == '\0') 102 return B_BAD_VALUE; 103 104 BLanguage* language = new(std::nothrow) BLanguage(languageCode); 105 if (language == NULL) 106 return B_NO_MEMORY; 107 108 *_language = language; 109 return B_OK; 110 } 111 112 113 status_t 114 BLocaleRoster::GetPreferredLanguages(BMessage* languages) const 115 { 116 if (!languages) 117 return B_BAD_VALUE; 118 119 BAutolock lock(gRosterData.fLock); 120 if (!lock.IsLocked()) 121 return B_ERROR; 122 123 *languages = gRosterData.fPreferredLanguages; 124 125 return B_OK; 126 } 127 128 129 status_t 130 BLocaleRoster::GetInstalledLanguages(BMessage* languages) const 131 { 132 if (!languages) 133 return B_BAD_VALUE; 134 135 int32 i; 136 UnicodeString icuLanguageName; 137 BString languageName; 138 139 int32_t localeCount; 140 const Locale* icuLocaleList 141 = Locale::getAvailableLocales(localeCount); 142 143 // TODO: Loop over the strings and add them to a std::set to remove 144 // duplicates? 145 for (i = 0; i < localeCount; i++) 146 languages->AddString("langs", icuLocaleList[i].getName()); 147 148 return B_OK; 149 } 150 151 152 status_t 153 BLocaleRoster::GetAvailableCountries(BMessage* countries) const 154 { 155 if (!countries) 156 return B_BAD_VALUE; 157 158 int32 i; 159 const char* const* countryList = uloc_getISOCountries(); 160 161 for (i = 0; countryList[i] != NULL; i++) 162 countries->AddString("countries", countryList[i]); 163 164 return B_OK; 165 } 166 167 168 status_t 169 BLocaleRoster::GetAvailableTimeZones(BMessage* timeZones) const 170 { 171 if (!timeZones) 172 return B_BAD_VALUE; 173 174 status_t status = B_OK; 175 176 StringEnumeration* zoneList = TimeZone::createEnumeration(); 177 178 UErrorCode icuStatus = U_ZERO_ERROR; 179 int32 count = zoneList->count(icuStatus); 180 if (U_SUCCESS(icuStatus)) { 181 for (int i = 0; i < count; ++i) { 182 const char* zoneID = zoneList->next(NULL, icuStatus); 183 if (zoneID == NULL || !U_SUCCESS(icuStatus)) { 184 status = B_ERROR; 185 break; 186 } 187 timeZones->AddString("timeZone", zoneID); 188 } 189 } else 190 status = B_ERROR; 191 192 delete zoneList; 193 194 return status; 195 } 196 197 198 status_t 199 BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage* timeZones, 200 const char* countryCode) const 201 { 202 if (!timeZones) 203 return B_BAD_VALUE; 204 205 status_t status = B_OK; 206 207 StringEnumeration* zoneList = TimeZone::createEnumeration(countryCode); 208 // countryCode == NULL will yield all timezones not bound to a country 209 210 UErrorCode icuStatus = U_ZERO_ERROR; 211 int32 count = zoneList->count(icuStatus); 212 if (U_SUCCESS(icuStatus)) { 213 for (int i = 0; i < count; ++i) { 214 const char* zoneID = zoneList->next(NULL, icuStatus); 215 if (zoneID == NULL || !U_SUCCESS(icuStatus)) { 216 status = B_ERROR; 217 break; 218 } 219 timeZones->AddString("timeZone", zoneID); 220 } 221 } else 222 status = B_ERROR; 223 224 delete zoneList; 225 226 return status; 227 } 228 229 230 status_t 231 BLocaleRoster::GetInstalledCatalogs(BMessage* languageList, 232 const char* sigPattern, const char* langPattern, int32 fingerprint) const 233 { 234 if (languageList == NULL) 235 return B_BAD_VALUE; 236 237 BAutolock lock(gRosterData.fLock); 238 if (!lock.IsLocked()) 239 return B_ERROR; 240 241 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 242 for (int32 i = 0; i < count; ++i) { 243 CatalogAddOnInfo* info 244 = (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 245 246 if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc) 247 continue; 248 249 info->fLanguagesFunc(languageList, sigPattern, langPattern, 250 fingerprint); 251 } 252 253 return B_OK; 254 } 255 256 257 BCatalog* 258 BLocaleRoster::_GetCatalog(BCatalog* catalog, vint32* catalogInitStatus) 259 { 260 // This function is used in the translation macros, so it can't return a 261 // status_t. Maybe it could throw exceptions ? 262 263 if (*catalogInitStatus == true) { 264 // Catalog already loaded - nothing else to do 265 return catalog; 266 } 267 268 // figure out image (shared object) from catalog address 269 image_info info; 270 int32 cookie = 0; 271 bool found = false; 272 273 while (get_next_image_info(0, &cookie, &info) == B_OK) { 274 if ((char*)info.data < (char*)catalog && (char*)info.data 275 + info.data_size > (char*)catalog) { 276 found = true; 277 break; 278 } 279 } 280 281 if (!found) { 282 log_team(LOG_DEBUG, "Catalog %x doesn't belong to any image !", 283 catalog); 284 return catalog; 285 } 286 // figure out mimetype from image 287 BFile objectFile(info.name, B_READ_ONLY); 288 BAppFileInfo objectInfo(&objectFile); 289 char objectSignature[B_MIME_TYPE_LENGTH]; 290 if (objectInfo.GetSignature(objectSignature) != B_OK) { 291 log_team(LOG_ERR, "File %s has no mimesignature, so it can't use" 292 "localization.", info.name); 293 return catalog; 294 } 295 296 // drop supertype from mimetype (should be "application/"): 297 char* stripSignature = objectSignature; 298 while (*stripSignature != '/') 299 stripSignature ++; 300 stripSignature ++; 301 302 log_team(LOG_DEBUG, 303 "Image %s (address %x) requested catalog with mimetype %s", 304 info.name, catalog, stripSignature); 305 306 // load the catalog for this mimetype and return it to the app 307 catalog->SetCatalog(stripSignature, 0); 308 *catalogInitStatus = true; 309 310 return catalog; 311 } 312