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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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