xref: /haiku/src/kits/locale/Locale.cpp (revision 1345706a9ff6ad0dc041339a02d4259998b0765d)
1 /*
2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include <AutoDeleter.h>
8 #include <CalendarView.h>
9 #include <Catalog.h>
10 #include <Locale.h>
11 #include <LocaleRoster.h>
12 
13 #include <unicode/datefmt.h>
14 #include <unicode/dcfmtsym.h>
15 #include <unicode/decimfmt.h>
16 #include <unicode/dtfmtsym.h>
17 #include <unicode/smpdtfmt.h>
18 #include <ICUWrapper.h>
19 
20 #include <vector>
21 
22 
23 #define ICU_VERSION icu_44
24 
25 
26 using BPrivate::ObjectDeleter;
27 using BPrivate::B_WEEK_START_MONDAY;
28 using BPrivate::B_WEEK_START_SUNDAY;
29 
30 
31 static DateFormat* CreateDateFormat(bool longFormat, const Locale& locale,
32 						const BString& format);
33 static DateFormat* CreateTimeFormat(bool longFormat, const Locale& locale,
34 						const BString& format);
35 
36 
37 BLocale::BLocale(const char* languageAndCountryCode)
38 	:
39 	fCountry(languageAndCountryCode),
40 	fLanguage(languageAndCountryCode),
41 	fICULocale(new ICU_VERSION::Locale(languageAndCountryCode))
42 {
43 }
44 
45 
46 BLocale::BLocale(const BLocale& other)
47 	:
48 	fCountry(other.fCountry),
49 	fLanguage(other.fLanguage),
50 	fICULocale(new ICU_VERSION::Locale(*other.fICULocale)),
51 	fLongDateFormat(other.fLongDateFormat),
52 	fShortDateFormat(other.fShortDateFormat),
53 	fLongTimeFormat(other.fLongTimeFormat),
54 	fShortTimeFormat(other.fShortTimeFormat)
55 {
56 }
57 
58 
59 BLocale&
60 BLocale::operator=(const BLocale& other)
61 {
62 	if (this == &other)
63 		return *this;
64 
65 	*fICULocale = *other.fICULocale;
66 
67 	fLongDateFormat = other.fLongDateFormat;
68 	fShortDateFormat = other.fShortDateFormat;
69 	fLongTimeFormat = other.fLongTimeFormat;
70 	fShortTimeFormat = other.fShortTimeFormat;
71 
72 	fCountry = other.fCountry;
73 	fLanguage = other.fLanguage;
74 
75 	return *this;
76 }
77 
78 
79 BLocale::~BLocale()
80 {
81 	delete fICULocale;
82 }
83 
84 
85 const char *
86 BLocale::GetString(uint32 id)
87 {
88 	// Note: this code assumes a certain order of the string bases
89 
90 	if (id >= B_OTHER_STRINGS_BASE) {
91 		if (id == B_CODESET)
92 			return "UTF-8";
93 
94 		return "";
95 	}
96 	return fLanguage.GetString(id);
97 }
98 
99 
100 void
101 BLocale::SetCountry(const BCountry& newCountry)
102 {
103 	fCountry = newCountry;
104 }
105 
106 
107 void
108 BLocale::SetCollator(const BCollator& newCollator)
109 {
110 	fCollator = newCollator;
111 }
112 
113 
114 void
115 BLocale::SetLanguage(const char* languageCode)
116 {
117 	fLanguage.SetTo(languageCode);
118 }
119 
120 
121 const char*
122 BLocale::Code() const
123 {
124 	return fICULocale->getName();
125 }
126 
127 
128 bool
129 BLocale::GetName(BString& name) const
130 {
131 	UnicodeString uString;
132 	fICULocale->getDisplayName(uString);
133 	BStringByteSink stringConverter(&name);
134 	uString.toUTF8(stringConverter);
135 	return true;
136 }
137 
138 // #pragma mark - Date
139 
140 
141 status_t
142 BLocale::FormatDate(char* string, size_t maxSize, time_t time, bool longFormat)
143 {
144 	ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
145 		*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
146 	if (dateFormatter.Get() == NULL)
147 		return B_NO_MEMORY;
148 
149 	UnicodeString ICUString;
150 	ICUString = dateFormatter->format((UDate)time * 1000, ICUString);
151 
152 	CheckedArrayByteSink stringConverter(string, maxSize);
153 
154 	ICUString.toUTF8(stringConverter);
155 
156 	if (stringConverter.Overflowed())
157 		return B_BAD_VALUE;
158 
159 	return B_OK;
160 }
161 
162 
163 status_t
164 BLocale::FormatDate(BString *string, time_t time, bool longFormat)
165 {
166 	string->Truncate(0);
167 		// We make the string empty, this way even in cases where ICU fail we at
168 		// least return something sane
169 	ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
170 		*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
171 	if (dateFormatter.Get() == NULL)
172 		return B_NO_MEMORY;
173 
174 	UnicodeString ICUString;
175 	ICUString = dateFormatter->format((UDate)time * 1000, ICUString);
176 
177 	BStringByteSink stringConverter(string);
178 
179 	ICUString.toUTF8(stringConverter);
180 
181 	return B_OK;
182 }
183 
184 
185 status_t
186 BLocale::FormatDate(BString* string, int*& fieldPositions, int& fieldCount,
187 	time_t time, bool longFormat)
188 {
189 	string->Truncate(0);
190 
191 	ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
192 		*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
193 	if (dateFormatter.Get() == NULL)
194 		return B_NO_MEMORY;
195 
196 	fieldPositions = NULL;
197 	UErrorCode error = U_ZERO_ERROR;
198 	ICU_VERSION::FieldPositionIterator positionIterator;
199 	UnicodeString ICUString;
200 	ICUString = dateFormatter->format((UDate)time * 1000, ICUString,
201 		&positionIterator, error);
202 
203 	if (error != U_ZERO_ERROR)
204 		return B_ERROR;
205 
206 	ICU_VERSION::FieldPosition field;
207 	std::vector<int> fieldPosStorage;
208 	fieldCount  = 0;
209 	while (positionIterator.next(field)) {
210 		fieldPosStorage.push_back(field.getBeginIndex());
211 		fieldPosStorage.push_back(field.getEndIndex());
212 		fieldCount += 2;
213 	}
214 
215 	fieldPositions = (int*) malloc(fieldCount * sizeof(int));
216 
217 	for (int i = 0 ; i < fieldCount ; i++ )
218 		fieldPositions[i] = fieldPosStorage[i];
219 
220 	BStringByteSink stringConverter(string);
221 
222 	ICUString.toUTF8(stringConverter);
223 
224 	return B_OK;
225 }
226 
227 
228 status_t
229 BLocale::GetDateFormat(BString& format, bool longFormat) const
230 {
231 	if (longFormat && fLongDateFormat.Length() > 0)
232 		format = fLongDateFormat;
233 	else if (!longFormat && fShortDateFormat.Length() > 0)
234 		format = fShortDateFormat;
235 	else {
236 		format.Truncate(0);
237 
238 		ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
239 			*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
240 		if (dateFormatter.Get() == NULL)
241 			return B_NO_MEMORY;
242 
243 		SimpleDateFormat* dateFormatterImpl
244 			= static_cast<SimpleDateFormat*>(dateFormatter.Get());
245 
246 		UnicodeString ICUString;
247 		ICUString = dateFormatterImpl->toPattern(ICUString);
248 
249 		BStringByteSink stringConverter(&format);
250 
251 		ICUString.toUTF8(stringConverter);
252 	}
253 
254 	return B_OK;
255 }
256 
257 
258 status_t
259 BLocale::SetDateFormat(const char* formatString, bool longFormat)
260 {
261 printf("FV::SetDateFormat: df='%s'\n", formatString);
262 	if (longFormat)
263 		fLongDateFormat = formatString;
264 	else
265 		fShortDateFormat = formatString;
266 
267 	return B_OK;
268 }
269 
270 
271 status_t
272 BLocale::GetDateFields(BDateElement*& fields, int& fieldCount,
273 	bool longFormat) const
274 {
275 	ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
276 		*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
277 	if (dateFormatter.Get() == NULL)
278 		return B_NO_MEMORY;
279 
280 	fields = NULL;
281 	UErrorCode error = U_ZERO_ERROR;
282 	ICU_VERSION::FieldPositionIterator positionIterator;
283 	UnicodeString ICUString;
284 	time_t now;
285 	ICUString = dateFormatter->format((UDate)time(&now) * 1000, ICUString,
286 		&positionIterator, error);
287 
288 	if (U_FAILURE(error))
289 		return B_ERROR;
290 
291 	ICU_VERSION::FieldPosition field;
292 	std::vector<int> fieldPosStorage;
293 	fieldCount  = 0;
294 	while (positionIterator.next(field)) {
295 		fieldPosStorage.push_back(field.getField());
296 		fieldCount ++;
297 	}
298 
299 	fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
300 
301 	for (int i = 0 ; i < fieldCount ; i++ ) {
302 		switch (fieldPosStorage[i]) {
303 			case UDAT_YEAR_FIELD:
304 				fields[i] = B_DATE_ELEMENT_YEAR;
305 				break;
306 			case UDAT_MONTH_FIELD:
307 				fields[i] = B_DATE_ELEMENT_MONTH;
308 				break;
309 			case UDAT_DATE_FIELD:
310 				fields[i] = B_DATE_ELEMENT_DAY;
311 				break;
312 			default:
313 				fields[i] = B_DATE_ELEMENT_INVALID;
314 				break;
315 		}
316 	}
317 
318 	return B_OK;
319 }
320 
321 
322 int
323 BLocale::StartOfWeek() const
324 {
325 	UErrorCode err = U_ZERO_ERROR;
326 	Calendar* c = Calendar::createInstance(*fICULocale, err);
327 
328 	if (err == U_ZERO_ERROR && c->getFirstDayOfWeek(err) == UCAL_SUNDAY) {
329 		delete c;
330 		return B_WEEK_START_SUNDAY;
331 	} else {
332 		delete c;
333 		// Might be another day, but BeAPI will not handle it
334 		return B_WEEK_START_MONDAY;
335 	}
336 }
337 
338 
339 status_t
340 BLocale::FormatDateTime(char* target, size_t maxSize, time_t time,
341 	bool longFormat)
342 {
343 	ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat,
344 		*fICULocale, longFormat ? fLongDateFormat : fShortDateFormat);
345 	if (dateFormatter.Get() == NULL)
346 		return B_NO_MEMORY;
347 
348 	ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
349 		*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
350 	if (timeFormatter.Get() == NULL)
351 		return B_NO_MEMORY;
352 
353 	UnicodeString ICUString;
354 	ICUString = dateFormatter->format((UDate)time * 1000, ICUString);
355 
356 	ICUString.append(UnicodeString::fromUTF8(", "));
357 
358 	ICUString = timeFormatter->format((UDate)time * 1000, ICUString);
359 
360 	CheckedArrayByteSink stringConverter(target, maxSize);
361 	ICUString.toUTF8(stringConverter);
362 
363 	if (stringConverter.Overflowed())
364 		return B_BAD_VALUE;
365 
366 	return B_OK;
367 }
368 
369 
370 // #pragma mark - Time
371 
372 
373 status_t
374 BLocale::FormatTime(char* string, size_t maxSize, time_t time, bool longFormat)
375 {
376 	ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
377 		*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
378 	if (timeFormatter.Get() == NULL)
379 		return B_NO_MEMORY;
380 
381 	UnicodeString ICUString;
382 	ICUString = timeFormatter->format((UDate)time * 1000, ICUString);
383 
384 	CheckedArrayByteSink stringConverter(string, maxSize);
385 
386 	ICUString.toUTF8(stringConverter);
387 
388 	if (stringConverter.Overflowed())
389 		return B_BAD_VALUE;
390 
391 	return B_OK;
392 }
393 
394 
395 status_t
396 BLocale::FormatTime(BString* string, time_t time, bool longFormat)
397 {
398 	string->Truncate(0);
399 
400 	ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
401 		*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
402 	if (timeFormatter.Get() == NULL)
403 		return B_NO_MEMORY;
404 
405 	UnicodeString ICUString;
406 	ICUString = timeFormatter->format((UDate)time * 1000, ICUString);
407 
408 	BStringByteSink stringConverter(string);
409 
410 	ICUString.toUTF8(stringConverter);
411 
412 	return B_OK;
413 }
414 
415 
416 status_t
417 BLocale::FormatTime(BString* string, int*& fieldPositions, int& fieldCount,
418 	time_t time, bool longFormat)
419 {
420 	string->Truncate(0);
421 
422 	ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
423 		*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
424 	if (timeFormatter.Get() == NULL)
425 		return B_NO_MEMORY;
426 
427 	fieldPositions = NULL;
428 	UErrorCode error = U_ZERO_ERROR;
429 	ICU_VERSION::FieldPositionIterator positionIterator;
430 	UnicodeString ICUString;
431 	ICUString = timeFormatter->format((UDate)time * 1000, ICUString,
432 		&positionIterator, error);
433 
434 	if (error != U_ZERO_ERROR)
435 		return B_ERROR;
436 
437 	ICU_VERSION::FieldPosition field;
438 	std::vector<int> fieldPosStorage;
439 	fieldCount  = 0;
440 	while (positionIterator.next(field)) {
441 		fieldPosStorage.push_back(field.getBeginIndex());
442 		fieldPosStorage.push_back(field.getEndIndex());
443 		fieldCount += 2;
444 	}
445 
446 	fieldPositions = (int*) malloc(fieldCount * sizeof(int));
447 
448 	for (int i = 0 ; i < fieldCount ; i++ )
449 		fieldPositions[i] = fieldPosStorage[i];
450 
451 	BStringByteSink stringConverter(string);
452 
453 	ICUString.toUTF8(stringConverter);
454 
455 	return B_OK;
456 }
457 
458 
459 status_t
460 BLocale::GetTimeFields(BDateElement*& fields, int& fieldCount,
461 	bool longFormat) const
462 {
463 	ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
464 		*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
465 	if (timeFormatter.Get() == NULL)
466 		return B_NO_MEMORY;
467 
468 	fields = NULL;
469 	UErrorCode error = U_ZERO_ERROR;
470 	ICU_VERSION::FieldPositionIterator positionIterator;
471 	UnicodeString ICUString;
472 	time_t now;
473 	ICUString = timeFormatter->format((UDate)time(&now) * 1000,	ICUString,
474 		&positionIterator, error);
475 
476 	if (error != U_ZERO_ERROR)
477 		return B_ERROR;
478 
479 	ICU_VERSION::FieldPosition field;
480 	std::vector<int> fieldPosStorage;
481 	fieldCount  = 0;
482 	while (positionIterator.next(field)) {
483 		fieldPosStorage.push_back(field.getField());
484 		fieldCount ++;
485 	}
486 
487 	fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
488 
489 	for (int i = 0 ; i < fieldCount ; i++ ) {
490 		switch (fieldPosStorage[i]) {
491 			case UDAT_HOUR_OF_DAY1_FIELD:
492 			case UDAT_HOUR_OF_DAY0_FIELD:
493 			case UDAT_HOUR1_FIELD:
494 			case UDAT_HOUR0_FIELD:
495 				fields[i] = B_DATE_ELEMENT_HOUR;
496 				break;
497 			case UDAT_MINUTE_FIELD:
498 				fields[i] = B_DATE_ELEMENT_MINUTE;
499 				break;
500 			case UDAT_SECOND_FIELD:
501 				fields[i] = B_DATE_ELEMENT_SECOND;
502 				break;
503 			case UDAT_AM_PM_FIELD:
504 				fields[i] = B_DATE_ELEMENT_AM_PM;
505 				break;
506 			default:
507 				fields[i] = B_DATE_ELEMENT_INVALID;
508 				break;
509 		}
510 	}
511 
512 	return B_OK;
513 }
514 
515 
516 status_t
517 BLocale::SetTimeFormat(const char* formatString, bool longFormat)
518 {
519 	if (longFormat)
520 		fLongTimeFormat = formatString;
521 	else
522 		fShortTimeFormat = formatString;
523 
524 	return B_OK;
525 }
526 
527 
528 status_t
529 BLocale::GetTimeFormat(BString& format, bool longFormat) const
530 {
531 	if (longFormat && fLongTimeFormat.Length() > 0)
532 		format = fLongTimeFormat;
533 	else if (!longFormat && fShortTimeFormat.Length() > 0)
534 		format = fShortTimeFormat;
535 	else {
536 		format.Truncate(0);
537 
538 		ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat,
539 			*fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat);
540 		if (timeFormatter.Get() == NULL)
541 			return B_NO_MEMORY;
542 
543 		SimpleDateFormat* timeFormatterImpl
544 			= static_cast<SimpleDateFormat*>(timeFormatter.Get());
545 
546 		UnicodeString ICUString;
547 		ICUString = timeFormatterImpl->toPattern(ICUString);
548 
549 		BStringByteSink stringConverter(&format);
550 		ICUString.toUTF8(stringConverter);
551 	}
552 
553 	return B_OK;
554 }
555 
556 
557 // #pragma mark - Numbers
558 
559 
560 status_t
561 BLocale::FormatNumber(char* string, size_t maxSize, double value)
562 {
563 	BString fullString;
564 	status_t status = FormatNumber(&fullString, value);
565 	if (status == B_OK)
566 		strlcpy(string, fullString.String(), maxSize);
567 
568 	return status;
569 }
570 
571 
572 status_t
573 BLocale::FormatNumber(BString* string, double value)
574 {
575 	UErrorCode err = U_ZERO_ERROR;
576 	ObjectDeleter<NumberFormat> numberFormatter	= NumberFormat::createInstance(
577 		*fICULocale, NumberFormat::kNumberStyle, err);
578 
579 	if (numberFormatter.Get() == NULL)
580 		return B_NO_MEMORY;
581 	if (U_FAILURE(err))
582 		return B_ERROR;
583 
584 	UnicodeString ICUString;
585 	ICUString = numberFormatter->format(value, ICUString);
586 
587 	string->Truncate(0);
588 	BStringByteSink stringConverter(string);
589 	ICUString.toUTF8(stringConverter);
590 
591 	return B_OK;
592 }
593 
594 
595 status_t
596 BLocale::FormatNumber(char* string, size_t maxSize, int32 value)
597 {
598 	BString fullString;
599 	status_t status = FormatNumber(&fullString, value);
600 	if (status == B_OK)
601 		strlcpy(string, fullString.String(), maxSize);
602 
603 	return status;
604 }
605 
606 
607 status_t
608 BLocale::FormatNumber(BString* string, int32 value)
609 {
610 	UErrorCode err = U_ZERO_ERROR;
611 	ObjectDeleter<NumberFormat> numberFormatter	= NumberFormat::createInstance(
612 		*fICULocale, NumberFormat::kNumberStyle, err);
613 
614 	if (numberFormatter.Get() == NULL)
615 		return B_NO_MEMORY;
616 	if (U_FAILURE(err))
617 		return B_ERROR;
618 
619 	UnicodeString ICUString;
620 	ICUString = numberFormatter->format((int32_t)value, ICUString);
621 
622 	string->Truncate(0);
623 	BStringByteSink stringConverter(string);
624 	ICUString.toUTF8(stringConverter);
625 
626 	return B_OK;
627 }
628 
629 
630 ssize_t
631 BLocale::FormatMonetary(char* string, size_t maxSize, double value)
632 {
633 	BString fullString;
634 	ssize_t written = FormatMonetary(&fullString, value);
635 	if (written < 0)
636 		return written;
637 
638 	return strlcpy(string, fullString.String(), maxSize);
639 }
640 
641 
642 ssize_t
643 BLocale::FormatMonetary(BString* string, double value)
644 {
645 	if (string == NULL)
646 		return B_BAD_VALUE;
647 
648 	UErrorCode err;
649 	ObjectDeleter<NumberFormat> numberFormatter
650 		= NumberFormat::createCurrencyInstance(*fICULocale, err);
651 
652 	if (numberFormatter.Get() == NULL)
653 		return B_NO_MEMORY;
654 	if (U_FAILURE(err))
655 		return B_ERROR;
656 
657 	UnicodeString ICUString;
658 	ICUString = numberFormatter->format(value, ICUString);
659 
660 	string->Truncate(0);
661 	BStringByteSink stringConverter(string);
662 	ICUString.toUTF8(stringConverter);
663 
664 	return string->Length();
665 }
666 
667 
668 // #pragma mark - Helpers
669 
670 
671 static DateFormat*
672 CreateDateFormat(bool longFormat, const Locale& locale,
673 	const BString& format)
674 {
675 	DateFormat* dateFormatter = DateFormat::createDateInstance(
676 		longFormat ? DateFormat::FULL : DateFormat::SHORT, locale);
677 
678 	if (format.Length() > 0) {
679 		SimpleDateFormat* dateFormatterImpl
680 			= static_cast<SimpleDateFormat*>(dateFormatter);
681 
682 		UnicodeString pattern(format.String());
683 		dateFormatterImpl->applyPattern(pattern);
684 	}
685 
686 	return dateFormatter;
687 }
688 
689 
690 static DateFormat*
691 CreateTimeFormat(bool longFormat, const Locale& locale,
692 	const BString& format)
693 {
694 	DateFormat* timeFormatter = DateFormat::createTimeInstance(
695 		longFormat ? DateFormat::MEDIUM : DateFormat::SHORT, locale);
696 
697 	if (format.Length() > 0) {
698 		SimpleDateFormat* timeFormatterImpl
699 			= static_cast<SimpleDateFormat*>(timeFormatter);
700 
701 		UnicodeString pattern(format.String());
702 		timeFormatterImpl->applyPattern(pattern);
703 	}
704 
705 	return timeFormatter;
706 }
707