diff --git a/CMakeLists.txt b/CMakeLists.txt index 91443da6a..e7c97b7b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,7 +108,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3) pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2) pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7) -pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.8.2) +pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.10.2) pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.6) string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION}) diff --git a/flake.lock b/flake.lock index 221c4558d..b9fb17867 100644 --- a/flake.lock +++ b/flake.lock @@ -261,11 +261,11 @@ ] }, "locked": { - "lastModified": 1762387740, - "narHash": "sha256-gQ9zJ+pUI4o+Gh4Z6jhJll7jjCSwi8ZqJIhCE2oqwhQ=", + "lastModified": 1762812168, + "narHash": "sha256-pY+dUqi2AYpH0HHT2JFzt1qWoJQBWtBdzzcL1ZK5Mwo=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "926689ddb9c0a8787e58c02c765a62e32d63d1f7", + "rev": "cb3e797fde5c748164eb70d9859336141136a166", "type": "github" }, "original": { diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 702ed8966..a7f26ba6d 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -65,6 +65,7 @@ #include "debug/HyprNotificationOverlay.hpp" #include "debug/HyprDebugOverlay.hpp" #include "helpers/MonitorFrameScheduler.hpp" +#include "i18n/Engine.hpp" #include #include @@ -2765,22 +2766,19 @@ void CCompositor::performUserChecks() { const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP"); if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") { g_pHyprNotificationOverlay->addNotification( - std::format("Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {}.\nThis might cause issues unless it's intentional.", - CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"), - CHyprColor{}, 15000, ICON_WARNING); + I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, {{"value", CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"}}), CHyprColor{}, 15000, + ICON_WARNING); } } if (!*PNOCHECKGUIUTILS) { if (!NFsUtils::executableExistsInPath("hyprland-dialog")) { - g_pHyprNotificationOverlay->addNotification( - "Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING); + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING); } } if (g_pHyprOpenGL->m_failedAssetsNo > 0) { - g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!", - g_pHyprOpenGL->m_failedAssetsNo, g_pHyprOpenGL->m_failedAssetsNo > 1 ? "s" : ""), + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}), CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR); } } @@ -2905,10 +2903,8 @@ void CCompositor::checkMonitorOverlaps() { for (const auto& m : m_monitors) { if (!monitorRegion.copy().intersect(m->logicalBox()).empty()) { Debug::log(ERR, "Monitor {}: detected overlap with layout", m->m_name); - g_pHyprNotificationOverlay->addNotification(std::format("Your monitor layout is set up incorrectly. Monitor {} overlaps with other monitor(s) in the " - "layout.\nPlease see the wiki (Monitors page) for more. This will cause issues.", - m->m_name), - CHyprColor{}, 15000, ICON_WARNING); + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, {{"name", m->m_name}}), CHyprColor{}, 15000, + ICON_WARNING); break; } diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 50a8c1204..1a7b4ac64 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -27,6 +27,7 @@ #include "../managers/animation/DesktopAnimationManager.hpp" #include "../managers/input/InputManager.hpp" #include "../hyprerror/HyprError.hpp" +#include "../i18n/Engine.hpp" #include "sync/SyncTimeline.hpp" #include "time/Time.hpp" #include "../desktop/LayerSurface.hpp" @@ -811,8 +812,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { if (!m_state.test()) continue; - auto errorMessage = - std::format("Monitor {} failed to set any requested modes, falling back to mode {:X0}@{:.2f}Hz", m_name, mode->pixelSize, mode->refreshRate / 1000.f); + auto errorMessage = I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_MODE_FAIL, + {{"name", m_name}, {"mode", std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f)}}); Debug::log(WARN, errorMessage); g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING); @@ -939,8 +940,10 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", m_scale, searchScale); static auto PDISABLENOTIFICATION = CConfigValue("misc:disable_scale_notification"); if (!*PDISABLENOTIFICATION) - g_pHyprNotificationOverlay->addNotification(std::format("Invalid scale passed to monitor: {}, using suggested scale: {}", m_scale, searchScale), - CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING); + g_pHyprNotificationOverlay->addNotification( + I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, + {{"name", m_name}, {"scale", std::format("{:.2f}", m_scale)}, {"fixed_scale", std::format("{:.2f}", searchScale)}}), + CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING); } m_scale = searchScale; } diff --git a/src/i18n/Engine.cpp b/src/i18n/Engine.cpp new file mode 100644 index 000000000..716d3fc31 --- /dev/null +++ b/src/i18n/Engine.cpp @@ -0,0 +1,175 @@ +#include "Engine.hpp" + +#include + +using namespace I18n; +using namespace Hyprutils::I18n; + +static SP huEngine; +static std::string localeStr; + +// +SP I18n::i18nEngine() { + static SP engine = makeShared(); + return engine; +} + +I18n::CI18nEngine::CI18nEngine() { + huEngine = makeShared(); + huEngine->setFallbackLocale("en_US"); + localeStr = huEngine->getSystemLocale().locale(); + + // en_US (English) + huEngine->registerEntry("en_US", TXT_KEY_ANR_TITLE, "Application Not Responding"); + huEngine->registerEntry("en_US", TXT_KEY_ANR_CONTENT, "An application {title} - {class} is not responding.\nWhat do you want to do with it?"); + huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_TERMINATE, "Terminate"); + huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_WAIT, "Wait"); + huEngine->registerEntry("en_US", TXT_KEY_ANR_PROP_UNKNOWN, "(unknown)"); + + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "An application {app} is requesting an unknown permission."); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "An application {app} is trying to capture your screen.\n\nDo you want to allow it to?"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "An application {app} is trying to load a plugin: {plugin}.\n\nDo you want to allow it to?"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "A new keyboard has been detected: {keyboard}.\n\nDo you want to allow it to operate?"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(unknown)"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_TITLE, "Permission request"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Hint: you can set persistent rules for these in the Hyprland config file."); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW, "Allow"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Allow and remember"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_ONCE, "Allow once"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_DENY, "Deny"); + huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Unknown application (wayland client ID {wayland_id})"); + + huEngine->registerEntry( + "en_US", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, + "Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {value}.\nThis might cause issues unless it's intentional."); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_NO_GUIUTILS, + "Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it."); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { + int assetsNo = std::stoi(vars.at("count")); + if (assetsNo <= 1) + return "Hyprland failed to load {count} essential asset, blame your distro's packager for doing a bad job at packaging!"; + return "Hyprland failed to load {count} essential assets, blame your distro's packager for doing a bad job at packaging!"; + }); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, + "Your monitor layout is set up incorrectly. Monitor {name} overlaps with other monitor(s) in the layout.\nPlease see the wiki (Monitors page) for " + "more. This will cause issues."); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} failed to set any requested modes, falling back to mode {mode}."); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Invalid scale passed to monitor {name}: {scale}, using suggested scale: {fixed_scale}"); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Failed to load plugin {name}: {error}"); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader reload failed, falling back to rgba/rgbx."); + huEngine->registerEntry("en_US", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: wide color gamut is enabled but the display is not in 10-bit mode."); + + // it_IT (Italian) + huEngine->registerEntry("it_IT", TXT_KEY_ANR_TITLE, "L'applicazione non risponde"); + huEngine->registerEntry("it_IT", TXT_KEY_ANR_CONTENT, "Un'applicazione {title} - {class} non risponde.\nCosa vuoi fare?"); + huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_TERMINATE, "Termina"); + huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_WAIT, "Attendi"); + huEngine->registerEntry("it_IT", TXT_KEY_ANR_PROP_UNKNOWN, "(sconosciuto)"); + + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Un'applicazione {app} richiede un'autorizzazione sconosciuta."); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Un'applicazione {app} sta provando a catturare il tuo schermo.\n\nGlie lo vuoi permettere?"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_PLUGIN, + "Un'applicazione {app} sta provando a caricare un plugin: {plugin}.\n\nGlie lo vuoi permettere?"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "È stata rilevata una nuova tastiera: {keyboard}.\n\nLe vuoi permettere di operare?"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(sconosciuto)"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_TITLE, "Richiesta di autorizzazione"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Consiglio: Puoi impostare una regola persistente nel tuo file di configurazione di Hyprland."); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW, "Permetti"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permetti e ricorda"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permetti una volta"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_DENY, "Nega"); + huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Applicazione sconosciuta (wayland client ID {wayland_id})"); + + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, + "L'ambiente XDG_CURRENT_DESKTOP sembra essere gestito esternamente, il valore attuale è {value}.\nSe non è voluto, potrebbe causare problemi."); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_NO_GUIUTILS, + "Sembra che hyprland-guiutils non sia installato. È una dipendenza richiesta per alcuni dialoghi che potresti voler installare."); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_ASSETS, + "Hyprland non ha potuto caricare {count} asset, dai la colpa al packager della tua distribuzione per il suo cattivo lavoro!"); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, + "I tuoi schermi sono configurati incorrettamente. Lo schermo {name} si sovrappone con altri nel layout.\nConsulta la wiki (voce Schermi) per " + "altre informazioni. Questo causerà problemi."); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Lo schermo {name} non ha potuto impostare alcuna modalità richiesta, sarà usata la modalità {mode}."); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, + "Fattore di scala non valido per lo schermo {name}: {scale}, utilizzando il fattore suggerito: {fixed_scale}"); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Impossibile caricare il plugin {name}: {error}"); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Impossibile ricaricare gli shader CM, sarà usato rgba/rgbx."); + huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Schermo {name}: la gamma di colori ampia è abilitata ma lo schermo non è in modalità 10-bit."); + + // ja_JP (Japanese) + huEngine->registerEntry("ja_JP", TXT_KEY_ANR_TITLE, "アプリは応答しません"); + huEngine->registerEntry("ja_JP", TXT_KEY_ANR_CONTENT, "アプリ {title} ー {class}は応答しません。\n何をしたいですか?"); + huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_TERMINATE, "強制終了"); + huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_WAIT, "待機"); + huEngine->registerEntry("ja_JP", TXT_KEY_ANR_PROP_UNKNOWN, "(不明)"); + + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "アプリ{app}は不明な許可を要求します。"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "アプリ{app}は画面へのアクセスを要求します。\n\n許可したいですか?"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "アプリ{app}は以下のプラグインをロード許可を要求します:{plugin}。\n\n許可したいですか?"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "新しいキーボードを見つけた:{keyboard}。\n\n稼働を許可したいですか?"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(不明)"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_TITLE, "許可要求"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "ヒント:Hyprlandのコンフィグで通常の許可や却下を設定できます。"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW, "許可"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "保存して許可"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_ONCE, "一度許可"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_DENY, "却下"); + huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "不明なアプリ (waylandクライアントID {wayland_id})"); + + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, + "エンバイアロンメント変数「XDG_CURRENT_DESKTOP」は外部から「{value}」に設定しました。\n意図的ではなければ、問題は発生可能性があります。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_NO_GUIUTILS, "システムにhyprland-guiutilsはインストールしていません。このパッケージをインストールしてください。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_ASSETS, + "{count}つの根本的なアセットをロードできませんでした。これはパッケージャーのせいだから、パッケージャーに文句してください。"); + huEngine->registerEntry( + "ja_JP", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, + "画面の位置設定は誤用です。画面{name}は他の画面の区域と重ね合わせます。\nウィキのモニターページで詳細を確認してください。これは絶対に問題になります。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "画面{name}は設定したモードを正常に受け入れませんでした。{mode}を使いました。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "画面{name}のスケールは無効:{scale}、代わりにおすすめのスケール{fixed_scale}を使いました。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "プラグイン{name}のロード失敗: {error}"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CMシェーダーのリロード失敗、rgba/rgbxを使いました。"); + huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "画面{name}:広い色域は設定していますけど、画面は10ビットモードに設定されていません。"); + + // pl_PL (Polish) + huEngine->registerEntry("pl_PL", TXT_KEY_ANR_TITLE, "Aplikacja Nie Odpowiada"); + huEngine->registerEntry("pl_PL", TXT_KEY_ANR_CONTENT, "Aplikacja {title} - {class} nie odpowiada.\nCo chcesz z nią zrobić?"); + huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_TERMINATE, "Zakończ proces"); + huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_WAIT, "Czekaj"); + huEngine->registerEntry("pl_PL", TXT_KEY_ANR_PROP_UNKNOWN, "(nieznane)"); + + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacja {app} prosi o pozwolenie na nieznany typ operacji."); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacja {app} prosi o dostęp do twojego ekranu.\n\nCzy chcesz jej na to pozwolić?"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacja {app} próbuje załadować plugin: {plugin}.\n\nCzy chcesz jej na to pozwolić?"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Wykryto nową klawiaturę: {keyboard}.\n\nCzy chcesz jej pozwolić operować?"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nieznane)"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_TITLE, "Prośba o pozwolenie"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Podpowiedź: możesz ustawić stałe zasady w konfiguracji Hyprland'a."); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW, "Zezwól"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Zezwól i zapamiętaj"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_ONCE, "Zezwól raz"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_DENY, "Odmów"); + huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nieznana aplikacja (ID klienta wayland {wayland_id})"); + + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, + "Zmienna środowiska XDG_CURRENT_DESKTOP została ustawiona zewnętrznie na {value}.\nTo może sprawić problemy, chyba, że jest celowe."); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_NO_GUIUTILS, "Twój system nie ma hyprland-guiutils zainstalowanych, co może sprawić problemy. Zainstaluj pakiet."); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { + int assetsNo = std::stoi(vars.at("count")); + if (assetsNo == 1) + return "Nie udało się załadować {count} kluczowego zasobu, wiń swojego packager'a za robienie słabej roboty!"; + + return "Nie udało się załadować {count} kluczowych zasobów, wiń swojego packager'a za robienie słabej roboty!"; + }); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, + "Pozycje twoich monitorów nie są ustawione poprawnie. Monitor {name} wchodzi na inne monitory.\nWejdź na wiki (stronę Monitory) " + "po więcej. To będzie sprawiać problemy."); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nie zaakceptował żadnego wybranego programu. Użyto {mode}."); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nieprawidłowa skala dla monitora {name}: {scale}, użyto proponowanej skali: {fixed_scale}"); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nie udało się załadować plugin'a {name}: {error}"); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Nie udało się przeładować shader'a CM, użyto rgba/rgbx."); + huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: skonfigurowano szeroką głębię barw, ale monitor nie jest w trybie 10-bit."); +} + +std::string I18n::CI18nEngine::localize(eI18nKeys key, const Hyprutils::I18n::translationVarMap& vars) { + return huEngine->localizeEntry(localeStr, key, vars); +} diff --git a/src/i18n/Engine.hpp b/src/i18n/Engine.hpp new file mode 100644 index 000000000..d1182632a --- /dev/null +++ b/src/i18n/Engine.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "../helpers/memory/Memory.hpp" +#include +#include +#include + +namespace I18n { + + enum eI18nKeys : uint8_t { + TXT_KEY_ANR_TITLE = 0, + TXT_KEY_ANR_CONTENT, + TXT_KEY_ANR_OPTION_TERMINATE, + TXT_KEY_ANR_OPTION_WAIT, + TXT_KEY_ANR_PROP_UNKNOWN, + + TXT_KEY_PERMISSION_REQUEST_UNKNOWN, + TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, + TXT_KEY_PERMISSION_REQUEST_PLUGIN, + TXT_KEY_PERMISSION_REQUEST_KEYBOARD, + TXT_KEY_PERMISSION_UNKNOWN_NAME, + TXT_KEY_PERMISSION_TITLE, + TXT_KEY_PERMISSION_PERSISTENCE_HINT, + TXT_KEY_PERMISSION_ALLOW, + TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, + TXT_KEY_PERMISSION_ALLOW_ONCE, + TXT_KEY_PERMISSION_DENY, + TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, + + TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, + TXT_KEY_NOTIF_NO_GUIUTILS, + TXT_KEY_NOTIF_FAILED_ASSETS, + TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, + TXT_KEY_NOTIF_MONITOR_MODE_FAIL, + TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, + TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, + TXT_KEY_NOTIF_CM_RELOAD_FAILED, + TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, + }; + + class CI18nEngine { + public: + CI18nEngine(); + ~CI18nEngine() = default; + + std::string localize(eI18nKeys key, const std::unordered_map& vars = {}); + }; + + SP i18nEngine(); +}; \ No newline at end of file diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp index 65e0dea13..daab4d0a9 100644 --- a/src/managers/ANRManager.cpp +++ b/src/managers/ANRManager.cpp @@ -8,6 +8,7 @@ #include "./eventLoop/EventLoopManager.hpp" #include "../config/ConfigValue.hpp" #include "../xwayland/XSurface.hpp" +#include "../i18n/Engine.hpp" using namespace Hyprutils::OS; @@ -83,7 +84,7 @@ void CANRManager::onTick() { if (data->missedResponses >= *PANRTHRESHOLD) { if (!data->isRunning() && !data->dialogSaidWait) { - data->runDialog("Application Not Responding", firstWindow->m_title, firstWindow->m_class, data->getPid()); + data->runDialog(firstWindow->m_title, firstWindow->m_class, data->getPid()); for (const auto& w : g_pCompositor->m_windows) { if (!w->m_isMapped) @@ -176,16 +177,29 @@ CANRManager::SANRData::~SANRData() { killDialog(); } -void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) { +void CANRManager::SANRData::runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID) { if (dialogBox && dialogBox->isRunning()) killDialog(); - dialogBox = CAsyncDialogBox::create(title, - std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?", appName.empty() ? "unknown" : appName, - appClass.empty() ? "unknown" : appClass), - std::vector{"Terminate", "Wait"}); + const auto OPTION_TERMINATE_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_TERMINATE, {}); + const auto OPTION_WAIT_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_WAIT, {}); - dialogBox->open()->then([dialogWmPID, this](SP> r) { + dialogBox = + CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}), + I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT, + { + // + {"class", appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass}, // + {"title", appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName} // + }), + std::vector{ + // + OPTION_TERMINATE_STR, // + OPTION_WAIT_STR // + } // + ); + + dialogBox->open()->then([dialogWmPID, this, OPTION_TERMINATE_STR, OPTION_WAIT_STR](SP> r) { if (r->hasError()) { Debug::log(ERR, "CANRManager::SANRData::runDialog: error spawning dialog"); return; @@ -193,9 +207,9 @@ void CANRManager::SANRData::runDialog(const std::string& title, const std::strin const auto& result = r->result(); - if (result.starts_with("Terminate")) + if (result.starts_with(OPTION_TERMINATE_STR)) ::kill(dialogWmPID, SIGKILL); - else if (result.starts_with("Wait")) + else if (result.starts_with(OPTION_WAIT_STR)) dialogSaidWait = true; else Debug::log(ERR, "CANRManager::SANRData::runDialog: lambda: unrecognized result: {}", result); diff --git a/src/managers/ANRManager.hpp b/src/managers/ANRManager.hpp index f5c0085ba..286e834f9 100644 --- a/src/managers/ANRManager.hpp +++ b/src/managers/ANRManager.hpp @@ -39,7 +39,7 @@ class CANRManager { bool dialogSaidWait = false; SP dialogBox; - void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID); + void runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID); bool isRunning(); void killDialog(); bool isDefunct() const; diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp index af1de990b..a54847737 100644 --- a/src/managers/permissions/DynamicPermissionManager.cpp +++ b/src/managers/permissions/DynamicPermissionManager.cpp @@ -5,6 +5,7 @@ #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" #include "../../helpers/MiscFunctions.hpp" +#include "../../i18n/Engine.hpp" #include using namespace Hyprutils::String; @@ -57,17 +58,6 @@ static const char* permissionToString(eDynamicPermissionType type) { return "error"; } -static const char* permissionToHumanString(eDynamicPermissionType type) { - switch (type) { - case PERMISSION_TYPE_UNKNOWN: return "An application {} is requesting an unknown permission."; - case PERMISSION_TYPE_SCREENCOPY: return "An application {} is trying to capture your screen.

Do you want to allow it to do so?"; - case PERMISSION_TYPE_PLUGIN: return "An application {} is trying to load a plugin: {}.

Do you want to load it?"; - case PERMISSION_TYPE_KEYBOARD: return "A new keyboard has been plugged in: {}.

Do you want to allow it to operate?"; - } - - return "error"; -} - static const char* specialPidToString(eSpecialPidTypes type) { switch (type) { case SPECIAL_PID_TYPE_CONFIG: return "config"; @@ -244,39 +234,41 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s rule->m_pid = pid; - std::string description = ""; + std::string appName = ""; if (binaryPath.empty()) - description = std::format(std::runtime_format(permissionToHumanString(type)), std::format("unknown application (wayland client ID 0x{:x})", rc(client))); + appName = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, {{"wayland_id", std::format("{:x}", rc(client))}}); else if (client) { - std::string binaryName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath; - description = std::format(std::runtime_format(permissionToHumanString(type)), std::format("{} ({})", binaryName, binaryPath)); + appName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath; } else { - std::string lookup = ""; if (pid < 0) - lookup = specialPidToString(sc(pid)); + appName = specialPidToString(sc(pid)); else { const auto LOOKUP = binaryNameForPid(pid); - lookup = LOOKUP.value_or("Unknown"); - } - - if (type == PERMISSION_TYPE_PLUGIN) { - const auto LOOKUP = binaryNameForPid(pid); - description = std::format(std::runtime_format(permissionToHumanString(type)), lookup, binaryPath); - } else { - const auto LOOKUP = binaryNameForPid(pid); - description = std::format(std::runtime_format(permissionToHumanString(type)), lookup, binaryPath); + appName = LOOKUP.value_or(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_NAME)); } } + std::string description = ""; + switch (rule->m_type) { + case PERMISSION_TYPE_SCREENCOPY: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, {{"app", appName}}); break; + case PERMISSION_TYPE_PLUGIN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, {{"app", appName}, {"plugin", binaryPath}}); break; + case PERMISSION_TYPE_KEYBOARD: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_KEYBOARD, {{"keyboard", binaryPath}}); break; + case PERMISSION_TYPE_UNKNOWN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_UNKNOWN, {{"app", appName}}); break; + } + std::vector options; + const auto ALLOW = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW); + const auto ALLOW_AND_REMEMBER = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER); + const auto ALLOW_ONCE = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_ONCE); + const auto DENY = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_DENY); if (!binaryPath.empty() && client) { - description += "

Hint: you can set persistent rules for these in the Hyprland config file."; - options = {"Deny", "Allow and remember app", "Allow once"}; + description += std::format("

{}", I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_PERSISTENCE_HINT)); + options = {DENY, ALLOW_AND_REMEMBER, ALLOW_ONCE}; } else - options = {"Deny", "Allow"}; + options = {DENY, ALLOW}; - rule->m_dialogBox = CAsyncDialogBox::create("Permission request", description, options); + rule->m_dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_TITLE), description, options); rule->m_dialogBox->m_priority = true; if (!rule->m_dialogBox) { @@ -286,7 +278,7 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s } rule->m_promise = rule->m_dialogBox->open(); - rule->m_promise->then([r = WP(rule), binaryPath](SP> pr) { + rule->m_promise->then([r = WP(rule), binaryPath, ALLOW, ALLOW_AND_REMEMBER, ALLOW_ONCE, DENY](SP> pr) { if (!r) return; @@ -303,15 +295,15 @@ void CDynamicPermissionManager::askForPermission(wl_client* client, const std::s Debug::log(TRACE, "CDynamicPermissionRule: user returned {}", result); - if (result.starts_with("Allow once")) + if (result.starts_with(ALLOW_ONCE)) r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - else if (result.starts_with("Deny")) { + else if (result.starts_with(DENY)) { r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_DENY; r->m_binaryPath = binaryPath; - } else if (result.starts_with("Allow and remember")) { + } else if (result.starts_with(ALLOW_AND_REMEMBER)) { r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; r->m_binaryPath = binaryPath; - } else if (result.starts_with("Allow")) + } else if (result.starts_with(ALLOW)) r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; if (r->m_promiseResolverForExternal) diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp index 740b2cce8..27e8232ec 100644 --- a/src/plugins/PluginSystem.cpp +++ b/src/plugins/PluginSystem.cpp @@ -9,6 +9,7 @@ #include "../managers/eventLoop/EventLoopManager.hpp" #include "../managers/permissions/DynamicPermissionManager.hpp" #include "../debug/HyprNotificationOverlay.hpp" +#include "../i18n/Engine.hpp" CPluginSystem::CPluginSystem() { g_pFunctionHookSystem = makeUnique(); @@ -224,7 +225,8 @@ void CPluginSystem::updateConfigPlugins(const std::vector& plugins, if (result->hasError()) { const auto NAME = path.contains('/') ? path.substr(path.find_last_of('/') + 1) : path; Debug::log(ERR, "CPluginSystem::updateConfigPlugins: failed to load plugin {}: {}", NAME, result->error()); - g_pHyprNotificationOverlay->addNotification(std::format("Failed to load plugin {}: {}", NAME, result->error()), CHyprColor{0, 0, 0, 0}, 5000, ICON_ERROR); + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, {{"name", NAME}, {"error", result->error()}}), + CHyprColor{0, 0, 0, 0}, 5000, ICON_ERROR); return; } diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index baf69b968..548b2f8b8 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -24,6 +24,7 @@ #include "../managers/CursorManager.hpp" #include "../helpers/fs/FsUtils.hpp" #include "../helpers/MainLoopExecutor.hpp" +#include "../i18n/Engine.hpp" #include "debug/HyprNotificationOverlay.hpp" #include "hyprerror/HyprError.hpp" #include "pass/TexPassElement.hpp" @@ -1006,7 +1007,7 @@ bool CHyprOpenGLImpl::initShaders() { prog = createProgram(shaders->TEXVERTSRC, TEXFRAGSRCCM, true, true); if (m_shadersInitialized && m_cmSupported && prog == 0) - g_pHyprNotificationOverlay->addNotification("CM shader reload failed, falling back to rgba/rgbx", CHyprColor{}, 15000, ICON_WARNING); + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_CM_RELOAD_FAILED), CHyprColor{}, 15000, ICON_WARNING); m_cmSupported = prog > 0; if (m_cmSupported) { diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index c13a54d16..9a2410f3a 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -26,6 +26,7 @@ #include "../hyprerror/HyprError.hpp" #include "../debug/HyprDebugOverlay.hpp" #include "../debug/HyprNotificationOverlay.hpp" +#include "../i18n/Engine.hpp" #include "helpers/CursorShapes.hpp" #include "helpers/Monitor.hpp" #include "pass/TexPassElement.hpp" @@ -1576,7 +1577,8 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { Debug::log(WARN, "Wide color gamut is enabled but the display is not in 10bit mode"); static bool shown = false; if (!shown) { - g_pHyprNotificationOverlay->addNotification("Wide color gamut is enabled but the display is not in 10bit mode", CHyprColor{}, 15000, ICON_WARNING); + g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, {{"name", pMonitor->m_name}}), CHyprColor{}, 15000, + ICON_WARNING); shown = true; } }