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 
48 QT_BEGIN_NAMESPACE
49 
50 #ifndef QT_NO_SYSTEMLOCALE
51 struct QSystemLocaleData
52 {
QSystemLocaleDataQSystemLocaleData53     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 
readEnvironment()76 void 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 
Q_GLOBAL_STATIC(QSystemLocaleData,qSystemLocaleData)108 Q_GLOBAL_STATIC(QSystemLocaleData, qSystemLocaleData)
109 
110 #endif
111 
112 #ifndef QT_NO_SYSTEMLOCALE
113 
114 static 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 
fallbackUiLocale() const141 QLocale 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 
query(QueryType type,QVariant in) const165 QVariant 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<QStringRef>(in));
287     case StringToAlternateQuotation:
288         return lc_messages.quoteString(qvariant_cast<QStringRef>(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 
300 QT_END_NAMESPACE
301