优化系统管理的布局和操作交互
This commit is contained in:
@@ -3,10 +3,10 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import {
|
import {
|
||||||
ArrowUpCircle,
|
ArrowUpCircle,
|
||||||
|
CheckCircle2,
|
||||||
Languages,
|
Languages,
|
||||||
Loader2,
|
Loader2,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Save,
|
|
||||||
Wifi,
|
Wifi,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import type { Update } from "@tauri-apps/plugin-updater"
|
import type { Update } from "@tauri-apps/plugin-updater"
|
||||||
@@ -153,7 +153,11 @@ export function SystemNetworkSettings() {
|
|||||||
loadSettings().catch((err) => {
|
loadSettings().catch((err) => {
|
||||||
console.error("[Settings] load system settings failed:", err)
|
console.error("[Settings] load system settings failed:", err)
|
||||||
})
|
})
|
||||||
}, [loadSettings])
|
checkForUpdates().catch((err) => {
|
||||||
|
console.error("[Settings] auto check update failed:", err)
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
@@ -164,48 +168,49 @@ export function SystemNetworkSettings() {
|
|||||||
}
|
}
|
||||||
}, [availableUpdate])
|
}, [availableUpdate])
|
||||||
|
|
||||||
const saveSettings = useCallback(async () => {
|
const saveProxySettings = useCallback(
|
||||||
if (enabled && !proxyUrl.trim()) {
|
async (nextEnabled: boolean, nextProxyUrl: string) => {
|
||||||
toast.error(t("proxyRequired"))
|
if (nextEnabled && !nextProxyUrl.trim()) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
const next = await updateSystemProxySettings({
|
const next = await updateSystemProxySettings({
|
||||||
enabled,
|
enabled: nextEnabled,
|
||||||
proxy_url: proxyUrl.trim() || null,
|
proxy_url: nextProxyUrl.trim() || null,
|
||||||
})
|
})
|
||||||
setEnabled(next.enabled)
|
setEnabled(next.enabled)
|
||||||
setProxyUrl(next.proxy_url ?? "")
|
setProxyUrl(next.proxy_url ?? "")
|
||||||
toast.success(t("saveSuccess"))
|
} catch (err) {
|
||||||
} catch (err) {
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
const message = err instanceof Error ? err.message : String(err)
|
toast.error(t("saveFailed", { message }))
|
||||||
toast.error(t("saveFailed", { message }))
|
} finally {
|
||||||
} finally {
|
setSaving(false)
|
||||||
setSaving(false)
|
}
|
||||||
}
|
},
|
||||||
}, [enabled, proxyUrl, t])
|
[t]
|
||||||
|
)
|
||||||
|
|
||||||
const saveLanguage = useCallback(async () => {
|
const saveLanguage = useCallback(
|
||||||
setSavingLanguage(true)
|
async (lang: LanguageSelectValue) => {
|
||||||
|
setSavingLanguage(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const next = await updateSystemLanguageSettings({
|
const next = await updateSystemLanguageSettings({
|
||||||
mode: appLanguage === "system" ? "system" : "manual",
|
mode: lang === "system" ? "system" : "manual",
|
||||||
language:
|
language:
|
||||||
appLanguage === "system" ? languageSettings.language : appLanguage,
|
lang === "system" ? languageSettings.language : lang,
|
||||||
})
|
})
|
||||||
|
|
||||||
setLanguageSettings(next)
|
setLanguageSettings(next)
|
||||||
toast.success(t("languageSaveSuccess"))
|
} catch (err) {
|
||||||
} catch (err) {
|
const message = err instanceof Error ? err.message : String(err)
|
||||||
const message = err instanceof Error ? err.message : String(err)
|
toast.error(t("languageSaveFailed", { message }))
|
||||||
toast.error(t("languageSaveFailed", { message }))
|
} finally {
|
||||||
} finally {
|
setSavingLanguage(false)
|
||||||
setSavingLanguage(false)
|
}
|
||||||
}
|
},
|
||||||
}, [appLanguage, languageSettings.language, setLanguageSettings, t])
|
[languageSettings.language, setLanguageSettings, t]
|
||||||
|
)
|
||||||
|
|
||||||
const formatUpdateError = useCallback(
|
const formatUpdateError = useCallback(
|
||||||
(error: unknown, action: UpdateAction): string => {
|
(error: unknown, action: UpdateAction): string => {
|
||||||
@@ -302,128 +307,16 @@ export function SystemNetworkSettings() {
|
|||||||
|
|
||||||
<section className="rounded-xl border bg-card p-4 space-y-4">
|
<section className="rounded-xl border bg-card p-4 space-y-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Wifi className="h-4 w-4 text-muted-foreground" />
|
{checkingUpdate ? (
|
||||||
<h2 className="text-sm font-semibold">{t("proxyTitle")}</h2>
|
<RefreshCw className="h-4 w-4 text-muted-foreground animate-spin" />
|
||||||
</div>
|
) : availableUpdate ? (
|
||||||
|
<ArrowUpCircle className="h-4 w-4 text-muted-foreground" />
|
||||||
<p className="text-xs text-muted-foreground leading-5">
|
) : lastCheckedAt ? (
|
||||||
{t("proxyDescription")}
|
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
||||||
</p>
|
) : (
|
||||||
|
<RefreshCw className="h-4 w-4 text-muted-foreground" />
|
||||||
{loadError && (
|
)}
|
||||||
<div className="rounded-md border border-red-500/30 bg-red-500/5 px-3 py-2 text-xs text-red-400">
|
<h2 className="text-sm font-semibold">{t("versionTitle")}</h2>
|
||||||
{t("loadFailed", { message: loadError })}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<label className="inline-flex items-center gap-2 text-sm">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={enabled}
|
|
||||||
onChange={(event) => setEnabled(event.target.checked)}
|
|
||||||
/>
|
|
||||||
{t("enableProxy")}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-xs font-medium text-muted-foreground">
|
|
||||||
{t("proxyAddress")}
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
value={proxyUrl}
|
|
||||||
onChange={(event) => setProxyUrl(event.target.value)}
|
|
||||||
placeholder={PROXY_EXAMPLE}
|
|
||||||
/>
|
|
||||||
<p className="text-[11px] text-muted-foreground">
|
|
||||||
{t("proxyHint", { example: PROXY_EXAMPLE })}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button size="sm" onClick={saveSettings} disabled={saving}>
|
|
||||||
{saving ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
||||||
{t("saving")}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Save className="h-3.5 w-3.5" />
|
|
||||||
{t("save")}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="rounded-xl border bg-card p-4 space-y-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Languages className="h-4 w-4 text-muted-foreground" />
|
|
||||||
<h2 className="text-sm font-semibold">{t("languageTitle")}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-xs text-muted-foreground leading-5">
|
|
||||||
{t("languageDescription")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className="text-xs font-medium text-muted-foreground">
|
|
||||||
{t("appLanguage")}
|
|
||||||
</label>
|
|
||||||
<Select
|
|
||||||
value={appLanguage}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
if (value === "system") {
|
|
||||||
setAppLanguage("system")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!isAppLocale(value)) return
|
|
||||||
setAppLanguage(value)
|
|
||||||
}}
|
|
||||||
disabled={savingLanguage || !languageSettingsLoaded}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-full sm:w-56">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent align="start">
|
|
||||||
<SelectItem value="system">
|
|
||||||
{tLanguage("followSystem")}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="en">{languageLabels.en}</SelectItem>
|
|
||||||
<SelectItem value="zh_cn">{languageLabels.zh_cn}</SelectItem>
|
|
||||||
<SelectItem value="zh_tw">{languageLabels.zh_tw}</SelectItem>
|
|
||||||
<SelectItem value="ja">{languageLabels.ja}</SelectItem>
|
|
||||||
<SelectItem value="ko">{languageLabels.ko}</SelectItem>
|
|
||||||
<SelectItem value="es">{languageLabels.es}</SelectItem>
|
|
||||||
<SelectItem value="de">{languageLabels.de}</SelectItem>
|
|
||||||
<SelectItem value="fr">{languageLabels.fr}</SelectItem>
|
|
||||||
<SelectItem value="pt">{languageLabels.pt}</SelectItem>
|
|
||||||
<SelectItem value="ar">{languageLabels.ar}</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button size="sm" onClick={saveLanguage} disabled={savingLanguage}>
|
|
||||||
{savingLanguage ? (
|
|
||||||
<>
|
|
||||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
||||||
{t("saving")}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Save className="h-3.5 w-3.5" />
|
|
||||||
{t("save")}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="rounded-xl border bg-card p-4 space-y-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<RefreshCw className="h-4 w-4 text-muted-foreground" />
|
|
||||||
<h2 className="text-sm font-semibold">{t("updateTitle")}</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-xs text-muted-foreground leading-5">
|
<p className="text-xs text-muted-foreground leading-5">
|
||||||
@@ -514,6 +407,105 @@ export function SystemNetworkSettings() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-xl border bg-card p-4 space-y-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Wifi className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<h2 className="text-sm font-semibold">{t("proxyTitle")}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-muted-foreground leading-5">
|
||||||
|
{t("proxyDescription")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{loadError && (
|
||||||
|
<div className="rounded-md border border-red-500/30 bg-red-500/5 px-3 py-2 text-xs text-red-400">
|
||||||
|
{t("loadFailed", { message: loadError })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<label className="inline-flex items-center gap-2 text-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={enabled}
|
||||||
|
disabled={saving}
|
||||||
|
onChange={(event) => {
|
||||||
|
const next = event.target.checked
|
||||||
|
setEnabled(next)
|
||||||
|
saveProxySettings(next, proxyUrl)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{t("enableProxy")}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-xs font-medium text-muted-foreground">
|
||||||
|
{t("proxyAddress")}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={proxyUrl}
|
||||||
|
onChange={(event) => setProxyUrl(event.target.value)}
|
||||||
|
onBlur={() => saveProxySettings(enabled, proxyUrl)}
|
||||||
|
placeholder={PROXY_EXAMPLE}
|
||||||
|
disabled={saving}
|
||||||
|
/>
|
||||||
|
<p className="text-[11px] text-muted-foreground">
|
||||||
|
{t("proxyHint", { example: PROXY_EXAMPLE })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-xl border bg-card p-4 space-y-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Languages className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<h2 className="text-sm font-semibold">{t("languageTitle")}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs text-muted-foreground leading-5">
|
||||||
|
{t("languageDescription")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-xs font-medium text-muted-foreground">
|
||||||
|
{t("appLanguage")}
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={appLanguage}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
let nextLang: LanguageSelectValue
|
||||||
|
if (value === "system") {
|
||||||
|
nextLang = "system"
|
||||||
|
} else if (isAppLocale(value)) {
|
||||||
|
nextLang = value
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setAppLanguage(nextLang)
|
||||||
|
saveLanguage(nextLang)
|
||||||
|
}}
|
||||||
|
disabled={savingLanguage || !languageSettingsLoaded}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full sm:w-56">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent align="start">
|
||||||
|
<SelectItem value="system">
|
||||||
|
{tLanguage("followSystem")}
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="en">{languageLabels.en}</SelectItem>
|
||||||
|
<SelectItem value="zh_cn">{languageLabels.zh_cn}</SelectItem>
|
||||||
|
<SelectItem value="zh_tw">{languageLabels.zh_tw}</SelectItem>
|
||||||
|
<SelectItem value="ja">{languageLabels.ja}</SelectItem>
|
||||||
|
<SelectItem value="ko">{languageLabels.ko}</SelectItem>
|
||||||
|
<SelectItem value="es">{languageLabels.es}</SelectItem>
|
||||||
|
<SelectItem value="de">{languageLabels.de}</SelectItem>
|
||||||
|
<SelectItem value="fr">{languageLabels.fr}</SelectItem>
|
||||||
|
<SelectItem value="pt">{languageLabels.pt}</SelectItem>
|
||||||
|
<SelectItem value="ar">{languageLabels.ar}</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "تم حفظ إعدادات اللغة",
|
"languageSaveSuccess": "تم حفظ إعدادات اللغة",
|
||||||
"languageSaveFailed": "فشل حفظ إعدادات اللغة: {message}",
|
"languageSaveFailed": "فشل حفظ إعدادات اللغة: {message}",
|
||||||
"updateTitle": "تحديث التطبيق",
|
"updateTitle": "تحديث التطبيق",
|
||||||
|
"versionTitle": "تحديث البرنامج",
|
||||||
"updateDescription": "تحقق من المصدر المهيأ للإصدارات الأحدث وثبّت التحديث مباشرة عند توفره.",
|
"updateDescription": "تحقق من المصدر المهيأ للإصدارات الأحدث وثبّت التحديث مباشرة عند توفره.",
|
||||||
"currentVersion": "الإصدار الحالي",
|
"currentVersion": "الإصدار الحالي",
|
||||||
"upgradableVersion": "أحدث إصدار",
|
"upgradableVersion": "أحدث إصدار",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "Spracheinstellungen wurden gespeichert",
|
"languageSaveSuccess": "Spracheinstellungen wurden gespeichert",
|
||||||
"languageSaveFailed": "Spracheinstellungen konnten nicht gespeichert werden: {message}",
|
"languageSaveFailed": "Spracheinstellungen konnten nicht gespeichert werden: {message}",
|
||||||
"updateTitle": "App-Update",
|
"updateTitle": "App-Update",
|
||||||
|
"versionTitle": "Softwareupdate",
|
||||||
"updateDescription": "Prüft die konfigurierte Release-Quelle auf neue Versionen und installiert sie bei Verfügbarkeit direkt.",
|
"updateDescription": "Prüft die konfigurierte Release-Quelle auf neue Versionen und installiert sie bei Verfügbarkeit direkt.",
|
||||||
"currentVersion": "Aktuelle Version",
|
"currentVersion": "Aktuelle Version",
|
||||||
"upgradableVersion": "Neueste Version",
|
"upgradableVersion": "Neueste Version",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "Language settings saved",
|
"languageSaveSuccess": "Language settings saved",
|
||||||
"languageSaveFailed": "Failed to save language settings: {message}",
|
"languageSaveFailed": "Failed to save language settings: {message}",
|
||||||
"updateTitle": "App Update",
|
"updateTitle": "App Update",
|
||||||
|
"versionTitle": "Software Update",
|
||||||
"updateDescription": "Check the configured release source for newer versions and install directly when available.",
|
"updateDescription": "Check the configured release source for newer versions and install directly when available.",
|
||||||
"currentVersion": "Current version",
|
"currentVersion": "Current version",
|
||||||
"upgradableVersion": "Latest version",
|
"upgradableVersion": "Latest version",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "La configuración de idioma se guardó",
|
"languageSaveSuccess": "La configuración de idioma se guardó",
|
||||||
"languageSaveFailed": "No se pudo guardar la configuración de idioma: {message}",
|
"languageSaveFailed": "No se pudo guardar la configuración de idioma: {message}",
|
||||||
"updateTitle": "Actualización de la app",
|
"updateTitle": "Actualización de la app",
|
||||||
|
"versionTitle": "Actualización de software",
|
||||||
"updateDescription": "Comprueba versiones más nuevas en la fuente de versiones configurada e instálalas directamente cuando estén disponibles.",
|
"updateDescription": "Comprueba versiones más nuevas en la fuente de versiones configurada e instálalas directamente cuando estén disponibles.",
|
||||||
"currentVersion": "Versión actual",
|
"currentVersion": "Versión actual",
|
||||||
"upgradableVersion": "Última versión",
|
"upgradableVersion": "Última versión",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "Les paramètres de langue ont été enregistrés",
|
"languageSaveSuccess": "Les paramètres de langue ont été enregistrés",
|
||||||
"languageSaveFailed": "Échec de l’enregistrement des paramètres de langue : {message}",
|
"languageSaveFailed": "Échec de l’enregistrement des paramètres de langue : {message}",
|
||||||
"updateTitle": "Mise à jour de l’app",
|
"updateTitle": "Mise à jour de l’app",
|
||||||
|
"versionTitle": "Mise à jour logicielle",
|
||||||
"updateDescription": "Vérifiez les nouvelles versions depuis la source de publication configurée et installez-les directement si disponibles.",
|
"updateDescription": "Vérifiez les nouvelles versions depuis la source de publication configurée et installez-les directement si disponibles.",
|
||||||
"currentVersion": "Version actuelle",
|
"currentVersion": "Version actuelle",
|
||||||
"upgradableVersion": "Dernière version",
|
"upgradableVersion": "Dernière version",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "言語設定を保存しました",
|
"languageSaveSuccess": "言語設定を保存しました",
|
||||||
"languageSaveFailed": "言語設定の保存に失敗しました: {message}",
|
"languageSaveFailed": "言語設定の保存に失敗しました: {message}",
|
||||||
"updateTitle": "アプリ更新",
|
"updateTitle": "アプリ更新",
|
||||||
|
"versionTitle": "ソフトウェアアップデート",
|
||||||
"updateDescription": "設定されたリリースソースで新しいバージョンを確認し、利用可能なら直接インストールします。",
|
"updateDescription": "設定されたリリースソースで新しいバージョンを確認し、利用可能なら直接インストールします。",
|
||||||
"currentVersion": "現在のバージョン",
|
"currentVersion": "現在のバージョン",
|
||||||
"upgradableVersion": "最新バージョン",
|
"upgradableVersion": "最新バージョン",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "언어 설정이 저장되었습니다",
|
"languageSaveSuccess": "언어 설정이 저장되었습니다",
|
||||||
"languageSaveFailed": "언어 설정 저장 실패: {message}",
|
"languageSaveFailed": "언어 설정 저장 실패: {message}",
|
||||||
"updateTitle": "앱 업데이트",
|
"updateTitle": "앱 업데이트",
|
||||||
|
"versionTitle": "소프트웨어 업데이트",
|
||||||
"updateDescription": "설정된 릴리스 소스에서 새 버전을 확인하고 가능하면 바로 설치합니다.",
|
"updateDescription": "설정된 릴리스 소스에서 새 버전을 확인하고 가능하면 바로 설치합니다.",
|
||||||
"currentVersion": "현재 버전",
|
"currentVersion": "현재 버전",
|
||||||
"upgradableVersion": "최신 버전",
|
"upgradableVersion": "최신 버전",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "As configurações de idioma foram salvas",
|
"languageSaveSuccess": "As configurações de idioma foram salvas",
|
||||||
"languageSaveFailed": "Falha ao salvar as configurações de idioma: {message}",
|
"languageSaveFailed": "Falha ao salvar as configurações de idioma: {message}",
|
||||||
"updateTitle": "Atualização do app",
|
"updateTitle": "Atualização do app",
|
||||||
|
"versionTitle": "Atualização de software",
|
||||||
"updateDescription": "Verifique versões mais novas na fonte de releases configurada e instale diretamente quando disponíveis.",
|
"updateDescription": "Verifique versões mais novas na fonte de releases configurada e instale diretamente quando disponíveis.",
|
||||||
"currentVersion": "Versão atual",
|
"currentVersion": "Versão atual",
|
||||||
"upgradableVersion": "Versão mais recente",
|
"upgradableVersion": "Versão mais recente",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "语言设置已保存",
|
"languageSaveSuccess": "语言设置已保存",
|
||||||
"languageSaveFailed": "语言设置保存失败:{message}",
|
"languageSaveFailed": "语言设置保存失败:{message}",
|
||||||
"updateTitle": "应用升级",
|
"updateTitle": "应用升级",
|
||||||
|
"versionTitle": "软件更新",
|
||||||
"updateDescription": "点击检查后会从配置的发布源拉取最新版本信息,有新版本时可直接下载并安装。",
|
"updateDescription": "点击检查后会从配置的发布源拉取最新版本信息,有新版本时可直接下载并安装。",
|
||||||
"currentVersion": "当前版本",
|
"currentVersion": "当前版本",
|
||||||
"upgradableVersion": "最新版本",
|
"upgradableVersion": "最新版本",
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
"languageSaveSuccess": "語言設定已儲存",
|
"languageSaveSuccess": "語言設定已儲存",
|
||||||
"languageSaveFailed": "語言設定儲存失敗:{message}",
|
"languageSaveFailed": "語言設定儲存失敗:{message}",
|
||||||
"updateTitle": "應用升級",
|
"updateTitle": "應用升級",
|
||||||
|
"versionTitle": "軟體更新",
|
||||||
"updateDescription": "點擊檢查後會從設定的發佈來源拉取最新版本資訊,有新版本時可直接下載並安裝。",
|
"updateDescription": "點擊檢查後會從設定的發佈來源拉取最新版本資訊,有新版本時可直接下載並安裝。",
|
||||||
"currentVersion": "目前版本",
|
"currentVersion": "目前版本",
|
||||||
"upgradableVersion": "最新版本",
|
"upgradableVersion": "最新版本",
|
||||||
|
|||||||
Reference in New Issue
Block a user