- {transaction.category || (transaction.type === "income" ? "Gelir" : "Gider")}
+ {transaction.category || (transaction.type === "income" ? t("projects.detail.income") : t("projects.detail.expense"))}
{formatDate(transaction.transaction_date)} · {transaction.payment_status}
@@ -1178,7 +1183,7 @@ function FinancePanel({ transactions }: { transactions: ProjectFinanceItem[] })
))}
) : (
-
+
)}
@@ -1284,11 +1289,7 @@ function formatDateTime(value: string) {
}).format(new Date(value));
}
-function getTaskStatusLabel(status: ProjectDetailTaskItem["status"]) {
- if (status === "done") return "Tamamlandı";
- if (status === "in_progress") return "Devam ediyor";
- return "Yapılacak";
-}
+// Removed function since it's localized inline now or no longer needed
function formatCurrency(value: number, currency: string) {
return new Intl.NumberFormat(getDocumentIntlLocale(), {
diff --git a/app/(dashboard)/projects/page.tsx b/app/(dashboard)/projects/page.tsx
index c60952e..afd137e 100644
--- a/app/(dashboard)/projects/page.tsx
+++ b/app/(dashboard)/projects/page.tsx
@@ -3,10 +3,13 @@ import { getSqliteConnection } from "@/server/db/client";
import { ContentTranslationService, type ContentTranslationRow } from "@/server/i18n/content";
import { resolveFreelancerLocale } from "@/server/i18n/resolver";
import { requireFreelancerBackend } from "@/server/web/freelancer";
+import { getClientI18nPayload } from "@/server/i18n/translator";
+import { I18nProvider } from "@/components/i18n/i18n-provider";
export default async function ProjectsPage() {
- const locale = await resolveFreelancerLocale();
- const { actor, service } = await requireFreelancerBackend();
+ const { context, actor, service } = await requireFreelancerBackend();
+ const locale = await resolveFreelancerLocale(context);
+ const payload = getClientI18nPayload(locale.locale, ["projects", "common"]);
const content = new ContentTranslationService(getSqliteConnection().db);
const localization = content.getLocalizationContext(actor);
const projectRows = service.listProjects(actor);
@@ -58,7 +61,13 @@ export default async function ProjectsPage() {
.sort((a, b) => a.name.localeCompare(b.name, locale.locale))
.map(({ id, name }) => ({ id, name }));
- return
;
+ const i18nPayload = await getClientI18nPayload(locale.locale, ["projects", "tasks", "common"]);
+
+ return (
+
+
+
+ );
}
function toLocalizedValues(rows: ContentTranslationRow[]) {
diff --git a/app/(dashboard)/projects/projects-client.tsx b/app/(dashboard)/projects/projects-client.tsx
index 3d3114a..0cf1d39 100644
--- a/app/(dashboard)/projects/projects-client.tsx
+++ b/app/(dashboard)/projects/projects-client.tsx
@@ -73,18 +73,18 @@ export type ProjectListItem = {
translations?: LocalizedFieldValues;
};
-const typeLabels = {
- client_project: "Müşteri projesi",
- side_project: "Side project",
-};
+const typeLabels = (t: any) => ({
+ client_project: t("projects.types.client"),
+ side_project: t("projects.types.side"),
+});
-const statusLabels = {
- planning: "Planlama",
- active: "Aktif",
- paused: "Duraklatıldı",
- completed: "Tamamlandı",
- cancelled: "İptal edildi",
-};
+const statusLabels = (t: any) => ({
+ planning: t("projects.status.planning"),
+ active: t("projects.status.active"),
+ paused: t("projects.status.paused"),
+ completed: t("projects.status.completed"),
+ cancelled: t("projects.status.cancelled"),
+});
const statusClasses = {
planning: "border-blue-200 bg-blue-50 text-blue-700",
@@ -108,9 +108,11 @@ export function ProjectsClient({ projects, clients, localization }: ProjectsClie
const [query, setQuery] = useState("");
const [view, setView] = useState<"grid" | "list">("grid");
const normalizedQuery = query.trim().toLowerCase();
+ const types = typeLabels(t);
+
const filteredProjects = normalizedQuery
? projects.filter((project) =>
- [project.name, project.description, project.clientName, typeLabels[project.type]]
+ [project.name, project.description, project.clientName, types[project.type]]
.filter(Boolean)
.some((value) => value!.toLowerCase().includes(normalizedQuery)),
)
@@ -140,7 +142,7 @@ export function ProjectsClient({ projects, clients, localization }: ProjectsClie
-
+
@@ -149,16 +151,16 @@ export function ProjectsClient({ projects, clients, localization }: ProjectsClie
-
Proje listesi
+
{t("projects.list.title")}
- {filteredProjects.length} kayıt görüntüleniyor.
+ {t("projects.list.count", { count: filteredProjects.length })}
@@ -195,11 +197,11 @@ export function ProjectsClient({ projects, clients, localization }: ProjectsClie
- Proje
- Tür
- Durum
- İlerleme
- İşlem
+ {t("projects.list.columns.project")}
+ {t("projects.list.columns.type")}
+ {t("projects.list.columns.status")}
+ {t("projects.list.columns.budgetDeadline")}
+ İşlemler
{filteredProjects.map((project) => (
@@ -227,6 +229,7 @@ function ProjectCard({
clients: ProjectClientOption[];
localization: ProjectsClientProps["localization"];
}) {
+ const t = useTranslations();
const router = useRouter();
const [isNavigating, startNavigation] = useTransition();
const detailHref = `/projects/${project.id}`;
@@ -273,10 +276,12 @@ function ProjectCard({
{project.name}
- {project.description || "Açıklama eklenmedi."}
+ {project.description || t("projects.card.noDescription")}
-
{statusLabels[project.status]}
+
+ {statusLabels(t)[project.status]}
+
@@ -284,7 +289,7 @@ function ProjectCard({
- {project.doneTaskCount}/{project.taskCount} görev tamamlandı
+ {t("projects.card.taskProgress", { done: project.doneTaskCount, total: project.taskCount })}
@@ -294,6 +299,7 @@ function ProjectCard({
}
function ProjectCover({ project }: { project: ProjectListItem }) {
+ const t = useTranslations();
if (project.coverImageUrl) {
return (
@@ -311,7 +317,7 @@ function ProjectCover({ project }: { project: ProjectListItem }) {
return (
- Kapak görseli yok
+ {t("projects.card.noCover")}
);
}
@@ -325,17 +331,18 @@ function ProjectRow({
clients: ProjectClientOption[];
localization: ProjectsClientProps["localization"];
}) {
+ const t = useTranslations();
return (
{project.name}
- {project.clientName || "Bağımsız side project"}
+ {project.clientName || t("projects.card.noClient")}
-
{typeLabels[project.type]}
+
{typeLabels(t)[project.type]}
- {statusLabels[project.status]}
+ {statusLabels(t)[project.status]}
@@ -348,15 +355,16 @@ function ProjectRow({
}
function ProjectMeta({ project }: { project: ProjectListItem }) {
+ const t = useTranslations();
return (
-
{typeLabels[project.type]}
-
{project.clientName || "Müşteri bağlantısı yok"}
+
{typeLabels(t)[project.type]}
+
{project.clientName || t("projects.card.noClient")}
- {project.due_date ? formatDate(project.due_date) : "Deadline yok"}
+ {project.due_date ? formatDate(project.due_date) : t("projects.card.noDeadline")}
-
{project.budget_amount ? formatCurrency(project.budget_amount) : "Bütçe yok"}
+
{project.budget_amount ? formatCurrency(project.budget_amount) : t("projects.card.noBudget")}
);
}
@@ -372,6 +380,7 @@ function ProjectActions({
localization: ProjectsClientProps["localization"];
showDetail: boolean;
}) {
+ const t = useTranslations();
return (
@@ -399,8 +408,8 @@ function ProjectActions({
}
>
@@ -423,6 +432,7 @@ function ProjectDialog({
localization: ProjectsClientProps["localization"];
iconOnly?: boolean;
}) {
+ const t = useTranslations();
const [open, setOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [projectType, setProjectType] = useState(project?.type || "client_project");
@@ -434,12 +444,12 @@ function ProjectDialog({
try {
await action(formData);
setOpen(false);
- toast.success(mode === "create" ? "Proje eklendi." : "Proje güncellendi.");
+ toast.success(mode === "create" ? t("projects.messages.created") : t("projects.messages.updated"));
} catch (error) {
toast.error(
error instanceof Error
? error.message
- : "Proje kaydedilirken beklenmeyen bir hata oluştu.",
+ : t("projects.errors.saveFailed"),
);
} finally {
setIsSubmitting(false);
@@ -453,20 +463,20 @@ function ProjectDialog({
variant={mode === "create" ? "default" : "secondary"}
size={iconOnly ? "icon" : "default"}
className={iconOnly ? undefined : "min-w-24 gap-2 px-3"}
- title={mode === "create" ? "Proje ekle" : "Düzenle"}
- aria-label={mode === "create" ? "Proje ekle" : "Düzenle"}
+ title={mode === "create" ? t("projects.actions.add") : t("projects.actions.edit")}
+ aria-label={mode === "create" ? t("projects.actions.add") : t("projects.actions.edit")}
>
{mode === "create" ? : }
- {iconOnly ? null : mode === "create" ? "Proje ekle" : "Düzenle"}
+ {iconOnly ? null : mode === "create" ? t("projects.actions.add") : t("projects.actions.edit")}
@@ -497,6 +507,7 @@ function ProjectDialog({
}
function CoverImageInput({ project }: { project?: ProjectListItem }) {
+ const t = useTranslations();
const inputId = `cover-${project?.id || "new"}`;
const [previewUrl, setPreviewUrl] = useState(project?.coverImageUrl || "");
@@ -528,7 +539,7 @@ function CoverImageInput({ project }: { project?: ProjectListItem }) {
return (
-
+
-
Kapak görseli seç
-
PNG, JPG, WebP veya GIF
+
{t("projects.form.coverImageSelect")}
+
{t("projects.form.coverImageFormat")}
)}
{previewUrl ? (
- Görseli değiştirmek için tıkla.
+ {t("projects.form.coverImageChange")}
) : null}
@@ -585,6 +596,7 @@ function ProjectFormFields({
projectType: ProjectListItem["type"];
onProjectTypeChange: (value: ProjectListItem["type"]) => void;
}) {
+ const t = useTranslations();
return (
@@ -593,7 +605,7 @@ function ProjectFormFields({
idPrefix={`project-${project?.id || "new"}`}
defaultLocale={localization.defaultLocale}
locales={localization.locales}
- fields={contentTranslationRegistry.project}
+ 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 }))}
values={project?.translations}
fallbackValues={{
name: project?.name,
@@ -604,30 +616,30 @@ function ProjectFormFields({
-
+
-
+