Files
minecraft-launcher/localization.h
T

109 lines
5.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <QAtomicInt>
#include <QHash>
#include <QJSValue>
#include <QList>
#include <QObject>
#include <QString>
#include <QStringList>
#include <QVariantList>
#include <QtQml/qqmlregistration.h>
class QQmlEngine;
class QJSEngine;
// Единственный источник всех текстов интерфейса. Каталог — i18n/translations.json,
// он лежит в ресурсах и правится руками; штатных .ts/.qm в проекте нет намеренно.
//
// Из QML читается свойством: text: Loc.t.settings.title
// Из C++ — свободной функцией: Loc::text("settings.title")
//
// Почему свойство, а не Q_INVOKABLE: вызов метода не регистрирует зависимость
// привязки, и text: Loc.t("ключ") никогда бы не обновился при смене языка.
// Чтение же Q_PROPERTY с сигналом NOTIFY зависимость регистрирует, поэтому
// languageChanged перевычисляет все привязки разом. Дальнейшие .settings.title —
// обычные обращения к членам JS-объекта, и отслеживать их не нужно: при смене
// языка дерево заменяется целиком.
//
// Тип свойства — QJSValue, а не QVariantMap: QVariantMap пересобирался бы в новый
// JS-объект при каждом чтении, а привязок в проекте полторы сотни. QJSValue
// строится один раз на смену языка.
class Localization : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(Loc)
QML_SINGLETON
Q_PROPERTY(QJSValue t READ t NOTIFY languageChanged)
// Что выбрано пользователем: "system", "ru" или "en".
Q_PROPERTY(QString language READ language NOTIFY languageChanged)
// Во что это разрешилось: всегда "ru" или "en", никогда "system".
Q_PROPERTY(QString resolvedLanguage READ resolvedLanguage NOTIFY languageChanged)
public:
static Localization &instance();
// Фабрика синглтона для движка QML. Объект к этому моменту уже загружен
// из main(), здесь только запоминается движок и строится дерево.
static Localization *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
// Читает каталог из ресурсов и язык из settings.json. Вызывается ровно один
// раз из main() до создания QQmlApplicationEngine. false — каталога нет или
// он битый; это ошибка сборки, и запускаться дальше незачем.
bool load();
QJSValue t() const { return m_tree; }
QString language() const { return m_language; }
QString resolvedLanguage() const;
// Только из GUI-потока: меняет дерево и рассылает languageChanged.
void setLanguage(const QString &code);
// Для комбобокса в настройках: [{ "code": "ru", "name": "Русский" }, …],
// первым пунктом — "system".
Q_INVOKABLE QVariantList availableLanguages() const;
// Рабочие лошадки Loc::text/Loc::list. Потокобезопасны: см. комментарий к
// полям ниже.
[[nodiscard]] QString textFor(const char *key) const;
[[nodiscard]] QStringList listFor(const char *key) const;
signals:
void languageChanged();
private:
explicit Localization(QObject *parent = nullptr);
void rebuildTree();
// Каталог заполняется один раз в load() и дальше только читается — поэтому
// обращаться к нему можно из любого потока без блокировок. Единственное, что
// меняется на ходу, — m_index, и он атомарный.
QHash<QString, QStringList> m_singles; // ключ -> значения по языкам
QHash<QString, QList<QStringList>> m_lists; // ключ -> списки по языкам
QStringList m_codes; // ["ru", "en"] — порядок из _meta
QHash<QString, QString> m_displayNames;
QAtomicInt m_index{0}; // индекс текущего языка в m_codes
QString m_language{QStringLiteral("system")};
QJSEngine *m_engine = nullptr;
QJSValue m_tree;
};
// ── Доступ из C++ ───────────────────────────────────────────────────────────
//
// Свободные функции, а не методы: их вызывают и из namespace-обёрток
// (LauncherPaths, JavaLocator, ZlibReference), где никакого QObject нет.
namespace Loc {
// Перевод по ключу. Ключа нет — возвращается сам ключ, в отладочной сборке ещё
// и qWarning: строка вида "launch.error.gameRunning" в интерфейсе сразу видна.
[[nodiscard]] QString text(const char *key);
// Для ключей со значением-списком (модели комбобоксов).
[[nodiscard]] QStringList list(const char *key);
} // namespace Loc