From 9ee767f5763b1f201800986aebd7156de0286441 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:52:40 +0100 Subject: [PATCH] fix: show a translated 404 to a signed-in user who mistypes a URL instead of the login page --- app/(main)/[locale]/[...rest]/page.tsx | 8 +++ app/(main)/[locale]/not-found.tsx | 10 +++ app/(main)/not-found.tsx | 47 +++--------- .../error/__tests__/not-found-view.test.tsx | 54 ++++++++++++++ components/error/not-found-view.tsx | 71 +++++++++++++++++++ locales/ar/common.json | 4 ++ locales/ca/common.json | 4 ++ locales/cs/common.json | 4 ++ locales/da/common.json | 4 ++ locales/de/common.json | 4 ++ locales/en/common.json | 4 ++ locales/es/common.json | 4 ++ locales/et/common.json | 4 ++ locales/fa/common.json | 4 ++ locales/fr/common.json | 4 ++ locales/he/common.json | 4 ++ locales/hu/common.json | 4 ++ locales/it/common.json | 4 ++ locales/ja/common.json | 4 ++ locales/ko/common.json | 4 ++ locales/lv/common.json | 4 ++ locales/mn/common.json | 4 ++ locales/nb/common.json | 4 ++ locales/nl/common.json | 4 ++ locales/pl/common.json | 4 ++ locales/pt/common.json | 4 ++ locales/ro/common.json | 4 ++ locales/ru/common.json | 4 ++ locales/sk/common.json | 4 ++ locales/th/common.json | 4 ++ locales/tr/common.json | 4 ++ locales/uk/common.json | 4 ++ locales/zh-TW/common.json | 4 ++ locales/zh/common.json | 4 ++ scripts/lite/lib.mjs | 1 + 35 files changed, 269 insertions(+), 38 deletions(-) create mode 100644 app/(main)/[locale]/[...rest]/page.tsx create mode 100644 app/(main)/[locale]/not-found.tsx create mode 100644 components/error/__tests__/not-found-view.test.tsx create mode 100644 components/error/not-found-view.tsx diff --git a/app/(main)/[locale]/[...rest]/page.tsx b/app/(main)/[locale]/[...rest]/page.tsx new file mode 100644 index 000000000..75c6193d9 --- /dev/null +++ b/app/(main)/[locale]/[...rest]/page.tsx @@ -0,0 +1,8 @@ +import { notFound } from 'next/navigation'; + +// Unknown paths below a locale (/de/typo) end here, so the 404 renders +// inside the locale layout and is translated ([locale]/not-found.tsx). +// Removed from the static Lite export, like (main)/[...rest]. +export default function LocaleCatchAll() { + notFound(); +} diff --git a/app/(main)/[locale]/not-found.tsx b/app/(main)/[locale]/not-found.tsx new file mode 100644 index 000000000..e1bcd8fe4 --- /dev/null +++ b/app/(main)/[locale]/not-found.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { NotFoundView } from "@/components/error/not-found-view"; + +export default function LocaleNotFound() { + const t = useTranslations("not_found"); + const tErrors = useTranslations("errors"); + return ; +} diff --git a/app/(main)/not-found.tsx b/app/(main)/not-found.tsx index 8e9c79550..38b171963 100644 --- a/app/(main)/not-found.tsx +++ b/app/(main)/not-found.tsx @@ -1,14 +1,14 @@ "use client"; import { useEffect } from "react"; -import { useAuthStore } from "@/stores/auth-store"; import { getPathPrefix } from "@/lib/browser-navigation"; +import { NotFoundView } from "@/components/error/not-found-view"; import { IS_LITE } from "@/lib/lite"; import { liteSurfaceRootFor, stashPendingLitePath } from "@/lib/lite-link-segments"; +// Outside the locale tree there are no translations: paths below a locale +// get the translated page in [locale]/not-found.tsx. export default function NotFound() { - const isAuthenticated = useAuthStore((s) => s.isAuthenticated); - useEffect(() => { // Static Lite build on a host without rewrite rules (GitHub Pages): a deep // link such as /en/mail/thread/abc lands here as 404.html. Park the link @@ -21,42 +21,13 @@ export default function NotFound() { return; } } - if (!isAuthenticated) { - const prefix = getPathPrefix(); - // Don't redirect admin routes to the webmail login page. Admin paths - // are mounted relative to the deployment prefix, so account for it. - const adminBase = `${prefix}/admin`; - const isAdminRoute = window.location.pathname === adminBase || window.location.pathname.startsWith(`${adminBase}/`); - if (!isAdminRoute) { - window.location.href = `${prefix}/login`; - } - } - }, [isAuthenticated]); - - if (!isAuthenticated) { - let isAdmin = false; - if (typeof window !== 'undefined') { - const prefix = getPathPrefix(); - const adminBase = `${prefix}/admin`; - isAdmin = window.location.pathname === adminBase || window.location.pathname.startsWith(`${adminBase}/`); - } - if (!isAdmin) return null; - } - - const prefix = typeof window !== 'undefined' ? getPathPrefix() : ''; + }, []); return ( -
-
-

404

-

This page could not be found.

- - Go home - -
-
+ ); } diff --git a/components/error/__tests__/not-found-view.test.tsx b/components/error/__tests__/not-found-view.test.tsx new file mode 100644 index 000000000..f0869b3ac --- /dev/null +++ b/components/error/__tests__/not-found-view.test.tsx @@ -0,0 +1,54 @@ +import { act, render, screen } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +// A mistyped URL with a still valid session: the 404 waits for the session +// to be restored instead of sending the user to the login page. + +const { default: LocaleNotFound } = await import('@/app/(main)/[locale]/not-found'); +const { useAuthStore } = await import('@/stores/auth-store'); + +const realLocation = window.location; +let href = ''; + +beforeEach(() => { + href = 'http://localhost/de/gibtsnicht'; + Object.defineProperty(window, 'location', { + configurable: true, + value: { + ...realLocation, + pathname: '/de/gibtsnicht', + get href() { return href; }, + set href(v: string) { href = v; }, + }, + }); +}); +afterEach(() => { + Object.defineProperty(window, 'location', { configurable: true, value: realLocation }); +}); + +describe('404 page', () => { + it('restores the session before deciding, and shows the translated page to a signed-in user', async () => { + let finish: () => void = () => {}; + const checkAuth = vi.fn(() => new Promise((resolve) => { + finish = () => { useAuthStore.setState({ isAuthenticated: true } as never); resolve(); }; + })); + useAuthStore.setState({ isAuthenticated: false, checkAuth } as never); + + render(); + expect(checkAuth).toHaveBeenCalled(); + expect(href).not.toContain('/login'); + + await act(async () => { finish(); }); + expect(href).not.toContain('/login'); + expect(screen.getByRole('heading', { name: 'title' })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'go_home' })).toBeInTheDocument(); + }); + + it('sends a visitor without a session to the login page once that is known', async () => { + const checkAuth = vi.fn(() => Promise.resolve()); + useAuthStore.setState({ isAuthenticated: false, checkAuth } as never); + render(); + await act(async () => {}); + expect(href).toBe('/login'); + }); +}); diff --git a/components/error/not-found-view.tsx b/components/error/not-found-view.tsx new file mode 100644 index 000000000..aa42c06b4 --- /dev/null +++ b/components/error/not-found-view.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { SearchX } from "@/components/icons"; +import { useAuthStore } from "@/stores/auth-store"; +import { getPathPrefix } from "@/lib/browser-navigation"; + +/** Whether the current URL is an admin page, which has its own sign-in. */ +function isAdminPath(): boolean { + if (typeof window === "undefined") return false; + const adminBase = `${getPathPrefix()}/admin`; + const path = window.location.pathname; + return path === adminBase || path.startsWith(`${adminBase}/`); +} + +/** + * Who is looking at a missing page: "checking" until the stored session has + * been restored, then signed in or not. Deciding before that sent a signed-in + * user with a mistyped URL to the login page. + */ +export function useNotFoundAuth(): "checking" | "signed-in" | "signed-out" { + const isAuthenticated = useAuthStore((s) => s.isAuthenticated); + const [checked, setChecked] = useState(() => useAuthStore.getState().isAuthenticated); + + useEffect(() => { + if (useAuthStore.getState().isAuthenticated) { + setChecked(true); + return; + } + let cancelled = false; + void useAuthStore.getState().checkAuth().finally(() => { if (!cancelled) setChecked(true); }); + return () => { cancelled = true; }; + }, []); + + useEffect(() => { + if (!checked || isAuthenticated || isAdminPath()) return; + window.location.href = `${getPathPrefix()}/login`; + }, [checked, isAuthenticated]); + + if (isAuthenticated) return "signed-in"; + return checked ? "signed-out" : "checking"; +} + +export interface NotFoundViewProps { + title: string; + description: string; + homeLabel: string; +} + +/** The 404 page: what happened and the way back. Signed out, it shows only on admin paths. */ +export function NotFoundView({ title, description, homeLabel }: NotFoundViewProps) { + const auth = useNotFoundAuth(); + if (auth !== "signed-in" && !isAdminPath()) return null; + + const prefix = typeof window !== "undefined" ? getPathPrefix() : ""; + return ( +
+
+
+
+ ); +} diff --git a/locales/ar/common.json b/locales/ar/common.json index 85b15c79f..9680a164b 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -2625,6 +2625,10 @@ "retry": "إعادة المحاولة", "go_home": "الانتقال إلى البريد الوارد" }, + "not_found": { + "title": "الصفحة غير موجودة", + "description": "هذه الصفحة غير موجودة أو نُقلت. تحقّق من العنوان أو عُد إلى صندوق الوارد." + }, "context_menu": { "reply": "رد", "reply_all": "الرد على الجميع", diff --git a/locales/ca/common.json b/locales/ca/common.json index f0614d43e..76cc628b9 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -2625,6 +2625,10 @@ "retry": "Reintenta", "go_home": "Vés a la safata d'entrada" }, + "not_found": { + "title": "No s'ha trobat la pàgina", + "description": "Aquesta pàgina no existeix o s'ha mogut. Comproveu l'adreça o torneu a la safata d'entrada." + }, "context_menu": { "reply": "Respon", "reply_all": "Respon a tots", diff --git a/locales/cs/common.json b/locales/cs/common.json index b921779e1..b2a16fcb7 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -2625,6 +2625,10 @@ "retry": "Opakovat", "go_home": "Přejít do doručené pošty" }, + "not_found": { + "title": "Stránka nenalezena", + "description": "Tato stránka neexistuje nebo byla přesunuta. Zkontrolujte adresu nebo se vraťte do doručené pošty." + }, "context_menu": { "reply": "Odpovědět", "reply_all": "Odpovědět všem", diff --git a/locales/da/common.json b/locales/da/common.json index 9e39f9bb6..6ba2d2f33 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -2625,6 +2625,10 @@ "retry": "Prøv igen", "go_home": "Gå til indbakke" }, + "not_found": { + "title": "Siden blev ikke fundet", + "description": "Siden findes ikke eller er flyttet. Tjek adressen, eller gå tilbage til din indbakke." + }, "context_menu": { "reply": "Svar", "reply_all": "Svar alle", diff --git a/locales/de/common.json b/locales/de/common.json index 288cfb3c6..1461e4a8b 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -2625,6 +2625,10 @@ "retry": "Wiederholen", "go_home": "Zum Posteingang" }, + "not_found": { + "title": "Seite nicht gefunden", + "description": "Diese Seite gibt es nicht oder sie wurde verschoben. Prüfen Sie die Adresse oder kehren Sie zu Ihrem Posteingang zurück." + }, "context_menu": { "reply": "Antworten", "reply_all": "Allen antworten", diff --git a/locales/en/common.json b/locales/en/common.json index fedbaeb2b..78719b817 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -2625,6 +2625,10 @@ "retry": "Retry", "go_home": "Go to inbox" }, + "not_found": { + "title": "Page not found", + "description": "This page doesn't exist or has moved. Check the address, or go back to your inbox." + }, "context_menu": { "reply": "Reply", "reply_all": "Reply All", diff --git a/locales/es/common.json b/locales/es/common.json index 572b3a824..c98e82ab6 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -2625,6 +2625,10 @@ "retry": "Reintentar", "go_home": "Ir a bandeja de entrada" }, + "not_found": { + "title": "No se ha encontrado la página", + "description": "Esta página no existe o se ha movido. Comprueba la dirección o vuelve a tu bandeja de entrada." + }, "context_menu": { "reply": "Responder", "reply_all": "Responder a Todos", diff --git a/locales/et/common.json b/locales/et/common.json index dd8aa0c6b..eaca8757c 100644 --- a/locales/et/common.json +++ b/locales/et/common.json @@ -2625,6 +2625,10 @@ "retry": "Proovi uuesti", "go_home": "Mine postkasti" }, + "not_found": { + "title": "Lehte ei leitud", + "description": "Seda lehte pole olemas või see on teisaldatud. Kontrollige aadressi või minge tagasi postkasti." + }, "context_menu": { "reply": "Vasta", "reply_all": "Vasta kõigile", diff --git a/locales/fa/common.json b/locales/fa/common.json index c45aa6ef0..2ee1e9747 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -2625,6 +2625,10 @@ "retry": "تلاش مجدد", "go_home": "رفتن به صندوق ورودی" }, + "not_found": { + "title": "صفحه پیدا نشد", + "description": "این صفحه وجود ندارد یا جابه‌جا شده است. نشانی را بررسی کنید یا به صندوق ورودی برگردید." + }, "context_menu": { "reply": "پاسخ", "reply_all": "پاسخ به همه", diff --git a/locales/fr/common.json b/locales/fr/common.json index 5e35bc960..051a31a6c 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -2625,6 +2625,10 @@ "retry": "Réessayer", "go_home": "Aller à la boîte de réception" }, + "not_found": { + "title": "Page introuvable", + "description": "Cette page n'existe pas ou a été déplacée. Vérifiez l'adresse ou revenez à votre boîte de réception." + }, "context_menu": { "reply": "Répondre", "reply_all": "Répondre à tous", diff --git a/locales/he/common.json b/locales/he/common.json index 701d22da7..bfa7059f4 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -2591,6 +2591,10 @@ "retry": "נסה שוב", "go_home": "עבור לתיבת הדואר הנכנס" }, + "not_found": { + "title": "הדף לא נמצא", + "description": "הדף הזה לא קיים או שהועבר. בדקו את הכתובת או חזרו לתיבת הדואר הנכנס." + }, "context_menu": { "reply": "השב", "reply_all": "השב לכולם", diff --git a/locales/hu/common.json b/locales/hu/common.json index 44f2ea9a1..9efe67330 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -2625,6 +2625,10 @@ "retry": "Újra", "go_home": "Ugrás a beérkezőhöz" }, + "not_found": { + "title": "Az oldal nem található", + "description": "Ez az oldal nem létezik, vagy áthelyezték. Ellenőrizze a címet, vagy térjen vissza a beérkezett üzenetekhez." + }, "context_menu": { "reply": "Válasz", "reply_all": "Válasz mindenkinek", diff --git a/locales/it/common.json b/locales/it/common.json index 24874ca00..38c756512 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -2625,6 +2625,10 @@ "retry": "Riprova", "go_home": "Vai alla posta in arrivo" }, + "not_found": { + "title": "Pagina non trovata", + "description": "Questa pagina non esiste o è stata spostata. Controlla l'indirizzo o torna alla posta in arrivo." + }, "context_menu": { "reply": "Rispondi", "reply_all": "Rispondi a tutti", diff --git a/locales/ja/common.json b/locales/ja/common.json index 38922e1e6..d4978091c 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -2625,6 +2625,10 @@ "retry": "リトライ", "go_home": "受信トレイに移動" }, + "not_found": { + "title": "ページが見つかりません", + "description": "このページは存在しないか、移動されました。アドレスを確認するか、受信トレイに戻ってください。" + }, "context_menu": { "reply": "返信", "reply_all": "全員に返信", diff --git a/locales/ko/common.json b/locales/ko/common.json index 95172af3e..a74721f69 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -2625,6 +2625,10 @@ "retry": "재시도", "go_home": "받은편지함으로 이동" }, + "not_found": { + "title": "페이지를 찾을 수 없습니다", + "description": "이 페이지가 없거나 이동되었습니다. 주소를 확인하거나 받은편지함으로 돌아가세요." + }, "context_menu": { "reply": "답장", "reply_all": "전체 답장", diff --git a/locales/lv/common.json b/locales/lv/common.json index 5294d6063..5cb9cbe76 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -2625,6 +2625,10 @@ "retry": "Mēģināt vēlreiz", "go_home": "Uz iesūtni" }, + "not_found": { + "title": "Lapa nav atrasta", + "description": "Šī lapa nepastāv vai ir pārvietota. Pārbaudiet adresi vai atgriezieties iesūtnē." + }, "context_menu": { "reply": "Atbildēt", "reply_all": "Atbildēt visiem", diff --git a/locales/mn/common.json b/locales/mn/common.json index 5b13cf362..5b62a975f 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -2625,6 +2625,10 @@ "retry": "Дахин оролдох", "go_home": "Ирсэн имэйл рүү очих" }, + "not_found": { + "title": "Хуудас олдсонгүй", + "description": "Энэ хуудас байхгүй эсвэл зөөгдсөн байна. Хаягаа шалгах эсвэл ирсэн захидал руу буцна уу." + }, "context_menu": { "reply": "Хариулах", "reply_all": "Бүгдэд нь хариулах", diff --git a/locales/nb/common.json b/locales/nb/common.json index f563da68b..1d646f905 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -2625,6 +2625,10 @@ "retry": "Prøv på nytt", "go_home": "Gå til innboksen" }, + "not_found": { + "title": "Fant ikke siden", + "description": "Denne siden finnes ikke eller er flyttet. Sjekk adressen, eller gå tilbake til innboksen." + }, "context_menu": { "reply": "Svar", "reply_all": "Svar alle", diff --git a/locales/nl/common.json b/locales/nl/common.json index afa37bff1..f4e4dd7f3 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -2625,6 +2625,10 @@ "retry": "Opnieuw proberen", "go_home": "Ga naar postvak IN" }, + "not_found": { + "title": "Pagina niet gevonden", + "description": "Deze pagina bestaat niet of is verplaatst. Controleer het adres of ga terug naar je inbox." + }, "context_menu": { "reply": "Beantwoorden", "reply_all": "Allen beantwoorden", diff --git a/locales/pl/common.json b/locales/pl/common.json index 9b2c3f7f4..4a7f4ea29 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -2625,6 +2625,10 @@ "retry": "Ponów", "go_home": "Przejdź do skrzynki odbiorczej" }, + "not_found": { + "title": "Nie znaleziono strony", + "description": "Ta strona nie istnieje lub została przeniesiona. Sprawdź adres albo wróć do skrzynki odbiorczej." + }, "context_menu": { "reply": "Odpowiedz", "reply_all": "Odpowiedz wszystkim", diff --git a/locales/pt/common.json b/locales/pt/common.json index d08ff7d30..344c3d826 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -2625,6 +2625,10 @@ "retry": "Tentar novamente", "go_home": "Ir para caixa de entrada" }, + "not_found": { + "title": "Página não encontrada", + "description": "Esta página não existe ou foi movida. Verifique o endereço ou volte à caixa de entrada." + }, "context_menu": { "reply": "Responder", "reply_all": "Responder a Todos", diff --git a/locales/ro/common.json b/locales/ro/common.json index e9cb8c328..6daa65529 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -2625,6 +2625,10 @@ "retry": "Reîncearcă", "go_home": "Accesați căsuța de intrare" }, + "not_found": { + "title": "Pagina nu a fost găsită", + "description": "Această pagină nu există sau a fost mutată. Verificați adresa sau reveniți la Inbox." + }, "context_menu": { "reply": "Răspunde", "reply_all": "Răspunde tuturor", diff --git a/locales/ru/common.json b/locales/ru/common.json index c25814daa..bba4f47b5 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -2625,6 +2625,10 @@ "retry": "Повторить", "go_home": "В папку «Входящие»" }, + "not_found": { + "title": "Страница не найдена", + "description": "Этой страницы нет или она перемещена. Проверьте адрес или вернитесь во «Входящие»." + }, "context_menu": { "reply": "Ответить", "reply_all": "Ответить всем", diff --git a/locales/sk/common.json b/locales/sk/common.json index c5ee5065f..2f686d928 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -2625,6 +2625,10 @@ "retry": "Opakovať", "go_home": "Prejsť do doručenej pošty" }, + "not_found": { + "title": "Stránka sa nenašla", + "description": "Táto stránka neexistuje alebo bola presunutá. Skontrolujte adresu alebo sa vráťte do doručenej pošty." + }, "context_menu": { "reply": "Odpovedať", "reply_all": "Odpovedať všetkým", diff --git a/locales/th/common.json b/locales/th/common.json index 34d52b387..896b8b600 100644 --- a/locales/th/common.json +++ b/locales/th/common.json @@ -101,6 +101,10 @@ "retry": "ลองใหม่", "go_home": "ไปยังกล่องข้อความ" }, + "not_found": { + "title": "ไม่พบหน้านี้", + "description": "ไม่มีหน้านี้หรือหน้านี้ถูกย้ายแล้ว ตรวจสอบที่อยู่ หรือกลับไปที่กล่องจดหมายเข้า" + }, "login": { "title": "เข้าสู่ระบบจดหมายอิเล็กทรอนิกส์", "username_label": "อีเมล", diff --git a/locales/tr/common.json b/locales/tr/common.json index 67d832661..284279d5a 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -2625,6 +2625,10 @@ "retry": "Yeniden dene", "go_home": "Gelen kutusuna git" }, + "not_found": { + "title": "Sayfa bulunamadı", + "description": "Bu sayfa yok veya taşınmış. Adresi kontrol edin ya da gelen kutunuza dönün." + }, "context_menu": { "reply": "Yanıtla", "reply_all": "Tümünü Yanıtla", diff --git a/locales/uk/common.json b/locales/uk/common.json index 66d650572..a44d9612d 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -2625,6 +2625,10 @@ "retry": "Повторіть спробу", "go_home": "Перейти до папки \"Вхідні\"." }, + "not_found": { + "title": "Сторінку не знайдено", + "description": "Цієї сторінки немає або її переміщено. Перевірте адресу або поверніться до вхідних." + }, "context_menu": { "reply": "Відповісти", "reply_all": "Відповісти всім", diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 53f7a2671..caf616f90 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -2625,6 +2625,10 @@ "retry": "重試", "go_home": "前往收件匣" }, + "not_found": { + "title": "找不到頁面", + "description": "此頁面不存在或已移動。請檢查網址,或返回收件匣。" + }, "context_menu": { "reply": "回覆", "reply_all": "全部回覆", diff --git a/locales/zh/common.json b/locales/zh/common.json index 1b6b532ef..303e158b4 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -2625,6 +2625,10 @@ "retry": "重试", "go_home": "前往收件箱" }, + "not_found": { + "title": "找不到页面", + "description": "此页面不存在或已移动。请检查地址,或返回收件箱。" + }, "context_menu": { "reply": "回复", "reply_all": "全部回复", diff --git a/scripts/lite/lib.mjs b/scripts/lite/lib.mjs index 6692f6c79..7cee6c0d0 100644 --- a/scripts/lite/lib.mjs +++ b/scripts/lite/lib.mjs @@ -39,6 +39,7 @@ export const LITE_REMOVED_PATHS = [ "instrumentation.ts", "instrumentation.node.ts", "app/(main)/[...rest]", + "app/(main)/[locale]/[...rest]", "app/(main)/protocol", "e2e", "integration",