Use typed backend errors for translations

This commit is contained in:
n200534
2026-08-20 01:11:30 +05:30
parent eff8bb16a0
commit 3efec5ce58
38 changed files with 164 additions and 79 deletions

View File

@@ -1,6 +1,7 @@
import { i18n } from "@lingui/core"
import type { AxiosError } from "axios"
import { describe, expect, it } from "vitest"
import { loginErrorToStrings } from "./client"
import { afterEach, describe, expect, it, vi } from "vitest"
import { errorToStrings } from "./client"
const axiosError = (status: number, data: unknown) =>
({
@@ -8,16 +9,28 @@ const axiosError = (status: number, data: unknown) =>
response: { status, data },
}) as AxiosError
describe("loginErrorToStrings", () => {
it("uses the translated message for authentication errors", () => {
const error = axiosError(401, { message: "wrong username or password" })
describe("errorToStrings", () => {
afterEach(() => vi.restoreAllMocks())
expect(loginErrorToStrings(error, "Translated authentication error")).toEqual(["Translated authentication error"])
it("translates known application error types", () => {
vi.spyOn(i18n, "_").mockReturnValue("Translated authentication error")
const error = axiosError(401, {
type: "WRONG_USERNAME_OR_PASSWORD",
message: "wrong username or password",
})
expect(errorToStrings(error)).toEqual(["Translated authentication error"])
})
it("preserves backend messages for unexpected errors", () => {
const error = axiosError(500, { message: "unexpected error" })
expect(loginErrorToStrings(error, "Translated authentication error")).toEqual(["unexpected error"])
expect(errorToStrings(error)).toEqual(["unexpected error"])
})
it("preserves backend messages for unknown application error types", () => {
const error = axiosError(400, { type: "UNKNOWN_ERROR", message: "unknown error" })
expect(errorToStrings(error)).toEqual(["unknown error"])
})
})

View File

@@ -1,3 +1,5 @@
import { i18n, type MessageDescriptor } from "@lingui/core"
import { msg } from "@lingui/core/macro"
import axios, { type AxiosError } from "axios"
import type {
AddCategoryRequest,
@@ -6,6 +8,8 @@ import type {
Category,
CategoryModificationRequest,
CollapseRequest,
CommaFeedApplicationError,
CommaFeedExceptionType,
Entries,
FeedInfo,
FeedInfoRequest,
@@ -31,6 +35,10 @@ import type {
UserModel,
} from "./types"
const applicationErrorMessages = {
WRONG_USERNAME_OR_PASSWORD: msg`Wrong username or password`,
} satisfies Record<CommaFeedExceptionType, MessageDescriptor>
const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true })
axiosInstance.interceptors.response.use(
response => response,
@@ -128,21 +136,23 @@ export const errorToStrings = (err: unknown) => {
let strings: string[] = []
if (axios.isAxiosError(err) && err.response) {
if (typeof err.response.data === "string") strings.push(err.response.data)
if (isMessageError(err)) strings.push(err.response.data.message)
if (isMessageArrayError(err)) strings = [...strings, ...err.response.data.errors]
if (isCommaFeedApplicationError(err)) {
strings.push(i18n._(applicationErrorMessages[err.response.data.type]))
} else {
if (typeof err.response.data === "string") strings.push(err.response.data)
if (isMessageError(err)) strings.push(err.response.data.message)
if (isMessageArrayError(err)) strings = [...strings, ...err.response.data.errors]
}
}
return strings
}
/**
* Transform a login error into messages that can be displayed to the user.
* Authentication failures use a client-provided message so it can be translated.
*/
export const loginErrorToStrings = (err: unknown, authenticationErrorMessage: string) => {
if (isAuthenticationError(err)) return [authenticationErrorMessage]
return errorToStrings(err)
function isCommaFeedApplicationError(err: AxiosError): err is AxiosError<CommaFeedApplicationError> {
const data = err.response?.data
if (!data || typeof data !== "object" || !("type" in data)) return false
const type = data.type
return typeof type === "string" && Object.hasOwn(applicationErrorMessages, type)
}
function isMessageError(err: AxiosError): err is AxiosError<{ message: string }> {

View File

@@ -338,3 +338,10 @@ export interface AuthenticationError {
message: string
allowRegistrations: boolean
}
export type CommaFeedExceptionType = "WRONG_USERNAME_OR_PASSWORD"
export interface CommaFeedApplicationError {
type: CommaFeedExceptionType
message: string
}

View File

@@ -1150,8 +1150,7 @@ msgstr "موقع الكتروني"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "مرحباً! يبدو أن هذه هي المرة الأولى التي تقوم فيها بتشغيل CommaFeed. يرجى إنشاء حساب مسؤول للبدء."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Lloc web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Benvingut! Sembla que aquesta és la primera vegada que executeu CommaFeed. Creeu un compte d'administrador per començar."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Webová stránka"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Vítejte! Zdá se, že je to poprvé, co spouštíte CommaFeed. Chcete-li začít, vytvořte si účet správce."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Gwefan"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Croeso! Ymddengys mai dyma'r tro cyntaf i chi redeg CommaFeed. Creuwch gyfrif gweinyddwr i ddechrau."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Hjemmeside"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Velkommen! Det ser ud til at være første gang, du kører CommaFeed. Opret venligst en administratorkonto for at komme i gang."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Webseite"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Willkommen! Dies scheint das erste Mal zu sein, dass Sie CommaFeed ausführen. Bitte erstellen Sie ein Administrator-Konto, um zu beginnen."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Website"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr "Wrong username or password"

View File

@@ -1151,8 +1151,7 @@ msgstr "Sitio web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "¡Bienvenido! Parece que esta es la primera vez que ejecutas CommaFeed. Por favor, crea una cuenta de administrador para empezar."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "وب سایت"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "خوش آمدید! به نظر می‌رسد این اولین بار است که CommaFeed را اجرا می‌کنید. لطفاً برای شروع یک حساب مدیر ایجاد کنید."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Verkkosivusto"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Tervetuloa! Näyttää siltä, että suoritat CommaFeediä ensimmäistä kertaa. Luo ylläpitäjän tili aloittaaksesi."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Site web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Bienvenue ! Il semble que ce soit le premier démarrage de Commafeed. Avant tout, vous devez créer un compte administrateur."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1151,8 +1151,7 @@ msgstr "Páxina web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Benvida! Semella que é a primeira vez que executas CommaFeed. Para comezar, crea unha conta de administración."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Webhely"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Üdvözöljük! Úgy tűnik, ez az első alkalom, hogy a CommaFeedet futtatja. Kérjük, hozzon létre egy rendszergazdai fiókot a kezdéshez."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Situs Web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Selamat datang! Ini sepertinya pertama kalinya Anda menjalankan CommaFeed. Silakan buat akun administrator untuk memulai."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Sito web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Benvenuto! Sembra che sia la prima volta che esegui CommaFeed. Crea un account amministratore per iniziare."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "ウェブサイト"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "ようこそCommaFeedを初めて実行するようです。開始するには管理者アカウントを作成してください。"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "웹사이트"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "환영합니다! CommaFeed를 처음 실행하는 것 같습니다. 시작하려면 관리자 계정을 만드십시오."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Laman web"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Selamat datang! Ini nampaknya kali pertama anda menjalankan CommaFeed. Sila cipta akaun pentadbir untuk bermula."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Nettsted"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Velkommen! Det ser ut til å være første gang du kjører CommaFeed. Vennligst opprett en administratorkonto for å komme i gang."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Website"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Welkom! Dit lijkt de eerste keer te zijn dat je CommaFeed draait. Maak een beheerdersaccount aan om aan de slag te gaan."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Nettstad"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Velkomen! Det ser ut til å vera fyrste gongen du køyrer CommaFeed. Ver venleg og opprett ein administratorkonto for å koma i gang."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Strona internetowa"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Witaj! Wygląda na to, że uruchamiasz CommaFeed po raz pierwszy. Aby rozpocząć, utwórz konto administratora."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Site"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Bem-vindo! Esta parece ser a primeira vez que você está executando o CommaFeed. Crie uma conta de administrador para começar."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Веб-сайт"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Добро пожаловать! Похоже, вы запускаете CommaFeed в первый раз. Пожалуйста, создайте учетную запись администратора, чтобы начать работу."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Webová stránka"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Vitajte! Zdá sa, že CommaFeed spúšťate prvýkrát. Ak chcete začať, vytvorte si účet správcu."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Webbplats"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Välkommen! Det verkar vara första gången du kör CommaFeed. Skapa ett administratörskonto för att komma igång."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "Web sitesi"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "Hoş geldiniz! Görünüşe göre CommaFeed'i ilk kez çalıştırıyorsunuz. Başlamak için lütfen bir yönetici hesabı oluşturun."
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr ""

View File

@@ -1150,8 +1150,7 @@ msgstr "网站"
msgid "Welcome! This appears to be the first time you're running CommaFeed. Please create an administrator account to get started."
msgstr "欢迎当前页仅当您第一次使用CommaFeed时出现请创建一个管理员帐号以开始使用。"
#: src/pages/auth/LoginPage.tsx
#: src/pages/auth/RegistrationPage.tsx
#: src/app/client.ts
msgid "Wrong username or password"
msgstr "用户名或密码错误"

View File

@@ -5,7 +5,7 @@ import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, St
import { useForm } from "@mantine/form"
import { useAsyncCallback } from "react-async-hook"
import { Link } from "react-router-dom"
import { client, loginErrorToStrings } from "@/app/client"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { LoginRequest } from "@/app/types"
@@ -39,7 +39,7 @@ export function LoginPage() {
</Title>
{login.error && (
<Box mb="md">
<Alert messages={loginErrorToStrings(login.error, _(msg`Wrong username or password`))} />
<Alert messages={errorToStrings(login.error)} />
</Box>
)}
<form onSubmit={form.onSubmit(login.execute)}>

View File

@@ -5,7 +5,7 @@ import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, St
import { useForm } from "@mantine/form"
import { useAsyncCallback } from "react-async-hook"
import { Link } from "react-router-dom"
import { client, errorToStrings, loginErrorToStrings } from "@/app/client"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { RegistrationRequest } from "@/app/types"
@@ -65,7 +65,7 @@ export function RegistrationPage() {
{login.error && (
<Box mb="md">
<Alert messages={loginErrorToStrings(login.error, _(msg`Wrong username or password`))} />
<Alert messages={errorToStrings(login.error)} />
</Box>
)}