diff options
Diffstat (limited to 'src/corelib/tools/qlocale.cpp')
-rw-r--r-- | src/corelib/tools/qlocale.cpp | 165 |
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; } /*! |