From 9b223a4d257870c2e7940036b66242f592f7e15a Mon Sep 17 00:00:00 2001 From: honjow Date: Fri, 14 Mar 2025 21:19:32 +0800 Subject: [PATCH 1/2] Add i18n --- package.json | 1 + src/components/SubList.tsx | 3 +- src/components/Version.tsx | 44 +++++++++---- src/i18n/bulgarian.json | 1 + src/i18n/english.json | 35 ++++++++++ src/i18n/english.ts | 39 +++++++++++ src/i18n/french.json | 1 + src/i18n/german.json | 1 + src/i18n/index.ts | 2 + src/i18n/italian.json | 1 + src/i18n/japanese.json | 1 + src/i18n/koreana.json | 1 + src/i18n/localization.ts | 46 +++++++++++++ src/i18n/localizeMap.ts | 125 ++++++++++++++++++++++++++++++++++++ src/i18n/schinese.json | 35 ++++++++++ src/i18n/schinese.ts | 37 +++++++++++ src/i18n/tchinese.json | 1 + src/i18n/thai.json | 1 + src/index.tsx | 98 ++++++++++++++++++++-------- src/pages/Subscriptions.tsx | 7 +- tsconfig.json | 1 + 21 files changed, 439 insertions(+), 42 deletions(-) create mode 100755 src/i18n/bulgarian.json create mode 100755 src/i18n/english.json create mode 100755 src/i18n/english.ts create mode 100755 src/i18n/french.json create mode 100755 src/i18n/german.json create mode 100755 src/i18n/index.ts create mode 100755 src/i18n/italian.json create mode 100755 src/i18n/japanese.json create mode 100755 src/i18n/koreana.json create mode 100755 src/i18n/localization.ts create mode 100755 src/i18n/localizeMap.ts create mode 100644 src/i18n/schinese.json create mode 100644 src/i18n/schinese.ts create mode 100755 src/i18n/tchinese.json create mode 100755 src/i18n/thai.json diff --git a/package.json b/package.json index 10677ef..a5b7e97 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "dependencies": { "@decky/api": "^1.1.2", "axios": "^1.7.9", + "i18next": "^23.15.1", "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-icons": "^5.4.0", diff --git a/src/components/SubList.tsx b/src/components/SubList.tsx index a294cf7..21cfb0f 100644 --- a/src/components/SubList.tsx +++ b/src/components/SubList.tsx @@ -1,6 +1,7 @@ import { ButtonItem } from "@decky/ui"; import { FC } from "react"; import * as backend from "../backend/backend"; +import { localizationManager, localizeStrEnum } from "../i18n"; interface appProp { Subscriptions: Array; UpdateSub: any; @@ -27,7 +28,7 @@ export const SubList: FC = ({ Subscriptions, UpdateSub, Refresh }) => { Refresh(); }} > - Delete + {localizationManager.getString(localizeStrEnum.DELETE)} ); diff --git a/src/components/Version.tsx b/src/components/Version.tsx index 60ff1c2..6a6e7ba 100644 --- a/src/components/Version.tsx +++ b/src/components/Version.tsx @@ -2,11 +2,15 @@ import { PanelSection, PanelSectionRow, Field } from "@decky/ui"; import { FC, useEffect, useState } from "react"; import { PyBackend } from "../backend/backend"; import { ActionButtonItem } from "."; - +import { localizationManager, localizeStrEnum } from "../i18n"; export const VersionComponent: FC = () => { - const [currentVersion, _] = useState(PyBackend.data.getCurrentVersion()); - const [latestVersion, setLatestVersion] = useState(PyBackend.data.getLatestVersion()); + const [currentVersion, _] = useState( + PyBackend.data.getCurrentVersion() + ); + const [latestVersion, setLatestVersion] = useState( + PyBackend.data.getLatestVersion() + ); useEffect(() => { const getData = async () => { @@ -17,34 +21,52 @@ export const VersionComponent: FC = () => { getData(); }); - let uptButtonText = 'Reinstall Plugin'; + let uptButtonText = localizationManager.getString( + localizeStrEnum.REINSTALL_PLUGIN + ); if (currentVersion !== latestVersion && Boolean(latestVersion)) { - uptButtonText = `Update to ${latestVersion}`; + uptButtonText = + localizationManager.getString(localizeStrEnum.UPDATE_TO) + + ` ${latestVersion}`; } return ( - + { await PyBackend.updateLatest(); }} - >{uptButtonText} + > + {uptButtonText} + - + {currentVersion} {Boolean(latestVersion) && ( - + {latestVersion} )} - ) -} \ No newline at end of file + ); +}; diff --git a/src/i18n/bulgarian.json b/src/i18n/bulgarian.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/bulgarian.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/english.json b/src/i18n/english.json new file mode 100755 index 0000000..206c729 --- /dev/null +++ b/src/i18n/english.json @@ -0,0 +1,35 @@ +{ + "SERVICE": "Service", + "TOOLS": "Tools", + "VERSION": "Version", + "ABOUT": "About", + "DEBUG": "Debug", + "SUBSCRIPTIONS": "Subscriptions", + "SUBSCRIPTIONS_LINK": "Subscriptions", + "SELECT_SUBSCRIPTION": "Select a Subscription", + "DOWNLOAD": "Download", + "UPDATE_ALL": "Update All", + "DELETE": "Delete", + "ENABLE_CLASH": "Enable Clash", + "ENABLE_CLASH_DESC": "Run Clash in background", + "ENABLE_CLASH_FAILED": "Failed to start, please check /tmp/tomoon.log", + "ENABLE_CLASH_LOADING": "Loading ...", + "ENABLE_CLASH_IS_RUNNING": "Clash is running.", + "MANAGE_SUBSCRIPTIONS": "Manage Subscriptions", + "OPEN_DASHBOARD": "Open Dashboard", + "SELECT_DASHBOARD": "Select Dashboard", + "ALLOW_REMOTE_ACCESS": "Allow Remote Access", + "ALLOW_REMOTE_ACCESS_DESC": "Allow Remote Access to Dashboard", + "SKIP_PROXY": "Skip Proxy", + "SKIP_PROXY_DESC": "Enable for direct Steam downloads", + "OVERRIDE_DNS": "Override DNS", + "OVERRIDE_DNS_DESC": "Force Clash to hijack DNS query", + "ENHANCED_MODE": "Enhanced Mode", + "ENHANCED_MODE_DESC": "Enhanced Mode", + "RESTART_CORE": "Restart Core", + "RESET_NETWORK": "Reset Network", + "REINSTALL_PLUGIN": "Reinstall Plugin", + "UPDATE_TO": "Update to", + "INSTALLED_VERSION": "Installed Version", + "LATEST_VERSION": "Latest Version" +} \ No newline at end of file diff --git a/src/i18n/english.ts b/src/i18n/english.ts new file mode 100755 index 0000000..d7e7a92 --- /dev/null +++ b/src/i18n/english.ts @@ -0,0 +1,39 @@ +import { localizeStrEnum } from "./localizeMap"; + +export const english: Record = { + [localizeStrEnum.SERVICE]: "Service", + [localizeStrEnum.TOOLS]: "Tools", + [localizeStrEnum.VERSION]: "Version", + [localizeStrEnum.ABOUT]: "About", + [localizeStrEnum.DEBUG]: "Debug", + [localizeStrEnum.SUBSCRIPTIONS]: "Subscriptions", + [localizeStrEnum.SUBSCRIPTIONS_LINK]: "Subscriptions", + [localizeStrEnum.SELECT_SUBSCRIPTION]: "Select a Subscription", + [localizeStrEnum.DOWNLOAD]: "Download", + [localizeStrEnum.UPDATE_ALL]: "Update All", + [localizeStrEnum.DELETE]: "Delete", + [localizeStrEnum.ENABLE_CLASH]: "Enable Clash", + [localizeStrEnum.ENABLE_CLASH_DESC]: "Run Clash in background", + [localizeStrEnum.ENABLE_CLASH_FAILED]: + "Failed to start, please check /tmp/tomoon.log", + [localizeStrEnum.ENABLE_CLASH_LOADING]: "Loading ...", + [localizeStrEnum.ENABLE_CLASH_IS_RUNNING]: "Clash is running.", + [localizeStrEnum.MANAGE_SUBSCRIPTIONS]: "Manage Subscriptions", + [localizeStrEnum.OPEN_DASHBOARD]: "Open Dashboard", + [localizeStrEnum.SELECT_DASHBOARD]: "Select Dashboard", + [localizeStrEnum.ALLOW_REMOTE_ACCESS]: "Allow Remote Access", + [localizeStrEnum.ALLOW_REMOTE_ACCESS_DESC]: + "Allow Remote Access to Dashboard", + [localizeStrEnum.SKIP_PROXY]: "Skip Proxy", + [localizeStrEnum.SKIP_PROXY_DESC]: "Enable for direct Steam downloads", + [localizeStrEnum.OVERRIDE_DNS]: "Override DNS", + [localizeStrEnum.OVERRIDE_DNS_DESC]: "Force Clash to hijack DNS query", + [localizeStrEnum.ENHANCED_MODE]: "Enhanced Mode", + [localizeStrEnum.ENHANCED_MODE_DESC]: "Enhanced Mode", + [localizeStrEnum.RESTART_CORE]: "Restart Core", + [localizeStrEnum.RESET_NETWORK]: "Reset Network", + [localizeStrEnum.REINSTALL_PLUGIN]: "Reinstall Plugin", + [localizeStrEnum.UPDATE_TO]: "Update to", + [localizeStrEnum.INSTALLED_VERSION]: "Installed Version", + [localizeStrEnum.LATEST_VERSION]: "Latest Version", +}; diff --git a/src/i18n/french.json b/src/i18n/french.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/french.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/german.json b/src/i18n/german.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/german.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100755 index 0000000..5725717 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,2 @@ +export * from "./localization" +export * from "./localizeMap" \ No newline at end of file diff --git a/src/i18n/italian.json b/src/i18n/italian.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/italian.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/japanese.json b/src/i18n/japanese.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/japanese.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/koreana.json b/src/i18n/koreana.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/koreana.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/localization.ts b/src/i18n/localization.ts new file mode 100755 index 0000000..48f21ea --- /dev/null +++ b/src/i18n/localization.ts @@ -0,0 +1,46 @@ +import { defaultLocale, localizeMap, localizeStrEnum } from "./localizeMap"; + +import i18n, { Resource } from "i18next"; + +export class localizationManager { + private static language = "english"; + + public static async init() { + const language = + (await SteamClient.Settings.GetCurrentLanguage()) || "english"; + this.language = language; + console.log(">>>>>>>>>> Language: " + this.language); + + const resources: Resource = Object.keys(localizeMap).reduce( + (acc: Resource, key) => { + acc[localizeMap[key].locale] = { + translation: localizeMap[key].strings, + }; + return acc; + }, + {} + ); + + i18n.init({ + resources: resources, + lng: this.getLocale(), // 目标语言 + fallbackLng: defaultLocale, // 回落语言 + returnEmptyString: false, // 空字符串不返回, 使用回落语言 + interpolation: { + escapeValue: false, + }, + }); + } + + private static getLocale() { + return localizeMap[this.language]?.locale ?? defaultLocale; + } + + public static getString( + defaultString: localizeStrEnum, + variables?: Record + ) { + console.log(">>>>>>>>>> getString: " + defaultString); + return i18n.t(defaultString, variables); + } +} diff --git a/src/i18n/localizeMap.ts b/src/i18n/localizeMap.ts new file mode 100755 index 0000000..36e2f6a --- /dev/null +++ b/src/i18n/localizeMap.ts @@ -0,0 +1,125 @@ +import * as schinese from "./schinese.json"; +import * as tchinese from "./tchinese.json"; +import * as english from "./english.json"; +import * as german from "./german.json"; +import * as japanese from "./japanese.json"; +import * as koreana from "./koreana.json"; +import * as thai from "./thai.json"; +import * as bulgarian from "./bulgarian.json"; +import * as italian from "./italian.json"; +import * as french from "./french.json"; + + +export interface LanguageProps { + label: string; + strings: any; + credit: string[]; + locale: string; +} + +export const defaultLanguage = "english"; +export const defaultLocale = "en"; +export const defaultMessages = english; + +export const localizeMap: { [key: string]: LanguageProps } = { + schinese: { + label: "简体中文", + strings: schinese, + credit: ["yxx"], + locale: "zh-CN", + }, + tchinese: { + label: "繁體中文", + strings: tchinese, + credit: [], + locale: "zh-TW", + }, + english: { + label: "English", + strings: english, + credit: [], + locale: "en", + }, + german: { + label: "Deutsch", + strings: german, + credit: ["dctr"], + locale: "de", + }, + japanese: { + label: "日本語", + strings: japanese, + credit: [], + locale: "ja", + }, + koreana: { + label: "한국어", + strings: koreana, + credit: [], + locale: "ko", + }, + thai: { + label: "ไทย", + strings: thai, + credit: [], + locale: "th", + }, + bulgarian: { + label: "Български", + strings: bulgarian, + credit: [], + locale: "bg", + }, + italian: { + label: "Italiano", + strings: italian, + credit: [], + locale: "it", + }, + french: { + label: "Français", + strings: french, + credit: [], + locale: "fr", + }, +}; + +export enum localizeStrEnum { + SERVICE = "SERVICE", + TOOLS = "TOOLS", + VERSION = "VERSION", + ABOUT = "ABOUT", + DEBUG = "DEBUG", + + // Subscriptions manager + SUBSCRIPTIONS = "SUBSCRIPTIONS", + SUBSCRIPTIONS_LINK = "SUBSCRIPTIONS_LINK", + SELECT_SUBSCRIPTION = "SELECT_SUBSCRIPTION", + DOWNLOAD = "DOWNLOAD", + UPDATE_ALL = "UPDATE_ALL", + DELETE = "DELETE", + + // QAM + ENABLE_CLASH = "ENABLE_CLASH", + ENABLE_CLASH_DESC = "ENABLE_CLASH_DESC", + ENABLE_CLASH_FAILED = "ENABLE_CLASH_FAILED", + ENABLE_CLASH_LOADING = "ENABLE_CLASH_LOADING", + ENABLE_CLASH_IS_RUNNING = "ENABLE_CLASH_IS_RUNNING", + MANAGE_SUBSCRIPTIONS = "MANAGE_SUBSCRIPTIONS", + OPEN_DASHBOARD = "OPEN_DASHBOARD", + SELECT_DASHBOARD = "SELECT_DASHBOARD", + ALLOW_REMOTE_ACCESS = "ALLOW_REMOTE_ACCESS", + ALLOW_REMOTE_ACCESS_DESC = "ALLOW_REMOTE_ACCESS_DESC", + SKIP_PROXY = "SKIP_PROXY", + SKIP_PROXY_DESC = "SKIP_PROXY_DESC", + OVERRIDE_DNS = "OVERRIDE_DNS", + OVERRIDE_DNS_DESC = "OVERRIDE_DNS_DESC", + ENHANCED_MODE = "ENHANCED_MODE", + ENHANCED_MODE_DESC = "ENHANCED_MODE_DESC", + RESTART_CORE = "RESTART_CORE", + RESET_NETWORK = "RESET_NETWORK", + REINSTALL_PLUGIN = "REINSTALL_PLUGIN", + UPDATE_TO = "UPDATE_TO", + INSTALLED_VERSION = "INSTALLED_VERSION", + LATEST_VERSION = "LATEST_VERSION", +} diff --git a/src/i18n/schinese.json b/src/i18n/schinese.json new file mode 100644 index 0000000..b800ad2 --- /dev/null +++ b/src/i18n/schinese.json @@ -0,0 +1,35 @@ +{ + "SERVICE": "服务", + "TOOLS": "工具", + "VERSION": "版本", + "ABOUT": "关于", + "DEBUG": "调试", + "SUBSCRIPTIONS": "订阅", + "SUBSCRIPTIONS_LINK": "订阅链接", + "SELECT_SUBSCRIPTION": "选择订阅", + "DOWNLOAD": "下载", + "UPDATE_ALL": "更新所有", + "DELETE": "删除", + "ENABLE_CLASH": "启用 Clash", + "ENABLE_CLASH_DESC": "在后台运行 Clash", + "ENABLE_CLASH_FAILED": "启动失败,请检查 /tmp/tomoon.log", + "ENABLE_CLASH_LOADING": "加载中 ...", + "ENABLE_CLASH_IS_RUNNING": "Clash 正在运行", + "MANAGE_SUBSCRIPTIONS": "管理订阅", + "OPEN_DASHBOARD": "打开 Dashboard", + "SELECT_DASHBOARD": "选择 Dashboard", + "ALLOW_REMOTE_ACCESS": "允许远程访问", + "ALLOW_REMOTE_ACCESS_DESC": "允许远程访问 Dashboard", + "SKIP_PROXY": "跳过代理", + "SKIP_PROXY_DESC": "Steam 下载不经过代理", + "OVERRIDE_DNS": "覆盖 DNS 设置", + "OVERRIDE_DNS_DESC": "强制 Clash 拦截 DNS 查询", + "ENHANCED_MODE": "增强模式", + "ENHANCED_MODE_DESC": "增强模式", + "RESTART_CORE": "重启核心", + "RESET_NETWORK": "重置网络", + "REINSTALL_PLUGIN": "重新安装插件", + "UPDATE_TO": "更新到", + "INSTALLED_VERSION": "已安装版本", + "LATEST_VERSION": "最新版本" +} \ No newline at end of file diff --git a/src/i18n/schinese.ts b/src/i18n/schinese.ts new file mode 100644 index 0000000..36c9c74 --- /dev/null +++ b/src/i18n/schinese.ts @@ -0,0 +1,37 @@ +import { localizeStrEnum } from "./localizeMap"; + +export const schinese: Record = { + [localizeStrEnum.SERVICE]: "服务", + [localizeStrEnum.TOOLS]: "工具", + [localizeStrEnum.VERSION]: "版本", + [localizeStrEnum.ABOUT]: "关于", + [localizeStrEnum.DEBUG]: "调试", + [localizeStrEnum.SUBSCRIPTIONS]: "订阅", + [localizeStrEnum.SUBSCRIPTIONS_LINK]: "订阅链接", + [localizeStrEnum.SELECT_SUBSCRIPTION]: "选择订阅", + [localizeStrEnum.DOWNLOAD]: "下载", + [localizeStrEnum.UPDATE_ALL]: "更新所有", + [localizeStrEnum.DELETE]: "删除", + [localizeStrEnum.ENABLE_CLASH]: "启用 Clash", + [localizeStrEnum.ENABLE_CLASH_DESC]: "在后台运行 Clash", + [localizeStrEnum.ENABLE_CLASH_FAILED]: "启动失败,请检查 /tmp/tomoon.log", + [localizeStrEnum.ENABLE_CLASH_LOADING]: "加载中 ...", + [localizeStrEnum.ENABLE_CLASH_IS_RUNNING]: "Clash 正在运行。", + [localizeStrEnum.MANAGE_SUBSCRIPTIONS]: "管理订阅", + [localizeStrEnum.OPEN_DASHBOARD]: "打开 Dashboard", + [localizeStrEnum.SELECT_DASHBOARD]: "选择 Dashboard", + [localizeStrEnum.ALLOW_REMOTE_ACCESS]: "允许远程访问", + [localizeStrEnum.ALLOW_REMOTE_ACCESS_DESC]: "允许远程访问 Dashboard", + [localizeStrEnum.SKIP_PROXY]: "跳过代理", + [localizeStrEnum.SKIP_PROXY_DESC]: "Steam 下载不经过代理", + [localizeStrEnum.OVERRIDE_DNS]: "Override DNS", + [localizeStrEnum.OVERRIDE_DNS_DESC]: "强制 Clash 拦截 DNS 查询", + [localizeStrEnum.ENHANCED_MODE]: "增强模式", + [localizeStrEnum.ENHANCED_MODE_DESC]: "增强模式", + [localizeStrEnum.RESTART_CORE]: "重启核心", + [localizeStrEnum.RESET_NETWORK]: "重置网络", + [localizeStrEnum.REINSTALL_PLUGIN]: "重新安装插件", + [localizeStrEnum.UPDATE_TO]: "更新到", + [localizeStrEnum.INSTALLED_VERSION]: "已安装版本", + [localizeStrEnum.LATEST_VERSION]: "最新版本", +}; diff --git a/src/i18n/tchinese.json b/src/i18n/tchinese.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/tchinese.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/i18n/thai.json b/src/i18n/thai.json new file mode 100755 index 0000000..9e26dfe --- /dev/null +++ b/src/i18n/thai.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 8bda344..b9a1070 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,6 +23,7 @@ import * as backend from "./backend/backend"; import { ApiCallBackend, PyBackend, EnhancedMode } from "./backend"; import { ActionButtonItem, VersionComponent } from "./components"; +import { localizationManager, localizeStrEnum } from "./i18n"; let enabledGlobal = false; let enabledSkipProxy = false; @@ -37,7 +38,7 @@ let current_dashboard = ""; let allow_remote_access = false; let _secret = ""; -const Content: FC<{}> = ({ }) => { +const Content: FC<{}> = ({}) => { if (!usdplReady) { return Init...; } @@ -50,7 +51,9 @@ const Content: FC<{}> = ({ }) => { !enabledGlobal ); const [isSelectionDisabled, setIsSelectionDisabled] = useState(false); - const [SelectionTips, setSelectionTips] = useState("Run Clash in background"); + const [SelectionTips, setSelectionTips] = useState( + localizationManager.getString(localizeStrEnum.ENABLE_CLASH_DESC) + ); const [skipProxyState, setSkipProxyState] = useState(enabledSkipProxy); const [overrideDNSState, setOverrideDNSState] = useState(enabledOverrideDNS); const [currentSub, setCurrentSub] = useState(current_sub); @@ -179,15 +182,21 @@ const Content: FC<{}> = ({ }) => { return (
- + { setIsSelectionDisabled(true); - setSelectionTips("Loading ..."); + setSelectionTips( + localizationManager.getString( + localizeStrEnum.ENABLE_CLASH_LOADING + ) + ); backend.resolve(backend.setEnabled(value), (v: boolean) => { enabledGlobal = v; setIsSelectionDisabled(false); @@ -199,16 +208,26 @@ const Content: FC<{}> = ({ }) => { // console.log(v); switch (v) { case "Loading": - setSelectionTips("Loading ..."); + setSelectionTips( + localizationManager.getString( + localizeStrEnum.ENABLE_CLASH_LOADING + ) + ); break; case "Failed": setSelectionTips( - "Failed to start, please check /tmp/tomoon.log" + localizationManager.getString( + localizeStrEnum.ENABLE_CLASH_FAILED + ) ); setClashState(false); break; case "Success": - setSelectionTips("Clash is running."); + setSelectionTips( + localizationManager.getString( + localizeStrEnum.ENABLE_CLASH_IS_RUNNING + ) + ); getConfig(); break; } @@ -218,7 +237,11 @@ const Content: FC<{}> = ({ }) => { }); }, 500); } else { - setSelectionTips("Run Clash in background"); + setSelectionTips( + localizationManager.getString( + localizeStrEnum.ENABLE_CLASH_DESC + ) + ); } setOptionDropdownDisabled(value); setOpenDashboardDisabled(!value); @@ -229,7 +252,9 @@ const Content: FC<{}> = ({ }) => { { @@ -255,7 +280,9 @@ const Content: FC<{}> = ({ }) => { Router.Navigate("/tomoon-config"); }} > - Manage Subscriptions + {localizationManager.getString( + localizeStrEnum.MANAGE_SUBSCRIPTIONS + )} @@ -292,13 +319,17 @@ const Content: FC<{}> = ({ }) => { }} disabled={openDashboardDisabled} > - Open Dashboard + {localizationManager.getString(localizeStrEnum.OPEN_DASHBOARD)} { return { label: path.split("/").pop(), @@ -316,8 +347,12 @@ const Content: FC<{}> = ({ }) => { { ApiCallBackend.allowRemoteAccess(value); @@ -327,8 +362,10 @@ const Content: FC<{}> = ({ }) => { { ApiCallBackend.skipProxy(value); @@ -338,8 +375,10 @@ const Content: FC<{}> = ({ }) => { { ApiCallBackend.overrideDns(value); @@ -350,7 +389,9 @@ const Content: FC<{}> = ({ }) => { {overrideDNSState && ( = ({ }) => { ApiCallBackend.restartClash(); }} > - Restart Core + {localizationManager.getString(localizeStrEnum.RESTART_CORE)} - + = ({ }) => { }); }} > - Reset Network + {localizationManager.getString(localizeStrEnum.RESET_NETWORK)} @@ -406,17 +449,17 @@ const DeckyPluginRouterTest: FC = () => { showTitle pages={[ { - title: "Subscriptions", + title: localizationManager.getString(localizeStrEnum.SUBSCRIPTIONS), content: , route: "/tomoon-config/subscriptions", }, { - title: "About", + title: localizationManager.getString(localizeStrEnum.ABOUT), content: , route: "/tomoon-config/about", }, { - title: "Debug", + title: localizationManager.getString(localizeStrEnum.DEBUG), content: , route: "/tomoon-config/debug", }, @@ -430,6 +473,7 @@ export default definePlugin(() => { (async function () { await backend.initBackend(); await backend.PyBackend.init(); + await localizationManager.init(); usdplReady = true; backend.resolve(backend.getEnabled(), (v: boolean) => { enabledGlobal = v; diff --git a/src/pages/Subscriptions.tsx b/src/pages/Subscriptions.tsx index 7632503..1a039f7 100644 --- a/src/pages/Subscriptions.tsx +++ b/src/pages/Subscriptions.tsx @@ -6,6 +6,7 @@ import { QRCodeCanvas } from "qrcode.react"; import * as backend from "../backend/backend"; import axios from "axios"; +import { localizationManager, localizeStrEnum } from "../i18n"; interface SubProp { Subscriptions: Array; @@ -128,7 +129,7 @@ export const Subscriptions: FC = ({ Subscriptions }) => {
setText(e?.target.value)} description={downloadTips} @@ -145,7 +146,7 @@ export const Subscriptions: FC = ({ Subscriptions }) => { checkStatusHandler = setInterval(refreshDownloadStatus, 500); }} > - Download + {localizationManager.getString(localizeStrEnum.DOWNLOAD)} = ({ Subscriptions }) => { }} disabled={updateBtnDisable} > - Update All + {localizationManager.getString(localizeStrEnum.UPDATE_ALL)} diff --git a/tsconfig.json b/tsconfig.json index a6f8c2c..38b4f2c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "strict": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, + "resolveJsonModule":true, "lib": ["DOM", "DOM.Iterable", "ESNext"], "types": ["react/jsx-runtime", "react-dom"] }, From 28eefade822d71e88e99dcab1a12a42510c24f23 Mon Sep 17 00:00:00 2001 From: honjow Date: Fri, 14 Mar 2025 21:47:27 +0800 Subject: [PATCH 2/2] refactor: optimize i18n implementation with key constant object --- src/components/SubList.tsx | 4 +- src/components/Version.tsx | 21 +++------ src/i18n/english.ts | 39 ---------------- src/i18n/localization.ts | 4 +- src/i18n/localizeMap.ts | 89 ++++++++++++++++++++++--------------- src/i18n/schinese.ts | 37 --------------- src/index.tsx | 80 +++++++++++---------------------- src/pages/Subscriptions.tsx | 8 ++-- 8 files changed, 94 insertions(+), 188 deletions(-) delete mode 100755 src/i18n/english.ts delete mode 100644 src/i18n/schinese.ts diff --git a/src/components/SubList.tsx b/src/components/SubList.tsx index 21cfb0f..1cda0e2 100644 --- a/src/components/SubList.tsx +++ b/src/components/SubList.tsx @@ -1,7 +1,7 @@ import { ButtonItem } from "@decky/ui"; import { FC } from "react"; import * as backend from "../backend/backend"; -import { localizationManager, localizeStrEnum } from "../i18n"; +import { localizationManager, L } from "../i18n"; interface appProp { Subscriptions: Array; UpdateSub: any; @@ -28,7 +28,7 @@ export const SubList: FC = ({ Subscriptions, UpdateSub, Refresh }) => { Refresh(); }} > - {localizationManager.getString(localizeStrEnum.DELETE)} + {localizationManager.getString(L.DELETE)}
); diff --git a/src/components/Version.tsx b/src/components/Version.tsx index 6a6e7ba..ddcb689 100644 --- a/src/components/Version.tsx +++ b/src/components/Version.tsx @@ -2,7 +2,7 @@ import { PanelSection, PanelSectionRow, Field } from "@decky/ui"; import { FC, useEffect, useState } from "react"; import { PyBackend } from "../backend/backend"; import { ActionButtonItem } from "."; -import { localizationManager, localizeStrEnum } from "../i18n"; +import { localizationManager, L } from "../i18n"; export const VersionComponent: FC = () => { const [currentVersion, _] = useState( @@ -21,20 +21,15 @@ export const VersionComponent: FC = () => { getData(); }); - let uptButtonText = localizationManager.getString( - localizeStrEnum.REINSTALL_PLUGIN - ); + let uptButtonText = localizationManager.getString(L.REINSTALL_PLUGIN); if (currentVersion !== latestVersion && Boolean(latestVersion)) { uptButtonText = - localizationManager.getString(localizeStrEnum.UPDATE_TO) + - ` ${latestVersion}`; + localizationManager.getString(L.UPDATE_TO) + ` ${latestVersion}`; } return ( - + { {currentVersion} @@ -59,9 +52,7 @@ export const VersionComponent: FC = () => { {latestVersion} diff --git a/src/i18n/english.ts b/src/i18n/english.ts deleted file mode 100755 index d7e7a92..0000000 --- a/src/i18n/english.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { localizeStrEnum } from "./localizeMap"; - -export const english: Record = { - [localizeStrEnum.SERVICE]: "Service", - [localizeStrEnum.TOOLS]: "Tools", - [localizeStrEnum.VERSION]: "Version", - [localizeStrEnum.ABOUT]: "About", - [localizeStrEnum.DEBUG]: "Debug", - [localizeStrEnum.SUBSCRIPTIONS]: "Subscriptions", - [localizeStrEnum.SUBSCRIPTIONS_LINK]: "Subscriptions", - [localizeStrEnum.SELECT_SUBSCRIPTION]: "Select a Subscription", - [localizeStrEnum.DOWNLOAD]: "Download", - [localizeStrEnum.UPDATE_ALL]: "Update All", - [localizeStrEnum.DELETE]: "Delete", - [localizeStrEnum.ENABLE_CLASH]: "Enable Clash", - [localizeStrEnum.ENABLE_CLASH_DESC]: "Run Clash in background", - [localizeStrEnum.ENABLE_CLASH_FAILED]: - "Failed to start, please check /tmp/tomoon.log", - [localizeStrEnum.ENABLE_CLASH_LOADING]: "Loading ...", - [localizeStrEnum.ENABLE_CLASH_IS_RUNNING]: "Clash is running.", - [localizeStrEnum.MANAGE_SUBSCRIPTIONS]: "Manage Subscriptions", - [localizeStrEnum.OPEN_DASHBOARD]: "Open Dashboard", - [localizeStrEnum.SELECT_DASHBOARD]: "Select Dashboard", - [localizeStrEnum.ALLOW_REMOTE_ACCESS]: "Allow Remote Access", - [localizeStrEnum.ALLOW_REMOTE_ACCESS_DESC]: - "Allow Remote Access to Dashboard", - [localizeStrEnum.SKIP_PROXY]: "Skip Proxy", - [localizeStrEnum.SKIP_PROXY_DESC]: "Enable for direct Steam downloads", - [localizeStrEnum.OVERRIDE_DNS]: "Override DNS", - [localizeStrEnum.OVERRIDE_DNS_DESC]: "Force Clash to hijack DNS query", - [localizeStrEnum.ENHANCED_MODE]: "Enhanced Mode", - [localizeStrEnum.ENHANCED_MODE_DESC]: "Enhanced Mode", - [localizeStrEnum.RESTART_CORE]: "Restart Core", - [localizeStrEnum.RESET_NETWORK]: "Reset Network", - [localizeStrEnum.REINSTALL_PLUGIN]: "Reinstall Plugin", - [localizeStrEnum.UPDATE_TO]: "Update to", - [localizeStrEnum.INSTALLED_VERSION]: "Installed Version", - [localizeStrEnum.LATEST_VERSION]: "Latest Version", -}; diff --git a/src/i18n/localization.ts b/src/i18n/localization.ts index 48f21ea..f59c81f 100755 --- a/src/i18n/localization.ts +++ b/src/i18n/localization.ts @@ -1,4 +1,4 @@ -import { defaultLocale, localizeMap, localizeStrEnum } from "./localizeMap"; +import { defaultLocale, localizeMap, LocalizeStrKey } from "./localizeMap"; import i18n, { Resource } from "i18next"; @@ -37,7 +37,7 @@ export class localizationManager { } public static getString( - defaultString: localizeStrEnum, + defaultString: LocalizeStrKey, variables?: Record ) { console.log(">>>>>>>>>> getString: " + defaultString); diff --git a/src/i18n/localizeMap.ts b/src/i18n/localizeMap.ts index 36e2f6a..21cf1e5 100755 --- a/src/i18n/localizeMap.ts +++ b/src/i18n/localizeMap.ts @@ -9,7 +9,6 @@ import * as bulgarian from "./bulgarian.json"; import * as italian from "./italian.json"; import * as french from "./french.json"; - export interface LanguageProps { label: string; strings: any; @@ -84,42 +83,60 @@ export const localizeMap: { [key: string]: LanguageProps } = { }, }; -export enum localizeStrEnum { - SERVICE = "SERVICE", - TOOLS = "TOOLS", - VERSION = "VERSION", - ABOUT = "ABOUT", - DEBUG = "DEBUG", +// 创建一个类型安全的常量生成函数 +function createLocalizeConstants(keys: T) { + return keys.reduce((obj, key) => { + obj[key as keyof typeof obj] = key; + return obj; + }, {} as { [K in T[number]]: K }); +} + +// 定义所有键名 +const I18N_KEYS = [ + "SERVICE", + "TOOLS", + "VERSION", + "ABOUT", + "DEBUG", // Subscriptions manager - SUBSCRIPTIONS = "SUBSCRIPTIONS", - SUBSCRIPTIONS_LINK = "SUBSCRIPTIONS_LINK", - SELECT_SUBSCRIPTION = "SELECT_SUBSCRIPTION", - DOWNLOAD = "DOWNLOAD", - UPDATE_ALL = "UPDATE_ALL", - DELETE = "DELETE", + "SUBSCRIPTIONS", + "SUBSCRIPTIONS_LINK", + "SELECT_SUBSCRIPTION", + "DOWNLOAD", + "UPDATE_ALL", + "DELETE", // QAM - ENABLE_CLASH = "ENABLE_CLASH", - ENABLE_CLASH_DESC = "ENABLE_CLASH_DESC", - ENABLE_CLASH_FAILED = "ENABLE_CLASH_FAILED", - ENABLE_CLASH_LOADING = "ENABLE_CLASH_LOADING", - ENABLE_CLASH_IS_RUNNING = "ENABLE_CLASH_IS_RUNNING", - MANAGE_SUBSCRIPTIONS = "MANAGE_SUBSCRIPTIONS", - OPEN_DASHBOARD = "OPEN_DASHBOARD", - SELECT_DASHBOARD = "SELECT_DASHBOARD", - ALLOW_REMOTE_ACCESS = "ALLOW_REMOTE_ACCESS", - ALLOW_REMOTE_ACCESS_DESC = "ALLOW_REMOTE_ACCESS_DESC", - SKIP_PROXY = "SKIP_PROXY", - SKIP_PROXY_DESC = "SKIP_PROXY_DESC", - OVERRIDE_DNS = "OVERRIDE_DNS", - OVERRIDE_DNS_DESC = "OVERRIDE_DNS_DESC", - ENHANCED_MODE = "ENHANCED_MODE", - ENHANCED_MODE_DESC = "ENHANCED_MODE_DESC", - RESTART_CORE = "RESTART_CORE", - RESET_NETWORK = "RESET_NETWORK", - REINSTALL_PLUGIN = "REINSTALL_PLUGIN", - UPDATE_TO = "UPDATE_TO", - INSTALLED_VERSION = "INSTALLED_VERSION", - LATEST_VERSION = "LATEST_VERSION", -} + "ENABLE_CLASH", + "ENABLE_CLASH_DESC", + "ENABLE_CLASH_FAILED", + "ENABLE_CLASH_LOADING", + "ENABLE_CLASH_IS_RUNNING", + "MANAGE_SUBSCRIPTIONS", + "OPEN_DASHBOARD", + "SELECT_DASHBOARD", + "ALLOW_REMOTE_ACCESS", + "ALLOW_REMOTE_ACCESS_DESC", + "SKIP_PROXY", + "SKIP_PROXY_DESC", + "OVERRIDE_DNS", + "OVERRIDE_DNS_DESC", + "ENHANCED_MODE", + "ENHANCED_MODE_DESC", + "RESTART_CORE", + "RESET_NETWORK", + "REINSTALL_PLUGIN", + "UPDATE_TO", + "INSTALLED_VERSION", + "LATEST_VERSION", +] as const; + +// 创建常量对象并导出 +export const L = createLocalizeConstants(I18N_KEYS); + +// 导出类型 +export type LocalizeStrKey = keyof typeof L; + +// 为了向后兼容,保留 localizeStrEnum 名称 +// export const localizeStrEnum = L; diff --git a/src/i18n/schinese.ts b/src/i18n/schinese.ts deleted file mode 100644 index 36c9c74..0000000 --- a/src/i18n/schinese.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { localizeStrEnum } from "./localizeMap"; - -export const schinese: Record = { - [localizeStrEnum.SERVICE]: "服务", - [localizeStrEnum.TOOLS]: "工具", - [localizeStrEnum.VERSION]: "版本", - [localizeStrEnum.ABOUT]: "关于", - [localizeStrEnum.DEBUG]: "调试", - [localizeStrEnum.SUBSCRIPTIONS]: "订阅", - [localizeStrEnum.SUBSCRIPTIONS_LINK]: "订阅链接", - [localizeStrEnum.SELECT_SUBSCRIPTION]: "选择订阅", - [localizeStrEnum.DOWNLOAD]: "下载", - [localizeStrEnum.UPDATE_ALL]: "更新所有", - [localizeStrEnum.DELETE]: "删除", - [localizeStrEnum.ENABLE_CLASH]: "启用 Clash", - [localizeStrEnum.ENABLE_CLASH_DESC]: "在后台运行 Clash", - [localizeStrEnum.ENABLE_CLASH_FAILED]: "启动失败,请检查 /tmp/tomoon.log", - [localizeStrEnum.ENABLE_CLASH_LOADING]: "加载中 ...", - [localizeStrEnum.ENABLE_CLASH_IS_RUNNING]: "Clash 正在运行。", - [localizeStrEnum.MANAGE_SUBSCRIPTIONS]: "管理订阅", - [localizeStrEnum.OPEN_DASHBOARD]: "打开 Dashboard", - [localizeStrEnum.SELECT_DASHBOARD]: "选择 Dashboard", - [localizeStrEnum.ALLOW_REMOTE_ACCESS]: "允许远程访问", - [localizeStrEnum.ALLOW_REMOTE_ACCESS_DESC]: "允许远程访问 Dashboard", - [localizeStrEnum.SKIP_PROXY]: "跳过代理", - [localizeStrEnum.SKIP_PROXY_DESC]: "Steam 下载不经过代理", - [localizeStrEnum.OVERRIDE_DNS]: "Override DNS", - [localizeStrEnum.OVERRIDE_DNS_DESC]: "强制 Clash 拦截 DNS 查询", - [localizeStrEnum.ENHANCED_MODE]: "增强模式", - [localizeStrEnum.ENHANCED_MODE_DESC]: "增强模式", - [localizeStrEnum.RESTART_CORE]: "重启核心", - [localizeStrEnum.RESET_NETWORK]: "重置网络", - [localizeStrEnum.REINSTALL_PLUGIN]: "重新安装插件", - [localizeStrEnum.UPDATE_TO]: "更新到", - [localizeStrEnum.INSTALLED_VERSION]: "已安装版本", - [localizeStrEnum.LATEST_VERSION]: "最新版本", -}; diff --git a/src/index.tsx b/src/index.tsx index b9a1070..b4d2fae 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,7 +23,7 @@ import * as backend from "./backend/backend"; import { ApiCallBackend, PyBackend, EnhancedMode } from "./backend"; import { ActionButtonItem, VersionComponent } from "./components"; -import { localizationManager, localizeStrEnum } from "./i18n"; +import { localizationManager, L } from "./i18n"; let enabledGlobal = false; let enabledSkipProxy = false; @@ -52,7 +52,7 @@ const Content: FC<{}> = ({}) => { ); const [isSelectionDisabled, setIsSelectionDisabled] = useState(false); const [SelectionTips, setSelectionTips] = useState( - localizationManager.getString(localizeStrEnum.ENABLE_CLASH_DESC) + localizationManager.getString(L.ENABLE_CLASH_DESC) ); const [skipProxyState, setSkipProxyState] = useState(enabledSkipProxy); const [overrideDNSState, setOverrideDNSState] = useState(enabledOverrideDNS); @@ -182,20 +182,16 @@ const Content: FC<{}> = ({}) => { return (
- + { setIsSelectionDisabled(true); setSelectionTips( - localizationManager.getString( - localizeStrEnum.ENABLE_CLASH_LOADING - ) + localizationManager.getString(L.ENABLE_CLASH_LOADING) ); backend.resolve(backend.setEnabled(value), (v: boolean) => { enabledGlobal = v; @@ -209,23 +205,19 @@ const Content: FC<{}> = ({}) => { switch (v) { case "Loading": setSelectionTips( - localizationManager.getString( - localizeStrEnum.ENABLE_CLASH_LOADING - ) + localizationManager.getString(L.ENABLE_CLASH_LOADING) ); break; case "Failed": setSelectionTips( - localizationManager.getString( - localizeStrEnum.ENABLE_CLASH_FAILED - ) + localizationManager.getString(L.ENABLE_CLASH_FAILED) ); setClashState(false); break; case "Success": setSelectionTips( localizationManager.getString( - localizeStrEnum.ENABLE_CLASH_IS_RUNNING + L.ENABLE_CLASH_IS_RUNNING ) ); getConfig(); @@ -238,9 +230,7 @@ const Content: FC<{}> = ({}) => { }, 500); } else { setSelectionTips( - localizationManager.getString( - localizeStrEnum.ENABLE_CLASH_DESC - ) + localizationManager.getString(L.ENABLE_CLASH_DESC) ); } setOptionDropdownDisabled(value); @@ -253,7 +243,7 @@ const Content: FC<{}> = ({}) => { = ({}) => { Router.Navigate("/tomoon-config"); }} > - {localizationManager.getString( - localizeStrEnum.MANAGE_SUBSCRIPTIONS - )} + {localizationManager.getString(L.MANAGE_SUBSCRIPTIONS)} @@ -319,17 +307,13 @@ const Content: FC<{}> = ({}) => { }} disabled={openDashboardDisabled} > - {localizationManager.getString(localizeStrEnum.OPEN_DASHBOARD)} + {localizationManager.getString(L.OPEN_DASHBOARD)} { return { label: path.split("/").pop(), @@ -347,11 +331,9 @@ const Content: FC<{}> = ({}) => { { @@ -362,10 +344,8 @@ const Content: FC<{}> = ({}) => { { ApiCallBackend.skipProxy(value); @@ -375,10 +355,8 @@ const Content: FC<{}> = ({}) => { { ApiCallBackend.overrideDns(value); @@ -389,9 +367,7 @@ const Content: FC<{}> = ({}) => { {overrideDNSState && ( = ({}) => { ApiCallBackend.restartClash(); }} > - {localizationManager.getString(localizeStrEnum.RESTART_CORE)} + {localizationManager.getString(L.RESTART_CORE)} - + = ({}) => { }); }} > - {localizationManager.getString(localizeStrEnum.RESET_NETWORK)} + {localizationManager.getString(L.RESET_NETWORK)} @@ -449,17 +423,17 @@ const DeckyPluginRouterTest: FC = () => { showTitle pages={[ { - title: localizationManager.getString(localizeStrEnum.SUBSCRIPTIONS), + title: localizationManager.getString(L.SUBSCRIPTIONS), content: , route: "/tomoon-config/subscriptions", }, { - title: localizationManager.getString(localizeStrEnum.ABOUT), + title: localizationManager.getString(L.ABOUT), content: , route: "/tomoon-config/about", }, { - title: localizationManager.getString(localizeStrEnum.DEBUG), + title: localizationManager.getString(L.DEBUG), content: , route: "/tomoon-config/debug", }, diff --git a/src/pages/Subscriptions.tsx b/src/pages/Subscriptions.tsx index 1a039f7..debf656 100644 --- a/src/pages/Subscriptions.tsx +++ b/src/pages/Subscriptions.tsx @@ -6,7 +6,7 @@ import { QRCodeCanvas } from "qrcode.react"; import * as backend from "../backend/backend"; import axios from "axios"; -import { localizationManager, localizeStrEnum } from "../i18n"; +import { localizationManager, L } from "../i18n"; interface SubProp { Subscriptions: Array; @@ -129,7 +129,7 @@ export const Subscriptions: FC = ({ Subscriptions }) => {
setText(e?.target.value)} description={downloadTips} @@ -146,7 +146,7 @@ export const Subscriptions: FC = ({ Subscriptions }) => { checkStatusHandler = setInterval(refreshDownloadStatus, 500); }} > - {localizationManager.getString(localizeStrEnum.DOWNLOAD)} + {localizationManager.getString(L.DOWNLOAD)} = ({ Subscriptions }) => { }} disabled={updateBtnDisable} > - {localizationManager.getString(localizeStrEnum.UPDATE_ALL)} + {localizationManager.getString(L.UPDATE_ALL)}