summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qlocale.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qlocale.cpp')
-rw-r--r--src/corelib/tools/qlocale.cpp165
1 files changed, 136 insertions, 29 deletions
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index 42cb3eda18..2f0e4970b0 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -208,17 +208,94 @@ QString QLocalePrivate::countryCode() const
return code;
}
-QString QLocalePrivate::bcp47Name() const
+// http://www.unicode.org/reports/tr35/#Likely_Subtags
+static bool addLikelySubtags(QLocaleId &localeId)
+{
+ // ### optimize with bsearch
+ const int likely_subtags_count = sizeof(likely_subtags) / sizeof(likely_subtags[0]);
+ const QLocaleId *p = likely_subtags;
+ const QLocaleId *const e = p + likely_subtags_count;
+ for ( ; p < e; p += 2) {
+ if (localeId == p[0]) {
+ localeId = p[1];
+ return true;
+ }
+ }
+ return false;
+}
+
+QLocaleId QLocaleId::withLikelySubtagsAdded() const
{
- if (m_data->m_language_id == QLocale::AnyLanguage)
+ // language_script_region
+ if (language_id || script_id || country_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, script_id, country_id);
+ if (addLikelySubtags(id))
+ return id;
+ }
+ // language_script
+ if (country_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0);
+ if (addLikelySubtags(id)) {
+ id.country_id = country_id;
+ return id;
+ }
+ }
+ // language_region
+ if (script_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, 0, country_id);
+ if (addLikelySubtags(id)) {
+ id.script_id = script_id;
+ return id;
+ }
+ }
+ // language
+ if (script_id && country_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, 0, 0);
+ if (addLikelySubtags(id)) {
+ id.script_id = script_id;
+ id.country_id = country_id;
+ return id;
+ }
+ }
+ return *this;
+}
+
+QLocaleId QLocaleId::withLikelySubtagsRemoved() const
+{
+ QLocaleId max = withLikelySubtagsAdded();
+ // language
+ {
+ QLocaleId id = QLocaleId::fromIds(language_id, 0, 0);
+ if (id.withLikelySubtagsAdded() == max)
+ return id;
+ }
+ // language_region
+ if (country_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, 0, country_id);
+ if (id.withLikelySubtagsAdded() == max)
+ return id;
+ }
+ // language_script
+ if (script_id) {
+ QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0);
+ if (id.withLikelySubtagsAdded() == max)
+ return id;
+ }
+ return max;
+}
+
+QString QLocaleId::bcp47Name() const
+{
+ if (language_id == QLocale::AnyLanguage)
return QString();
- if (m_data->m_language_id == QLocale::C)
+ if (language_id == QLocale::C)
return QStringLiteral("C");
- const unsigned char *lang = language_code_list + 3*(uint(m_data->m_language_id));
+
+ const unsigned char *lang = language_code_list + 3*uint(language_id);
const unsigned char *script =
- (m_data->m_script_id != QLocale::AnyScript ? script_code_list + 4*(uint(m_data->m_script_id)) : 0);
+ (script_id != QLocale::AnyScript ? script_code_list + 4*uint(script_id) : 0);
const unsigned char *country =
- (m_data->m_country_id != QLocale::AnyCountry ? country_code_list + 3*(uint(m_data->m_country_id)) : 0);
+ (country_id != QLocale::AnyCountry ? country_code_list + 3*uint(country_id) : 0);
char len = (lang[2] != 0 ? 3 : 2) + (script ? 4+1 : 0) + (country ? (country[2] != 0 ? 3 : 2)+1 : 0);
QString name(len, Qt::Uninitialized);
QChar *uc = name.data();
@@ -243,42 +320,59 @@ QString QLocalePrivate::bcp47Name() const
return name;
}
+QString QLocalePrivate::bcp47Name() const
+{
+ if (m_data->m_language_id == QLocale::AnyLanguage)
+ return QString();
+ if (m_data->m_language_id == QLocale::C)
+ return QStringLiteral("C");
+
+ QLocaleId localeId = QLocaleId::fromIds(m_data->m_language_id, m_data->m_script_id, m_data->m_country_id);
+ return localeId.withLikelySubtagsRemoved().bcp47Name();
+}
+
const QLocaleData *QLocaleData::findLocaleData(QLocale::Language language, QLocale::Script script, QLocale::Country country)
{
- const unsigned language_id = language;
- const unsigned script_id = script;
- const unsigned country_id = country;
+ QLocaleId localeId = QLocaleId::fromIds(language, script, country);
+ localeId = localeId.withLikelySubtagsAdded();
- uint idx = locale_index[language_id];
+ uint idx = locale_index[localeId.language_id];
const QLocaleData *data = locale_data + idx;
if (idx == 0) // default language has no associated country
return data;
- if (script == QLocale::AnyScript && country == QLocale::AnyCountry)
- return data;
+ Q_ASSERT(data->m_language_id == localeId.language_id);
- Q_ASSERT(data->m_language_id == language_id);
-
- if (country == QLocale::AnyCountry) {
- while (data->m_language_id == language_id && data->m_script_id != script_id)
+ if (localeId.script_id != QLocale::AnyScript && localeId.country_id != QLocale::AnyCountry) {
+ // both script and country are explicitly specified
+ do {
+ if (data->m_script_id == localeId.script_id && data->m_country_id == localeId.country_id)
+ return data;
++data;
- if (data->m_language_id == language_id && data->m_script_id == script_id)
- return data;
- } else if (script == QLocale::AnyScript) {
- while (data->m_language_id == language_id) {
- if (data->m_script_id == script_id && data->m_country_id == country_id)
+ } while (data->m_language_id == localeId.language_id);
+
+ // no match; try again with default script
+ localeId.script_id = QLocale::AnyScript;
+ data = locale_data + idx;
+ }
+
+ if (localeId.script_id == QLocale::AnyScript && localeId.country_id == QLocale::AnyCountry)
+ return data;
+
+ if (localeId.script_id == QLocale::AnyScript) {
+ do {
+ if (data->m_country_id == localeId.country_id)
return data;
++data;
- }
- } else {
- // both script and country are explicitly specified
- while (data->m_language_id == language_id) {
- if (data->m_script_id == script_id && data->m_country_id == country_id)
+ } while (data->m_language_id == localeId.language_id);
+ } else if (localeId.country_id == QLocale::AnyCountry) {
+ do {
+ if (data->m_script_id == localeId.script_id)
return data;
++data;
- }
+ } while (data->m_language_id == localeId.language_id);
}
return locale_data + idx;
@@ -3227,7 +3321,7 @@ QString QLocale::toCurrencyString(double value, const QString &symbol) const
\since 4.8
Returns an ordered list of locale names for translation purposes in
- preference order.
+ preference order (like "en", "en-US", "en-Latn-US").
The return value represents locale names that the user expects to see the
UI translation in.
@@ -3251,7 +3345,20 @@ QStringList QLocale::uiLanguages() const
}
}
#endif
- return QStringList(bcp47Name());
+ QLocaleId id = QLocaleId::fromIds(d->m_data->m_language_id, d->m_data->m_script_id, d->m_data->m_country_id);
+ const QLocaleId max = id.withLikelySubtagsAdded();
+ const QLocaleId min = max.withLikelySubtagsRemoved();
+
+ QStringList uiLanguages;
+ uiLanguages.append(min.bcp47Name());
+ if (id.script_id) {
+ id.script_id = 0;
+ if (id != min && id.withLikelySubtagsAdded() == max)
+ uiLanguages.append(id.bcp47Name());
+ }
+ if (max != min && max != id)
+ uiLanguages.append(max.bcp47Name());
+ return uiLanguages;
}
/*!