mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #654 from Zagrios/chore/add-outdate-bs-version-warn-modal
[chore] Added a pre-download warning modal for outdated BS versions
(cherry picked from commit e17b7f70fd)
This commit is contained in:
@@ -14,7 +14,12 @@
|
||||
"refuse": "Ablehnen",
|
||||
"apply": "Übernehmen",
|
||||
"copy": "Kopieren",
|
||||
"copied": "Kopiert!"
|
||||
"copied": "Kopiert!",
|
||||
"confirm": "Bestätigen",
|
||||
"choose-folder": "Ordner Wählen",
|
||||
"unknown": "Ein unbekannter Fehler ist aufgetreten ¯\\_(ツ)_/¯",
|
||||
"warning": "Achtung",
|
||||
"continue": "Weiter"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Version Hinzufügen",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "Als Administrator starten",
|
||||
"not-remind-me": "Nicht mehr erinnern"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "Die Version {outdatedVersion} ist veraltet, und einige Mods oder Funktionen funktionieren möglicherweise nicht mehr wie erwartet. Bitte lade die neueste empfohlene Version ({recommendedVersion}) von Beat Saber herunter, um die neuesten Funktionen und Fehlerbehebungen zu nutzen."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "Refuse",
|
||||
"apply": "Apply",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied!"
|
||||
"copied": "Copied!",
|
||||
"confirm": "Confirm",
|
||||
"choose-folder": "Choose folder",
|
||||
"unknown": "An unknown error occurred ¯\\_(ツ)_/¯",
|
||||
"warning": "Warning",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Add a version",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "Launch as Administrator",
|
||||
"not-remind-me": "Do not remind me"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "The version {outdatedVersion} is outdated, and some mods or features may no longer work as expected. Please download the latest recommended version ({recommendedVersion}) of Beat Saber to enjoy the latest features and bugfixes."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "Rechazar",
|
||||
"apply": "Aplicar",
|
||||
"copy": "Copiar",
|
||||
"copied": "¡Copiado!"
|
||||
"copied": "¡Copiado!",
|
||||
"confirm": "Confirmar",
|
||||
"choose-folder": "Elige la carpeta",
|
||||
"unknown": "Se ha producido un error desconocido ¯\\_(ツ)_/¯",
|
||||
"warning": "Advertencia",
|
||||
"continue": "Continuar"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Agregar una versión",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "Iniciar como Administrador",
|
||||
"not-remind-me": "No volver a recordármelo"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "La versión {outdatedVersion} está obsoleta, y algunos mods o funciones pueden no funcionar como se espera. Por favor, descarga la última versión recomendada ({recommendedVersion}) de Beat Saber para disfrutar de las últimas funciones y correcciones de errores."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "Refuser",
|
||||
"apply": "Appliquer",
|
||||
"copy": "Copier",
|
||||
"copied": "Copié !"
|
||||
"copied": "Copié !",
|
||||
"confirm": "Confirmer",
|
||||
"choose-folder": "Choisir un dossier",
|
||||
"unknown": "Une erreur inconnue s'est produite ¯\\_(ツ)_/¯",
|
||||
"warning": "Attention",
|
||||
"continue": "Continuer"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Ajouter une version",
|
||||
@@ -736,6 +741,9 @@
|
||||
},
|
||||
"launch-as-admin": "Lancer en administateur",
|
||||
"not-remind-me": "Ne plus me rappeler"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "La version {outdatedVersion} est obsolète, et certains mods ou fonctionnalités peuvent ne plus fonctionner comme prévu. Veuillez télécharger la dernière version recommandée ({recommendedVersion}) de Beat Saber pour profiter des dernières fonctionnalités et corrections de bugs."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "拒否する",
|
||||
"apply": "適用",
|
||||
"copy": "コピー",
|
||||
"copied": "コピー済み!"
|
||||
"copied": "コピー済み!",
|
||||
"confirm": "確認",
|
||||
"choose-folder": "フォルダーを選択",
|
||||
"unknown": "不明なエラーが発生しました (´・ω・`)",
|
||||
"warning": "警告",
|
||||
"continue": "続ける"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "バージョンを追加",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "管理者として起動",
|
||||
"not-remind-me": "Больше не напоминать"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "バージョン {outdatedVersion} は古いため、一部のモッドや機能が期待通りに動作しない場合があります。最新の機能やバグ修正を楽しむために、推奨される最新バージョンの Beat Saber ({recommendedVersion}) をダウンロードしてください。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "Отказаться",
|
||||
"apply": "Применить",
|
||||
"copy": "Скопировать",
|
||||
"copied": "Скопировано!"
|
||||
"copied": "Скопировано!",
|
||||
"confirm": "Подтвердить",
|
||||
"choose-folder": "Изменить папку",
|
||||
"unknown": "Неизвестная ошибка ¯\\_(ツ)_/¯",
|
||||
"warning": "Внимание",
|
||||
"continue": "Продолжить"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "Добавить версию игры",
|
||||
@@ -734,6 +739,9 @@
|
||||
"info-3": "Обратите внимание, что не рекомендуется предоставлять права администратора Steam, так как это также влияет на установленные игры и моды, представляя угрозу безопасности."
|
||||
},
|
||||
"launch-as-admin": "Запустить от имени администратора"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "Версия {outdatedVersion} устарела, и некоторые моды или функции могут работать некорректно. Пожалуйста, скачайте последнюю рекомендуемую версию ({recommendedVersion}) Beat Saber, чтобы воспользоваться новейшими функциями и исправлениями ошибок."
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "拒絕",
|
||||
"apply": "應用",
|
||||
"copy": "複製",
|
||||
"copied": "已複製!"
|
||||
"copied": "已複製!",
|
||||
"confirm": "確認",
|
||||
"choose-folder": "選擇文件夾",
|
||||
"unknown": "發生了一個錯誤 ¯\\_(ツ)_/¯",
|
||||
"warning": "警告",
|
||||
"continue": "繼續"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "新增版本",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "以管理員身份啟動",
|
||||
"not-remind-me": "不再提醒我"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "版本 {outdatedVersion} 已過時,某些模組或功能可能無法正常運作。請下載最新推薦的 Beat Saber 版本 ({recommendedVersion}),以享受最新功能和修正。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
"refuse": "拒绝",
|
||||
"apply": "应用",
|
||||
"copy": "复制",
|
||||
"copied": "已复制!"
|
||||
"copied": "已复制!",
|
||||
"confirm": "确认",
|
||||
"choose-folder": "选择文件夹",
|
||||
"Unknown": "发生了一个错误 ¯\\_(ツ)_/¯",
|
||||
"warning": "警告",
|
||||
"continue": "继续"
|
||||
},
|
||||
"nav-bar": {
|
||||
"add-version": "添加版本",
|
||||
@@ -735,6 +740,9 @@
|
||||
},
|
||||
"launch-as-admin": "以管理员身份启动",
|
||||
"not-remind-me": "不再提醒我"
|
||||
},
|
||||
"bs-version-outdated": {
|
||||
"body": "版本 {outdatedVersion} 已过时,某些模组或功能可能无法正常工作。请下载最新推荐的 Beat Saber 版本 ({recommendedVersion}),以享受最新功能和修复。"
|
||||
}
|
||||
},
|
||||
"maps": {
|
||||
|
||||
Generated
+18
-13
@@ -21,7 +21,7 @@
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"framer-motion": "^10.16.16",
|
||||
"framer-motion": "^11.2.6",
|
||||
"fs-extra": "^11.2.0",
|
||||
"history": "^5.3.0",
|
||||
"is-elevated": "^3.0.0",
|
||||
@@ -1017,6 +1017,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
}
|
||||
@@ -1025,7 +1026,8 @@
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "1.3.3",
|
||||
@@ -8899,20 +8901,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "10.16.16",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.16.tgz",
|
||||
"integrity": "sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==",
|
||||
"version": "11.11.17",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.17.tgz",
|
||||
"integrity": "sha512-O8QzvoKiuzI5HSAHbcYuL6xU+ZLXbrH7C8Akaato4JzQbX2ULNeniqC2Vo5eiCtFktX9XsJ+7nUhxcl2E2IjpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@emotion/is-prop-valid": "^0.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -19631,6 +19635,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
}
|
||||
@@ -19639,7 +19644,8 @@
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "1.3.3",
|
||||
@@ -25602,11 +25608,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"framer-motion": {
|
||||
"version": "10.16.16",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.16.tgz",
|
||||
"integrity": "sha512-je6j91rd7NmUX7L1XHouwJ4v3R+SO4umso2LUcgOct3rHZ0PajZ80ETYZTajzEXEl9DlKyzjyt4AvGQ+lrebOw==",
|
||||
"version": "11.11.17",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.11.17.tgz",
|
||||
"integrity": "sha512-O8QzvoKiuzI5HSAHbcYuL6xU+ZLXbrH7C8Akaato4JzQbX2ULNeniqC2Vo5eiCtFktX9XsJ+7nUhxcl2E2IjpA==",
|
||||
"requires": {
|
||||
"@emotion/is-prop-valid": "^0.8.2",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
@@ -223,7 +223,7 @@
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.1.7",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"framer-motion": "^10.16.16",
|
||||
"framer-motion": "^11.2.6",
|
||||
"fs-extra": "^11.2.0",
|
||||
"history": "^5.3.0",
|
||||
"is-elevated": "^3.0.0",
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
|
||||
import { useTranslationV2 } from "renderer/hooks/use-translation.hook";
|
||||
import { useState } from "react";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
|
||||
export const BSVersionOutdatedModal: ModalComponent<void, { outdated: BSVersion, recommended: BSVersion }> = ({ resolver, data : { outdated, recommended }}) => {
|
||||
|
||||
const t = useTranslationV2();
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [remember, setRemember] = useState(false);
|
||||
|
||||
const handleContinue = () => {
|
||||
if (remember) {
|
||||
config.set("not-show-bs-version-outdated", true);
|
||||
}
|
||||
|
||||
resolver({ exitCode: ModalExitCode.COMPLETED });
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="text-gray-800 dark:text-gray-200 max-w-sm">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t.text("misc.warning")}</h1>
|
||||
<BsmImage className="mx-auto h-24" image={BeatConflict} />
|
||||
<p>{t.element("modals.bs-version-outdated.body", { outdatedVersion: <b>{outdated.BSVersion}</b>, recommendedVersion: <b>{recommended.BSVersion}</b> })}</p>
|
||||
<div className="flex items-center relative py-2 gap-1 mt-1">
|
||||
<BsmCheckbox className="h-5 relative z-[1]" checked={remember} onChange={val => setRemember(() => val)} />
|
||||
<span className="italic">{t.text("modals.misc.remember-my-choice")}</span>
|
||||
</div>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4 h-8">
|
||||
<BsmButton typeColor="cancel" className="rounded-md flex justify-center items-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md flex justify-center items-center transition-all" onClick={handleContinue} withBar={false} text="misc.continue" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,12 @@
|
||||
import { I18nService } from "renderer/services/i18n.service";
|
||||
import { useService } from "./use-service.hook";
|
||||
import { useObservable } from "./use-observable.hook";
|
||||
import { useConstant } from "./use-constant.hook";
|
||||
import { createElement, Fragment } from "react";
|
||||
|
||||
/**
|
||||
* @deprecated Use useTranslationV2 instead
|
||||
*/
|
||||
export function useTranslation(): (translationKey: string, args?: Record<string, string>) => string {
|
||||
|
||||
const i18nService = useService(I18nService);
|
||||
@@ -16,3 +21,35 @@ export function useTranslation(): (translationKey: string, args?: Record<string,
|
||||
return tranlatables.map(key => i18nService.translate(key, args)).join(" ");
|
||||
};
|
||||
}
|
||||
|
||||
type TranslateFunctions = {
|
||||
text: (translationKey: string, args?: Record<string, string>) => string;
|
||||
element: (translationKey: string, args?: Record<string, JSX.Element>) => JSX.Element;
|
||||
}
|
||||
|
||||
export function useTranslationV2(): TranslateFunctions {
|
||||
|
||||
const i18nService = useService(I18nService);
|
||||
|
||||
useObservable(() => i18nService.currentLanguage$);
|
||||
|
||||
const text = useConstant(() => {
|
||||
return (key: string, args?: Record<string, string>) => {
|
||||
if (!key) {
|
||||
return key;
|
||||
}
|
||||
return i18nService.translate(key, args);
|
||||
};
|
||||
});
|
||||
|
||||
const element = useConstant(() => {
|
||||
return (key: string, args?: Record<string, JSX.Element>) => {
|
||||
if (!key) {
|
||||
return createElement(Fragment, null, key);
|
||||
}
|
||||
return i18nService.translateElement(key, args);
|
||||
};
|
||||
});
|
||||
|
||||
return { text, element };
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
import { logRenderError } from "renderer";
|
||||
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
|
||||
import { BSVersionOutdatedModal } from "renderer/components/modal/modal-types/bs-version-outdated-modal.component";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { coerce, lt, valid } from "semver";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
|
||||
export const AvailableVersionsContext = createContext<{ selectedVersion: BSVersion; setSelectedVersion: (version: BSVersion) => void }>(null);
|
||||
|
||||
@@ -18,6 +24,8 @@ export function AvailableVersionsList() {
|
||||
|
||||
const bsDownloader = useService(BsDownloaderService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
const modals = useService(ModalService);
|
||||
const config = useService(ConfigurationService);
|
||||
|
||||
const [selectedVersion, setSelectedVersion] = useState<BSVersion>(null);
|
||||
const contextValue = useMemo(() => ({ selectedVersion, setSelectedVersion }), [selectedVersion]);
|
||||
@@ -25,10 +33,25 @@ export function AvailableVersionsList() {
|
||||
const downloading = useObservable(() => bsDownloader.downloadingVersion$.pipe(map(v => !!v)));
|
||||
const t = useTranslation();
|
||||
|
||||
const startDownload = async () => {
|
||||
const startDownload = async (version: BSVersion) => {
|
||||
const showOutdated = !config.get("not-show-bs-version-outdated");
|
||||
const recommendedVersion = showOutdated ? versionManager.availableVersions$.value.find(v => v.recommended) : undefined;
|
||||
|
||||
return bsDownloader.downloadVersion(selectedVersion)
|
||||
.catch(console.log)
|
||||
const isVersionOutdated = recommendedVersion?.BSVersion ? (
|
||||
tryit(() => lt(valid(coerce(version.BSVersion)), valid(coerce(recommendedVersion.BSVersion)))).result ?? false
|
||||
) : false;
|
||||
|
||||
if (showOutdated && isVersionOutdated) {
|
||||
|
||||
const res = await modals.openModal(BSVersionOutdatedModal, { outdated: version, recommended: recommendedVersion });
|
||||
|
||||
if (res.exitCode !== ModalExitCode.COMPLETED) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return bsDownloader.downloadVersion(version)
|
||||
.catch(logRenderError)
|
||||
.finally(() => setSelectedVersion(null));
|
||||
};
|
||||
|
||||
@@ -45,17 +68,17 @@ export function AvailableVersionsList() {
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full flex items-center flex-col pt-2">
|
||||
|
||||
|
||||
<Slideshow className="absolute w-full h-full top-0" />
|
||||
<h1 className="text-gray-100 text-2xl mb-4 z-[1]">{t("pages.available-versions.title")}</h1>
|
||||
|
||||
<AvailableVersionsContext.Provider value={contextValue}>
|
||||
<AvailableVersionsSlider />
|
||||
</AvailableVersionsContext.Provider>
|
||||
|
||||
|
||||
<AnimatePresence>
|
||||
{selectedVersion && !downloading && (
|
||||
<motion.div initial={{ y: "150%" }} animate={{ y: "0%" }} exit={{ y: "150%" }} className="absolute bottom-5" onClick={startDownload}>
|
||||
<motion.div initial={{ y: "150%" }} animate={{ y: "0%" }} exit={{ y: "150%" }} className="absolute bottom-5" onClick={() => startDownload(selectedVersion)}>
|
||||
<BsmButton text="misc.download" className="relative text-gray-800 dark:text-gray-100 rounded-md text-3xl font-bold italic tracking-wide px-3 pb-2 pt-1 shadow-md shadow-black" />
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Observable } from "rxjs";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
import { getProperty } from "dot-prop";
|
||||
import { i18n } from "dateformat";
|
||||
import { createElement, Fragment } from "react";
|
||||
|
||||
export class I18nService {
|
||||
private static instance: I18nService;
|
||||
@@ -87,4 +88,30 @@ export class I18nService {
|
||||
});
|
||||
return translated;
|
||||
}
|
||||
|
||||
public translateElement(translationKey: string, args?: Record<string, JSX.Element>): JSX.Element {
|
||||
let translated = this.cache.get(translationKey);
|
||||
if (!translated) {
|
||||
translated = getProperty(this.dictionary, translationKey) ?? translationKey;
|
||||
this.cache.set(translationKey, translated);
|
||||
}
|
||||
|
||||
if (!args) {
|
||||
return createElement(Fragment, null, translated);
|
||||
}
|
||||
|
||||
// Sort keys by length in descending order to avoid partial matches within keys
|
||||
const sortedKeys = Object.keys(args).sort((a, b) => b.length - a.length);
|
||||
|
||||
const pattern = sortedKeys.map(key => `\\{${key}\\}`).join('|');
|
||||
const regex = new RegExp(`(${pattern})`, 'g');
|
||||
const segments = translated.split(regex);
|
||||
|
||||
const children = segments.map((segment) => {
|
||||
const key = segment.slice(1, -1); // Remove surrounding {}
|
||||
return args[key] || segment;
|
||||
});
|
||||
|
||||
return createElement(Fragment, null, ...children);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user