From 81fe6eac7530ad508bf54e7996cbdc9e2f0cb5d1 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 18 Feb 2011 17:58:47 +0100 Subject: Added UI languages property to QLocale The return value is a sorted list of locale names that could be used for presenting data to the user (i.e. for translations). Task-number: QTBUG-7329 Reviewed-by: Zeno Albisser --- src/corelib/tools/qlocale.cpp | 133 +++++++++++++++++++++++++++++++++++-- src/corelib/tools/qlocale.h | 3 + tests/auto/qlocale/tst_qlocale.cpp | 16 +++++ tests/manual/qlocale/languages.cpp | 62 +++++++++++++++++ tests/manual/qlocale/languages.h | 62 +++++++++++++++++ tests/manual/qlocale/qlocale.pro | 4 +- tests/manual/qlocale/window.cpp | 3 + tests/manual/qlocale/window.h | 2 + 8 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 tests/manual/qlocale/languages.cpp create mode 100644 tests/manual/qlocale/languages.h diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 6f21633..ba9400c 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -69,6 +69,7 @@ QT_END_NAMESPACE # include #endif #include "private/qnumeric_p.h" +#include "private/qsystemlibrary_p.h" #include #include @@ -254,12 +255,18 @@ static const QLocalePrivate *findLocale(QLocale::Language language, QLocale::Cou return locale_data + idx; } -static bool splitLocaleName(const QString &name, QChar *lang_begin, QChar *cntry_begin) +static bool splitLocaleName(const QString &name, + QChar *lang_begin, QChar *cntry_begin, + int *lang_len = 0, int *cntry_len = 0) { for (int i = 0; i < 3; ++i) lang_begin[i] = 0; for (int i = 0; i < 3; ++i) cntry_begin[i] = 0; + if (lang_len) + *lang_len = 0; + if (cntry_len) + *cntry_len = 0; int l = name.length(); @@ -302,9 +309,13 @@ static bool splitLocaleName(const QString &name, QChar *lang_begin, QChar *cntry ++uc; } - int lang_len = lang - lang_begin; + if (lang_len) + *lang_len = lang - lang_begin; + if (cntry_len) + *cntry_len = cntry - cntry_begin; - return lang_len == 2 || lang_len == 3; + int lang_length = lang - lang_begin; + return lang_length == 2 || lang_length == 3; } void getLangAndCountry(const QString &name, QLocale::Language &lang, QLocale::Country &cntry) @@ -733,6 +744,51 @@ static QString winFormatCurrency(const QVariant &in) return QString::fromWCharArray(out.data()); } +QStringList winUILanguages() +{ + if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) { + typedef BOOL (*GetUserPreferredUILanguagesFunc) ( + DWORD dwFlags, + PULONG pulNumLanguages, + PWSTR pwszLanguagesBuffer, + PULONG pcchLanguagesBuffer); + static GetUserPreferredUILanguagesFunc GetUserPreferredUILanguages_ptr = 0; + if (!GetUserPreferredUILanguages_ptr) { + QSystemLibrary lib(QLatin1String("kernel32")); + if (lib.load()) + GetUserPreferredUILanguages_ptr = (GetUserPreferredUILanguagesFunc)lib.resolve("GetUserPreferredUILanguages"); + } + if (GetUserPreferredUILanguages_ptr) { + unsigned long cnt = 0; + QVarLengthArray buf(64); + unsigned long size = buf.size(); + if (!GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) { + size = 0; + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && + GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, NULL, &size)) { + buf.resize(size); + if (!GetUserPreferredUILanguages_ptr(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) + return QStringList(); + } + } + QStringList result; + result.reserve(cnt); + const wchar_t *str = buf.constData(); + for (; cnt > 0; --cnt) { + QString s = QString::fromWCharArray(str); + if (s.isEmpty()) + break; // something is wrong + result.append(s); + str += s.size()+1; + } + return result; + } + } + + // old Windows before Vista + return QStringList(QString::fromLatin1(winLangCodeToIsoName(GetUserDefaultUILanguage()))); +} + /*! \since 4.6 Returns the fallback locale obtained from the system. @@ -828,6 +884,8 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const return QVariant(winCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()))); case FormatCurrency: return QVariant(winFormatCurrency(in)); + case UILanguages: + return QVariant(winUILanguages()); default: break; } @@ -1428,6 +1486,21 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const return QVariant(macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()))); case FormatCurrency: return macFormatCurrency(in); + case UILanguages: { + QCFType languages = (CFArrayRef)CFPreferencesCopyValue( + CFSTR("AppleLanguages"), + kCFPreferencesAnyApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + const int cnt = CFArrayGetCount(languages); + QStringList result; + result.reserve(cnt); + for (int i = 0; i < cnt; ++i) { + const QString lang = QCFString::toQString( + static_cast(CFArrayGetValueAtIndex(languages, i))); + result.append(lang); + } + return QVariant(result); case QuotationBegin: case QuotationEnd: { return macQuotationSymbol(type,in); @@ -1477,9 +1550,37 @@ QVariant QSystemLocale::query(QueryType type, QVariant /* in */) const { if (type == MeasurementSystem) { return QVariant(unixGetSystemMeasurementSystem()); - } else { + } else if (type == UILanguages) { + QString languages = QString::fromLocal8Bit(qgetenv("LANGUAGE")); + if (!languages.isEmpty()) { + QStringList lst = languages.split(QLatin1Char(':')); + for (int i = 0; i < lst.size();) { + const QString &name = lst.at(i); + QChar lang[3]; + QChar cntry[3]; + if (name.isEmpty() || !splitLocaleName(name, lang, cntry)) + lst.removeAt(i); + else + ++i; + } + return lst; + } + QString name = QString::fromLocal8Bit(qgetenv("LC_ALL")); + if (name.isEmpty()) { + name = QString::fromLocal8Bit(qgetenv("LC_MESSAGES")); + if (name.isEmpty()) + name = QString::fromLocal8Bit(qgetenv("LANG")); + } + if (!name.isEmpty()) { + QChar lang[3]; + QChar cntry[3]; + int lang_len, cntry_len; + if (splitLocaleName(name, lang, cntry, &lang_len, &cntry_len)) + return QStringList(QString::fromRawData(lang, lang_len) % QLatin1Char('-') % QString::fromRawData(cntry, cntry_len)); + } return QVariant(); } + return QVariant(); } #elif !defined(Q_OS_SYMBIAN) @@ -1574,6 +1675,7 @@ Q_GLOBAL_STATIC(QLocalePrivate, globalLocalePrivate) \value FirstDayOfWeek a Qt::DayOfWeek enum specifiying the first day of the week \value CurrencySymbol a string that represents a currency in a format QLocale::CurrencyFormat. \value FormatCurrency a localized string representation of a number with a currency symbol. + \value UILanguages a list of strings representing locale names that could be used for UI translation. \value QuotationBegin a QString specifying the start of a quotation. the in variant contains a QLocale::QuotationStyle \value QuotationEnd a QString specifying the end of a quotation. the in variant contains a QLocale::QuotationStyle */ @@ -5078,6 +5180,29 @@ QString QLocale::toCurrencyString(double value) const return format.arg(str, symbol); } +/*! + \since 4.8 + + Returns a sorted list of locale names that could be used for translation + of messages presented to the user. + + \sa QTranslator +*/ +QStringList QLocale::uiLanguages() const +{ +#ifndef QT_NO_SYSTEMLOCALE + if (d() == systemPrivate()) { + QVariant res = systemLocale()->query(QSystemLocale::UILanguages, QVariant()); + if (!res.isNull()) { + QStringList result = res.toStringList(); + if (!result.isEmpty()) + return result; + } + } +#endif + return QStringList(name()); +} + /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 924cb7b..18c6db3 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -98,6 +98,7 @@ public: FirstDayOfWeek, // Qt::DayOfWeek CurrencySymbol, // QString in: format FormatCurrency, // QString in: qlonglong, qulonglong or double + UILanguages, // QStringList QuotationBegin, // QString in: StandardQuotation or AlternateQuotation QuotationEnd // QString in: StandardQuotation or AlternateQuotation }; @@ -694,6 +695,8 @@ public: QString toCurrencyString(double) const; inline QString toCurrencyString(float) const; + QStringList uiLanguages() const; + inline bool operator==(const QLocale &other) const; inline bool operator!=(const QLocale &other) const; diff --git a/tests/auto/qlocale/tst_qlocale.cpp b/tests/auto/qlocale/tst_qlocale.cpp index 250aac9..878a31e 100644 --- a/tests/auto/qlocale/tst_qlocale.cpp +++ b/tests/auto/qlocale/tst_qlocale.cpp @@ -142,6 +142,7 @@ private slots: void ampm(); void currency(); void quoteString(); + void uiLanguages(); private: QString m_decimal, m_thousand, m_sdate, m_ldate, m_time; @@ -2169,5 +2170,20 @@ void tst_QLocale::quoteString() } +void tst_QLocale::uiLanguages() +{ + const QLocale c(QLocale::C); + QCOMPARE(c.uiLanguages().size(), 1); + QCOMPARE(c.uiLanguages().at(0), QLatin1String("C")); + + const QLocale en_US("en_US"); + QCOMPARE(en_US.uiLanguages().size(), 1); + QCOMPARE(en_US.uiLanguages().at(0), QLatin1String("en_US")); + + const QLocale ru_RU("ru_RU"); + QCOMPARE(ru_RU.uiLanguages().size(), 1); + QCOMPARE(ru_RU.uiLanguages().at(0), QLatin1String("ru_RU")); +} + QTEST_APPLESS_MAIN(tst_QLocale) #include "tst_qlocale.moc" diff --git a/tests/manual/qlocale/languages.cpp b/tests/manual/qlocale/languages.cpp new file mode 100644 index 0000000..65029c3 --- /dev/null +++ b/tests/manual/qlocale/languages.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "languages.h" + +LanguagesWidget::LanguagesWidget() +{ + QVBoxLayout *l = new QVBoxLayout(this); + + languagesLabel = new QLabel("Preferred languages:"); + languagesList = new QListWidget; + + l->addWidget(languagesLabel); + l->addWidget(languagesList); + + localeChanged(QLocale()); +} + +void LanguagesWidget::localeChanged(QLocale locale) +{ + languagesList->clear(); + languagesList->addItems(locale.uiLanguages()); +} + + diff --git a/tests/manual/qlocale/languages.h b/tests/manual/qlocale/languages.h new file mode 100644 index 0000000..18e503f --- /dev/null +++ b/tests/manual/qlocale/languages.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LANGUAGES_H +#define LANGUAGES_H + +#include + +class LanguagesWidget : public QWidget +{ + Q_OBJECT +public: + LanguagesWidget(); + +private: + QLocale currentLocale; + + QLabel *languagesLabel; + QListWidget *languagesList; + +private slots: + void localeChanged(QLocale locale); +}; + +#endif diff --git a/tests/manual/qlocale/qlocale.pro b/tests/manual/qlocale/qlocale.pro index 66d527b..46eaa16 100644 --- a/tests/manual/qlocale/qlocale.pro +++ b/tests/manual/qlocale/qlocale.pro @@ -4,5 +4,5 @@ DEPENDPATH += . INCLUDEPATH += . # Input -HEADERS += currency.h calendar.h window.h miscellaneous.h -SOURCES += currency.cpp main.cpp calendar.cpp window.cpp miscellaneous.cpp +HEADERS += currency.h calendar.h languages.h window.h miscellaneous.h +SOURCES += currency.cpp main.cpp calendar.cpp languages.cpp window.cpp miscellaneous.cpp diff --git a/tests/manual/qlocale/window.cpp b/tests/manual/qlocale/window.cpp index caafcee..ebe5ea9 100644 --- a/tests/manual/qlocale/window.cpp +++ b/tests/manual/qlocale/window.cpp @@ -67,6 +67,8 @@ Window::Window() connect(this, SIGNAL(localeChanged(QLocale)), calendar, SLOT(localeChanged(QLocale))); currency = new CurrencyWidget; connect(this, SIGNAL(localeChanged(QLocale)), currency, SLOT(localeChanged(QLocale))); + languages = new LanguagesWidget; + connect(this, SIGNAL(localeChanged(QLocale)), languages, SLOT(localeChanged(QLocale))); miscellaneous = new MiscWidget; connect(this, SIGNAL(localeChanged(QLocale)), miscellaneous, SLOT(localeChanged(QLocale))); @@ -83,6 +85,7 @@ Window::Window() tabWidget->addTab(calendar, "Calendar"); tabWidget->addTab(currency, "Currency"); + tabWidget->addTab(languages, "Languages"); tabWidget->addTab(miscellaneous, "Misc"); localeCombo->setCurrentIndex(0); systemLocaleChanged(); diff --git a/tests/manual/qlocale/window.h b/tests/manual/qlocale/window.h index c9a5480..35719ae 100644 --- a/tests/manual/qlocale/window.h +++ b/tests/manual/qlocale/window.h @@ -45,6 +45,7 @@ #include "calendar.h" #include "currency.h" +#include "languages.h" #include "miscellaneous.h" class Window : public QWidget @@ -58,6 +59,7 @@ public: QTabWidget *tabWidget; CalendarWidget *calendar; CurrencyWidget *currency; + LanguagesWidget *languages; MiscWidget *miscellaneous; private: -- cgit v0.12