33 if (!lcLocale().isDebugEnabled())
36#if defined(Q_OS_MACOS)
39 Q_UNUSED(NSUserDefaults.standardUserDefaults);
42 auto singleLineDescription = [](NSArray *array) {
43 NSString *str = [array description];
44 str = [str stringByReplacingOccurrencesOfString:@
"\n" withString:@
""];
45 return [str stringByReplacingOccurrencesOfString:@
" " withString:@
""];
48 bool allowMixedLocalizations = [NSBundle.mainBundle.infoDictionary[@
"CFBundleAllowMixedLocalizations"] boolValue];
50 NSBundle *foundation = [NSBundle bundleForClass:NSBundle.
class];
51 qCDebug(lcLocale).nospace() <<
"Launched with locale \"" << NSLocale.currentLocale.localeIdentifier
52 <<
"\" based on user's preferred languages " << singleLineDescription(NSLocale.preferredLanguages)
53 <<
", main bundle localizations " << singleLineDescription(NSBundle.mainBundle.localizations)
54 <<
", and allowing mixed localizations " << allowMixedLocalizations
55 <<
"; resulting in main bundle preferred localizations "
56 << singleLineDescription(NSBundle.mainBundle.preferredLocalizations)
57 <<
" and Foundation preferred localizations "
58 << singleLineDescription(foundation.preferredLocalizations);
59 qCDebug(lcLocale) <<
"Reflected by Qt as system locale"
60 << QLocale::system() <<
"with UI languges " << QLocale::system().uiLanguages();
71static QVariant
macMonthName(
int month, QSystemLocale::QueryType type)
74 if (month < 0 || month > 11)
77 QCFType<CFDateFormatterRef> formatter
78 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
79 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
81 CFDateFormatterKey formatterType;
83 case QSystemLocale::MonthNameLong:
84 formatterType = kCFDateFormatterMonthSymbols;
86 case QSystemLocale::MonthNameShort:
87 formatterType = kCFDateFormatterShortMonthSymbols;
89 case QSystemLocale::MonthNameNarrow:
90 formatterType = kCFDateFormatterVeryShortMonthSymbols;
92 case QSystemLocale::StandaloneMonthNameLong:
93 formatterType = kCFDateFormatterStandaloneMonthSymbols;
95 case QSystemLocale::StandaloneMonthNameShort:
96 formatterType = kCFDateFormatterShortStandaloneMonthSymbols;
98 case QSystemLocale::StandaloneMonthNameNarrow:
99 formatterType = kCFDateFormatterVeryShortStandaloneMonthSymbols;
102 qWarning(
"macMonthName: Unsupported query type %d", type);
105 QCFType<CFArrayRef> values
106 =
static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
109 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, month));
110 return QString::fromCFString(cfstring);
115static QVariant
macDayName(
int day, QSystemLocale::QueryType type)
117 if (day < 1 || day > 7)
120 QCFType<CFDateFormatterRef> formatter
121 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
122 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
124 CFDateFormatterKey formatterType;
126 case QSystemLocale::DayNameLong:
127 formatterType = kCFDateFormatterWeekdaySymbols;
129 case QSystemLocale::DayNameShort:
130 formatterType = kCFDateFormatterShortWeekdaySymbols;
132 case QSystemLocale::DayNameNarrow:
133 formatterType = kCFDateFormatterVeryShortWeekdaySymbols;
135 case QSystemLocale::StandaloneDayNameLong:
136 formatterType = kCFDateFormatterStandaloneWeekdaySymbols;
138 case QSystemLocale::StandaloneDayNameShort:
139 formatterType = kCFDateFormatterShortStandaloneWeekdaySymbols;
141 case QSystemLocale::StandaloneDayNameNarrow:
142 formatterType = kCFDateFormatterVeryShortStandaloneWeekdaySymbols;
145 qWarning(
"macDayName: Unsupported query type %d", type);
148 QCFType<CFArrayRef> values =
149 static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
152 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, day % 7));
153 return QString::fromCFString(cfstring);
160 static QString cachedZeroDigit;
162 if (cachedZeroDigit.isNull()) {
163 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
164 QCFType<CFNumberFormatterRef> numberFormatter =
165 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
166 const int zeroDigit = 0;
167 QCFType<CFStringRef> value
168 = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
169 kCFNumberIntType, &zeroDigit);
170 cachedZeroDigit = QString::fromCFString(value);
173 static QMacNotificationObserver localeChangeObserver = QMacNotificationObserver(
174 nil, NSCurrentLocaleDidChangeNotification, [&] {
175 qCDebug(lcLocale) <<
"System locale changed";
176 cachedZeroDigit = QString();
179 return cachedZeroDigit;
182static QString zeroPad(QString &&number, qsizetype minDigits,
const QString &zero)
185 qsizetype insert = -1, digits = 0;
186 auto it = QStringIterator(number);
187 while (it.hasNext()) {
188 qsizetype here = it.index();
189 if (QChar::isDigit(it.next())) {
195 Q_ASSERT(digits > 0);
196 Q_ASSERT(insert >= 0);
197 while (digits++ < minDigits)
198 number.insert(insert, zero);
200 return std::move(number);
208 qsizetype first = -1, prev = -1, last = -1;
209 auto it = QStringIterator(number);
210 while (it.hasNext()) {
211 qsizetype here = it.index();
212 if (QChar::isDigit(it.next())) {
216 prev = std::exchange(last, here);
219 Q_ASSERT(first >= 0);
220 Q_ASSERT(prev > first);
221 Q_ASSERT(last > prev);
222 number.remove(first, prev - first);
223 return std::move(number);
229 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
230 QCFType<CFNumberFormatterRef> numberFormatter =
231 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
232 QCFType<CFStringRef> value = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
233 kCFNumberIntType, &year);
234 auto text = QString::fromCFString(value);
235 if (year > -1000 && year < 1000)
236 text = zeroPad(std::move(text), 4, zero);
246 QCFType<CFDateRef> myDate = QDateTime(date, QTime(12, 0)).toCFDate();
247 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
248 QCFType<CFDateFormatterRef> myFormatter
249 = CFDateFormatterCreate(kCFAllocatorDefault, mylocale, style,
250 kCFDateFormatterNoStyle);
251 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(
nullptr, myFormatter, myDate);
252 return QString::fromCFString(text);
257 const int year = date.year();
258 QString fakeYear, trueYear;
265 int matcher = QGregorianCalendar::yearSharingWeekDays(date);
266 Q_ASSERT(matcher >= 1583);
267 Q_ASSERT(matcher % 100 != date.month());
268 Q_ASSERT(matcher % 100 != date.day());
271 QString zero = macZeroDigit();
272 fakeYear = fourDigitYear(matcher, zero);
273 trueYear = fourDigitYear(year, zero);
274 date = QDate(matcher, date.month(), date.day());
276 QString text = macDateToStringImpl(date, short_format
277 ? kCFDateFormatterShortStyle
278 : kCFDateFormatterLongStyle);
280 if (text.contains(fakeYear))
281 return std::move(text).replace(fakeYear, trueYear);
283 fakeYear = trimTwoDigits(std::move(fakeYear));
284 trueYear = trimTwoDigits(std::move(trueYear));
285 if (text.contains(fakeYear))
286 return std::move(text).replace(fakeYear, trueYear);
288 qWarning(
"Failed to fix up year when formatting a date in year %d", year);
295 QCFType<CFDateRef> myDate = QDateTime(QDate::currentDate(), time).toCFDate();
296 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
297 CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle;
298 QCFType<CFDateFormatterRef> myFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
300 kCFDateFormatterNoStyle,
302 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate);
303 return QString::fromCFString(text);
316 while (i < sys_fmt.size()) {
317 if (sys_fmt.at(i).unicode() ==
'\'') {
318 QString text = qt_readEscapedFormatString(sys_fmt, &i);
322 result += u'\'' + text + u'\'';
326 QChar c = sys_fmt.at(i);
327 qsizetype repeat = qt_repeatCount(sys_fmt.sliced(i));
329 switch (c.unicode()) {
365 result += QString(repeat, u'M');
368 result += QString(repeat, c);
388 result += QString(repeat,
'h'_L1);
393 result += QString(repeat,
'H'_L1);
397 result += QString(repeat, c);
415 result += (repeat > 1 && (repeat & 1)) ?
"ttt"_L1 :
"tt"_L1;
419 result += repeat < 4 ?
"tt"_L1 : repeat > 4 ?
"ttt"_L1 :
"t"_L1;
425 if (c < u'A' || c > u'z' || (c > u'Z' && c < u'a'))
426 result += QString(repeat, c);
433 return !result.isEmpty() ? QVariant::fromValue(result) : QVariant();
444 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
445 QCFType<CFNumberFormatterRef> numberFormatter =
446 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
448 CFNumberFormatterCopyProperty(numberFormatter, kCFNumberFormatterGroupingSize);
449 CFNumberRef num =
static_cast<CFNumberRef>(numTref);
451 if (CFNumberGetValue(num, kCFNumberIntType, &value) && value > 0) {
455 return QVariant::fromValue(sizes);
460 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
461 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
462 l, style, kCFDateFormatterNoStyle);
463 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
468 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
469 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
470 l, kCFDateFormatterNoStyle, style);
471 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
506 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
508 case QLocale::CurrencyIsoCode:
509 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode)));
510 case QLocale::CurrencySymbol:
511 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencySymbol)));
512 case QLocale::CurrencyDisplayName: {
513 CFStringRef code =
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode));
514 QCFType<CFStringRef> value = CFLocaleCopyDisplayNameForPropertyValue(locale, kCFLocaleCurrencyCode, code);
515 return QString::fromCFString(value);
526 QCFType<CFNumberRef> value;
527 switch (arg.value.metaType().id()) {
529 case QMetaType::UInt: {
530 int v = arg.value.toInt();
531 value = CFNumberCreate(NULL, kCFNumberIntType, &v);
534 case QMetaType::Double: {
535 double v = arg.value.toDouble();
536 value = CFNumberCreate(NULL, kCFNumberDoubleType, &v);
539 case QMetaType::LongLong:
540 case QMetaType::ULongLong: {
541 qint64 v = arg.value.toLongLong();
542 value = CFNumberCreate(NULL, kCFNumberLongLongType, &v);
549 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
550 QCFType<CFNumberFormatterRef> currencyFormatter =
551 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterCurrencyStyle);
552 if (!arg.symbol.isEmpty()) {
553 CFNumberFormatterSetProperty(currencyFormatter, kCFNumberFormatterCurrencySymbol,
554 arg.symbol.toCFString());
556 QCFType<CFStringRef> result = CFNumberFormatterCreateStringWithNumber(NULL, currencyFormatter, value);
557 return QString::fromCFString(result);
563 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
565 case QSystemLocale::StringToStandardQuotation:
566 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationBeginDelimiterKey)));
567 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationEndDelimiterKey)));
568 return QString(begin % str % end);
569 case QSystemLocale::StringToAlternateQuotation:
570 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationBeginDelimiterKey)));
571 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationEndDelimiterKey)));
572 return QString(begin % str % end);
607QVariant QSystemLocale::query(QueryType type, QVariant &&in)
const
609 QMacAutoReleasePool pool;
613 return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode);
615 return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode);
617 return getLocaleValue<QLocalePrivate::codeToScript>(kCFLocaleScriptCode);
619 return getCFLocaleValue(kCFLocaleDecimalSeparator);
621 return getGroupingSizes();
623 return getCFLocaleValue(kCFLocaleGroupingSeparator);
625 case DateFormatShort:
626 return getMacDateFormat(type == DateFormatShort
627 ? kCFDateFormatterShortStyle
628 : kCFDateFormatterLongStyle);
630 case TimeFormatShort:
631 return getMacTimeFormat(type == TimeFormatShort
632 ? kCFDateFormatterShortStyle
633 : kCFDateFormatterLongStyle);
637 case StandaloneDayNameLong:
638 case StandaloneDayNameShort:
639 case StandaloneDayNameNarrow:
640 return macDayName(in.toInt(), type);
643 case MonthNameNarrow:
644 case StandaloneMonthNameLong:
645 case StandaloneMonthNameShort:
646 case StandaloneMonthNameNarrow:
647 return macMonthName(in.toInt(), type);
648 case DateToStringShort:
649 case DateToStringLong:
650 return macDateToString(in.toDate(), (type == DateToStringShort));
651 case TimeToStringShort:
652 case TimeToStringLong:
653 return macTimeToString(in.toTime(), (type == TimeToStringShort));
659 return macZeroDigit();
661 case MeasurementSystem:
662 return macMeasurementSystem();
666 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
667 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterLongStyle, kCFDateFormatterLongStyle);
668 QCFType<CFStringRef> value =
static_cast<CFStringRef>(CFDateFormatterCopyProperty(formatter,
669 (type == AMText ? kCFDateFormatterAMSymbol : kCFDateFormatterPMSymbol)));
670 return QString::fromCFString(value);
673 return QVariant(macFirstDayOfWeek());
675 return macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
676 case CurrencyToString:
677 return macFormatCurrency(in.value<CurrencyToStringArgument>());
680 QCFType<CFArrayRef> languages = CFLocaleCopyPreferredLanguages();
681 const CFIndex cnt = CFArrayGetCount(languages);
683 for (CFIndex i = 0; i < cnt; ++i) {
684 const QString lang = QString::fromCFString(
685 static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages, i)));
688 return QVariant(result);
690 case StringToStandardQuotation:
691 case StringToAlternateQuotation:
692 return macQuoteString(type, in.value<QStringView>());
703static QString localeConvertString(
const QByteArray &localeID,
const QString &str,
bool *ok,
706 QMacAutoReleasePool pool;
708 NSString *localestring = [[NSString alloc] initWithData:localeID.toNSData()
709 encoding:NSUTF8StringEncoding];
710 NSLocale *locale = [NSLocale localeWithLocaleIdentifier:localestring];
716 NSString *nsstring = str.toNSString();
718 nsstring = [nsstring lowercaseStringWithLocale:locale];
720 nsstring = [nsstring uppercaseStringWithLocale:locale];
722 return QString::fromNSString(nsstring);