mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-26 22:22:13 +08:00
chore: i18n (#5276)
* chore: notice i18n * feat: add script to clean up unused i18n keys * chore: cleanup i18n keys * refactor(i18n/proxies): migrate proxies UI to structured locale keys * chore: i18n for rule module * chore: i18n for profile module * chore: i18n for connections module * chore: i18n for settings module * chore: i18n for verge settings * chore: i18n for theme settings * chore: i18n for theme * chore(i18n): components.home.* * chore(i18n): remove unused i18n keys * chore(i18n): components.profile.* * chore(i18n): components.connection * chore(i18n): pages.logs.* * chore(i18n): pages.*.provider * chore(i18n): components.settings.externalCors.* * chore(i18n): components.settings.clash.* * chore(i18n): components.settings.liteMode.* * chore(i18n): components.settings.backup.* * chore(i18n): components.settings.clash.port.* * chore(i18n): components.settings.misc.* * chore(i18n): components.settings.update.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): pages.profiles.notices/components.providers.notices * refactor(notice): unify showNotice usage * refactor(notice): add typed showNotice shortcuts, centralize defaults, and simplify subscriptions * refactor: unify showNotice usage * refactor(notice): unify showNotice API * refactor(notice): unify showNotice usage * chore(i18n): components.test.* * chore(i18n): components.settings.dns.* * chore(i18n): components.home.clashInfo.* * chore(i18n): components.home.systemInfo.* * chore(i18n): components.home.ipInfo/traffic.* * chore(i18n): navigation.* * refactor(i18n): remove pages.* namespace and migrate route texts under module-level page keys * chore(i18n): common.* * chore(i18n): common.* * fix: change error handling in patch_profiles_config to return false when a switch is in progress * fix: improve error handling in patch_profiles_config to prevent requests during profile switching * fix: change error handling in patch_profiles_config to return false when a switch is in progress fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config * chore(i18n): restructure root-level locale keys into namespaces * chore(i18n): add missing i18n keys * docs: i18n guide * chore: adjust i18n * refactor(i18n): align UI actions and status labels with common keys * refactor(i18n): unify two-name locale namespaces * refactor(i18n/components): unify locale keys and update component references * chore(i18n): add shared and entities namespaces to all locale files * refactor(i18n): consolidate shared and entity namespaces across features * chore(deps): update npm dependencies to ^7.3.5 (#5310) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(i18n): migrate shared editor modes and consolidate entities namespaces * tmp * refactor(i18n): flatten locales and move theme/validation strings * docs: CONTRIBUTING_i18n.md * refactor(i18n): restructure feedback and profile namespaces for better organization * refactor(i18n): unify settings locale structure and update references * refactor(i18n): reorganize locale keys for home, proxies, rules, connections, logs, unlock, and tests * refactor(i18n/feedback/layout): unify shared toasts & normalize layout namespace * refactor(i18n): centralize common UI strings in shared * refactor(i18n): flatten headers and unify locale schema * refactor(i18n): consolidate duplicate per-feature translations into shared namespace * refactor(i18n): split locales into per-namespace files * style: lint * refactor(i18n): unify unlock UI translations under tests namespace * feat(i18n): add type-checked translation keys * style: eslint import order * feat(i18n): replace ad-hoc loader with rust-i18n backend bundles * chore(prebuild): remove locale-copy step * fix(i18n, notice): propagate runtime params and update cleanup script path * fix(i18n,notice): make locale formatting idempotent and guard early notice translations * fix(i18n): resolve locale aliases and match OS codes correctly * fix(unlock): use i18next-compatible double-brace interpolation in failure notice * fix(i18n): route unlock error notices through translation keys * fix(i18n): i18n types * feat(i18n): localize upgrade notice for Clash core viewer * fix(notice): ensure runtime overrides apply to prefix translations * chore(i18n): replace literal notices with translation keys * chore(i18n): types * chore(i18n): regen typings before formatting to keep keys in sync * chore(i18n): simply labels * chore(i18n): adjust translation * chore: remove eslint-plugin-i18next * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend (#5341) * chore(i18n): translate settings.json (missed in previous pass) (#5343) * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * fix(i18n-tauri): quote placeholder-leading value in ko.yml to prevent rust_i18n parse panic * chore(i18n): translate settings.json (forgot to include previously) --------- Co-authored-by: rozan <34974262+thelojan@users.noreply.github.com>
This commit is contained in:
co-authored by
renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
rozan
parent
c1a2e99f63
commit
c8aa72186e
@@ -84,22 +84,22 @@ export const BackupConfigViewer = memo(
|
||||
|
||||
if (!url) {
|
||||
urlRef.current?.focus();
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("WebDAV URL Required"));
|
||||
showNotice.error("settings.modals.backup.messages.webdavUrlRequired");
|
||||
throw new Error(t("settings.modals.backup.messages.webdavUrlRequired"));
|
||||
} else if (!isValidUrl(url)) {
|
||||
urlRef.current?.focus();
|
||||
showNotice("error", t("Invalid WebDAV URL"));
|
||||
throw new Error(t("Invalid WebDAV URL"));
|
||||
showNotice.error("settings.modals.backup.messages.invalidWebdavUrl");
|
||||
throw new Error(t("settings.modals.backup.messages.invalidWebdavUrl"));
|
||||
}
|
||||
if (!username) {
|
||||
usernameRef.current?.focus();
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("Username Required"));
|
||||
showNotice.error("settings.modals.backup.messages.usernameRequired");
|
||||
throw new Error(t("settings.modals.backup.messages.usernameRequired"));
|
||||
}
|
||||
if (!password) {
|
||||
passwordRef.current?.focus();
|
||||
showNotice("error", t("WebDAV URL Required"));
|
||||
throw new Error(t("Password Required"));
|
||||
showNotice.error("settings.modals.backup.messages.passwordRequired");
|
||||
throw new Error(t("settings.modals.backup.messages.passwordRequired"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,11 +112,17 @@ export const BackupConfigViewer = memo(
|
||||
data.username.trim(),
|
||||
data.password,
|
||||
).then(() => {
|
||||
showNotice("success", t("WebDAV Config Saved"));
|
||||
showNotice.success(
|
||||
"settings.modals.backup.messages.webdavConfigSaved",
|
||||
);
|
||||
onSaveSuccess();
|
||||
});
|
||||
} catch (error) {
|
||||
showNotice("error", t("WebDAV Config Save Failed", { error }), 3000);
|
||||
showNotice.error(
|
||||
"settings.modals.backup.messages.webdavConfigSaveFailed",
|
||||
{ error },
|
||||
3000,
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -127,11 +133,13 @@ export const BackupConfigViewer = memo(
|
||||
try {
|
||||
setLoading(true);
|
||||
await createWebdavBackup().then(async () => {
|
||||
showNotice("success", t("Backup Created"));
|
||||
showNotice.success("settings.modals.backup.messages.backupCreated");
|
||||
await onBackupSuccess();
|
||||
});
|
||||
} catch (error) {
|
||||
showNotice("error", t("Backup Failed", { error }));
|
||||
showNotice.error("settings.modals.backup.messages.backupFailed", {
|
||||
error,
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -145,7 +153,7 @@ export const BackupConfigViewer = memo(
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={t("WebDAV Server URL")}
|
||||
label={t("settings.modals.backup.fields.webdavUrl")}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
{...register("url")}
|
||||
@@ -157,7 +165,7 @@ export const BackupConfigViewer = memo(
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6 }}>
|
||||
<TextField
|
||||
label={t("Username")}
|
||||
label={t("settings.modals.backup.fields.username")}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
{...register("username")}
|
||||
@@ -169,7 +177,7 @@ export const BackupConfigViewer = memo(
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6 }}>
|
||||
<TextField
|
||||
label={t("Password")}
|
||||
label={t("shared.labels.password")}
|
||||
type={showPassword ? "text" : "password"}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
@@ -214,7 +222,7 @@ export const BackupConfigViewer = memo(
|
||||
type="button"
|
||||
onClick={handleSubmit(save)}
|
||||
>
|
||||
{t("Save")}
|
||||
{t("shared.actions.save")}
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
@@ -225,7 +233,7 @@ export const BackupConfigViewer = memo(
|
||||
type="button"
|
||||
size="large"
|
||||
>
|
||||
{t("Backup")}
|
||||
{t("settings.modals.backup.actions.backup")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -233,7 +241,7 @@ export const BackupConfigViewer = memo(
|
||||
type="button"
|
||||
size="large"
|
||||
>
|
||||
{t("Refresh")}
|
||||
{t("shared.actions.refresh")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -75,7 +75,7 @@ export const BackupTableViewer = memo(
|
||||
|
||||
const handleRestore = useLockFn(async (filename: string) => {
|
||||
await onRestore(filename).then(() => {
|
||||
showNotice("success", t("Restore Success, App will restart in 1s"));
|
||||
showNotice.success("settings.modals.backup.messages.restoreSuccess");
|
||||
});
|
||||
await restartApp();
|
||||
});
|
||||
@@ -92,10 +92,14 @@ export const BackupTableViewer = memo(
|
||||
return;
|
||||
}
|
||||
await onExport(filename, savePath);
|
||||
showNotice("success", t("Local Backup Exported"));
|
||||
showNotice.success(
|
||||
"settings.modals.backup.messages.localBackupExported",
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showNotice("error", t("Local Backup Export Failed"));
|
||||
showNotice.error(
|
||||
"settings.modals.backup.messages.localBackupExportFailed",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -104,9 +108,15 @@ export const BackupTableViewer = memo(
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>{t("Filename")}</TableCell>
|
||||
<TableCell>{t("Backup Time")}</TableCell>
|
||||
<TableCell align="right">{t("Actions")}</TableCell>
|
||||
<TableCell>
|
||||
{t("settings.modals.backup.table.filename")}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{t("settings.modals.backup.table.backupTime")}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{t("settings.modals.backup.table.actions")}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -140,9 +150,13 @@ export const BackupTableViewer = memo(
|
||||
<>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label={t("Export")}
|
||||
aria-label={t(
|
||||
"settings.modals.backup.actions.export",
|
||||
)}
|
||||
size="small"
|
||||
title={t("Export Backup")}
|
||||
title={t(
|
||||
"settings.modals.backup.actions.exportBackup",
|
||||
)}
|
||||
onClick={async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
await handleExport(file.filename);
|
||||
@@ -159,13 +173,17 @@ export const BackupTableViewer = memo(
|
||||
)}
|
||||
<IconButton
|
||||
color="secondary"
|
||||
aria-label={t("Delete")}
|
||||
aria-label={t("shared.actions.delete")}
|
||||
size="small"
|
||||
title={t("Delete Backup")}
|
||||
title={t(
|
||||
"settings.modals.backup.actions.deleteBackup",
|
||||
)}
|
||||
onClick={async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const confirmed = await confirmAsync(
|
||||
t("Confirm to delete this backup file?"),
|
||||
t(
|
||||
"settings.modals.backup.messages.confirmDelete",
|
||||
),
|
||||
);
|
||||
if (confirmed) {
|
||||
await handleDelete(file.filename);
|
||||
@@ -181,14 +199,20 @@ export const BackupTableViewer = memo(
|
||||
/>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label={t("Restore")}
|
||||
aria-label={t(
|
||||
"settings.modals.backup.actions.restore",
|
||||
)}
|
||||
size="small"
|
||||
title={t("Restore Backup")}
|
||||
title={t(
|
||||
"settings.modals.backup.actions.restoreBackup",
|
||||
)}
|
||||
disabled={!file.allow_apply}
|
||||
onClick={async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const confirmed = await confirmAsync(
|
||||
t("Confirm to restore this backup file?"),
|
||||
t(
|
||||
"settings.modals.backup.messages.confirmRestore",
|
||||
),
|
||||
);
|
||||
if (confirmed) {
|
||||
await handleRestore(file.filename);
|
||||
@@ -219,7 +243,7 @@ export const BackupTableViewer = memo(
|
||||
color="textSecondary"
|
||||
align="center"
|
||||
>
|
||||
{t("No Backups")}
|
||||
{t("settings.modals.backup.table.noBackups")}
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
@@ -234,7 +258,7 @@ export const BackupTableViewer = memo(
|
||||
rowsPerPage={DEFAULT_ROWS_PER_PAGE}
|
||||
page={page}
|
||||
onPageChange={onPageChange}
|
||||
labelRowsPerPage={t("Rows per page")}
|
||||
labelRowsPerPage={t("settings.modals.backup.table.rowsPerPage")}
|
||||
/>
|
||||
</TableContainer>
|
||||
);
|
||||
|
||||
@@ -127,7 +127,6 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
setBackupFiles([]);
|
||||
setTotal(0);
|
||||
console.error(error);
|
||||
// Notice.error(t("Failed to fetch backup files"));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -247,7 +246,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Backup Setting")}
|
||||
title={t("settings.modals.backup.title")}
|
||||
contentSx={{
|
||||
minWidth: { xs: 320, sm: 620 },
|
||||
maxWidth: "unset",
|
||||
@@ -278,11 +277,14 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
<Tabs
|
||||
value={source}
|
||||
onChange={handleChangeSource}
|
||||
aria-label={t("Select Backup Target")}
|
||||
aria-label={t("settings.modals.backup.actions.selectTarget")}
|
||||
sx={{ mb: 2 }}
|
||||
>
|
||||
<Tab value="local" label={t("Local Backup")} />
|
||||
<Tab value="webdav" label={t("WebDAV Backup")} />
|
||||
<Tab value="local" label={t("settings.modals.backup.tabs.local")} />
|
||||
<Tab
|
||||
value="webdav"
|
||||
label={t("settings.modals.backup.tabs.webdav")}
|
||||
/>
|
||||
</Tabs>
|
||||
{source === "local" ? (
|
||||
<LocalBackupActions
|
||||
@@ -342,7 +344,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
backgroundColor: (theme) => theme.palette.background.paper,
|
||||
}}
|
||||
>
|
||||
{t("Close")}
|
||||
{t("shared.actions.close")}
|
||||
</Button>
|
||||
</Box>,
|
||||
dialogPaper,
|
||||
|
||||
@@ -24,8 +24,16 @@ import { changeClashCore, restartCore } from "@/services/cmds";
|
||||
import { showNotice } from "@/services/noticeService";
|
||||
|
||||
const VALID_CORE = [
|
||||
{ name: "Mihomo", core: "verge-mihomo", chip: "Release Version" },
|
||||
{ name: "Mihomo Alpha", core: "verge-mihomo-alpha", chip: "Alpha Version" },
|
||||
{
|
||||
name: "Mihomo",
|
||||
core: "verge-mihomo",
|
||||
chipKey: "settings.modals.clashCore.variants.release",
|
||||
},
|
||||
{
|
||||
name: "Mihomo Alpha",
|
||||
core: "verge-mihomo-alpha",
|
||||
chipKey: "settings.modals.clashCore.variants.alpha",
|
||||
},
|
||||
];
|
||||
|
||||
export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
@@ -54,7 +62,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
const errorMsg = await changeClashCore(core);
|
||||
|
||||
if (errorMsg) {
|
||||
showNotice("error", errorMsg);
|
||||
showNotice.error(errorMsg);
|
||||
setChangingCore(null);
|
||||
return;
|
||||
}
|
||||
@@ -65,9 +73,9 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
mutate("getVersion");
|
||||
setChangingCore(null);
|
||||
}, 500);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setChangingCore(null);
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -75,11 +83,13 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
try {
|
||||
setRestarting(true);
|
||||
await restartCore();
|
||||
showNotice("success", t(`Clash Core Restarted`));
|
||||
showNotice.success(
|
||||
t("settings.feedback.notifications.clash.restartSuccess"),
|
||||
);
|
||||
setRestarting(false);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
setRestarting(false);
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,14 +98,16 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
setUpgrading(true);
|
||||
await upgradeCore();
|
||||
setUpgrading(false);
|
||||
showNotice("success", t(`Core Version Updated`));
|
||||
showNotice.success(
|
||||
t("settings.feedback.notifications.clash.versionUpdated"),
|
||||
);
|
||||
} catch (err: any) {
|
||||
setUpgrading(false);
|
||||
const errMsg = err.response?.data?.message || err.toString();
|
||||
const errMsg = err?.response?.data?.message ?? String(err);
|
||||
const showMsg = errMsg.includes("already using latest version")
|
||||
? "Already Using Latest Core Version"
|
||||
? t("settings.feedback.notifications.clash.alreadyLatestVersion")
|
||||
: errMsg;
|
||||
showNotice("error", t(showMsg));
|
||||
showNotice.info(showMsg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -104,7 +116,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
{t("Clash Core")}
|
||||
{t("settings.sections.clash.form.fields.clashCore")}
|
||||
<Box>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
@@ -116,7 +128,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
sx={{ marginRight: "8px" }}
|
||||
onClick={onUpgrade}
|
||||
>
|
||||
{t("Upgrade")}
|
||||
{t("shared.actions.upgrade")}
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
@@ -127,7 +139,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
disabled={upgrading}
|
||||
onClick={onRestart}
|
||||
>
|
||||
{t("Restart")}
|
||||
{t("shared.actions.restart")}
|
||||
</LoadingButton>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -141,7 +153,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
marginTop: "-8px",
|
||||
}}
|
||||
disableOk
|
||||
cancelBtn={t("Close")}
|
||||
cancelBtn={t("shared.actions.close")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
>
|
||||
@@ -157,7 +169,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
{changingCore === each.core ? (
|
||||
<CircularProgress size={20} sx={{ mr: 1 }} />
|
||||
) : (
|
||||
<Chip label={t(`${each.chip}`)} size="small" />
|
||||
<Chip label={t(each.chipKey)} size="small" />
|
||||
)}
|
||||
</ListItemButton>
|
||||
))}
|
||||
|
||||
@@ -69,11 +69,13 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
showNotice("success", t("Port settings saved"));
|
||||
showNotice.success("settings.modals.clashPort.messages.saved");
|
||||
},
|
||||
onError: (e) => {
|
||||
showNotice("error", e.message || t("Failed to save port settings"));
|
||||
// showNotice("error", t("Failed to save port settings"));
|
||||
onError: (error) => {
|
||||
showNotice.error(
|
||||
"settings.modals.clashPort.messages.saveFailed",
|
||||
error,
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -150,7 +152,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Port Config")}
|
||||
title={t("settings.modals.clashPort.title")}
|
||||
contentSx={{
|
||||
width: 400,
|
||||
}}
|
||||
@@ -158,13 +160,13 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
loading ? (
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<CircularProgress size={20} />
|
||||
{t("Saving...")}
|
||||
{t("shared.statuses.saving")}
|
||||
</Stack>
|
||||
) : (
|
||||
t("Save")
|
||||
t("shared.actions.save")
|
||||
)
|
||||
}
|
||||
cancelBtn={t("Cancel")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
@@ -172,7 +174,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<List sx={{ width: "100%" }}>
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Mixed Port")}
|
||||
primary={t("settings.modals.clashPort.fields.mixed")}
|
||||
slotProps={{ primary: { sx: { fontSize: 12 } } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
@@ -188,7 +190,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setMixedPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
title={t("settings.modals.clashPort.actions.random")}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
<Shuffle fontSize="small" />
|
||||
@@ -204,7 +206,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Socks Port")}
|
||||
primary={t("settings.modals.clashPort.fields.socks")}
|
||||
slotProps={{ primary: { sx: { fontSize: 12 } } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
@@ -221,7 +223,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setSocksPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
title={t("settings.modals.clashPort.actions.random")}
|
||||
disabled={!socksEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
@@ -238,7 +240,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Http Port")}
|
||||
primary={t("settings.modals.clashPort.fields.http")}
|
||||
slotProps={{ primary: { sx: { fontSize: 12 } } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
@@ -255,7 +257,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setHttpPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
title={t("settings.modals.clashPort.actions.random")}
|
||||
disabled={!httpEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
@@ -273,7 +275,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
{OS !== "windows" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Redir Port")}
|
||||
primary={t("settings.modals.clashPort.fields.redir")}
|
||||
slotProps={{ primary: { sx: { fontSize: 12 } } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
@@ -290,7 +292,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setRedirPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
title={t("settings.modals.clashPort.actions.random")}
|
||||
disabled={!redirEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
@@ -309,7 +311,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
{OS === "linux" && (
|
||||
<ListItem sx={{ padding: "4px 0", minHeight: 36 }}>
|
||||
<ListItemText
|
||||
primary={t("Tproxy Port")}
|
||||
primary={t("settings.modals.clashPort.fields.tproxy")}
|
||||
slotProps={{ primary: { sx: { fontSize: 12 } } }}
|
||||
/>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
@@ -326,7 +328,7 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => setTproxyPort(generateRandomPort())}
|
||||
title={t("Random Port")}
|
||||
title={t("settings.modals.clashPort.actions.random")}
|
||||
disabled={!tproxyEnabled}
|
||||
sx={{ mr: 0.5 }}
|
||||
>
|
||||
|
||||
@@ -27,8 +27,8 @@ export const ConfigViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
open={true}
|
||||
title={
|
||||
<Box display="flex" alignItems="center" gap={2}>
|
||||
{t("Runtime Config")}
|
||||
<Chip label={t("ReadOnly")} size="small" />
|
||||
{t("settings.components.verge.advanced.fields.runtimeConfig")}
|
||||
<Chip label={t("shared.labels.readOnly")} size="small" />
|
||||
</Box>
|
||||
}
|
||||
initialData={Promise.resolve(runtimeConfig)}
|
||||
|
||||
@@ -56,12 +56,16 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
// 如果启用了外部控制器,则保存控制器地址和密钥
|
||||
if (enableController) {
|
||||
if (!controller.trim()) {
|
||||
showNotice("error", t("Controller address cannot be empty"));
|
||||
showNotice.error(
|
||||
"settings.sections.externalController.messages.addressRequired",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!secret.trim()) {
|
||||
showNotice("error", t("Secret cannot be empty"));
|
||||
showNotice.error(
|
||||
"settings.sections.externalController.messages.secretRequired",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,12 +75,12 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
await patchInfo({ "external-controller": "" });
|
||||
}
|
||||
|
||||
showNotice("success", t("Configuration saved successfully"));
|
||||
showNotice.success("shared.feedback.notifications.common.saveSuccess");
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice(
|
||||
"error",
|
||||
err.message || t("Failed to save configuration"),
|
||||
} catch (err) {
|
||||
showNotice.error(
|
||||
"shared.feedback.notifications.common.saveFailed",
|
||||
err,
|
||||
4000,
|
||||
);
|
||||
} finally {
|
||||
@@ -93,7 +97,9 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
setTimeout(() => setCopySuccess(null));
|
||||
} catch (err) {
|
||||
console.warn("[ControllerViewer] copy to clipboard failed:", err);
|
||||
showNotice("error", t("Failed to copy"));
|
||||
showNotice.error(
|
||||
"settings.sections.externalController.messages.copyFailed",
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -101,19 +107,19 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("External Controller")}
|
||||
title={t("settings.sections.externalController.title")}
|
||||
contentSx={{ width: 400 }}
|
||||
okBtn={
|
||||
isSaving ? (
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<CircularProgress size={16} color="inherit" />
|
||||
{t("Saving...")}
|
||||
{t("shared.statuses.saving")}
|
||||
</Box>
|
||||
) : (
|
||||
t("Save")
|
||||
t("shared.actions.save")
|
||||
)
|
||||
}
|
||||
cancelBtn={t("Cancel")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
@@ -126,7 +132,9 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t("Enable External Controller")} />
|
||||
<ListItemText
|
||||
primary={t("settings.sections.externalController.fields.enable")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={enableController}
|
||||
@@ -142,7 +150,9 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t("External Controller")} />
|
||||
<ListItemText
|
||||
primary={t("settings.sections.externalController.fields.address")}
|
||||
/>
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -152,11 +162,15 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
pointerEvents: enableController ? "auto" : "none",
|
||||
}}
|
||||
value={controller}
|
||||
placeholder="Required"
|
||||
placeholder={t(
|
||||
"settings.sections.externalController.placeholders.address",
|
||||
)}
|
||||
onChange={(e) => setController(e.target.value)}
|
||||
disabled={isSaving || !enableController}
|
||||
/>
|
||||
<Tooltip title={t("Copy to clipboard")}>
|
||||
<Tooltip
|
||||
title={t("settings.sections.externalController.tooltips.copy")}
|
||||
>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => handleCopyToClipboard(controller, "controller")}
|
||||
@@ -176,7 +190,9 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t("Core Secret")} />
|
||||
<ListItemText
|
||||
primary={t("settings.sections.externalController.fields.secret")}
|
||||
/>
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<TextField
|
||||
size="small"
|
||||
@@ -186,11 +202,15 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
pointerEvents: enableController ? "auto" : "none",
|
||||
}}
|
||||
value={secret}
|
||||
placeholder={t("Recommended")}
|
||||
placeholder={t(
|
||||
"settings.sections.externalController.placeholders.secret",
|
||||
)}
|
||||
onChange={(e) => setSecret(e.target.value)}
|
||||
disabled={isSaving || !enableController}
|
||||
/>
|
||||
<Tooltip title={t("Copy to clipboard")}>
|
||||
<Tooltip
|
||||
title={t("settings.sections.externalController.tooltips.copy")}
|
||||
>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => handleCopyToClipboard(secret, "secret")}
|
||||
@@ -211,8 +231,10 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
>
|
||||
<Alert severity="success">
|
||||
{copySuccess === "controller"
|
||||
? t("Controller address copied to clipboard")
|
||||
: t("Secret copied to clipboard")}
|
||||
? t(
|
||||
"settings.sections.externalController.messages.controllerCopied",
|
||||
)
|
||||
: t("settings.sections.externalController.messages.secretCopied")}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -424,9 +424,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
skipYamlSyncRef.current = true;
|
||||
updateValuesFromConfig(parsedYaml);
|
||||
} catch {
|
||||
showNotice("error", t("Invalid YAML format"));
|
||||
showNotice.error("settings.modals.dns.errors.invalidYaml");
|
||||
}
|
||||
}, [yamlContent, t, updateValuesFromConfig]);
|
||||
}, [yamlContent, updateValuesFromConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (skipYamlSyncRef.current) {
|
||||
@@ -509,7 +509,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
// 使用YAML编辑器的值
|
||||
const parsedConfig = yaml.load(yamlContent);
|
||||
if (typeof parsedConfig !== "object" || parsedConfig === null) {
|
||||
throw new Error(t("Invalid configuration"));
|
||||
throw new Error(t("settings.modals.dns.errors.invalid"));
|
||||
}
|
||||
config = parsedConfig as Record<string, any>;
|
||||
}
|
||||
@@ -547,9 +547,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
}
|
||||
}
|
||||
|
||||
showNotice(
|
||||
"error",
|
||||
t("DNS configuration error") + ": " + cleanErrorMsg,
|
||||
showNotice.error(
|
||||
"settings.modals.dns.messages.configError",
|
||||
cleanErrorMsg,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -561,9 +561,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
showNotice("success", t("DNS settings saved"));
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.success("settings.modals.dns.messages.saved");
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -613,7 +613,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
{t("DNS Overwrite")}
|
||||
{t("settings.modals.dns.dialog.title")}
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -622,7 +622,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
startIcon={<RestartAltRounded />}
|
||||
onClick={resetToDefaults}
|
||||
>
|
||||
{t("Reset to Default")}
|
||||
{t("shared.actions.resetToDefault")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -631,7 +631,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
setVisualization((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
{visualization ? t("Advanced") : t("Visualization")}
|
||||
{visualization
|
||||
? t("shared.editorModes.advanced")
|
||||
: t("shared.editorModes.visualization")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -643,8 +645,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
? {}
|
||||
: { padding: "0 24px", display: "flex", flexDirection: "column" }),
|
||||
}}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
@@ -655,7 +657,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
color="warning.main"
|
||||
sx={{ mb: 2, mt: 0, fontStyle: "italic" }}
|
||||
>
|
||||
{t("DNS Settings Warning")}
|
||||
{t("settings.modals.dns.dialog.warning")}
|
||||
</Typography>
|
||||
|
||||
{visualization ? (
|
||||
@@ -664,11 +666,11 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
variant="subtitle1"
|
||||
sx={{ mt: 1, mb: 1, fontWeight: "bold" }}
|
||||
>
|
||||
{t("DNS Settings")}
|
||||
{t("settings.modals.dns.sections.general")}
|
||||
</Typography>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Enable DNS")} />
|
||||
<ListItemText primary={t("settings.modals.dns.fields.enable")} />
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.enable}
|
||||
@@ -677,7 +679,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("DNS Listen")} />
|
||||
<ListItemText primary={t("settings.modals.dns.fields.listen")} />
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
@@ -689,7 +691,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Enhanced Mode")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.dns.fields.enhancedMode")}
|
||||
/>
|
||||
<FormControl size="small" sx={{ width: 150 }}>
|
||||
<Select
|
||||
value={values.enhancedMode}
|
||||
@@ -702,7 +706,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Fake IP Range")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.dns.fields.fakeIpRange")}
|
||||
/>
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
@@ -714,7 +720,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Fake IP Filter Mode")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.dns.fields.fakeIpFilterMode")}
|
||||
/>
|
||||
<FormControl size="small" sx={{ width: 150 }}>
|
||||
<Select
|
||||
value={values.fakeIpFilterMode}
|
||||
@@ -728,8 +736,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("IPv6")}
|
||||
secondary={t("Enable IPv6 DNS resolution")}
|
||||
primary={t("settings.modals.dns.fields.ipv6.label")}
|
||||
secondary={t("settings.modals.dns.fields.ipv6.description")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -740,8 +748,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Prefer H3")}
|
||||
secondary={t("DNS DOH使用HTTP/3")}
|
||||
primary={t("settings.modals.dns.fields.preferH3.label")}
|
||||
secondary={t("settings.modals.dns.fields.preferH3.description")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -752,8 +760,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Respect Rules")}
|
||||
secondary={t("DNS connections follow routing rules")}
|
||||
primary={t("settings.modals.dns.fields.respectRules.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.respectRules.description",
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -764,8 +774,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Use Hosts")}
|
||||
secondary={t("Enable to resolve hosts through hosts file")}
|
||||
primary={t("settings.modals.dns.fields.useHosts.label")}
|
||||
secondary={t("settings.modals.dns.fields.useHosts.description")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -776,8 +786,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Use System Hosts")}
|
||||
secondary={t("Enable to resolve hosts through system hosts file")}
|
||||
primary={t("settings.modals.dns.fields.useSystemHosts.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.useSystemHosts.description",
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -788,8 +800,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("Direct Nameserver Follow Policy")}
|
||||
secondary={t("Whether to follow nameserver policy")}
|
||||
primary={t("settings.modals.dns.fields.directPolicy.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.directPolicy.description",
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -800,8 +814,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Default Nameserver")}
|
||||
secondary={t("Default DNS servers used to resolve DNS servers")}
|
||||
primary={t("settings.modals.dns.fields.defaultNameserver.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.defaultNameserver.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -817,8 +833,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Nameserver")}
|
||||
secondary={t("List of DNS servers")}
|
||||
primary={t("settings.modals.dns.fields.nameserver.label")}
|
||||
secondary={t("settings.modals.dns.fields.nameserver.description")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -834,8 +850,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback")}
|
||||
secondary={t("List of fallback DNS servers")}
|
||||
primary={t("settings.modals.dns.fields.fallback.label")}
|
||||
secondary={t("settings.modals.dns.fields.fallback.description")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -851,8 +867,8 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Proxy Server Nameserver")}
|
||||
secondary={t("Proxy Node Nameserver")}
|
||||
primary={t("settings.modals.dns.fields.proxy.label")}
|
||||
secondary={t("settings.modals.dns.fields.proxy.description")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -868,8 +884,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Direct Nameserver")}
|
||||
secondary={t("Direct outbound Nameserver")}
|
||||
primary={t("settings.modals.dns.fields.directNameserver.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.directNameserver.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -885,8 +903,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fake IP Filter")}
|
||||
secondary={t("Domains that skip fake IP resolution")}
|
||||
primary={t("settings.modals.dns.fields.fakeIpFilter.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.fakeIpFilter.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -902,8 +922,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Nameserver Policy")}
|
||||
secondary={t("Domain-specific DNS server")}
|
||||
primary={t("settings.modals.dns.fields.nameserverPolicy.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.nameserverPolicy.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -921,13 +943,15 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
variant="subtitle2"
|
||||
sx={{ mt: 2, mb: 1, fontWeight: "bold" }}
|
||||
>
|
||||
{t("Fallback Filter Settings")}
|
||||
{t("settings.modals.dns.sections.fallbackFilter")}
|
||||
</Typography>
|
||||
|
||||
<Item>
|
||||
<ListItemText
|
||||
primary={t("GeoIP Filtering")}
|
||||
secondary={t("Enable GeoIP filtering for fallback")}
|
||||
primary={t("settings.modals.dns.fields.geoipFiltering.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.geoipFiltering.description",
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -937,7 +961,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("GeoIP Code")} />
|
||||
<ListItemText primary={t("settings.modals.dns.fields.geoipCode")} />
|
||||
<TextField
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
@@ -950,8 +974,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback IP CIDR")}
|
||||
secondary={t("IP CIDRs not using fallback servers")}
|
||||
primary={t("settings.modals.dns.fields.fallbackIpCidr.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.fallbackIpCidr.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -967,8 +993,10 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Fallback Domain")}
|
||||
secondary={t("Domains using fallback servers")}
|
||||
primary={t("settings.modals.dns.fields.fallbackDomain.label")}
|
||||
secondary={t(
|
||||
"settings.modals.dns.fields.fallbackDomain.description",
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
@@ -987,13 +1015,13 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
variant="subtitle1"
|
||||
sx={{ mt: 3, mb: 0, fontWeight: "bold" }}
|
||||
>
|
||||
{t("Hosts Settings")}
|
||||
{t("settings.modals.dns.sections.hosts")}
|
||||
</Typography>
|
||||
|
||||
<Item sx={{ flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<ListItemText
|
||||
primary={t("Hosts")}
|
||||
secondary={t("Custom domain to IP or domain mapping")}
|
||||
primary={t("settings.modals.dns.fields.hosts.label")}
|
||||
secondary={t("settings.modals.dns.fields.hosts.description")}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
|
||||
@@ -140,10 +140,12 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
setOpen(false);
|
||||
showNotice("success", t("Configuration saved successfully"));
|
||||
showNotice.success(
|
||||
"shared.feedback.notifications.common.saveSuccess",
|
||||
);
|
||||
},
|
||||
onError: () => {
|
||||
showNotice("error", t("Failed to save configuration"));
|
||||
showNotice.error("shared.feedback.notifications.common.saveFailed");
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -181,10 +183,10 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("External Cors Configuration")}
|
||||
title={t("settings.sections.externalCors.title")}
|
||||
contentSx={{ width: 500 }}
|
||||
okBtn={loading ? t("Saving...") : t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={loading ? t("shared.statuses.saving") : t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={handleSave}
|
||||
@@ -198,7 +200,7 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
width="100%"
|
||||
>
|
||||
<span style={{ fontWeight: "normal" }}>
|
||||
{t("Allow private network access")}
|
||||
{t("settings.sections.externalCors.fields.allowPrivateNetwork")}
|
||||
</span>
|
||||
<Switch
|
||||
edge="end"
|
||||
@@ -218,7 +220,7 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
<ListItem sx={{ padding: "8px 0" }}>
|
||||
<div style={{ width: "100%" }}>
|
||||
<div style={{ marginBottom: 8, fontWeight: "bold" }}>
|
||||
{t("Allowed Origins")}
|
||||
{t("settings.sections.externalCors.fields.allowedOrigins")}
|
||||
</div>
|
||||
{originEntries.map(({ origin, index, key }) => (
|
||||
<div
|
||||
@@ -235,7 +237,9 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
sx={{ fontSize: 14, marginRight: 2 }}
|
||||
value={origin}
|
||||
onChange={(e) => handleUpdateOrigin(index, e.target.value)}
|
||||
placeholder={t("Please enter a valid url")}
|
||||
placeholder={t(
|
||||
"settings.sections.externalCors.placeholders.origin",
|
||||
)}
|
||||
inputProps={{ style: { fontSize: 14 } }}
|
||||
/>
|
||||
<Button
|
||||
@@ -256,7 +260,7 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
onClick={handleAddOrigin}
|
||||
sx={addButtonStyle}
|
||||
>
|
||||
{t("Add")}
|
||||
{t("settings.sections.externalCors.actions.add")}
|
||||
</Button>
|
||||
|
||||
<div
|
||||
@@ -270,7 +274,7 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
||||
<div
|
||||
style={{ color: "#666", fontSize: 12, fontStyle: "italic" }}
|
||||
>
|
||||
{t("Always included origins: {{urls}}", {
|
||||
{t("settings.sections.externalCors.messages.alwaysIncluded", {
|
||||
urls: DEV_URLS.join(", "),
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -102,7 +102,7 @@ export const HotkeyInput = (props: Props) => {
|
||||
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Delete")}
|
||||
title={t("shared.actions.delete")}
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
onChange([]);
|
||||
|
||||
@@ -24,7 +24,19 @@ const HOTKEY_FUNC = [
|
||||
"toggle_system_proxy",
|
||||
"toggle_tun_mode",
|
||||
"entry_lightweight_mode",
|
||||
];
|
||||
] as const;
|
||||
|
||||
const HOTKEY_FUNC_LABELS: Record<(typeof HOTKEY_FUNC)[number], string> = {
|
||||
open_or_close_dashboard:
|
||||
"settings.modals.hotkey.functions.openOrCloseDashboard",
|
||||
clash_mode_rule: "settings.modals.hotkey.functions.rule",
|
||||
clash_mode_global: "settings.modals.hotkey.functions.global",
|
||||
clash_mode_direct: "settings.modals.hotkey.functions.direct",
|
||||
toggle_system_proxy: "settings.modals.hotkey.functions.toggleSystemProxy",
|
||||
toggle_tun_mode: "settings.modals.hotkey.functions.toggleTunMode",
|
||||
entry_lightweight_mode:
|
||||
"settings.modals.hotkey.functions.entryLightweightMode",
|
||||
};
|
||||
|
||||
export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -81,24 +93,26 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
enable_global_hotkey: enableGlobalHotkey,
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Hotkey Setting")}
|
||||
title={t("settings.modals.hotkey.title")}
|
||||
contentSx={{ width: 450, maxHeight: 380 }}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<ItemWrapper style={{ marginBottom: 16 }}>
|
||||
<Typography>{t("Enable Global Hotkey")}</Typography>
|
||||
<Typography>
|
||||
{t("settings.modals.hotkey.toggles.enableGlobal")}
|
||||
</Typography>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={enableGlobalHotkey}
|
||||
@@ -108,7 +122,7 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
{HOTKEY_FUNC.map((func) => (
|
||||
<ItemWrapper key={func}>
|
||||
<Typography>{t(func)}</Typography>
|
||||
<Typography>{t(HOTKEY_FUNC_LABELS[func])}</Typography>
|
||||
<HotkeyInput
|
||||
value={hotkeyMap[func] ?? []}
|
||||
onChange={(v) => setHotkeyMap((m) => ({ ...m, [func]: v }))}
|
||||
|
||||
@@ -104,7 +104,7 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
|
||||
const onSwitchFormat = (_e: any, value: boolean) => value;
|
||||
const onError = (err: any) => {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
};
|
||||
const onChangeData = (patch: Partial<IVergeConfig>) => {
|
||||
mutateVerge({ ...verge, ...patch }, false);
|
||||
@@ -113,16 +113,20 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Layout Setting")}
|
||||
title={t("settings.components.verge.layout.title")}
|
||||
contentSx={{ width: 450 }}
|
||||
disableOk
|
||||
cancelBtn={t("Close")}
|
||||
cancelBtn={t("shared.actions.close")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
>
|
||||
<List>
|
||||
<Item>
|
||||
<ListItemText primary={t("Prefer System Titlebar")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.components.verge.layout.fields.preferSystemTitlebar",
|
||||
)}
|
||||
/>
|
||||
<GuardState
|
||||
value={decorated}
|
||||
valueProps="checked"
|
||||
@@ -137,7 +141,9 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Traffic Graph")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.layout.fields.trafficGraph")}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.traffic_graph ?? true}
|
||||
valueProps="checked"
|
||||
@@ -151,7 +157,9 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Memory Usage")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.layout.fields.memoryUsage")}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.enable_memory_usage ?? true}
|
||||
valueProps="checked"
|
||||
@@ -165,7 +173,11 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Proxy Group Icon")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.components.verge.layout.fields.proxyGroupIcon",
|
||||
)}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.enable_group_icon ?? true}
|
||||
valueProps="checked"
|
||||
@@ -182,9 +194,13 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
<ListItemText
|
||||
primary={
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
|
||||
<span>{t("Hover Jump Navigator")}</span>
|
||||
<span>
|
||||
{t("settings.components.verge.layout.fields.hoverNavigator")}
|
||||
</span>
|
||||
<TooltipIcon
|
||||
title={t("Hover Jump Navigator Info")}
|
||||
title={t(
|
||||
"settings.components.verge.layout.tooltips.hoverNavigator",
|
||||
)}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
</Box>
|
||||
@@ -206,9 +222,15 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
<ListItemText
|
||||
primary={
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
|
||||
<span>{t("Hover Jump Navigator Delay")}</span>
|
||||
<span>
|
||||
{t(
|
||||
"settings.components.verge.layout.fields.hoverNavigatorDelay",
|
||||
)}
|
||||
</span>
|
||||
<TooltipIcon
|
||||
title={t("Hover Jump Navigator Delay Info")}
|
||||
title={t(
|
||||
"settings.components.verge.layout.tooltips.hoverNavigatorDelay",
|
||||
)}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
</Box>
|
||||
@@ -241,7 +263,7 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
{t("millis")}
|
||||
{t("shared.units.milliseconds")}
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
@@ -256,7 +278,9 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Nav Icon")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.layout.fields.navIcon")}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.menu_icon ?? "monochrome"}
|
||||
onCatch={onError}
|
||||
@@ -265,16 +289,24 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
onGuard={(value) => patchVerge({ menu_icon: value })}
|
||||
>
|
||||
<Select size="small" sx={{ width: 140, "> div": { py: "7.5px" } }}>
|
||||
<MenuItem value="monochrome">{t("Monochrome")}</MenuItem>
|
||||
<MenuItem value="colorful">{t("Colorful")}</MenuItem>
|
||||
<MenuItem value="disable">{t("Disable")}</MenuItem>
|
||||
<MenuItem value="monochrome">
|
||||
{t("settings.components.verge.layout.options.icon.monochrome")}
|
||||
</MenuItem>
|
||||
<MenuItem value="colorful">
|
||||
{t("settings.components.verge.layout.options.icon.colorful")}
|
||||
</MenuItem>
|
||||
<MenuItem value="disable">
|
||||
{t("settings.components.verge.layout.options.icon.disable")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</Item>
|
||||
|
||||
{OS === "macos" && (
|
||||
<Item>
|
||||
<ListItemText primary={t("Tray Icon")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.layout.fields.trayIcon")}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.tray_icon ?? "monochrome"}
|
||||
onCatch={onError}
|
||||
@@ -286,15 +318,21 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
size="small"
|
||||
sx={{ width: 140, "> div": { py: "7.5px" } }}
|
||||
>
|
||||
<MenuItem value="monochrome">{t("Monochrome")}</MenuItem>
|
||||
<MenuItem value="colorful">{t("Colorful")}</MenuItem>
|
||||
<MenuItem value="monochrome">
|
||||
{t(
|
||||
"settings.components.verge.layout.options.icon.monochrome",
|
||||
)}
|
||||
</MenuItem>
|
||||
<MenuItem value="colorful">
|
||||
{t("settings.components.verge.layout.options.icon.colorful")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</Item>
|
||||
)}
|
||||
{/* {OS === "macos" && (
|
||||
<Item>
|
||||
<ListItemText primary={t("Enable Tray Speed")} />
|
||||
<ListItemText primary={t("settings.components.verge.layout.fields.enableTraySpeed")} />
|
||||
<GuardState
|
||||
value={verge?.enable_tray_speed ?? false}
|
||||
valueProps="checked"
|
||||
@@ -309,7 +347,7 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
)} */}
|
||||
{/* {OS === "macos" && (
|
||||
<Item>
|
||||
<ListItemText primary={t("Enable Tray Icon")} />
|
||||
<ListItemText primary={t("settings.components.verge.layout.fields.enableTrayIcon")} />
|
||||
<GuardState
|
||||
value={
|
||||
verge?.enable_tray_icon === false &&
|
||||
@@ -328,7 +366,11 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
)} */}
|
||||
<Item>
|
||||
<ListItemText primary={t("Show Proxy Groups Inline")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.components.verge.layout.fields.showProxyGroupsInline",
|
||||
)}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.tray_inline_proxy_groups ?? false}
|
||||
valueProps="checked"
|
||||
@@ -342,7 +384,11 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Common Tray Icon")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.components.verge.layout.fields.commonTrayIcon",
|
||||
)}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.common_tray_icon}
|
||||
onCatch={onError}
|
||||
@@ -384,13 +430,19 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{verge?.common_tray_icon ? t("Clear") : t("Browse")}
|
||||
{verge?.common_tray_icon
|
||||
? t("shared.actions.clear")
|
||||
: t("settings.components.verge.basic.actions.browse")}
|
||||
</Button>
|
||||
</GuardState>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("System Proxy Tray Icon")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.components.verge.layout.fields.systemProxyTrayIcon",
|
||||
)}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.sysproxy_tray_icon}
|
||||
onCatch={onError}
|
||||
@@ -430,13 +482,17 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{verge?.sysproxy_tray_icon ? t("Clear") : t("Browse")}
|
||||
{verge?.sysproxy_tray_icon
|
||||
? t("shared.actions.clear")
|
||||
: t("settings.components.verge.basic.actions.browse")}
|
||||
</Button>
|
||||
</GuardState>
|
||||
</Item>
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Tun Tray Icon")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.layout.fields.tunTrayIcon")}
|
||||
/>
|
||||
<GuardState
|
||||
value={verge?.tun_tray_icon}
|
||||
onCatch={onError}
|
||||
@@ -474,7 +530,9 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{verge?.tun_tray_icon ? t("Clear") : t("Browse")}
|
||||
{verge?.tun_tray_icon
|
||||
? t("shared.actions.clear")
|
||||
: t("settings.components.verge.basic.actions.browse")}
|
||||
</Button>
|
||||
</GuardState>
|
||||
</Item>
|
||||
|
||||
@@ -45,25 +45,27 @@ export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
auto_light_weight_minutes: values.autoEnterLiteModeDelay,
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("LightWeight Mode Settings")}
|
||||
title={t("settings.modals.liteMode.title")}
|
||||
contentSx={{ width: 450 }}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Enter LightWeight Mode Now")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.liteMode.actions.enterNow")}
|
||||
/>
|
||||
<Typography
|
||||
variant="button"
|
||||
sx={{
|
||||
@@ -73,17 +75,17 @@ export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
}}
|
||||
onClick={async () => await entry_lightweight_mode()}
|
||||
>
|
||||
{t("Enable")}
|
||||
{t("shared.actions.enable")}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Auto Enter LightWeight Mode")}
|
||||
primary={t("settings.modals.liteMode.toggles.autoEnter")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon
|
||||
title={t("Auto Enter LightWeight Mode Info")}
|
||||
title={t("settings.modals.liteMode.tooltips.autoEnter")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<Switch
|
||||
@@ -99,7 +101,9 @@ export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
{values.autoEnterLiteMode && (
|
||||
<>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Auto Enter LightWeight Mode Delay")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.liteMode.fields.delay")}
|
||||
/>
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
size="small"
|
||||
@@ -119,7 +123,7 @@ export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
{t("mins")}
|
||||
{t("shared.units.minutes")}
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
@@ -133,10 +137,9 @@ export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
color="text.secondary"
|
||||
sx={{ fontStyle: "italic" }}
|
||||
>
|
||||
{t(
|
||||
"When closing the window, LightWeight Mode will be automatically activated after _n minutes",
|
||||
{ n: values.autoEnterLiteModeDelay },
|
||||
)}
|
||||
{t("settings.modals.liteMode.messages.autoEnterHint", {
|
||||
n: values.autoEnterLiteModeDelay,
|
||||
})}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</>
|
||||
|
||||
@@ -20,11 +20,13 @@ export const LocalBackupActions = memo(
|
||||
try {
|
||||
setLoading(true);
|
||||
await createLocalBackup();
|
||||
showNotice("success", t("Local Backup Created"));
|
||||
showNotice.success(
|
||||
"settings.modals.backup.messages.localBackupCreated",
|
||||
);
|
||||
await onBackupSuccess();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showNotice("error", t("Local Backup Failed"));
|
||||
showNotice.error("settings.modals.backup.messages.localBackupFailed");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -43,7 +45,7 @@ export const LocalBackupActions = memo(
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={{ xs: 12, sm: 9 }}>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t("Local Backup Info")}
|
||||
{t("settings.modals.backup.fields.info")}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 3 }}>
|
||||
@@ -60,7 +62,7 @@ export const LocalBackupActions = memo(
|
||||
type="button"
|
||||
size="large"
|
||||
>
|
||||
{t("Backup")}
|
||||
{t("settings.modals.backup.actions.backup")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
@@ -68,7 +70,7 @@ export const LocalBackupActions = memo(
|
||||
type="button"
|
||||
size="large"
|
||||
>
|
||||
{t("Refresh")}
|
||||
{t("shared.actions.refresh")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
@@ -69,25 +69,27 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
auto_log_clean: values.autoLogClean as any,
|
||||
});
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.toString());
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Miscellaneous")}
|
||||
title={t("settings.modals.misc.title")}
|
||||
contentSx={{ width: 450 }}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("App Log Level")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.misc.fields.appLogLevel")}
|
||||
/>
|
||||
<Select
|
||||
size="small"
|
||||
sx={{ width: 100, "> div": { py: "7.5px" } }}
|
||||
@@ -109,7 +111,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("App Log Max Size")}
|
||||
primary={t("settings.modals.misc.fields.appLogMaxSize")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TextField
|
||||
@@ -130,7 +132,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("KB")}</InputAdornment>
|
||||
<InputAdornment position="end">
|
||||
{t("shared.units.kilobytes")}
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
@@ -139,7 +143,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("App Log Max Count")}
|
||||
primary={t("settings.modals.misc.fields.appLogMaxCount")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TextField
|
||||
@@ -160,7 +164,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("Files")}</InputAdornment>
|
||||
<InputAdornment position="end">
|
||||
{t("shared.units.files")}
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
@@ -169,11 +175,11 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Auto Close Connections")}
|
||||
primary={t("settings.modals.misc.fields.autoCloseConnections")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon
|
||||
title={t("Auto Close Connections Info")}
|
||||
title={t("settings.modals.misc.tooltips.autoCloseConnections")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<Switch
|
||||
@@ -187,7 +193,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Auto Check Update")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.misc.fields.autoCheckUpdate")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.autoCheckUpdate}
|
||||
@@ -199,11 +207,11 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Enable Builtin Enhanced")}
|
||||
primary={t("settings.modals.misc.fields.enableBuiltinEnhanced")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon
|
||||
title={t("Enable Builtin Enhanced Info")}
|
||||
title={t("settings.modals.misc.tooltips.enableBuiltinEnhanced")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<Switch
|
||||
@@ -217,7 +225,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Proxy Layout Columns")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.misc.fields.proxyLayoutColumns")}
|
||||
/>
|
||||
<Select
|
||||
size="small"
|
||||
sx={{ width: 160, "> div": { py: "7.5px" } }}
|
||||
@@ -230,7 +240,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
}
|
||||
>
|
||||
<MenuItem value={6} key={6}>
|
||||
{t("Auto Columns")}
|
||||
{t("settings.modals.misc.options.proxyLayoutColumns.auto")}
|
||||
</MenuItem>
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<MenuItem value={i} key={i}>
|
||||
@@ -241,7 +251,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Auto Log Clean")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.misc.fields.autoLogClean")}
|
||||
/>
|
||||
<Select
|
||||
size="small"
|
||||
sx={{ width: 160, "> div": { py: "7.5px" } }}
|
||||
@@ -255,11 +267,34 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
>
|
||||
{/* 1: 1天, 2: 7天, 3: 30天, 4: 90天*/}
|
||||
{[
|
||||
{ key: t("Never Clean"), value: 0 },
|
||||
{ key: t("Retain _n Days", { n: 1 }), value: 1 },
|
||||
{ key: t("Retain _n Days", { n: 7 }), value: 2 },
|
||||
{ key: t("Retain _n Days", { n: 30 }), value: 3 },
|
||||
{ key: t("Retain _n Days", { n: 90 }), value: 4 },
|
||||
{
|
||||
key: t("settings.modals.misc.options.autoLogClean.never"),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
key: t("settings.modals.misc.options.autoLogClean.retainDays", {
|
||||
n: 1,
|
||||
}),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
key: t("settings.modals.misc.options.autoLogClean.retainDays", {
|
||||
n: 7,
|
||||
}),
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
key: t("settings.modals.misc.options.autoLogClean.retainDays", {
|
||||
n: 30,
|
||||
}),
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
key: t("settings.modals.misc.options.autoLogClean.retainDays", {
|
||||
n: 90,
|
||||
}),
|
||||
value: 4,
|
||||
},
|
||||
].map((i) => (
|
||||
<MenuItem key={i.value} value={i.value}>
|
||||
{i.key}
|
||||
@@ -270,11 +305,11 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Auto Delay Detection")}
|
||||
primary={t("settings.modals.misc.fields.autoDelayDetection")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon
|
||||
title={t("Auto Delay Detection Info")}
|
||||
title={t("settings.modals.misc.tooltips.autoDelayDetection")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<Switch
|
||||
@@ -289,11 +324,11 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Default Latency Test")}
|
||||
primary={t("settings.modals.misc.fields.defaultLatencyTest")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon
|
||||
title={t("Default Latency Test Info")}
|
||||
title={t("settings.modals.misc.tooltips.defaultLatencyTest")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<TextField
|
||||
@@ -312,7 +347,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Default Latency Timeout")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.misc.fields.defaultLatencyTimeout")}
|
||||
/>
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
size="small"
|
||||
@@ -332,7 +369,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("millis")}</InputAdornment>
|
||||
<InputAdornment position="end">
|
||||
{t("shared.units.milliseconds")}
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -35,7 +35,7 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
{t("Network Interface")}
|
||||
{t("settings.modals.networkInterface.title")}
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -51,7 +51,7 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
}
|
||||
contentSx={{ width: 450 }}
|
||||
disableOk
|
||||
cancelBtn={t("Close")}
|
||||
cancelBtn={t("shared.actions.close")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
>
|
||||
@@ -66,13 +66,17 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
address.V4 && (
|
||||
<AddressDisplay
|
||||
key={address.V4.ip}
|
||||
label={t("Ip Address")}
|
||||
label={t(
|
||||
"settings.modals.networkInterface.fields.ipAddress",
|
||||
)}
|
||||
content={address.V4.ip}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AddressDisplay
|
||||
label={t("Mac Address")}
|
||||
label={t(
|
||||
"settings.modals.networkInterface.fields.macAddress",
|
||||
)}
|
||||
content={item.mac_addr ?? ""}
|
||||
/>
|
||||
</>
|
||||
@@ -84,13 +88,17 @@ export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
address.V6 && (
|
||||
<AddressDisplay
|
||||
key={address.V6.ip}
|
||||
label={t("Ip Address")}
|
||||
label={t(
|
||||
"settings.modals.networkInterface.fields.ipAddress",
|
||||
)}
|
||||
content={address.V6.ip}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
<AddressDisplay
|
||||
label={t("Mac Address")}
|
||||
label={t(
|
||||
"settings.modals.networkInterface.fields.macAddress",
|
||||
)}
|
||||
content={item.mac_addr ?? ""}
|
||||
/>
|
||||
</>
|
||||
@@ -109,8 +117,6 @@ const AddressDisplay = ({
|
||||
label: string;
|
||||
content: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -135,7 +141,9 @@ const AddressDisplay = ({
|
||||
size="small"
|
||||
onClick={async () => {
|
||||
await writeText(content);
|
||||
showNotice("success", t("Copy Success"));
|
||||
showNotice.success(
|
||||
"shared.feedback.notifications.common.copySuccess",
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ContentCopyRounded sx={{ fontSize: "18px" }} />
|
||||
|
||||
@@ -21,13 +21,15 @@ export const PasswordInput = (props: Props) => {
|
||||
|
||||
return (
|
||||
<Dialog open={true} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>{t("Please enter your root password")}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{t("settings.modals.password.prompts.enterRoot")}
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<TextField
|
||||
sx={{ mt: 1 }}
|
||||
autoFocus
|
||||
label={t("Password")}
|
||||
label={t("shared.labels.password")}
|
||||
fullWidth
|
||||
size="small"
|
||||
type="password"
|
||||
@@ -42,7 +44,7 @@ export const PasswordInput = (props: Props) => {
|
||||
onClick={async () => await onConfirm(passwd)}
|
||||
variant="contained"
|
||||
>
|
||||
{t("Confirm")}
|
||||
{t("shared.actions.confirm")}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
@@ -160,8 +160,8 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
mutate("getAutotemProxy"),
|
||||
]);
|
||||
}
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.toString());
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -277,14 +277,11 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
const onSave = useLockFn(async () => {
|
||||
if (value.duration < 1) {
|
||||
showNotice(
|
||||
"error",
|
||||
t("Proxy Daemon Duration Cannot be Less than 1 Second"),
|
||||
);
|
||||
showNotice.error("settings.modals.sysproxy.messages.durationTooShort");
|
||||
return;
|
||||
}
|
||||
if (value.bypass && !validReg.test(value.bypass)) {
|
||||
showNotice("error", t("Invalid Bypass Format"));
|
||||
showNotice.error("settings.modals.sysproxy.messages.invalidBypass");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -301,7 +298,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
!ipv6Regex.test(value.proxy_host) &&
|
||||
!hostnameRegex.test(value.proxy_host)
|
||||
) {
|
||||
showNotice("error", t("Invalid Proxy Host Format"));
|
||||
showNotice.error("settings.modals.sysproxy.messages.invalidProxyHost");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -401,10 +398,10 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
console.warn("代理状态更新失败:", err);
|
||||
}
|
||||
}, 50);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
console.error("配置保存失败:", err);
|
||||
mutateVerge();
|
||||
showNotice("error", err.toString());
|
||||
showNotice.error(err);
|
||||
// setOpen(true);
|
||||
}
|
||||
});
|
||||
@@ -413,10 +410,10 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("System Proxy Setting")}
|
||||
title={t("settings.modals.sysproxy.title")}
|
||||
contentSx={{ width: 450, maxHeight: 565 }}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
@@ -424,28 +421,37 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
disableOk={saving}
|
||||
>
|
||||
<List>
|
||||
<BaseFieldset label={t("Current System Proxy")} padding="15px 10px">
|
||||
<BaseFieldset
|
||||
label={t("settings.modals.sysproxy.fieldsets.currentStatus")}
|
||||
padding="15px 10px"
|
||||
>
|
||||
<FlexBox>
|
||||
<Typography className="label">{t("Enable status")}</Typography>
|
||||
<Typography className="label">
|
||||
{t("settings.modals.sysproxy.fields.enableStatus")}
|
||||
</Typography>
|
||||
<Typography className="value">
|
||||
{value.pac
|
||||
? autoproxy?.enable
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
? t("shared.statuses.enabled")
|
||||
: t("shared.statuses.disabled")
|
||||
: sysproxy?.enable
|
||||
? t("Enabled")
|
||||
: t("Disabled")}
|
||||
? t("shared.statuses.enabled")
|
||||
: t("shared.statuses.disabled")}
|
||||
</Typography>
|
||||
</FlexBox>
|
||||
{!value.pac && (
|
||||
<FlexBox>
|
||||
<Typography className="label">{t("Server Addr")}</Typography>
|
||||
<Typography className="label">
|
||||
{t("settings.modals.sysproxy.fields.serverAddr")}
|
||||
</Typography>
|
||||
<Typography className="value">{getSystemProxyAddress}</Typography>
|
||||
</FlexBox>
|
||||
)}
|
||||
{value.pac && (
|
||||
<FlexBox>
|
||||
<Typography className="label">{t("PAC URL")}</Typography>
|
||||
<Typography className="label">
|
||||
{t("settings.modals.sysproxy.fields.pacUrl")}
|
||||
</Typography>
|
||||
<Typography className="value">
|
||||
{getCurrentPacUrl || "-"}
|
||||
</Typography>
|
||||
@@ -453,7 +459,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
)}
|
||||
</BaseFieldset>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Proxy Host")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.sysproxy.fields.proxyHost")}
|
||||
/>
|
||||
<Autocomplete
|
||||
size="small"
|
||||
sx={{ width: 150 }}
|
||||
@@ -478,7 +486,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Use PAC Mode")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.sysproxy.fields.usePacMode")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
disabled={!enabled}
|
||||
@@ -489,10 +499,13 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText
|
||||
primary={t("Proxy Guard")}
|
||||
primary={t("settings.modals.sysproxy.fields.proxyGuard")}
|
||||
sx={{ maxWidth: "fit-content" }}
|
||||
/>
|
||||
<TooltipIcon title={t("Proxy Guard Info")} sx={{ opacity: "0.7" }} />
|
||||
<TooltipIcon
|
||||
title={t("settings.modals.sysproxy.tooltips.proxyGuard")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
disabled={!enabled}
|
||||
@@ -503,7 +516,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Guard Duration")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.sysproxy.fields.guardDuration")}
|
||||
/>
|
||||
<TextField
|
||||
disabled={!enabled}
|
||||
size="small"
|
||||
@@ -524,7 +539,11 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
</ListItem>
|
||||
{!value.pac && (
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Always use Default Bypass")} />
|
||||
<ListItemText
|
||||
primary={t(
|
||||
"settings.modals.sysproxy.fields.alwaysUseDefaultBypass",
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
disabled={!enabled}
|
||||
@@ -543,7 +562,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
{!value.pac && !value.use_default && (
|
||||
<>
|
||||
<ListItemText primary={t("Proxy Bypass")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.sysproxy.fields.proxyBypass")}
|
||||
/>
|
||||
<TextField
|
||||
error={value.bypass ? !validReg.test(value.bypass) : false}
|
||||
disabled={!enabled}
|
||||
@@ -561,7 +582,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
|
||||
{!value.pac && value.use_default && (
|
||||
<>
|
||||
<ListItemText primary={t("Bypass")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.sysproxy.fields.bypass")}
|
||||
/>
|
||||
<FlexBox>
|
||||
<TextField
|
||||
disabled={true}
|
||||
@@ -578,7 +601,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
{value.pac && (
|
||||
<ListItem sx={{ padding: "5px 2px", alignItems: "start" }}>
|
||||
<ListItemText
|
||||
primary={t("PAC Script Content")}
|
||||
primary={t("settings.modals.sysproxy.fields.pacScriptContent")}
|
||||
sx={{ padding: "3px 0" }}
|
||||
/>
|
||||
<Button
|
||||
@@ -588,12 +611,12 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
setEditorOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("Edit")} PAC
|
||||
{t("settings.modals.sysproxy.actions.editPac")}
|
||||
</Button>
|
||||
{editorOpen && (
|
||||
<EditorViewer
|
||||
open={true}
|
||||
title={`${t("Edit")} PAC`}
|
||||
title={t("settings.modals.sysproxy.actions.editPac")}
|
||||
initialData={Promise.resolve(value.pac_content ?? "")}
|
||||
language="javascript"
|
||||
onSave={(_prev, curr) => {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ThemeModeSwitch = (props: Props) => {
|
||||
onClick={() => onChange?.(mode)}
|
||||
sx={{ textTransform: "capitalize" }}
|
||||
>
|
||||
{t(`theme.${mode}`)}
|
||||
{t(`settings.sections.appearance.${mode}`)}
|
||||
</Button>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useImperativeHandle, useState } from "react";
|
||||
import { useImperativeHandle, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { BaseDialog, DialogRef } from "@/components/base";
|
||||
@@ -50,8 +50,8 @@ export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||
try {
|
||||
await patchVerge({ theme_setting: theme });
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.toString());
|
||||
} catch (err) {
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,9 +62,48 @@ export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||
|
||||
type ThemeKey = keyof typeof theme & keyof typeof defaultTheme;
|
||||
|
||||
const renderItem = (label: string, key: ThemeKey) => {
|
||||
const fieldDefinitions: Array<{ labelKey: string; key: ThemeKey }> = useMemo(
|
||||
() => [
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.primaryColor",
|
||||
key: "primary_color",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.secondaryColor",
|
||||
key: "secondary_color",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.primaryText",
|
||||
key: "primary_text",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.secondaryText",
|
||||
key: "secondary_text",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.infoColor",
|
||||
key: "info_color",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.warningColor",
|
||||
key: "warning_color",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.errorColor",
|
||||
key: "error_color",
|
||||
},
|
||||
{
|
||||
labelKey: "settings.components.verge.theme.fields.successColor",
|
||||
key: "success_color",
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
const renderItem = (labelKey: string, key: ThemeKey) => {
|
||||
const label = t(labelKey);
|
||||
return (
|
||||
<Item>
|
||||
<Item key={key}>
|
||||
<ListItemText primary={label} />
|
||||
<Round sx={{ background: theme[key] || dt[key] }} />
|
||||
<TextField
|
||||
@@ -81,33 +120,21 @@ export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||
return (
|
||||
<BaseDialog
|
||||
open={open}
|
||||
title={t("Theme Setting")}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
title={t("settings.components.verge.theme.title")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
contentSx={{ width: 400, maxHeight: 505, overflow: "auto", pb: 0 }}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List sx={{ pt: 0 }}>
|
||||
{renderItem(t("Primary Color"), "primary_color")}
|
||||
|
||||
{renderItem(t("Secondary Color"), "secondary_color")}
|
||||
|
||||
{renderItem(t("Primary Text"), "primary_text")}
|
||||
|
||||
{renderItem(t("Secondary Text"), "secondary_text")}
|
||||
|
||||
{renderItem(t("Info Color"), "info_color")}
|
||||
|
||||
{renderItem(t("Warning Color"), "warning_color")}
|
||||
|
||||
{renderItem(t("Error Color"), "error_color")}
|
||||
|
||||
{renderItem(t("Success Color"), "success_color")}
|
||||
{fieldDefinitions.map((field) => renderItem(field.labelKey, field.key))}
|
||||
|
||||
<Item>
|
||||
<ListItemText primary={t("Font Family")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.theme.fields.fontFamily")}
|
||||
/>
|
||||
<TextField
|
||||
{...textProps}
|
||||
value={theme.font_family ?? ""}
|
||||
@@ -116,7 +143,9 @@ export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<ListItemText primary={t("CSS Injection")} />
|
||||
<ListItemText
|
||||
primary={t("settings.components.verge.theme.fields.cssInjection")}
|
||||
/>
|
||||
<Button
|
||||
startIcon={<EditRounded />}
|
||||
variant="outlined"
|
||||
@@ -124,12 +153,12 @@ export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||
setEditorOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("Edit")} CSS
|
||||
{t("settings.components.verge.theme.actions.editCss")}
|
||||
</Button>
|
||||
{editorOpen && (
|
||||
<EditorViewer
|
||||
open={true}
|
||||
title={`${t("Edit")} CSS`}
|
||||
title={t("settings.components.verge.theme.dialogs.editCssTitle")}
|
||||
initialData={Promise.resolve(theme.css_injection ?? "")}
|
||||
language="css"
|
||||
onSave={(_prev, curr) => {
|
||||
|
||||
@@ -80,13 +80,13 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
);
|
||||
try {
|
||||
await enhanceProfiles();
|
||||
showNotice("success", t("Settings Applied"));
|
||||
showNotice.success("settings.modals.tun.messages.applied");
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
setOpen(false);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,7 +95,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between" gap={1}>
|
||||
<Typography variant="h6">{t("Tun Mode")}</Typography>
|
||||
<Typography variant="h6">{t("settings.modals.tun.title")}</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
@@ -128,20 +128,20 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("Reset to Default")}
|
||||
{t("shared.actions.resetToDefault")}
|
||||
</Button>
|
||||
</Box>
|
||||
}
|
||||
contentSx={{ width: 450 }}
|
||||
okBtn={t("Save")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("shared.actions.save")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onSave}
|
||||
>
|
||||
<List>
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Stack")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.stack")} />
|
||||
<StackModeSwitch
|
||||
value={values.stack}
|
||||
onChange={(value) => {
|
||||
@@ -154,7 +154,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Device")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.device")} />
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
size="small"
|
||||
@@ -171,7 +171,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Auto Route")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.autoRoute")} />
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.autoRoute}
|
||||
@@ -180,7 +180,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Strict Route")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.strictRoute")} />
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.strictRoute}
|
||||
@@ -189,7 +189,9 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("Auto Detect Interface")} />
|
||||
<ListItemText
|
||||
primary={t("settings.modals.tun.fields.autoDetectInterface")}
|
||||
/>
|
||||
<Switch
|
||||
edge="end"
|
||||
checked={values.autoDetectInterface}
|
||||
@@ -200,7 +202,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("DNS Hijack")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.dnsHijack")} />
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
size="small"
|
||||
@@ -209,7 +211,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
spellCheck="false"
|
||||
sx={{ width: 250 }}
|
||||
value={values.dnsHijack.join(",")}
|
||||
placeholder="Please use , to separate multiple DNS servers"
|
||||
placeholder={t("settings.modals.tun.tooltips.dnsHijack")}
|
||||
onChange={(e) =>
|
||||
setValues((v) => ({ ...v, dnsHijack: e.target.value.split(",") }))
|
||||
}
|
||||
@@ -217,7 +219,7 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
</ListItem>
|
||||
|
||||
<ListItem sx={{ padding: "5px 2px" }}>
|
||||
<ListItemText primary={t("MTU")} />
|
||||
<ListItemText primary={t("settings.modals.tun.fields.mtu")} />
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
size="small"
|
||||
|
||||
@@ -58,12 +58,12 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
const onUpdate = useLockFn(async () => {
|
||||
if (portableFlag) {
|
||||
showNotice("error", t("Portable Updater Error"));
|
||||
showNotice.error("settings.modals.update.messages.portableError");
|
||||
return;
|
||||
}
|
||||
if (!updateInfo?.body) return;
|
||||
if (breakChangeFlag) {
|
||||
showNotice("error", t("Break Change Update Error"));
|
||||
showNotice.error("settings.modals.update.messages.breakChangeError");
|
||||
return;
|
||||
}
|
||||
if (updateState) return;
|
||||
@@ -89,7 +89,7 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
await updateInfo.downloadAndInstall();
|
||||
await relaunch();
|
||||
} catch (err: any) {
|
||||
showNotice("error", err?.message || err.toString());
|
||||
showNotice.error(err);
|
||||
} finally {
|
||||
setUpdateState(false);
|
||||
if (progressListener) {
|
||||
@@ -113,7 +113,9 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
{`New Version v${updateInfo?.version}`}
|
||||
{t("settings.modals.update.title", {
|
||||
version: updateInfo?.version ?? "",
|
||||
})}
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -124,14 +126,14 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("Go to Release Page")}
|
||||
{t("settings.modals.update.actions.goToRelease")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
contentSx={{ minWidth: 360, maxWidth: 400, height: "50vh" }}
|
||||
okBtn={t("Update")}
|
||||
cancelBtn={t("Cancel")}
|
||||
okBtn={t("settings.modals.update.actions.update")}
|
||||
cancelBtn={t("shared.actions.cancel")}
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
onOk={onUpdate}
|
||||
|
||||
@@ -56,11 +56,13 @@ export const WebUIItem = (props: Props) => {
|
||||
size="small"
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
placeholder={t("Support %host, %port, %secret")}
|
||||
placeholder={t(
|
||||
"settings.modals.webUI.messages.supportedPlaceholders",
|
||||
)}
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Save")}
|
||||
title={t("shared.actions.save")}
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
onChange(editValue);
|
||||
@@ -71,7 +73,7 @@ export const WebUIItem = (props: Props) => {
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Cancel")}
|
||||
title={t("shared.actions.cancel")}
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
onCancel?.();
|
||||
@@ -127,7 +129,7 @@ export const WebUIItem = (props: Props) => {
|
||||
</Typography>
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Open URL")}
|
||||
title={t("settings.modals.webUI.actions.openUrl")}
|
||||
color="inherit"
|
||||
onClick={() => onOpenUrl?.(value)}
|
||||
>
|
||||
@@ -135,7 +137,7 @@ export const WebUIItem = (props: Props) => {
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Edit")}
|
||||
title={t("shared.actions.edit")}
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
setEditing(true);
|
||||
@@ -146,7 +148,7 @@ export const WebUIItem = (props: Props) => {
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small"
|
||||
title={t("Delete")}
|
||||
title={t("shared.actions.delete")}
|
||||
color="inherit"
|
||||
onClick={onDelete}
|
||||
>
|
||||
|
||||
@@ -92,7 +92,7 @@ export function WebUIViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
|
||||
await openWebUrl(url);
|
||||
} catch (e: any) {
|
||||
showNotice("error", e.message || e.toString());
|
||||
showNotice.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -101,14 +101,14 @@ export function WebUIViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
open={open}
|
||||
title={
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
{t("Web UI")}
|
||||
{t("settings.modals.webUI.title")}
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
disabled={editing}
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
{t("New")}
|
||||
{t("shared.actions.new")}
|
||||
</Button>
|
||||
</Box>
|
||||
}
|
||||
@@ -119,7 +119,7 @@ export function WebUIViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
overflowY: "auto",
|
||||
userSelect: "text",
|
||||
}}
|
||||
cancelBtn={t("Close")}
|
||||
cancelBtn={t("shared.actions.close")}
|
||||
disableOk
|
||||
onClose={() => setOpen(false)}
|
||||
onCancel={() => setOpen(false)}
|
||||
@@ -128,7 +128,7 @@ export function WebUIViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||
<BaseEmpty
|
||||
extra={
|
||||
<Typography mt={2} sx={{ fontSize: "12px" }}>
|
||||
{t("Replace host, port, secret with %host, %port, %secret")}
|
||||
{t("settings.modals.webUI.messages.placeholderInstruction")}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -67,9 +67,11 @@ const SettingClash = ({ onError }: Props) => {
|
||||
const onUpdateGeo = async () => {
|
||||
try {
|
||||
await updateGeo();
|
||||
showNotice("success", t("GeoData Updated"));
|
||||
showNotice.success(
|
||||
"settings.feedback.notifications.clash.geoDataUpdated",
|
||||
);
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,14 +88,14 @@ const SettingClash = ({ onError }: Props) => {
|
||||
} catch (err: any) {
|
||||
setDnsSettingsEnabled(!enable);
|
||||
localStorage.setItem("dns_settings_enabled", String(!enable));
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
await patchVerge({ enable_dns_settings: !enable }).catch(() => {});
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<SettingList title={t("Clash Setting")}>
|
||||
<SettingList title={t("settings.sections.clash.title")}>
|
||||
<WebUIViewer ref={webRef} />
|
||||
<ClashPortViewer ref={portRef} />
|
||||
<ControllerViewer ref={ctrlRef} />
|
||||
@@ -103,10 +105,10 @@ const SettingClash = ({ onError }: Props) => {
|
||||
<HeaderConfiguration ref={corsRef} />
|
||||
|
||||
<SettingItem
|
||||
label={t("Allow Lan")}
|
||||
label={t("settings.sections.clash.form.fields.allowLan")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("Network Interface")}
|
||||
title={t("settings.sections.clash.form.tooltips.networkInterface")}
|
||||
color={"inherit"}
|
||||
icon={LanRounded}
|
||||
onClick={() => {
|
||||
@@ -128,7 +130,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("DNS Overwrite")}
|
||||
label={t("settings.sections.clash.form.fields.dnsOverwrite")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={SettingsRounded}
|
||||
@@ -143,7 +145,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("IPv6")}>
|
||||
<SettingItem label={t("settings.sections.clash.form.fields.ipv6")}>
|
||||
<GuardState
|
||||
value={ipv6 ?? false}
|
||||
valueProps="checked"
|
||||
@@ -157,10 +159,10 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("Unified Delay")}
|
||||
label={t("settings.sections.clash.form.fields.unifiedDelay")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("Unified Delay Info")}
|
||||
title={t("settings.sections.clash.form.tooltips.unifiedDelay")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
@@ -178,9 +180,12 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("Log Level")}
|
||||
label={t("settings.sections.clash.form.fields.logLevel")}
|
||||
extra={
|
||||
<TooltipIcon title={t("Log Level Info")} sx={{ opacity: "0.7" }} />
|
||||
<TooltipIcon
|
||||
title={t("settings.sections.clash.form.tooltips.logLevel")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<GuardState
|
||||
@@ -194,16 +199,26 @@ const SettingClash = ({ onError }: Props) => {
|
||||
}}
|
||||
>
|
||||
<Select size="small" sx={{ width: 100, "> div": { py: "7.5px" } }}>
|
||||
<MenuItem value="debug">Debug</MenuItem>
|
||||
<MenuItem value="info">Info</MenuItem>
|
||||
<MenuItem value="warning">Warn</MenuItem>
|
||||
<MenuItem value="error">Error</MenuItem>
|
||||
<MenuItem value="silent">Silent</MenuItem>
|
||||
<MenuItem value="debug">
|
||||
{t("settings.sections.clash.form.options.logLevel.debug")}
|
||||
</MenuItem>
|
||||
<MenuItem value="info">
|
||||
{t("settings.sections.clash.form.options.logLevel.info")}
|
||||
</MenuItem>
|
||||
<MenuItem value="warning">
|
||||
{t("settings.sections.clash.form.options.logLevel.warning")}
|
||||
</MenuItem>
|
||||
<MenuItem value="error">
|
||||
{t("settings.sections.clash.form.options.logLevel.error")}
|
||||
</MenuItem>
|
||||
<MenuItem value="silent">
|
||||
{t("settings.sections.clash.form.options.logLevel.silent")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("Port Config")}>
|
||||
<SettingItem label={t("settings.sections.clash.form.fields.portConfig")}>
|
||||
<TextField
|
||||
autoComplete="new-password"
|
||||
disabled={false}
|
||||
@@ -218,10 +233,10 @@ const SettingClash = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={<>{t("External")}</>}
|
||||
label={t("settings.sections.clash.form.fields.external")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("External Cors Settings")}
|
||||
title={t("settings.sections.externalCors.tooltips.open")}
|
||||
icon={SettingsRounded}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -234,10 +249,13 @@ const SettingClash = ({ onError }: Props) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem onClick={() => webRef.current?.open()} label={t("Web UI")} />
|
||||
<SettingItem
|
||||
onClick={() => webRef.current?.open()}
|
||||
label={t("settings.sections.clash.form.fields.webUI")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("Clash Core")}
|
||||
label={t("settings.sections.clash.form.fields.clashCore")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={SettingsRounded}
|
||||
@@ -251,17 +269,20 @@ const SettingClash = ({ onError }: Props) => {
|
||||
{isWIN && (
|
||||
<SettingItem
|
||||
onClick={invoke_uwp_tool}
|
||||
label={t("Open UWP tool")}
|
||||
label={t("settings.sections.clash.form.fields.openUwpTool")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("Open UWP tool Info")}
|
||||
title={t("settings.sections.clash.form.tooltips.openUwpTool")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SettingItem onClick={onUpdateGeo} label={t("Update GeoData")} />
|
||||
<SettingItem
|
||||
onClick={onUpdateGeo}
|
||||
label={t("settings.sections.clash.form.fields.updateGeoData")}
|
||||
/>
|
||||
</SettingList>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -41,20 +41,26 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingList title={t("System Setting")}>
|
||||
<SettingList title={t("settings.sections.system.title")}>
|
||||
<SysproxyViewer ref={sysproxyRef} />
|
||||
<TunViewer ref={tunRef} />
|
||||
|
||||
<ProxyControlSwitches label={t("Tun Mode")} onError={onError} />
|
||||
<ProxyControlSwitches
|
||||
label={t("settings.sections.system.toggles.tunMode")}
|
||||
onError={onError}
|
||||
/>
|
||||
|
||||
<ProxyControlSwitches label={t("System Proxy")} onError={onError} />
|
||||
<ProxyControlSwitches
|
||||
label={t("settings.sections.system.toggles.systemProxy")}
|
||||
onError={onError}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("Auto Launch")}
|
||||
label={t("settings.sections.system.fields.autoLaunch")}
|
||||
extra={
|
||||
isAdminMode && (
|
||||
<Tooltip
|
||||
title={t("Administrator mode may not support auto launch")}
|
||||
title={t("settings.sections.system.tooltips.autoLaunchAdmin")}
|
||||
>
|
||||
<WarningRounded sx={{ color: "warning.main", mr: 1 }} />
|
||||
</Tooltip>
|
||||
@@ -72,9 +78,8 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
}}
|
||||
onGuard={async (e) => {
|
||||
if (isAdminMode) {
|
||||
showNotice(
|
||||
"info",
|
||||
t("Administrator mode may not support auto launch"),
|
||||
showNotice.info(
|
||||
"settings.sections.system.tooltips.autoLaunchAdmin",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -96,9 +101,12 @@ const SettingSystem = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("Silent Start")}
|
||||
label={t("settings.sections.system.fields.silentStart")}
|
||||
extra={
|
||||
<TooltipIcon title={t("Silent Start Info")} sx={{ opacity: "0.7" }} />
|
||||
<TooltipIcon
|
||||
title={t("settings.sections.system.tooltips.silentStart")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<GuardState
|
||||
|
||||
@@ -47,28 +47,36 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
||||
try {
|
||||
const info = await checkUpdate();
|
||||
if (!info?.available) {
|
||||
showNotice("success", t("Currently on the Latest Version"));
|
||||
showNotice.success(
|
||||
"settings.components.verge.advanced.notifications.latestVersion",
|
||||
);
|
||||
} else {
|
||||
updateRef.current?.open();
|
||||
}
|
||||
} catch (err: any) {
|
||||
showNotice("error", err.message || err.toString());
|
||||
showNotice.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const onExportDiagnosticInfo = useCallback(async () => {
|
||||
await exportDiagnosticInfo();
|
||||
showNotice("success", t("Copy Success"), 1000);
|
||||
}, [t]);
|
||||
showNotice.success(
|
||||
"shared.feedback.notifications.common.copySuccess",
|
||||
1000,
|
||||
);
|
||||
}, []);
|
||||
|
||||
const copyVersion = useCallback(() => {
|
||||
navigator.clipboard.writeText(`v${version}`).then(() => {
|
||||
showNotice("success", t("Version copied to clipboard"), 1000);
|
||||
showNotice.success(
|
||||
"settings.components.verge.advanced.notifications.versionCopied",
|
||||
1000,
|
||||
);
|
||||
});
|
||||
}, [t]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SettingList title={t("Verge Advanced Setting")}>
|
||||
<SettingList title={t("settings.components.verge.advanced.title")}>
|
||||
<ThemeViewer ref={themeRef} />
|
||||
<ConfigViewer ref={configRef} />
|
||||
<HotkeyViewer ref={hotkeyRef} />
|
||||
@@ -80,10 +88,10 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
||||
|
||||
<SettingItem
|
||||
onClick={() => backupRef.current?.open()}
|
||||
label={t("Backup Setting")}
|
||||
label={t("settings.components.verge.advanced.fields.backupSetting")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("Backup Setting Info")}
|
||||
title={t("settings.components.verge.advanced.tooltips.backupInfo")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
@@ -91,33 +99,45 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
||||
|
||||
<SettingItem
|
||||
onClick={() => configRef.current?.open()}
|
||||
label={t("Runtime Config")}
|
||||
label={t("settings.components.verge.advanced.fields.runtimeConfig")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={openAppDir}
|
||||
label={t("Open Conf Dir")}
|
||||
label={t("settings.components.verge.advanced.fields.openConfDir")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("Open Conf Dir Info")}
|
||||
title={t("settings.components.verge.advanced.tooltips.openConfDir")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingItem onClick={openCoreDir} label={t("Open Core Dir")} />
|
||||
|
||||
<SettingItem onClick={openLogsDir} label={t("Open Logs Dir")} />
|
||||
|
||||
<SettingItem onClick={onCheckUpdate} label={t("Check for Updates")} />
|
||||
|
||||
<SettingItem onClick={openDevTools} label={t("Open Dev Tools")} />
|
||||
<SettingItem
|
||||
onClick={openCoreDir}
|
||||
label={t("settings.components.verge.advanced.fields.openCoreDir")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("LightWeight Mode Settings")}
|
||||
onClick={openLogsDir}
|
||||
label={t("settings.components.verge.advanced.fields.openLogsDir")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={onCheckUpdate}
|
||||
label={t("settings.components.verge.advanced.fields.checkUpdates")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={openDevTools}
|
||||
label={t("settings.components.verge.advanced.fields.openDevTools")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("settings.components.verge.advanced.fields.liteModeSettings")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
title={t("LightWeight Mode Info")}
|
||||
title={t("settings.components.verge.advanced.tooltips.liteMode")}
|
||||
sx={{ opacity: "0.7" }}
|
||||
/>
|
||||
}
|
||||
@@ -128,11 +148,11 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
||||
onClick={() => {
|
||||
exitApp();
|
||||
}}
|
||||
label={t("Exit")}
|
||||
label={t("settings.components.verge.advanced.fields.exit")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
label={t("Export Diagnostic Info")}
|
||||
label={t("settings.components.verge.advanced.fields.exportDiagnostics")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={ContentCopyRounded}
|
||||
@@ -142,12 +162,12 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
||||
></SettingItem>
|
||||
|
||||
<SettingItem
|
||||
label={t("Verge Version")}
|
||||
label={t("settings.components.verge.advanced.fields.vergeVersion")}
|
||||
extra={
|
||||
<TooltipIcon
|
||||
icon={ContentCopyRounded}
|
||||
onClick={copyVersion}
|
||||
title={t("Copy Version")}
|
||||
title={t("settings.components.verge.advanced.actions.copyVersion")}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -76,11 +76,14 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
|
||||
const onCopyClashEnv = useCallback(async () => {
|
||||
await copyClashEnv();
|
||||
showNotice("success", t("Copy Success"), 1000);
|
||||
}, [t]);
|
||||
showNotice.success(
|
||||
"shared.feedback.notifications.common.copySuccess",
|
||||
1000,
|
||||
);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SettingList title={t("Verge Basic Setting")}>
|
||||
<SettingList title={t("settings.components.verge.basic.title")}>
|
||||
<ThemeViewer ref={themeRef} />
|
||||
<ConfigViewer ref={configRef} />
|
||||
<HotkeyViewer ref={hotkeyRef} />
|
||||
@@ -89,7 +92,7 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
<UpdateViewer ref={updateRef} />
|
||||
<BackupViewer ref={backupRef} />
|
||||
|
||||
<SettingItem label={t("Language")}>
|
||||
<SettingItem label={t("settings.components.verge.basic.fields.language")}>
|
||||
<GuardState
|
||||
value={language ?? "en"}
|
||||
onCatch={onError}
|
||||
@@ -107,7 +110,9 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("Theme Mode")}>
|
||||
<SettingItem
|
||||
label={t("settings.components.verge.basic.fields.themeMode")}
|
||||
>
|
||||
<GuardState
|
||||
value={theme_mode}
|
||||
onCatch={onError}
|
||||
@@ -119,7 +124,9 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
</SettingItem>
|
||||
|
||||
{OS !== "linux" && (
|
||||
<SettingItem label={t("Tray Click Event")}>
|
||||
<SettingItem
|
||||
label={t("settings.components.verge.basic.fields.trayClickEvent")}
|
||||
>
|
||||
<GuardState
|
||||
value={tray_event ?? "main_window"}
|
||||
onCatch={onError}
|
||||
@@ -128,18 +135,30 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
onGuard={(e) => patchVerge({ tray_event: e })}
|
||||
>
|
||||
<Select size="small" sx={{ width: 140, "> div": { py: "7.5px" } }}>
|
||||
<MenuItem value="main_window">{t("Show Main Window")}</MenuItem>
|
||||
<MenuItem value="tray_menu">{t("Show Tray Menu")}</MenuItem>
|
||||
<MenuItem value="system_proxy">{t("System Proxy")}</MenuItem>
|
||||
<MenuItem value="tun_mode">{t("Tun Mode")}</MenuItem>
|
||||
<MenuItem value="disable">{t("Disable")}</MenuItem>
|
||||
<MenuItem value="main_window">
|
||||
{t(
|
||||
"settings.components.verge.basic.trayOptions.showMainWindow",
|
||||
)}
|
||||
</MenuItem>
|
||||
<MenuItem value="tray_menu">
|
||||
{t("settings.components.verge.basic.trayOptions.showTrayMenu")}
|
||||
</MenuItem>
|
||||
<MenuItem value="system_proxy">
|
||||
{t("settings.sections.system.toggles.systemProxy")}
|
||||
</MenuItem>
|
||||
<MenuItem value="tun_mode">
|
||||
{t("settings.sections.system.toggles.tunMode")}
|
||||
</MenuItem>
|
||||
<MenuItem value="disable">
|
||||
{t("settings.components.verge.basic.trayOptions.disable")}
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
)}
|
||||
|
||||
<SettingItem
|
||||
label={t("Copy Env Type")}
|
||||
label={t("settings.components.verge.basic.fields.copyEnvType")}
|
||||
extra={
|
||||
<TooltipIcon icon={ContentCopyRounded} onClick={onCopyClashEnv} />
|
||||
}
|
||||
@@ -161,7 +180,9 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("Start Page")}>
|
||||
<SettingItem
|
||||
label={t("settings.components.verge.basic.fields.startPage")}
|
||||
>
|
||||
<GuardState
|
||||
value={start_page ?? "/"}
|
||||
onCatch={onError}
|
||||
@@ -181,7 +202,9 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
</GuardState>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem label={t("Startup Script")}>
|
||||
<SettingItem
|
||||
label={t("settings.components.verge.basic.fields.startupScript")}
|
||||
>
|
||||
<GuardState
|
||||
value={startup_script ?? ""}
|
||||
onCatch={onError}
|
||||
@@ -214,7 +237,7 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t("Browse")}
|
||||
{t("settings.components.verge.basic.actions.browse")}
|
||||
</Button>
|
||||
{startup_script && (
|
||||
<Button
|
||||
@@ -223,7 +246,7 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
patchVerge({ startup_script: "" });
|
||||
}}
|
||||
>
|
||||
{t("Clear")}
|
||||
{t("shared.actions.clear")}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
@@ -234,22 +257,22 @@ const SettingVergeBasic = ({ onError }: Props) => {
|
||||
|
||||
<SettingItem
|
||||
onClick={() => themeRef.current?.open()}
|
||||
label={t("Theme Setting")}
|
||||
label={t("settings.components.verge.basic.fields.themeSetting")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={() => layoutRef.current?.open()}
|
||||
label={t("Layout Setting")}
|
||||
label={t("settings.components.verge.basic.fields.layoutSetting")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={() => miscRef.current?.open()}
|
||||
label={t("Miscellaneous")}
|
||||
label={t("settings.components.verge.basic.fields.misc")}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
onClick={() => hotkeyRef.current?.open()}
|
||||
label={t("Hotkey Setting")}
|
||||
label={t("settings.components.verge.basic.fields.hotkeySetting")}
|
||||
/>
|
||||
</SettingList>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user