feat(i18n): implement new locale resolution logic and service layer
This commit is contained in:
+2
-2
@@ -33,8 +33,8 @@ export const contentTranslationRegistry = {
|
||||
{ name: "description", label: "Açıklama", kind: "textarea", maxLength: 20_000, placeholder: "Kapsam, not veya teslim kriterleri..." },
|
||||
],
|
||||
branding: [
|
||||
{ name: "portalWelcome", label: "Portal karşılama metni", kind: "textarea", maxLength: 10_000 },
|
||||
{ name: "portalFooter", label: "Portal footer metni", kind: "textarea", maxLength: 5_000 },
|
||||
{ name: "portalWelcome", label: "Portal karşılama metni", kind: "textarea", maxLength: 2_000 },
|
||||
{ name: "portalFooter", label: "Portal footer metni", kind: "textarea", maxLength: 1_000 },
|
||||
],
|
||||
calendar_event: [
|
||||
{ name: "title", label: "Başlık", required: true, maxLength: 300 },
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
export type LocaleCandidate = {
|
||||
code: string;
|
||||
status: "draft" | "active" | "archived" | "test";
|
||||
textDirection: "ltr" | "rtl";
|
||||
};
|
||||
|
||||
export type LocaleResolutionSource =
|
||||
| "client"
|
||||
| "fallback"
|
||||
| "instance"
|
||||
| "invitation"
|
||||
| "user";
|
||||
|
||||
export type LocaleResolution = {
|
||||
locale: string;
|
||||
requestedLocale: string | null;
|
||||
defaultLocale: string;
|
||||
direction: "ltr" | "rtl";
|
||||
source: LocaleResolutionSource;
|
||||
};
|
||||
|
||||
type ResolveLocalePolicyInput = {
|
||||
activeLocales: readonly LocaleCandidate[];
|
||||
defaultLocale: string | null | undefined;
|
||||
candidates: ReadonlyArray<{
|
||||
locale: string | null | undefined;
|
||||
source: Exclude<LocaleResolutionSource, "fallback" | "instance">;
|
||||
}>;
|
||||
fallbackLocale?: string;
|
||||
};
|
||||
|
||||
export function resolveLocalePolicy(input: ResolveLocalePolicyInput): LocaleResolution {
|
||||
const fallbackLocale = input.fallbackLocale ?? "tr";
|
||||
const activeLocales = new Map(
|
||||
input.activeLocales
|
||||
.filter((locale) => locale.status === "active")
|
||||
.map((locale) => [locale.code, locale] as const),
|
||||
);
|
||||
const safeDefault = activeLocales.has(input.defaultLocale ?? "")
|
||||
? input.defaultLocale!
|
||||
: activeLocales.has(fallbackLocale)
|
||||
? fallbackLocale
|
||||
: activeLocales.keys().next().value ?? fallbackLocale;
|
||||
|
||||
for (const candidate of input.candidates) {
|
||||
const requestedLocale = normalizeLocaleCode(candidate.locale);
|
||||
if (!requestedLocale) continue;
|
||||
const locale = activeLocales.get(requestedLocale);
|
||||
if (!locale) continue;
|
||||
|
||||
return {
|
||||
locale: locale.code,
|
||||
requestedLocale,
|
||||
defaultLocale: safeDefault,
|
||||
direction: locale.textDirection,
|
||||
source: candidate.source,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultRecord = activeLocales.get(safeDefault);
|
||||
return {
|
||||
locale: safeDefault,
|
||||
requestedLocale: normalizeLocaleCode(input.defaultLocale),
|
||||
defaultLocale: safeDefault,
|
||||
direction: defaultRecord?.textDirection ?? directionForLocale(safeDefault),
|
||||
source: activeLocales.has(input.defaultLocale ?? "") ? "instance" : "fallback",
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeLocaleCode(value: string | null | undefined): string | null {
|
||||
if (!value) return null;
|
||||
const normalized = value.trim();
|
||||
return /^[a-z]{2}(?:-[A-Z]{2}[0-9]?)?$/.test(normalized) ? normalized : null;
|
||||
}
|
||||
|
||||
export function directionForLocale(locale: string): "ltr" | "rtl" {
|
||||
return /^(ar|fa|he|ur)(-|$)/.test(locale) ? "rtl" : "ltr";
|
||||
}
|
||||
Reference in New Issue
Block a user