Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlocale_mac.mm
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qlocale_p.h"
7
8#include "qstringlist.h"
9#include "qvariant.h"
10#include "qdatetime.h"
11
12#include "private/qstringiterator_p.h"
13#include "private/qgregoriancalendar_p.h"
14#ifdef Q_OS_DARWIN
15#include "private/qcore_mac_p.h"
16#include <CoreFoundation/CoreFoundation.h>
17#endif
18
19#include <QtCore/qloggingcategory.h>
20#include <QtCore/qcoreapplication.h>
21
23
24using namespace Qt::StringLiterals;
25
26/******************************************************************************
27** Wrappers for Mac locale system functions
28*/
29
30Q_STATIC_LOGGING_CATEGORY(lcLocale, "qt.core.locale")
31
33{
34 if (!lcLocale().isDebugEnabled())
35 return;
36
37#if defined(Q_OS_MACOS)
38 // Trigger initialization of standard user defaults, so that Foundation picks
39 // up -AppleLanguages and -AppleLocale passed on the command line.
40 Q_UNUSED(NSUserDefaults.standardUserDefaults);
41#endif
42
43 auto singleLineDescription = [](NSArray *array) {
44 NSString *str = [array description];
45 str = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
46 return [str stringByReplacingOccurrencesOfString:@" " withString:@""];
47 };
48
49 bool allowMixedLocalizations = [NSBundle.mainBundle.infoDictionary[@"CFBundleAllowMixedLocalizations"] boolValue];
50
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();
62}
64
65static QString getMacLocaleName()
66{
67 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
68 CFStringRef locale = CFLocaleGetIdentifier(l);
69 return QString::fromCFString(locale);
70}
71
72static QVariant macMonthName(int month, QSystemLocale::QueryType type)
73{
74 month -= 1;
75 if (month < 0 || month > 11)
76 return {};
77
78 QCFType<CFDateFormatterRef> formatter
79 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
80 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
81
82 CFDateFormatterKey formatterType;
83 switch (type) {
84 case QSystemLocale::MonthNameLong:
85 formatterType = kCFDateFormatterMonthSymbols;
86 break;
87 case QSystemLocale::MonthNameShort:
88 formatterType = kCFDateFormatterShortMonthSymbols;
89 break;
90 case QSystemLocale::MonthNameNarrow:
91 formatterType = kCFDateFormatterVeryShortMonthSymbols;
92 break;
93 case QSystemLocale::StandaloneMonthNameLong:
94 formatterType = kCFDateFormatterStandaloneMonthSymbols;
95 break;
96 case QSystemLocale::StandaloneMonthNameShort:
97 formatterType = kCFDateFormatterShortStandaloneMonthSymbols;
98 break;
99 case QSystemLocale::StandaloneMonthNameNarrow:
100 formatterType = kCFDateFormatterVeryShortStandaloneMonthSymbols;
101 break;
102 default:
103 qWarning("macMonthName: Unsupported query type %d", type);
104 return {};
105 }
106 QCFType<CFArrayRef> values
107 = static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
108
109 if (values != 0) {
110 CFStringRef cfstring = static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, month));
111 return QString::fromCFString(cfstring);
112 }
113 return {};
114}
115
116static QVariant macDayName(int day, QSystemLocale::QueryType type)
117{
118 if (day < 1 || day > 7)
119 return {};
120
121 QCFType<CFDateFormatterRef> formatter
122 = CFDateFormatterCreate(0, QCFType<CFLocaleRef>(CFLocaleCopyCurrent()),
123 kCFDateFormatterNoStyle, kCFDateFormatterNoStyle);
124
125 CFDateFormatterKey formatterType;
126 switch (type) {
127 case QSystemLocale::DayNameLong:
128 formatterType = kCFDateFormatterWeekdaySymbols;
129 break;
130 case QSystemLocale::DayNameShort:
131 formatterType = kCFDateFormatterShortWeekdaySymbols;
132 break;
133 case QSystemLocale::DayNameNarrow:
134 formatterType = kCFDateFormatterVeryShortWeekdaySymbols;
135 break;
136 case QSystemLocale::StandaloneDayNameLong:
137 formatterType = kCFDateFormatterStandaloneWeekdaySymbols;
138 break;
139 case QSystemLocale::StandaloneDayNameShort:
140 formatterType = kCFDateFormatterShortStandaloneWeekdaySymbols;
141 break;
142 case QSystemLocale::StandaloneDayNameNarrow:
143 formatterType = kCFDateFormatterVeryShortStandaloneWeekdaySymbols;
144 break;
145 default:
146 qWarning("macDayName: Unsupported query type %d", type);
147 return {};
148 }
149 QCFType<CFArrayRef> values =
150 static_cast<CFArrayRef>(CFDateFormatterCopyProperty(formatter, formatterType));
151
152 if (values != 0) {
153 CFStringRef cfstring = static_cast<CFStringRef>(CFArrayGetValueAtIndex(values, day % 7));
154 return QString::fromCFString(cfstring);
155 }
156 return {};
157}
158
159static QString macZeroDigit()
160{
161 static QString cachedZeroDigit;
162
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);
172 }
173
174 static QMacNotificationObserver localeChangeObserver = QMacNotificationObserver(
175 nil, NSCurrentLocaleDidChangeNotification, [&] {
176 qCDebug(lcLocale) << "System locale changed";
177 cachedZeroDigit = QString();
178 });
179
180 return cachedZeroDigit;
181}
182
183static QString zeroPad(QString &&number, qsizetype minDigits, const QString &zero)
184{
185 // Need to pad with zeros, possibly after a sign.
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())) {
191 if (insert < 0)
192 insert = here;
193 ++digits;
194 } // else: assume we're stepping over a sign (or maybe grouping separator)
195 }
196 Q_ASSERT(digits > 0);
197 Q_ASSERT(insert >= 0);
198 while (digits++ < minDigits)
199 number.insert(insert, zero);
200
201 return std::move(number);
202}
203
204static QString trimTwoDigits(QString &&number)
205{
206 // Retain any sign, but remove all but the last two digits.
207 // We know number has at least four digits - it came from fourDigitYear().
208 // Note that each digit might be a surrogate pair.
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())) {
214 if (first == -1)
215 last = first = here;
216 else if (last != -1)
217 prev = std::exchange(last, here);
218 }
219 }
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);
225}
226
227static QString fourDigitYear(int year, const QString &zero)
228{
229 // Return year formatted as an (at least) four digit 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);
238 return text;
239}
240
241static QString macDateToStringImpl(QDate date, CFDateFormatterStyle style)
242{
243 // Use noon on the given date, to avoid complications that can arise for
244 // dates before 1900 (see QTBUG-54955) using different UTC offset than
245 // QDateTime extrapolates backwards from time_t functions that only work
246 // back to 1900. (Alaska and Phillipines may still be borked, though.)
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);
254}
255
256static QVariant macDateToString(QDate date, bool short_format)
257{
258 const int year = date.year();
259 QString fakeYear, trueYear;
260 if (year < 1583) {
261 // System API (in macOS 11.0, at least) discards sign :-(
262 // Simply negating the year won't do as the resulting year typically has
263 // a different pattern of week-days.
264 // Furthermore (see QTBUG-54955), Darwin uses the Julian calendar for
265 // dates before 1582-10-15, leading to discrepancies.
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());
270 // i.e. there can't be any confusion between the two-digit year and
271 // month or day-of-month in the formatted date.
272 QString zero = macZeroDigit();
273 fakeYear = fourDigitYear(matcher, zero);
274 trueYear = fourDigitYear(year, zero);
275 date = QDate(matcher, date.month(), date.day());
276 }
277 QString text = macDateToStringImpl(date, short_format
278 ? kCFDateFormatterShortStyle
279 : kCFDateFormatterLongStyle);
280 if (year < 1583) {
281 if (text.contains(fakeYear))
282 return std::move(text).replace(fakeYear, trueYear);
283 // Cope with two-digit year:
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);
288 // That should have worked.
289 qWarning("Failed to fix up year when formatting a date in year %d", year);
290 }
291 return text;
292}
293
294static QVariant macTimeToString(QTime time, bool short_format)
295{
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,
300 mylocale,
301 kCFDateFormatterNoStyle,
302 style);
303 QCFType<CFStringRef> text = CFDateFormatterCreateStringWithDate(0, myFormatter, myDate);
304 return QString::fromCFString(text);
305}
306
307// Mac uses the Unicode CLDR format codes
308// http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
309// See also qtbase/util/locale_database/dateconverter.py
310// Makes the assumption that input formats are always well formed and consecutive letters
311// never exceed the maximum for the format code.
312static QVariant macToQtFormat(QStringView sys_fmt)
313{
314 QString result;
315 qsizetype i = 0;
316
317 while (i < sys_fmt.size()) {
318 if (sys_fmt.at(i).unicode() == '\'') {
319 QString text = qt_readEscapedFormatString(sys_fmt, &i);
320 if (text == "'"_L1)
321 result += "''"_L1;
322 else
323 result += u'\'' + text + u'\'';
324 continue;
325 }
326
327 QChar c = sys_fmt.at(i);
328 qsizetype repeat = qt_repeatCount(sys_fmt.sliced(i));
329
330 switch (c.unicode()) {
331 // Qt does not support the following options
332 case 'A': // Milliseconds in Day (1..n): 1..n = padded number
333 case 'C': // Input skeleton symbol.
334 case 'D': // Day of Year (1..3): 1..3 = padded number
335 case 'F': // Day of Week in Month (1): 1 = number
336 case 'g': // Modified Julian Day (1..n): 1..n = padded number
337 case 'G': // Era (1..5): 4 = long, 1..3 = short, 5 = narrow
338 case 'j': // Input skeleton symbol.
339 case 'J': // Input skeleton symbol.
340 case 'l': // Deprecated Chinese leap month indicator.
341 case 'q': // Standalone Quarter (1..4): 4 = long, 3 = short, 1,2 = padded number
342 case 'Q': // Quarter (1..4): 4 = long, 3 = short, 1,2 = padded number
343 case 'U': // Cyclic Year Name (1..5): 4 = long, 1..3 = short, 5 = narrow
344 case 'w': // Week of Year (1,2): 1,2 = padded number
345 case 'W': // Week of Month (1): 1 = number
346 case 'Y': // Year for Week-of-year calendars (1..n): 1..n = padded number
347 break;
348
349 case 'u': // Extended Year (1..n), padded number.
350 // Explicitly has no special case for 'uu' as only the last two digits.
351 result += "yyyy"_L1;
352 break;
353 case 'y': // Year (1..n): 2 = short year, 1 & 3..n = padded number
354 // Qt only supports long (4) or short (2) year, use long for all others
355 if (repeat == 2)
356 result += "yy"_L1;
357 else
358 result += "yyyy"_L1;
359 break;
360 case 'L': // Standalone Month (1..5): 4 = long, 3 = short, 1,2 = number, 5 = narrow
361 case 'M': // Month (1..5): 4 = long, 3 = short, 1,2 = number, 5 = narrow
362 // Qt only supports long, short and number, use short for narrow
363 if (repeat == 5)
364 result += "MMM"_L1;
365 else
366 result += QString(repeat, u'M');
367 break;
368 case 'd': // Day of Month (1,2): 1,2 padded number
369 result += QString(repeat, c);
370 break;
371 case 'c': // Standalone version of 'e'
372 case 'e': // Local Day of Week (1..6): 4 = long, 3 = short, 5,6 = narrow, 1,2 padded number
373 // "Local" only affects numeric form: depends on locale's start-day of the week.
374 case 'E': // Day of Week (1..6): 4 = long, 1..3 = short, 5,6 = narrow
375 // Qt only supports long, short: use short for narrow and padded number.
376 if (repeat == 4)
377 result += "dddd"_L1;
378 else
379 result += "ddd"_L1;
380 break;
381 case 'a': // AM/PM (1..n): Qt supports no distinctions
382 case 'b': // Like a, but also distinguishing noon, midnight (ignore difference).
383 case 'B': // Flexible day period (at night, &c.)
384 // Translate to Qt AM/PM, using locale-appropriate case:
385 result += "Ap"_L1;
386 break;
387 case 'h': // Hour [1..12] (1,2): 1,2 = padded number
388 case 'K': // Hour [0..11] (1,2): 1,2 = padded number
389 result += QString(repeat, 'h'_L1);
390 break;
391 case 'H': // Hour [0..23] (1,2): 1,2 = padded number
392 case 'k': // Hour [1..24] (1,2): 1,2 = padded number
393 // Qt H is 0..23 hour
394 result += QString(repeat, 'H'_L1);
395 break;
396 case 'm': // Minutes (1,2): 1,2 = padded number
397 case 's': // Seconds (1,2): 1,2 = padded number
398 result += QString(repeat, c);
399 break;
400 case 'S': // Fractional second (1..n): 1..n = truncates to decimal places
401 // Qt uses msecs either unpadded or padded to 3 places
402 if (repeat < 3)
403 result += u'z';
404 else
405 result += "zzz"_L1;
406 break;
407 case 'O': // Time Zone (1, 4)
408 result += u't';
409 break;
410 case 'v': // Time Zone (1, 4)
411 case 'V': // Time Zone (1..4)
412 result += "tttt"_L1;
413 break;
414 case 'x': // Time Zone (1..5)
415 case 'X': // Time Zone (1..5)
416 result += (repeat > 1 && (repeat & 1)) ? "ttt"_L1 : "tt"_L1;
417 break;
418 case 'z': // Time Zone (1..4)
419 case 'Z': // Time Zone (1..5)
420 result += repeat < 4 ? "tt"_L1 : repeat > 4 ? "ttt"_L1 : "t"_L1;
421 break;
422 default:
423 // a..z and A..Z are reserved for format codes, so any occurrence of these not
424 // already processed are not known and so unsupported formats to be ignored.
425 // All other chars are allowed as literals.
426 if (c < u'A' || c > u'z' || (c > u'Z' && c < u'a'))
427 result += QString(repeat, c);
428 break;
429 }
430
431 i += repeat;
432 }
433
434 return !result.isEmpty() ? QVariant::fromValue(result) : QVariant();
435}
436
437static QVariant getGroupingSizes()
438{
439 // It does not seem like you can directly query the group sizes from CFLocale as there
440 // is no key that corresponds to it, see:
441 // https://developer.apple.com/documentation/corefoundation/cflocalekey
442 // We have to create a number formatter for the locale and query the data from there.
443 // see: https://developer.apple.com/documentation/corefoundation/1390801-cfnumberformattercopyproperty
444 QLocaleData::GroupSizes sizes;
445 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
446 QCFType<CFNumberFormatterRef> numberFormatter =
447 CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
448 CFTypeRef numTref =
449 CFNumberFormatterCopyProperty(numberFormatter, kCFNumberFormatterGroupingSize);
450 CFNumberRef num = static_cast<CFNumberRef>(numTref);
451 int value;
452 if (CFNumberGetValue(num, kCFNumberIntType, &value) && value > 0) {
453 sizes.least = value;
454 sizes.higher = value;
455 }
456 return QVariant::fromValue(sizes);
457}
458
459static QVariant getMacDateFormat(CFDateFormatterStyle style)
460{
461 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
462 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
463 l, style, kCFDateFormatterNoStyle);
464 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
465}
466
467static QVariant getMacTimeFormat(CFDateFormatterStyle style)
468{
469 QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
470 QCFType<CFDateFormatterRef> formatter = CFDateFormatterCreate(kCFAllocatorDefault,
471 l, kCFDateFormatterNoStyle, style);
472 return macToQtFormat(QString::fromCFString(CFDateFormatterGetFormat(formatter)));
473}
474
475static QVariant getCFLocaleValue(CFStringRef key)
476{
477 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
478 CFTypeRef value = CFLocaleGetValue(locale, key);
479 if (!value)
480 return QVariant();
481 return QString::fromCFString(CFStringRef(static_cast<CFTypeRef>(value)));
482}
483
484static QVariant macMeasurementSystem()
485{
486 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
487 CFStringRef system = static_cast<CFStringRef>(CFLocaleGetValue(locale, kCFLocaleMeasurementSystem));
488 if (QString::fromCFString(system) == "Metric"_L1) {
489 return QLocale::MetricSystem;
490 } else {
491 return QLocale::ImperialSystem;
492 }
493}
494
495
497{
498 QCFType<CFCalendarRef> calendar = CFCalendarCopyCurrent();
499 quint8 day = static_cast<quint8>(CFCalendarGetFirstWeekday(calendar))-1;
500 if (day == 0)
501 day = 7;
502 return day;
503}
504
505static QVariant macCurrencySymbol(QLocale::CurrencySymbolFormat format)
506{
507 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
508 switch (format) {
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);
517 }
518 default:
519 break;
520 }
521 return {};
522}
523
524#ifndef QT_NO_SYSTEMLOCALE
525static QVariant macFormatCurrency(const QSystemLocale::CurrencyToStringArgument &arg)
526{
527 QCFType<CFNumberRef> value;
528 switch (arg.value.metaType().id()) {
529 case QMetaType::Int:
530 case QMetaType::UInt: {
531 int v = arg.value.toInt();
532 value = CFNumberCreate(NULL, kCFNumberIntType, &v);
533 break;
534 }
535 case QMetaType::Double: {
536 double v = arg.value.toDouble();
537 value = CFNumberCreate(NULL, kCFNumberDoubleType, &v);
538 break;
539 }
540 case QMetaType::LongLong:
541 case QMetaType::ULongLong: {
542 qint64 v = arg.value.toLongLong();
543 value = CFNumberCreate(NULL, kCFNumberLongLongType, &v);
544 break;
545 }
546 default:
547 return {};
548 }
549
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());
556 }
557 QCFType<CFStringRef> result = CFNumberFormatterCreateStringWithNumber(NULL, currencyFormatter, value);
558 return QString::fromCFString(result);
559}
560
561static QVariant macQuoteString(QSystemLocale::QueryType type, QStringView str)
562{
563 QString begin, end;
564 QCFType<CFLocaleRef> locale = CFLocaleCopyCurrent();
565 switch (type) {
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);
574 default:
575 break;
576 }
577 return QVariant();
578}
579#endif //QT_NO_SYSTEMLOCALE
580
581#ifndef QT_NO_SYSTEMLOCALE
582
583QLocale QSystemLocale::fallbackLocale() const
584{
585 return QLocale(getMacLocaleName());
586}
587
588template <auto CodeToValueFunction>
589static QVariant getLocaleValue(CFStringRef key)
590{
591 if (auto code = getCFLocaleValue(key); !code.isNull()) {
592 // If an invalid locale is requested with -AppleLocale, the system APIs
593 // will report invalid or empty locale values back to us, which codeToLanguage()
594 // and friends will fail to parse, resulting in returning QLocale::Any{L/C/S}.
595 // If this is the case, we fall down and return a null-variant, which
596 // QLocale's updateSystemPrivate() will interpret to use fallback logic.
597 if (auto value = CodeToValueFunction(code.toString()))
598 return value;
599 }
600 return QVariant();
601}
602
603static QLocale::Language codeToLanguage(QStringView s)
604{
605 return QLocalePrivate::codeToLanguage(s);
606}
607
608QVariant QSystemLocale::query(QueryType type, QVariant &&in) const
609{
610 QMacAutoReleasePool pool;
611
612 switch(type) {
613 case LanguageId:
614 return getLocaleValue<codeToLanguage>(kCFLocaleLanguageCode);
615 case TerritoryId:
616 return getLocaleValue<QLocalePrivate::codeToTerritory>(kCFLocaleCountryCode);
617 case ScriptId:
618 return getLocaleValue<QLocalePrivate::codeToScript>(kCFLocaleScriptCode);
619 case DecimalPoint:
620 return getCFLocaleValue(kCFLocaleDecimalSeparator);
621 case Grouping:
622 return getGroupingSizes();
623 case GroupSeparator:
624 return getCFLocaleValue(kCFLocaleGroupingSeparator);
625 case DateFormatLong:
626 case DateFormatShort:
627 return getMacDateFormat(type == DateFormatShort
628 ? kCFDateFormatterShortStyle
629 : kCFDateFormatterLongStyle);
630 case TimeFormatLong:
631 case TimeFormatShort:
632 return getMacTimeFormat(type == TimeFormatShort
633 ? kCFDateFormatterShortStyle
634 : kCFDateFormatterLongStyle);
635 case DayNameLong:
636 case DayNameShort:
637 case DayNameNarrow:
638 case StandaloneDayNameLong:
639 case StandaloneDayNameShort:
640 case StandaloneDayNameNarrow:
641 return macDayName(in.toInt(), type);
642 case MonthNameLong:
643 case MonthNameShort:
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));
655
656 case NegativeSign:
657 case PositiveSign:
658 break;
659 case ZeroDigit:
660 return macZeroDigit();
661
662 case MeasurementSystem:
663 return macMeasurementSystem();
664
665 case AMText:
666 case PMText: {
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);
672 }
673 case FirstDayOfWeek:
674 return QVariant(macFirstDayOfWeek());
675 case CurrencySymbol:
676 return macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
677 case CurrencyToString:
678 return macFormatCurrency(in.value<CurrencyToStringArgument>());
679 case UILanguages: {
680 QStringList result;
681 QCFType<CFArrayRef> languages = CFLocaleCopyPreferredLanguages();
682 const CFIndex cnt = CFArrayGetCount(languages);
683 result.reserve(cnt);
684 for (CFIndex i = 0; i < cnt; ++i) {
685 const QString lang = QString::fromCFString(
686 static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages, i)));
687 result.append(lang);
688 }
689 return QVariant(result);
690 }
691 case StringToStandardQuotation:
692 case StringToAlternateQuotation:
693 return macQuoteString(type, in.value<QStringView>());
694 default:
695 break;
696 }
697 return QVariant();
698}
699
700#endif // QT_NO_SYSTEMLOCALE
701
702#if !QT_CONFIG(icu)
703
704static QString localeConvertString(const QByteArray &localeID, const QString &str, bool *ok,
705 bool toLowerCase)
706{
707 QMacAutoReleasePool pool;
708 Q_ASSERT(ok);
709 NSString *localestring = [[NSString alloc] initWithData:localeID.toNSData()
710 encoding:NSUTF8StringEncoding];
711 NSLocale *locale = [NSLocale localeWithLocaleIdentifier:localestring];
712 if (!locale) {
713 *ok = false;
714 return QString();
715 }
716 *ok = true;
717 NSString *nsstring = str.toNSString();
718 if (toLowerCase)
719 nsstring = [nsstring lowercaseStringWithLocale:locale];
720 else
721 nsstring = [nsstring uppercaseStringWithLocale:locale];
722
723 return QString::fromNSString(nsstring);
724}
725
726QString QLocalePrivate::toLower(const QString &str, bool *ok) const
727{
728 return localeConvertString(bcp47Name('-'), str, ok, true);
729}
730
731QString QLocalePrivate::toUpper(const QString &str, bool *ok) const
732{
733 return localeConvertString(bcp47Name('-'), str, ok, false);
734}
735
736#endif
737
738QT_END_NAMESPACE
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QVariant getMacTimeFormat(CFDateFormatterStyle style)
static QVariant macDateToString(QDate date, bool short_format)
static QVariant getCFLocaleValue(CFStringRef key)
static void printLocalizationInformation()
static QVariant macDayName(int day, QSystemLocale::QueryType type)
static QVariant macMeasurementSystem()
static QVariant macTimeToString(QTime time, bool short_format)
static QVariant macMonthName(int month, QSystemLocale::QueryType type)
static QVariant macCurrencySymbol(QLocale::CurrencySymbolFormat format)
static QVariant getGroupingSizes()
static QString zeroPad(QString &&number, qsizetype minDigits, const QString &zero)
static QVariant macToQtFormat(QStringView sys_fmt)
static QString fourDigitYear(int year, const QString &zero)
static QVariant macFormatCurrency(const QSystemLocale::CurrencyToStringArgument &arg)
static QString getMacLocaleName()
static QString trimTwoDigits(QString &&number)
static QVariant getMacDateFormat(CFDateFormatterStyle style)
static QString macDateToStringImpl(QDate date, CFDateFormatterStyle style)
static QString macZeroDigit()
static quint8 macFirstDayOfWeek()
Q_COREAPP_STARTUP_FUNCTION(printLocalizationInformation)
static QLocale::Language codeToLanguage(QStringView s)
static QVariant macQuoteString(QSystemLocale::QueryType type, QStringView str)
static QVariant getLocaleValue(CFStringRef key)