32 if (!lcLocale().isDebugEnabled())
35#if defined(Q_OS_MACOS)
38 Q_UNUSED(NSUserDefaults.standardUserDefaults);
41 auto singleLineDescription = [](NSArray *array) {
42 NSString *str = [array description];
43 str = [str stringByReplacingOccurrencesOfString:@
"\n" withString:@
""];
44 return [str stringByReplacingOccurrencesOfString:@
" " withString:@
""];
47 bool allowMixedLocalizations = [NSBundle.mainBundle.infoDictionary[@
"CFBundleAllowMixedLocalizations"] boolValue];
49 NSBundle *foundation = [NSBundle bundleForClass:NSBundle.
class];
50 qCDebug(lcLocale).nospace() <<
"Launched with locale \"" << NSLocale.currentLocale.localeIdentifier
51 <<
"\" based on user's preferred languages " << singleLineDescription(NSLocale.preferredLanguages)
52 <<
", main bundle localizations " << singleLineDescription(NSBundle.mainBundle.localizations)
53 <<
", and allowing mixed localizations " << allowMixedLocalizations
54 <<
"; resulting in main bundle preferred localizations "
55 << singleLineDescription(NSBundle.mainBundle.preferredLocalizations)
56 <<
" and Foundation preferred localizations "
57 << singleLineDescription(foundation.preferredLocalizations);
58 qCDebug(lcLocale) <<
"Reflected by Qt as system locale"
59 << QLocale::system() <<
"with UI languges " << QLocale::system().uiLanguages();
70static QVariant
macMonthName(
int month, QSystemLocale::QueryType type)
73 if (month < 0 || month > 11)
76 QCFType<CFDateFormatterRef> formatter
77 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
78 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
80 CFDateFormatterKey formatterType;
82 case QSystemLocale::MonthNameLong:
83 formatterType = kCFDateFormatterMonthSymbols;
85 case QSystemLocale::MonthNameShort:
86 formatterType = kCFDateFormatterShortMonthSymbols;
88 case QSystemLocale::MonthNameNarrow:
89 formatterType = kCFDateFormatterVeryShortMonthSymbols;
91 case QSystemLocale::StandaloneMonthNameLong:
92 formatterType = kCFDateFormatterStandaloneMonthSymbols;
94 case QSystemLocale::StandaloneMonthNameShort:
95 formatterType = kCFDateFormatterShortStandaloneMonthSymbols;
97 case QSystemLocale::StandaloneMonthNameNarrow:
98 formatterType = kCFDateFormatterVeryShortStandaloneMonthSymbols;
101 qWarning(
"macMonthName: Unsupported query type %d", type);
104 QCFType<CFArrayRef> values
105 =
static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
108 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, month));
109 return QString::fromCFString(cfstring);
114static QVariant
macDayName(
int day, QSystemLocale::QueryType type)
116 if (day < 1 || day > 7)
119 QCFType<CFDateFormatterRef> formatter
120 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
121 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
123 CFDateFormatterKey formatterType;
125 case QSystemLocale::DayNameLong:
126 formatterType = kCFDateFormatterWeekdaySymbols;
128 case QSystemLocale::DayNameShort:
129 formatterType = kCFDateFormatterShortWeekdaySymbols;
131 case QSystemLocale::DayNameNarrow:
132 formatterType = kCFDateFormatterVeryShortWeekdaySymbols;
134 case QSystemLocale::StandaloneDayNameLong:
135 formatterType = kCFDateFormatterStandaloneWeekdaySymbols;
137 case QSystemLocale::StandaloneDayNameShort:
138 formatterType = kCFDateFormatterShortStandaloneWeekdaySymbols;
140 case QSystemLocale::StandaloneDayNameNarrow:
141 formatterType = kCFDateFormatterVeryShortStandaloneWeekdaySymbols;
144 qWarning(
"macDayName: Unsupported query type %d", type);
147 QCFType<CFArrayRef> values =
148 static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
151 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, day % 7));
152 return QString::fromCFString(cfstring);
159 static QString cachedZeroDigit;
161 if (cachedZeroDigit.isNull()) {
162 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
163 QCFType<CFNumberFormatterRef> numberFormatter =
164 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
165 const int zeroDigit = 0;
166 QCFType<CFStringRef> value
167 = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
168 kCFNumberIntType, &zeroDigit);
169 cachedZeroDigit = QString::fromCFString(value);
172 static QMacNotificationObserver localeChangeObserver = QMacNotificationObserver(
173 nil, NSCurrentLocaleDidChangeNotification, [&] {
174 qCDebug(lcLocale) <<
"System locale changed";
175 cachedZeroDigit = QString();
178 return cachedZeroDigit;
181static QString zeroPad(QString &&number, qsizetype minDigits,
const QString &zero)
184 qsizetype insert = -1, digits = 0;
185 auto it = QStringIterator(number);
186 while (it.hasNext()) {
187 qsizetype here = it.index();
188 if (QChar::isDigit(it.next())) {
194 Q_ASSERT(digits > 0);
195 Q_ASSERT(insert >= 0);
196 while (digits++ < minDigits)
197 number.insert(insert, zero);
199 return std::move(number);
207 qsizetype first = -1, prev = -1, last = -1;
208 auto it = QStringIterator(number);
209 while (it.hasNext()) {
210 qsizetype here = it.index();
211 if (QChar::isDigit(it.next())) {
215 prev = std::exchange(last, here);
218 Q_ASSERT(first >= 0);
219 Q_ASSERT(prev > first);
220 Q_ASSERT(last > prev);
221 number.remove(first, prev - first);
222 return std::move(number);
228 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
229 QCFType<CFNumberFormatterRef> numberFormatter =
230 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
231 QCFType<CFStringRef> value = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
232 kCFNumberIntType, &year);
233 auto text = QString::fromCFString(value);
234 if (year > -1000 && year < 1000)
235 text = zeroPad(std::move(text), 4, zero);
245 QCFType<CFDateRef> myDate = QDateTime(date, QTime(12, 0)).toCFDate();
246 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
247 QCFType<CFDateFormatterRef> myFormatter
248 = CFDateFormatterCreate(kCFAllocatorDefault, mylocale, style,
249 kCFDateFormatterNoStyle);
250 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(
nullptr, myFormatter, myDate);
251 return QString::fromCFString(text);
256 const int year = date.year();
257 QString fakeYear, trueYear;
264 int matcher = QGregorianCalendar::yearSharingWeekDays(date);
265 Q_ASSERT(matcher >= 1583);
266 Q_ASSERT(matcher % 100 != date.month());
267 Q_ASSERT(matcher % 100 != date.day());
270 QString zero = macZeroDigit();
271 fakeYear = fourDigitYear(matcher, zero);
272 trueYear = fourDigitYear(year, zero);
273 date = QDate(matcher, date.month(), date.day());
275 QString text = macDateToStringImpl(date, short_format
276 ? kCFDateFormatterShortStyle
277 : kCFDateFormatterLongStyle);
279 if (text.contains(fakeYear))
280 return std::move(text).replace(fakeYear, trueYear);
282 fakeYear = trimTwoDigits(std::move(fakeYear));
283 trueYear = trimTwoDigits(std::move(trueYear));
284 if (text.contains(fakeYear))
285 return std::move(text).replace(fakeYear, trueYear);
287 qWarning(
"Failed to fix up year when formatting a date in year %d", year);
294 QCFType<CFDateRef> myDate = QDateTime(QDate::currentDate(), time).toCFDate();
295 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
296 CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle;
297 QCFType<CFDateFormatterRef> myFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
299 kCFDateFormatterNoStyle,
301 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate);
302 return QString::fromCFString(text);
315 while (i < sys_fmt.size()) {
316 if (sys_fmt.at(i).unicode() ==
'\'') {
317 QString text = qt_readEscapedFormatString(sys_fmt, &i);
321 result += u'\'' + text + u'\'';
325 QChar c = sys_fmt.at(i);
326 qsizetype repeat = qt_repeatCount(sys_fmt.sliced(i));
328 switch (c.unicode()) {
364 result += QString(repeat, u'M');
367 result += QString(repeat, c);
387 result += QString(repeat,
'h'_L1);
392 result += QString(repeat,
'H'_L1);
396 result += QString(repeat, c);
414 result += (repeat > 1 && (repeat & 1)) ?
"ttt"_L1 :
"tt"_L1;
418 result += repeat < 4 ?
"tt"_L1 : repeat > 4 ?
"ttt"_L1 :
"t"_L1;
424 if (c < u'A' || c > u'z' || (c > u'Z' && c < u'a'))
425 result += QString(repeat, c);
432 return !result.isEmpty() ? QVariant::fromValue(result) : QVariant();
443 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
444 QCFType<CFNumberFormatterRef> numberFormatter =
445 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
447 CFNumberFormatterCopyProperty(numberFormatter, kCFNumberFormatterGroupingSize);
448 CFNumberRef num =
static_cast<CFNumberRef>(numTref);
450 if (CFNumberGetValue(num, kCFNumberIntType, &value) && value > 0) {
454 return QVariant::fromValue(sizes);
459 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
460 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
461 l, style, kCFDateFormatterNoStyle);
462 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
467 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
468 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
469 l, kCFDateFormatterNoStyle, style);
470 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
505 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
507 case QLocale::CurrencyIsoCode:
508 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode)));
509 case QLocale::CurrencySymbol:
510 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencySymbol)));
511 case QLocale::CurrencyDisplayName: {
512 CFStringRef code =
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode));
513 QCFType<CFStringRef> value = CFLocaleCopyDisplayNameForPropertyValue(locale, kCFLocaleCurrencyCode, code);
514 return QString::fromCFString(value);
525 QCFType<CFNumberRef> value;
526 switch (arg.value.metaType().id()) {
528 case QMetaType::UInt: {
529 int v = arg.value.toInt();
530 value = CFNumberCreate(NULL, kCFNumberIntType, &v);
533 case QMetaType::Double: {
534 double v = arg.value.toDouble();
535 value = CFNumberCreate(NULL, kCFNumberDoubleType, &v);
538 case QMetaType::LongLong:
539 case QMetaType::ULongLong: {
540 qint64 v = arg.value.toLongLong();
541 value = CFNumberCreate(NULL, kCFNumberLongLongType, &v);
548 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
549 QCFType<CFNumberFormatterRef> currencyFormatter =
550 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterCurrencyStyle);
551 if (!arg.symbol.isEmpty()) {
552 CFNumberFormatterSetProperty(currencyFormatter, kCFNumberFormatterCurrencySymbol,
553 arg.symbol.toCFString());
555 QCFType<CFStringRef> result = CFNumberFormatterCreateStringWithNumber(NULL, currencyFormatter, value);
556 return QString::fromCFString(result);
562 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
564 case QSystemLocale::StringToStandardQuotation:
565 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationBeginDelimiterKey)));
566 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationEndDelimiterKey)));
567 return QString(begin % str % end);
568 case QSystemLocale::StringToAlternateQuotation:
569 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationBeginDelimiterKey)));
570 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationEndDelimiterKey)));
571 return QString(begin % str % end);
606QVariant QSystemLocale::query(QueryType type, QVariant &&in)
const
608 QMacAutoReleasePool pool;
612 return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode);
614 return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode);
616 return getLocaleValue<QLocalePrivate::codeToScript>(kCFLocaleScriptCode);
618 return getCFLocaleValue(kCFLocaleDecimalSeparator);
620 return getGroupingSizes();
622 return getCFLocaleValue(kCFLocaleGroupingSeparator);
624 case DateFormatShort:
625 return getMacDateFormat(type == DateFormatShort
626 ? kCFDateFormatterShortStyle
627 : kCFDateFormatterLongStyle);
629 case TimeFormatShort:
630 return getMacTimeFormat(type == TimeFormatShort
631 ? kCFDateFormatterShortStyle
632 : kCFDateFormatterLongStyle);
636 case StandaloneDayNameLong:
637 case StandaloneDayNameShort:
638 case StandaloneDayNameNarrow:
639 return macDayName(in.toInt(), type);
642 case MonthNameNarrow:
643 case StandaloneMonthNameLong:
644 case StandaloneMonthNameShort:
645 case StandaloneMonthNameNarrow:
646 return macMonthName(in.toInt(), type);
647 case DateToStringShort:
648 case DateToStringLong:
649 return macDateToString(in.toDate(), (type == DateToStringShort));
650 case TimeToStringShort:
651 case TimeToStringLong:
652 return macTimeToString(in.toTime(), (type == TimeToStringShort));
658 return macZeroDigit();
660 case MeasurementSystem:
661 return macMeasurementSystem();
665 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
666 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterLongStyle, kCFDateFormatterLongStyle);
667 QCFType<CFStringRef> value =
static_cast<CFStringRef>(CFDateFormatterCopyProperty(formatter,
668 (type == AMText ? kCFDateFormatterAMSymbol : kCFDateFormatterPMSymbol)));
669 return QString::fromCFString(value);
672 return QVariant(macFirstDayOfWeek());
674 return macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
675 case CurrencyToString:
676 return macFormatCurrency(in.value<CurrencyToStringArgument>());
679 QCFType<CFArrayRef> languages = CFLocaleCopyPreferredLanguages();
680 const CFIndex cnt = CFArrayGetCount(languages);
682 for (CFIndex i = 0; i < cnt; ++i) {
683 const QString lang = QString::fromCFString(
684 static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages, i)));
687 return QVariant(result);
689 case StringToStandardQuotation:
690 case StringToAlternateQuotation:
691 return macQuoteString(type, in.value<QStringView>());
702static QString localeConvertString(
const QByteArray &localeID,
const QString &str,
bool *ok,
705 QMacAutoReleasePool pool;
707 NSString *localestring = [[NSString alloc] initWithData:localeID.toNSData()
708 encoding:NSUTF8StringEncoding];
709 NSLocale *locale = [NSLocale localeWithLocaleIdentifier:localestring];
715 NSString *nsstring = str.toNSString();
717 nsstring = [nsstring lowercaseStringWithLocale:locale];
719 nsstring = [nsstring uppercaseStringWithLocale:locale];
721 return QString::fromNSString(nsstring);