feat(i18n): add mobile localization release gates

This commit is contained in:
poyrazavsever
2026-07-21 13:38:06 +03:00
parent 1653e4f61d
commit c40c025d05
12 changed files with 784 additions and 91 deletions
+66
View File
@@ -0,0 +1,66 @@
import { cookies } from "next/headers";
import { z } from "zod";
import { apiV1Error, apiV1Success } from "@/server/api/v1/responses";
import { domainActorFromSession } from "@/server/auth/domain-actor";
import { getSessionContextFromHeaders } from "@/server/auth/session";
import { getServerConfig } from "@/server/config";
import { DomainError } from "@/server/domain/errors";
import {
getUserPreferences,
updateColorModePreference,
updateLanguagePreference,
} from "@/server/settings/preferences";
import {
COLOR_MODE_COOKIE,
COLOR_MODE_COOKIE_MAX_AGE,
isColorMode,
} from "@/lib/color-mode";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
const inputSchema = z.object({
colorMode: z.string().optional(),
language: z.string().trim().min(2).max(12).optional(),
});
export async function PATCH(request: Request) {
try {
const context = await getSessionContextFromHeaders(new Headers(request.headers));
if (!context) {
throw new DomainError("UNAUTHENTICATED", "Authentication required.", {
messageKey: "api.errors.unauthenticated",
});
}
const parsed = inputSchema.safeParse(await request.json());
if (!parsed.success) {
throw new DomainError("VALIDATION_ERROR", "Invalid preference payload.", {
messageKey: "validation.required",
});
}
const actor = domainActorFromSession(context);
let preferences = getUserPreferences(actor);
if (parsed.data.language) {
preferences = updateLanguagePreference(actor, { language: parsed.data.language });
}
if (parsed.data.colorMode) {
if (!isColorMode(parsed.data.colorMode)) {
throw new DomainError("VALIDATION_ERROR", "Invalid color mode.");
}
preferences = updateColorModePreference(actor, { colorMode: parsed.data.colorMode });
const config = getServerConfig();
(await cookies()).set(COLOR_MODE_COOKIE, preferences.colorMode, {
httpOnly: false,
maxAge: COLOR_MODE_COOKIE_MAX_AGE,
path: "/",
sameSite: "lax",
secure: config.secureCookies,
});
}
return apiV1Success({ preferences });
} catch (error) {
return apiV1Error(error);
}
}
+3 -3
View File
@@ -52,11 +52,11 @@ export async function GET(request: Request) {
},
preferences,
localization: {
language: preferences.language,
portalLocale,
userPreferenceLocale: preferences.language,
clientDefaultLocale: portalLocale,
resolvedLocale: resolvedLocale.locale,
requestedLocale: resolvedLocale.requestedLocale,
defaultLocale: resolvedLocale.defaultLocale,
instanceDefaultLocale: resolvedLocale.defaultLocale,
source: resolvedLocale.source,
fallbackChain: resolvedLocale.fallbackChain,
},