2 * Copyright 2003-2012, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Oliver Tappe, zooey@hirschkaefer.de
11 #include <unicode/uversion.h>
12 #include <LocaleRoster.h>
23 #include <FormattingConventions.h>
25 #include <IconUtils.h>
28 #include <LocaleRosterData.h>
29 #include <MutableLocaleRoster.h>
35 #include <ICUWrapper.h>
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
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])) {
78 case 'cs': // Czech Republic
84 case 'en': // United Kingdom
90 case 'ko': // South Korea
94 case 'pa': // Pakistan
103 // Languages with a matching country name
104 case 'de': // Germany
106 case 'fi': // Finland
108 case 'hr': // Croatia
109 case 'hu': // Hungary
111 case 'lt': // Lithuania
112 case 'nl': // Netherlands
114 case 'pt': // Portugal
115 case 'ro': // Romania
117 case 'sk': // Slovakia
118 return language
.Code();
128 BLocaleRoster::BLocaleRoster()
130 fData(new(std::nothrow
) BPrivate::LocaleRosterData(BLanguage("en_US"),
131 BFormattingConventions("en_US")))
136 BLocaleRoster::~BLocaleRoster()
142 /*static*/ BLocaleRoster
*
143 BLocaleRoster::Default()
145 return MutableLocaleRoster::Default();
150 BLocaleRoster::Refresh()
152 return fData
->Refresh();
157 BLocaleRoster::GetDefaultTimeZone(BTimeZone
* timezone
) const
162 BAutolock
lock(fData
->fLock
);
163 if (!lock
.IsLocked())
166 *timezone
= fData
->fDefaultTimeZone
;
173 BLocaleRoster::GetDefaultLocale() const
175 return &fData
->fDefaultLocale
;
180 BLocaleRoster::GetLanguage(const char* languageCode
,
181 BLanguage
** _language
) const
183 if (_language
== NULL
|| languageCode
== NULL
|| languageCode
[0] == '\0')
186 BLanguage
* language
= new(std::nothrow
) BLanguage(languageCode
);
187 if (language
== NULL
)
190 *_language
= language
;
196 BLocaleRoster::GetPreferredLanguages(BMessage
* languages
) const
201 BAutolock
lock(fData
->fLock
);
202 if (!lock
.IsLocked())
205 *languages
= fData
->fPreferredLanguages
;
212 * \brief Fills \c message with 'language'-fields containing the language-
213 * ID(s) of all available languages.
216 BLocaleRoster::GetAvailableLanguages(BMessage
* languages
) const
222 const Locale
* icuLocaleList
= Locale::getAvailableLocales(localeCount
);
224 for (int i
= 0; i
< localeCount
; i
++)
225 languages
->AddString("language", icuLocaleList
[i
].getName());
232 BLocaleRoster::GetAvailableCountries(BMessage
* countries
) const
238 const char* const* countryList
= uloc_getISOCountries();
240 for (i
= 0; countryList
[i
] != NULL
; i
++)
241 countries
->AddString("country", countryList
[i
]);
248 BLocaleRoster::GetAvailableTimeZones(BMessage
* timeZones
) const
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
)) {
266 timeZones
->AddString("timeZone", zoneID
);
278 BLocaleRoster::GetAvailableTimeZonesWithRegionInfo(BMessage
* timeZones
) const
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
)) {
298 timeZones
->AddString("timeZone", zoneID
);
301 icuStatus
= U_ZERO_ERROR
;
302 TimeZone::getRegion(zoneID
, region
, 5, icuStatus
);
303 if (!U_SUCCESS(icuStatus
)) {
307 timeZones
->AddString("region", region
);
319 BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage
* timeZones
,
320 const char* countryCode
) const
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
)) {
339 timeZones
->AddString("timeZone", zoneID
);
351 BLocaleRoster::GetFlagIconForCountry(BBitmap
* flagIcon
, const char* countryCode
)
353 if (countryCode
== NULL
)
356 BAutolock
lock(fData
->fLock
);
357 if (!lock
.IsLocked())
360 BResources
* resources
;
361 status_t status
= fData
->GetResources(&resources
);
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
);
372 char normalizedCode
[3];
373 normalizedCode
[0] = toupper(countryCode
[codeLength
- 2]);
374 normalizedCode
[1] = toupper(countryCode
[codeLength
- 1]);
375 normalizedCode
[2] = '\0';
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
,
389 BLocaleRoster::GetFlagIconForLanguage(BBitmap
* flagIcon
,
390 const char* languageCode
)
392 if (languageCode
== NULL
|| languageCode
[0] == '\0'
393 || languageCode
[1] == '\0')
396 BAutolock
lock(fData
->fLock
);
397 if (!lock
.IsLocked())
400 BResources
* resources
;
401 status_t status
= fData
->GetResources(&resources
);
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';
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
),
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
);
433 BLocaleRoster::GetAvailableCatalogs(BMessage
* languageList
,
434 const char* sigPattern
, const char* langPattern
, int32 fingerprint
) const
436 if (languageList
== NULL
)
439 BAutolock
lock(fData
->fLock
);
440 if (!lock
.IsLocked())
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
)
451 info
->fLanguagesFunc(languageList
, sigPattern
, langPattern
,
460 BLocaleRoster::IsFilesystemTranslationPreferred() const
462 BAutolock
lock(fData
->fLock
);
463 if (!lock
.IsLocked())
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
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.
477 - \c B_ENTRY_NOT_FOUND: failure. Attribute not found, entry not found
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.
488 BLocaleRoster::GetLocalizedFileName(BString
& localizedFileName
,
489 const entry_ref
& ref
, bool traverse
)
495 status_t status
= _PrepareCatalogEntry(ref
, signature
, context
, string
,
501 // Try to get entry_ref for signature from above
503 entry_ref catalogRef
;
504 // The signature is missing application/
505 signature
.Prepend("application/");
506 status
= roster
.FindApp(signature
, &catalogRef
);
510 BCatalog
catalog(catalogRef
);
511 const char* temp
= catalog
.GetString(string
, context
);
514 return B_ENTRY_NOT_FOUND
;
516 localizedFileName
= temp
;
522 _InitializeCatalog(void* param
)
524 BCatalog
* catalog
= (BCatalog
*)param
;
526 // figure out image (shared object) from catalog address
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
) {
540 return B_NAME_NOT_FOUND
;
542 // load the catalog for this mimetype
544 if (BEntry(info
.name
).GetRef(&ref
) == B_OK
&& catalog
->SetTo(ref
) == B_OK
);
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
);
563 BLocaleRoster::_PrepareCatalogEntry(const entry_ref
& ref
, BString
& signature
,
564 BString
& context
, BString
& string
, bool traverse
)
566 BEntry
entry(&ref
, traverse
);
568 return B_ENTRY_NOT_FOUND
;
571 status_t status
= node
.InitCheck();
575 status
= node
.ReadAttrString("SYS:NAME", &signature
);
579 int32 first
= signature
.FindFirst(':');
580 int32 last
= signature
.FindLast(':');
582 return B_ENTRY_NOT_FOUND
;
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
;