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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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