1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include "qglobal.h"
7
8#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1000
9/* gcc has complained about storing a pointer to a static QLocalePrivate in a
10 QSharedDataPointer, whose destructor would free the non-heap object if the
11 refcount ever got down to zero. The static instances this happens to are
12 instantiated with a refcount of 1 that never gets decremented so as long as
13 QSharedDataPointer keeps its incref()s and decref()s balanced it'll never get
14 down to zero - but the clever compiler isn't quite smart enough to figure
15 that out.
16*/
17QT_WARNING_DISABLE_GCC("-Wfree-nonheap-object") // false positive tracking
18#endif
19
20#if defined(Q_OS_MACOS)
21# include "private/qcore_mac_p.h"
22# include <CoreFoundation/CoreFoundation.h>
23#endif
24
25#include "qplatformdefs.h"
26
27#include "qcalendar.h"
28#include "qdatastream.h"
29#include "qdebug.h"
30#include "private/qduplicatetracker_p.h"
31#include "qhashfunctions.h"
32#include "qstring.h"
33#include "qstringiterator_p.h"
34#include "qlocale.h"
35#include "qlocale_p.h"
36#include "qlocale_tools_p.h"
37#include <private/qtools_p.h>
38#if QT_CONFIG(datetimeparser)
39#include "private/qdatetimeparser_p.h"
40#endif
41#include "qnamespace.h"
42#include "qdatetime.h"
43#include "qstringlist.h"
44#include "qvariant.h"
45#include "qvarlengtharray.h"
46#include "qstringbuilder.h"
47#if QT_CONFIG(timezone)
48# include "qtimezone.h"
49#endif
50#include "private/qnumeric_p.h"
51#include "private/qtools_p.h"
52#include <cmath>
53#ifndef QT_NO_SYSTEMLOCALE
54# include "qmutex.h"
55#endif
56#ifdef Q_OS_WIN
57# include <qt_windows.h>
58# include <time.h>
59#endif
60
61#include "private/qcalendarbackend_p.h"
62#include "private/qgregoriancalendar_p.h"
63#if QT_CONFIG(timezone) && QT_CONFIG(timezone_locale) && !QT_CONFIG(icu)
64# include "private/qtimezonelocale_p.h"
65#endif
66
67#include <q20iterator.h>
68
69QT_BEGIN_NAMESPACE
70
71constexpr int QLocale::DefaultTwoDigitBaseYear;
72
73QT_IMPL_METATYPE_EXTERN_TAGGED(QList<Qt::DayOfWeek>, QList_Qt__DayOfWeek)
74#ifndef QT_NO_SYSTEMLOCALE
75QT_IMPL_METATYPE_EXTERN_TAGGED(QSystemLocale::CurrencyToStringArgument,
76 QSystemLocale__CurrencyToStringArgument)
77#endif
78
79using namespace Qt::StringLiterals;
80using namespace QtMiscUtils;
81
82#ifndef QT_NO_SYSTEMLOCALE
83Q_CONSTINIT static QSystemLocale *_systemLocale = nullptr;
84Q_CONSTINIT static QLocaleData systemLocaleData = {};
85#endif
86
87static_assert(ascii_isspace(c: ' '));
88static_assert(ascii_isspace(c: '\t'));
89static_assert(ascii_isspace(c: '\n'));
90static_assert(ascii_isspace(c: '\v'));
91static_assert(ascii_isspace(c: '\f'));
92static_assert(ascii_isspace(c: '\r'));
93static_assert(!ascii_isspace(c: '\0'));
94static_assert(!ascii_isspace(c: '\a'));
95static_assert(!ascii_isspace(c: 'a'));
96static_assert(!ascii_isspace(c: '\177'));
97static_assert(!ascii_isspace(c: uchar('\200')));
98static_assert(!ascii_isspace(c: uchar('\xA0'))); // NBSP (is a space but Latin 1, not ASCII)
99static_assert(!ascii_isspace(c: uchar('\377')));
100
101/******************************************************************************
102** Helpers for accessing Qt locale database
103*/
104
105QT_BEGIN_INCLUDE_NAMESPACE
106#include "qlocale_data_p.h"
107QT_END_INCLUDE_NAMESPACE
108
109QLocale::Language QLocalePrivate::codeToLanguage(QStringView code,
110 QLocale::LanguageCodeTypes codeTypes) noexcept
111{
112 const auto len = code.size();
113 if (len != 2 && len != 3)
114 return QLocale::AnyLanguage;
115
116 const char16_t uc1 = code[0].toLower().unicode();
117 const char16_t uc2 = code[1].toLower().unicode();
118 const char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0;
119
120 // All language codes are ASCII.
121 if (uc1 > 0x7F || uc2 > 0x7F || uc3 > 0x7F)
122 return QLocale::AnyLanguage;
123
124 const AlphaCode codeBuf = { char(uc1), char(uc2), char(uc3) };
125
126 auto searchCode = [codeBuf](auto f) {
127 return std::find_if(languageCodeList.begin(), languageCodeList.end(),
128 [=](LanguageCodeEntry i) { return f(i) == codeBuf; });
129 };
130
131 if (codeTypes.testFlag(flag: QLocale::ISO639Part1) && uc3 == 0) {
132 auto i = searchCode([](LanguageCodeEntry i) { return i.part1; });
133 if (i != languageCodeList.end())
134 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
135 }
136
137 if (uc3 != 0) {
138 if (codeTypes.testFlag(flag: QLocale::ISO639Part2B)) {
139 auto i = searchCode([](LanguageCodeEntry i) { return i.part2B; });
140 if (i != languageCodeList.end())
141 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
142 }
143
144 // Optimization: Part 2T code if present is always the same as Part 3 code.
145 // This is asserted in iso639_3.LanguageCodeData.
146 if (codeTypes.testFlag(flag: QLocale::ISO639Part2T)
147 && !codeTypes.testFlag(flag: QLocale::ISO639Part3)) {
148 auto i = searchCode([](LanguageCodeEntry i) { return i.part2T; });
149 if (i != languageCodeList.end())
150 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
151 }
152
153 if (codeTypes.testFlag(flag: QLocale::ISO639Part3)) {
154 auto i = searchCode([](LanguageCodeEntry i) { return i.part3; });
155 if (i != languageCodeList.end())
156 return QLocale::Language(std::distance(first: languageCodeList.begin(), last: i));
157 }
158 }
159
160 if (codeTypes.testFlag(flag: QLocale::LegacyLanguageCode) && uc3 == 0) {
161 constexpr struct LegacyCodes {
162 AlphaCode code;
163 QLocale::Language language;
164 } legacyCodes[] = {
165 { .code: {'n', 'o'}, .language: QLocale::NorwegianBokmal }, // no -> nb
166 { .code: {'t', 'l'}, .language: QLocale::Filipino }, // tl -> fil
167 { .code: {'s', 'h'}, .language: QLocale::Serbian }, // sh -> sr[_Latn]
168 { .code: {'m', 'o'}, .language: QLocale::Romanian }, // mo -> ro
169 // Android uses the following deprecated codes:
170 { .code: {'i', 'w'}, .language: QLocale::Hebrew }, // iw -> he
171 { .code: {'i', 'n'}, .language: QLocale::Indonesian }, // in -> id
172 { .code: {'j', 'i'}, .language: QLocale::Yiddish }, // ji -> yi
173 };
174 // We don't need binary search for seven entries (and they're not
175 // sorted), so search linearly:
176 for (const auto &e : legacyCodes) {
177 if (codeBuf == e.code)
178 return e.language;
179 }
180 }
181 return QLocale::AnyLanguage;
182}
183
184static qsizetype scriptIndex(QStringView code, Qt::CaseSensitivity cs) noexcept
185{
186 if (code.size() != 4)
187 return -1;
188
189 // Scripts are titlecased in script_code_list.
190 const bool fixCase = cs == Qt::CaseInsensitive;
191 const unsigned char c0 = (fixCase ? code[0].toUpper() : code[0]).toLatin1();
192 const unsigned char c1 = (fixCase ? code[1].toLower() : code[1]).toLatin1();
193 const unsigned char c2 = (fixCase ? code[2].toLower() : code[2]).toLatin1();
194 const unsigned char c3 = (fixCase ? code[3].toLower() : code[3]).toLatin1();
195 // Any outside the Latin1 repertoire aren't ASCII => will not match.
196 if (!c0 || !c1 || !c2 || !c3)
197 return -1;
198
199 constexpr qsizetype NumScripts = QLocale::LastScript + 1;
200 static_assert(sizeof(script_code_list) == 4 * NumScripts + 1); // +1 for an extra NUL
201 const unsigned char *c = script_code_list;
202 for (qsizetype i = 0; i < NumScripts; ++i, c += 4) {
203 if (c0 == c[0] && c1 == c[1] && c2 == c[2] && c3 == c[3])
204 return i;
205 }
206 return -1;
207}
208
209QLocale::Script QLocalePrivate::codeToScript(QStringView code) noexcept
210{
211 qsizetype index = scriptIndex(code, cs: Qt::CaseInsensitive);
212 return index < 0 ? QLocale::AnyScript : QLocale::Script(index);
213}
214
215QLocale::Territory QLocalePrivate::codeToTerritory(QStringView code) noexcept
216{
217 const auto len = code.size();
218 if (len != 2 && len != 3)
219 return QLocale::AnyTerritory;
220
221 char16_t uc1 = code[0].toUpper().unicode();
222 char16_t uc2 = code[1].toUpper().unicode();
223 char16_t uc3 = len > 2 ? code[2].toUpper().unicode() : 0;
224
225 const unsigned char *c = territory_code_list;
226 for (; *c != 0; c += 3) {
227 if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2])
228 return QLocale::Territory((c - territory_code_list)/3);
229 }
230
231 return QLocale::AnyTerritory;
232}
233
234std::array<char, 4> QLocalePrivate::languageToCode(QLocale::Language language,
235 QLocale::LanguageCodeTypes codeTypes)
236{
237 if (language == QLocale::AnyLanguage || language > QLocale::LastLanguage)
238 return {};
239 if (language == QLocale::C)
240 return {'C'};
241
242 const LanguageCodeEntry &i = languageCodeList[language];
243
244 if (codeTypes.testFlag(flag: QLocale::ISO639Part1) && i.part1.isValid())
245 return i.part1.decode();
246
247 if (codeTypes.testFlag(flag: QLocale::ISO639Part2B) && i.part2B.isValid())
248 return i.part2B.decode();
249
250 if (codeTypes.testFlag(flag: QLocale::ISO639Part2T) && i.part2T.isValid())
251 return i.part2T.decode();
252
253 if (codeTypes.testFlag(flag: QLocale::ISO639Part3))
254 return i.part3.decode();
255
256 return {};
257}
258
259QLatin1StringView QLocalePrivate::scriptToCode(QLocale::Script script)
260{
261 if (script == QLocale::AnyScript || script > QLocale::LastScript)
262 return {};
263 const unsigned char *c = script_code_list + 4 * script;
264 return {reinterpret_cast<const char *>(c), 4};
265}
266
267QLatin1StringView QLocalePrivate::territoryToCode(QLocale::Territory territory)
268{
269 if (territory == QLocale::AnyTerritory || territory > QLocale::LastTerritory)
270 return {};
271
272 const unsigned char *c = territory_code_list + 3 * territory;
273 return {reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3};
274}
275
276namespace {
277struct LikelyPair
278{
279 QLocaleId key; // Search key.
280 QLocaleId value = QLocaleId { .language_id: 0, .script_id: 0, .territory_id: 0 };
281};
282
283bool operator<(LikelyPair lhs, LikelyPair rhs)
284{
285 // Must match the comparison LocaleDataWriter.likelySubtags() uses when
286 // sorting, see qtbase/util/locale_database.qlocalexml2cpp.py
287 const auto compare = [](int lhs, int rhs) {
288 // 0 sorts after all other values; lhs and rhs are passed ushort values.
289 const int huge = 0x10000;
290 return (lhs ? lhs : huge) - (rhs ? rhs : huge);
291 };
292 const auto &left = lhs.key;
293 const auto &right = rhs.key;
294 // Comparison order: language, region, script:
295 if (int cmp = compare(left.language_id, right.language_id))
296 return cmp < 0;
297 if (int cmp = compare(left.territory_id, right.territory_id))
298 return cmp < 0;
299 return compare(left.script_id, right.script_id) < 0;
300}
301} // anonymous namespace
302
303/*!
304 Fill in blank fields of a locale ID.
305
306 An ID in which some fields are zero stands for any locale that agrees with
307 it in its non-zero fields. CLDR's likely-subtag data is meant to help us
308 chose which candidate to prefer. (Note, however, that CLDR does have some
309 cases where it maps an ID to a "best match" for which CLDR does not provide
310 data, even though there are locales for which CLDR does provide data that do
311 match the given ID. It's telling us, unhelpfully but truthfully, what
312 locale would (most likely) be meant by (someone using) the combination
313 requested, even when that locale isn't yet supported.) It may also map an
314 obsolete or generic tag to a modern or more specific replacement, possibly
315 filling in some of the other fields in the process (presently only for
316 countries). Note that some fields of the result may remain blank, but there
317 is no more specific recommendation available.
318
319 For the formal specification, see
320 https://www.unicode.org/reports/tr35/#Likely_Subtags
321
322 \note We also search und_script_region and und_region; they're not mentioned
323 in the spec, but the examples clearly presume them and CLDR does provide
324 such likely matches.
325*/
326QLocaleId QLocaleId::withLikelySubtagsAdded() const noexcept
327{
328 /* Each pattern that appears in a comments below, language_script_region and
329 similar, indicates which of this's fields (even if blank) are being
330 attended to in a given search; for fields left out of the pattern, the
331 search uses 0 regardless of whether this has specified the field.
332
333 If a key matches what we're searching for (possibly with a wildcard in
334 the key matching a non-wildcard in our search), the tags from this that
335 are specified in the key are replaced by the match (even if different);
336 but the other tags of this replace what's in the match (even when the
337 match does specify a value).
338
339 Keep QLocaleXmlReader.__fillLikely() in sync with this, to ensure
340 locale-appropriate time-zone naming works correctly.
341 */
342 static_assert(std::size(likely_subtags) % 2 == 0);
343 auto *pairs = reinterpret_cast<const LikelyPair *>(likely_subtags);
344 auto *const afterPairs = pairs + std::size(likely_subtags) / 2;
345 LikelyPair sought { .key: *this };
346 // Our array is sorted in the order that puts all candidate matches in the
347 // order we would want them; ones we should prefer appear before the others.
348 if (language_id) {
349 // language_script_region, language_region, language_script, language:
350 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
351 // Single language's block isn't long enough to warrant more binary
352 // chopping within it - just traverse it all:
353 for (; pairs < afterPairs && pairs->key.language_id == language_id; ++pairs) {
354 const QLocaleId &key = pairs->key;
355 if (key.territory_id && key.territory_id != territory_id)
356 continue;
357 if (key.script_id && key.script_id != script_id)
358 continue;
359 QLocaleId value = pairs->value;
360 if (territory_id && !key.territory_id)
361 value.territory_id = territory_id;
362 if (script_id && !key.script_id)
363 value.script_id = script_id;
364 return value;
365 }
366 }
367 // und_script_region or und_region (in that order):
368 if (territory_id) {
369 sought.key = QLocaleId { .language_id: 0, .script_id: script_id, .territory_id: territory_id };
370 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
371 // Again, individual und_?_region block isn't long enough to make binary
372 // chop a win:
373 for (; pairs < afterPairs && pairs->key.territory_id == territory_id; ++pairs) {
374 const QLocaleId &key = pairs->key;
375 Q_ASSERT(!key.language_id);
376 if (key.script_id && key.script_id != script_id)
377 continue;
378 QLocaleId value = pairs->value;
379 if (language_id)
380 value.language_id = language_id;
381 if (script_id && !key.script_id)
382 value.script_id = script_id;
383 return value;
384 }
385 }
386 // und_script:
387 if (script_id) {
388 sought.key = QLocaleId { .language_id: 0, .script_id: script_id, .territory_id: 0 };
389 pairs = std::lower_bound(first: pairs, last: afterPairs, val: sought);
390 if (pairs < afterPairs && pairs->key.script_id == script_id) {
391 Q_ASSERT(!pairs->key.language_id && !pairs->key.territory_id);
392 QLocaleId value = pairs->value;
393 if (language_id)
394 value.language_id = language_id;
395 if (territory_id)
396 value.territory_id = territory_id;
397 return value;
398 }
399 }
400 // Finally, fall back to the match-all rule (if there is one):
401 pairs = afterPairs - 1; // All other keys are < match-all.
402 if (pairs->key.matchesAll()) {
403 QLocaleId value = pairs->value;
404 if (language_id)
405 value.language_id = language_id;
406 if (territory_id)
407 value.territory_id = territory_id;
408 if (script_id)
409 value.script_id = script_id;
410 return value;
411 }
412 return *this;
413}
414
415QLocaleId QLocaleId::withLikelySubtagsRemoved() const noexcept
416{
417 QLocaleId max = withLikelySubtagsAdded();
418 // language
419 {
420 QLocaleId id { .language_id: language_id, .script_id: 0, .territory_id: 0 };
421 if (id.withLikelySubtagsAdded() == max)
422 return id;
423 }
424 // language_region
425 if (territory_id) {
426 QLocaleId id { .language_id: language_id, .script_id: 0, .territory_id: territory_id };
427 if (id.withLikelySubtagsAdded() == max)
428 return id;
429 }
430 // language_script
431 if (script_id) {
432 QLocaleId id { .language_id: language_id, .script_id: script_id, .territory_id: 0 };
433 if (id.withLikelySubtagsAdded() == max)
434 return id;
435 }
436 return max;
437}
438
439QByteArray QLocaleId::name(char separator) const
440{
441 if (language_id == QLocale::AnyLanguage)
442 return QByteArray();
443 if (language_id == QLocale::C)
444 return QByteArrayLiteral("C");
445 Q_ASSERT(language_id <= QLocale::LastLanguage);
446 Q_ASSERT(script_id <= QLocale::LastScript);
447 Q_ASSERT(territory_id <= QLocale::LastTerritory);
448
449 const LanguageCodeEntry &language = languageCodeList[language_id];
450 AlphaCode lang;
451 qsizetype langLen;
452
453 if (language.part1.isValid()) {
454 lang = language.part1;
455 langLen = 2;
456 } else {
457 lang = language.part2B.isValid() ? language.part2B : language.part3;
458 langLen = 3;
459 }
460
461 const unsigned char *script =
462 (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
463 const unsigned char *country =
464 (territory_id != QLocale::AnyTerritory
465 ? territory_code_list + 3 * territory_id : nullptr);
466 qsizetype len = langLen + (script ? 4 + 1 : 0) + (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
467 QByteArray name(len, Qt::Uninitialized);
468 char *uc = name.data();
469
470 auto langArray = lang.decode();
471
472 *uc++ = langArray[0];
473 *uc++ = langArray[1];
474 if (langLen > 2)
475 *uc++ = langArray[2];
476
477 if (script) {
478 *uc++ = separator;
479 *uc++ = script[0];
480 *uc++ = script[1];
481 *uc++ = script[2];
482 *uc++ = script[3];
483 }
484 if (country) {
485 *uc++ = separator;
486 *uc++ = country[0];
487 *uc++ = country[1];
488 if (country[2] != 0)
489 *uc++ = country[2];
490 }
491 return name;
492}
493
494QByteArray QLocalePrivate::bcp47Name(char separator) const
495{
496 if (m_data->m_language_id == QLocale::AnyLanguage)
497 return QByteArray();
498 if (m_data->m_language_id == QLocale::C)
499 return QByteArrayView("en") % separator % QByteArrayView("POSIX");
500
501 return m_data->id().withLikelySubtagsRemoved().name(separator);
502}
503
504static qsizetype findLocaleIndexById(QLocaleId localeId) noexcept
505{
506 qsizetype idx = locale_index[localeId.language_id];
507 // If there are no locales for specified language (so we we've got the
508 // default language, which has no associated script or country), give up:
509 if (localeId.language_id && idx == 0)
510 return idx;
511
512 Q_ASSERT(localeId.acceptLanguage(locale_data[idx].m_language_id));
513
514 do {
515 if (localeId.acceptScriptTerritory(other: locale_data[idx].id()))
516 return idx;
517 ++idx;
518 } while (localeId.acceptLanguage(lang: locale_data[idx].m_language_id));
519
520 return -1;
521}
522
523static constexpr qsizetype locale_data_size = q20::ssize(locale_data) - 1; // trailing guard
524bool QLocaleData::allLocaleDataRows(bool (*check)(qsizetype, const QLocaleData &))
525{
526 for (qsizetype index = 0; index < locale_data_size; ++index) {
527 if (!(*check)(index, locale_data[index]))
528 return false;
529 }
530 return true;
531}
532
533#if QT_CONFIG(timezone) && QT_CONFIG(timezone_locale) && !QT_CONFIG(icu)
534namespace QtTimeZoneLocale {
535
536// Indices of locales obtained from the given by likely subtag fall-backs.
537QList<qsizetype> fallbackLocalesFor(qsizetype index)
538{
539 // Should match QLocaleXmlReader.pruneZoneNaming()'s fallbacks() helper,
540 // aside from the special-case kludge for C -> en_US.
541 Q_ASSERT(index < locale_data_size);
542 QList<qsizetype> result = {index};
543 QLocaleId id = locale_data[index].id();
544 if (id.language_id == QLocale::C) {
545 id = { QLocale::English, QLocale::LatinScript, QLocale::UnitedStates };
546 qsizetype it = findLocaleIndexById(id);
547 Q_ASSERT_X(it != -1, Q_FUNC_INFO, "Missing en_Latn_US from locale data");
548 Q_ASSERT_X(it != index, // equivalent to !result.contains(it)
549 Q_FUNC_INFO, "en_Latn_US != C");
550 result << it;
551 }
552
553 const QLocaleId base = id;
554 QLocaleId likely = id.withLikelySubtagsAdded();
555 if (likely != base) {
556 qsizetype it = findLocaleIndexById(likely);
557 if (it != -1 && !result.contains(it))
558 result << it;
559 }
560 if (id.territory_id) {
561 id.territory_id = 0;
562 likely = id.withLikelySubtagsAdded();
563 if (likely != base) {
564 qsizetype it = findLocaleIndexById(likely);
565 if (it != -1 && !result.contains(it))
566 result << it;
567 }
568 }
569 if (id.script_id) {
570 id.script_id = 0;
571 likely = id.withLikelySubtagsAdded();
572 if (likely != base) {
573 qsizetype it = findLocaleIndexById(likely);
574 if (it != -1 && !result.contains(it))
575 result << it;
576 }
577 }
578 return result;
579}
580
581} // QtTimeZoneLocale
582#endif // timezone_locale && !icu
583
584qsizetype QLocaleData::findLocaleIndex(QLocaleId lid) noexcept
585{
586 QLocaleId localeId = lid;
587 QLocaleId likelyId = localeId.withLikelySubtagsAdded();
588 const ushort fallback = likelyId.language_id;
589
590 // Try a straight match with the likely data:
591 qsizetype index = findLocaleIndexById(localeId: likelyId);
592 if (index >= 0)
593 return index;
594 QVarLengthArray<QLocaleId, 6> tried;
595 tried.push_back(t: likelyId);
596
597#define CheckCandidate(id) do { \
598 if (!tried.contains(id)) { \
599 index = findLocaleIndexById(id); \
600 if (index >= 0) \
601 return index; \
602 tried.push_back(id); \
603 } \
604 } while (false) // end CheckCandidate
605
606 // No match; try again with raw data:
607 CheckCandidate(localeId);
608
609 // No match; try again with likely country for language_script
610 if (lid.territory_id && (lid.language_id || lid.script_id)) {
611 localeId.territory_id = 0;
612 likelyId = localeId.withLikelySubtagsAdded();
613 CheckCandidate(likelyId);
614
615 // No match; try again with any country
616 CheckCandidate(localeId);
617 }
618
619 // No match; try again with likely script for language_region
620 if (lid.script_id && (lid.language_id || lid.territory_id)) {
621 localeId = QLocaleId { .language_id: lid.language_id, .script_id: 0, .territory_id: lid.territory_id };
622 likelyId = localeId.withLikelySubtagsAdded();
623 CheckCandidate(likelyId);
624
625 // No match; try again with any script
626 CheckCandidate(localeId);
627 }
628#undef CheckCandidate
629
630 // No match; return base index for initial likely language:
631 return locale_index[fallback];
632}
633
634static QStringView findTag(QStringView name) noexcept
635{
636 const std::u16string_view v(name.utf16(), size_t(name.size()));
637 const auto i = v.find_first_of(str: u"_-.@");
638 if (i == std::string_view::npos)
639 return name;
640 return name.first(n: qsizetype(i));
641}
642
643static bool validTag(QStringView tag)
644{
645 // Is tag is a non-empty sequence of ASCII letters and/or digits ?
646 for (QChar uc : tag) {
647 const char16_t ch = uc.unicode();
648 if (!isAsciiLetterOrNumber(c: ch))
649 return false;
650 }
651 return tag.size() > 0;
652}
653
654bool qt_splitLocaleName(QStringView name,
655 QStringView *lang, QStringView *script, QStringView *land) noexcept
656{
657 // Assume each of lang, script and land is nullptr or points to an empty QStringView.
658 enum ParserState { NoState, LangState, ScriptState, CountryState };
659 ParserState state = LangState;
660 while (name.size() && state != NoState) {
661 const QStringView tag = findTag(name);
662 if (!validTag(tag))
663 break;
664 name = name.sliced(pos: tag.size());
665 const bool sep = name.size() > 0;
666 if (sep) // tag wasn't all that remained; there was a separator
667 name = name.sliced(pos: 1);
668
669 switch (state) {
670 case LangState:
671 if (tag.size() != 2 && tag.size() != 3)
672 return false;
673 if (lang)
674 *lang = tag;
675 state = sep ? ScriptState : NoState;
676 break;
677 case ScriptState:
678 if (scriptIndex(code: tag, cs: Qt::CaseSensitive) >= 0) {
679 if (script)
680 *script = tag;
681 state = sep ? CountryState : NoState;
682 break;
683 }
684 // It wasn't a script, assume it's a country.
685 Q_FALLTHROUGH();
686 case CountryState:
687 if (land)
688 *land = tag;
689 state = NoState;
690 break;
691 case NoState: // Precluded by loop condition !
692 Q_UNREACHABLE();
693 break;
694 }
695 }
696 return state != LangState;
697}
698
699QLocaleId QLocaleId::fromName(QStringView name) noexcept
700{
701 QStringView lang;
702 QStringView script;
703 QStringView land;
704 if (!qt_splitLocaleName(name, lang: &lang, script: &script, land: &land))
705 return { .language_id: QLocale::C, .script_id: 0, .territory_id: 0 };
706
707 // POSIX is a variant, but looks like a territory.
708 if (land.compare(other: "POSIX", cs: Qt::CaseInsensitive) == 0)
709 return { .language_id: QLocale::C, .script_id: 0, .territory_id: 0 };
710
711 QLocale::Language langId = QLocalePrivate::codeToLanguage(code: lang);
712 if (langId == QLocale::AnyLanguage)
713 return { .language_id: QLocale::C, .script_id: 0, .territory_id: 0 };
714 return { .language_id: langId, .script_id: QLocalePrivate::codeToScript(code: script), .territory_id: QLocalePrivate::codeToTerritory(code: land) };
715}
716
717QString qt_readEscapedFormatString(QStringView format, qsizetype *idx)
718{
719 qsizetype &i = *idx;
720
721 Q_ASSERT(format.at(i) == u'\'');
722 ++i;
723 if (i == format.size())
724 return QString();
725 if (format.at(n: i).unicode() == '\'') { // "''" outside of a quoted string
726 ++i;
727 return "'"_L1;
728 }
729
730 QString result;
731
732 while (i < format.size()) {
733 if (format.at(n: i).unicode() == '\'') {
734 if (format.mid(pos: i + 1).startsWith(c: u'\'')) {
735 // "''" inside a quoted string
736 result.append(c: u'\'');
737 i += 2;
738 } else {
739 break;
740 }
741 } else {
742 result.append(c: format.at(n: i++));
743 }
744 }
745 if (i < format.size())
746 ++i;
747
748 return result;
749}
750
751/*!
752 \internal
753
754 Counts the number of identical leading characters in \a s.
755
756 If \a s is empty, returns 0.
757
758 Otherwise, returns the number of consecutive \c{s.front()}
759 characters at the start of \a s.
760
761 \code
762 qt_repeatCount(u"a"); // == 1
763 qt_repeatCount(u"ab"); // == 1
764 qt_repeatCount(u"aab"); // == 2
765 \endcode
766*/
767qsizetype qt_repeatCount(QStringView s) noexcept
768{
769 if (s.isEmpty())
770 return 0;
771 const QChar c = s.front();
772 qsizetype j = 1;
773 while (j < s.size() && s.at(n: j) == c)
774 ++j;
775 return j;
776}
777
778Q_CONSTINIT static const QLocaleData *default_data = nullptr;
779Q_CONSTINIT QBasicAtomicInt QLocalePrivate::s_generation = Q_BASIC_ATOMIC_INITIALIZER(0);
780
781static QLocalePrivate *c_private() noexcept
782{
783 Q_CONSTINIT static QLocalePrivate c_locale(locale_data, 0, QLocale::OmitGroupSeparator, 1);
784 return &c_locale;
785}
786
787static constexpr QLocale::NumberOptions defaultNumberOptions(QLocale::Language forLanguage)
788{
789 return forLanguage == QLocale::C ? QLocale::OmitGroupSeparator : QLocale::DefaultNumberOptions;
790}
791
792static constexpr QLocale::NumberOptions defaultNumberOptions(quint16 forLanguage)
793{
794 return defaultNumberOptions(forLanguage: QLocale::Language(forLanguage));
795}
796
797#ifndef QT_NO_SYSTEMLOCALE
798/******************************************************************************
799** Default system locale behavior
800*/
801
802/*!
803 \internal
804 Constructs a QSystemLocale object.
805
806 The constructor will automatically install this object as the system locale.
807 It and the destructor maintain a stack of system locales, with the
808 most-recently-created instance (that hasn't yet been deleted) used as the
809 system locale. This is only intended as a way to let a platform plugin
810 install its own system locale, overriding what might otherwise be provided
811 for its class of platform (as Android does, differing from Linux), and to
812 let tests transiently override the system or plugin-supplied one. As such,
813 there should not be diverse threads creating and destroying QSystemLocale
814 instances concurrently, so no attempt is made at thread-safety in managing
815 the stack.
816
817 This constructor also resets the flag that'll prompt QLocale::system() to
818 re-initialize its data, so that instantiating a QSystemLocale (even
819 transiently) triggers a refresh of the system locale's data. This is
820 exploited by some test code.
821*/
822QSystemLocale::QSystemLocale() : next(_systemLocale)
823{
824 _systemLocale = this;
825
826 systemLocaleData.m_language_id = 0;
827}
828
829/*!
830 \internal
831 Deletes the object.
832*/
833QSystemLocale::~QSystemLocale()
834{
835 if (_systemLocale == this) {
836 _systemLocale = next;
837
838 // Change to system locale => force refresh.
839 systemLocaleData.m_language_id = 0;
840 } else {
841 for (QSystemLocale *p = _systemLocale; p; p = p->next) {
842 if (p->next == this)
843 p->next = next;
844 }
845 }
846}
847
848static const QSystemLocale *systemLocale()
849{
850 if (_systemLocale)
851 return _systemLocale;
852
853 // As this is only ever instantiated with _systemLocale null, it is
854 // necessarily the ->next-most in any chain that may subsequently develop;
855 // and it won't be destructed until exit()-time.
856 static QSystemLocale globalInstance;
857 return &globalInstance;
858}
859
860static void updateSystemPrivate()
861{
862 // This function is NOT thread-safe!
863 // It *should not* be called by anything but systemData()
864 // It *is* called before {system,default}LocalePrivate exist.
865 const QSystemLocale *sys_locale = systemLocale();
866
867 // tell the object that the system locale has changed.
868 sys_locale->query(type: QSystemLocale::LocaleChanged);
869
870 // Populate system locale with fallback as basis
871 systemLocaleData = locale_data[sys_locale->fallbackLocaleIndex()];
872
873 QVariant res = sys_locale->query(type: QSystemLocale::LanguageId);
874 if (!res.isNull()) {
875 systemLocaleData.m_language_id = res.toInt();
876 systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
877 }
878 res = sys_locale->query(type: QSystemLocale::TerritoryId);
879 if (!res.isNull()) {
880 systemLocaleData.m_territory_id = res.toInt();
881 systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
882 }
883 res = sys_locale->query(type: QSystemLocale::ScriptId);
884 if (!res.isNull())
885 systemLocaleData.m_script_id = res.toInt();
886
887 // Should we replace Any values based on likely sub-tags ?
888
889 // If system locale is default locale, update the default collator's generation:
890 if (default_data == &systemLocaleData)
891 QLocalePrivate::s_generation.fetchAndAddRelaxed(valueToAdd: 1);
892}
893#endif // !QT_NO_SYSTEMLOCALE
894
895static const QLocaleData *systemData(qsizetype *sysIndex = nullptr)
896{
897#ifndef QT_NO_SYSTEMLOCALE
898 /*
899 Copy over the information from the fallback locale and modify.
900
901 If sysIndex is passed, it should be the m_index of the system locale's
902 QLocalePrivate, which we'll update if it needs it.
903
904 This modifies (cross-thread) global state, so is mutex-protected.
905 */
906 {
907 Q_CONSTINIT static QLocaleId sysId;
908 bool updated = false;
909
910 Q_CONSTINIT static QBasicMutex systemDataMutex;
911 systemDataMutex.lock();
912 if (systemLocaleData.m_language_id == 0) {
913 updateSystemPrivate();
914 updated = true;
915 }
916 // Initialization of system private has *sysIndex == -1 to hit this.
917 if (sysIndex && (updated || *sysIndex < 0)) {
918 const QLocaleId nowId = systemLocaleData.id();
919 if (sysId != nowId || *sysIndex < 0) {
920 // This look-up may be expensive:
921 *sysIndex = QLocaleData::findLocaleIndex(lid: nowId);
922 sysId = nowId;
923 }
924 }
925 systemDataMutex.unlock();
926 }
927
928 return &systemLocaleData;
929#else
930 Q_UNUSED(sysIndex);
931 return locale_data;
932#endif
933}
934
935static const QLocaleData *defaultData()
936{
937 if (!default_data)
938 default_data = systemData();
939 return default_data;
940}
941
942static qsizetype defaultIndex()
943{
944 const QLocaleData *const data = defaultData();
945#ifndef QT_NO_SYSTEMLOCALE
946 if (data == &systemLocaleData) {
947 // Work out a suitable index matching the system data, for use when
948 // accessing calendar data, when not fetched from system.
949 return QLocaleData::findLocaleIndex(lid: data->id());
950 }
951#endif
952
953 using QtPrivate::q_points_into_range;
954 Q_ASSERT(q_points_into_range(data, locale_data));
955 return data - locale_data;
956}
957
958const QLocaleData *QLocaleData::c() noexcept
959{
960 Q_ASSERT(locale_index[QLocale::C] == 0);
961 return locale_data;
962}
963
964#ifndef QT_NO_DATASTREAM
965QDataStream &operator<<(QDataStream &ds, const QLocale &l)
966{
967 ds << l.name();
968 return ds;
969}
970
971QDataStream &operator>>(QDataStream &ds, QLocale &l)
972{
973 QString s;
974 ds >> s;
975 l = QLocale(s);
976 return ds;
977}
978#endif // QT_NO_DATASTREAM
979
980Q_GLOBAL_STATIC(QSharedDataPointer<QLocalePrivate>, defaultLocalePrivate,
981 new QLocalePrivate(defaultData(), defaultIndex(),
982 defaultNumberOptions(defaultData()->m_language_id)))
983
984static QLocalePrivate *localePrivateByName(QStringView name)
985{
986 if (name == u"C")
987 return c_private();
988 const qsizetype index = QLocaleData::findLocaleIndex(lid: QLocaleId::fromName(name));
989 Q_ASSERT(index >= 0 && index < locale_data_size);
990 return new QLocalePrivate(locale_data + index, index,
991 defaultNumberOptions(forLanguage: locale_data[index].m_language_id));
992}
993
994static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Script script,
995 QLocale::Territory territory)
996{
997 if (language == QLocale::C)
998 return c_private();
999
1000 qsizetype index = QLocaleData::findLocaleIndex(lid: QLocaleId { .language_id: language, .script_id: script, .territory_id: territory });
1001 Q_ASSERT(index >= 0 && index < locale_data_size);
1002 const QLocaleData *data = locale_data + index;
1003
1004 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;
1005
1006 // If not found, should use default locale:
1007 if (data->m_language_id == QLocale::C) {
1008 if (defaultLocalePrivate.exists())
1009 numberOptions = defaultLocalePrivate->data()->m_numberOptions;
1010 data = defaultData();
1011 index = defaultIndex();
1012 }
1013 return new QLocalePrivate(data, index, numberOptions);
1014}
1015
1016bool comparesEqual(const QLocale &loc, QLocale::Language lang)
1017{
1018 // Keep in sync with findLocalePrivate()!
1019 auto compareWithPrivate = [&loc](const QLocaleData *data, QLocale::NumberOptions opts)
1020 {
1021 return loc.d->m_data == data && loc.d->m_numberOptions == opts;
1022 };
1023
1024 if (lang == QLocale::C)
1025 return compareWithPrivate(c_private()->m_data, c_private()->m_numberOptions);
1026
1027 qsizetype index = QLocaleData::findLocaleIndex(lid: QLocaleId { .language_id: lang });
1028 Q_ASSERT(index >= 0 && index < locale_data_size);
1029 const QLocaleData *data = locale_data + index;
1030
1031 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;
1032
1033 // If not found, should use default locale:
1034 if (data->m_language_id == QLocale::C) {
1035 if (defaultLocalePrivate.exists())
1036 numberOptions = defaultLocalePrivate->data()->m_numberOptions;
1037 data = defaultData();
1038 }
1039 return compareWithPrivate(data, numberOptions);
1040}
1041
1042static std::optional<QString>
1043systemLocaleString(const QLocaleData *that, QSystemLocale::QueryType type)
1044{
1045#ifndef QT_NO_SYSTEMLOCALE
1046 if (that != &systemLocaleData)
1047 return std::nullopt;
1048
1049 QVariant v = systemLocale()->query(type);
1050 if (v.metaType() != QMetaType::fromType<QString>())
1051 return std::nullopt;
1052
1053 return v.toString();
1054#else
1055 Q_UNUSED(that)
1056 Q_UNUSED(type)
1057 return std::nullopt;
1058#endif
1059}
1060
1061static QString localeString(const QLocaleData *that, QSystemLocale::QueryType type,
1062 QLocaleData::DataRange range)
1063{
1064 if (auto opt = systemLocaleString(that, type))
1065 return *opt;
1066 return range.getData(table: single_character_data);
1067}
1068
1069QString QLocaleData::decimalPoint() const
1070{
1071 return localeString(that: this, type: QSystemLocale::DecimalPoint, range: decimalSeparator());
1072}
1073
1074QString QLocaleData::groupSeparator() const
1075{
1076 return localeString(that: this, type: QSystemLocale::GroupSeparator, range: groupDelim());
1077}
1078
1079QString QLocaleData::percentSign() const
1080{
1081 return percent().getData(table: single_character_data);
1082}
1083
1084QString QLocaleData::listSeparator() const
1085{
1086 return listDelimit().getData(table: single_character_data);
1087}
1088
1089QString QLocaleData::zeroDigit() const
1090{
1091 return localeString(that: this, type: QSystemLocale::ZeroDigit, range: zero());
1092}
1093
1094char32_t QLocaleData::zeroUcs() const
1095{
1096#ifndef QT_NO_SYSTEMLOCALE
1097 if (this == &systemLocaleData) {
1098 const auto text = systemLocale()->query(type: QSystemLocale::ZeroDigit).toString();
1099 if (!text.isEmpty()) {
1100 if (text.size() == 1 && !text.at(i: 0).isSurrogate())
1101 return text.at(i: 0).unicode();
1102 if (text.size() == 2 && text.at(i: 0).isHighSurrogate())
1103 return QChar::surrogateToUcs4(high: text.at(i: 0), low: text.at(i: 1));
1104 }
1105 }
1106#endif
1107 return zero().ucsFirst(table: single_character_data);
1108}
1109
1110QString QLocaleData::negativeSign() const
1111{
1112 return localeString(that: this, type: QSystemLocale::NegativeSign, range: minus());
1113}
1114
1115QString QLocaleData::positiveSign() const
1116{
1117 return localeString(that: this, type: QSystemLocale::PositiveSign, range: plus());
1118}
1119
1120QString QLocaleData::exponentSeparator() const
1121{
1122 return exponential().getData(table: single_character_data);
1123}
1124
1125QLocaleData::GroupSizes QLocaleData::groupSizes() const
1126{
1127#ifndef QT_NO_SYSTEMLOCALE
1128 if (this == &systemLocaleData) {
1129 QVariant queryResult = systemLocale()->query(type: QSystemLocale::Grouping);
1130 if (!queryResult.isNull()) {
1131 QLocaleData::GroupSizes sysGroupSizes =
1132 queryResult.value<QLocaleData::GroupSizes>();
1133 if (sysGroupSizes.first <= 0)
1134 sysGroupSizes.first = m_grouping_first;
1135 if (sysGroupSizes.higher <= 0)
1136 sysGroupSizes.higher = m_grouping_higher;
1137 if (sysGroupSizes.least <= 0)
1138 sysGroupSizes.least = m_grouping_least;
1139 return sysGroupSizes;
1140 }
1141 }
1142#endif
1143 return { .first: m_grouping_first,
1144 .higher: m_grouping_higher,
1145 .least: m_grouping_least };
1146}
1147
1148/*!
1149 \internal
1150*/
1151QLocale::QLocale(QLocalePrivate &dd)
1152 : d(&dd)
1153{}
1154
1155/*!
1156 \variable QLocale::DefaultTwoDigitBaseYear
1157 \since 6.7
1158
1159 \brief The default start year of the century within which a format taking
1160 a two-digit year will select. The value of the constant is \c {1900}.
1161
1162 Some locales use, particularly for ShortFormat, only the last two digits of
1163 the year. Proir to 6.7 the year 1900 was always used as a base year for
1164 such cases. Now various QLocale and QDate functions have the overloads that
1165 allow callers to specify the base year, and this constant is used as its
1166 default value.
1167
1168 \sa toDate(), toDateTime(), QDate::fromString(), QDateTime::fromString()
1169*/
1170
1171/*!
1172 \since 6.3
1173
1174 Constructs a QLocale object with the specified \a name.
1175
1176 The name has the format "language[_script][_territory][.codeset][@modifier]"
1177 or "C", where:
1178
1179 \list
1180 \li language is a lowercase, two-letter, ISO 639 language code (some
1181 three-letter codes are also recognized),
1182 \li script is a capitalized, four-letter, ISO 15924 script code,
1183 \li territory is an uppercase, two-letter, ISO 3166 territory code
1184 (some numeric codes are also recognized), and
1185 \li codeset and modifier are ignored.
1186 \endlist
1187
1188 The separator can be either underscore \c{'_'} (U+005F, "low line") or a
1189 dash \c{'-'} (U+002D, "hyphen-minus"). If QLocale has no data for the
1190 specified combination of language, script, and territory, then it uses the
1191 most suitable match it can find instead. If the string violates the locale
1192 format, or no suitable data can be found for the specified keys, the "C"
1193 locale is used instead.
1194
1195 This constructor is much slower than QLocale(Language, Script, Territory) or
1196 QLocale(Language, Territory).
1197
1198 \sa bcp47Name(), {Matching combinations of language, script and territory}
1199*/
1200QLocale::QLocale(QStringView name)
1201 : d(localePrivateByName(name))
1202{
1203}
1204
1205/*!
1206 \fn QLocale::QLocale(const QString &name)
1207 \overload
1208*/
1209
1210/*!
1211 Constructs a QLocale object initialized with the default locale.
1212
1213 If no default locale was set using setDefault(), this locale will be the
1214 same as the one returned by system().
1215
1216 \sa setDefault(), system()
1217*/
1218
1219QLocale::QLocale()
1220 : d(c_private())
1221{
1222 if (!defaultLocalePrivate.isDestroyed()) {
1223 // Make sure system data is up to date:
1224 systemData();
1225 d = *defaultLocalePrivate;
1226 }
1227}
1228
1229/*!
1230 Constructs a QLocale object for the specified \a language and \a territory.
1231
1232 If there is more than one script in use for this combination, a likely
1233 script will be selected. If QLocale has no data for the specified \a
1234 language, the default locale is used. If QLocale has no data for the
1235 specified combination of \a language and \a territory, an alternative
1236 territory may be used instead.
1237
1238 \sa setDefault(), {Matching combinations of language, script and territory}
1239*/
1240
1241QLocale::QLocale(Language language, Territory territory)
1242 : d(findLocalePrivate(language, script: AnyScript, territory))
1243{
1244}
1245
1246/*!
1247 \since 4.8
1248
1249 Constructs a QLocale object for the specified \a language, \a script and \a
1250 territory.
1251
1252 If QLocale does not have data for the given combination, it will find data
1253 for as good a match as it can. It falls back on the default locale if
1254
1255 \list
1256 \li \a language is \c AnyLanguage and no language can be inferred from \a
1257 script and \a territory
1258 \li QLocale has no data for the language, either given as \a language or
1259 inferred as above.
1260 \endlist
1261
1262 \sa setDefault(), {Matching combinations of language, script and territory}
1263*/
1264
1265QLocale::QLocale(Language language, Script script, Territory territory)
1266 : d(findLocalePrivate(language, script, territory))
1267{
1268}
1269
1270/*!
1271 Constructs a QLocale object as a copy of \a other.
1272*/
1273
1274QLocale::QLocale(const QLocale &other) noexcept = default;
1275
1276/*!
1277 Destructor
1278*/
1279
1280QLocale::~QLocale()
1281{
1282}
1283
1284/*!
1285 Assigns \a other to this QLocale object and returns a reference
1286 to this QLocale object.
1287*/
1288
1289QLocale &QLocale::operator=(const QLocale &other) noexcept = default;
1290
1291/*!
1292 \internal
1293 Equality comparison.
1294*/
1295
1296bool QLocale::equals(const QLocale &other) const noexcept
1297{
1298 return d->m_data == other.d->m_data && d->m_numberOptions == other.d->m_numberOptions;
1299}
1300
1301/*!
1302 \fn void QLocale::swap(QLocale &other)
1303 \since 5.6
1304 \memberswap{locale}
1305*/
1306
1307/*!
1308 \since 5.6
1309 \qhashold{QLocale}
1310*/
1311size_t qHash(const QLocale &key, size_t seed) noexcept
1312{
1313 return qHashMulti(seed, args: key.d->m_data, args: key.d->m_numberOptions);
1314}
1315
1316/*!
1317 \since 4.2
1318
1319 Sets the \a options related to number conversions for this QLocale instance.
1320
1321 \sa numberOptions(), FloatingPointPrecisionOption
1322*/
1323void QLocale::setNumberOptions(NumberOptions options)
1324{
1325 d->m_numberOptions = options;
1326}
1327
1328/*!
1329 \since 4.2
1330
1331 Returns the options related to number conversions for this QLocale instance.
1332
1333 By default, no options are set for the standard locales, except for the "C"
1334 locale, which has OmitGroupSeparator set by default.
1335
1336 \sa setNumberOptions(), toString(), groupSeparator(), FloatingPointPrecisionOption
1337*/
1338QLocale::NumberOptions QLocale::numberOptions() const
1339{
1340 return d->m_numberOptions;
1341}
1342
1343/*!
1344 \fn QString QLocale::quoteString(const QString &str, QuotationStyle style) const
1345
1346 \since 4.8
1347
1348 Returns \a str quoted according to the current locale using the given
1349 quotation \a style.
1350*/
1351
1352/*!
1353 \since 6.0
1354
1355 \overload
1356*/
1357QString QLocale::quoteString(QStringView str, QuotationStyle style) const
1358{
1359#ifndef QT_NO_SYSTEMLOCALE
1360 if (d->m_data == &systemLocaleData) {
1361 QVariant res;
1362 if (style == AlternateQuotation)
1363 res = systemLocale()->query(type: QSystemLocale::StringToAlternateQuotation,
1364 in: QVariant::fromValue(value: str));
1365 if (res.isNull() || style == StandardQuotation)
1366 res = systemLocale()->query(type: QSystemLocale::StringToStandardQuotation,
1367 in: QVariant::fromValue(value: str));
1368 if (!res.isNull())
1369 return res.toString();
1370 }
1371#endif
1372
1373 QLocaleData::DataRange start, end;
1374 if (style == StandardQuotation) {
1375 start = d->m_data->quoteStart();
1376 end = d->m_data->quoteEnd();
1377 } else {
1378 start = d->m_data->quoteStartAlternate();
1379 end = d->m_data->quoteEndAlternate();
1380 }
1381
1382 return start.viewData(table: single_character_data) % str % end.viewData(table: single_character_data);
1383}
1384
1385/*!
1386 \since 4.8
1387
1388 Returns a string that represents a join of a given \a list of strings with
1389 a separator defined by the locale.
1390*/
1391QString QLocale::createSeparatedList(const QStringList &list) const
1392{
1393 // May be empty if list is empty or sole entry is empty.
1394#ifndef QT_NO_SYSTEMLOCALE
1395 if (d->m_data == &systemLocaleData) {
1396 QVariant res =
1397 systemLocale()->query(type: QSystemLocale::ListToSeparatedString, in: QVariant::fromValue(value: list));
1398
1399 if (!res.isNull())
1400 return res.toString();
1401 }
1402#endif
1403
1404 const qsizetype size = list.size();
1405 if (size < 1)
1406 return QString();
1407
1408 if (size == 1)
1409 return list.at(i: 0);
1410
1411 if (size == 2)
1412 return d->m_data->pairListPattern().getData(
1413 table: list_pattern_part_data).arg(args: list.at(i: 0), args: list.at(i: 1));
1414
1415 QStringView formatStart = d->m_data->startListPattern().viewData(table: list_pattern_part_data);
1416 QStringView formatMid = d->m_data->midListPattern().viewData(table: list_pattern_part_data);
1417 QStringView formatEnd = d->m_data->endListPattern().viewData(table: list_pattern_part_data);
1418 QString result = formatStart.arg(args: list.at(i: 0), args: list.at(i: 1));
1419 for (qsizetype i = 2; i < size - 1; ++i)
1420 result = formatMid.arg(args&: result, args: list.at(i));
1421 result = formatEnd.arg(args&: result, args: list.at(i: size - 1));
1422 return result;
1423}
1424
1425/*!
1426 \nonreentrant
1427
1428 Sets the global default locale to \a locale.
1429
1430 This locale is used when a QLocale object is constructed with no
1431 arguments. If this function is not called, the system's locale is used.
1432
1433 \warning In a multithreaded application, the default locale should be set at
1434 application startup, before any non-GUI threads are created.
1435
1436 \sa system(), c()
1437*/
1438
1439void QLocale::setDefault(const QLocale &locale)
1440{
1441 default_data = locale.d->m_data;
1442
1443 if (defaultLocalePrivate.isDestroyed())
1444 return; // avoid crash on exit
1445 if (!defaultLocalePrivate.exists()) {
1446 // Force it to exist; see QTBUG-83016
1447 QLocale ignoreme;
1448 Q_ASSERT(defaultLocalePrivate.exists());
1449 }
1450
1451 // update the cached private
1452 *defaultLocalePrivate = locale.d;
1453 QLocalePrivate::s_generation.fetchAndAddRelaxed(valueToAdd: 1);
1454}
1455
1456/*!
1457 Returns the language of this locale.
1458
1459 \sa script(), territory(), languageToString(), bcp47Name()
1460*/
1461QLocale::Language QLocale::language() const
1462{
1463 return Language(d->languageId());
1464}
1465
1466/*!
1467 \since 4.8
1468
1469 Returns the script of this locale.
1470
1471 \sa language(), territory(), languageToString(), scriptToString(), bcp47Name()
1472*/
1473QLocale::Script QLocale::script() const
1474{
1475 return Script(d->m_data->m_script_id);
1476}
1477
1478/*!
1479 \since 6.2
1480
1481 Returns the territory of this locale.
1482
1483 \sa language(), script(), territoryToString(), bcp47Name()
1484*/
1485QLocale::Territory QLocale::territory() const
1486{
1487 return Territory(d->territoryId());
1488}
1489
1490#if QT_DEPRECATED_SINCE(6, 6)
1491/*!
1492 \deprecated [6.6] Use \l territory() instead.
1493
1494 Returns the territory of this locale.
1495
1496 \sa language(), script(), territoryToString(), bcp47Name()
1497*/
1498QLocale::Country QLocale::country() const
1499{
1500 return territory();
1501}
1502#endif
1503
1504/*!
1505 \since 6.7
1506 \enum QLocale::TagSeparator
1507
1508 Indicate how to combine the parts that make up a locale identifier.
1509
1510 A locale identifier may be made up of several tags, indicating language,
1511 script and territory (plus, potentially, other details), joined together to
1512 form the identifier. Various standards and conventional forms use either a
1513 dash (the Unicode HYPHEN-MINUS, U+002D) or an underscore (LOW LINE, U+005F).
1514 Different clients of QLocale may thus need one or the other.
1515
1516 \value Dash Use \c{'-'}, the dash or hyphen character.
1517 \value Underscore Use \c{'_'}, the underscore character.
1518
1519 \note Although dash and underscore are the only separators used in public
1520 standards (as at 2023), it is possible to cast any \l
1521 {https://en.cppreference.com/w/cpp/language/ascii} {ASCII} character to this
1522 type if a non-standard ASCII separator is needed. Casting a non-ASCII
1523 character (with decimal value above 127) is not supported: such values are
1524 reserved for future use as enum members if some public standard ever uses a
1525 non-ASCII separator. It is, of course, possible to use QString::replace() to
1526 replace the separator used by a function taking a parameter of this type
1527 with an arbitrary Unicode character or string.
1528*/
1529
1530Q_DECL_COLD_FUNCTION static void badSeparatorWarning(const char *method, char sep)
1531{
1532 qWarning(msg: "QLocale::%s(): Using non-ASCII separator '%c' (%02x) is unsupported",
1533 method, sep, uint(uchar(sep)));
1534}
1535
1536/*!
1537 \brief The short name of this locale.
1538
1539 Returns the language and territory of this locale as a string of the form
1540 "language_territory", where language is a lowercase, two-letter ISO 639
1541 language code, and territory is an uppercase, two- or three-letter ISO 3166
1542 territory code. If the locale has no specified territory, only the language
1543 name is returned. Since Qt 6.7 an optional \a separator parameter can be
1544 supplied to override the default underscore character separating the two
1545 tags.
1546
1547 Even if the QLocale object was constructed with an explicit script, name()
1548 will not contain it for compatibility reasons. Use \l bcp47Name() instead if
1549 you need a full locale name, or construct the string you want to identify a
1550 locale by from those returned by passing its \l language() to \l
1551 languageToCode() and similar for the script and territory.
1552
1553 \sa QLocale(), language(), script(), territory(), bcp47Name(), uiLanguages()
1554*/
1555
1556QString QLocale::name(TagSeparator separator) const
1557{
1558 const char sep = char(separator);
1559 if (uchar(sep) > 0x7f) {
1560 badSeparatorWarning(method: "name", sep);
1561 return {};
1562 }
1563 const auto code = d->languageCode();
1564 QLatin1StringView view{code.data()};
1565
1566 Language l = language();
1567 if (l == C)
1568 return view;
1569
1570 Territory c = territory();
1571 if (c == AnyTerritory)
1572 return view;
1573
1574 return view + QLatin1Char(sep) + d->territoryCode();
1575}
1576
1577template <typename T> static inline
1578T toIntegral_helper(const QLocalePrivate *d, QStringView str, bool *ok)
1579{
1580 constexpr bool isUnsigned = std::is_unsigned_v<T>;
1581 using Int64 = typename std::conditional_t<isUnsigned, quint64, qint64>;
1582
1583 QSimpleParsedNumber<Int64> r{};
1584 if constexpr (isUnsigned)
1585 r = d->m_data->stringToUnsLongLong(str, base: 10, options: d->m_numberOptions);
1586 else
1587 r = d->m_data->stringToLongLong(str, base: 10, options: d->m_numberOptions);
1588
1589 if (ok)
1590 *ok = r.ok();
1591
1592 Int64 val = r.result;
1593 if (T(val) != val) {
1594 if (ok != nullptr)
1595 *ok = false;
1596 val = 0;
1597 }
1598 return T(val);
1599}
1600
1601
1602/*!
1603 \since 4.8
1604
1605 \brief Returns the BCP47 field names joined with dashes.
1606
1607 This combines as many of language, script and territory (and possibly other
1608 BCP47 fields) for this locale as are needed to uniquely specify it. Note
1609 that fields may be omitted if the Unicode consortium's \l {Matching
1610 combinations of language, script and territory}{Likely Subtag Rules} imply
1611 the omitted fields when given those retained. See \l name() for how to
1612 construct a string from individual fields, if some other format is needed.
1613
1614 Unlike uiLanguages(), the value returned by bcp47Name() represents the
1615 locale name of the QLocale data; this need not be the language the
1616 user-interface should be in.
1617
1618 This function tries to conform the locale name to the IETF Best Common
1619 Practice 47, defined by RFC 5646. Since Qt 6.7, it supports an optional \a
1620 separator parameter which can be used to override the BCP47-specified use of
1621 a hyphen to separate the tags. For use in IETF-defined protocols, however,
1622 the default, QLocale::TagSeparator::Dash, should be retained.
1623
1624 \sa name(), language(), territory(), script(), uiLanguages()
1625*/
1626QString QLocale::bcp47Name(TagSeparator separator) const
1627{
1628 const char sep = char(separator);
1629 if (uchar(sep) > 0x7f) {
1630 badSeparatorWarning(method: "bcp47Name", sep);
1631 return {};
1632 }
1633 return QString::fromLatin1(ba: d->bcp47Name(separator: sep));
1634}
1635
1636/*!
1637 Returns the two- or three-letter language code for \a language, as defined
1638 in the ISO 639 standards.
1639
1640 If specified, \a codeTypes selects which set of codes to consider. The first
1641 code from the set that is defined for \a language is returned. Otherwise,
1642 all ISO-639 codes are considered. The codes are considered in the following
1643 order: \c ISO639Part1, \c ISO639Part2B, \c ISO639Part2T, \c ISO639Part3.
1644 \c LegacyLanguageCode is ignored by this function.
1645
1646 \note For \c{QLocale::C} the function returns \c{"C"}.
1647 For \c QLocale::AnyLanguage an empty string is returned.
1648 If the language has no code in any selected code set, an empty string
1649 is returned.
1650
1651 \since 6.3
1652 \sa codeToLanguage(), language(), name(), bcp47Name(), territoryToCode(), scriptToCode()
1653*/
1654QString QLocale::languageToCode(Language language, LanguageCodeTypes codeTypes)
1655{
1656 const auto code = QLocalePrivate::languageToCode(language, codeTypes);
1657 return QLatin1StringView{code.data()};
1658}
1659
1660/*!
1661 Returns the QLocale::Language enum corresponding to the two- or three-letter
1662 \a languageCode, as defined in the ISO 639 standards.
1663
1664 If specified, \a codeTypes selects which set of codes to consider for
1665 conversion. By default all codes known to Qt are considered. The codes are
1666 matched in the following order: \c ISO639Part1, \c ISO639Part2B,
1667 \c ISO639Part2T, \c ISO639Part3, \c LegacyLanguageCode.
1668
1669 If the code is invalid or not known \c QLocale::AnyLanguage is returned.
1670
1671 \since 6.3
1672 \sa languageToCode(), codeToTerritory(), codeToScript()
1673*/
1674QLocale::Language QLocale::codeToLanguage(QStringView languageCode,
1675 LanguageCodeTypes codeTypes) noexcept
1676{
1677 return QLocalePrivate::codeToLanguage(code: languageCode, codeTypes);
1678}
1679
1680/*!
1681 \since 6.2
1682
1683 Returns the two-letter territory code for \a territory, as defined
1684 in the ISO 3166 standard.
1685
1686 \note For \c{QLocale::AnyTerritory} an empty string is returned.
1687
1688 \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
1689*/
1690QString QLocale::territoryToCode(QLocale::Territory territory)
1691{
1692 return QLocalePrivate::territoryToCode(territory);
1693}
1694
1695/*!
1696 \since 6.2
1697
1698 Returns the QLocale::Territory enum corresponding to the two-letter or
1699 three-digit \a territoryCode, as defined in the ISO 3166 standard.
1700
1701 If the code is invalid or not known QLocale::AnyTerritory is returned.
1702
1703 \sa territoryToCode(), codeToLanguage(), codeToScript()
1704*/
1705QLocale::Territory QLocale::codeToTerritory(QStringView territoryCode) noexcept
1706{
1707 return QLocalePrivate::codeToTerritory(code: territoryCode);
1708}
1709
1710#if QT_DEPRECATED_SINCE(6, 6)
1711/*!
1712 \deprecated [6.6] Use \l territoryToCode() instead.
1713
1714 Returns the two-letter territory code for \a country, as defined
1715 in the ISO 3166 standard.
1716
1717 \note For \c{QLocale::AnyTerritory} or \c{QLocale::AnyCountry} an empty string is returned.
1718
1719 \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
1720*/
1721QString QLocale::countryToCode(Country country)
1722{
1723 return territoryToCode(territory: country);
1724}
1725
1726/*!
1727 Returns the QLocale::Territory enum corresponding to the two-letter or
1728 three-digit \a countryCode, as defined in the ISO 3166 standard.
1729
1730 If the code is invalid or not known QLocale::AnyTerritory is returned.
1731
1732 \deprecated [6.6] Use codeToTerritory(QStringView) instead.
1733 \since 6.1
1734 \sa territoryToCode(), codeToLanguage(), codeToScript()
1735*/
1736QLocale::Country QLocale::codeToCountry(QStringView countryCode) noexcept
1737{
1738 return QLocalePrivate::codeToTerritory(code: countryCode);
1739}
1740#endif
1741
1742/*!
1743 Returns the four-letter script code for \a script, as defined in the
1744 ISO 15924 standard.
1745
1746 \note For \c{QLocale::AnyScript} an empty string is returned.
1747
1748 \since 6.1
1749 \sa script(), name(), bcp47Name(), languageToCode(), territoryToCode()
1750*/
1751QString QLocale::scriptToCode(Script script)
1752{
1753 return QLocalePrivate::scriptToCode(script);
1754}
1755
1756/*!
1757 Returns the QLocale::Script enum corresponding to the four-letter script
1758 \a scriptCode, as defined in the ISO 15924 standard.
1759
1760 If the code is invalid or not known QLocale::AnyScript is returned.
1761
1762 \since 6.1
1763 \sa scriptToCode(), codeToLanguage(), codeToTerritory()
1764*/
1765QLocale::Script QLocale::codeToScript(QStringView scriptCode) noexcept
1766{
1767 return QLocalePrivate::codeToScript(code: scriptCode);
1768}
1769
1770/*!
1771 Returns a QString containing the name of \a language.
1772
1773 \sa territoryToString(), scriptToString(), bcp47Name()
1774*/
1775
1776QString QLocale::languageToString(Language language)
1777{
1778 if (language > LastLanguage)
1779 return "Unknown"_L1;
1780 return QString::fromUtf8(utf8: language_name_list + language_name_index[language]);
1781}
1782
1783/*!
1784 \since 6.2
1785
1786 Returns a QString containing the name of \a territory.
1787
1788 \sa languageToString(), scriptToString(), territory(), bcp47Name()
1789*/
1790QString QLocale::territoryToString(Territory territory)
1791{
1792 if (territory > LastTerritory)
1793 return "Unknown"_L1;
1794 return QString::fromUtf8(utf8: territory_name_list + territory_name_index[territory]);
1795}
1796
1797#if QT_DEPRECATED_SINCE(6, 6)
1798/*!
1799 \deprecated [6.6] Use \l territoryToString() instead.
1800
1801 Returns a QString containing the name of \a country.
1802
1803 \sa languageToString(), scriptToString(), territory(), bcp47Name()
1804*/
1805QString QLocale::countryToString(Country country)
1806{
1807 return territoryToString(territory: country);
1808}
1809#endif
1810
1811/*!
1812 \since 4.8
1813
1814 Returns a QString containing the name of \a script.
1815
1816 \sa languageToString(), territoryToString(), script(), bcp47Name()
1817*/
1818QString QLocale::scriptToString(Script script)
1819{
1820 if (script > LastScript)
1821 return "Unknown"_L1;
1822 return QString::fromUtf8(utf8: script_name_list + script_name_index[script]);
1823}
1824
1825/*!
1826 \fn short QLocale::toShort(const QString &s, bool *ok) const
1827
1828 Returns the short int represented by the localized string \a s.
1829
1830 If the conversion fails the function returns 0.
1831
1832 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1833 to \c false, and success by setting *\a{ok} to \c true.
1834
1835 This function ignores leading and trailing whitespace.
1836
1837 \sa toUShort(), toString()
1838*/
1839
1840/*!
1841 \fn ushort QLocale::toUShort(const QString &s, bool *ok) const
1842
1843 Returns the unsigned short int represented by the localized string \a s.
1844
1845 If the conversion fails the function returns 0.
1846
1847 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1848 to \c false, and success by setting *\a{ok} to \c true.
1849
1850 This function ignores leading and trailing whitespace.
1851
1852 \sa toShort(), toString()
1853*/
1854
1855/*!
1856 \fn int QLocale::toInt(const QString &s, bool *ok) const
1857 Returns the int represented by the localized string \a s.
1858
1859 If the conversion fails the function returns 0.
1860
1861 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1862 to \c false, and success by setting *\a{ok} to \c true.
1863
1864 This function ignores leading and trailing whitespace.
1865
1866 \sa toUInt(), toString()
1867*/
1868
1869/*!
1870 \fn uint QLocale::toUInt(const QString &s, bool *ok) const
1871 Returns the unsigned int represented by the localized string \a s.
1872
1873 If the conversion fails the function returns 0.
1874
1875 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1876 to \c false, and success by setting *\a{ok} to \c true.
1877
1878 This function ignores leading and trailing whitespace.
1879
1880 \sa toInt(), toString()
1881*/
1882
1883/*!
1884 \since 5.13
1885 \fn long QLocale::toLong(const QString &s, bool *ok) const
1886
1887 Returns the long int represented by the localized string \a s.
1888
1889 If the conversion fails the function returns 0.
1890
1891 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1892 to \c false, and success by setting *\a{ok} to \c true.
1893
1894 This function ignores leading and trailing whitespace.
1895
1896 \sa toInt(), toULong(), toDouble(), toString()
1897*/
1898
1899/*!
1900 \since 5.13
1901 \fn ulong QLocale::toULong(const QString &s, bool *ok) const
1902
1903 Returns the unsigned long int represented by the localized
1904 string \a s.
1905
1906 If the conversion fails the function returns 0.
1907
1908 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1909 to \c false, and success by setting *\a{ok} to \c true.
1910
1911 This function ignores leading and trailing whitespace.
1912
1913 \sa toLong(), toInt(), toDouble(), toString()
1914*/
1915
1916/*!
1917 \fn qlonglong QLocale::toLongLong(const QString &s, bool *ok) const
1918 Returns the long long int represented by the localized string \a s.
1919
1920 If the conversion fails the function returns 0.
1921
1922 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1923 to \c false, and success by setting *\a{ok} to \c true.
1924
1925 This function ignores leading and trailing whitespace.
1926
1927 \sa toInt(), toULongLong(), toDouble(), toString()
1928*/
1929
1930/*!
1931 \fn qulonglong QLocale::toULongLong(const QString &s, bool *ok) const
1932
1933 Returns the unsigned long long int represented by the localized
1934 string \a s.
1935
1936 If the conversion fails the function returns 0.
1937
1938 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1939 to \c false, and success by setting *\a{ok} to \c true.
1940
1941 This function ignores leading and trailing whitespace.
1942
1943 \sa toLongLong(), toInt(), toDouble(), toString()
1944*/
1945
1946/*!
1947 \fn float QLocale::toFloat(const QString &s, bool *ok) const
1948
1949 Returns the float represented by the localized string \a s.
1950
1951 Returns an infinity if the conversion overflows or 0.0 if the
1952 conversion fails for any other reason (e.g. underflow).
1953
1954 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1955 to \c false, and success by setting *\a{ok} to \c true.
1956
1957 This function ignores leading and trailing whitespace.
1958
1959 \sa toDouble(), toInt(), toString()
1960*/
1961
1962/*!
1963 \fn double QLocale::toDouble(const QString &s, bool *ok) const
1964 Returns the double represented by the localized string \a s.
1965
1966 Returns an infinity if the conversion overflows or 0.0 if the
1967 conversion fails for any other reason (e.g. underflow).
1968
1969 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1970 to \c false, and success by setting *\a{ok} to \c true.
1971
1972 \snippet code/src_corelib_text_qlocale.cpp 3
1973
1974 Notice that the last conversion returns 1234.0, because '.' is the
1975 thousands group separator in the German locale.
1976
1977 This function ignores leading and trailing whitespace.
1978
1979 \sa toFloat(), toInt(), toString()
1980*/
1981
1982/*!
1983 Returns the short int represented by the localized string \a s.
1984
1985 If the conversion fails, the function returns 0.
1986
1987 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
1988 to \c false, and success by setting *\a{ok} to \c true.
1989
1990 This function ignores leading and trailing whitespace.
1991
1992 \sa toUShort(), toString()
1993
1994 \since 5.10
1995*/
1996
1997short QLocale::toShort(QStringView s, bool *ok) const
1998{
1999 return toIntegral_helper<short>(d, str: s, ok);
2000}
2001
2002/*!
2003 Returns the unsigned short int represented by the localized string \a s.
2004
2005 If the conversion fails, the function returns 0.
2006
2007 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2008 to \c false, and success by setting *\a{ok} to \c true.
2009
2010 This function ignores leading and trailing whitespace.
2011
2012 \sa toShort(), toString()
2013
2014 \since 5.10
2015*/
2016
2017ushort QLocale::toUShort(QStringView s, bool *ok) const
2018{
2019 return toIntegral_helper<ushort>(d, str: s, ok);
2020}
2021
2022/*!
2023 Returns the int represented by the localized string \a s.
2024
2025 If the conversion fails, the function returns 0.
2026
2027 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2028 to \c false, and success by setting *\a{ok} to \c true.
2029
2030 This function ignores leading and trailing whitespace.
2031
2032 \sa toUInt(), toString()
2033
2034 \since 5.10
2035*/
2036
2037int QLocale::toInt(QStringView s, bool *ok) const
2038{
2039 return toIntegral_helper<int>(d, str: s, ok);
2040}
2041
2042/*!
2043 Returns the unsigned int represented by the localized string \a s.
2044
2045 If the conversion fails, the function returns 0.
2046
2047 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2048 to \c false, and success by setting *\a{ok} to \c true.
2049
2050 This function ignores leading and trailing whitespace.
2051
2052 \sa toInt(), toString()
2053
2054 \since 5.10
2055*/
2056
2057uint QLocale::toUInt(QStringView s, bool *ok) const
2058{
2059 return toIntegral_helper<uint>(d, str: s, ok);
2060}
2061
2062/*!
2063 \since 5.13
2064 Returns the long int represented by the localized string \a s.
2065
2066 If the conversion fails the function returns 0.
2067
2068 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2069 to \c false, and success by setting *\a{ok} to \c true.
2070
2071 This function ignores leading and trailing whitespace.
2072
2073 \sa toInt(), toULong(), toDouble(), toString()
2074*/
2075
2076long QLocale::toLong(QStringView s, bool *ok) const
2077{
2078 return toIntegral_helper<long>(d, str: s, ok);
2079}
2080
2081/*!
2082 \since 5.13
2083 Returns the unsigned long int represented by the localized
2084 string \a s.
2085
2086 If the conversion fails the function returns 0.
2087
2088 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2089 to \c false, and success by setting *\a{ok} to \c true.
2090
2091 This function ignores leading and trailing whitespace.
2092
2093 \sa toLong(), toInt(), toDouble(), toString()
2094*/
2095
2096ulong QLocale::toULong(QStringView s, bool *ok) const
2097{
2098 return toIntegral_helper<ulong>(d, str: s, ok);
2099}
2100
2101/*!
2102 Returns the long long int represented by the localized string \a s.
2103
2104 If the conversion fails, the function returns 0.
2105
2106 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2107 to \c false, and success by setting *\a{ok} to \c true.
2108
2109 This function ignores leading and trailing whitespace.
2110
2111 \sa toInt(), toULongLong(), toDouble(), toString()
2112
2113 \since 5.10
2114*/
2115
2116
2117qlonglong QLocale::toLongLong(QStringView s, bool *ok) const
2118{
2119 return toIntegral_helper<qlonglong>(d, str: s, ok);
2120}
2121
2122/*!
2123 Returns the unsigned long long int represented by the localized
2124 string \a s.
2125
2126 If the conversion fails, the function returns 0.
2127
2128 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2129 to \c false, and success by setting *\a{ok} to \c true.
2130
2131 This function ignores leading and trailing whitespace.
2132
2133 \sa toLongLong(), toInt(), toDouble(), toString()
2134
2135 \since 5.10
2136*/
2137
2138qulonglong QLocale::toULongLong(QStringView s, bool *ok) const
2139{
2140 return toIntegral_helper<qulonglong>(d, str: s, ok);
2141}
2142
2143/*!
2144 Returns the float represented by the localized string \a s.
2145
2146 Returns an infinity if the conversion overflows or 0.0 if the
2147 conversion fails for any other reason (e.g. underflow).
2148
2149 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2150 to \c false, and success by setting *\a{ok} to \c true.
2151
2152 This function ignores leading and trailing whitespace.
2153
2154 \sa toDouble(), toInt(), toString()
2155
2156 \since 5.10
2157*/
2158
2159float QLocale::toFloat(QStringView s, bool *ok) const
2160{
2161 return QLocaleData::convertDoubleToFloat(d: toDouble(s, ok), ok);
2162}
2163
2164/*!
2165 Returns the double represented by the localized string \a s.
2166
2167 Returns an infinity if the conversion overflows or 0.0 if the
2168 conversion fails for any other reason (e.g. underflow).
2169
2170 If \a ok is not \nullptr, failure is reported by setting *\a{ok}
2171 to \c false, and success by setting *\a{ok} to \c true.
2172
2173 \snippet code/src_corelib_text_qlocale.cpp 3-qstringview
2174
2175 Notice that the last conversion returns 1234.0, because '.' is the
2176 thousands group separator in the German locale.
2177
2178 This function ignores leading and trailing whitespace.
2179
2180 \sa toFloat(), toInt(), toString()
2181
2182 \since 5.10
2183*/
2184
2185double QLocale::toDouble(QStringView s, bool *ok) const
2186{
2187 return d->m_data->stringToDouble(str: s, ok, options: d->m_numberOptions);
2188}
2189
2190/*!
2191 Returns a localized string representation of \a i.
2192
2193 \sa toLongLong(), numberOptions(), zeroDigit(), positiveSign()
2194*/
2195
2196QString QLocale::toString(qlonglong i) const
2197{
2198 int flags = (d->m_numberOptions & OmitGroupSeparator
2199 ? 0 : QLocaleData::GroupDigits);
2200
2201 return d->m_data->longLongToString(l: i, precision: -1, base: 10, width: -1, flags);
2202}
2203
2204/*!
2205 \overload
2206
2207 \sa toULongLong(), numberOptions(), zeroDigit(), positiveSign()
2208*/
2209
2210QString QLocale::toString(qulonglong i) const
2211{
2212 int flags = (d->m_numberOptions & OmitGroupSeparator
2213 ? 0 : QLocaleData::GroupDigits);
2214
2215 return d->m_data->unsLongLongToString(l: i, precision: -1, base: 10, width: -1, flags);
2216}
2217// ### Incorrect way of calculating the width, will be fixed soon.
2218static qsizetype stringWidth(QStringView text)
2219{
2220 QStringIterator counter(text);
2221 qsizetype count = 0;
2222 while (counter.hasNext()) {
2223 ++count;
2224 [[maybe_unused]] auto ch = counter.next();
2225 }
2226 return count;
2227}
2228
2229static unsigned calculateFlags(int fieldWidth, char32_t fillChar,
2230 const QLocale &locale)
2231{
2232 unsigned flags = QLocaleData::NoFlags;
2233 if (!(locale.numberOptions() & QLocale::OmitGroupSeparator))
2234 flags |= QLocaleData::GroupDigits;
2235 if (fieldWidth < 0)
2236 flags |= QLocaleData::LeftAdjusted;
2237 else if (fillChar == U'0')
2238 flags |= QLocaleData::ZeroPadded;
2239
2240 return flags;
2241}
2242
2243static QString calculateFiller(qsizetype padding,
2244 char32_t fillChar,
2245 [[maybe_unused]] qsizetype fieldWidth,
2246 const QLocaleData *localeData)
2247{
2248 QString filler;
2249 if (fillChar == U'0') {
2250 Q_ASSERT(fieldWidth < 0);
2251 filler = localeData->zeroDigit();
2252 } else {
2253 filler = QString(QChar::fromUcs4(c: fillChar));
2254 }
2255 // ### size is not width
2256 if (padding > 1)
2257 filler = filler.repeated(times: padding);
2258 return filler;
2259}
2260
2261/*!
2262 \fn QString QLocale::toString(short number, int fieldWidth, char32_t fillChar) const
2263 \fn QString QLocale::toString(int number, int fieldWidth, char32_t fillChar) const
2264 \fn QString QLocale::toString(long number, int fieldWidth, char32_t fillChar) const
2265 \include qlocale.cpp tostring-with-padding
2266 \include qlocale.cpp tostring-signed-padding
2267*/
2268/*!
2269//! [tostring-with-padding]
2270 Returns a string representation of the given \a number.
2271
2272 The string's length shall be at least the absolute value of \a fieldWidth,
2273 using \a fillChar as padding if the \a number has fewer digits. If
2274 \a fillChar is \c{'0'} the zero digit of this locale is used as padding.
2275 If \a fieldWidth is negative the string starts with its representation
2276 of \a number and, if shorter, is padded to length \c{-fieldWidth} with
2277 the given \a fillChar. For positive fieldWidth, the padding appears before
2278 the representation of \a number.
2279//! [tostring-with-padding]
2280//! [tostring-signed-padding]
2281 When the \a number is negative and \a fieldWidth is positive, if
2282 \a fillChar is a \c{'0'} the padding is inserted between this locale's
2283 minus sign and the start of the number's digits.
2284 \overload toString()
2285//! [tostring-signed-padding]
2286 */
2287QString QLocale::toString(qlonglong number, int fieldWidth, char32_t fillChar) const
2288{
2289 int absFieldWidth = qAbs(t: fieldWidth);
2290 int width = (fillChar == U'0') ? absFieldWidth : -1;
2291 unsigned flags = calculateFlags(fieldWidth, fillChar, locale: *this);
2292
2293 QString result = d->m_data->longLongToString(l: number, precision: -1, base: 10, width, flags);
2294 qsizetype padding = absFieldWidth - stringWidth(text: result);
2295
2296 if (padding > 0) {
2297 QString filler = calculateFiller(padding, fillChar, fieldWidth, localeData: d->m_data);
2298 if (fieldWidth < 0)
2299 result.append(s: filler);
2300 else
2301 result.prepend(s: filler);
2302 }
2303 return result;
2304}
2305
2306/*!
2307 \fn QString QLocale::toString(ushort number, int fieldWidth, char32_t fillChar) const
2308 \fn QString QLocale::toString(uint number, int fieldWidth, char32_t fillChar) const
2309 \fn QString QLocale::toString(ulong number, int fieldWidth, char32_t fillChar) const
2310 \include qlocale.cpp tostring-with-padding
2311 \overload toString()
2312 */
2313/*!
2314 \include qlocale.cpp tostring-with-padding
2315 \overload toString()
2316 */
2317QString QLocale::toString(qulonglong number, int fieldWidth, char32_t fillChar) const
2318{
2319 int absFieldWidth = qAbs(t: fieldWidth);
2320 int width = (fillChar == U'0') ? absFieldWidth : -1;
2321 unsigned flags = calculateFlags(fieldWidth, fillChar, locale: *this);
2322
2323 QString result = d->m_data->unsLongLongToString(l: number, precision: -1, base: 10, width, flags);
2324 qsizetype padding = absFieldWidth - stringWidth(text: result);
2325
2326 if (padding > 0) {
2327 QString filler = calculateFiller(padding, fillChar, fieldWidth, localeData: d->m_data);
2328 if (fieldWidth < 0)
2329 result.append(s: filler);
2330 else
2331 result.prepend(s: filler);
2332 }
2333 return result;
2334}
2335
2336/*!
2337 Returns a localized string representation of the given \a date in the
2338 specified \a format.
2339 If \a format is an empty string, an empty string is returned.
2340
2341 \sa QDate::toString()
2342*/
2343
2344QString QLocale::toString(QDate date, const QString &format) const
2345{
2346 return toString(date, format: qToStringViewIgnoringNull(s: format));
2347}
2348
2349/*!
2350 Returns a localized string representation of the given \a time according
2351 to the specified \a format.
2352 If \a format is an empty string, an empty string is returned.
2353
2354 \sa QTime::toString()
2355*/
2356
2357QString QLocale::toString(QTime time, const QString &format) const
2358{
2359 return toString(time, format: qToStringViewIgnoringNull(s: format));
2360}
2361
2362/*!
2363 \since 4.4
2364 \fn QString QLocale::toString(const QDateTime &dateTime, const QString &format) const
2365
2366 Returns a localized string representation of the given \a dateTime according
2367 to the specified \a format.
2368 If \a format is an empty string, an empty string is returned.
2369
2370 \sa QDateTime::toString(), QDate::toString(), QTime::toString()
2371*/
2372
2373/*!
2374 \since 5.14
2375
2376 Returns a localized string representation of the given \a date in the
2377 specified \a format, optionally for a specified calendar \a cal.
2378 If \a format is an empty string, an empty string is returned.
2379
2380 \sa QDate::toString()
2381*/
2382QString QLocale::toString(QDate date, QStringView format, QCalendar cal) const
2383{
2384 return cal.dateTimeToString(format, datetime: QDateTime(), dateOnly: date, timeOnly: QTime(), locale: *this);
2385}
2386
2387/*!
2388 \since 5.10
2389 \overload
2390*/
2391QString QLocale::toString(QDate date, QStringView format) const
2392{
2393 return QCalendar().dateTimeToString(format, datetime: QDateTime(), dateOnly: date, timeOnly: QTime(), locale: *this);
2394}
2395
2396/*!
2397 \since 5.14
2398
2399 Returns a localized string representation of the given \a date according
2400 to the specified \a format (see dateFormat()), optionally for a specified
2401 calendar \a cal.
2402
2403 \note Some locales may use formats that limit the range of years they can
2404 represent.
2405*/
2406QString QLocale::toString(QDate date, FormatType format, QCalendar cal) const
2407{
2408 if (!date.isValid())
2409 return QString();
2410
2411#ifndef QT_NO_SYSTEMLOCALE
2412 if (cal.isGregorian() && d->m_data == &systemLocaleData) {
2413 QVariant res = systemLocale()->query(type: format == LongFormat
2414 ? QSystemLocale::DateToStringLong
2415 : QSystemLocale::DateToStringShort,
2416 in: date);
2417 if (!res.isNull())
2418 return res.toString();
2419 }
2420#endif
2421
2422 QString format_str = dateFormat(format);
2423 return toString(date, format: format_str, cal);
2424}
2425
2426/*!
2427 \since 4.5
2428 \overload
2429*/
2430QString QLocale::toString(QDate date, FormatType format) const
2431{
2432 if (!date.isValid())
2433 return QString();
2434
2435#ifndef QT_NO_SYSTEMLOCALE
2436 if (d->m_data == &systemLocaleData) {
2437 QVariant res = systemLocale()->query(type: format == LongFormat
2438 ? QSystemLocale::DateToStringLong
2439 : QSystemLocale::DateToStringShort,
2440 in: date);
2441 if (!res.isNull())
2442 return res.toString();
2443 }
2444#endif
2445
2446 QString format_str = dateFormat(format);
2447 return toString(date, format: format_str);
2448}
2449
2450static bool timeFormatContainsAP(QStringView format)
2451{
2452 qsizetype i = 0;
2453 while (i < format.size()) {
2454 if (format.at(n: i).unicode() == '\'') {
2455 qt_readEscapedFormatString(format, idx: &i);
2456 continue;
2457 }
2458
2459 if (format.at(n: i).toLower().unicode() == 'a')
2460 return true;
2461
2462 ++i;
2463 }
2464 return false;
2465}
2466
2467/*!
2468 \since 4.5
2469
2470 Returns a localized string representation of the given \a time according
2471 to the specified \a format.
2472 If \a format is an empty string, an empty string is returned.
2473
2474 \sa QTime::toString()
2475*/
2476QString QLocale::toString(QTime time, QStringView format) const
2477{
2478 return QCalendar().dateTimeToString(format, datetime: QDateTime(), dateOnly: QDate(), timeOnly: time, locale: *this);
2479}
2480
2481/*!
2482 \since 5.14
2483
2484 Returns a localized string representation of the given \a dateTime according
2485 to the specified \a format, optionally for a specified calendar \a cal.
2486 If \a format is an empty string, an empty string is returned.
2487
2488 \sa QDateTime::toString(), QDate::toString(), QTime::toString()
2489*/
2490QString QLocale::toString(const QDateTime &dateTime, QStringView format, QCalendar cal) const
2491{
2492 return cal.dateTimeToString(format, datetime: dateTime, dateOnly: QDate(), timeOnly: QTime(), locale: *this);
2493}
2494
2495/*!
2496 \since 5.10
2497 \overload
2498*/
2499QString QLocale::toString(const QDateTime &dateTime, QStringView format) const
2500{
2501 return QCalendar().dateTimeToString(format, datetime: dateTime, dateOnly: QDate(), timeOnly: QTime(), locale: *this);
2502}
2503
2504/*!
2505 \since 5.14
2506
2507 Returns a localized string representation of the given \a dateTime according
2508 to the specified \a format (see dateTimeFormat()), optionally for a
2509 specified calendar \a cal.
2510
2511 \note Some locales may use formats that limit the range of years they can
2512 represent.
2513*/
2514QString QLocale::toString(const QDateTime &dateTime, FormatType format, QCalendar cal) const
2515{
2516 if (!dateTime.isValid())
2517 return QString();
2518
2519#ifndef QT_NO_SYSTEMLOCALE
2520 if (cal.isGregorian() && d->m_data == &systemLocaleData) {
2521 QVariant res = systemLocale()->query(type: format == LongFormat
2522 ? QSystemLocale::DateTimeToStringLong
2523 : QSystemLocale::DateTimeToStringShort,
2524 in: dateTime);
2525 if (!res.isNull())
2526 return res.toString();
2527 }
2528#endif
2529
2530 const QString format_str = dateTimeFormat(format);
2531 return toString(dateTime, format: format_str, cal);
2532}
2533
2534/*!
2535 \since 4.4
2536 \overload
2537*/
2538QString QLocale::toString(const QDateTime &dateTime, FormatType format) const
2539{
2540 if (!dateTime.isValid())
2541 return QString();
2542
2543#ifndef QT_NO_SYSTEMLOCALE
2544 if (d->m_data == &systemLocaleData) {
2545 QVariant res = systemLocale()->query(type: format == LongFormat
2546 ? QSystemLocale::DateTimeToStringLong
2547 : QSystemLocale::DateTimeToStringShort,
2548 in: dateTime);
2549 if (!res.isNull())
2550 return res.toString();
2551 }
2552#endif
2553
2554 const QString format_str = dateTimeFormat(format);
2555 return toString(dateTime, format: format_str);
2556}
2557
2558
2559/*!
2560 Returns a localized string representation of the given \a time in the
2561 specified \a format (see timeFormat()).
2562*/
2563
2564QString QLocale::toString(QTime time, FormatType format) const
2565{
2566 if (!time.isValid())
2567 return QString();
2568
2569#ifndef QT_NO_SYSTEMLOCALE
2570 if (d->m_data == &systemLocaleData) {
2571 QVariant res = systemLocale()->query(type: format == LongFormat
2572 ? QSystemLocale::TimeToStringLong
2573 : QSystemLocale::TimeToStringShort,
2574 in: time);
2575 if (!res.isNull())
2576 return res.toString();
2577 }
2578#endif
2579
2580 QString format_str = timeFormat(format);
2581 return toString(time, format: format_str);
2582}
2583
2584/*!
2585 \since 4.1
2586
2587 Returns the date format used for the current locale.
2588
2589 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2590 For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy},
2591 ShortFormat is \c{M/d/yy}.
2592
2593 \sa QDate::toString(), QDate::fromString()
2594*/
2595
2596QString QLocale::dateFormat(FormatType format) const
2597{
2598#ifndef QT_NO_SYSTEMLOCALE
2599 if (d->m_data == &systemLocaleData) {
2600 QVariant res = systemLocale()->query(type: format == LongFormat
2601 ? QSystemLocale::DateFormatLong
2602 : QSystemLocale::DateFormatShort,
2603 in: QVariant());
2604 if (!res.isNull())
2605 return res.toString();
2606 }
2607#endif
2608
2609 return (format == LongFormat
2610 ? d->m_data->longDateFormat()
2611 : d->m_data->shortDateFormat()
2612 ).getData(table: date_format_data);
2613}
2614
2615/*!
2616 \since 4.1
2617
2618 Returns the time format used for the current locale.
2619
2620 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2621 For example, LongFormat for the \c{en_US} locale is \c{h:mm:ss AP t},
2622 ShortFormat is \c{h:mm AP}.
2623
2624 \sa QTime::toString(), QTime::fromString()
2625*/
2626
2627QString QLocale::timeFormat(FormatType format) const
2628{
2629#ifndef QT_NO_SYSTEMLOCALE
2630 if (d->m_data == &systemLocaleData) {
2631 QVariant res = systemLocale()->query(type: format == LongFormat
2632 ? QSystemLocale::TimeFormatLong
2633 : QSystemLocale::TimeFormatShort,
2634 in: QVariant());
2635 if (!res.isNull())
2636 return res.toString();
2637 }
2638#endif
2639
2640 return (format == LongFormat
2641 ? d->m_data->longTimeFormat()
2642 : d->m_data->shortTimeFormat()
2643 ).getData(table: time_format_data);
2644}
2645
2646/*!
2647 \since 4.4
2648
2649 Returns the date time format used for the current locale.
2650
2651 If \a format is LongFormat, the format will be elaborate, otherwise it will be short.
2652 For example, LongFormat for the \c{en_US} locale is \c{dddd, MMMM d, yyyy h:mm:ss AP t},
2653 ShortFormat is \c{M/d/yy h:mm AP}.
2654
2655 \sa QDateTime::toString(), QDateTime::fromString()
2656*/
2657
2658QString QLocale::dateTimeFormat(FormatType format) const
2659{
2660#ifndef QT_NO_SYSTEMLOCALE
2661 if (d->m_data == &systemLocaleData) {
2662 QVariant res = systemLocale()->query(type: format == LongFormat
2663 ? QSystemLocale::DateTimeFormatLong
2664 : QSystemLocale::DateTimeFormatShort,
2665 in: QVariant());
2666 if (!res.isNull()) {
2667 return res.toString();
2668 }
2669 }
2670#endif
2671 return dateFormat(format) + u' ' + timeFormat(format);
2672}
2673
2674#if QT_CONFIG(datestring)
2675/*!
2676 \since 4.4
2677
2678 Reads \a string as a time in a locale-specific \a format.
2679
2680 Parses \a string and returns the time it represents. The format of the time
2681 string is chosen according to the \a format parameter (see timeFormat()).
2682
2683 \note Any am/pm indicators used must match \l amText() or \l pmText(),
2684 ignoring case.
2685
2686 If the time could not be parsed, returns an invalid time.
2687
2688 \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
2689*/
2690QTime QLocale::toTime(const QString &string, FormatType format) const
2691{
2692 return toTime(string, format: timeFormat(format));
2693}
2694
2695/*!
2696 \since 4.4
2697
2698 Reads \a string as a date in a locale-specific \a format.
2699
2700 Parses \a string and returns the date it represents. The format of the date
2701 string is chosen according to the \a format parameter (see dateFormat()).
2702
2703//! [base-year-for-short]
2704 Some locales use, particularly for ShortFormat, only the last two digits of
2705 the year. In such a case, the 100 years starting at \a baseYear are the
2706 candidates first considered. Prior to 6.7 there was no \a baseYear parameter
2707 and 1900 was always used. This is the default for \a baseYear, selecting a
2708 year from then to 1999. In some cases, other fields may lead to the next or
2709 previous century being selected, to get a result consistent with all fields
2710 given. See \l QDate::fromString() for details.
2711//! [base-year-for-short]
2712
2713 \note Month and day names, where used, must be given in the locale's
2714 language.
2715
2716 If the date could not be parsed, returns an invalid date.
2717
2718 \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
2719*/
2720QDate QLocale::toDate(const QString &string, FormatType format, int baseYear) const
2721{
2722 return toDate(string, format: dateFormat(format), baseYear);
2723}
2724
2725/*!
2726 \since 5.14
2727 \overload
2728*/
2729QDate QLocale::toDate(const QString &string, FormatType format, QCalendar cal, int baseYear) const
2730{
2731 return toDate(string, format: dateFormat(format), cal, baseYear);
2732}
2733
2734/*!
2735 \since 4.4
2736
2737 Reads \a string as a date-time in a locale-specific \a format.
2738
2739 Parses \a string and returns the date-time it represents. The format of the
2740 date string is chosen according to the \a format parameter (see
2741 dateFormat()).
2742
2743 \include qlocale.cpp base-year-for-short
2744
2745 \note Month and day names, where used, must be given in the locale's
2746 language. Any am/pm indicators used must match \l amText() or \l pmText(),
2747 ignoring case.
2748
2749 If the string could not be parsed, returns an invalid QDateTime.
2750
2751 \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
2752*/
2753QDateTime QLocale::toDateTime(const QString &string, FormatType format, int baseYear) const
2754{
2755 return toDateTime(string, format: dateTimeFormat(format), baseYear);
2756}
2757
2758/*!
2759 \since 5.14
2760 \overload
2761*/
2762QDateTime QLocale::toDateTime(const QString &string, FormatType format, QCalendar cal,
2763 int baseYear) const
2764{
2765 return toDateTime(string, format: dateTimeFormat(format), cal, baseYear);
2766}
2767
2768/*!
2769 \since 4.4
2770
2771 Reads \a string as a time in the given \a format.
2772
2773 Parses \a string and returns the time it represents. See QTime::fromString()
2774 for the interpretation of \a format.
2775
2776 \note Any am/pm indicators used must match \l amText() or \l pmText(),
2777 ignoring case.
2778
2779 If the time could not be parsed, returns an invalid time.
2780
2781 \sa timeFormat(), toDate(), toDateTime(), QTime::fromString()
2782*/
2783QTime QLocale::toTime(const QString &string, const QString &format) const
2784{
2785 QTime time;
2786#if QT_CONFIG(datetimeparser)
2787 QDateTimeParser dt(QMetaType::QTime, QDateTimeParser::FromString, QCalendar());
2788 dt.setDefaultLocale(*this);
2789 if (dt.parseFormat(format))
2790 dt.fromString(text: string, date: nullptr, time: &time);
2791#else
2792 Q_UNUSED(string);
2793 Q_UNUSED(format);
2794#endif
2795 return time;
2796}
2797
2798/*!
2799 \since 4.4
2800
2801 Reads \a string as a date in the given \a format.
2802
2803 Parses \a string and returns the date it represents. See QDate::fromString()
2804 for the interpretation of \a format.
2805
2806//! [base-year-for-two-digit]
2807 When \a format only specifies the last two digits of a year, the 100 years
2808 starting at \a baseYear are the candidates first considered. Prior to 6.7
2809 there was no \a baseYear parameter and 1900 was always used. This is the
2810 default for \a baseYear, selecting a year from then to 1999. In some cases,
2811 other fields may lead to the next or previous century being selected, to get
2812 a result consistent with all fields given. See \l QDate::fromString() for
2813 details.
2814//! [base-year-for-two-digit]
2815
2816 \note Month and day names, where used, must be given in the locale's
2817 language.
2818
2819 If the date could not be parsed, returns an invalid date.
2820
2821 \sa dateFormat(), toTime(), toDateTime(), QDate::fromString()
2822*/
2823QDate QLocale::toDate(const QString &string, const QString &format, int baseYear) const
2824{
2825 return toDate(string, format, cal: QCalendar(), baseYear);
2826}
2827
2828/*!
2829 \since 5.14
2830 \overload
2831*/
2832QDate QLocale::toDate(const QString &string, const QString &format, QCalendar cal, int baseYear) const
2833{
2834 QDate date;
2835#if QT_CONFIG(datetimeparser)
2836 QDateTimeParser dt(QMetaType::QDate, QDateTimeParser::FromString, cal);
2837 dt.setDefaultLocale(*this);
2838 if (dt.parseFormat(format))
2839 dt.fromString(text: string, date: &date, time: nullptr, baseYear);
2840#else
2841 Q_UNUSED(string);
2842 Q_UNUSED(format);
2843 Q_UNUSED(baseYear);
2844 Q_UNUSED(cal);
2845#endif
2846 return date;
2847}
2848
2849/*!
2850 \since 4.4
2851
2852 Reads \a string as a date-time in the given \a format.
2853
2854 Parses \a string and returns the date-time it represents. See
2855 QDateTime::fromString() for the interpretation of \a format.
2856
2857 \include qlocale.cpp base-year-for-two-digit
2858
2859 \note Month and day names, where used, must be given in the locale's
2860 language. Any am/pm indicators used must match \l amText() or \l pmText(),
2861 ignoring case.
2862
2863 If the string could not be parsed, returns an invalid QDateTime. If the
2864 string can be parsed and represents an invalid date-time (e.g. in a gap
2865 skipped by a time-zone transition), an invalid QDateTime is returned, whose
2866 toMSecsSinceEpoch() represents a near-by date-time that is valid. Passing
2867 that to fromMSecsSinceEpoch() will produce a valid date-time that isn't
2868 faithfully represented by the string parsed.
2869
2870 \sa dateTimeFormat(), toTime(), toDate(), QDateTime::fromString()
2871*/
2872QDateTime QLocale::toDateTime(const QString &string, const QString &format, int baseYear) const
2873{
2874 return toDateTime(string, format, cal: QCalendar(), baseYear);
2875}
2876
2877/*!
2878 \since 5.14
2879 \overload
2880*/
2881QDateTime QLocale::toDateTime(const QString &string, const QString &format, QCalendar cal,
2882 int baseYear) const
2883{
2884#if QT_CONFIG(datetimeparser)
2885 QDateTime datetime;
2886
2887 QDateTimeParser dt(QMetaType::QDateTime, QDateTimeParser::FromString, cal);
2888 dt.setDefaultLocale(*this);
2889 if (dt.parseFormat(format) && (dt.fromString(text: string, datetime: &datetime, baseYear)
2890 || !datetime.isValid())) {
2891 return datetime;
2892 }
2893#else
2894 Q_UNUSED(string);
2895 Q_UNUSED(format);
2896 Q_UNUSED(baseYear);
2897 Q_UNUSED(cal);
2898#endif
2899 return QDateTime();
2900}
2901#endif // datestring
2902
2903/*!
2904 \since 4.1
2905
2906 Returns the fractional part separator for this locale.
2907
2908 This is the token that separates the whole number part from the fracional
2909 part in the representation of a number which has a fractional part. This is
2910 commonly called the "decimal point character" - even though, in many
2911 locales, it is not a "point" (or similar dot). It is (since Qt 6.0) returned
2912 as a string in case some locale needs more than one UTF-16 code-point to
2913 represent its separator.
2914
2915 \sa groupSeparator(), toString()
2916*/
2917QString QLocale::decimalPoint() const
2918{
2919 return d->m_data->decimalPoint();
2920}
2921
2922/*!
2923 \since 4.1
2924
2925 Returns the digit-grouping separator for this locale.
2926
2927 This is a token used to break up long sequences of digits, in the
2928 representation of a number, to make it easier to read. In some locales it
2929 may be empty, indicating that digits should not be broken up into groups in
2930 this way. In others it may be a spacing character. It is (since Qt 6.0)
2931 returned as a string in case some locale needs more than one UTF-16
2932 code-point to represent its separator.
2933
2934 \sa decimalPoint(), toString()
2935*/
2936QString QLocale::groupSeparator() const
2937{
2938 return d->m_data->groupSeparator();
2939}
2940
2941/*!
2942 \since 4.1
2943
2944 Returns the percent marker of this locale.
2945
2946 This is a token presumed to be appended to a number to indicate a
2947 percentage. It is (since Qt 6.0) returned as a string because, in some
2948 locales, it is not a single character - for example, because it includes a
2949 text-direction-control character.
2950
2951 \sa toString()
2952*/
2953QString QLocale::percent() const
2954{
2955 return d->m_data->percentSign();
2956}
2957
2958/*!
2959 \since 4.1
2960
2961 Returns the zero digit character of this locale.
2962
2963 This is a single Unicode character but may be encoded as a surrogate pair,
2964 so is (since Qt 6.0) returned as a string. In most locales, other digits
2965 follow it in Unicode ordering - however, some number systems, notably those
2966 using U+3007 as zero, do not have contiguous digits. Use toString() to
2967 obtain suitable representations of numbers, rather than trying to construct
2968 them from this zero digit.
2969
2970 \sa toString()
2971*/
2972QString QLocale::zeroDigit() const
2973{
2974 return d->m_data->zeroDigit();
2975}
2976
2977/*!
2978 \since 4.1
2979
2980 Returns the negative sign indicator of this locale.
2981
2982 This is a token presumed to be used as a prefix to a number to indicate that
2983 it is negative. It is (since Qt 6.0) returned as a string because, in some
2984 locales, it is not a single character - for example, because it includes a
2985 text-direction-control character.
2986
2987 \sa positiveSign(), toString()
2988*/
2989QString QLocale::negativeSign() const
2990{
2991 return d->m_data->negativeSign();
2992}
2993
2994/*!
2995 \since 4.5
2996
2997 Returns the positive sign indicator of this locale.
2998
2999 This is a token presumed to be used as a prefix to a number to indicate that
3000 it is positive. It is (since Qt 6.0) returned as a string because, in some
3001 locales, it is not a single character - for example, because it includes a
3002 text-direction-control character.
3003
3004 \sa negativeSign(), toString()
3005*/
3006QString QLocale::positiveSign() const
3007{
3008 return d->m_data->positiveSign();
3009}
3010
3011/*!
3012 \since 4.1
3013
3014 Returns the exponent separator for this locale.
3015
3016 This is a token used to separate mantissa from exponent in some
3017 floating-point numeric representations. It is (since Qt 6.0) returned as a
3018 string because, in some locales, it is not a single character - for example,
3019 it may consist of a multiplication sign and a representation of the "ten to
3020 the power" operator.
3021
3022 \sa toString(double, char, int)
3023*/
3024QString QLocale::exponential() const
3025{
3026 return d->m_data->exponentSeparator();
3027}
3028
3029/*!
3030 \overload
3031 Returns a string representing the floating-point number \a f.
3032
3033 The form of the representation is controlled by the \a format and \a
3034 precision parameters.
3035
3036 The \a format defaults to \c{'g'}. It can be any of the following:
3037
3038 \table
3039 \header \li Format \li Meaning \li Meaning of \a precision
3040 \row \li \c 'e' \li format as [-]9.9e[+|-]999 \li number of digits \e after the decimal point
3041 \row \li \c 'E' \li format as [-]9.9E[+|-]999 \li "
3042 \row \li \c 'f' \li format as [-]9.9 \li "
3043 \row \li \c 'F' \li same as \c 'f' except for INF and NAN (see below) \li "
3044 \row \li \c 'g' \li use \c 'e' or \c 'f' format, whichever is more concise \li maximum number of significant digits (trailing zeroes are omitted)
3045 \row \li \c 'G' \li use \c 'E' or \c 'F' format, whichever is more concise \li "
3046 \endtable
3047
3048 The special \a precision value QLocale::FloatingPointShortest selects the
3049 shortest representation that, when read as a number, gets back the original floating-point
3050 value. Aside from that, any negative \a precision is ignored in favor of the
3051 default, 6.
3052
3053 For the \c 'e', \c 'f' and \c 'g' formats, positive infinity is represented
3054 as "inf", negative infinity as "-inf" and floating-point NaN (not-a-number)
3055 values are represented as "nan". For the \c 'E', \c 'F' and \c 'G' formats,
3056 "INF" and "NAN" are used instead. This does not vary with locale.
3057
3058 \sa toDouble(), numberOptions(), exponential(), decimalPoint(), zeroDigit(),
3059 positiveSign(), percent(), toCurrencyString(), formattedDataSize(),
3060 QLocale::FloatingPointPrecisionOption
3061*/
3062
3063QString QLocale::toString(double f, char format, int precision) const
3064{
3065 QLocaleData::DoubleForm form = QLocaleData::DFDecimal;
3066 uint flags = isAsciiUpper(c: format) ? QLocaleData::CapitalEorX : 0;
3067
3068 switch (QtMiscUtils::toAsciiLower(ch: format)) {
3069 case 'f':
3070 form = QLocaleData::DFDecimal;
3071 break;
3072 case 'e':
3073 form = QLocaleData::DFExponent;
3074 break;
3075 case 'g':
3076 form = QLocaleData::DFSignificantDigits;
3077 break;
3078 default:
3079 break;
3080 }
3081
3082 if (!(d->m_numberOptions & OmitGroupSeparator))
3083 flags |= QLocaleData::GroupDigits;
3084 if (!(d->m_numberOptions & OmitLeadingZeroInExponent))
3085 flags |= QLocaleData::ZeroPadExponent;
3086 if (d->m_numberOptions & IncludeTrailingZeroesAfterDot)
3087 flags |= QLocaleData::AddTrailingZeroes;
3088 return d->m_data->doubleToString(d: f, precision, form, width: -1, flags);
3089}
3090
3091/*!
3092 \fn QLocale QLocale::c()
3093
3094 Returns a QLocale object initialized to the "C" locale.
3095
3096 This locale is based on en_US but with various quirks of its own, such as
3097 simplified number formatting and its own date formatting. It implements the
3098 POSIX standards that describe the behavior of standard library functions of
3099 the "C" programming language.
3100
3101 Among other things, this means its collation order is based on the ASCII
3102 values of letters, so that (for case-sensitive sorting) all upper-case
3103 letters sort before any lower-case one (rather than each letter's upper- and
3104 lower-case forms sorting adjacent to one another, before the next letter's
3105 two forms).
3106
3107 \sa system()
3108*/
3109
3110/*!
3111 Returns a QLocale object initialized to the system locale.
3112
3113 The system locale may use system-specific sources for locale data, where
3114 available, otherwise falling back on QLocale's built-in database entry for
3115 the language, script and territory the system reports.
3116
3117 For example, on Windows and Mac, this locale will use the decimal/grouping
3118 characters and date/time formats specified in the system configuration
3119 panel.
3120
3121 \sa c()
3122*/
3123
3124QLocale QLocale::system()
3125{
3126 constexpr auto sysData = []() {
3127 // Same return as systemData(), but leave the setup to the actual call to it.
3128#ifdef QT_NO_SYSTEMLOCALE
3129 return locale_data;
3130#else
3131 return &systemLocaleData;
3132#endif
3133 };
3134 Q_CONSTINIT static QLocalePrivate locale(sysData(), -1, DefaultNumberOptions, 1);
3135 // Calling systemData() ensures system data is up to date; we also need it
3136 // to ensure that locale's index stays up to date:
3137 systemData(sysIndex: &locale.m_index);
3138 Q_ASSERT(locale.m_index >= 0 && locale.m_index < locale_data_size);
3139 locale.m_numberOptions = defaultNumberOptions(forLanguage: locale.m_data->m_language_id);
3140
3141 return QLocale(locale);
3142}
3143
3144/*!
3145 Returns a list of valid locale objects that match the given \a language, \a
3146 script and \a territory.
3147
3148 Getting a list of all locales:
3149 QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
3150 QLocale::AnyTerritory);
3151
3152 Getting a list of locales suitable for Russia:
3153 QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
3154 QLocale::Russia);
3155*/
3156QList<QLocale> QLocale::matchingLocales(Language language, Script script, Territory territory)
3157{
3158 const QLocaleId filter { .language_id: language, .script_id: script, .territory_id: territory };
3159 if (!filter.isValid())
3160 return QList<QLocale>();
3161
3162 if (language == C)
3163 return QList<QLocale>{QLocale(C)};
3164
3165 QList<QLocale> result;
3166 if (filter.matchesAll())
3167 result.reserve(asize: locale_data_size);
3168
3169 quint16 index = locale_index[language];
3170 // There may be no matches, for some languages (e.g. Abkhazian at CLDR v39).
3171 while (filter.acceptLanguage(lang: locale_data[index].m_language_id)) {
3172 const QLocaleId id = locale_data[index].id();
3173 if (filter.acceptScriptTerritory(other: id)) {
3174 result.append(t: QLocale(*(id.language_id == C ? c_private()
3175 : new QLocalePrivate(locale_data + index, index))));
3176 }
3177 ++index;
3178 }
3179
3180 // Add current system locale, if it matches
3181 const auto syslocaledata = systemData();
3182
3183 if (filter.acceptLanguage(lang: syslocaledata->m_language_id)) {
3184 const QLocaleId id = syslocaledata->id();
3185 if (filter.acceptScriptTerritory(other: id))
3186 result.append(t: system());
3187 }
3188
3189 return result;
3190}
3191
3192#if QT_DEPRECATED_SINCE(6, 6)
3193/*!
3194 \deprecated [6.6] Use \l matchingLocales() instead and consult the \l territory() of each.
3195 \since 4.3
3196
3197 Returns the list of countries that have entries for \a language in Qt's locale
3198 database. If the result is an empty list, then \a language is not represented in
3199 Qt's locale database.
3200
3201 \sa matchingLocales()
3202*/
3203QList<QLocale::Country> QLocale::countriesForLanguage(Language language)
3204{
3205 const auto locales = matchingLocales(language, script: AnyScript, territory: AnyCountry);
3206 QList<Country> result;
3207 result.reserve(asize: locales.size());
3208 for (const auto &locale : locales)
3209 result.append(t: locale.territory());
3210 return result;
3211}
3212#endif
3213
3214/*!
3215 \since 4.2
3216
3217 Returns the localized name of \a month, in the format specified
3218 by \a type.
3219
3220 For example, if the locale is \c en_US and \a month is 1,
3221 \l LongFormat will return \c January. \l ShortFormat \c Jan,
3222 and \l NarrowFormat \c J.
3223
3224 \sa dayName(), standaloneMonthName()
3225*/
3226QString QLocale::monthName(int month, FormatType type) const
3227{
3228 return QCalendar().monthName(locale: *this, month, year: QCalendar::Unspecified, format: type);
3229}
3230
3231/*!
3232 \since 4.5
3233
3234 Returns the localized name of \a month that is used as a
3235 standalone text, in the format specified by \a type.
3236
3237 If the locale information doesn't specify the standalone month
3238 name then return value is the same as in monthName().
3239
3240 \sa monthName(), standaloneDayName()
3241*/
3242QString QLocale::standaloneMonthName(int month, FormatType type) const
3243{
3244 return QCalendar().standaloneMonthName(locale: *this, month, year: QCalendar::Unspecified, format: type);
3245}
3246
3247/*!
3248 \since 4.2
3249
3250 Returns the localized name of the \a day (where 1 represents
3251 Monday, 2 represents Tuesday and so on), in the format specified
3252 by \a type.
3253
3254 For example, if the locale is \c en_US and \a day is 1,
3255 \l LongFormat will return \c Monday, \l ShortFormat \c Mon,
3256 and \l NarrowFormat \c M.
3257
3258 \sa monthName(), standaloneDayName()
3259*/
3260QString QLocale::dayName(int day, FormatType type) const
3261{
3262 return QCalendar().weekDayName(locale: *this, day, format: type);
3263}
3264
3265/*!
3266 \since 4.5
3267
3268 Returns the localized name of the \a day (where 1 represents
3269 Monday, 2 represents Tuesday and so on) that is used as a
3270 standalone text, in the format specified by \a type.
3271
3272 If the locale information does not specify the standalone day
3273 name then return value is the same as in dayName().
3274
3275 \sa dayName(), standaloneMonthName()
3276*/
3277QString QLocale::standaloneDayName(int day, FormatType type) const
3278{
3279 return QCalendar().standaloneWeekDayName(locale: *this, day, format: type);
3280}
3281
3282// Calendar look-up of month and day names:
3283
3284// Get locale-specific month name data:
3285static const QCalendarLocale &getMonthDataFor(const QLocalePrivate *loc,
3286 const QCalendarLocale *table)
3287{
3288 // Only used in assertions
3289 [[maybe_unused]] const auto sameLocale = [](const QLocaleData &locale,
3290 const QCalendarLocale &cal) {
3291 return locale.m_language_id == cal.m_language_id
3292 && locale.m_script_id == cal.m_script_id
3293 && locale.m_territory_id == cal.m_territory_id;
3294 };
3295 const QCalendarLocale &monthly = table[loc->m_index];
3296#ifdef QT_NO_SYSTEMLOCALE
3297 [[maybe_unused]] constexpr bool isSys = false;
3298#else // Can't have preprocessor directives in a macro's parameter list, so use local.
3299 [[maybe_unused]] const bool isSys = loc->m_data == &systemLocaleData;
3300#endif
3301 Q_ASSERT(loc->m_data == &locale_data[loc->m_index] || isSys);
3302 // Compare monthly to locale_data[] entry, as the m_index used with
3303 // systemLocaleData is a best fit, not necessarily an exact match.
3304 Q_ASSERT(sameLocale(locale_data[loc->m_index], monthly));
3305 return monthly;
3306}
3307
3308/*!
3309 \internal
3310 */
3311
3312static QString rawMonthName(const QCalendarLocale &localeData,
3313 const char16_t *monthsData, int month,
3314 QLocale::FormatType type)
3315{
3316 const QLocaleData::DataRange range = localeData.monthName(type);
3317 return range.getListEntry(table: monthsData, index: month - 1);
3318}
3319
3320/*!
3321 \internal
3322 */
3323
3324static QString rawStandaloneMonthName(const QCalendarLocale &localeData,
3325 const char16_t *monthsData, int month,
3326 QLocale::FormatType type)
3327{
3328 const QLocaleData::DataRange range = localeData.standaloneMonthName(type);
3329 if (QString name = range.getListEntry(table: monthsData, index: month - 1); !name.isEmpty())
3330 return name;
3331 return rawMonthName(localeData, monthsData, month, type);
3332}
3333
3334/*!
3335 \internal
3336 */
3337
3338static QString rawWeekDayName(const QLocaleData *data, const int day,
3339 QLocale::FormatType type)
3340{
3341 QLocaleData::DataRange range;
3342 switch (type) {
3343 case QLocale::LongFormat:
3344 range = data->longDayNames();
3345 break;
3346 case QLocale::ShortFormat:
3347 range = data->shortDayNames();
3348 break;
3349 case QLocale::NarrowFormat:
3350 range = data->narrowDayNames();
3351 break;
3352 default:
3353 return QString();
3354 }
3355 return range.getListEntry(table: days_data, index: day == 7 ? 0 : day);
3356}
3357
3358/*!
3359 \internal
3360 */
3361
3362static QString rawStandaloneWeekDayName(const QLocaleData *data, const int day,
3363 QLocale::FormatType type)
3364{
3365 QLocaleData::DataRange range;
3366 switch (type) {
3367 case QLocale::LongFormat:
3368 range =data->longDayNamesStandalone();
3369 break;
3370 case QLocale::ShortFormat:
3371 range = data->shortDayNamesStandalone();
3372 break;
3373 case QLocale::NarrowFormat:
3374 range = data->narrowDayNamesStandalone();
3375 break;
3376 default:
3377 return QString();
3378 }
3379 QString name = range.getListEntry(table: days_data, index: day == 7 ? 0 : day);
3380 if (name.isEmpty())
3381 return rawWeekDayName(data, day, type);
3382 return name;
3383}
3384
3385// Refugees from qcalendar.cpp that need functions above:
3386
3387QString QCalendarBackend::monthName(const QLocale &locale, int month, int,
3388 QLocale::FormatType format) const
3389{
3390 Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
3391 return rawMonthName(localeData: getMonthDataFor(loc: locale.d, table: localeMonthIndexData()),
3392 monthsData: localeMonthData(), month, type: format);
3393}
3394
3395QString QRomanCalendar::monthName(const QLocale &locale, int month, int year,
3396 QLocale::FormatType format) const
3397{
3398#ifndef QT_NO_SYSTEMLOCALE
3399 if (locale.d->m_data == &systemLocaleData) {
3400 Q_ASSERT(month >= 1 && month <= 12);
3401 QSystemLocale::QueryType queryType = QSystemLocale::MonthNameLong;
3402 switch (format) {
3403 case QLocale::LongFormat:
3404 queryType = QSystemLocale::MonthNameLong;
3405 break;
3406 case QLocale::ShortFormat:
3407 queryType = QSystemLocale::MonthNameShort;
3408 break;
3409 case QLocale::NarrowFormat:
3410 queryType = QSystemLocale::MonthNameNarrow;
3411 break;
3412 }
3413 QVariant res = systemLocale()->query(type: queryType, in: month);
3414 if (!res.isNull())
3415 return res.toString();
3416 }
3417#endif
3418
3419 return QCalendarBackend::monthName(locale, month, year, format);
3420}
3421
3422QString QCalendarBackend::standaloneMonthName(const QLocale &locale, int month, int,
3423 QLocale::FormatType format) const
3424{
3425 Q_ASSERT(month >= 1 && month <= maximumMonthsInYear());
3426 return rawStandaloneMonthName(localeData: getMonthDataFor(loc: locale.d, table: localeMonthIndexData()),
3427 monthsData: localeMonthData(), month, type: format);
3428}
3429
3430QString QRomanCalendar::standaloneMonthName(const QLocale &locale, int month, int year,
3431 QLocale::FormatType format) const
3432{
3433#ifndef QT_NO_SYSTEMLOCALE
3434 if (locale.d->m_data == &systemLocaleData) {
3435 Q_ASSERT(month >= 1 && month <= 12);
3436 QSystemLocale::QueryType queryType = QSystemLocale::StandaloneMonthNameLong;
3437 switch (format) {
3438 case QLocale::LongFormat:
3439 queryType = QSystemLocale::StandaloneMonthNameLong;
3440 break;
3441 case QLocale::ShortFormat:
3442 queryType = QSystemLocale::StandaloneMonthNameShort;
3443 break;
3444 case QLocale::NarrowFormat:
3445 queryType = QSystemLocale::StandaloneMonthNameNarrow;
3446 break;
3447 }
3448 QVariant res = systemLocale()->query(type: queryType, in: month);
3449 if (!res.isNull())
3450 return res.toString();
3451 }
3452#endif
3453
3454 return QCalendarBackend::standaloneMonthName(locale, month, year, format);
3455}
3456
3457// Most calendars share the common week-day naming, modulo locale.
3458// Calendars that don't must override these methods.
3459QString QCalendarBackend::weekDayName(const QLocale &locale, int day,
3460 QLocale::FormatType format) const
3461{
3462 if (day < 1 || day > 7)
3463 return QString();
3464
3465#ifndef QT_NO_SYSTEMLOCALE
3466 if (locale.d->m_data == &systemLocaleData) {
3467 QSystemLocale::QueryType queryType = QSystemLocale::DayNameLong;
3468 switch (format) {
3469 case QLocale::LongFormat:
3470 queryType = QSystemLocale::DayNameLong;
3471 break;
3472 case QLocale::ShortFormat:
3473 queryType = QSystemLocale::DayNameShort;
3474 break;
3475 case QLocale::NarrowFormat:
3476 queryType = QSystemLocale::DayNameNarrow;
3477 break;
3478 }
3479 QVariant res = systemLocale()->query(type: queryType, in: day);
3480 if (!res.isNull())
3481 return res.toString();
3482 }
3483#endif
3484
3485 return rawWeekDayName(data: locale.d->m_data, day, type: format);
3486}
3487
3488QString QCalendarBackend::standaloneWeekDayName(const QLocale &locale, int day,
3489 QLocale::FormatType format) const
3490{
3491 if (day < 1 || day > 7)
3492 return QString();
3493
3494#ifndef QT_NO_SYSTEMLOCALE
3495 if (locale.d->m_data == &systemLocaleData) {
3496 QSystemLocale::QueryType queryType = QSystemLocale::StandaloneDayNameLong;
3497 switch (format) {
3498 case QLocale::LongFormat:
3499 queryType = QSystemLocale::StandaloneDayNameLong;
3500 break;
3501 case QLocale::ShortFormat:
3502 queryType = QSystemLocale::StandaloneDayNameShort;
3503 break;
3504 case QLocale::NarrowFormat:
3505 queryType = QSystemLocale::StandaloneDayNameNarrow;
3506 break;
3507 }
3508 QVariant res = systemLocale()->query(type: queryType, in: day);
3509 if (!res.isNull())
3510 return res.toString();
3511 }
3512#endif
3513
3514 return rawStandaloneWeekDayName(data: locale.d->m_data, day, type: format);
3515}
3516
3517// End of this block of qcalendar.cpp refugees. (One more follows.)
3518
3519/*!
3520 \since 4.8
3521
3522 Returns the first day of the week according to the current locale.
3523*/
3524Qt::DayOfWeek QLocale::firstDayOfWeek() const
3525{
3526#ifndef QT_NO_SYSTEMLOCALE
3527 if (d->m_data == &systemLocaleData) {
3528 const auto res = systemLocale()->query(type: QSystemLocale::FirstDayOfWeek);
3529 if (!res.isNull())
3530 return static_cast<Qt::DayOfWeek>(res.toUInt());
3531 }
3532#endif
3533 return static_cast<Qt::DayOfWeek>(d->m_data->m_first_day_of_week);
3534}
3535
3536QLocale::MeasurementSystem QLocalePrivate::measurementSystem() const
3537{
3538 /* Unicode CLDR's information about measurement systems doesn't say which to
3539 use by default in each locale. Even if it did, adding another entry in
3540 every locale's row of locale_data[] would take up much more memory than
3541 the small table below.
3542 */
3543 struct TerritoryLanguage
3544 {
3545 quint16 languageId;
3546 quint16 territoryId;
3547 QLocale::MeasurementSystem system;
3548 };
3549 // TODO: research how realistic and/or complete this is:
3550 constexpr TerritoryLanguage ImperialMeasurementSystems[] = {
3551 { .languageId: QLocale::English, .territoryId: QLocale::UnitedStates, .system: QLocale::ImperialUSSystem },
3552 { .languageId: QLocale::English, .territoryId: QLocale::UnitedStatesMinorOutlyingIslands, .system: QLocale::ImperialUSSystem },
3553 { .languageId: QLocale::Spanish, .territoryId: QLocale::UnitedStates, .system: QLocale::ImperialUSSystem },
3554 { .languageId: QLocale::Hawaiian, .territoryId: QLocale::UnitedStates, .system: QLocale::ImperialUSSystem },
3555 { .languageId: QLocale::English, .territoryId: QLocale::UnitedKingdom, .system: QLocale::ImperialUKSystem }
3556 };
3557
3558 for (const auto &system : ImperialMeasurementSystems) {
3559 if (system.languageId == m_data->m_language_id
3560 && system.territoryId == m_data->m_territory_id) {
3561 return system.system;
3562 }
3563 }
3564 return QLocale::MetricSystem;
3565}
3566
3567/*!
3568 \since 4.8
3569
3570 Returns a list of days that are considered weekdays according to the current locale.
3571*/
3572QList<Qt::DayOfWeek> QLocale::weekdays() const
3573{
3574#ifndef QT_NO_SYSTEMLOCALE
3575 if (d->m_data == &systemLocaleData) {
3576 auto res
3577 = qvariant_cast<QList<Qt::DayOfWeek> >(v: systemLocale()->query(type: QSystemLocale::Weekdays));
3578 if (!res.isEmpty())
3579 return res;
3580 }
3581#endif
3582 QList<Qt::DayOfWeek> weekdays;
3583 quint16 weekendStart = d->m_data->m_weekend_start;
3584 quint16 weekendEnd = d->m_data->m_weekend_end;
3585 for (int day = Qt::Monday; day <= Qt::Sunday; day++) {
3586 if ((weekendEnd >= weekendStart && (day < weekendStart || day > weekendEnd)) ||
3587 (weekendEnd < weekendStart && (day > weekendEnd && day < weekendStart)))
3588 weekdays << static_cast<Qt::DayOfWeek>(day);
3589 }
3590 return weekdays;
3591}
3592
3593/*!
3594 \since 4.4
3595
3596 Returns the measurement system for the locale.
3597*/
3598QLocale::MeasurementSystem QLocale::measurementSystem() const
3599{
3600#ifndef QT_NO_SYSTEMLOCALE
3601 if (d->m_data == &systemLocaleData) {
3602 const auto res = systemLocale()->query(type: QSystemLocale::MeasurementSystem);
3603 if (!res.isNull())
3604 return MeasurementSystem(res.toInt());
3605 }
3606#endif
3607
3608 return d->measurementSystem();
3609}
3610
3611/*!
3612 \since 4.7
3613
3614 Returns the text direction of the language.
3615*/
3616Qt::LayoutDirection QLocale::textDirection() const
3617{
3618 switch (script()) {
3619 case AdlamScript:
3620 case ArabicScript:
3621 case AvestanScript:
3622 case CypriotScript:
3623 case HatranScript:
3624 case HebrewScript:
3625 case ImperialAramaicScript:
3626 case InscriptionalPahlaviScript:
3627 case InscriptionalParthianScript:
3628 case KharoshthiScript:
3629 case LydianScript:
3630 case MandaeanScript:
3631 case ManichaeanScript:
3632 case MendeKikakuiScript:
3633 case MeroiticCursiveScript:
3634 case MeroiticScript:
3635 case NabataeanScript:
3636 case NkoScript:
3637 case OldHungarianScript:
3638 case OldNorthArabianScript:
3639 case OldSouthArabianScript:
3640 case OrkhonScript:
3641 case PalmyreneScript:
3642 case PhoenicianScript:
3643 case PsalterPahlaviScript:
3644 case SamaritanScript:
3645 case SyriacScript:
3646 case ThaanaScript:
3647 return Qt::RightToLeft;
3648 default:
3649 break;
3650 }
3651 return Qt::LeftToRight;
3652}
3653
3654/*!
3655 \since 4.8
3656
3657 Returns an uppercase copy of \a str.
3658
3659 If Qt Core is using the ICU libraries, they will be used to perform
3660 the transformation according to the rules of the current locale.
3661 Otherwise the conversion may be done in a platform-dependent manner,
3662 with QString::toUpper() as a generic fallback.
3663
3664 \note In some cases the uppercase form of a string may be longer than the
3665 original.
3666
3667 \sa QString::toUpper()
3668*/
3669QString QLocale::toUpper(const QString &str) const
3670{
3671#if !defined(QT_BOOTSTRAPPED) && (QT_CONFIG(icu) || defined(Q_OS_WIN) || defined(Q_OS_APPLE))
3672 bool ok = true;
3673 QString result = d->toUpper(str, ok: &ok);
3674 if (ok)
3675 return result;
3676 // else fall through and use Qt's toUpper
3677#endif
3678 return str.toUpper();
3679}
3680
3681/*!
3682 \since 4.8
3683
3684 Returns a lowercase copy of \a str.
3685
3686 If Qt Core is using the ICU libraries, they will be used to perform
3687 the transformation according to the rules of the current locale.
3688 Otherwise the conversion may be done in a platform-dependent manner,
3689 with QString::toLower() as a generic fallback.
3690
3691 \sa QString::toLower()
3692*/
3693QString QLocale::toLower(const QString &str) const
3694{
3695#if !defined(QT_BOOTSTRAPPED) && (QT_CONFIG(icu) || defined(Q_OS_WIN) || defined(Q_OS_APPLE))
3696 bool ok = true;
3697 const QString result = d->toLower(str, ok: &ok);
3698 if (ok)
3699 return result;
3700 // else fall through and use Qt's toLower
3701#endif
3702 return str.toLower();
3703}
3704
3705
3706/*!
3707 \since 4.5
3708
3709 Returns the localized name of the "AM" suffix for times specified using
3710 the conventions of the 12-hour clock.
3711
3712 \sa pmText()
3713*/
3714QString QLocale::amText() const
3715{
3716#ifndef QT_NO_SYSTEMLOCALE
3717 if (d->m_data == &systemLocaleData) {
3718 auto res = systemLocale()->query(type: QSystemLocale::AMText).toString();
3719 if (!res.isEmpty())
3720 return res;
3721 }
3722#endif
3723 return d->m_data->anteMeridiem().getData(table: am_data);
3724}
3725
3726/*!
3727 \since 4.5
3728
3729 Returns the localized name of the "PM" suffix for times specified using
3730 the conventions of the 12-hour clock.
3731
3732 \sa amText()
3733*/
3734QString QLocale::pmText() const
3735{
3736#ifndef QT_NO_SYSTEMLOCALE
3737 if (d->m_data == &systemLocaleData) {
3738 auto res = systemLocale()->query(type: QSystemLocale::PMText).toString();
3739 if (!res.isEmpty())
3740 return res;
3741 }
3742#endif
3743 return d->m_data->postMeridiem().getData(table: pm_data);
3744}
3745
3746// For the benefit of QCalendar, below.
3747static QString offsetFromAbbreviation(QString &&text)
3748{
3749 QStringView tail{text};
3750 // May need to strip a prefix:
3751 if (tail.startsWith(s: "UTC"_L1) || tail.startsWith(s: "GMT"_L1))
3752 tail = tail.sliced(pos: 3);
3753 // TODO: there may be a locale-specific alternative prefix.
3754 // Hard to know without zone-name L10n details, though.
3755 return (tail.isEmpty() // The Qt::UTC case omits the zero offset:
3756 ? u"+00:00"_s
3757 // Whole-hour offsets may lack the zero minutes:
3758 : (tail.size() <= 3
3759 ? tail + ":00"_L1
3760 : std::move(text).right(n: tail.size())));
3761}
3762
3763// For the benefit of QCalendar, below, when not provided by QTZL.
3764#if QT_CONFIG(icu) || !(QT_CONFIG(timezone) && QT_CONFIG(timezone_locale))
3765namespace QtTimeZoneLocale {
3766
3767// TODO: is there a way to get this non-kludgily from ICU ?
3768// If so, that version goes in QTZL.cpp's relevant #if-ery branch.
3769QString zoneOffsetFormat([[maybe_unused]] const QLocale &locale,
3770 qsizetype,
3771 [[maybe_unused]] QLocale::FormatType width,
3772 const QDateTime &when,
3773 int offsetSeconds)
3774{
3775 // Only the non-ICU TZ-locale code uses the other two widths:
3776 Q_ASSERT(width == QLocale::ShortFormat); //
3777 QString text =
3778#if QT_CONFIG(timezone)
3779 locale != QLocale::system()
3780 ? when.timeRepresentation().displayName(atDateTime: when, nameType: QTimeZone::OffsetName, locale)
3781 :
3782#endif
3783 when.toOffsetFromUtc(offsetSeconds).timeZoneAbbreviation();
3784
3785 if (!text.isEmpty())
3786 text = offsetFromAbbreviation(text: std::move(text));
3787 // else: no suitable representation of the zone.
3788 return text;
3789}
3790
3791} // QtTimeZoneLocale
3792#endif // ICU or no TZ L10n
3793
3794// Another intrusion from QCalendar, using some of the tools above:
3795
3796QString QCalendarBackend::dateTimeToString(QStringView format, const QDateTime &datetime,
3797 QDate dateOnly, QTime timeOnly,
3798 const QLocale &locale) const
3799{
3800 QDate date;
3801 QTime time;
3802 bool formatDate = false;
3803 bool formatTime = false;
3804 if (datetime.isValid()) {
3805 date = datetime.date();
3806 time = datetime.time();
3807 formatDate = true;
3808 formatTime = true;
3809 } else if (dateOnly.isValid()) {
3810 date = dateOnly;
3811 formatDate = true;
3812 } else if (timeOnly.isValid()) {
3813 time = timeOnly;
3814 formatTime = true;
3815 } else {
3816 return QString();
3817 }
3818
3819 QString result;
3820 int year = 0, month = 0, day = 0;
3821 if (formatDate) {
3822 const auto parts = julianDayToDate(jd: date.toJulianDay());
3823 if (!parts.isValid())
3824 return QString();
3825 year = parts.year;
3826 month = parts.month;
3827 day = parts.day;
3828 }
3829
3830 auto appendToResult = [&](int t, int repeat) {
3831 auto data = locale.d->m_data;
3832 if (repeat > 1)
3833 result.append(s: data->longLongToString(l: t, precision: -1, base: 10, width: repeat, flags: QLocaleData::ZeroPadded));
3834 else
3835 result.append(s: data->longLongToString(l: t));
3836 };
3837
3838 auto formatType = [](int repeat) {
3839 return repeat == 3 ? QLocale::ShortFormat : QLocale::LongFormat;
3840 };
3841
3842 qsizetype i = 0;
3843 while (i < format.size()) {
3844 if (format.at(n: i).unicode() == '\'') {
3845 result.append(s: qt_readEscapedFormatString(format, idx: &i));
3846 continue;
3847 }
3848
3849 const QChar c = format.at(n: i);
3850 qsizetype rep = qt_repeatCount(s: format.mid(pos: i));
3851 Q_ASSERT(rep < std::numeric_limits<int>::max());
3852 int repeat = int(rep);
3853 bool used = false;
3854 if (formatDate) {
3855 switch (c.unicode()) {
3856 case 'y':
3857 used = true;
3858 if (repeat >= 4)
3859 repeat = 4;
3860 else if (repeat >= 2)
3861 repeat = 2;
3862
3863 switch (repeat) {
3864 case 4:
3865 appendToResult(year, (year < 0) ? 5 : 4);
3866 break;
3867 case 2:
3868 appendToResult(year % 100, 2);
3869 break;
3870 default:
3871 repeat = 1;
3872 result.append(c);
3873 break;
3874 }
3875 break;
3876
3877 case 'M':
3878 used = true;
3879 repeat = qMin(a: repeat, b: 4);
3880 if (repeat <= 2)
3881 appendToResult(month, repeat);
3882 else
3883 result.append(s: monthName(locale, month, year, format: formatType(repeat)));
3884 break;
3885
3886 case 'd':
3887 used = true;
3888 repeat = qMin(a: repeat, b: 4);
3889 if (repeat <= 2)
3890 appendToResult(day, repeat);
3891 else
3892 result.append(
3893 s: locale.dayName(day: dayOfWeek(jd: date.toJulianDay()), type: formatType(repeat)));
3894 break;
3895
3896 default:
3897 break;
3898 }
3899 }
3900 if (!used && formatTime) {
3901 switch (c.unicode()) {
3902 case 'h': {
3903 used = true;
3904 repeat = qMin(a: repeat, b: 2);
3905 int hour = time.hour();
3906 if (timeFormatContainsAP(format)) {
3907 if (hour > 12)
3908 hour -= 12;
3909 else if (hour == 0)
3910 hour = 12;
3911 }
3912 appendToResult(hour, repeat);
3913 break;
3914 }
3915 case 'H':
3916 used = true;
3917 repeat = qMin(a: repeat, b: 2);
3918 appendToResult(time.hour(), repeat);
3919 break;
3920
3921 case 'm':
3922 used = true;
3923 repeat = qMin(a: repeat, b: 2);
3924 appendToResult(time.minute(), repeat);
3925 break;
3926
3927 case 's':
3928 used = true;
3929 repeat = qMin(a: repeat, b: 2);
3930 appendToResult(time.second(), repeat);
3931 break;
3932
3933 case 'A':
3934 case 'a': {
3935 QString text = time.hour() < 12 ? locale.amText() : locale.pmText();
3936 used = true;
3937 repeat = 1;
3938 if (format.mid(pos: i + 1).startsWith(c: u'p', cs: Qt::CaseInsensitive))
3939 ++repeat;
3940 if (c.unicode() == 'A' && (repeat == 1 || format.at(n: i + 1).unicode() == 'P'))
3941 text = std::move(text).toUpper();
3942 else if (c.unicode() == 'a' && (repeat == 1 || format.at(n: i + 1).unicode() == 'p'))
3943 text = std::move(text).toLower();
3944 // else 'Ap' or 'aP' => use CLDR text verbatim, preserving case
3945 result.append(s: text);
3946 break;
3947 }
3948
3949 case 'z':
3950 used = true;
3951 repeat = qMin(a: repeat, b: 3);
3952
3953 // note: the millisecond component is treated like the decimal part of the seconds
3954 // so ms == 2 is always printed as "002", but ms == 200 can be either "2" or "200"
3955 appendToResult(time.msec(), 3);
3956 if (repeat != 3) {
3957 if (result.endsWith(s: locale.zeroDigit()))
3958 result.chop(n: 1);
3959 if (result.endsWith(s: locale.zeroDigit()))
3960 result.chop(n: 1);
3961 }
3962 break;
3963
3964 case 't': {
3965 enum AbbrType { Long, Offset, Short };
3966 const auto tzAbbr = [locale](const QDateTime &when, AbbrType type) {
3967 QString text;
3968 if (type == Offset) {
3969 text = QtTimeZoneLocale::zoneOffsetFormat(locale, locale.d->m_index,
3970 width: QLocale::ShortFormat,
3971 when, offsetSeconds: when.offsetFromUtc());
3972 // When using timezone_locale data, this should always succeed:
3973 if (!text.isEmpty())
3974 return text;
3975 }
3976#if QT_CONFIG(timezone)
3977 if (type != Short || locale != QLocale::system()) {
3978 QTimeZone::NameType mode =
3979 type == Short ? QTimeZone::ShortName
3980 : type == Long ? QTimeZone::LongName : QTimeZone::OffsetName;
3981 text = when.timeRepresentation().displayName(atDateTime: when, nameType: mode, locale);
3982 if (!text.isEmpty())
3983 return text;
3984 // else fall back to an unlocalized one if we can find one.
3985 }
3986 if (type == Long) {
3987 // If no long name found, use IANA ID:
3988 text = QString::fromLatin1(ba: when.timeZone().id());
3989 if (!text.isEmpty())
3990 return text;
3991 }
3992 // else: prefer QDateTime's abbreviation, for backwards-compatibility.
3993#endif // else, make do with non-localized abbreviation:
3994 // Absent timezone_locale data, Offset might still reach here:
3995 if (type == Offset) // Our prior failure might not have tried this:
3996 text = when.toOffsetFromUtc(offsetSeconds: when.offsetFromUtc()).timeZoneAbbreviation();
3997 if (text.isEmpty()) // Notably including type != Offset
3998 text = when.timeZoneAbbreviation();
3999 if (type == Offset)
4000 text = offsetFromAbbreviation(text: std::move(text));
4001 return text;
4002 };
4003
4004 used = true;
4005 repeat = qMin(a: repeat, b: 4);
4006 // If we don't have a date-time, use the current system time:
4007 const QDateTime when = formatDate ? datetime : QDateTime::currentDateTime();
4008 QString text;
4009 switch (repeat) {
4010 case 4:
4011 text = tzAbbr(when, Long);
4012 break;
4013 case 3: // ±hh:mm
4014 case 2: // ±hhmm (we'll remove the ':' at the end)
4015 text = tzAbbr(when, Offset);
4016 if (repeat == 2)
4017 text.remove(c: u':');
4018 break;
4019 default:
4020 text = tzAbbr(when, Short);
4021 // UTC-offset zones only include minutes if non-zero.
4022 if (text.startsWith(s: "UTC"_L1) && text.size() == 6)
4023 text += ":00"_L1;
4024 break;
4025 }
4026 if (!text.isEmpty())
4027 result.append(s: text);
4028 break;
4029 }
4030
4031 default:
4032 break;
4033 }
4034 }
4035 if (!used)
4036 result.resize(size: result.size() + repeat, fillChar: c);
4037 i += repeat;
4038 }
4039
4040 return result;
4041}
4042
4043// End of QCalendar intrustions
4044
4045QString QLocaleData::doubleToString(double d, int precision, DoubleForm form,
4046 int width, unsigned flags) const
4047{
4048 // Although the special handling of F.P.Shortest below is limited to
4049 // DFSignificantDigits, the double-conversion library does treat it
4050 // specially for the other forms, shedding trailing zeros for DFDecimal and
4051 // using the shortest mantissa that faithfully represents the value for
4052 // DFExponent.
4053 if (precision != QLocale::FloatingPointShortest && precision < 0)
4054 precision = 6;
4055 if (width < 0)
4056 width = 0;
4057
4058 int decpt;
4059 qsizetype bufSize = 1;
4060 if (precision == QLocale::FloatingPointShortest)
4061 bufSize += std::numeric_limits<double>::max_digits10;
4062 else if (form == DFDecimal && qt_is_finite(d))
4063 bufSize += wholePartSpace(d: qAbs(t: d)) + precision;
4064 else // Add extra digit due to different interpretations of precision.
4065 bufSize += qMax(a: 2, b: precision) + 1; // Must also be big enough for "nan" or "inf"
4066
4067 QVarLengthArray<char> buf(bufSize);
4068 int length;
4069 bool negative = false;
4070 qt_doubleToAscii(d, form, precision, buf: buf.data(), bufSize, sign&: negative, length, decpt);
4071
4072 const QString prefix = signPrefix(negative: negative && !qIsNull(d), flags);
4073 QString numStr;
4074
4075 if (length == 3
4076 && (qstrncmp(str1: buf.data(), str2: "inf", len: 3) == 0 || qstrncmp(str1: buf.data(), str2: "nan", len: 3) == 0)) {
4077 numStr = QString::fromLatin1(str: buf.data(), size: length);
4078 } else { // Handle finite values
4079 const QString zero = zeroDigit();
4080 QString digits = QString::fromLatin1(str: buf.data(), size: length);
4081
4082 if (zero == u"0") {
4083 // No need to convert digits.
4084 Q_ASSERT(std::all_of(buf.cbegin(), buf.cbegin() + length, isAsciiDigit));
4085 // That check is taken care of in unicodeForDigits, below.
4086 } else if (zero.size() == 2 && zero.at(i: 0).isHighSurrogate()) {
4087 const char32_t zeroUcs4 = QChar::surrogateToUcs4(high: zero.at(i: 0), low: zero.at(i: 1));
4088 QString converted;
4089 converted.reserve(asize: 2 * digits.size());
4090 for (QChar ch : std::as_const(t&: digits)) {
4091 const char32_t digit = unicodeForDigit(digit: ch.unicode() - '0', zero: zeroUcs4);
4092 Q_ASSERT(QChar::requiresSurrogates(digit));
4093 converted.append(c: QChar::highSurrogate(ucs4: digit));
4094 converted.append(c: QChar::lowSurrogate(ucs4: digit));
4095 }
4096 digits = std::move(converted);
4097 } else {
4098 Q_ASSERT(zero.size() == 1);
4099 Q_ASSERT(!zero.at(0).isSurrogate());
4100 char16_t z = zero.at(i: 0).unicode();
4101 char16_t *const value = reinterpret_cast<char16_t *>(digits.data());
4102 for (qsizetype i = 0; i < digits.size(); ++i)
4103 value[i] = unicodeForDigit(digit: value[i] - '0', zero: z);
4104 }
4105
4106 const bool mustMarkDecimal = flags & ForcePoint;
4107 const bool groupDigits = flags & GroupDigits;
4108 const int minExponentDigits = flags & ZeroPadExponent ? 2 : 1;
4109 switch (form) {
4110 case DFExponent:
4111 numStr = exponentForm(digits: std::move(digits), decpt, precision, pm: PMDecimalDigits,
4112 mustMarkDecimal, minExponentDigits);
4113 break;
4114 case DFDecimal:
4115 numStr = decimalForm(digits: std::move(digits), decpt, precision, pm: PMDecimalDigits,
4116 mustMarkDecimal, groupDigits);
4117 break;
4118 case DFSignificantDigits: {
4119 PrecisionMode mode
4120 = (flags & AddTrailingZeroes) ? PMSignificantDigits : PMChopTrailingZeros;
4121
4122 /* POSIX specifies sprintf() to follow fprintf(), whose 'g/G' format
4123 says; with P = 6 if precision unspecified else 1 if precision is
4124 0 else precision; when 'e/E' would have exponent X, use:
4125 * 'f/F' if P > X >= -4, with precision P-1-X
4126 * 'e/E' otherwise, with precision P-1
4127 Helpfully, we already have mapped precision < 0 to 6 - except for
4128 F.P.Shortest mode, which is its own story - and those of our
4129 callers with unspecified precision either used 6 or -1 for it.
4130 */
4131 bool useDecimal;
4132 if (precision == QLocale::FloatingPointShortest) {
4133 // Find out which representation is shorter.
4134 // Set bias to everything added to exponent form but not
4135 // decimal, minus the converse.
4136
4137 const QLocaleData::GroupSizes grouping = groupSizes();
4138 // Exponent adds separator, sign and digits:
4139 int bias = 2 + minExponentDigits;
4140 // Decimal form may get grouping separators inserted:
4141 if (groupDigits && decpt >= grouping.first + grouping.least)
4142 bias -= (decpt - grouping.least) / grouping.higher + 1;
4143 // X = decpt - 1 needs two digits if decpt > 10:
4144 if (decpt > 10 && minExponentDigits == 1)
4145 ++bias;
4146 // Assume digitCount < 95, so we can ignore the 3-digit
4147 // exponent case (we'll set useDecimal false anyway).
4148
4149 const qsizetype digitCount = digits.size() / zero.size();
4150 if (!mustMarkDecimal) {
4151 // Decimal separator is skipped if at end; adjust if
4152 // that happens for only one form:
4153 if (digitCount <= decpt && digitCount > 1)
4154 ++bias; // decimal but not exponent
4155 else if (digitCount == 1 && decpt <= 0)
4156 --bias; // exponent but not decimal
4157 }
4158 // When 0 < decpt <= digitCount, the forms have equal digit
4159 // counts, plus things bias has taken into account; otherwise
4160 // decimal form's digit count is right-padded with zeros to
4161 // decpt, when decpt is positive, otherwise it's left-padded
4162 // with 1 - decpt zeros.
4163 useDecimal = (decpt <= 0 ? 1 - decpt <= bias
4164 : decpt <= digitCount ? 0 <= bias : decpt <= digitCount + bias);
4165 } else {
4166 // X == decpt - 1, POSIX's P; -4 <= X < P iff -4 < decpt <= P
4167 Q_ASSERT(precision >= 0);
4168 useDecimal = decpt > -4 && decpt <= (precision ? precision : 1);
4169 }
4170
4171 numStr = useDecimal
4172 ? decimalForm(digits: std::move(digits), decpt, precision, pm: mode,
4173 mustMarkDecimal, groupDigits)
4174 : exponentForm(digits: std::move(digits), decpt, precision, pm: mode,
4175 mustMarkDecimal, minExponentDigits);
4176 break;
4177 }
4178 }
4179
4180 // Pad with zeros. LeftAdjusted overrides ZeroPadded.
4181 if (flags & ZeroPadded && !(flags & LeftAdjusted)) {
4182 for (qsizetype i = numStr.size() / zero.size() + prefix.size(); i < width; ++i)
4183 numStr.prepend(s: zero);
4184 }
4185 }
4186
4187 return prefix + (flags & CapitalEorX
4188 ? std::move(numStr).toUpper()
4189 : std::move(numStr).toLower());
4190}
4191
4192QString QLocaleData::decimalForm(QString &&digits, int decpt, int precision,
4193 PrecisionMode pm, bool mustMarkDecimal,
4194 bool groupDigits) const
4195{
4196 const QString zero = zeroDigit();
4197 const auto digitWidth = zero.size();
4198 Q_ASSERT(digitWidth == 1 || digitWidth == 2);
4199 Q_ASSERT(digits.size() % digitWidth == 0);
4200
4201 // Separator needs to go at index decpt: so add zeros before or after the
4202 // given digits, if they don't reach that position already:
4203 if (decpt < 0) {
4204 for (; decpt < 0; ++decpt)
4205 digits.prepend(s: zero);
4206 } else {
4207 for (qsizetype i = digits.size() / digitWidth; i < decpt; ++i)
4208 digits.append(s: zero);
4209 }
4210
4211 switch (pm) {
4212 case PMDecimalDigits:
4213 for (qsizetype i = digits.size() / digitWidth - decpt; i < precision; ++i)
4214 digits.append(s: zero);
4215 break;
4216 case PMSignificantDigits:
4217 for (qsizetype i = digits.size() / digitWidth; i < precision; ++i)
4218 digits.append(s: zero);
4219 break;
4220 case PMChopTrailingZeros:
4221 Q_ASSERT(digits.size() / digitWidth <= qMax(decpt, 1) || !digits.endsWith(zero));
4222 break;
4223 }
4224
4225 if (mustMarkDecimal || decpt < digits.size() / digitWidth)
4226 digits.insert(i: decpt * digitWidth, s: decimalPoint());
4227
4228 if (groupDigits) {
4229 const QLocaleData::GroupSizes grouping = groupSizes();
4230 const QString group = groupSeparator();
4231 qsizetype i = decpt - grouping.least;
4232 if (i >= grouping.first) {
4233 digits.insert(i: i * digitWidth, s: group);
4234 while ((i -= grouping.higher) > 0)
4235 digits.insert(i: i * digitWidth, s: group);
4236 }
4237 }
4238
4239 if (decpt == 0)
4240 digits.prepend(s: zero);
4241
4242 return std::move(digits);
4243}
4244
4245QString QLocaleData::exponentForm(QString &&digits, int decpt, int precision,
4246 PrecisionMode pm, bool mustMarkDecimal,
4247 int minExponentDigits) const
4248{
4249 const QString zero = zeroDigit();
4250 const auto digitWidth = zero.size();
4251 Q_ASSERT(digitWidth == 1 || digitWidth == 2);
4252 Q_ASSERT(digits.size() % digitWidth == 0);
4253
4254 switch (pm) {
4255 case PMDecimalDigits:
4256 for (qsizetype i = digits.size() / digitWidth; i < precision + 1; ++i)
4257 digits.append(s: zero);
4258 break;
4259 case PMSignificantDigits:
4260 for (qsizetype i = digits.size() / digitWidth; i < precision; ++i)
4261 digits.append(s: zero);
4262 break;
4263 case PMChopTrailingZeros:
4264 Q_ASSERT(digits.size() / digitWidth <= 1 || !digits.endsWith(zero));
4265 break;
4266 }
4267
4268 if (mustMarkDecimal || digits.size() > digitWidth)
4269 digits.insert(i: digitWidth, s: decimalPoint());
4270
4271 digits.append(s: exponentSeparator());
4272 digits.append(s: longLongToString(l: decpt - 1, precision: minExponentDigits, base: 10, width: -1, flags: AlwaysShowSign));
4273
4274 return std::move(digits);
4275}
4276
4277QString QLocaleData::signPrefix(bool negative, unsigned flags) const
4278{
4279 if (negative)
4280 return negativeSign();
4281 if (flags & AlwaysShowSign)
4282 return positiveSign();
4283 if (flags & BlankBeforePositive)
4284 return u" "_s;
4285 return {};
4286}
4287
4288QString QLocaleData::longLongToString(qlonglong n, int precision,
4289 int base, int width, unsigned flags) const
4290{
4291 bool negative = n < 0;
4292
4293 /*
4294 Negating std::numeric_limits<qlonglong>::min() hits undefined behavior, so
4295 taking an absolute value has to take a slight detour.
4296 */
4297 QString numStr = qulltoa(l: negative ? 1u + qulonglong(-(n + 1)) : qulonglong(n),
4298 base, zero: zeroDigit());
4299
4300 return applyIntegerFormatting(numStr: std::move(numStr), negative, precision, base, width, flags);
4301}
4302
4303QString QLocaleData::unsLongLongToString(qulonglong l, int precision,
4304 int base, int width, unsigned flags) const
4305{
4306 return applyIntegerFormatting(numStr: qulltoa(l, base, zero: zeroDigit()),
4307 negative: false, precision, base, width, flags);
4308}
4309
4310QString QLocaleData::applyIntegerFormatting(QString &&numStr, bool negative, int precision,
4311 int base, int width, unsigned flags) const
4312{
4313 const QString zero = base == 10 ? zeroDigit() : QStringLiteral("0");
4314 const auto digitWidth = zero.size();
4315 const auto digitCount = numStr.size() / digitWidth;
4316
4317 const auto basePrefix = [&] () -> QStringView {
4318 if (flags & ShowBase) {
4319 const bool upper = flags & UppercaseBase;
4320 if (base == 16)
4321 return upper ? u"0X" : u"0x";
4322 if (base == 2)
4323 return upper ? u"0B" : u"0b";
4324 if (base == 8 && !numStr.startsWith(s: zero))
4325 return zero;
4326 }
4327 return {};
4328 }();
4329
4330 const QString prefix = signPrefix(negative, flags) + basePrefix;
4331 // Count how much of width we've used up. Each digit counts as one
4332 qsizetype usedWidth = digitCount + prefix.size();
4333
4334 if (base == 10 && flags & GroupDigits) {
4335 const QLocaleData::GroupSizes grouping = groupSizes();
4336 const QString group = groupSeparator();
4337 qsizetype i = digitCount - grouping.least;
4338 if (i >= grouping.first) {
4339 numStr.insert(i: i * digitWidth, s: group);
4340 ++usedWidth;
4341 while ((i -= grouping.higher) > 0) {
4342 numStr.insert(i: i * digitWidth, s: group);
4343 ++usedWidth;
4344 }
4345 }
4346 // TODO: should we group any zero-padding we add later ?
4347 }
4348
4349 const bool noPrecision = precision == -1;
4350 if (noPrecision)
4351 precision = 1;
4352
4353 for (qsizetype i = numStr.size(); i < precision; ++i) {
4354 numStr.prepend(s: zero);
4355 usedWidth++;
4356 }
4357
4358 // LeftAdjusted overrides ZeroPadded; and sprintf() only pads when
4359 // precision is not specified in the format string.
4360 if (noPrecision && flags & ZeroPadded && !(flags & LeftAdjusted)) {
4361 for (qsizetype i = usedWidth; i < width; ++i)
4362 numStr.prepend(s: zero);
4363 }
4364
4365 QString result(flags & CapitalEorX ? std::move(numStr).toUpper() : std::move(numStr));
4366 if (prefix.size())
4367 result.prepend(s: prefix);
4368 return result;
4369}
4370
4371inline QLocaleData::NumericData QLocaleData::numericData(QLocaleData::NumberMode mode) const
4372{
4373 NumericData result;
4374 if (this == c()) {
4375 result.isC = true;
4376 return result;
4377 }
4378 result.setZero(zero().viewData(table: single_character_data));
4379 result.group = groupDelim().viewData(table: single_character_data);
4380 // Note: minus, plus and exponent might not actually be single characters.
4381 result.minus = minus().viewData(table: single_character_data);
4382 result.plus = plus().viewData(table: single_character_data);
4383 if (mode != IntegerMode)
4384 result.decimal = decimalSeparator().viewData(table: single_character_data);
4385 if (mode == DoubleScientificMode) {
4386 result.exponent = exponential().viewData(table: single_character_data);
4387 // exponentCyrillic means "apply the Cyrrilic-specific exponent hack"
4388 result.exponentCyrillic = m_script_id == QLocale::CyrillicScript;
4389 }
4390#ifndef QT_NO_SYSTEMLOCALE
4391 if (this == &systemLocaleData) {
4392 const auto getString = [sys = systemLocale()](QSystemLocale::QueryType query) {
4393 return sys->query(type: query).toString();
4394 };
4395 if (mode != IntegerMode) {
4396 result.sysDecimal = getString(QSystemLocale::DecimalPoint);
4397 if (result.sysDecimal.size())
4398 result.decimal = QStringView{result.sysDecimal};
4399 }
4400 result.sysGroup = getString(QSystemLocale::GroupSeparator);
4401 if (result.sysGroup.size())
4402 result.group = QStringView{result.sysGroup};
4403 result.sysMinus = getString(QSystemLocale::NegativeSign);
4404 if (result.sysMinus.size())
4405 result.minus = QStringView{result.sysMinus};
4406 result.sysPlus = getString(QSystemLocale::PositiveSign);
4407 if (result.sysPlus.size())
4408 result.plus = QStringView{result.sysPlus};
4409 result.setZero(getString(QSystemLocale::ZeroDigit));
4410 }
4411#endif
4412
4413 return result;
4414}
4415
4416namespace {
4417// A bit like QStringIterator but rather specialized ... and some of the tokens
4418// it recognizes aren't single Unicode code-points (but it does map each to a
4419// single character).
4420class NumericTokenizer
4421{
4422 // TODO: use deterministic finite-state-automata.
4423 // TODO QTBUG-95460: CLDR has Inf/NaN representations per locale.
4424 static constexpr char lettersInfNaN[] = "afin"; // Letters of Inf, NaN
4425 static constexpr auto matchInfNaN = QtPrivate::makeCharacterSetMatch<lettersInfNaN>();
4426 const QStringView m_text;
4427 const QLocaleData::NumericData m_guide;
4428 qsizetype m_index = 0;
4429 const QLocaleData::NumberMode m_mode;
4430 static_assert('+' + 1 == ',' && ',' + 1 == '-' && '-' + 1 == '.');
4431 char lastMark; // C locale accepts '+' through lastMark.
4432public:
4433 NumericTokenizer(QStringView text, QLocaleData::NumericData &&guide,
4434 QLocaleData::NumberMode mode)
4435 : m_text(text), m_guide(guide), m_mode(mode),
4436 lastMark(mode == QLocaleData::IntegerMode ? '-' : '.')
4437 {
4438 Q_ASSERT(m_guide.isValid(mode));
4439 }
4440 bool done() const { return !(m_index < m_text.size()); }
4441 qsizetype index() const { return m_index; }
4442 inline int asBmpDigit(char16_t digit) const;
4443 inline bool isInfNanChar(char ch) const { return matchInfNaN.matches(c: ch); }
4444 char nextToken();
4445 bool fractionGroupClash() const
4446 {
4447 // If the user's hand-configuration of the system makes group and
4448 // fractional part separators coincide, we have some kludges to apply,
4449 // though we can skip them in integer mode.
4450 return Q_UNLIKELY(m_mode != QLocaleData::IntegerMode && m_guide.group == m_guide.decimal);
4451 }
4452};
4453
4454int NumericTokenizer::asBmpDigit(char16_t digit) const
4455{
4456 // If digit *is* a digit, result will be in range 0 through 9; otherwise not.
4457 // Must match qlocale_tools.h's unicodeForDigit()
4458 if (m_guide.zeroUcs != u'\u3007' || digit == m_guide.zeroUcs)
4459 return digit - m_guide.zeroUcs;
4460
4461 // QTBUG-85409: Suzhou's digits aren't contiguous !
4462 if (digit == u'\u3020') // U+3020 POSTAL MARK FACE is not a digit.
4463 return -1;
4464 // ... but is followed by digits 1 through 9.
4465 return digit - u'\u3020';
4466}
4467
4468char NumericTokenizer::nextToken()
4469{
4470 // As long as caller stops iterating on a zero return, those don't need to
4471 // keep m_index correctly updated.
4472 Q_ASSERT(!done());
4473 // Mauls non-letters above 'Z' but we don't care:
4474 const auto asciiLower = [](unsigned char c) { return c >= 'A' ? c | 0x20 : c; };
4475 const QStringView tail = m_text.sliced(pos: m_index);
4476 const QChar ch = tail.front();
4477 if (ch == u'\u2212') {
4478 // Special case: match the "proper" minus sign, for all locales.
4479 ++m_index;
4480 return '-';
4481 }
4482 if (m_guide.isC) {
4483 // "Conversion" to C locale is just a filter:
4484 ++m_index;
4485 if (Q_LIKELY(ch.unicode() < 256)) {
4486 unsigned char ascii = asciiLower(ch.toLatin1());
4487 if (Q_LIKELY(isAsciiDigit(ascii) || ('+' <= ascii && ascii <= lastMark)
4488 // No caller presently (6.5) passes DoubleStandardMode,
4489 // so !IntegerMode implies scientific, for now.
4490 || (m_mode != QLocaleData::IntegerMode && isInfNanChar(ascii))
4491 || (m_mode == QLocaleData::DoubleScientificMode && ascii == 'e'))) {
4492 return ascii;
4493 }
4494 }
4495 return 0;
4496 }
4497 if (ch.unicode() < 256) {
4498 // Accept the C locale's digits and signs in all locales:
4499 char ascii = asciiLower(ch.toLatin1());
4500 if (isAsciiDigit(c: ascii) || ascii == '-' || ascii == '+'
4501 // Also its Inf and NaN letters:
4502 || (m_mode != QLocaleData::IntegerMode && isInfNanChar(ch: ascii))) {
4503 ++m_index;
4504 return ascii;
4505 }
4506 }
4507
4508 // Other locales may be trickier:
4509 if (tail.startsWith(s: m_guide.minus)) {
4510 m_index += m_guide.minus.size();
4511 return '-';
4512 }
4513 if (tail.startsWith(s: m_guide.plus)) {
4514 m_index += m_guide.plus.size();
4515 return '+';
4516 }
4517 if (!m_guide.group.isEmpty() && tail.startsWith(s: m_guide.group)) {
4518 m_index += m_guide.group.size();
4519 // When group and decimal coincide, and a fractional part is not
4520 // unexpected, treat the last as a fractional part separator (and leave
4521 // the caller to special-case the situations where that causes a
4522 // parse-fail that we can dodge by not reading it that way).
4523 if (fractionGroupClash() && tail.indexOf(s: m_guide.decimal, from: m_guide.group.size()) == -1)
4524 return '.';
4525 return ',';
4526 }
4527 if (m_mode != QLocaleData::IntegerMode && tail.startsWith(s: m_guide.decimal)) {
4528 m_index += m_guide.decimal.size();
4529 return '.';
4530 }
4531 if (m_mode == QLocaleData::DoubleScientificMode
4532 && tail.startsWith(s: m_guide.exponent, cs: Qt::CaseInsensitive)) {
4533 m_index += m_guide.exponent.size();
4534 return 'e';
4535 }
4536
4537 // Must match qlocale_tools.h's unicodeForDigit()
4538 if (m_guide.zeroLen == 1) {
4539 if (!ch.isSurrogate()) {
4540 const int gap = asBmpDigit(digit: ch.unicode());
4541 if (gap >= 0 && gap < 10) {
4542 ++m_index;
4543 return '0' + gap;
4544 }
4545 } else if (ch.isHighSurrogate() && tail.size() > 1 && tail.at(n: 1).isLowSurrogate()) {
4546 return 0;
4547 }
4548 } else if (ch.isHighSurrogate()) {
4549 // None of the corner cases below matches a surrogate, so (update
4550 // already and) return early if we don't have a digit.
4551 if (tail.size() > 1) {
4552 QChar low = tail.at(n: 1);
4553 if (low.isLowSurrogate()) {
4554 m_index += 2;
4555 const uint gap = QChar::surrogateToUcs4(high: ch, low) - m_guide.zeroUcs;
4556 return gap < 10u ? '0' + gap : 0;
4557 }
4558 }
4559 return 0;
4560 }
4561
4562 // All cases where tail starts with properly-matched surrogate pair
4563 // have been handled by this point.
4564 Q_ASSERT(!(ch.isHighSurrogate() && tail.size() > 1 && tail.at(1).isLowSurrogate()));
4565
4566 // Weird corner cases follow (code above assumes these match no surrogates).
4567
4568 // Some locales use a non-breaking space (U+00A0) or its thin version
4569 // (U+202f) for grouping. These look like spaces, so people (and thus some
4570 // of our tests) use a regular space instead and complain if it doesn't
4571 // work.
4572 // Should this be extended generally to any case where group is a space ?
4573 if ((m_guide.group == u"\u00a0" || m_guide.group == u"\u202f") && tail.startsWith(c: u' ')) {
4574 ++m_index;
4575 return ',';
4576 }
4577
4578 // Cyrillic has its own E, used by Ukrainian as exponent; but others
4579 // writing Cyrillic may well use that; and Ukrainians might well use E.
4580 // All other Cyrillic locales (officially) use plain ASCII E.
4581 if (m_guide.exponentCyrillic // Only true in scientific float mode.
4582 && (tail.startsWith(s: u"\u0415", cs: Qt::CaseInsensitive)
4583 || tail.startsWith(s: u"E", cs: Qt::CaseInsensitive))) {
4584 ++m_index;
4585 return 'e';
4586 }
4587
4588 return 0;
4589}
4590} // namespace with no name
4591
4592/*
4593 Converts a number in locale representation to the C locale equivalent.
4594
4595 Only has to guarantee that a string that is a correct representation of a
4596 number will be converted. Checks signs, separators and digits appear in all
4597 the places they should, and nowhere else.
4598
4599 Returns true precisely if the number appears to be well-formed, modulo
4600 things a parser for C Locale strings (without digit-grouping separators;
4601 they're stripped) will catch. When it returns true, it records (and
4602 '\0'-terminates) the C locale representation in *result.
4603
4604 Note: only QString integer-parsing methods have a base parameter (hence need
4605 to cope with letters as possible digits); but these are now all routed via
4606 byteArrayToU?LongLong(), so no longer come via here. The QLocale
4607 number-parsers only work in decimal, so don't have to cope with any digits
4608 other than 0 through 9.
4609*/
4610bool QLocaleData::numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
4611 NumberMode mode, CharBuff *result) const
4612{
4613 s = s.trimmed();
4614 if (s.size() < 1)
4615 return false;
4616 NumericTokenizer tokens(s, numericData(mode), mode);
4617
4618 // Reflects order constraints on possible parts of a number:
4619 enum { Whole, Grouped, Fraction, Exponent, Name } stage = Whole;
4620 // Grouped is just Whole with some digit-grouping separators in it.
4621 // Name is Inf or NaN; excludes all others (so none can be after it).
4622
4623 // Fractional part *or* whole-number part can be empty, but not both, unless
4624 // we have Name. Exponent must have some digits in it.
4625 bool wantDigits = true;
4626
4627 // Digit-grouping details (all modes):
4628 bool needHigherGroup = false; // Set when first group is too short to be the only one
4629 qsizetype digitsInGroup = 0;
4630 const QLocaleData::GroupSizes grouping = groupSizes();
4631 const auto badLeastGroup = [&]() {
4632 // In principle we could object to a complete absence of grouping, when
4633 // digitsInGroup >= qMax(grouping.first, grouping.least), unless the
4634 // locale itself would omit them. However, when merely not rejecting
4635 // grouping separators, we have historically accepted ungrouped digits,
4636 // so objecting now would break existing code.
4637 if (stage == Grouped) {
4638 Q_ASSERT(!number_options.testFlag(QLocale::RejectGroupSeparator));
4639 // First group was invalid if it was short and we've not seen a separator since:
4640 if (needHigherGroup)
4641 return true;
4642 // Were there enough digits since the last group separator?
4643 if (digitsInGroup != grouping.least)
4644 return true;
4645 }
4646 return false;
4647 };
4648
4649 char last = '\0';
4650 while (!tokens.done()) {
4651 char out = tokens.nextToken();
4652 if (out == 0)
4653 return false;
4654
4655 // Note that out can only be '.', 'e' or an inf/NaN character if the
4656 // mode allows it (else nextToken() would return 0 instead), so we don't
4657 // need to check mode.
4658 if (out == '.') {
4659 if (stage > Grouped) // Too late to start a fractional part.
4660 return false;
4661
4662 if (tokens.fractionGroupClash() && badLeastGroup()
4663 && digitsInGroup == grouping.higher) {
4664 // Reinterpret '.' as ',' (as they're indistinguishable) to
4665 // interpret the recent digits as a group, with the least to
4666 // follow (hopefully of a suitable length):
4667 out = ',';
4668 stage = Grouped;
4669 needHigherGroup = false;
4670 digitsInGroup = 0;
4671 } else {
4672 // That's the end of the integral part - check size of last group:
4673 if (badLeastGroup())
4674 return false;
4675 stage = Fraction;
4676 }
4677 } else if (out == 'e') {
4678 if (wantDigits || stage == Name || stage > Fraction)
4679 return false;
4680
4681 if (stage < Fraction) {
4682 // The 'e' ends the whole-number part, so check its last group:
4683 if (badLeastGroup())
4684 return false;
4685 } else if (number_options.testFlag(flag: QLocale::RejectTrailingZeroesAfterDot)) {
4686 // In a fractional part, a 0 just before the exponent is trailing:
4687 if (last == '0')
4688 return false;
4689 }
4690 stage = Exponent;
4691 wantDigits = true; // We need some in the exponent
4692 } else if (out == ',') {
4693 // (If tokens.fractionGroupClash(), a comma only comes out of
4694 // nextToken() if there's a later separator, since the last is
4695 // always treated as dot. So if we have a comma here, treating it as
4696 // a dot wouldn't save the parse: the later dot-or-comma would make
4697 // the text malformed.)
4698 if (number_options.testFlag(flag: QLocale::RejectGroupSeparator))
4699 return false;
4700
4701 switch (stage) {
4702 case Whole:
4703 // Check size of most significant group
4704 if (digitsInGroup == 0
4705 || digitsInGroup > qMax(a: grouping.first, b: grouping.higher)) {
4706 return false;
4707 }
4708 Q_ASSERT(!needHigherGroup);
4709 // First group is only allowed fewer than grouping.first digits
4710 // if it's followed by a grouping.higher group, i.e. there's a
4711 // later group separator:
4712 if (grouping.first > digitsInGroup)
4713 needHigherGroup = true;
4714 stage = Grouped;
4715 break;
4716 case Grouped:
4717 // Check size of group between two separators:
4718 if (digitsInGroup != grouping.higher)
4719 return false;
4720 needHigherGroup = false; // We just found it, if needed.
4721 break;
4722 // Only allow group chars within the whole-number part:
4723 case Fraction:
4724 case Exponent:
4725 case Name:
4726 return false;
4727 }
4728 digitsInGroup = 0;
4729 } else if (isAsciiDigit(c: out)) {
4730 if (stage == Name)
4731 return false;
4732 if (out == '0' && number_options.testFlag(flag: QLocale::RejectLeadingZeroInExponent)
4733 && stage > Fraction && !tokens.done() && !isAsciiDigit(c: last)) {
4734 // After the exponent there can only be '+', '-' or digits. If
4735 // we find a '0' directly after some non-digit, then that is a
4736 // leading zero, acceptable only if it is the whole exponent.
4737 return false;
4738 }
4739 wantDigits = false;
4740 ++digitsInGroup;
4741 } else if (stage == Whole && tokens.isInfNanChar(ch: out)) {
4742 if (!wantDigits) // Mixed digits with Inf/NaN
4743 return false;
4744 wantDigits = false;
4745 stage = Name;
4746 }
4747 // else: nothing special to do.
4748
4749 last = out;
4750 if (out != ',') // Leave group separators out of the result.
4751 result->append(t: out);
4752 }
4753 if (wantDigits)
4754 return false;
4755
4756 if (!number_options.testFlag(flag: QLocale::RejectGroupSeparator)) {
4757 // If this is the end of the whole-part, check least significant group:
4758 if (stage < Fraction && badLeastGroup())
4759 return false;
4760 }
4761
4762 if (number_options.testFlag(flag: QLocale::RejectTrailingZeroesAfterDot) && stage == Fraction) {
4763 // In the fractional part, a final zero is trailing:
4764 if (last == '0')
4765 return false;
4766 }
4767
4768 return true;
4769}
4770
4771QLocaleData::ParsingResult
4772QLocaleData::validateChars(QStringView str, NumberMode numMode, int decDigits,
4773 QLocale::NumberOptions number_options) const
4774{
4775 ParsingResult result;
4776 result.buff.reserve(sz: str.size());
4777
4778 enum { Whole, Fractional, Exponent } state = Whole;
4779 const bool scientific = numMode == DoubleScientificMode;
4780 NumericTokenizer tokens(str, numericData(mode: numMode), numMode);
4781 char last = '\0';
4782
4783 while (!tokens.done()) {
4784 char c = tokens.nextToken();
4785
4786 if (isAsciiDigit(c)) {
4787 switch (state) {
4788 case Whole:
4789 // Nothing special to do (unless we want to check grouping sizes).
4790 break;
4791 case Fractional:
4792 // If a double has too many digits in its fractional part it is Invalid.
4793 if (decDigits-- == 0)
4794 return {};
4795 break;
4796 case Exponent:
4797 if (!isAsciiDigit(c: last)) {
4798 // This is the first digit in the exponent (there may have beena '+'
4799 // or '-' in before). If it's a zero, the exponent is zero-padded.
4800 if (c == '0' && (number_options & QLocale::RejectLeadingZeroInExponent))
4801 return {};
4802 }
4803 break;
4804 }
4805
4806 } else {
4807 switch (c) {
4808 case '.':
4809 // If an integer has a decimal point, it is Invalid.
4810 // A double can only have one, at the end of its whole-number part.
4811 if (numMode == IntegerMode || state != Whole)
4812 return {};
4813 // Even when decDigits is 0, we do allow the decimal point to be
4814 // present - just as long as no digits follow it.
4815
4816 state = Fractional;
4817 break;
4818
4819 case '+':
4820 case '-':
4821 // A sign can only appear at the start or after the e of scientific:
4822 if (last != '\0' && !(scientific && last == 'e'))
4823 return {};
4824 break;
4825
4826 case ',':
4827 // Grouping is only allowed after a digit in the whole-number portion:
4828 if ((number_options & QLocale::RejectGroupSeparator) || state != Whole
4829 || !isAsciiDigit(c: last)) {
4830 return {};
4831 }
4832 // We could check grouping sizes are correct, but fixup()s are
4833 // probably better off correcting any misplacement instead.
4834 break;
4835
4836 case 'e':
4837 // Only one e is allowed and only in scientific:
4838 if (!scientific || state == Exponent)
4839 return {};
4840 state = Exponent;
4841 break;
4842
4843 default:
4844 // Nothing else can validly appear in a number.
4845 // NumericTokenizer allows letters of "inf" and "nan", but
4846 // validators don't accept those values.
4847 // For anything else, tokens.nextToken() must have returned 0.
4848 Q_ASSERT(!c || c == 'a' || c == 'f' || c == 'i' || c == 'n');
4849 return {};
4850 }
4851 }
4852
4853 last = c;
4854 if (c != ',') // Skip grouping
4855 result.buff.append(t: c);
4856 }
4857
4858 result.state = ParsingResult::Acceptable;
4859
4860 // Intermediate if it ends with any character that requires a digit after
4861 // it to be valid e.g. group separator, sign, or exponent
4862 if (last == ',' || last == '-' || last == '+' || last == 'e')
4863 result.state = ParsingResult::Intermediate;
4864
4865 return result;
4866}
4867
4868double QLocaleData::stringToDouble(QStringView str, bool *ok,
4869 QLocale::NumberOptions number_options) const
4870{
4871 CharBuff buff;
4872 if (!numberToCLocale(s: str, number_options, mode: DoubleScientificMode, result: &buff)) {
4873 if (ok != nullptr)
4874 *ok = false;
4875 return 0.0;
4876 }
4877 auto r = qt_asciiToDouble(num: buff.constData(), numLen: buff.size());
4878 if (ok != nullptr)
4879 *ok = r.ok();
4880 return r.result;
4881}
4882
4883QSimpleParsedNumber<qint64>
4884QLocaleData::stringToLongLong(QStringView str, int base,
4885 QLocale::NumberOptions number_options) const
4886{
4887 CharBuff buff;
4888 if (!numberToCLocale(s: str, number_options, mode: IntegerMode, result: &buff))
4889 return {};
4890
4891 return bytearrayToLongLong(num: QByteArrayView(buff), base);
4892}
4893
4894QSimpleParsedNumber<quint64>
4895QLocaleData::stringToUnsLongLong(QStringView str, int base,
4896 QLocale::NumberOptions number_options) const
4897{
4898 CharBuff buff;
4899 if (!numberToCLocale(s: str, number_options, mode: IntegerMode, result: &buff))
4900 return {};
4901
4902 return bytearrayToUnsLongLong(num: QByteArrayView(buff), base);
4903}
4904
4905static bool checkParsed(QByteArrayView num, qsizetype used)
4906{
4907 if (used <= 0)
4908 return false;
4909
4910 const qsizetype len = num.size();
4911 if (used < len && num[used] != '\0') {
4912 while (used < len && ascii_isspace(c: num[used]))
4913 ++used;
4914 }
4915
4916 if (used < len && num[used] != '\0')
4917 // we stopped at a non-digit character after converting some digits
4918 return false;
4919
4920 return true;
4921}
4922
4923QSimpleParsedNumber<qint64> QLocaleData::bytearrayToLongLong(QByteArrayView num, int base)
4924{
4925 auto r = qstrntoll(nptr: num.data(), size: num.size(), base);
4926 if (!checkParsed(num, used: r.used))
4927 return {};
4928 return r;
4929}
4930
4931QSimpleParsedNumber<quint64> QLocaleData::bytearrayToUnsLongLong(QByteArrayView num, int base)
4932{
4933 auto r = qstrntoull(nptr: num.data(), size: num.size(), base);
4934 if (!checkParsed(num, used: r.used))
4935 return {};
4936 return r;
4937}
4938
4939/*!
4940 \since 4.8
4941
4942 \enum QLocale::CurrencySymbolFormat
4943
4944 Specifies the format of the currency symbol.
4945
4946 \value CurrencyIsoCode a ISO-4217 code of the currency.
4947 \value CurrencySymbol a currency symbol.
4948 \value CurrencyDisplayName a user readable name of the currency.
4949*/
4950
4951/*!
4952 \since 4.8
4953 Returns a currency symbol according to the \a format.
4954*/
4955QString QLocale::currencySymbol(CurrencySymbolFormat format) const
4956{
4957#ifndef QT_NO_SYSTEMLOCALE
4958 if (d->m_data == &systemLocaleData) {
4959 auto res = systemLocale()->query(type: QSystemLocale::CurrencySymbol, in: format).toString();
4960 if (!res.isEmpty())
4961 return res;
4962 }
4963#endif
4964 switch (format) {
4965 case CurrencySymbol:
4966 return d->m_data->currencySymbol().getData(table: currency_symbol_data);
4967 case CurrencyDisplayName:
4968 return d->m_data->currencyDisplayName().getData(table: currency_display_name_data);
4969 case CurrencyIsoCode: {
4970 const char *code = d->m_data->m_currency_iso_code;
4971 if (auto len = qstrnlen(str: code, maxlen: 3))
4972 return QString::fromLatin1(str: code, size: qsizetype(len));
4973 break;
4974 }
4975 }
4976 return QString();
4977}
4978
4979/*!
4980 \since 4.8
4981
4982 Returns a localized string representation of \a value as a currency.
4983 If the \a symbol is provided it is used instead of the default currency symbol.
4984
4985 \sa currencySymbol()
4986*/
4987QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const
4988{
4989#ifndef QT_NO_SYSTEMLOCALE
4990 if (d->m_data == &systemLocaleData) {
4991 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
4992 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
4993 in: QVariant::fromValue(value: arg)).toString();
4994 if (!res.isEmpty())
4995 return res;
4996 }
4997#endif
4998 QLocaleData::DataRange range = d->m_data->currencyFormatNegative();
4999 if (!range.size || value >= 0)
5000 range = d->m_data->currencyFormat();
5001 else
5002 value = -value;
5003 QString str = toString(i: value);
5004 QString sym = symbol.isNull() ? currencySymbol() : symbol;
5005 if (sym.isEmpty())
5006 sym = currencySymbol(format: CurrencyIsoCode);
5007 return range.viewData(table: currency_format_data).arg(args&: str, args&: sym);
5008}
5009
5010/*!
5011 \since 4.8
5012 \overload
5013*/
5014QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const
5015{
5016#ifndef QT_NO_SYSTEMLOCALE
5017 if (d->m_data == &systemLocaleData) {
5018 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
5019 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
5020 in: QVariant::fromValue(value: arg)).toString();
5021 if (!res.isEmpty())
5022 return res;
5023 }
5024#endif
5025 QString str = toString(i: value);
5026 QString sym = symbol.isNull() ? currencySymbol() : symbol;
5027 if (sym.isEmpty())
5028 sym = currencySymbol(format: CurrencyIsoCode);
5029 return d->m_data->currencyFormat().getData(table: currency_format_data).arg(args&: str, args&: sym);
5030}
5031
5032/*!
5033 \since 5.7
5034 \overload toCurrencyString()
5035
5036 Returns a localized string representation of \a value as a currency.
5037 If the \a symbol is provided it is used instead of the default currency symbol.
5038 If the \a precision is provided it is used to set the precision of the currency value.
5039
5040 \sa currencySymbol()
5041 */
5042QString QLocale::toCurrencyString(double value, const QString &symbol, int precision) const
5043{
5044#ifndef QT_NO_SYSTEMLOCALE
5045 if (d->m_data == &systemLocaleData) {
5046 QSystemLocale::CurrencyToStringArgument arg(value, symbol);
5047 auto res = systemLocale()->query(type: QSystemLocale::CurrencyToString,
5048 in: QVariant::fromValue(value: arg)).toString();
5049 if (!res.isEmpty())
5050 return res;
5051 }
5052#endif
5053 QLocaleData::DataRange range = d->m_data->currencyFormatNegative();
5054 if (!range.size || value >= 0)
5055 range = d->m_data->currencyFormat();
5056 else
5057 value = -value;
5058 QString str = toString(f: value, format: 'f', precision: precision == -1 ? d->m_data->m_currency_digits : precision);
5059 QString sym = symbol.isNull() ? currencySymbol() : symbol;
5060 if (sym.isEmpty())
5061 sym = currencySymbol(format: CurrencyIsoCode);
5062 return range.viewData(table: currency_format_data).arg(args&: str, args&: sym);
5063}
5064
5065/*!
5066 \fn QString QLocale::toCurrencyString(float i, const QString &symbol, int precision) const
5067 \overload toCurrencyString()
5068*/
5069
5070/*!
5071 \since 5.10
5072
5073 \enum QLocale::DataSizeFormat
5074
5075 Specifies the format for representation of data quantities.
5076
5077 \omitvalue DataSizeBase1000
5078 \omitvalue DataSizeSIQuantifiers
5079 \value DataSizeIecFormat format using base 1024 and IEC prefixes: KiB, MiB, GiB, ...
5080 \value DataSizeTraditionalFormat format using base 1024 and SI prefixes: kB, MB, GB, ...
5081 \value DataSizeSIFormat format using base 1000 and SI prefixes: kB, MB, GB, ...
5082
5083 \sa formattedDataSize()
5084*/
5085
5086/*!
5087 \since 5.10
5088
5089 Converts a size in bytes to a human-readable localized string, comprising a
5090 number and a quantified unit. The quantifier is chosen such that the number
5091 is at least one, and as small as possible. For example if \a bytes is
5092 16384, \a precision is 2, and \a format is \l DataSizeIecFormat (the
5093 default), this function returns "16.00 KiB"; for 1330409069609 bytes it
5094 returns "1.21 GiB"; and so on. If \a format is \l DataSizeIecFormat or
5095 \l DataSizeTraditionalFormat, the given number of bytes is divided by a
5096 power of 1024, with result less than 1024; for \l DataSizeSIFormat, it is
5097 divided by a power of 1000, with result less than 1000.
5098 \c DataSizeIecFormat uses the new IEC standard quantifiers Ki, Mi and so on,
5099 whereas \c DataSizeSIFormat uses the older SI quantifiers k, M, etc., and
5100 \c DataSizeTraditionalFormat abuses them.
5101*/
5102QString QLocale::formattedDataSize(qint64 bytes, int precision, DataSizeFormats format) const
5103{
5104 int power, base = 1000;
5105 if (!bytes) {
5106 power = 0;
5107 } else if (format & DataSizeBase1000) {
5108 constexpr auto log10_1000 = 3; // std::log10(1000U)
5109 power = int(std::log10(x: QtPrivate::qUnsignedAbs(t: bytes))) / log10_1000;
5110 } else {
5111 constexpr auto log2_1024 = 10; // QtPrivate::log2i(1024U);
5112 power = QtPrivate::log2i(x: QtPrivate::qUnsignedAbs(t: bytes)) / log2_1024;
5113 base = 1024;
5114 }
5115 // Only go to doubles if we'll be using a quantifier:
5116 const QString number = power
5117 ? toString(f: bytes / std::pow(x: double(base), y: power), format: 'f', precision: qMin(a: precision, b: 3 * power))
5118 : toString(i: bytes);
5119
5120 // We don't support sizes in units larger than exbibytes because
5121 // the number of bytes would not fit into qint64.
5122 Q_ASSERT(power <= 6 && power >= 0);
5123 QStringView unit;
5124 if (power > 0) {
5125 QLocaleData::DataRange range = (format & DataSizeSIQuantifiers)
5126 ? d->m_data->byteAmountSI() : d->m_data->byteAmountIEC();
5127 unit = range.viewListEntry(table: byte_unit_data, index: power - 1);
5128 } else {
5129 unit = d->m_data->byteCount().viewData(table: byte_unit_data);
5130 }
5131
5132 return number + u' ' + unit;
5133}
5134
5135/*!
5136 \since 4.8
5137 \brief List of locale names for use in selecting translations
5138
5139 Each entry in the returned list is the name of a locale suitable to the
5140 user's preferences for what to translate the UI into. Where a name in the
5141 list is composed of several tags, they are joined as indicated by \a
5142 separator. Prior to Qt 6.7 a dash was used as separator.
5143
5144 For example, using the default separator QLocale::TagSeparator::Dash, if the
5145 user has configured their system to use English as used in the USA, the list
5146 would be "en-Latn-US", "en-US", "en-Latn", "en". The order of entries is the
5147 order in which to check for translations; earlier items in the list are to
5148 be preferred over later ones. If your translation files (or other resources
5149 specific to locale) use underscores, rather than dashes, to separate locale
5150 tags, pass QLocale::TagSeparator::Underscore as \a separator.
5151
5152 Returns a list of locale names. This may include multiple languages,
5153 especially for the system locale when multiple UI translation languages are
5154 configured. The order of entries is significant. For example, for the system
5155 locale, it reflects user preferences.
5156
5157 Prior to Qt 6.9, the list only contained explicitly configured locales and
5158 their equivalents. This led some callers to add truncations (such as from
5159 'en-Latn-DE' to 'en') as fallbacks. This could sometimes result in
5160 inappropriate choices, especially if these were tried before later entries
5161 that would be more appropriate fallbacks.
5162
5163 Starting from Qt 6.9, reasonable truncations are included in the returned
5164 list \e after all entries equivalent to the explicitly specified
5165 locales. This change allows for more accurate fallback options without
5166 callers needing to do any truncation.
5167
5168 Users can explicitly include preferred fallback locales (such as en-US) in
5169 their system configuration to control the order of preference. You are
5170 advised to rely on the order of entries in uiLanguages() rather than using
5171 custom fallback methods.
5172
5173 Most likely you do not need to use this function directly, but just pass the
5174 QLocale object to the QTranslator::load() function.
5175
5176 \sa QTranslator, bcp47Name()
5177*/
5178QStringList QLocale::uiLanguages(TagSeparator separator) const
5179{
5180 const char sep = char(separator);
5181 QStringList uiLanguages;
5182 if (uchar(sep) > 0x7f) {
5183 badSeparatorWarning(method: "uiLanguages", sep);
5184 return uiLanguages;
5185 }
5186 QList<QLocaleId> localeIds;
5187#ifdef QT_NO_SYSTEMLOCALE
5188 constexpr bool isSystem = false;
5189#else
5190 const bool isSystem = d->m_data == &systemLocaleData;
5191 if (isSystem) {
5192 uiLanguages = systemLocale()->query(type: QSystemLocale::UILanguages).toStringList();
5193 if (separator != TagSeparator::Dash) {
5194 // Map from default separator, Dash, used by backends:
5195 const QChar join = QLatin1Char(sep);
5196 uiLanguages.replaceInStrings(before: u"-", after: QStringView(&join, 1));
5197 }
5198 // ... but we need to include likely-adjusted forms of each of those, too.
5199 // For now, collect up locale Ids representing the entries, for later processing:
5200 for (const auto &entry : std::as_const(t&: uiLanguages))
5201 localeIds.append(t: QLocaleId::fromName(name: entry));
5202 if (localeIds.isEmpty())
5203 localeIds.append(t: systemLocale()->fallbackLocale().d->m_data->id());
5204 /* Note: Darwin allows entirely independent choice of locale and of
5205 preferred languages, so it's possible the locale implied by
5206 LanguageId, ScriptId and TerritoryId is absent from the UILanguages
5207 list and that this faithfully reflects the user's wishes. None the
5208 less, we include it (if it isn't C) in the list below, after the last
5209 with the same language and script or (if none has) at the end, in
5210 case there is no better option available. (See, QTBUG-104930.)
5211 */
5212 const QString name = QString::fromLatin1(ba: d->m_data->id().name(separator: sep)); // Raw name
5213 if (!name.isEmpty() && language() != C && !uiLanguages.contains(str: name)) {
5214 // That uses contains(name) as a cheap pre-test, but there may be an
5215 // entry that matches this on purging likely subtags.
5216 const QLocaleId id = d->m_data->id();
5217 const QLocaleId max = id.withLikelySubtagsAdded();
5218 const QLocaleId mine = max.withLikelySubtagsRemoved();
5219 // Default to putting at the end:
5220 qsizetype lastAlike = uiLanguages.size() - 1;
5221 bool seen = false;
5222 for (qsizetype i = 0; !seen && i < uiLanguages.size(); ++i) {
5223 const auto its = QLocaleId::fromName(name: uiLanguages.at(i)).withLikelySubtagsAdded();
5224 seen = its.withLikelySubtagsRemoved() == mine;
5225 if (!seen && its.language_id == max.language_id && its.script_id == max.script_id)
5226 lastAlike = i;
5227 }
5228 if (!seen) {
5229 localeIds.insert(i: lastAlike + 1, t: id);
5230 uiLanguages.insert(i: lastAlike + 1, t: QString::fromLatin1(ba: id.name(separator: sep)));
5231 }
5232 }
5233 } else
5234#endif
5235 {
5236 localeIds.append(t: d->m_data->id());
5237 }
5238
5239 for (qsizetype i = localeIds.size(); i-- > 0; ) {
5240 const QLocaleId id = localeIds.at(i);
5241 Q_ASSERT(id.language_id);
5242 if (id.language_id == C) {
5243 if (!uiLanguages.contains(str: u"C"_s))
5244 uiLanguages.append(t: u"C"_s);
5245 // Attempt no likely sub-tag amendments to C.
5246 continue;
5247 }
5248
5249 qsizetype j;
5250 const QByteArray prior = id.name(separator: sep);
5251 bool faithful = true; // prior matches uiLanguages.at(j - 1)
5252 if (isSystem && i < uiLanguages.size()) {
5253 // Adding likely-adjusted forms to system locale's list.
5254 faithful = uiLanguages.at(i) == QLatin1StringView(prior);
5255 Q_ASSERT(faithful
5256 // A legacy code may get mapped to an ID with a different name:
5257 || QLocaleId::fromName(uiLanguages.at(i)).name(sep) == prior);
5258 // Insert just after the entry we're supplementing:
5259 j = i + 1;
5260 } else {
5261 // Plain locale or empty system uiLanguages; just append.
5262 if (!uiLanguages.contains(str: QLatin1StringView(prior)))
5263 uiLanguages.append(t: QString::fromLatin1(ba: prior));
5264 j = uiLanguages.size();
5265 }
5266
5267 const QLocaleId max = id.withLikelySubtagsAdded();
5268 Q_ASSERT(max.language_id);
5269 Q_ASSERT(max.language_id == id.language_id);
5270 // We can't say the same for script or territory, though.
5271
5272 // We have various candidates to consider.
5273 const auto addIfEquivalent = [&j, &uiLanguages, max, sep, &prior, faithful](QLocaleId cid) {
5274 if (cid.withLikelySubtagsAdded() == max) {
5275 if (const QByteArray name = cid.name(separator: sep); name != prior)
5276 uiLanguages.insert(i: j, t: QString::fromLatin1(ba: name));
5277 else if (faithful) // Later candidates are more specific, so go before.
5278 --j;
5279 }
5280 };
5281 // language
5282 addIfEquivalent({ .language_id: max.language_id, .script_id: 0, .territory_id: 0 });
5283 // language-script
5284 if (max.script_id)
5285 addIfEquivalent({ .language_id: max.language_id, .script_id: max.script_id, .territory_id: 0 });
5286 if (id.script_id && id.script_id != max.script_id)
5287 addIfEquivalent({ .language_id: id.language_id, .script_id: id.script_id, .territory_id: 0 });
5288 // language-territory
5289 if (max.territory_id)
5290 addIfEquivalent({ .language_id: max.language_id, .script_id: 0, .territory_id: max.territory_id });
5291 if (id.territory_id && id.territory_id != max.territory_id)
5292 addIfEquivalent({ .language_id: id.language_id, .script_id: 0, .territory_id: id.territory_id });
5293 // full
5294 if (max.territory_id && max.script_id)
5295 addIfEquivalent(max);
5296 if (max.territory_id && id.script_id && id.script_id != max.script_id)
5297 addIfEquivalent({ .language_id: id.language_id, .script_id: id.script_id, .territory_id: max.territory_id });
5298 if (max.script_id && id.territory_id && id.territory_id != max.territory_id)
5299 addIfEquivalent({ .language_id: id.language_id, .script_id: max.script_id, .territory_id: id.territory_id });
5300 if (id.territory_id && id.territory_id != max.territory_id
5301 && id.script_id && id.script_id != max.script_id) {
5302 addIfEquivalent(id);
5303 }
5304 }
5305
5306 // Second pass: deduplicate.
5307 // Can't use QStringList::removeDuplicates() here, because we still need
5308 // the QDuplicateTracker, later.
5309 QDuplicateTracker<QString> known(uiLanguages.size());
5310 uiLanguages.removeIf(pred: [&](const QString &s) { return known.hasSeen(s); });
5311
5312 // Third pass: add truncations, when not already present.
5313 // Cubic in list length, but hopefully that's at most a dozen or so.
5314 const QLatin1Char cut(sep);
5315 const auto hasPrefix = [cut](auto name, QStringView stem) {
5316 // A prefix only counts if it's either full or followed by a separator.
5317 return name.startsWith(stem)
5318 && (name.size() == stem.size() || name.at(stem.size()) == cut);
5319 };
5320 // As we now forward-traverse the list, we need to keep track of the
5321 // positions just after (a) the block of things added above that are
5322 // equivalent to the current entry and (b) the block of truncations (if any)
5323 // added just after this block. All truncations of entries in (a) belong at
5324 // the end of (b); once i advances to the end of (a) it must jump to just
5325 // after (b). The more specific entries in (a) may well have truncations
5326 // that can also arise from less specific ones later in (a); for the
5327 // purposes of determining whether such truncations go at the end of (b) or
5328 // the end of the list, we thus need to ignore these matches.
5329 qsizetype afterEquivs = 0;
5330 qsizetype afterTruncs = 0;
5331 // From here onwards, we only have the truncations we're adding, whose
5332 // truncations should all have been included already.
5333 // If advancing i brings us to the end of block (a), jump to the end of (b):
5334 for (qsizetype i = 0; i < uiLanguages.size(); ++i >= afterEquivs && (i = afterTruncs)) {
5335 const QString entry = uiLanguages.at(i);
5336 const QLocaleId max = QLocaleId::fromName(name: entry).withLikelySubtagsAdded();
5337 // Keep track of our two blocks:
5338 if (i >= afterEquivs) {
5339 Q_ASSERT(i >= afterTruncs); // i.e. we just skipped past the end of a block
5340 afterEquivs = i + 1;
5341 // Advance past equivalents of entry:
5342 while (afterEquivs < uiLanguages.size()
5343 && QLocaleId::fromName(name: uiLanguages.at(i: afterEquivs))
5344 .withLikelySubtagsAdded() == max) {
5345 ++afterEquivs;
5346 }
5347 // We'll add any truncations starting there:
5348 afterTruncs = afterEquivs;
5349 }
5350 if (hasPrefix(entry, u"C") || hasPrefix(entry, u"und"))
5351 continue;
5352 qsizetype stopAt = uiLanguages.size();
5353 qsizetype at = entry.size(); // if 0, calls lastIndexOf(cut, -1), which is in-contract
5354 while ((at = entry.lastIndexOf(c: cut, from: at - 1)) > 0) {
5355 QString prefix = entry.first(n: at);
5356 // Don't test with hasSeen() as we might defer adding to later, when
5357 // we'll need known to see the later entry's offering of this prefix
5358 // as a new entry.
5359 bool found = known.contains(s: prefix);
5360 /* By default we append but if no later entry has this as a prefix
5361 and the locale it implies would use the same script as entry, put
5362 it after the block of consecutive equivalents of which entry is a
5363 part instead. Thus [en-NL, nl-NL, en-GB] will append en but
5364 [en-NL, en-GB, nl-NL] will put it before nl-NL, for example. We
5365 require a script match so we don't pick translations that the
5366 user cannot read, despite knowing the language. (Ideally that
5367 would be a constraint the caller can opt into / out of. See
5368 QTBUG-112765.)
5369 */
5370 bool justAfter
5371 = (QLocaleId::fromName(name: prefix).withLikelySubtagsAdded().script_id == max.script_id);
5372 for (qsizetype j = afterTruncs; !found && j < stopAt; ++j) {
5373 QString later = uiLanguages.at(i: j);
5374 if (!later.startsWith(s: prefix)) {
5375 const QByteArray laterFull =
5376 QLocaleId::fromName(name: later.replace(before: cut, after: u'-')
5377 ).withLikelySubtagsAdded().name(separator: sep);
5378 // When prefix matches a later entry's max, it belongs later.
5379 if (hasPrefix(QLatin1StringView(laterFull), prefix))
5380 justAfter = false;
5381 continue;
5382 }
5383 // The duplicate tracker would already have spotted if equal:
5384 Q_ASSERT(later.size() > prefix.size());
5385 if (later.at(i: prefix.size()) == cut) {
5386 justAfter = false;
5387 // Prefix match. Shall produce the same prefix, but possibly
5388 // after prefixes of other entries in the list. If later has
5389 // a longer prefix not yet in the list, we want that before
5390 // this shorter prefix, so leave this for later, otherwise,
5391 // we include this prefix right away.
5392 QStringView head{later};
5393 for (qsizetype as = head.lastIndexOf(c: cut);
5394 !found && as > prefix.size(); as = head.lastIndexOf(c: cut)) {
5395 head = head.first(n: as);
5396 bool seen = false;
5397 for (qsizetype k = j + 1; !seen && k < uiLanguages.size(); ++k)
5398 seen = uiLanguages.at(i: k) == head;
5399 if (!seen)
5400 found = true;
5401 }
5402 }
5403 }
5404 if (found) // Don't duplicate.
5405 continue; // Some shorter truncations may still be missing.
5406 // Now we're committed to adding it, get it into known:
5407 (void) known.hasSeen(s: prefix);
5408 if (justAfter) {
5409 uiLanguages.insert(i: afterTruncs++, t: std::move(prefix));
5410 ++stopAt; // All later entries have moved one step later.
5411 } else {
5412 uiLanguages.append(t: std::move(prefix));
5413 }
5414 }
5415 }
5416
5417 return uiLanguages;
5418}
5419
5420/*!
5421 \since 5.13
5422
5423 Returns the locale to use for collation.
5424
5425 The result is usually this locale; however, the system locale (which is
5426 commonly the default locale) will return the system collation locale.
5427 The result is suitable for passing to QCollator's constructor.
5428
5429 \sa QCollator
5430*/
5431QLocale QLocale::collation() const
5432{
5433#ifndef QT_NO_SYSTEMLOCALE
5434 if (d->m_data == &systemLocaleData) {
5435 const auto res = systemLocale()->query(type: QSystemLocale::Collation).toString();
5436 if (!res.isEmpty())
5437 return QLocale(res);
5438 }
5439#endif
5440 return *this;
5441}
5442
5443/*!
5444 \since 4.8
5445
5446 Returns a native name of the language for the locale. For example
5447 "Schweizer Hochdeutsch" for the Swiss-German locale.
5448
5449 \sa nativeTerritoryName(), languageToString()
5450*/
5451QString QLocale::nativeLanguageName() const
5452{
5453#ifndef QT_NO_SYSTEMLOCALE
5454 if (d->m_data == &systemLocaleData) {
5455 auto res = systemLocale()->query(type: QSystemLocale::NativeLanguageName).toString();
5456 if (!res.isEmpty())
5457 return res;
5458 }
5459#endif
5460 return d->m_data->endonymLanguage().getData(table: endonyms_data);
5461}
5462
5463/*!
5464 \since 6.2
5465
5466 Returns a native name of the territory for the locale. For example
5467 "España" for Spanish/Spain locale.
5468
5469 \sa nativeLanguageName(), territoryToString()
5470*/
5471QString QLocale::nativeTerritoryName() const
5472{
5473#ifndef QT_NO_SYSTEMLOCALE
5474 if (d->m_data == &systemLocaleData) {
5475 auto res = systemLocale()->query(type: QSystemLocale::NativeTerritoryName).toString();
5476 if (!res.isEmpty())
5477 return res;
5478 }
5479#endif
5480 return d->m_data->endonymTerritory().getData(table: endonyms_data);
5481}
5482
5483#if QT_DEPRECATED_SINCE(6, 6)
5484/*!
5485 \deprecated [6.6] Use \l nativeTerritoryName() instead.
5486 \since 4.8
5487
5488 Returns a native name of the territory for the locale. For example
5489 "España" for Spanish/Spain locale.
5490
5491 \sa nativeLanguageName(), territoryToString()
5492*/
5493QString QLocale::nativeCountryName() const
5494{
5495 return nativeTerritoryName();
5496}
5497#endif
5498
5499#ifndef QT_NO_DEBUG_STREAM
5500QDebug operator<<(QDebug dbg, const QLocale &l)
5501{
5502 QDebugStateSaver saver(dbg);
5503 const bool isSys = l == QLocale::system();
5504 dbg.nospace().noquote()
5505 << (isSys ? "QLocale::system()/* " : "QLocale(")
5506 << QLocale::languageToString(language: l.language()) << ", "
5507 << QLocale::scriptToString(script: l.script()) << ", "
5508 << QLocale::territoryToString(territory: l.territory()) << (isSys ? " */" : ")");
5509 return dbg;
5510}
5511#endif
5512QT_END_NAMESPACE
5513
5514#ifndef QT_NO_QOBJECT
5515#include "moc_qlocale.cpp"
5516#endif
5517

source code of qtbase/src/corelib/text/qlocale.cpp