From 88a44b13494587e2f9ab1483b9537861770f3a85 Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Tue, 2 Jun 2026 23:47:35 +0330 Subject: [PATCH] feat(admin): full Persian (fa) localization + RTL polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - admin-resources: all config titles/descriptions/columns/fields → Persian - AdminResource: generic UI strings (new/edit/delete/save/cancel/loading/empty/ actions/confirm) → Persian; text-left/right → logical text-start/end - TemplatesAdmin: all labels, table, modal, statuses, errors → Persian - FileUploadField, WebsiteSettingsAdmin, FileManager → Persian - layout: ms-auto logical prop (NodesTable/RenderQueue/AiContentStudio already i18n with Persian values) Co-Authored-By: Claude Opus 4.8 --- src/app/[locale]/admin/layout.tsx | 2 +- src/components/admin/AdminResource.tsx | 22 +- src/components/admin/FileManager.tsx | 22 +- src/components/admin/FileUploadField.tsx | 10 +- src/components/admin/TemplatesAdmin.tsx | 84 ++--- src/components/admin/WebsiteSettingsAdmin.tsx | 40 +-- src/components/admin/admin-resources.tsx | 290 +++++++++--------- 7 files changed, 235 insertions(+), 235 deletions(-) diff --git a/src/app/[locale]/admin/layout.tsx b/src/app/[locale]/admin/layout.tsx index 0647182..5471afe 100644 --- a/src/app/[locale]/admin/layout.tsx +++ b/src/app/[locale]/admin/layout.tsx @@ -56,7 +56,7 @@ export default async function AdminLayout({ ))} {t("backToDashboard")} diff --git a/src/components/admin/AdminResource.tsx b/src/components/admin/AdminResource.tsx index f5e06c9..e85f2f8 100644 --- a/src/components/admin/AdminResource.tsx +++ b/src/components/admin/AdminResource.tsx @@ -119,7 +119,7 @@ export function AdminResource({ config }: { config: ResourceConfig }) { }; const remove = async (row: Record) => { - if (!confirm(`Delete this ${config.title.replace(/s$/, "").toLowerCase()}?`)) return; + if (!confirm("حذف این مورد؟")) return; const res = await fetch(url(`/${row[idKey]}`), { method: "DELETE" }); if (res.ok) reload(); else { @@ -136,7 +136,7 @@ export function AdminResource({ config }: { config: ResourceConfig }) { {config.description &&

{config.description}

} {config.canCreate && config.fields && ( - + )} @@ -145,20 +145,20 @@ export function AdminResource({ config }: { config: ResourceConfig }) {
- + {config.columns.map((c) => ( ))} {(config.canEdit || config.canDelete || config.rowActions) && ( - + )} {loading ? ( - + ) : rows.length === 0 ? ( - + ) : ( rows.map((row, i) => ( @@ -172,14 +172,14 @@ export function AdminResource({ config }: { config: ResourceConfig }) {
{config.rowActions?.(row, reload)} {config.canEdit && config.fields && ( - + )} {config.canDelete && ( )}
@@ -196,7 +196,7 @@ export function AdminResource({ config }: { config: ResourceConfig }) {
e.stopPropagation()}>

- {editing ? "Edit" : "New"} {config.title.replace(/s$/, "")} + {editing ? "ویرایش" : "افزودن"} — {config.title}

{config.fields.map((f) => ( @@ -235,8 +235,8 @@ export function AdminResource({ config }: { config: ResourceConfig }) { ))}
- - + +
diff --git a/src/components/admin/FileManager.tsx b/src/components/admin/FileManager.tsx index bdfbc11..fb0223f 100644 --- a/src/components/admin/FileManager.tsx +++ b/src/components/admin/FileManager.tsx @@ -47,7 +47,7 @@ export function FileManager() { const data = await res.json(); setFiles(data?.data ?? data?.items ?? (Array.isArray(data) ? data : [])); } catch { - setError("Failed to load files"); + setError("بارگذاری فایل‌ها ناموفق بود"); } finally { setLoading(false); } @@ -64,7 +64,7 @@ export function FileManager() { const res = await fetch("/api/admin/files/upload", { method: "POST", body: fd }); if (!res.ok) { const d = await res.json().catch(() => null); - setError(d?.error ?? `Failed to upload ${file.name}`); + setError(d?.error ?? `آپلود ${file.name} ناموفق بود`); } } setUploading(false); @@ -73,9 +73,9 @@ export function FileManager() { }; const remove = async (f: FileItem) => { - if (!confirm(`Delete ${f.name}?`)) return; + if (!confirm(`«${f.name}» حذف شود؟`)) return; const res = await fetch(`/api/admin/resource/files/${f.id}`, { method: "DELETE" }); - if (res.ok) reload(); else setError("Delete failed"); + if (res.ok) reload(); else setError("حذف ناموفق بود"); }; const copy = (url: string) => { @@ -88,11 +88,11 @@ export function FileManager() {
-

Media Library

-

Upload and manage images & files. Public URLs can be copied into any field.

+

کتابخانه رسانه

+

آپلود و مدیریت تصاویر و فایل‌ها. نشانی‌های عمومی را می‌توان در هر فیلدی کپی کرد.

e.target.files && uploadFiles(e.target.files)} />
@@ -105,9 +105,9 @@ export function FileManager() { onDrop={(e) => { e.preventDefault(); if (e.dataTransfer.files.length) uploadFiles(e.dataTransfer.files); }} > {loading ? ( -

Loading…

+

در حال بارگذاری…

) : files.length === 0 ? ( -

No files yet. Drag & drop here or click Upload.

+

هنوز فایلی وجود ندارد. فایل را اینجا بکشید و رها کنید یا روی آپلود بزنید.

) : (
{files.map((f) => { @@ -127,10 +127,10 @@ export function FileManager() {
{url && ( )} - +
); diff --git a/src/components/admin/FileUploadField.tsx b/src/components/admin/FileUploadField.tsx index 5b8652b..ff27ca4 100644 --- a/src/components/admin/FileUploadField.tsx +++ b/src/components/admin/FileUploadField.tsx @@ -28,10 +28,10 @@ export function FileUploadField({ fd.append("file", file); const res = await fetch("/api/admin/files/upload", { method: "POST", body: fd }); const data = await res.json(); - if (!res.ok || !data?.url) throw new Error(data?.error ?? "Upload failed"); + if (!res.ok || !data?.url) throw new Error(data?.error ?? "بارگذاری ناموفق بود"); onChange(data.url); } catch (e) { - setError(e instanceof Error ? e.message : "Upload failed"); + setError(e instanceof Error ? e.message : "بارگذاری ناموفق بود"); } finally { setUploading(false); if (inputRef.current) inputRef.current.value = ""; @@ -53,7 +53,7 @@ export function FileUploadField({ ) ) : ( - none + ندارد )}
@@ -73,7 +73,7 @@ export function FileUploadField({ onClick={() => inputRef.current?.click()} className="rounded-lg bg-indigo-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-indigo-500 disabled:opacity-50" > - {uploading ? "Uploading…" : value ? "Replace" : "Upload"} + {uploading ? "در حال بارگذاری…" : value ? "تعویض" : "بارگذاری"} {value && ( )}
diff --git a/src/components/admin/TemplatesAdmin.tsx b/src/components/admin/TemplatesAdmin.tsx index fa67c6e..d7b5ea3 100644 --- a/src/components/admin/TemplatesAdmin.tsx +++ b/src/components/admin/TemplatesAdmin.tsx @@ -80,7 +80,7 @@ export function TemplatesAdmin() { }); if (!res.ok) { const d = await res.json().catch(() => null); - setError(d?.error ?? "Failed to save variant"); + setError(d?.error ?? "ذخیرهٔ نسخه ناموفق بود"); } setSavingProj(null); }; @@ -97,7 +97,7 @@ export function TemplatesAdmin() { setCats(Array.isArray(ct) ? ct : ct?.items ?? []); setTags(tg?.items ?? (Array.isArray(tg) ? tg : [])); } catch { - setError("Failed to load templates"); + setError("بارگذاری قالب‌ها ناموفق بود"); } finally { setLoading(false); } @@ -136,24 +136,24 @@ export function TemplatesAdmin() { }); const data = await res.json().catch(() => null); if (res.ok) { setOpen(false); reload(); } - else setError(data?.error ?? "Save failed"); + else setError(data?.error ?? "ذخیره ناموفق بود"); setSaving(false); }; const remove = async (row: Container) => { - if (!confirm(`Delete template "${row.name}"?`)) return; + if (!confirm(`قالب «${row.name}» حذف شود؟`)) return; const res = await fetch(api(`templates/${row.id}`), { method: "DELETE" }); - if (res.ok) reload(); else setError("Delete failed"); + if (res.ok) reload(); else setError("حذف ناموفق بود"); }; return (
-

Templates

-

Template packs — name, description, keywords, categories, tags, publishing.

+

قالب‌ها

+

بسته‌های قالب — نام، توضیحات، کلمات کلیدی، دسته‌بندی، برچسب و انتشار. هر قالب می‌تواند چند نسخه (تناسب/کیفیت) داشته باشد.

- +
{error &&

{error}

} @@ -161,17 +161,17 @@ export function TemplatesAdmin() {
{c.label}Actionsعملیات
Loading…
در حال بارگذاری…
No records.
رکوردی یافت نشد.
- - - - + + + + {loading ? ( - + ) : rows.length === 0 ? ( - + ) : rows.map((r) => ( @@ -179,16 +179,16 @@ export function TemplatesAdmin() { @@ -200,37 +200,37 @@ export function TemplatesAdmin() { {open && (
setOpen(false)}>
e.stopPropagation()}> -

{editId ? "Edit" : "New"} template

+

{editId ? "ویرایش قالب" : "قالب جدید"}

-
setForm({ ...form, name: e.target.value })} />
-
setForm({ ...form, slug: e.target.value })} />
+
setForm({ ...form, name: e.target.value })} />
+
setForm({ ...form, slug: e.target.value })} />
-
ImageNameSlugStatusModeSortActions
تصویرناماسلاگوضعیتحالتترتیبعملیات
Loading…
در حال بارگذاری…
No templates.
قالبی وجود ندارد.
{r.slug} - {r.is_published ? "published" : "draft"} + {r.is_published ? "منتشرشده" : "پیش‌نویس"} - {r.is_premium ? premium : null} + {r.is_premium ? ویژه : null} {r.primary_mode} {r.sort}
- - + +