feat: RTL/i18n, AI translate+undo, no-refresh saves, settings perf

- RTL: force dir=rtl on LabelFilter, NotesViewToggle, LabelManagementDialog
- i18n: add missing keys (notifications, privacy, edit/preview, AI translate/undo)
- Settings pages: convert to Server Components (general, appearance) + loading skeleton
- AI menu: add Translate option (10 languages) + Undo AI button in toolbar
- Fix: saveInline uses REST API instead of Server Action → eliminates all implicit refreshes in list mode
- Fix: NotesTabsView notes sync effect preserves selected note on content changes
- Fix: auto-tag suggestions now filter already-assigned labels
- Fix: color change in card view uses local state (no refresh)
- Fix: nav links use <Link> for prefetching (Settings, Admin)
- Fix: suppress duplicate label suggestions already on note
- Route: add /api/ai/translate endpoint
This commit is contained in:
Sepehr Ramezani
2026-04-15 23:48:28 +02:00
parent 39671c6472
commit b6a548acd8
68 changed files with 5014 additions and 485 deletions

View File

@@ -229,7 +229,15 @@
"transformError": "خطا در تبدیل",
"transformMarkdown": "تبدیل به مارک‌داون",
"transformSuccess": "متن با موفقیت به مارک‌داون تبدیل شد!",
"transforming": "در حال تبدیل..."
"transforming": "در حال تبدیل...",
"translate": "ترجمه",
"translateDesc": "تغییر زبان متن",
"translateBack": "بازگشت",
"translationApplied": "ترجمه اعمال شد",
"translationFailed": "ترجمه ناموفق بود",
"undo": "لغو هوش مصنوعی",
"undoAI": "لغو تبدیل هوش مصنوعی",
"undoApplied": "متن اصلی بازگردانده شد"
},
"aiSettings": {
"description": "ویژگی‌ها و ترجیحات هوش مصنوعی خود را پیکربندی کنید",
@@ -568,7 +576,7 @@
"delete": "حذف",
"deleteTooltip": "حذف برچسب",
"editLabels": "ویرایش برچسب‌ها",
"editLabelsDescription": "ایجاد، ویرایش رنگ‌ها یا حذف برچسب‌ها.",
"editLabelsDescription": "برچسب‌های خود را مدیریت کنید",
"filter": "فیلتر بر اساس برچسب",
"filterByLabel": "فیلتر بر اساس برچسب",
"labelColor": "رنگ برچسب",
@@ -579,15 +587,16 @@
"manageLabelsDescription": "Add or remove labels for this note. Click on a label to change its color.",
"manageTooltip": "مدیریت برچسب‌ها",
"namePlaceholder": "نام برچسب",
"newLabelPlaceholder": "ایجاد برچسب جدید",
"newLabelPlaceholder": "برچسب جدید...",
"noLabels": "بدون برچسب",
"noLabelsFound": "برچسبی یافت نشد.",
"noLabelsFound": "برچسبی یافت نشد",
"notebookRequired": "⚠️ برچسب‌ها فقط در دفترچه‌ها در دسترس هستند. این یادداشت را ابتدا به یک دفترچه منتقل کنید.",
"selectedLabels": "Selected Labels",
"showLess": "نمایش کمتر",
"showMore": "نمایش بیشتر",
"tagAdded": "برچسب \"{tag}\" اضافه شد",
"title": "برچسب‌ها"
"title": "برچسب‌ها",
"confirmDeleteShort": "تایید؟"
},
"memoryEcho": {
"clickToView": "برای مشاهده یادداشت کلیک کنید",
@@ -684,7 +693,7 @@
"logout": "خروج",
"manageAISettings": "Manage AI Settings",
"myLibrary": "کتابخانه من",
"notebooks": "Notebooks",
"notebooks": "دفترچه‌ها",
"notes": "یادداشت‌ها",
"proPlan": "پلن پرو",
"profile": "پروفایل",
@@ -765,7 +774,7 @@
"delete": "حذف",
"dragToReorder": "بکشید تا مرتب کنید",
"duplicate": "تکثیر",
"edit": "Edit Note",
"edit": "ویرایش",
"emptyState": "یادداشتی نیست",
"fileTooLarge": "فایل خیلی بزرگ است: {fileName}. حداکثر اندازه {maxSize}.",
"improveFailed": "بهبود شکست خورد",
@@ -801,7 +810,7 @@
"pinned": "سنجاق شده",
"pinnedNotes": "یادداشت‌های سنجاق شده",
"placeholder": "یادداشت بگیرید...",
"preview": "Preview",
"preview": "پیش‌نمایش",
"readOnly": "Read Only",
"recent": "اخیر",
"redo": "انجام مجدد",
@@ -837,7 +846,11 @@
"unpinned": "سنجاق نشده",
"untitled": "بدون عنوان",
"uploadFailed": "آپلود {fileName} شکست خورد",
"view": "View Note"
"view": "View Note",
"modified": "ویرایش شده",
"created": "ایجاد شده",
"viewTabs": "نمایش زبانه‌ای",
"viewCards": "نمایش کارتی"
},
"pagination": {
"next": "→",
@@ -962,15 +975,26 @@
"themeLight": "روشن",
"themeSystem": "سیستم",
"title": "تنظیمات",
"version": "نسخه"
"version": "نسخه",
"emailNotifications": "اعلان‌های ایمیل",
"emailNotificationsDesc": "دریافت اعلان‌های مهم از طریق ایمیل",
"desktopNotifications": "اعلان‌های مرورگر",
"desktopNotificationsDesc": "دریافت اعلان‌ها در مرورگر",
"anonymousAnalytics": "تحلیل‌های ناشناس",
"anonymousAnalyticsDesc": "اشتراک داده‌های استفاده ناشناس برای بهبود برنامه",
"notificationsDesc": "مدیریت تنظیمات اعلان",
"privacyDesc": "کنترل داده‌ها و حریم خصوصی شما"
},
"sidebar": {
"archive": "Archive",
"editLabels": "Edit labels",
"archive": "بایگانی",
"editLabels": "ویرایش برچسب‌ها",
"labels": "Labels",
"notes": "Notes",
"reminders": "Reminders",
"trash": "Trash"
"notes": "یادداشت‌ها",
"reminders": "یادآورها",
"trash": "زباله‌دان",
"newNoteTabs": "یادداشت جدید",
"newNoteTabsHint": "ایجاد یادداشت جدید در این دفترچه",
"edit": "ویرایش یادداشت"
},
"support": {
"aiApiCosts": "هزینه‌های AI API:",
@@ -1050,5 +1074,65 @@
"collapse": "جمع کردن",
"expand": "بسط دادن",
"open": "باز کردن"
},
"mcpSettings": {
"title": "تنظیمات MCP",
"description": "مدیریت کلیدهای API و پیکربندی ابزارهای خارجی",
"whatIsMcp": {
"title": "MCP چیست؟",
"description": "پروتکل زمینه مدل (MCP) یک پروتکل باز است که به مدل‌های هوش مصنوعی امکان تعامل امن با ابزارها و منابع داده خارجی را می‌دهد. با MCP می‌توانید ابزارهایی مانند Claude Code، Cursor یا N8N را به نمونه Keep Notes خود متصل کنید تا یادداشت‌های خود را به صورت برنامه‌نویسی بخوانید، ایجاد کنید و سازماندهی کنید.",
"learnMore": "بیشتر درباره MCP بدانید"
},
"serverStatus": {
"title": "وضعیت سرور",
"running": "در حال اجرا",
"stopped": "متوقف",
"mode": "حالت",
"url": "URL"
},
"apiKeys": {
"title": "کلیدهای API",
"description": "کلیدهای API به ابزارهای خارجی اجازه می‌دهند از طریق MCP به یادداشت‌های شما دسترسی پیدا کنند. کلیدهای خود را محرمانه نگه دارید.",
"generate": "ایجاد کلید جدید",
"empty": "هنوز کلید API وجود ندارد. برای شروع یکی ایجاد کنید.",
"active": "فعال",
"revoked": "لغو شده",
"revoke": "لغو",
"delete": "حذف",
"createdAt": "تاریخ ایجاد",
"lastUsed": "آخرین استفاده",
"never": "هرگز",
"confirmRevoke": "آیا مطمئن هستید که می‌خواهید این کلید را لغو کنید؟ ابزارهایی که از آن استفاده می‌کنند دسترسی خود را از دست می‌دهند.",
"confirmDelete": "آیا مطمئن هستید که می‌خواهید این کلید را برای همیشه حذف کنید؟"
},
"createDialog": {
"title": "ایجاد کلید API",
"description": "یک کلید API جدید برای اتصال ابزارهای خارجی به یادداشت‌های خود ایجاد کنید.",
"nameLabel": "نام کلید",
"namePlaceholder": "مثال: Claude Code، Cursor، N8N",
"generating": "در حال ایجاد...",
"generate": "ایجاد",
"successTitle": "کلید API ایجاد شد",
"successDescription": "کلید API خود را همین حالا کپی کنید. دیگر نمی‌توانید آن را ببینید.",
"copy": "کپی",
"copied": "کپی شد!",
"done": "انجام شد"
},
"configInstructions": {
"title": "دستورالعمل پیکربندی",
"description": "از کلید API خود برای پیکربندی این ابزارها استفاده کنید.",
"claudeCode": {
"title": "Claude Code",
"description": "این را به فایل پیکربندی MCP Claude Code خود اضافه کنید:"
},
"cursor": {
"title": "Cursor",
"description": "این را به تنظیمات MCP Cursor خود اضافه کنید:"
},
"n8n": {
"title": "N8N",
"description": "از این اعتبارنامه‌ها در گره N8N MCP خود استفاده کنید:"
}
}
}
}
}