1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlocale_p.h"
41
42#include "qstringbuilder.h"
43#include "qdatetime.h"
44#include "qstringlist.h"
45#include "qvariant.h"
46#include "qreadwritelock.h"
47
48QT_BEGIN_NAMESPACE
49
50#ifndef QT_NO_SYSTEMLOCALE
51struct QSystemLocaleData
52{
53 QSystemLocaleData()
54 : lc_numeric(QLocale::C)
55 ,lc_time(QLocale::C)
56 ,lc_monetary(QLocale::C)
57 ,lc_messages(QLocale::C)
58 {
59 readEnvironment();
60 }
61
62 void readEnvironment();
63
64 QReadWriteLock lock;
65
66 QLocale lc_numeric;
67 QLocale lc_time;
68 QLocale lc_monetary;
69 QLocale lc_messages;
70 QByteArray lc_messages_var;
71 QByteArray lc_measurement_var;
72 QByteArray lc_collate_var;
73 QStringList uiLanguages;
74};
75
76void QSystemLocaleData::readEnvironment()
77{
78 QWriteLocker locker(&lock);
79
80 QByteArray all = qgetenv("LC_ALL");
81 QByteArray numeric = all.isEmpty() ? qgetenv("LC_NUMERIC") : all;
82 QByteArray time = all.isEmpty() ? qgetenv("LC_TIME") : all;
83 QByteArray monetary = all.isEmpty() ? qgetenv("LC_MONETARY") : all;
84 lc_messages_var = all.isEmpty() ? qgetenv("LC_MESSAGES") : all;
85 lc_measurement_var = all.isEmpty() ? qgetenv("LC_MEASUREMENT") : all;
86 lc_collate_var = all.isEmpty() ? qgetenv("LC_COLLATE") : all;
87 QByteArray lang = qgetenv("LANG");
88 if (lang.isEmpty())
89 lang = QByteArray("C");
90 if (numeric.isEmpty())
91 numeric = lang;
92 if (time.isEmpty())
93 time = lang;
94 if (monetary.isEmpty())
95 monetary = lang;
96 if (lc_messages_var.isEmpty())
97 lc_messages_var = lang;
98 if (lc_measurement_var.isEmpty())
99 lc_measurement_var = lang;
100 if (lc_collate_var.isEmpty())
101 lc_collate_var = lang;
102 lc_numeric = QLocale(QString::fromLatin1(numeric));
103 lc_time = QLocale(QString::fromLatin1(time));
104 lc_monetary = QLocale(QString::fromLatin1(monetary));
105 lc_messages = QLocale(QString::fromLatin1(lc_messages_var));
106}
107
108Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData)
109
110#endif
111
112#ifndef QT_NO_SYSTEMLOCALE
113
114static bool contradicts(const QString &maybe, const QString &known)
115{
116 if (maybe.isEmpty())
117 return false;
118
119 /*
120 If \a known (our current best shot at deciding which language to use)
121 provides more information (e.g. script, country) than \a maybe (a
122 candidate to replace \a known) and \a maybe agrees with \a known in what
123 it does provide, we keep \a known; this happens when \a maybe comes from
124 LANGUAGE (usually a simple language code) and LANG includes script and/or
125 country. A textual comparison won't do because, for example, bn (Bengali)
126 isn't a prefix of ben_IN, but the latter is a refinement of the former.
127 (Meanwhile, bn is a prefix of bnt, Bantu; and a prefix of ben is be,
128 Belarusian. There are many more such prefixings between two- and
129 three-letter codes.)
130 */
131 QLocale::Language langm, langk;
132 QLocale::Script scriptm, scriptk;
133 QLocale::Country landm, landk;
134 QLocalePrivate::getLangAndCountry(maybe, langm, scriptm, landm);
135 QLocalePrivate::getLangAndCountry(known, langk, scriptk, landk);
136 return (langm != QLocale::AnyLanguage && langm != langk)
137 || (scriptm != QLocale::AnyScript && scriptm != scriptk)
138 || (landm != QLocale::AnyCountry && landm != landk);
139}
140
141QLocale QSystemLocale::fallbackUiLocale() const
142{
143 // See man 7 locale for precedence - LC_ALL beats LC_MESSAGES beats LANG:
144 QString lang = qEnvironmentVariable("LC_ALL");
145 if (lang.isEmpty())
146 lang = qEnvironmentVariable("LC_MESSAGES");
147 if (lang.isEmpty())
148 lang = qEnvironmentVariable("LANG");
149 // if the locale is the "C" locale, then we can return the language we found here:
150 if (lang.isEmpty() || lang == QLatin1String("C") || lang == QLatin1String("POSIX"))
151 return QLocale(lang);
152
153 // ... otherwise, if the first part of LANGUAGE says more than or
154 // contradicts what we have, use that:
155 QString language = qEnvironmentVariable("LANGUAGE");
156 if (!language.isEmpty()) {
157 language = language.split(QLatin1Char(':')).constFirst();
158 if (contradicts(language, lang))
159 return QLocale(language);
160 }
161
162 return QLocale(lang);
163}
164
165QVariant QSystemLocale::query(QueryType type, QVariant in) const
166{
167 QSystemLocaleData *d = qSystemLocaleData();
168
169 if (type == LocaleChanged) {
170 d->readEnvironment();
171 return QVariant();
172 }
173
174 QReadLocker locker(&d->lock);
175
176 const QLocale &lc_numeric = d->lc_numeric;
177 const QLocale &lc_time = d->lc_time;
178 const QLocale &lc_monetary = d->lc_monetary;
179 const QLocale &lc_messages = d->lc_messages;
180
181 switch (type) {
182 case DecimalPoint:
183 return lc_numeric.decimalPoint();
184 case GroupSeparator:
185 return lc_numeric.groupSeparator();
186 case ZeroDigit:
187 return lc_numeric.zeroDigit();
188 case NegativeSign:
189 return lc_numeric.negativeSign();
190 case DateFormatLong:
191 return lc_time.dateFormat(QLocale::LongFormat);
192 case DateFormatShort:
193 return lc_time.dateFormat(QLocale::ShortFormat);
194 case TimeFormatLong:
195 return lc_time.timeFormat(QLocale::LongFormat);
196 case TimeFormatShort:
197 return lc_time.timeFormat(QLocale::ShortFormat);
198 case DayNameLong:
199 return lc_time.dayName(in.toInt(), QLocale::LongFormat);
200 case DayNameShort:
201 return lc_time.dayName(in.toInt(), QLocale::ShortFormat);
202 case MonthNameLong:
203 return lc_time.monthName(in.toInt(), QLocale::LongFormat);
204 case MonthNameShort:
205 return lc_time.monthName(in.toInt(), QLocale::ShortFormat);
206 case StandaloneMonthNameLong:
207 return lc_time.standaloneMonthName(in.toInt(), QLocale::LongFormat);
208 case StandaloneMonthNameShort:
209 return lc_time.standaloneMonthName(in.toInt(), QLocale::ShortFormat);
210 case DateToStringLong:
211 return lc_time.toString(in.toDate(), QLocale::LongFormat);
212 case DateToStringShort:
213 return lc_time.toString(in.toDate(), QLocale::ShortFormat);
214 case TimeToStringLong:
215 return lc_time.toString(in.toTime(), QLocale::LongFormat);
216 case TimeToStringShort:
217 return lc_time.toString(in.toTime(), QLocale::ShortFormat);
218 case DateTimeFormatLong:
219 return lc_time.dateTimeFormat(QLocale::LongFormat);
220 case DateTimeFormatShort:
221 return lc_time.dateTimeFormat(QLocale::ShortFormat);
222 case DateTimeToStringLong:
223 return lc_time.toString(in.toDateTime(), QLocale::LongFormat);
224 case DateTimeToStringShort:
225 return lc_time.toString(in.toDateTime(), QLocale::ShortFormat);
226 case PositiveSign:
227 return lc_numeric.positiveSign();
228 case AMText:
229 return lc_time.amText();
230 case PMText:
231 return lc_time.pmText();
232 case FirstDayOfWeek:
233 return lc_time.firstDayOfWeek();
234 case CurrencySymbol:
235 return lc_monetary.currencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()));
236 case CurrencyToString: {
237 switch (in.userType()) {
238 case QMetaType::Int:
239 return lc_monetary.toCurrencyString(in.toInt());
240 case QMetaType::UInt:
241 return lc_monetary.toCurrencyString(in.toUInt());
242 case QMetaType::Double:
243 return lc_monetary.toCurrencyString(in.toDouble());
244 case QMetaType::LongLong:
245 return lc_monetary.toCurrencyString(in.toLongLong());
246 case QMetaType::ULongLong:
247 return lc_monetary.toCurrencyString(in.toULongLong());
248 default:
249 break;
250 }
251 return QString();
252 }
253 case MeasurementSystem: {
254 const QString meas_locale = QString::fromLatin1(d->lc_measurement_var);
255 if (meas_locale.compare(QLatin1String("Metric"), Qt::CaseInsensitive) == 0)
256 return QLocale::MetricSystem;
257 if (meas_locale.compare(QLatin1String("Other"), Qt::CaseInsensitive) == 0)
258 return QLocale::MetricSystem;
259 return QVariant((int)QLocale(meas_locale).measurementSystem());
260 }
261 case Collation:
262 return QString::fromLatin1(d->lc_collate_var);
263 case UILanguages: {
264 if (!d->uiLanguages.isEmpty())
265 return d->uiLanguages;
266 QString languages = QString::fromLatin1(qgetenv("LANGUAGE"));
267 QStringList lst;
268 if (languages.isEmpty())
269 lst.append(QString::fromLatin1(d->lc_messages_var));
270 else
271 lst = languages.split(QLatin1Char(':'));
272
273 for (int i = 0; i < lst.size(); ++i) {
274 const QString &name = lst.at(i);
275 QString lang, script, cntry;
276 if (qt_splitLocaleName(name, lang, script, cntry)) {
277 if (!cntry.length())
278 d->uiLanguages.append(lang);
279 else
280 d->uiLanguages.append(lang % QLatin1Char('-') % cntry);
281 }
282 }
283 return d->uiLanguages.isEmpty() ? QVariant() : QVariant(d->uiLanguages);
284 }
285 case StringToStandardQuotation:
286 return lc_messages.quoteString(qvariant_cast<QStringView>(in));
287 case StringToAlternateQuotation:
288 return lc_messages.quoteString(qvariant_cast<QStringView>(in), QLocale::AlternateQuotation);
289 case ListToSeparatedString:
290 return lc_messages.createSeparatedList(in.toStringList());
291 case LocaleChanged:
292 Q_ASSERT(false);
293 default:
294 break;
295 }
296 return QVariant();
297}
298#endif // QT_NO_SYSTEMLOCALE
299
300QT_END_NAMESPACE
301