218 lines
6.9 KiB
C++
218 lines
6.9 KiB
C++
/*
|
|
* Deskflow -- mouse and keyboard sharing utility
|
|
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
|
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
|
*/
|
|
|
|
#include "I18N.h"
|
|
|
|
#include "common/Constants.h"
|
|
#include "common/Settings.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QList>
|
|
#include <QMap>
|
|
#include <QObject>
|
|
#include <QTranslator>
|
|
|
|
I18N *I18N::instance()
|
|
{
|
|
static I18N m;
|
|
return &m;
|
|
}
|
|
|
|
I18N::I18N(QObject *parent) : QObject{parent}
|
|
{
|
|
const auto appDir = QCoreApplication::applicationDirPath();
|
|
const auto homeDir = QDir::homePath();
|
|
|
|
const QList<QDir> appTrDirs{
|
|
{QStringLiteral("%1/%2").arg(appDir, QStringLiteral("translations"))},
|
|
{QStringLiteral("%1/../translations").arg(appDir)},
|
|
{QStringLiteral("%1/../Resources/translations").arg(appDir)},
|
|
{QStringLiteral("%1/../share/%2/translations").arg(appDir, kAppId)},
|
|
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, kAppId)},
|
|
{QStringLiteral("/usr/local/share/%1/translations").arg(kAppId)},
|
|
{QStringLiteral("/usr/share/%1/translations").arg(kAppId)}
|
|
};
|
|
const QStringList appTrFilter{QStringLiteral("%1*.qm").arg(kAppId)};
|
|
|
|
for (const auto &dir : appTrDirs) {
|
|
if (!dir.entryList(appTrFilter, QDir::Files, QDir::Name).isEmpty()) {
|
|
m_appTrPath = dir.absolutePath();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_appTrPath.isEmpty()) {
|
|
qInfo() << "no app translations found";
|
|
}
|
|
|
|
const auto qt = QStringLiteral("qt");
|
|
const auto qt6 = QStringLiteral("qt6");
|
|
|
|
const QList<QDir> qtTrDirs{
|
|
{QStringLiteral("%1/%2").arg(appDir, QStringLiteral("translations"))},
|
|
{QStringLiteral("%1/../Resources/translations").arg(appDir)},
|
|
{QStringLiteral("%1/../qt-depends/translations").arg(appDir)},
|
|
{QStringLiteral("%1/../share/%2/translations").arg(appDir, qt6)},
|
|
{QStringLiteral("%1/../share/%2/translations").arg(appDir, qt)},
|
|
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, qt6)},
|
|
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, qt)},
|
|
{QStringLiteral("/usr/local/share/%2/translations").arg(qt6)},
|
|
{QStringLiteral("/usr/local/share/%2/translations").arg(qt)},
|
|
{QStringLiteral("/usr/share/%2/translations").arg(qt6)},
|
|
{QStringLiteral("/usr/share/%2/translations").arg(qt)}
|
|
};
|
|
const QStringList qtTrFilter{QStringLiteral("qt_*.qm")};
|
|
|
|
for (const auto &dir : qtTrDirs) {
|
|
if (!dir.entryList(qtTrFilter, QDir::Files, QDir::Name).isEmpty()) {
|
|
m_qtTrPath = dir.absolutePath();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_qtTrPath.isEmpty()) {
|
|
qInfo() << "no qt translations found";
|
|
}
|
|
|
|
detectLanguages();
|
|
|
|
if (Settings::value(Settings::Core::Language).isNull()) {
|
|
auto appTranslator = new QTranslator(this);
|
|
if (appTranslator->load(QLocale(), kAppId, "_", m_appTrPath)) {
|
|
m_currentTranslations.append(appTranslator);
|
|
QCoreApplication::installTranslator(appTranslator);
|
|
}
|
|
|
|
m_currentLang = appTranslator->translate("i18n", "LocalizedName");
|
|
if (m_currentLang.isEmpty())
|
|
m_currentLang = QStringLiteral("English");
|
|
|
|
auto qtTranslator = new QTranslator(this);
|
|
if (qtTranslator->load(QLocale(), QStringLiteral("qt"), "_", m_qtTrPath)) {
|
|
m_currentTranslations.append(qtTranslator);
|
|
QCoreApplication::installTranslator(qtTranslator);
|
|
}
|
|
} else {
|
|
m_currentLang = Settings::value(Settings::Core::Language).toString();
|
|
const auto translations = m_translations.value(m_currentLang);
|
|
for (const auto &translation : translations) {
|
|
auto translator = new QTranslator(this);
|
|
if (translator->load(translation)) {
|
|
m_currentTranslations.append(translator);
|
|
QCoreApplication::installTranslator(translator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QStringList I18N::detectedLanguages()
|
|
{
|
|
return instance()->m_nameMap.values();
|
|
}
|
|
|
|
QString I18N::nativeTo639Name(QString nativeName)
|
|
{
|
|
return instance()->m_nameMap.key(nativeName);
|
|
}
|
|
|
|
QString I18N::toNativeName(QString shortName)
|
|
{
|
|
return instance()->m_nameMap.value(shortName);
|
|
}
|
|
|
|
QString I18N::currentLanguage()
|
|
{
|
|
return instance()->m_currentLang;
|
|
}
|
|
|
|
void I18N::setLanguage(const QString &langName)
|
|
{
|
|
if (langName == instance()->m_currentLang) {
|
|
return;
|
|
}
|
|
|
|
if (!instance()->m_translations.contains(langName)) {
|
|
return;
|
|
}
|
|
|
|
instance()->m_currentLang = langName;
|
|
Settings::setValue(Settings::Core::Language, langName);
|
|
|
|
for (const auto &translation : std::as_const(instance()->m_currentTranslations))
|
|
QCoreApplication::removeTranslator(translation);
|
|
|
|
qDeleteAll(instance()->m_currentTranslations);
|
|
instance()->m_currentTranslations.clear();
|
|
|
|
const auto translations = instance()->m_translations.value(langName);
|
|
for (const auto &translation : translations) {
|
|
auto translator = new QTranslator(instance());
|
|
if (translator->load(translation)) {
|
|
instance()->m_currentTranslations.append(translator);
|
|
QCoreApplication::installTranslator(translator);
|
|
}
|
|
}
|
|
|
|
Q_EMIT instance()->languageChanged(langName);
|
|
}
|
|
|
|
void I18N::reDetectLanguages()
|
|
{
|
|
instance()->detectLanguages();
|
|
}
|
|
|
|
void I18N::detectLanguages()
|
|
{
|
|
const auto oldList = m_translations;
|
|
m_translations.clear();
|
|
m_nameMap.clear();
|
|
|
|
QStringList nameFilter = {QStringLiteral("%1_*.qm").arg(kAppId)};
|
|
QMap<QString, QString> appTranslations;
|
|
QStringList detectedLangCodes;
|
|
QDir dir(m_appTrPath);
|
|
QStringList langList = dir.entryList(nameFilter, QDir::Files, QDir::Name);
|
|
|
|
for (const QString &translation : std::as_const(langList)) {
|
|
QTranslator translator;
|
|
std::ignore = translator.load(translation, dir.absolutePath());
|
|
const auto longCode = translator.language();
|
|
//: Replace with your Language name
|
|
//: This is a required string
|
|
QString nativeLang = translator.translate("i18n", "LocalizedName");
|
|
if (nativeLang.isEmpty())
|
|
nativeLang = QStringLiteral("English");
|
|
|
|
QString shortCode;
|
|
if (longCode.startsWith(QStringLiteral("zh")) || longCode.startsWith(QStringLiteral("pt")))
|
|
shortCode = longCode;
|
|
else
|
|
shortCode = longCode.mid(0, 2);
|
|
|
|
appTranslations.insert(shortCode, translator.filePath());
|
|
m_nameMap.insert(shortCode, nativeLang);
|
|
detectedLangCodes.append(QStringLiteral("qt_%1.qm").arg(shortCode));
|
|
}
|
|
|
|
dir.setPath(m_qtTrPath);
|
|
const static auto qtTrNameLen = 3; // length of qt_
|
|
langList = dir.entryList(detectedLangCodes, QDir::Files, QDir::Name);
|
|
|
|
QMap<QString, QString> qtTranslations;
|
|
for (const QString &translation : std::as_const(langList)) {
|
|
QString lang = translation.mid(qtTrNameLen, translation.lastIndexOf('.') - qtTrNameLen);
|
|
qtTranslations.insert(lang, QStringLiteral("%1/%2").arg(m_qtTrPath, translation));
|
|
}
|
|
|
|
const QStringList keys = appTranslations.keys();
|
|
for (const QString &lang : keys)
|
|
m_translations.insert(lang, {appTranslations.value(lang), qtTranslations.value(lang)});
|
|
|
|
if (oldList != m_translations)
|
|
Q_EMIT languagesChanged(m_translations.keys());
|
|
}
|