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