mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
fix(i18n): survive Safari's 7-day cookie cap on the chosen locale
The first-paint ladder consulted only the healthlog-locale cookie and Accept-Language — never User.locale. The cookie is written client-side via document.cookie, which Safari ITP caps at 7 days, so a chosen language silently reverted to the browser language once a week. Three seams close it: - resolveInitialLocale (extracted to src/lib/i18n/resolve-initial-locale.ts) now reads User.locale on a cookie miss via a lightweight session read (no sliding-expiry write, no cookie mutation, fail-soft) before the Accept-Language fallback. - PUT /api/auth/me/locale mirrors the accepted locale into the cookie via Set-Cookie (365 days, SameSite=Lax, not HttpOnly, Secure via shouldEmitSecureCookie) — server-set cookies are exempt from the ITP cap, and the mount-time backfill hitting the route doubles as the weekly refresh. - getSession re-emits the cookie when the user has a persisted locale but the cookie is absent (absent-only, so a mid-switch cookie is never reverted before the PUT lands). Users who never chose a language stay on Accept-Language.
This commit is contained in:
1 parent
ef82545870
commit
e9692f0b81
6 files changed
+233
-20
No files matched your search
@@ -20,11 +20,13 @@
|
||||
* holds a value the resolver would later have to defend against.
|
||||
*/
|
||||
import { NextRequest } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { apiHandler, requireAuth } from "@/lib/api-handler";
|
||||
import { apiError, apiSuccess, safeJson } from "@/lib/api-response";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import { locales, type Locale } from "@/lib/i18n/config";
|
||||
import { setLocaleCookie } from "@/lib/i18n/locale-cookie";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -58,5 +60,15 @@ export const PUT = apiHandler(async (request: NextRequest) => {
|
||||
annotate({ meta: { locale_next: locale } });
|
||||
}
|
||||
|
||||
// Mirror the accepted locale into the `healthlog-locale` cookie via
|
||||
// Set-Cookie. The client's own `document.cookie` write is capped at 7
|
||||
// days by Safari ITP; the server-set copy is not, so an explicit choice
|
||||
// survives on every device. Runs on the idempotent path too — the
|
||||
// mount-time backfill hitting this route is exactly the weekly refresh
|
||||
// that keeps the cookie alive. Harmless no-op payload for Bearer
|
||||
// callers (native clients ignore cookies).
|
||||
const cookieStore = await cookies();
|
||||
setLocaleCookie(cookieStore, locale);
|
||||
|
||||
return apiSuccess({ locale });
|
||||
});
|
||||
Reference in new issue
Block a user