deskflow/src/lib/common/I18N.cpp

196 lines
6.4 KiB
C++
Raw Normal View History

2025-10-15 02:39:45 +00:00
/*
* 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 QList<QDir> appTrDirs{
QDir(QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(), QStringLiteral("translations"))),
QDir(QStringLiteral("%1/../translations").arg(QCoreApplication::applicationDirPath())),
QDir(QStringLiteral("%1/../share/%2/translations").arg(QCoreApplication::applicationDirPath(), kAppId)),
QDir(QStringLiteral("%1/.local/share/%2/translations").arg(QDir::homePath(), kAppId)),
QDir(QStringLiteral("/usr/local/share/%1/translations").arg(kAppId)),
QDir(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 QList<QDir> qtTrDirs{
QDir(QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(), QStringLiteral("translations"))),
QDir(QStringLiteral("%1/../qt-depends/translations").arg(QCoreApplication::applicationDirPath())),
QDir(QStringLiteral("%1/../share/qt/translations").arg(QCoreApplication::applicationDirPath())),
QDir(QStringLiteral("%1/.local/share/%2/translations").arg(QDir::homePath(), QStringLiteral("qt"))),
QDir(QStringLiteral("/usr/local/share/qt/translations")),
QDir(QStringLiteral("/usr/share/qt/translations"))
};
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_translations.keys();
}
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();
QStringList nameFilter = {QStringLiteral("%1_*.qm").arg(kAppId)};
QMap<QString, QString> appTranslations;
QMap<QString, QString> shortToNative;
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());
shortToNative.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(shortToNative.value(lang), {appTranslations.value(lang), qtTranslations.value(lang)});
if (oldList != m_translations)
Q_EMIT langaugesChanged(m_translations.keys());
}