Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlocale_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qlocale_p.h"
6
7#include "qdatetime.h"
8#include "qjniobject.h"
10#include "qstringlist.h"
11#include "qvariant.h"
12
13#include <QtCore/private/qjnihelpers_p.h>
14
16
17#ifndef QT_NO_SYSTEMLOCALE
18
19Q_DECLARE_JNI_CLASS(Locale, "java/util/Locale")
20Q_DECLARE_JNI_CLASS(Resources, "android/content/res/Resources")
21Q_DECLARE_JNI_CLASS(Configuration, "android/content/res/Configuration")
22Q_DECLARE_JNI_CLASS(LocaleList, "android/os/LocaleList")
23Q_DECLARE_JNI_CLASS(DateFormat, "java/text/DateFormat")
24
25namespace {
26
27using namespace Qt::StringLiterals;
28using namespace QtJniTypes;
29
40
42{
45 {
46 QReadLocker locker(&lock);
47 return localeInfo;
48 }
49
52};
53
54// Translate the Java SimpleDateFormat tokens Qt doesn't accept to Qt's
55// (E/EE/EEE → ddd, EEEE → dddd, y/yyy+ → yyyy; a → aP to keep the locale's
56// own AM/PM casing rather than force lower-case). Other tokens pass through.
57// See https://developer.android.com/reference/java/text/SimpleDateFormat
58QString qtPatternFromJava(QStringView pattern)
59{
60 QString out;
61 out.reserve(pattern.size());
62 bool inQuoted = false;
63 while (!pattern.isEmpty()) {
64 const QChar ch = pattern.front();
65 qsizetype consumed = 1;
66 if (ch == u'\'') {
67 inQuoted = !inQuoted;
68 out.append(ch);
69 } else if (inQuoted) {
70 out.append(ch);
71 } else if (ch == u'E' || ch == u'y' || ch == u'a') {
72 while (consumed < pattern.size() && pattern[consumed] == ch)
73 ++consumed;
74 if (ch == u'E')
75 out.append(consumed >= 4 ? "dddd"_L1 : "ddd"_L1);
76 else if (ch == u'y')
77 out.append(consumed == 2 ? "yy"_L1 : "yyyy"_L1);
78 else // 'a'
79 out.append("aP"_L1);
80 } else {
81 out.append(ch);
82 }
83 pattern = pattern.sliced(consumed);
84 }
85 return out;
86}
87
88// Qt pattern via java.text.DateFormat.get{Date,Time}Instance(style, locale).
89QString patternForStyle(QSystemLocale::QueryType type, const Locale &locale)
90{
91 // https://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html
92 constexpr jint SHORT = 3;
93 constexpr jint MEDIUM = 2;
94 constexpr jint FULL = 0;
95
96 const char *factory = nullptr;
97 jint style = 0;
98 switch (type) {
99 case QSystemLocale::DateFormatShort: factory = "getDateInstance"; style = SHORT; break;
100 case QSystemLocale::DateFormatLong: factory = "getDateInstance"; style = FULL; break;
101 case QSystemLocale::TimeFormatShort: factory = "getTimeInstance"; style = SHORT; break;
102 case QSystemLocale::TimeFormatLong: factory = "getTimeInstance"; style = MEDIUM; break;
103 default: Q_UNREACHABLE(); break;
104 }
105
106 const auto dateFormat = DateFormat::callStaticMethod<DateFormat>(factory, style, locale);
107 return qtPatternFromJava(dateFormat.callMethod<QString>("toPattern"));
108}
109
111{
112 const QJniObject context = QtAndroidPrivate::context();
113 const Locale androidLocale = [context]{
114 if (context.isValid()) {
115 const auto resources = context.callMethod<Resources>("getResources");
116 const auto configuration = resources.callMethod<Configuration>("getConfiguration");
117 return configuration.getField<Locale>("locale");
118 }
119 return Locale::callStaticMethod<Locale>("getDefault");
120 }();
121
122 LocaleInfo updated;
123 updated.language = QLocale::codeToLanguage(androidLocale.callMethod<QString>("getLanguage"));
124 updated.script = QLocale::codeToScript(androidLocale.callMethod<QString>("getScript"));
125 updated.territory = QLocale::codeToTerritory(androidLocale.callMethod<QString>("getCountry"));
126 // QLocale does not yet support variants (QTBUG-81051)
127 // updated.variant = androidLocale.callMethod<QString>("getVariant");
128
129 updated.dateFormatShort = patternForStyle(QSystemLocale::DateFormatShort, androidLocale);
130 updated.dateFormatLong = patternForStyle(QSystemLocale::DateFormatLong, androidLocale);
131 updated.timeFormatShort = patternForStyle(QSystemLocale::TimeFormatShort, androidLocale);
132 updated.timeFormatLong = patternForStyle(QSystemLocale::TimeFormatLong, androidLocale);
133 // QTBUG-145605: locale-correct date/time gluing is possible with
134 // java.text.DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale).
135 // QLocale handles DateTimeFormat{Short,Long} gluing, so they are deliberately left out.
136
137 QWriteLocker locker(&lock);
138 localeInfo = updated;
139}
140
141Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData)
142
143} // unnamed namespace
144
145QLocale QSystemLocale::fallbackLocale() const
146{
147 QSystemLocaleData *d = qSystemLocaleData();
148 if (!d)
149 return QLocale(QLocale::C);
150
151 const LocaleInfo info = d->snapshot();
152 return QLocale(info.language, info.script, info.territory);
153}
154
155QVariant QSystemLocale::query(QueryType type, QVariant &&in) const
156{
157 Q_UNUSED(in);
158 QSystemLocaleData *d = qSystemLocaleData();
159 if (!d)
160 return QVariant();
161
162 if (type == LocaleChanged) {
163 d->readLocaleFromJava();
164 return QVariant();
165 }
166
167 const LocaleInfo info = d->snapshot();
168
169 switch (type) {
170 case LanguageId:
171 return QVariant::fromValue(static_cast<int>(info.language));
172 case TerritoryId:
173 return QVariant::fromValue(static_cast<int>(info.territory));
174 case ScriptId:
175 return QVariant::fromValue(static_cast<int>(info.script));
176 case Collation:
177 return QLocale(info.language, info.script, info.territory).name();
178 case TimeFormatShort:
179 return info.timeFormatShort;
180 case TimeFormatLong:
181 return info.timeFormatLong;
182 case DateFormatShort:
183 return info.dateFormatShort;
184 case DateFormatLong:
185 return info.dateFormatLong;
186 case UILanguages: {
187 const LocaleList localeList = LocaleList::callStaticMethod<LocaleList>("getDefault");
188 if (!localeList.isValid())
189 return QVariant();
190 QString tags = localeList.callMethod<QString>("toLanguageTags");
191 // Some devices wrap the list in [] - strip those if present.
192 if (tags.startsWith(u'[') && tags.endsWith(u']'))
193 tags = tags.mid(1, tags.length() - 2);
194 return tags.split(u',');
195 }
196 case LocaleChanged:
197 Q_UNREACHABLE(); // handled before the switch
198 default:
199 break;
200 }
201 return QVariant();
202}
203
204#endif // QT_NO_SYSTEMLOCALE
205
206QT_END_NAMESPACE
Combined button and popup list for selecting options.
QString qtPatternFromJava(QStringView pattern)
QString patternForStyle(QSystemLocale::QueryType type, const Locale &locale)