refactor(api): code-review fixes (R7 S1)

- localizeDetail: don't leak FastAPI's snake_case `loc` field names into a
  translated 422 toast; return a clean, fully localized validation message
  (drops the loc filter/pop parsing entirely)
- me(): drop the dead `r &&` guard now that req<T> types r non-nullable
This commit is contained in:
2026-07-26 22:17:04 +02:00
parent 16585d868d
commit d0aa1cf9a7
3 changed files with 7 additions and 12 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"title": "Couldn't complete that", "title": "Couldn't complete that",
"generic": "The server couldn't carry out that action. Please try again.", "generic": "The server couldn't carry out that action. Please try again.",
"validation": "Invalid value for “{{field}}”. Please check it and try again.", "validation": "Some of the values you entered weren't valid. Please check them and try again.",
"server": "Server error", "server": "Server error",
"ok": "OK", "ok": "OK",
"offline": { "offline": {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"title": "Nem sikerült", "title": "Nem sikerült",
"generic": "A szerver nem tudta végrehajtani a műveletet. Próbáld újra.", "generic": "A szerver nem tudta végrehajtani a műveletet. Próbáld újra.",
"validation": "Érvénytelen érték: „{{field}}”. Ellenőrizd, majd próbáld újra.", "validation": "Néhány megadott érték érvénytelen volt. Ellenőrizd, majd próbáld újra.",
"server": "Szerverhiba", "server": "Szerverhiba",
"ok": "OK", "ok": "OK",
"offline": { "offline": {
+5 -10
View File
@@ -228,15 +228,10 @@ class HttpError extends Error {
function localizeDetail(d: unknown): string { function localizeDetail(d: unknown): string {
const t = i18n.t.bind(i18n); const t = i18n.t.bind(i18n);
// FastAPI request-validation errors: `detail` is an ARRAY of { loc, msg, type }. The UI sends // FastAPI request-validation errors: `detail` is an ARRAY of { loc, msg, type }. The UI sends
// well-formed requests, so this only fires on genuinely malformed input — surface the offending // well-formed requests, so this only fires on genuinely malformed input. The per-field `msg` and
// field name (localized) rather than FastAPI's raw English `msg`, which reads wrong in a HU UI. // `loc` field names are English, snake_case internal tokens — surface a clean, fully localized
if (Array.isArray(d)) { // "check your input" message rather than leaking them into a translated sentence.
const loc = (d[0] as { loc?: unknown } | undefined)?.loc; if (Array.isArray(d)) return t("errors.validation");
const field = Array.isArray(loc)
? loc.filter((s): s is string => typeof s === "string" && s !== "body").pop()
: undefined;
return field ? t("errors.validation", { field }) : t("errors.generic");
}
const o = d as { code?: string; reason?: string; limit?: number } | null | undefined; const o = d as { code?: string; reason?: string; limit?: number } | null | undefined;
if (o?.code === "quota") { if (o?.code === "quota") {
if (o.reason === "max_bytes") { if (o.reason === "max_bytes") {
@@ -1117,7 +1112,7 @@ export const api = {
me: async (): Promise<Me | null> => { me: async (): Promise<Me | null> => {
// Always 200: `{ authenticated: false }` when logged out, else the full Me + `authenticated`. // Always 200: `{ authenticated: false }` when logged out, else the full Me + `authenticated`.
const r = await req<{ authenticated: boolean } & Partial<Me>>("/api/me"); const r = await req<{ authenticated: boolean } & Partial<Me>>("/api/me");
return r && r.authenticated ? (r as Me) : null; return r.authenticated ? (r as Me) : null;
}, },
accounts: (): Promise<Account[]> => req("/api/me/accounts"), accounts: (): Promise<Account[]> => req("/api/me/accounts"),
deleteAccount: (): Promise<{ deleted: boolean }> => req("/api/me/account", { method: "DELETE" }), deleteAccount: (): Promise<{ deleted: boolean }> => req("/api/me/account", { method: "DELETE" }),