mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-19 05:32:19 +03:00
fix(i18n): preserve remaining Vietnamese localization (#7935)
* fix(i18n): preserve remaining Vietnamese localization * chore(quality): rebaseline file-size cap for 9 dashboard components (i18n wiring) Restoring the Vietnamese localization on 9 dashboard components (useTranslations wiring + t()/tc() call-site swaps for previously hardcoded strings) grows each file by a small, irreducible amount. Bumps the frozen file-size-baseline.json caps to match, with a justification entry per the project's own ratchet policy. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(i18n): wire weekday localization + add missing qwen CLI description Two gaps left by this PR's own new contract tests, caught while reconciling the branch against the release tip: - CostOverviewTab.tsx added formatWeekdayLabel() but never called it; the Weekly Usage Pattern chart still showed raw English day abbreviations regardless of locale. Now maps weeklyPattern rows through it before handing them to WeeklyPatternCard. - cliTools.toolDescriptions was missing an entry for "qwen" (a baseUrlSupport:"full" tool) in both en.json and vi.json, failing the PR's own cli-catalog-display-contract.test.ts. Covered by the PR's existing tests/unit/dashboard-localization-contract.test.ts and tests/unit/cli-catalog-display-contract.test.ts (both now pass). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * i18n(vi): backfill the 4 proxySubscription keys #7299 added to en.json #7299 (proxy subscriptions) merged while this branch was rebasing, adding settings.proxySubscriptionsTab and settings.proxySubscription.error.{LOCAL_CORE_ENDPOINT_INVALID, NEEDS_CORE_NOT_CONFIGURED,NO_USABLE_NODES} to en.json. This PR's own i18n-vi-completeness contract asserts full en↔vi key parity, so the merge of the current release tip surfaced them as missing. Adds the Vietnamese translations, keeping the SS/VMess/Trojan/VLESS/SOCKS5 technical terms verbatim. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: nguyenha935 <208228297+nguyenha935@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: nguyenha935 <nguyenha935@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, useRef, type ReactNode } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Card from "./Card";
|
||||
import Button from "./Button";
|
||||
import DistributeProxiesButton from "./DistributeProxiesButton";
|
||||
@@ -92,13 +93,16 @@ export default function NoAuthAccountCard({
|
||||
generateAccountId,
|
||||
generateApiKey,
|
||||
dataKey = "fingerprints",
|
||||
description = "Ready to use — no signup needed. Add accounts for rate-limit rotation.",
|
||||
addLabel = "Add Account",
|
||||
description,
|
||||
addLabel,
|
||||
enabled = true,
|
||||
savingEnabled = false,
|
||||
onEnabledChange,
|
||||
providerProxyControl,
|
||||
}: NoAuthAccountCardProps) {
|
||||
const t = useTranslations("noAuthProvider");
|
||||
const resolvedDescription = description || t("accountDescription");
|
||||
const resolvedAddLabel = addLabel || t("addAccount");
|
||||
const [connections, setConnections] = useState<Connection[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [adding, setAdding] = useState(false);
|
||||
@@ -144,8 +148,12 @@ export default function NoAuthAccountCard({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void fetchConnections();
|
||||
void fetchSavedProxies();
|
||||
const loadTimer = window.setTimeout(() => {
|
||||
void fetchConnections();
|
||||
void fetchSavedProxies();
|
||||
}, 0);
|
||||
|
||||
return () => window.clearTimeout(loadTimer);
|
||||
}, [fetchConnections, fetchSavedProxies]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -176,14 +184,14 @@ export default function NoAuthAccountCard({
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
provider: providerId,
|
||||
name: `${providerName} Account 1`,
|
||||
name: t("accountName", { provider: providerName, number: 1 }),
|
||||
...(apiKey ? { apiKey } : {}),
|
||||
providerSpecificData: { [dataKey]: [accountId] },
|
||||
}),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const errData = await res.json().catch(() => ({}));
|
||||
throw new Error(errData?.error || `Failed to create connection (${res.status})`);
|
||||
throw new Error(errData?.error || t("createConnectionFailed"));
|
||||
}
|
||||
} else {
|
||||
const updated = [...allAccountIds, accountId];
|
||||
@@ -194,7 +202,7 @@ export default function NoAuthAccountCard({
|
||||
providerSpecificData: { [dataKey]: updated },
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to update connection");
|
||||
if (!res.ok) throw new Error(t("updateConnectionFailed"));
|
||||
}
|
||||
await fetchConnections();
|
||||
} catch (err) {
|
||||
@@ -302,11 +310,11 @@ export default function NoAuthAccountCard({
|
||||
if (!conn || allAccountIds.length === 0) return;
|
||||
|
||||
const proxiesRes = await fetch("/api/settings/proxies");
|
||||
if (!proxiesRes.ok) throw new Error("Failed to fetch proxies");
|
||||
if (!proxiesRes.ok) throw new Error(t("fetchProxiesFailed"));
|
||||
const proxiesData = await proxiesRes.json();
|
||||
const savedProxies = (proxiesData?.items || []).filter((p: any) => p.status === "active");
|
||||
if (savedProxies.length === 0) {
|
||||
throw new Error("No saved proxies found. Add proxies in Settings → Proxy first.");
|
||||
throw new Error(t("noSavedProxiesError"));
|
||||
}
|
||||
|
||||
// #5217 (Gap 1): distribute stores by-id references too, so editing a pool
|
||||
@@ -323,7 +331,7 @@ export default function NoAuthAccountCard({
|
||||
providerSpecificData: { accountProxies: updatedProxies },
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error("Failed to update connection");
|
||||
if (!res.ok) throw new Error(t("updateConnectionFailed"));
|
||||
|
||||
await fetchConnections();
|
||||
};
|
||||
@@ -336,8 +344,8 @@ export default function NoAuthAccountCard({
|
||||
<span className="material-symbols-outlined text-[20px]">lock_open</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium">No authentication required</p>
|
||||
<p className="text-xs text-text-muted">{description}</p>
|
||||
<p className="text-sm font-medium">{t("title")}</p>
|
||||
<p className="text-xs text-text-muted">{resolvedDescription}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap items-center justify-end gap-2 sm:w-auto sm:flex-nowrap">
|
||||
@@ -354,7 +362,7 @@ export default function NoAuthAccountCard({
|
||||
<div className="border-t border-border pt-3 mt-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-medium">
|
||||
Accounts ({loading ? "..." : allAccountIds.length})
|
||||
{t("accounts", { count: loading ? "..." : allAccountIds.length })}
|
||||
</span>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{!loading && allAccountIds.length > 0 && (
|
||||
@@ -365,14 +373,14 @@ export default function NoAuthAccountCard({
|
||||
/>
|
||||
)}
|
||||
<Button size="sm" icon="add" onClick={handleAddAccount} disabled={adding || !enabled}>
|
||||
{adding ? "Adding..." : addLabel}
|
||||
{adding ? t("adding") : resolvedAddLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!loading && allAccountIds.length === 0 && (
|
||||
<p className="text-xs text-text-muted py-2">
|
||||
Using auto-generated account. Click "{addLabel}" for rate-limit rotation.
|
||||
{t("autoGeneratedAccount", { addLabel: resolvedAddLabel })}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -405,9 +413,11 @@ export default function NoAuthAccountCard({
|
||||
title={
|
||||
proxy
|
||||
? `Proxy: ${proxy.type}://${proxy.host}:${proxy.port}`
|
||||
: "Configure proxy"
|
||||
: t("configureProxy")
|
||||
}
|
||||
aria-label={
|
||||
proxy ? t("proxyConfigured", { host: proxy.host }) : t("configureProxy")
|
||||
}
|
||||
aria-label={proxy ? `Proxy configured: ${proxy.host}` : "Configure proxy"}
|
||||
>
|
||||
<span
|
||||
className="material-symbols-outlined text-[16px]"
|
||||
@@ -420,7 +430,7 @@ export default function NoAuthAccountCard({
|
||||
type="button"
|
||||
onClick={() => handleRemoveAccount(id)}
|
||||
className="shrink-0 rounded p-1 text-text-muted opacity-0 transition-colors hover:bg-red-500/10 hover:text-red-500 group-hover:opacity-100"
|
||||
aria-label="Remove account"
|
||||
aria-label={t("removeAccount")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]">delete</span>
|
||||
</button>
|
||||
@@ -437,7 +447,9 @@ export default function NoAuthAccountCard({
|
||||
className="w-80 max-w-full rounded-lg border border-black/10 bg-surface p-4 shadow-lg dark:border-white/10"
|
||||
>
|
||||
<p className="mb-3 text-sm font-medium">
|
||||
Proxy for Account {allAccountIds.indexOf(proxyAccountId) + 1}
|
||||
{t("proxyForAccount", {
|
||||
number: allAccountIds.indexOf(proxyAccountId) + 1,
|
||||
})}
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
{/* #5217 (Gap 1): pick a pre-saved Proxy Pool entry by reference,
|
||||
@@ -452,7 +464,7 @@ export default function NoAuthAccountCard({
|
||||
: "text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
Saved
|
||||
{t("saved")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -463,7 +475,7 @@ export default function NoAuthAccountCard({
|
||||
: "text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
Custom
|
||||
{t("custom")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -474,9 +486,7 @@ export default function NoAuthAccountCard({
|
||||
className="w-full rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10"
|
||||
>
|
||||
<option value="">
|
||||
{savedProxies.length === 0
|
||||
? "No saved proxies — add one in Settings → Proxy"
|
||||
: "Direct (no proxy)"}
|
||||
{savedProxies.length === 0 ? t("noSavedProxies") : t("directConnection")}
|
||||
</option>
|
||||
{savedProxies.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
@@ -502,14 +512,14 @@ export default function NoAuthAccountCard({
|
||||
type="text"
|
||||
value={proxyHost}
|
||||
onChange={(e) => setProxyHost(e.target.value)}
|
||||
placeholder="Host"
|
||||
placeholder={t("host")}
|
||||
className="flex-1 rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={proxyPort}
|
||||
onChange={(e) => setProxyPort(e.target.value)}
|
||||
placeholder="Port"
|
||||
placeholder={t("port")}
|
||||
className="w-16 rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10"
|
||||
/>
|
||||
</div>
|
||||
@@ -517,14 +527,14 @@ export default function NoAuthAccountCard({
|
||||
type="text"
|
||||
value={proxyUsername}
|
||||
onChange={(e) => setProxyUsername(e.target.value)}
|
||||
placeholder="Username (optional)"
|
||||
placeholder={t("usernameOptional")}
|
||||
className="w-full rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
value={proxyPassword}
|
||||
onChange={(e) => setProxyPassword(e.target.value)}
|
||||
placeholder="Password (optional)"
|
||||
placeholder={t("passwordOptional")}
|
||||
className="w-full rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10"
|
||||
/>
|
||||
</>
|
||||
@@ -534,14 +544,14 @@ export default function NoAuthAccountCard({
|
||||
onClick={() => setProxyAccountId(null)}
|
||||
className="rounded-md px-3 py-1.5 text-xs text-text-muted transition-colors hover:bg-black/5 hover:text-text-main dark:hover:bg-white/5"
|
||||
>
|
||||
Cancel
|
||||
{t("cancel")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSaveProxy}
|
||||
disabled={savingProxy}
|
||||
className="rounded-md bg-primary/10 px-3 py-1.5 text-xs text-primary transition-colors hover:bg-primary/20 disabled:opacity-50"
|
||||
>
|
||||
{savingProxy ? "Saving..." : "Save"}
|
||||
{savingProxy ? t("saving") : t("save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user