BPicture: Fix archive constructor.
[haiku.git] / src / kits / locale / LocaleRoster.cpp
blob1770960180d9c372212a596541db46144a76ba93
1 /*
2 * Copyright 2003-2012, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Oliver Tappe, zooey@hirschkaefer.de
8 */
11 #include <unicode/uversion.h>
12 #include <LocaleRoster.h>
14 #include <assert.h>
15 #include <ctype.h>
17 #include <new>
19 #include <Autolock.h>
20 #include <Bitmap.h>
21 #include <Catalog.h>
22 #include <Entry.h>
23 #include <FormattingConventions.h>
24 #include <fs_attr.h>
25 #include <IconUtils.h>
26 #include <Language.h>
27 #include <Locale.h>
28 #include <LocaleRosterData.h>
29 #include <MutableLocaleRoster.h>
30 #include <Node.h>
31 #include <Roster.h>
32 #include <String.h>
33 #include <TimeZone.h>
35 #include <ICUWrapper.h>
36 #include <locks.h>
38 // ICU includes
39 #include <unicode/locdspnm.h>
40 #include <unicode/locid.h>
41 #include <unicode/timezone.h>
44 using BPrivate::CatalogAddOnInfo;
45 using BPrivate::MutableLocaleRoster;
49 * several attributes/resource-IDs used within the Locale Kit:
51 const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
52 // name of catalog language, lives in every catalog file
53 const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
54 // catalog signature, lives in every catalog file
55 const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
56 // catalog fingerprint, may live in catalog file
58 const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG";
59 // attribute which contains flattened data of embedded catalog
60 // this may live in an app- or add-on-file
61 int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA;
62 // a unique value used to identify the resource (=> embedded CAtalog DAta)
63 // which contains flattened data of embedded catalog.
64 // this may live in an app- or add-on-file
67 static const char*
68 country_code_for_language(const BLanguage& language)
70 if (language.IsCountrySpecific())
71 return language.CountryCode();
73 // TODO: implement for real! For now, we just map some well known
74 // languages to countries to make FirstBootPrompt happy.
75 switch ((tolower(language.Code()[0]) << 8) | tolower(language.Code()[1])) {
76 case 'be': // Belarus
77 return "BY";
78 case 'cs': // Czech Republic
79 return "CZ";
80 case 'da': // Denmark
81 return "DK";
82 case 'el': // Greece
83 return "GR";
84 case 'en': // United Kingdom
85 return "GB";
86 case 'hi': // India
87 return "IN";
88 case 'ja': // Japan
89 return "JP";
90 case 'ko': // South Korea
91 return "KR";
92 case 'nb': // Norway
93 return "NO";
94 case 'pa': // Pakistan
95 return "PK";
96 case 'sv': // Sweden
97 return "SE";
98 case 'uk': // Ukraine
99 return "UA";
100 case 'zh': // China
101 return "CN";
103 // Languages with a matching country name
104 case 'de': // Germany
105 case 'es': // Spain
106 case 'fi': // Finland
107 case 'fr': // France
108 case 'hr': // Croatia
109 case 'hu': // Hungary
110 case 'it': // Italy
111 case 'lt': // Lithuania
112 case 'nl': // Netherlands
113 case 'pl': // Poland
114 case 'pt': // Portugal
115 case 'ro': // Romania
116 case 'ru': // Russia
117 case 'sk': // Slovakia
118 return language.Code();
121 return NULL;
125 // #pragma mark -
128 BLocaleRoster::BLocaleRoster()
130 fData(new(std::nothrow) BPrivate::LocaleRosterData(BLanguage("en_US"),
131 BFormattingConventions("en_US")))
136 BLocaleRoster::~BLocaleRoster()
138 delete fData;
142 /*static*/ BLocaleRoster*
143 BLocaleRoster::Default()
145 return MutableLocaleRoster::Default();
149 status_t
150 BLocaleRoster::Refresh()
152 return fData->Refresh();
156 status_t
157 BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const
159 if (!timezone)
160 return B_BAD_VALUE;
162 BAutolock lock(fData->fLock);
163 if (!lock.IsLocked())
164 return B_ERROR;
166 *timezone = fData->fDefaultTimeZone;
168 return B_OK;
172 const BLocale*
173 BLocaleRoster::GetDefaultLocale() const
175 return &fData->fDefaultLocale;
179 status_t
180 BLocaleRoster::GetLanguage(const char* languageCode,
181 BLanguage** _language) const
183 if (_language == NULL || languageCode == NULL || languageCode[0] == '\0')
184 return B_BAD_VALUE;
186 BLanguage* language = new(std::nothrow) BLanguage(languageCode);
187 if (language == NULL)
188 return B_NO_MEMORY;
190 *_language = language;
191 return B_OK;
195 status_t
196 BLocaleRoster::GetPreferredLanguages(BMessage* languages) const
198 if (!languages)
199 return B_BAD_VALUE;
201 BAutolock lock(fData->fLock);
202 if (!lock.IsLocked())
203 return B_ERROR;
205 *languages = fData->fPreferredLanguages;
207 return B_OK;
212 * \brief Fills \c message with 'language'-fields containing the language-
213 * ID(s) of all available languages.
215 status_t
216 BLocaleRoster::GetAvailableLanguages(BMessage* languages) const
218 if (!languages)
219 return B_BAD_VALUE;
221 int32_t localeCount;
222 const Locale* icuLocaleList = Locale::getAvailableLocales(localeCount);
224 for (int i = 0; i < localeCount; i++)
225 languages->AddString("language", icuLocaleList[i].getName());
227 return B_OK;
231 status_t
232 BLocaleRoster::GetAvailableCountries(BMessage* countries) const
234 if (!countries)
235 return B_BAD_VALUE;
237 int32 i;
238 const char* const* countryList = uloc_getISOCountries();
240 for (i = 0; countryList[i] != NULL; i++)
241 countries->AddString("country", countryList[i]);
243 return B_OK;
247 status_t
248 BLocaleRoster::GetAvailableTimeZones(BMessage* timeZones) const
250 if (!timeZones)
251 return B_BAD_VALUE;
253 status_t status = B_OK;
255 StringEnumeration* zoneList = TimeZone::createEnumeration();
257 UErrorCode icuStatus = U_ZERO_ERROR;
258 int32 count = zoneList->count(icuStatus);
259 if (U_SUCCESS(icuStatus)) {
260 for (int i = 0; i < count; ++i) {
261 const char* zoneID = zoneList->next(NULL, icuStatus);
262 if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
263 status = B_ERROR;
264 break;
266 timeZones->AddString("timeZone", zoneID);
268 } else
269 status = B_ERROR;
271 delete zoneList;
273 return status;
277 status_t
278 BLocaleRoster::GetAvailableTimeZonesWithRegionInfo(BMessage* timeZones) const
280 if (!timeZones)
281 return B_BAD_VALUE;
283 status_t status = B_OK;
285 UErrorCode icuStatus = U_ZERO_ERROR;
287 StringEnumeration* zoneList = TimeZone::createTimeZoneIDEnumeration(
288 UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, icuStatus);
290 int32 count = zoneList->count(icuStatus);
291 if (U_SUCCESS(icuStatus)) {
292 for (int i = 0; i < count; ++i) {
293 const char* zoneID = zoneList->next(NULL, icuStatus);
294 if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
295 status = B_ERROR;
296 break;
298 timeZones->AddString("timeZone", zoneID);
300 char region[5];
301 icuStatus = U_ZERO_ERROR;
302 TimeZone::getRegion(zoneID, region, 5, icuStatus);
303 if (!U_SUCCESS(icuStatus)) {
304 status = B_ERROR;
305 break;
307 timeZones->AddString("region", region);
309 } else
310 status = B_ERROR;
312 delete zoneList;
314 return status;
318 status_t
319 BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage* timeZones,
320 const char* countryCode) const
322 if (!timeZones)
323 return B_BAD_VALUE;
325 status_t status = B_OK;
327 StringEnumeration* zoneList = TimeZone::createEnumeration(countryCode);
328 // countryCode == NULL will yield all timezones not bound to a country
330 UErrorCode icuStatus = U_ZERO_ERROR;
331 int32 count = zoneList->count(icuStatus);
332 if (U_SUCCESS(icuStatus)) {
333 for (int i = 0; i < count; ++i) {
334 const char* zoneID = zoneList->next(NULL, icuStatus);
335 if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
336 status = B_ERROR;
337 break;
339 timeZones->AddString("timeZone", zoneID);
341 } else
342 status = B_ERROR;
344 delete zoneList;
346 return status;
350 status_t
351 BLocaleRoster::GetFlagIconForCountry(BBitmap* flagIcon, const char* countryCode)
353 if (countryCode == NULL)
354 return B_BAD_VALUE;
356 BAutolock lock(fData->fLock);
357 if (!lock.IsLocked())
358 return B_ERROR;
360 BResources* resources;
361 status_t status = fData->GetResources(&resources);
362 if (status != B_OK)
363 return status;
365 // Normalize the country code: 2 letters uppercase
366 // filter things out so that "pt_BR" gives the flag for brazil
368 int codeLength = strlen(countryCode);
369 if (codeLength < 2)
370 return B_BAD_VALUE;
372 char normalizedCode[3];
373 normalizedCode[0] = toupper(countryCode[codeLength - 2]);
374 normalizedCode[1] = toupper(countryCode[codeLength - 1]);
375 normalizedCode[2] = '\0';
377 size_t size;
378 const void* buffer = resources->LoadResource(B_VECTOR_ICON_TYPE,
379 normalizedCode, &size);
380 if (buffer == NULL || size == 0)
381 return B_NAME_NOT_FOUND;
383 return BIconUtils::GetVectorIcon(static_cast<const uint8*>(buffer), size,
384 flagIcon);
388 status_t
389 BLocaleRoster::GetFlagIconForLanguage(BBitmap* flagIcon,
390 const char* languageCode)
392 if (languageCode == NULL || languageCode[0] == '\0'
393 || languageCode[1] == '\0')
394 return B_BAD_VALUE;
396 BAutolock lock(fData->fLock);
397 if (!lock.IsLocked())
398 return B_ERROR;
400 BResources* resources;
401 status_t status = fData->GetResources(&resources);
402 if (status != B_OK)
403 return status;
405 // Normalize the language code: first two letters, lowercase
407 char normalizedCode[3];
408 normalizedCode[0] = tolower(languageCode[0]);
409 normalizedCode[1] = tolower(languageCode[1]);
410 normalizedCode[2] = '\0';
412 size_t size;
413 const void* buffer = resources->LoadResource(B_VECTOR_ICON_TYPE,
414 normalizedCode, &size);
415 if (buffer != NULL && size != 0) {
416 return BIconUtils::GetVectorIcon(static_cast<const uint8*>(buffer),
417 size, flagIcon);
420 // There is no language flag, try to get the default country's flag for
421 // the language instead.
423 BLanguage language(languageCode);
424 const char* countryCode = country_code_for_language(language);
425 if (countryCode == NULL)
426 return B_NAME_NOT_FOUND;
428 return GetFlagIconForCountry(flagIcon, countryCode);
432 status_t
433 BLocaleRoster::GetAvailableCatalogs(BMessage* languageList,
434 const char* sigPattern, const char* langPattern, int32 fingerprint) const
436 if (languageList == NULL)
437 return B_BAD_VALUE;
439 BAutolock lock(fData->fLock);
440 if (!lock.IsLocked())
441 return B_ERROR;
443 int32 count = fData->fCatalogAddOnInfos.CountItems();
444 for (int32 i = 0; i < count; ++i) {
445 CatalogAddOnInfo* info
446 = (CatalogAddOnInfo*)fData->fCatalogAddOnInfos.ItemAt(i);
448 if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc)
449 continue;
451 info->fLanguagesFunc(languageList, sigPattern, langPattern,
452 fingerprint);
455 return B_OK;
459 bool
460 BLocaleRoster::IsFilesystemTranslationPreferred() const
462 BAutolock lock(fData->fLock);
463 if (!lock.IsLocked())
464 return B_ERROR;
466 return fData->fIsFilesystemTranslationPreferred;
470 /*! \brief Looks up a localized filename from a catalog.
471 \param localizedFileName A pre-allocated BString object for the result
472 of the lookup.
473 \param ref An entry_ref with an attribute holding data for catalog lookup.
474 \param traverse A boolean to decide if symlinks are to be traversed.
475 \return
476 - \c B_OK: success
477 - \c B_ENTRY_NOT_FOUND: failure. Attribute not found, entry not found
478 in catalog, etc
479 - other error codes: failure
481 Attribute format: "signature:context:string"
482 (no colon in any of signature, context and string)
484 Lookup is done for the top preferred language, only.
485 Lookup fails if a comment is present in the catalog entry.
487 status_t
488 BLocaleRoster::GetLocalizedFileName(BString& localizedFileName,
489 const entry_ref& ref, bool traverse)
491 BString signature;
492 BString context;
493 BString string;
495 status_t status = _PrepareCatalogEntry(ref, signature, context, string,
496 traverse);
498 if (status != B_OK)
499 return status;
501 // Try to get entry_ref for signature from above
502 BRoster roster;
503 entry_ref catalogRef;
504 // The signature is missing application/
505 signature.Prepend("application/");
506 status = roster.FindApp(signature, &catalogRef);
507 if (status != B_OK)
508 return status;
510 BCatalog catalog(catalogRef);
511 const char* temp = catalog.GetString(string, context);
513 if (temp == NULL)
514 return B_ENTRY_NOT_FOUND;
516 localizedFileName = temp;
517 return B_OK;
521 static status_t
522 _InitializeCatalog(void* param)
524 BCatalog* catalog = (BCatalog*)param;
526 // figure out image (shared object) from catalog address
527 image_info info;
528 int32 cookie = 0;
529 bool found = false;
531 while (get_next_image_info(0, &cookie, &info) == B_OK) {
532 if ((char*)info.data < (char*)catalog && (char*)info.data
533 + info.data_size > (char*)catalog) {
534 found = true;
535 break;
539 if (!found)
540 return B_NAME_NOT_FOUND;
542 // load the catalog for this mimetype
543 entry_ref ref;
544 if (BEntry(info.name).GetRef(&ref) == B_OK && catalog->SetTo(ref) == B_OK);
545 return B_OK;
547 return B_ERROR;
551 BCatalog*
552 BLocaleRoster::_GetCatalog(BCatalog* catalog, int32* catalogInitStatus)
554 // This function is used in the translation macros, so it can't return a
555 // status_t. Maybe it could throw exceptions ?
557 __init_once(catalogInitStatus, _InitializeCatalog, catalog);
558 return catalog;
562 status_t
563 BLocaleRoster::_PrepareCatalogEntry(const entry_ref& ref, BString& signature,
564 BString& context, BString& string, bool traverse)
566 BEntry entry(&ref, traverse);
567 if (!entry.Exists())
568 return B_ENTRY_NOT_FOUND;
570 BNode node(&entry);
571 status_t status = node.InitCheck();
572 if (status != B_OK)
573 return status;
575 status = node.ReadAttrString("SYS:NAME", &signature);
576 if (status != B_OK)
577 return status;
579 int32 first = signature.FindFirst(':');
580 int32 last = signature.FindLast(':');
581 if (first == last)
582 return B_ENTRY_NOT_FOUND;
584 context = signature;
585 string = signature;
587 signature.Truncate(first);
588 context.Truncate(last);
589 context.Remove(0, first + 1);
590 string.Remove(0, last + 1);
592 if (signature.Length() == 0 || context.Length() == 0
593 || string.Length() == 0)
594 return B_ENTRY_NOT_FOUND;
596 return B_OK;