fix(i18n): harden owner localized forms

This commit is contained in:
poyrazavsever
2026-07-21 13:37:53 +03:00
parent 1227588320
commit 1653e4f61d
9 changed files with 25 additions and 34 deletions
+1 -9
View File
@@ -56,14 +56,6 @@ export type CalendarEventItem = {
translations?: Record<string, Record<string, string>>;
};
const typeLabels = {
meeting: "Toplantı",
focus: "Odak",
deadline: "Deadline",
personal: "Kişisel",
finance: "Finans",
};
const typeClasses = {
meeting: "border-blue-200 bg-blue-50 text-blue-700",
focus: "border-emerald-200 bg-emerald-50 text-emerald-700",
@@ -251,7 +243,7 @@ function EventList({
</div>
) : null}
</div>
<Badge className={typeClasses[event.type]}>{t(`calendar.types.${event.type}` as any)}</Badge>
<Badge className={typeClasses[event.type]}>{t(`calendar.types.${event.type}`)}</Badge>
</div>
{!compact ? (
<div className="mt-3 flex gap-2">
+3 -3
View File
@@ -217,7 +217,7 @@ export function ClientsClient({
return (
<DroppableColumn
key={stage.id}
title={t(`clients.pipeline.${stage.id}` as any)}
title={t(`clients.pipeline.${stage.id}`)}
count={stageClients.length}
color={stage.color.split(' ')[1]}
onDrop={() => handleDrop(stage.id)}
@@ -383,7 +383,7 @@ function ClientRow({ client, activeLocales }: { client: ClientListItem, activeLo
<div>
<Badge className={stage.color}>
{t(`clients.pipeline.${stage.id}` as any)}
{t(`clients.pipeline.${stage.id}`)}
</Badge>
</div>
@@ -528,7 +528,7 @@ function ClientFormFields({ client, activeLocales }: { client?: ClientListItem,
</SelectTrigger>
<SelectContent>
{pipelineStages.map(stage => (
<SelectItem key={stage.id} value={stage.id}>{t(`clients.pipeline.${stage.id}` as any)}</SelectItem>
<SelectItem key={stage.id} value={stage.id}>{t(`clients.pipeline.${stage.id}`)}</SelectItem>
))}
</SelectContent>
</Select>
+9 -3
View File
@@ -73,12 +73,14 @@ export type ProjectListItem = {
translations?: LocalizedFieldValues;
};
const typeLabels = (t: any) => ({
type Translate = ReturnType<typeof useTranslations>;
const typeLabels = (t: Translate) => ({
client_project: t("projects.types.client"),
side_project: t("projects.types.side"),
});
const statusLabels = (t: any) => ({
const statusLabels = (t: Translate) => ({
planning: t("projects.status.planning"),
active: t("projects.status.active"),
paused: t("projects.status.paused"),
@@ -605,7 +607,11 @@ function ProjectFormFields({
idPrefix={`project-${project?.id || "new"}`}
defaultLocale={localization.defaultLocale}
locales={localization.locales}
fields={contentTranslationRegistry.project.map((f: any) => ({ ...f, label: (t as any)(`projects.fields.${f.name}`) || f.label, placeholder: f.placeholder ? (t as any)(`projects.placeholders.${f.name}`) || f.placeholder : undefined }))}
fields={contentTranslationRegistry.project.map((f) => ({
...f,
label: t(`projects.fields.${f.name}`) || f.label,
placeholder: f.placeholder ? t(`projects.placeholders.${f.name}`) || f.placeholder : undefined,
}))}
values={project?.translations}
fallbackValues={{
name: project?.name,
@@ -120,7 +120,7 @@ export function TranslationEditor({
</div>
<div className="flex items-center gap-3">
<Select value={filter} onValueChange={(v: any) => setFilter(v)}>
<Select value={filter} onValueChange={(value) => setFilter(value as typeof filter)}>
<SelectTrigger className="w-[140px]">
<SelectValue />
</SelectTrigger>
@@ -21,8 +21,8 @@ export async function importTranslationsAction(content: string) {
service.importPackage(actor, data);
revalidatePath("/settings/languages");
return { success: true };
} catch (error: any) {
} catch (error) {
console.error("Import error:", error);
return { errorKey: "common.error.description", details: error.message };
return { errorKey: "common.error.description", details: error instanceof Error ? error.message : String(error) };
}
}
+5 -14
View File
@@ -60,19 +60,6 @@ export type TaskListItem = {
translations?: LocalizedFieldValues;
};
const statusLabels = {
todo: "Yapılacak",
in_progress: "Devam ediyor",
done: "Tamamlandı",
};
const priorityLabels = {
low: "Düşük",
medium: "Orta",
high: "Yüksek",
urgent: "Acil",
};
const priorityClasses = {
low: "border-zinc-200 bg-zinc-50 text-zinc-700",
medium: "border-blue-200 bg-blue-50 text-blue-700",
@@ -687,7 +674,11 @@ function TaskFormFields({
idPrefix={`task-${task?.id || "new"}`}
defaultLocale={localization.defaultLocale}
locales={localization.locales}
fields={contentTranslationRegistry.task.map((f: any) => ({ ...f, label: (t as any)(`tasks.fields.${f.name}`) || f.label, placeholder: f.placeholder ? (t as any)(`tasks.placeholders.${f.name}`) || f.placeholder : undefined }))}
fields={contentTranslationRegistry.task.map((f) => ({
...f,
label: t(`tasks.fields.${f.name}`) || f.label,
placeholder: f.placeholder ? t(`tasks.placeholders.${f.name}`) || f.placeholder : undefined,
}))}
values={task?.translations}
fallbackValues={{
title: task?.title,
+2 -2
View File
@@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import { readFileSync } from "fs";
import { join } from "path";
import { compareCatalogKeys, I18N_NAMESPACES } from "../lib/i18n";
import { compareCatalogKeys } from "../lib/i18n";
import { trCatalog } from "../locales/tr";
import { enCatalog } from "../locales/en";
@@ -24,7 +24,7 @@ for (const page of authPages) {
assert.equal(content.includes("resolvePublicLocale"), true, `${page} missing resolvePublicLocale`);
}
const result = compareCatalogKeys(trCatalog as any, enCatalog as any, ["auth"]);
const result = compareCatalogKeys(trCatalog, enCatalog, ["auth"]);
const authMissingInEn = result.missingInRight.filter((k: string) => k.startsWith("auth."));
const authMissingInTr = result.missingInLeft.filter((k: string) => k.startsWith("auth."));
+1
View File
@@ -219,6 +219,7 @@ export const revisionCreateSchema = z.object({
id: resourceIdSchema.optional(),
projectId: resourceIdSchema,
description: z.string().trim().min(1).max(20_000),
sourceLocale: z.string().trim().min(2).max(12).nullable().optional(),
});
export const revisionStatusSchema = z.enum(revisionStatuses);
+1
View File
@@ -433,6 +433,7 @@ export class DomainService {
clientId: scope.clientId,
requestedByUserId: scope.authUserId,
description: value.description,
sourceLocale: value.sourceLocale,
}).returning().get();
}, { behavior: "immediate" });
}