34 if (!lcLocale().isDebugEnabled())
37#if defined(Q_OS_MACOS)
40 Q_UNUSED(NSUserDefaults.standardUserDefaults);
43 auto singleLineDescription = [](NSArray *array) {
44 NSString *str = [array description];
45 str = [str stringByReplacingOccurrencesOfString:@
"\n" withString:@
""];
46 return [str stringByReplacingOccurrencesOfString:@
" " withString:@
""];
49 bool allowMixedLocalizations = [NSBundle.mainBundle.infoDictionary[@
"CFBundleAllowMixedLocalizations"] boolValue];
51 NSBundle *foundation = [NSBundle bundleForClass:NSBundle.
class];
52 qCDebug(lcLocale).nospace() <<
"Launched with locale \"" << NSLocale.currentLocale.localeIdentifier
53 <<
"\" based on user's preferred languages " << singleLineDescription(NSLocale.preferredLanguages)
54 <<
", main bundle localizations " << singleLineDescription(NSBundle.mainBundle.localizations)
55 <<
", and allowing mixed localizations " << allowMixedLocalizations
56 <<
"; resulting in main bundle preferred localizations "
57 << singleLineDescription(NSBundle.mainBundle.preferredLocalizations)
58 <<
" and Foundation preferred localizations "
59 << singleLineDescription(foundation.preferredLocalizations);
60 qCDebug(lcLocale) <<
"Reflected by Qt as system locale"
61 << QLocale::system() <<
"with UI languges " << QLocale::system().uiLanguages();
72static QVariant
macMonthName(
int month, QSystemLocale::QueryType type)
75 if (month < 0 || month > 11)
78 QCFType<CFDateFormatterRef> formatter
79 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
80 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
82 CFDateFormatterKey formatterType;
84 case QSystemLocale::MonthNameLong:
85 formatterType = kCFDateFormatterMonthSymbols;
87 case QSystemLocale::MonthNameShort:
88 formatterType = kCFDateFormatterShortMonthSymbols;
90 case QSystemLocale::MonthNameNarrow:
91 formatterType = kCFDateFormatterVeryShortMonthSymbols;
93 case QSystemLocale::StandaloneMonthNameLong:
94 formatterType = kCFDateFormatterStandaloneMonthSymbols;
96 case QSystemLocale::StandaloneMonthNameShort:
97 formatterType = kCFDateFormatterShortStandaloneMonthSymbols;
99 case QSystemLocale::StandaloneMonthNameNarrow:
100 formatterType = kCFDateFormatterVeryShortStandaloneMonthSymbols;
103 qWarning(
"macMonthName: Unsupported query type %d", type);
106 QCFType<CFArrayRef> values
107 =
static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
110 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, month));
111 return QString::fromCFString(cfstring);
116static QVariant
macDayName(
int day, QSystemLocale::QueryType type)
118 if (day < 1 || day > 7)
121 QCFType<CFDateFormatterRef> formatter
122 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
123 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
125 CFDateFormatterKey formatterType;
127 case QSystemLocale::DayNameLong:
128 formatterType = kCFDateFormatterWeekdaySymbols;
130 case QSystemLocale::DayNameShort:
131 formatterType = kCFDateFormatterShortWeekdaySymbols;
133 case QSystemLocale::DayNameNarrow:
134 formatterType = kCFDateFormatterVeryShortWeekdaySymbols;
136 case QSystemLocale::StandaloneDayNameLong:
137 formatterType = kCFDateFormatterStandaloneWeekdaySymbols;
139 case QSystemLocale::StandaloneDayNameShort:
140 formatterType = kCFDateFormatterShortStandaloneWeekdaySymbols;
142 case QSystemLocale::StandaloneDayNameNarrow:
143 formatterType = kCFDateFormatterVeryShortStandaloneWeekdaySymbols;
146 qWarning(
"macDayName: Unsupported query type %d", type);
149 QCFType<CFArrayRef> values =
150 static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
153 CFStringRef cfstring =
static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, day % 7));
154 return QString::fromCFString(cfstring);
161 static QString cachedZeroDigit;
163 if (cachedZeroDigit.isNull()) {
164 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
165 QCFType<CFNumberFormatterRef> numberFormatter =
166 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
167 const int zeroDigit = 0;
168 QCFType<CFStringRef> value
169 = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
170 kCFNumberIntType, &zeroDigit);
171 cachedZeroDigit = QString::fromCFString(value);
174 static QMacNotificationObserver localeChangeObserver = QMacNotificationObserver(
175 nil, NSCurrentLocaleDidChangeNotification, [&] {
176 qCDebug(lcLocale) <<
"System locale changed";
177 cachedZeroDigit = QString();
180 return cachedZeroDigit;
183static QString
zeroPad(QString &&number, qsizetype minDigits,
const QString &zero)
186 qsizetype insert = -1, digits = 0;
187 auto it = QStringIterator(number);
188 while (it.hasNext()) {
189 qsizetype here = it.index();
190 if (QChar::isDigit(it.next())) {
196 Q_ASSERT(digits > 0);
197 Q_ASSERT(insert >= 0);
198 while (digits++ < minDigits)
199 number.insert(insert, zero);
201 return std::move(number);
209 qsizetype first = -1, prev = -1, last = -1;
210 auto it = QStringIterator(number);
211 while (it.hasNext()) {
212 qsizetype here = it.index();
213 if (QChar::isDigit(it.next())) {
217 prev = std::exchange(last, here);
220 Q_ASSERT(first >= 0);
221 Q_ASSERT(prev > first);
222 Q_ASSERT(last > prev);
223 number.remove(first, prev - first);
224 return std::move(number);
230 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
231 QCFType<CFNumberFormatterRef> numberFormatter =
232 CFNumberFormatterCreate(
nullptr, locale, kCFNumberFormatterNoStyle);
233 QCFType<CFStringRef> value = CFNumberFormatterCreateStringWithValue(
nullptr, numberFormatter,
234 kCFNumberIntType, &year);
235 auto text = QString::fromCFString(value);
236 if (year > -1000 && year < 1000)
237 text = zeroPad(std::move(text), 4, zero);
247 QCFType<CFDateRef> myDate = QDateTime(date, QTime(12, 0)).toCFDate();
248 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
249 QCFType<CFDateFormatterRef> myFormatter
250 = CFDateFormatterCreate(kCFAllocatorDefault, mylocale, style,
251 kCFDateFormatterNoStyle);
252 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(
nullptr, myFormatter, myDate);
253 return QString::fromCFString(text);
258 const int year = date.year();
259 QString fakeYear, trueYear;
266 int matcher = QGregorianCalendar::yearSharingWeekDays(date);
267 Q_ASSERT(matcher >= 1583);
268 Q_ASSERT(matcher % 100 != date.month());
269 Q_ASSERT(matcher % 100 != date.day());
272 QString zero = macZeroDigit();
273 fakeYear = fourDigitYear(matcher, zero);
274 trueYear = fourDigitYear(year, zero);
275 date = QDate(matcher, date.month(), date.day());
277 QString text = macDateToStringImpl(date, short_format
278 ? kCFDateFormatterShortStyle
279 : kCFDateFormatterLongStyle);
281 if (text.contains(fakeYear))
282 return std::move(text).replace(fakeYear, trueYear);
284 fakeYear = trimTwoDigits(std::move(fakeYear));
285 trueYear = trimTwoDigits(std::move(trueYear));
286 if (text.contains(fakeYear))
287 return std::move(text).replace(fakeYear, trueYear);
289 qWarning(
"Failed to fix up year when formatting a date in year %d", year);
296 QCFType<CFDateRef> myDate = QDateTime(QDate::currentDate(), time).toCFDate();
297 QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent();
298 CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle;
299 QCFType<CFDateFormatterRef> myFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
301 kCFDateFormatterNoStyle,
303 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate);
304 return QString::fromCFString(text);
317 while (i < sys_fmt.size()) {
318 if (sys_fmt.at(i).unicode() ==
'\'') {
319 QString text = qt_readEscapedFormatString(sys_fmt, &i);
323 result += u'\'' + text + u'\'';
327 QChar c = sys_fmt.at(i);
328 qsizetype repeat = qt_repeatCount(sys_fmt.sliced(i));
330 switch (c.unicode()) {
366 result += QString(repeat, u'M');
369 result += QString(repeat, c);
389 result += QString(repeat,
'h'_L1);
394 result += QString(repeat,
'H'_L1);
398 result += QString(repeat, c);
416 result += (repeat > 1 && (repeat & 1)) ?
"ttt"_L1 :
"tt"_L1;
420 result += repeat < 4 ?
"tt"_L1 : repeat > 4 ?
"ttt"_L1 :
"t"_L1;
426 if (c < u'A' || c > u'z' || (c > u'Z' && c < u'a'))
427 result += QString(repeat, c);
434 return !result.isEmpty() ? QVariant::fromValue(result) : QVariant();
445 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
446 QCFType<CFNumberFormatterRef> numberFormatter =
447 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
449 CFNumberFormatterCopyProperty(numberFormatter, kCFNumberFormatterGroupingSize);
450 CFNumberRef num =
static_cast<CFNumberRef>(numTref);
452 if (CFNumberGetValue(num, kCFNumberIntType, &value) && value > 0) {
456 return QVariant::fromValue(sizes);
461 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
462 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
463 l, style, kCFDateFormatterNoStyle);
464 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
469 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
470 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
471 l, kCFDateFormatterNoStyle, style);
472 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
507 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
509 case QLocale::CurrencyIsoCode:
510 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode)));
511 case QLocale::CurrencySymbol:
512 return QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencySymbol)));
513 case QLocale::CurrencyDisplayName: {
514 CFStringRef code =
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleCurrencyCode));
515 QCFType<CFStringRef> value = CFLocaleCopyDisplayNameForPropertyValue(locale, kCFLocaleCurrencyCode, code);
516 return QString::fromCFString(value);
527 QCFType<CFNumberRef> value;
528 switch (arg.value.metaType().id()) {
530 case QMetaType::UInt: {
531 int v = arg.value.toInt();
532 value = CFNumberCreate(NULL, kCFNumberIntType, &v);
535 case QMetaType::Double: {
536 double v = arg.value.toDouble();
537 value = CFNumberCreate(NULL, kCFNumberDoubleType, &v);
540 case QMetaType::LongLong:
541 case QMetaType::ULongLong: {
542 qint64 v = arg.value.toLongLong();
543 value = CFNumberCreate(NULL, kCFNumberLongLongType, &v);
550 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
551 QCFType<CFNumberFormatterRef> currencyFormatter =
552 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterCurrencyStyle);
553 if (!arg.symbol.isEmpty()) {
554 CFNumberFormatterSetProperty(currencyFormatter, kCFNumberFormatterCurrencySymbol,
555 arg.symbol.toCFString());
557 QCFType<CFStringRef> result = CFNumberFormatterCreateStringWithNumber(NULL, currencyFormatter, value);
558 return QString::fromCFString(result);
564 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
566 case QSystemLocale::StringToStandardQuotation:
567 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationBeginDelimiterKey)));
568 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleQuotationEndDelimiterKey)));
569 return QString(begin % str % end);
570 case QSystemLocale::StringToAlternateQuotation:
571 begin = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationBeginDelimiterKey)));
572 end = QString::fromCFString(
static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleAlternateQuotationEndDelimiterKey)));
573 return QString(begin % str % end);
608QVariant QSystemLocale::query(QueryType type, QVariant &&in)
const
610 QMacAutoReleasePool pool;
614 return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode);
616 return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode);
618 return getLocaleValue<QLocalePrivate::codeToScript>(kCFLocaleScriptCode);
620 return getCFLocaleValue(kCFLocaleDecimalSeparator);
622 return getGroupingSizes();
624 return getCFLocaleValue(kCFLocaleGroupingSeparator);
626 case DateFormatShort:
627 return getMacDateFormat(type == DateFormatShort
628 ? kCFDateFormatterShortStyle
629 : kCFDateFormatterLongStyle);
631 case TimeFormatShort:
632 return getMacTimeFormat(type == TimeFormatShort
633 ? kCFDateFormatterShortStyle
634 : kCFDateFormatterLongStyle);
638 case StandaloneDayNameLong:
639 case StandaloneDayNameShort:
640 case StandaloneDayNameNarrow:
641 return macDayName(in.toInt(), type);
644 case MonthNameNarrow:
645 case StandaloneMonthNameLong:
646 case StandaloneMonthNameShort:
647 case StandaloneMonthNameNarrow:
648 return macMonthName(in.toInt(), type);
649 case DateToStringShort:
650 case DateToStringLong:
651 return macDateToString(in.toDate(), (type == DateToStringShort));
652 case TimeToStringShort:
653 case TimeToStringLong:
654 return macTimeToString(in.toTime(), (type == TimeToStringShort));
660 return macZeroDigit();
662 case MeasurementSystem:
663 return macMeasurementSystem();
667 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
668 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterLongStyle, kCFDateFormatterLongStyle);
669 QCFType<CFStringRef> value =
static_cast<CFStringRef>(CFDateFormatterCopyProperty(formatter,
670 (type == AMText ? kCFDateFormatterAMSymbol : kCFDateFormatterPMSymbol)));
671 return QString::fromCFString(value);
674 return QVariant(macFirstDayOfWeek());
676 return macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
677 case CurrencyToString:
678 return macFormatCurrency(in.value<CurrencyToStringArgument>());
681 QCFType<CFArrayRef> languages = CFLocaleCopyPreferredLanguages();
682 const CFIndex cnt = CFArrayGetCount(languages);
684 for (CFIndex i = 0; i < cnt; ++i) {
685 const QString lang = QString::fromCFString(
686 static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages, i)));
689 return QVariant(result);
691 case StringToStandardQuotation:
692 case StringToAlternateQuotation:
693 return macQuoteString(type, in.value<QStringView>());
704static QString localeConvertString(
const QByteArray &localeID,
const QString &str,
bool *ok,
707 QMacAutoReleasePool pool;
709 NSString *localestring = [[NSString alloc] initWithData:localeID.toNSData()
710 encoding:NSUTF8StringEncoding];
711 NSLocale *locale = [NSLocale localeWithLocaleIdentifier:localestring];
717 NSString *nsstring = str.toNSString();
719 nsstring = [nsstring lowercaseStringWithLocale:locale];
721 nsstring = [nsstring uppercaseStringWithLocale:locale];
723 return QString::fromNSString(nsstring);