mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
🐛 fix: add unsaved changes guard to prevent data loss on navigation (#12332)
* 🐛 fix: add unsaved changes guard to prevent data loss on navigation Migrate from BrowserRouter to createBrowserRouter (data router API) to enable route-level navigation blocking. Add UnsavedChangesGuard component that uses useBlocker to prevent leaving editor pages with unsaved changes, auto-saving before navigation. Remove legacy renderRoutes/RouteConfig dead code. Fixes LOBE-4973 * 🔧 chore: remove unused ESLint suppressions for welcome.ts Cleaned up eslint-suppressions.json by removing suppressions related to sort-keys-fix and typescript-sort-keys for welcome.ts, as they are no longer needed. Signed-off-by: Innei <tukon479@gmail.com> * ⚡ perf: skip JSON snapshot on selection-only Lexical updates Reintroduce dirtyElements/dirtyLeaves guard before editor.getDocument('json') and deep-equality check, avoiding O(document-size) work on caret/selection updates that do not mutate content. * 🔧 test: update UnsavedChangesGuard tests to use message.destroy instead of message.success Signed-off-by: Innei <tukon479@gmail.com> * fix: dayjs init - Moved dayjs plugin extensions (relativeTime, utc, isToday, isYesterday) to src/initialize.ts for centralized initialization. - Removed redundant extensions from individual components to prevent duplicate calls. - Updated locale loading logic in Locale.tsx to ensure correct dayjs locale handling. This change improves performance by ensuring dayjs plugins are only extended once during application initialization. Signed-off-by: Innei <tukon479@gmail.com> * refactor: update router configuration to use RouteObject type - Changed the type of desktopRoutes from RouteConfig[] to RouteObject[] for better compatibility with react-router-dom. - Removed the RouteConfig interface from the router utility file to streamline the codebase. This refactor enhances the router's integration with the latest routing library standards. Signed-off-by: Innei <tukon479@gmail.com> * feat: enhance Vite configuration and chunk management - Added a function to suppress Vite's default URL print in the server configuration. - Updated chunk file naming strategy in sharedRollupOutput to organize output files into specific directories based on chunk type (i18n, vendor, assets). - Removed redundant dayjs chunk handling logic to streamline the manualChunks function. These changes improve the clarity of the build output and enhance the server's configuration options. Signed-off-by: Innei <tukon479@gmail.com> * ✨ feat: add collapsible error stack with __CI__ default expand - Add Collapse + Highlighter for error stack in Error component - Define __CI__ in Vite (sharedRendererDefine) based on process.env.CI - Add __CI__ to global.d.ts - Add error.stack i18n to all 18 locales Made-with: Cursor * chore: update build:spa:copy script to handle multiple asset directories - Modified the build:spa:copy script in package.json to iterate over multiple directories (assets, i18n, vendor) for both desktop and mobile builds, improving the asset copying process. This change enhances the build process by ensuring all relevant directories are copied correctly. Signed-off-by: Innei <tukon479@gmail.com> * 🐛 fix: mark initialize.ts as sideEffects to prevent tree-shaking sideEffects: false caused Rollup to drop the side-effect-only import of initialize.ts, removing dayjs.extend(relativeTime) and enableMapSet() from the production bundle. --------- Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
026af3f6bc
commit
58fb45d251
74 changed files with 1561 additions and 1373 deletions
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "العودة إلى الصفحة الرئيسية",
|
||||
"error.desc": "حاول مرة أخرى لاحقًا، أو عد إلى العالم المعروف.",
|
||||
"error.retry": "إعادة التحميل",
|
||||
"error.stack": "مكدس الأخطاء",
|
||||
"error.title": "عذرًا، حدث خطأ ما..",
|
||||
"fetchError.detail": "تفاصيل الخطأ",
|
||||
"fetchError.title": "فشل الطلب",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "الصورة الرمزية",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "زيادة التحميل",
|
||||
"tokenTag.remained": "المتبقي",
|
||||
"tokenTag.used": "المستخدم"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "إلغاء",
|
||||
"common.confirm": "تأكيد",
|
||||
"common.delete": "حذف",
|
||||
"common.edit": "تعديل"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "إضافة خصائص",
|
||||
"editableMessage.delete": "حذف",
|
||||
"editableMessage.input": "المدخلات",
|
||||
"editableMessage.inputPlaceholder": "يرجى إدخال محتوى مدخلات تجريبي",
|
||||
"editableMessage.output": "المخرجات",
|
||||
"editableMessage.outputPlaceholder": "يرجى إدخال محتوى مخرجات تجريبي",
|
||||
"editableMessage.system": "النظام"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "حذف",
|
||||
"emojiPicker.draggerDesc": "انقر أو اسحب الصورة إلى هذه المنطقة للتحميل",
|
||||
"emojiPicker.emoji": "رموز تعبيرية",
|
||||
"emojiPicker.fileTypeError": "يمكنك تحميل ملفات الصور فقط!",
|
||||
"emojiPicker.upload": "تحميل",
|
||||
"emojiPicker.uploadBtn": "قص وتحميل"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "إعادة تعيين",
|
||||
"form.submit": "إرسال",
|
||||
"form.unsavedChanges": "تغييرات غير محفوظة",
|
||||
"form.unsavedWarning": "لديك تغييرات غير محفوظة. هل أنت متأكد أنك تريد المغادرة؟"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "هذا الاختصار يتعارض مع اختصار موجود.",
|
||||
"hotkey.invalidCombination": "يجب أن يتضمن الاختصار مفتاح تعديل (Ctrl أو Alt أو Shift) ومفتاح عادي واحد فقط.",
|
||||
"hotkey.placeholder": "اضغط على المفاتيح لتسجيل الاختصار",
|
||||
"hotkey.reset": "إعادة التعيين إلى الوضع الافتراضي"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "إلغاء",
|
||||
"messageModal.confirm": "تأكيد",
|
||||
"messageModal.edit": "تعديل"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "طي الشريط الجانبي",
|
||||
"sideNav.demoActiveLabel": "نشط",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "اسحب أسفل العتبة للطي الذكي",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "الطي التلقائي",
|
||||
"sideNav.demoFeaturePerformanceDesc": "بدون رسوم متحركة لأداء أفضل",
|
||||
"sideNav.demoFeaturePerformanceTitle": "الأداء",
|
||||
"sideNav.demoFeatureResizeDesc": "اسحب لتعديل عرض اللوحة",
|
||||
"sideNav.demoFeatureResizeTitle": "تعديل مرن",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "مرر لإظهار زر التبديل",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "مقبض ذكي",
|
||||
"sideNav.demoFeaturesTitle": "الميزات",
|
||||
"sideNav.demoHint": "جرّب سحب حافة اللوحة واستخدام زر التبديل ->",
|
||||
"sideNav.demoSubtitle": "لوحة جانبية بأسلوب مساحة العمل مع إمكانية تغيير الحجم بالسحب",
|
||||
"sideNav.demoTitle": "عرض توضيحي لـ DraggableSideNav",
|
||||
"sideNav.expand": "توسيع الشريط الجانبي"
|
||||
}
|
||||
"chat.avatar": "الصورة الرمزية",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "إلغاء",
|
||||
"common.confirm": "تأكيد",
|
||||
"common.delete": "حذف",
|
||||
"common.edit": "تعديل",
|
||||
"editableMessage.addProps": "إضافة خصائص",
|
||||
"editableMessage.delete": "حذف",
|
||||
"editableMessage.input": "المدخلات",
|
||||
"editableMessage.inputPlaceholder": "يرجى إدخال محتوى مدخلات تجريبي",
|
||||
"editableMessage.output": "المخرجات",
|
||||
"editableMessage.outputPlaceholder": "يرجى إدخال محتوى مخرجات تجريبي",
|
||||
"editableMessage.system": "النظام",
|
||||
"emojiPicker.delete": "حذف",
|
||||
"emojiPicker.draggerDesc": "انقر أو اسحب الصورة إلى هذه المنطقة للتحميل",
|
||||
"emojiPicker.emoji": "رموز تعبيرية",
|
||||
"emojiPicker.fileTypeError": "يمكنك تحميل ملفات الصور فقط!",
|
||||
"emojiPicker.upload": "تحميل",
|
||||
"emojiPicker.uploadBtn": "قص وتحميل",
|
||||
"form.reset": "إعادة تعيين",
|
||||
"form.submit": "إرسال",
|
||||
"form.unsavedChanges": "تغييرات غير محفوظة",
|
||||
"form.unsavedWarning": "لديك تغييرات غير محفوظة. هل أنت متأكد أنك تريد المغادرة؟",
|
||||
"hotkey.conflict": "هذا الاختصار يتعارض مع اختصار موجود.",
|
||||
"hotkey.invalidCombination": "يجب أن يتضمن الاختصار مفتاح تعديل (Ctrl أو Alt أو Shift) ومفتاح عادي واحد فقط.",
|
||||
"hotkey.placeholder": "اضغط على المفاتيح لتسجيل الاختصار",
|
||||
"hotkey.reset": "إعادة التعيين إلى الوضع الافتراضي",
|
||||
"messageModal.cancel": "إلغاء",
|
||||
"messageModal.confirm": "تأكيد",
|
||||
"messageModal.edit": "تعديل",
|
||||
"sideNav.collapse": "طي الشريط الجانبي",
|
||||
"sideNav.demoActiveLabel": "نشط",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "اسحب أسفل العتبة للطي الذكي",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "الطي التلقائي",
|
||||
"sideNav.demoFeaturePerformanceDesc": "بدون رسوم متحركة لأداء أفضل",
|
||||
"sideNav.demoFeaturePerformanceTitle": "الأداء",
|
||||
"sideNav.demoFeatureResizeDesc": "اسحب لتعديل عرض اللوحة",
|
||||
"sideNav.demoFeatureResizeTitle": "تعديل مرن",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "مرر لإظهار زر التبديل",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "مقبض ذكي",
|
||||
"sideNav.demoFeaturesTitle": "الميزات",
|
||||
"sideNav.demoHint": "جرّب سحب حافة اللوحة واستخدام زر التبديل ->",
|
||||
"sideNav.demoSubtitle": "لوحة جانبية بأسلوب مساحة العمل مع إمكانية تغيير الحجم بالسحب",
|
||||
"sideNav.demoTitle": "عرض توضيحي لـ DraggableSideNav",
|
||||
"sideNav.expand": "توسيع الشريط الجانبي",
|
||||
"tokenTag.overload": "زيادة التحميل",
|
||||
"tokenTag.remained": "المتبقي",
|
||||
"tokenTag.used": "المستخدم"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Обратно към началната страница",
|
||||
"error.desc": "Опитайте отново по-късно или се върнете към познатия свят.",
|
||||
"error.retry": "Презареди",
|
||||
"error.stack": "Стек на грешките",
|
||||
"error.title": "Упс, нещо се обърка..",
|
||||
"fetchError.detail": "Подробности за грешката",
|
||||
"fetchError.title": "Заявката не бе успешна",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Аватар",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Претоварване",
|
||||
"tokenTag.remained": "Останали",
|
||||
"tokenTag.used": "Използвани"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Отказ",
|
||||
"common.confirm": "Потвърди",
|
||||
"common.delete": "Изтрий",
|
||||
"common.edit": "Редактирай"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Добави свойства",
|
||||
"editableMessage.delete": "Изтрий",
|
||||
"editableMessage.input": "Вход",
|
||||
"editableMessage.inputPlaceholder": "Моля, въведете примерен вход",
|
||||
"editableMessage.output": "Изход",
|
||||
"editableMessage.outputPlaceholder": "Моля, въведете примерен изход",
|
||||
"editableMessage.system": "Система"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Изтрий",
|
||||
"emojiPicker.draggerDesc": "Кликнете или плъзнете изображение тук, за да го качите",
|
||||
"emojiPicker.emoji": "Емотикони",
|
||||
"emojiPicker.fileTypeError": "Можете да качвате само файлове с изображения!",
|
||||
"emojiPicker.upload": "Качване",
|
||||
"emojiPicker.uploadBtn": "Изрежи и качи"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Нулирай",
|
||||
"form.submit": "Изпрати",
|
||||
"form.unsavedChanges": "Незаписани промени",
|
||||
"form.unsavedWarning": "Имате незаписани промени. Сигурни ли сте, че искате да напуснете?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Този клавишен шорткът е в конфликт със съществуващ.",
|
||||
"hotkey.invalidCombination": "Шорткътът трябва да включва модифициращ клавиш (Ctrl, Alt, Shift) и само един обикновен клавиш.",
|
||||
"hotkey.placeholder": "Натиснете клавиши за запис на шорткът",
|
||||
"hotkey.reset": "Възстанови по подразбиране"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Отказ",
|
||||
"messageModal.confirm": "Потвърди",
|
||||
"messageModal.edit": "Редактирай"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Свий страничната лента",
|
||||
"sideNav.demoActiveLabel": "Активен",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Плъзнете под прага за интелигентно свиване",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Автоматично свиване",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Без анимации за по-добра производителност",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Производителност",
|
||||
"sideNav.demoFeatureResizeDesc": "Плъзнете за промяна на ширината на панела",
|
||||
"sideNav.demoFeatureResizeTitle": "Гъвкаво преоразмеряване",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Задръжте курсора за показване на бутона за превключване",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Интелигентна дръжка",
|
||||
"sideNav.demoFeaturesTitle": "Функции",
|
||||
"sideNav.demoHint": "Опитайте да плъзнете ръба на панела и използвайте бутона за превключване ->",
|
||||
"sideNav.demoSubtitle": "Страничен панел в стил работно пространство с възможност за преоразмеряване чрез плъзгане",
|
||||
"sideNav.demoTitle": "Демо на DraggableSideNav",
|
||||
"sideNav.expand": "Разгъни страничната лента"
|
||||
}
|
||||
"chat.avatar": "Аватар",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Отказ",
|
||||
"common.confirm": "Потвърди",
|
||||
"common.delete": "Изтрий",
|
||||
"common.edit": "Редактирай",
|
||||
"editableMessage.addProps": "Добави свойства",
|
||||
"editableMessage.delete": "Изтрий",
|
||||
"editableMessage.input": "Вход",
|
||||
"editableMessage.inputPlaceholder": "Моля, въведете примерен вход",
|
||||
"editableMessage.output": "Изход",
|
||||
"editableMessage.outputPlaceholder": "Моля, въведете примерен изход",
|
||||
"editableMessage.system": "Система",
|
||||
"emojiPicker.delete": "Изтрий",
|
||||
"emojiPicker.draggerDesc": "Кликнете или плъзнете изображение тук, за да го качите",
|
||||
"emojiPicker.emoji": "Емотикони",
|
||||
"emojiPicker.fileTypeError": "Можете да качвате само файлове с изображения!",
|
||||
"emojiPicker.upload": "Качване",
|
||||
"emojiPicker.uploadBtn": "Изрежи и качи",
|
||||
"form.reset": "Нулирай",
|
||||
"form.submit": "Изпрати",
|
||||
"form.unsavedChanges": "Незаписани промени",
|
||||
"form.unsavedWarning": "Имате незаписани промени. Сигурни ли сте, че искате да напуснете?",
|
||||
"hotkey.conflict": "Този клавишен шорткът е в конфликт със съществуващ.",
|
||||
"hotkey.invalidCombination": "Шорткътът трябва да включва модифициращ клавиш (Ctrl, Alt, Shift) и само един обикновен клавиш.",
|
||||
"hotkey.placeholder": "Натиснете клавиши за запис на шорткът",
|
||||
"hotkey.reset": "Възстанови по подразбиране",
|
||||
"messageModal.cancel": "Отказ",
|
||||
"messageModal.confirm": "Потвърди",
|
||||
"messageModal.edit": "Редактирай",
|
||||
"sideNav.collapse": "Свий страничната лента",
|
||||
"sideNav.demoActiveLabel": "Активен",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Плъзнете под прага за интелигентно свиване",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Автоматично свиване",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Без анимации за по-добра производителност",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Производителност",
|
||||
"sideNav.demoFeatureResizeDesc": "Плъзнете за промяна на ширината на панела",
|
||||
"sideNav.demoFeatureResizeTitle": "Гъвкаво преоразмеряване",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Задръжте курсора за показване на бутона за превключване",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Интелигентна дръжка",
|
||||
"sideNav.demoFeaturesTitle": "Функции",
|
||||
"sideNav.demoHint": "Опитайте да плъзнете ръба на панела и използвайте бутона за превключване ->",
|
||||
"sideNav.demoSubtitle": "Страничен панел в стил работно пространство с възможност за преоразмеряване чрез плъзгане",
|
||||
"sideNav.demoTitle": "Демо на DraggableSideNav",
|
||||
"sideNav.expand": "Разгъни страничната лента",
|
||||
"tokenTag.overload": "Претоварване",
|
||||
"tokenTag.remained": "Останали",
|
||||
"tokenTag.used": "Използвани"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Zurück zur Startseite",
|
||||
"error.desc": "Versuchen Sie es später erneut oder kehren Sie in die bekannte Welt zurück.",
|
||||
"error.retry": "Neu laden",
|
||||
"error.stack": "Fehlerstapel",
|
||||
"error.title": "Hoppla, da ist etwas schiefgelaufen..",
|
||||
"fetchError.detail": "Fehlerdetails",
|
||||
"fetchError.title": "Anfrage fehlgeschlagen",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Überlastung",
|
||||
"tokenTag.remained": "Verbleibend",
|
||||
"tokenTag.used": "Verbraucht"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Abbrechen",
|
||||
"common.confirm": "Bestätigen",
|
||||
"common.delete": "Löschen",
|
||||
"common.edit": "Bearbeiten"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Eigenschaften hinzufügen",
|
||||
"editableMessage.delete": "Löschen",
|
||||
"editableMessage.input": "Eingabe",
|
||||
"editableMessage.inputPlaceholder": "Bitte Beispielinhalt für die Eingabe eingeben",
|
||||
"editableMessage.output": "Ausgabe",
|
||||
"editableMessage.outputPlaceholder": "Bitte Beispielinhalt für die Ausgabe eingeben",
|
||||
"editableMessage.system": "System"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Löschen",
|
||||
"emojiPicker.draggerDesc": "Klicken oder Bild hierher ziehen, um es hochzuladen",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Sie können nur Bilddateien hochladen!",
|
||||
"emojiPicker.upload": "Hochladen",
|
||||
"emojiPicker.uploadBtn": "Zuschneiden und hochladen"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Zurücksetzen",
|
||||
"form.submit": "Absenden",
|
||||
"form.unsavedChanges": "Nicht gespeicherte Änderungen",
|
||||
"form.unsavedWarning": "Sie haben nicht gespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Diese Tastenkombination steht in Konflikt mit einer bestehenden.",
|
||||
"hotkey.invalidCombination": "Die Tastenkombination muss eine Modifikatortaste (Strg, Alt, Umschalt) und nur eine normale Taste enthalten.",
|
||||
"hotkey.placeholder": "Tasten drücken, um Tastenkombination aufzuzeichnen",
|
||||
"hotkey.reset": "Auf Standard zurücksetzen"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Abbrechen",
|
||||
"messageModal.confirm": "Bestätigen",
|
||||
"messageModal.edit": "Bearbeiten"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Seitenleiste einklappen",
|
||||
"sideNav.demoActiveLabel": "Aktiv",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Unterhalb des Schwellenwerts ziehen, um automatisch einzuklappen",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatisches Einklappen",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Keine Animation für bessere Leistung",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Leistung",
|
||||
"sideNav.demoFeatureResizeDesc": "Ziehen, um die Panelbreite anzupassen",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexible Größenanpassung",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Zum Anzeigen der Umschalttaste mit der Maus darüberfahren",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Intelligenter Griff",
|
||||
"sideNav.demoFeaturesTitle": "Funktionen",
|
||||
"sideNav.demoHint": "Versuchen Sie, den Rand des Panels zu ziehen und die Umschalttaste zu verwenden ->",
|
||||
"sideNav.demoSubtitle": "Ein Seitenpanel im Arbeitsbereich-Stil mit ziehbarer Größenanpassung",
|
||||
"sideNav.demoTitle": "DraggableSideNav-Demo",
|
||||
"sideNav.expand": "Seitenleiste ausklappen"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Abbrechen",
|
||||
"common.confirm": "Bestätigen",
|
||||
"common.delete": "Löschen",
|
||||
"common.edit": "Bearbeiten",
|
||||
"editableMessage.addProps": "Eigenschaften hinzufügen",
|
||||
"editableMessage.delete": "Löschen",
|
||||
"editableMessage.input": "Eingabe",
|
||||
"editableMessage.inputPlaceholder": "Bitte Beispielinhalt für die Eingabe eingeben",
|
||||
"editableMessage.output": "Ausgabe",
|
||||
"editableMessage.outputPlaceholder": "Bitte Beispielinhalt für die Ausgabe eingeben",
|
||||
"editableMessage.system": "System",
|
||||
"emojiPicker.delete": "Löschen",
|
||||
"emojiPicker.draggerDesc": "Klicken oder Bild hierher ziehen, um es hochzuladen",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Sie können nur Bilddateien hochladen!",
|
||||
"emojiPicker.upload": "Hochladen",
|
||||
"emojiPicker.uploadBtn": "Zuschneiden und hochladen",
|
||||
"form.reset": "Zurücksetzen",
|
||||
"form.submit": "Absenden",
|
||||
"form.unsavedChanges": "Nicht gespeicherte Änderungen",
|
||||
"form.unsavedWarning": "Sie haben nicht gespeicherte Änderungen. Möchten Sie die Seite wirklich verlassen?",
|
||||
"hotkey.conflict": "Diese Tastenkombination steht in Konflikt mit einer bestehenden.",
|
||||
"hotkey.invalidCombination": "Die Tastenkombination muss eine Modifikatortaste (Strg, Alt, Umschalt) und nur eine normale Taste enthalten.",
|
||||
"hotkey.placeholder": "Tasten drücken, um Tastenkombination aufzuzeichnen",
|
||||
"hotkey.reset": "Auf Standard zurücksetzen",
|
||||
"messageModal.cancel": "Abbrechen",
|
||||
"messageModal.confirm": "Bestätigen",
|
||||
"messageModal.edit": "Bearbeiten",
|
||||
"sideNav.collapse": "Seitenleiste einklappen",
|
||||
"sideNav.demoActiveLabel": "Aktiv",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Unterhalb des Schwellenwerts ziehen, um automatisch einzuklappen",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatisches Einklappen",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Keine Animation für bessere Leistung",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Leistung",
|
||||
"sideNav.demoFeatureResizeDesc": "Ziehen, um die Panelbreite anzupassen",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexible Größenanpassung",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Zum Anzeigen der Umschalttaste mit der Maus darüberfahren",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Intelligenter Griff",
|
||||
"sideNav.demoFeaturesTitle": "Funktionen",
|
||||
"sideNav.demoHint": "Versuchen Sie, den Rand des Panels zu ziehen und die Umschalttaste zu verwenden ->",
|
||||
"sideNav.demoSubtitle": "Ein Seitenpanel im Arbeitsbereich-Stil mit ziehbarer Größenanpassung",
|
||||
"sideNav.demoTitle": "DraggableSideNav-Demo",
|
||||
"sideNav.expand": "Seitenleiste ausklappen",
|
||||
"tokenTag.overload": "Überlastung",
|
||||
"tokenTag.remained": "Verbleibend",
|
||||
"tokenTag.used": "Verbraucht"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Back to Home",
|
||||
"error.desc": "Give it a try later, or go back to the known world.",
|
||||
"error.retry": "Reload",
|
||||
"error.stack": "Error Stack",
|
||||
"error.title": "Oops, something went wrong..",
|
||||
"fetchError.detail": "Error details",
|
||||
"fetchError.title": "Request failed",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Overload",
|
||||
"tokenTag.remained": "Remained",
|
||||
"tokenTag.used": "Used"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Cancel",
|
||||
"common.confirm": "Confirm",
|
||||
"common.delete": "Delete",
|
||||
"common.edit": "Edit"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Add Props",
|
||||
"editableMessage.delete": "Delete",
|
||||
"editableMessage.input": "Input",
|
||||
"editableMessage.inputPlaceholder": "Please enter sample input content",
|
||||
"editableMessage.output": "Output",
|
||||
"editableMessage.outputPlaceholder": "Please enter sample output content",
|
||||
"editableMessage.system": "System"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Delete",
|
||||
"emojiPicker.draggerDesc": "Click or drag image to this area to upload",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "You can only upload image files!",
|
||||
"emojiPicker.upload": "Upload",
|
||||
"emojiPicker.uploadBtn": "Crop and upload"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Reset",
|
||||
"form.submit": "Submit",
|
||||
"form.unsavedChanges": "Unsaved changes",
|
||||
"form.unsavedWarning": "You have unsaved changes. Are you sure you want to leave?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "This hotkey conflicts with an existing one.",
|
||||
"hotkey.invalidCombination": "The hotkey must include a modifier key (Ctrl, Alt, Shift) and only one normal key.",
|
||||
"hotkey.placeholder": "Press keys to record hotkey",
|
||||
"hotkey.reset": "Reset to default"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Cancel",
|
||||
"messageModal.confirm": "Confirm",
|
||||
"messageModal.edit": "Edit"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Collapse sidebar",
|
||||
"sideNav.demoActiveLabel": "Active",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Drag below threshold to smart collapse",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Auto collapse",
|
||||
"sideNav.demoFeaturePerformanceDesc": "No animation overhead for better performance",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performance",
|
||||
"sideNav.demoFeatureResizeDesc": "Drag to adjust panel width",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexible resize",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Hover to show toggle button",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Smart handle",
|
||||
"sideNav.demoFeaturesTitle": "Features",
|
||||
"sideNav.demoHint": "Try dragging the panel edge and using the toggle button ->",
|
||||
"sideNav.demoSubtitle": "A workspace style side panel with draggable resize",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Expand sidebar"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Cancel",
|
||||
"common.confirm": "Confirm",
|
||||
"common.delete": "Delete",
|
||||
"common.edit": "Edit",
|
||||
"editableMessage.addProps": "Add Props",
|
||||
"editableMessage.delete": "Delete",
|
||||
"editableMessage.input": "Input",
|
||||
"editableMessage.inputPlaceholder": "Please enter sample input content",
|
||||
"editableMessage.output": "Output",
|
||||
"editableMessage.outputPlaceholder": "Please enter sample output content",
|
||||
"editableMessage.system": "System",
|
||||
"emojiPicker.delete": "Delete",
|
||||
"emojiPicker.draggerDesc": "Click or drag image to this area to upload",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "You can only upload image files!",
|
||||
"emojiPicker.upload": "Upload",
|
||||
"emojiPicker.uploadBtn": "Crop and upload",
|
||||
"form.reset": "Reset",
|
||||
"form.submit": "Submit",
|
||||
"form.unsavedChanges": "Unsaved changes",
|
||||
"form.unsavedWarning": "You have unsaved changes. Are you sure you want to leave?",
|
||||
"hotkey.conflict": "This hotkey conflicts with an existing one.",
|
||||
"hotkey.invalidCombination": "The hotkey must include a modifier key (Ctrl, Alt, Shift) and only one normal key.",
|
||||
"hotkey.placeholder": "Press keys to record hotkey",
|
||||
"hotkey.reset": "Reset to default",
|
||||
"messageModal.cancel": "Cancel",
|
||||
"messageModal.confirm": "Confirm",
|
||||
"messageModal.edit": "Edit",
|
||||
"sideNav.collapse": "Collapse sidebar",
|
||||
"sideNav.demoActiveLabel": "Active",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Drag below threshold to smart collapse",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Auto collapse",
|
||||
"sideNav.demoFeaturePerformanceDesc": "No animation overhead for better performance",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performance",
|
||||
"sideNav.demoFeatureResizeDesc": "Drag to adjust panel width",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexible resize",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Hover to show toggle button",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Smart handle",
|
||||
"sideNav.demoFeaturesTitle": "Features",
|
||||
"sideNav.demoHint": "Try dragging the panel edge and using the toggle button ->",
|
||||
"sideNav.demoSubtitle": "A workspace style side panel with draggable resize",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Expand sidebar",
|
||||
"tokenTag.overload": "Overload",
|
||||
"tokenTag.remained": "Remained",
|
||||
"tokenTag.used": "Used"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Volver al inicio",
|
||||
"error.desc": "Inténtalo más tarde o regresa al mundo conocido.",
|
||||
"error.retry": "Recargar",
|
||||
"error.stack": "Pila de errores",
|
||||
"error.title": "Vaya, algo salió mal...",
|
||||
"fetchError.detail": "Detalles del error",
|
||||
"fetchError.title": "La solicitud falló",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Sobrecarga",
|
||||
"tokenTag.remained": "Restante",
|
||||
"tokenTag.used": "Usado"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Cancelar",
|
||||
"common.confirm": "Confirmar",
|
||||
"common.delete": "Eliminar",
|
||||
"common.edit": "Editar"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Agregar propiedades",
|
||||
"editableMessage.delete": "Eliminar",
|
||||
"editableMessage.input": "Entrada",
|
||||
"editableMessage.inputPlaceholder": "Por favor, introduce contenido de entrada de ejemplo",
|
||||
"editableMessage.output": "Salida",
|
||||
"editableMessage.outputPlaceholder": "Por favor, introduce contenido de salida de ejemplo",
|
||||
"editableMessage.system": "Sistema"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Eliminar",
|
||||
"emojiPicker.draggerDesc": "Haz clic o arrastra una imagen a esta área para subirla",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "¡Solo puedes subir archivos de imagen!",
|
||||
"emojiPicker.upload": "Subir",
|
||||
"emojiPicker.uploadBtn": "Recortar y subir"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Restablecer",
|
||||
"form.submit": "Enviar",
|
||||
"form.unsavedChanges": "Cambios no guardados",
|
||||
"form.unsavedWarning": "Tienes cambios no guardados. ¿Estás seguro de que deseas salir?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Este atajo de teclado entra en conflicto con uno existente.",
|
||||
"hotkey.invalidCombination": "El atajo debe incluir una tecla modificadora (Ctrl, Alt, Shift) y solo una tecla normal.",
|
||||
"hotkey.placeholder": "Presiona teclas para registrar el atajo",
|
||||
"hotkey.reset": "Restablecer a valores predeterminados"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Cancelar",
|
||||
"messageModal.confirm": "Confirmar",
|
||||
"messageModal.edit": "Editar"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Colapsar barra lateral",
|
||||
"sideNav.demoActiveLabel": "Activo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Arrastra por debajo del umbral para colapsar automáticamente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Colapso automático",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Sin animaciones para un mejor rendimiento",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Rendimiento",
|
||||
"sideNav.demoFeatureResizeDesc": "Arrastra para ajustar el ancho del panel",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionamiento flexible",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Pasa el cursor para mostrar el botón de alternar",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Mango inteligente",
|
||||
"sideNav.demoFeaturesTitle": "Características",
|
||||
"sideNav.demoHint": "Prueba arrastrar el borde del panel y usar el botón de alternar ->",
|
||||
"sideNav.demoSubtitle": "Un panel lateral estilo espacio de trabajo con redimensionamiento arrastrable",
|
||||
"sideNav.demoTitle": "Demostración de DraggableSideNav",
|
||||
"sideNav.expand": "Expandir barra lateral"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Cancelar",
|
||||
"common.confirm": "Confirmar",
|
||||
"common.delete": "Eliminar",
|
||||
"common.edit": "Editar",
|
||||
"editableMessage.addProps": "Agregar propiedades",
|
||||
"editableMessage.delete": "Eliminar",
|
||||
"editableMessage.input": "Entrada",
|
||||
"editableMessage.inputPlaceholder": "Por favor, introduce contenido de entrada de ejemplo",
|
||||
"editableMessage.output": "Salida",
|
||||
"editableMessage.outputPlaceholder": "Por favor, introduce contenido de salida de ejemplo",
|
||||
"editableMessage.system": "Sistema",
|
||||
"emojiPicker.delete": "Eliminar",
|
||||
"emojiPicker.draggerDesc": "Haz clic o arrastra una imagen a esta área para subirla",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "¡Solo puedes subir archivos de imagen!",
|
||||
"emojiPicker.upload": "Subir",
|
||||
"emojiPicker.uploadBtn": "Recortar y subir",
|
||||
"form.reset": "Restablecer",
|
||||
"form.submit": "Enviar",
|
||||
"form.unsavedChanges": "Cambios no guardados",
|
||||
"form.unsavedWarning": "Tienes cambios no guardados. ¿Estás seguro de que deseas salir?",
|
||||
"hotkey.conflict": "Este atajo de teclado entra en conflicto con uno existente.",
|
||||
"hotkey.invalidCombination": "El atajo debe incluir una tecla modificadora (Ctrl, Alt, Shift) y solo una tecla normal.",
|
||||
"hotkey.placeholder": "Presiona teclas para registrar el atajo",
|
||||
"hotkey.reset": "Restablecer a valores predeterminados",
|
||||
"messageModal.cancel": "Cancelar",
|
||||
"messageModal.confirm": "Confirmar",
|
||||
"messageModal.edit": "Editar",
|
||||
"sideNav.collapse": "Colapsar barra lateral",
|
||||
"sideNav.demoActiveLabel": "Activo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Arrastra por debajo del umbral para colapsar automáticamente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Colapso automático",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Sin animaciones para un mejor rendimiento",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Rendimiento",
|
||||
"sideNav.demoFeatureResizeDesc": "Arrastra para ajustar el ancho del panel",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionamiento flexible",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Pasa el cursor para mostrar el botón de alternar",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Mango inteligente",
|
||||
"sideNav.demoFeaturesTitle": "Características",
|
||||
"sideNav.demoHint": "Prueba arrastrar el borde del panel y usar el botón de alternar ->",
|
||||
"sideNav.demoSubtitle": "Un panel lateral estilo espacio de trabajo con redimensionamiento arrastrable",
|
||||
"sideNav.demoTitle": "Demostración de DraggableSideNav",
|
||||
"sideNav.expand": "Expandir barra lateral",
|
||||
"tokenTag.overload": "Sobrecarga",
|
||||
"tokenTag.remained": "Restante",
|
||||
"tokenTag.used": "Usado"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "بازگشت به خانه",
|
||||
"error.desc": "بعداً دوباره امتحان کنید یا به دنیای آشنا بازگردید.",
|
||||
"error.retry": "بارگذاری مجدد",
|
||||
"error.stack": "پشته خطا",
|
||||
"error.title": "اوه، مشکلی پیش آمده...",
|
||||
"fetchError.detail": "جزئیات خطا",
|
||||
"fetchError.title": "درخواست ناموفق بود",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "آواتار",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "بیشبار",
|
||||
"tokenTag.remained": "باقیمانده",
|
||||
"tokenTag.used": "استفادهشده"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "لغو",
|
||||
"common.confirm": "تأیید",
|
||||
"common.delete": "حذف",
|
||||
"common.edit": "ویرایش"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "افزودن ویژگیها",
|
||||
"editableMessage.delete": "حذف",
|
||||
"editableMessage.input": "ورودی",
|
||||
"editableMessage.inputPlaceholder": "لطفاً محتوای نمونه ورودی را وارد کنید",
|
||||
"editableMessage.output": "خروجی",
|
||||
"editableMessage.outputPlaceholder": "لطفاً محتوای نمونه خروجی را وارد کنید",
|
||||
"editableMessage.system": "سیستم"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "حذف",
|
||||
"emojiPicker.draggerDesc": "برای بارگذاری، تصویر را کلیک یا به این ناحیه بکشید",
|
||||
"emojiPicker.emoji": "ایموجی",
|
||||
"emojiPicker.fileTypeError": "فقط میتوانید فایلهای تصویری بارگذاری کنید!",
|
||||
"emojiPicker.upload": "بارگذاری",
|
||||
"emojiPicker.uploadBtn": "برش و بارگذاری"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "بازنشانی",
|
||||
"form.submit": "ارسال",
|
||||
"form.unsavedChanges": "تغییرات ذخیرهنشده",
|
||||
"form.unsavedWarning": "تغییرات ذخیرهنشده دارید. آیا مطمئن هستید که میخواهید خارج شوید؟"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "این کلید میانبر با یک کلید دیگر تداخل دارد.",
|
||||
"hotkey.invalidCombination": "کلید میانبر باید شامل یک کلید ترکیبی (Ctrl، Alt، Shift) و فقط یک کلید عادی باشد.",
|
||||
"hotkey.placeholder": "برای ضبط کلید میانبر، کلیدها را فشار دهید",
|
||||
"hotkey.reset": "بازنشانی به پیشفرض"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "لغو",
|
||||
"messageModal.confirm": "تأیید",
|
||||
"messageModal.edit": "ویرایش"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "بستن نوار کناری",
|
||||
"sideNav.demoActiveLabel": "فعال",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "برای جمع شدن هوشمند، کمتر از حد آستانه بکشید",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "جمع شدن خودکار",
|
||||
"sideNav.demoFeaturePerformanceDesc": "بدون انیمیشن برای عملکرد بهتر",
|
||||
"sideNav.demoFeaturePerformanceTitle": "عملکرد",
|
||||
"sideNav.demoFeatureResizeDesc": "برای تنظیم عرض پنل بکشید",
|
||||
"sideNav.demoFeatureResizeTitle": "تغییر اندازه انعطافپذیر",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "برای نمایش دکمه تغییر وضعیت، نشانگر را نگه دارید",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "دسته هوشمند",
|
||||
"sideNav.demoFeaturesTitle": "ویژگیها",
|
||||
"sideNav.demoHint": "لبه پنل را بکشید و از دکمه تغییر وضعیت استفاده کنید ->",
|
||||
"sideNav.demoSubtitle": "پنل کناری به سبک محیط کاری با قابلیت تغییر اندازه با کشیدن",
|
||||
"sideNav.demoTitle": "دموی DraggableSideNav",
|
||||
"sideNav.expand": "باز کردن نوار کناری"
|
||||
}
|
||||
"chat.avatar": "آواتار",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "لغو",
|
||||
"common.confirm": "تأیید",
|
||||
"common.delete": "حذف",
|
||||
"common.edit": "ویرایش",
|
||||
"editableMessage.addProps": "افزودن ویژگیها",
|
||||
"editableMessage.delete": "حذف",
|
||||
"editableMessage.input": "ورودی",
|
||||
"editableMessage.inputPlaceholder": "لطفاً محتوای نمونه ورودی را وارد کنید",
|
||||
"editableMessage.output": "خروجی",
|
||||
"editableMessage.outputPlaceholder": "لطفاً محتوای نمونه خروجی را وارد کنید",
|
||||
"editableMessage.system": "سیستم",
|
||||
"emojiPicker.delete": "حذف",
|
||||
"emojiPicker.draggerDesc": "برای بارگذاری، تصویر را کلیک یا به این ناحیه بکشید",
|
||||
"emojiPicker.emoji": "ایموجی",
|
||||
"emojiPicker.fileTypeError": "فقط میتوانید فایلهای تصویری بارگذاری کنید!",
|
||||
"emojiPicker.upload": "بارگذاری",
|
||||
"emojiPicker.uploadBtn": "برش و بارگذاری",
|
||||
"form.reset": "بازنشانی",
|
||||
"form.submit": "ارسال",
|
||||
"form.unsavedChanges": "تغییرات ذخیرهنشده",
|
||||
"form.unsavedWarning": "تغییرات ذخیرهنشده دارید. آیا مطمئن هستید که میخواهید خارج شوید؟",
|
||||
"hotkey.conflict": "این کلید میانبر با یک کلید دیگر تداخل دارد.",
|
||||
"hotkey.invalidCombination": "کلید میانبر باید شامل یک کلید ترکیبی (Ctrl، Alt، Shift) و فقط یک کلید عادی باشد.",
|
||||
"hotkey.placeholder": "برای ضبط کلید میانبر، کلیدها را فشار دهید",
|
||||
"hotkey.reset": "بازنشانی به پیشفرض",
|
||||
"messageModal.cancel": "لغو",
|
||||
"messageModal.confirm": "تأیید",
|
||||
"messageModal.edit": "ویرایش",
|
||||
"sideNav.collapse": "بستن نوار کناری",
|
||||
"sideNav.demoActiveLabel": "فعال",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "برای جمع شدن هوشمند، کمتر از حد آستانه بکشید",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "جمع شدن خودکار",
|
||||
"sideNav.demoFeaturePerformanceDesc": "بدون انیمیشن برای عملکرد بهتر",
|
||||
"sideNav.demoFeaturePerformanceTitle": "عملکرد",
|
||||
"sideNav.demoFeatureResizeDesc": "برای تنظیم عرض پنل بکشید",
|
||||
"sideNav.demoFeatureResizeTitle": "تغییر اندازه انعطافپذیر",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "برای نمایش دکمه تغییر وضعیت، نشانگر را نگه دارید",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "دسته هوشمند",
|
||||
"sideNav.demoFeaturesTitle": "ویژگیها",
|
||||
"sideNav.demoHint": "لبه پنل را بکشید و از دکمه تغییر وضعیت استفاده کنید ->",
|
||||
"sideNav.demoSubtitle": "پنل کناری به سبک محیط کاری با قابلیت تغییر اندازه با کشیدن",
|
||||
"sideNav.demoTitle": "دموی DraggableSideNav",
|
||||
"sideNav.expand": "باز کردن نوار کناری",
|
||||
"tokenTag.overload": "بیشبار",
|
||||
"tokenTag.remained": "باقیمانده",
|
||||
"tokenTag.used": "استفادهشده"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Retour à l'accueil",
|
||||
"error.desc": "Réessayez plus tard ou revenez dans un territoire connu.",
|
||||
"error.retry": "Recharger",
|
||||
"error.stack": "Pile d'erreurs",
|
||||
"error.title": "Oups, une erreur s'est produite...",
|
||||
"fetchError.detail": "Détails de l'erreur",
|
||||
"fetchError.title": "Échec de la requête",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Surcharge",
|
||||
"tokenTag.remained": "Restant",
|
||||
"tokenTag.used": "Utilisé"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Annuler",
|
||||
"common.confirm": "Confirmer",
|
||||
"common.delete": "Supprimer",
|
||||
"common.edit": "Modifier"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Ajouter des propriétés",
|
||||
"editableMessage.delete": "Supprimer",
|
||||
"editableMessage.input": "Entrée",
|
||||
"editableMessage.inputPlaceholder": "Veuillez saisir un contenu d'entrée exemple",
|
||||
"editableMessage.output": "Sortie",
|
||||
"editableMessage.outputPlaceholder": "Veuillez saisir un contenu de sortie exemple",
|
||||
"editableMessage.system": "Système"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Supprimer",
|
||||
"emojiPicker.draggerDesc": "Cliquez ou faites glisser une image ici pour la téléverser",
|
||||
"emojiPicker.emoji": "Émoji",
|
||||
"emojiPicker.fileTypeError": "Vous ne pouvez téléverser que des fichiers image !",
|
||||
"emojiPicker.upload": "Téléverser",
|
||||
"emojiPicker.uploadBtn": "Rogner et téléverser"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Réinitialiser",
|
||||
"form.submit": "Soumettre",
|
||||
"form.unsavedChanges": "Modifications non enregistrées",
|
||||
"form.unsavedWarning": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter ?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Ce raccourci est en conflit avec un autre existant.",
|
||||
"hotkey.invalidCombination": "Le raccourci doit inclure une touche de modification (Ctrl, Alt, Maj) et une seule touche normale.",
|
||||
"hotkey.placeholder": "Appuyez sur les touches pour enregistrer le raccourci",
|
||||
"hotkey.reset": "Réinitialiser par défaut"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Annuler",
|
||||
"messageModal.confirm": "Confirmer",
|
||||
"messageModal.edit": "Modifier"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Réduire la barre latérale",
|
||||
"sideNav.demoActiveLabel": "Actif",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Faites glisser sous le seuil pour réduire intelligemment",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Réduction automatique",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Aucune animation pour de meilleures performances",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performance",
|
||||
"sideNav.demoFeatureResizeDesc": "Faites glisser pour ajuster la largeur du panneau",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionnement flexible",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Survolez pour afficher le bouton de bascule",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Poignée intelligente",
|
||||
"sideNav.demoFeaturesTitle": "Fonctionnalités",
|
||||
"sideNav.demoHint": "Essayez de faire glisser le bord du panneau et d'utiliser le bouton de bascule ->",
|
||||
"sideNav.demoSubtitle": "Un panneau latéral de type espace de travail avec redimensionnement par glissement",
|
||||
"sideNav.demoTitle": "Démo DraggableSideNav",
|
||||
"sideNav.expand": "Développer la barre latérale"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Annuler",
|
||||
"common.confirm": "Confirmer",
|
||||
"common.delete": "Supprimer",
|
||||
"common.edit": "Modifier",
|
||||
"editableMessage.addProps": "Ajouter des propriétés",
|
||||
"editableMessage.delete": "Supprimer",
|
||||
"editableMessage.input": "Entrée",
|
||||
"editableMessage.inputPlaceholder": "Veuillez saisir un contenu d'entrée exemple",
|
||||
"editableMessage.output": "Sortie",
|
||||
"editableMessage.outputPlaceholder": "Veuillez saisir un contenu de sortie exemple",
|
||||
"editableMessage.system": "Système",
|
||||
"emojiPicker.delete": "Supprimer",
|
||||
"emojiPicker.draggerDesc": "Cliquez ou faites glisser une image ici pour la téléverser",
|
||||
"emojiPicker.emoji": "Émoji",
|
||||
"emojiPicker.fileTypeError": "Vous ne pouvez téléverser que des fichiers image !",
|
||||
"emojiPicker.upload": "Téléverser",
|
||||
"emojiPicker.uploadBtn": "Rogner et téléverser",
|
||||
"form.reset": "Réinitialiser",
|
||||
"form.submit": "Soumettre",
|
||||
"form.unsavedChanges": "Modifications non enregistrées",
|
||||
"form.unsavedWarning": "Vous avez des modifications non enregistrées. Êtes-vous sûr de vouloir quitter ?",
|
||||
"hotkey.conflict": "Ce raccourci est en conflit avec un autre existant.",
|
||||
"hotkey.invalidCombination": "Le raccourci doit inclure une touche de modification (Ctrl, Alt, Maj) et une seule touche normale.",
|
||||
"hotkey.placeholder": "Appuyez sur les touches pour enregistrer le raccourci",
|
||||
"hotkey.reset": "Réinitialiser par défaut",
|
||||
"messageModal.cancel": "Annuler",
|
||||
"messageModal.confirm": "Confirmer",
|
||||
"messageModal.edit": "Modifier",
|
||||
"sideNav.collapse": "Réduire la barre latérale",
|
||||
"sideNav.demoActiveLabel": "Actif",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Faites glisser sous le seuil pour réduire intelligemment",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Réduction automatique",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Aucune animation pour de meilleures performances",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performance",
|
||||
"sideNav.demoFeatureResizeDesc": "Faites glisser pour ajuster la largeur du panneau",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionnement flexible",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Survolez pour afficher le bouton de bascule",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Poignée intelligente",
|
||||
"sideNav.demoFeaturesTitle": "Fonctionnalités",
|
||||
"sideNav.demoHint": "Essayez de faire glisser le bord du panneau et d'utiliser le bouton de bascule ->",
|
||||
"sideNav.demoSubtitle": "Un panneau latéral de type espace de travail avec redimensionnement par glissement",
|
||||
"sideNav.demoTitle": "Démo DraggableSideNav",
|
||||
"sideNav.expand": "Développer la barre latérale",
|
||||
"tokenTag.overload": "Surcharge",
|
||||
"tokenTag.remained": "Restant",
|
||||
"tokenTag.used": "Utilisé"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Torna alla Home",
|
||||
"error.desc": "Riprova più tardi o torna al mondo conosciuto.",
|
||||
"error.retry": "Ricarica",
|
||||
"error.stack": "Stack degli errori",
|
||||
"error.title": "Ops, qualcosa è andato storto...",
|
||||
"fetchError.detail": "Dettagli dell'errore",
|
||||
"fetchError.title": "Richiesta fallita",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Sovraccarico",
|
||||
"tokenTag.remained": "Rimanenti",
|
||||
"tokenTag.used": "Utilizzati"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Annulla",
|
||||
"common.confirm": "Conferma",
|
||||
"common.delete": "Elimina",
|
||||
"common.edit": "Modifica"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Aggiungi Proprietà",
|
||||
"editableMessage.delete": "Elimina",
|
||||
"editableMessage.input": "Input",
|
||||
"editableMessage.inputPlaceholder": "Inserisci un contenuto di input di esempio",
|
||||
"editableMessage.output": "Output",
|
||||
"editableMessage.outputPlaceholder": "Inserisci un contenuto di output di esempio",
|
||||
"editableMessage.system": "Sistema"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Elimina",
|
||||
"emojiPicker.draggerDesc": "Clicca o trascina un'immagine in quest'area per caricarla",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Puoi caricare solo file immagine!",
|
||||
"emojiPicker.upload": "Carica",
|
||||
"emojiPicker.uploadBtn": "Ritaglia e carica"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Reimposta",
|
||||
"form.submit": "Invia",
|
||||
"form.unsavedChanges": "Modifiche non salvate",
|
||||
"form.unsavedWarning": "Hai modifiche non salvate. Sei sicuro di voler uscire?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Questa scorciatoia è in conflitto con un'altra esistente.",
|
||||
"hotkey.invalidCombination": "La scorciatoia deve includere un tasto modificatore (Ctrl, Alt, Shift) e solo un tasto normale.",
|
||||
"hotkey.placeholder": "Premi i tasti per registrare la scorciatoia",
|
||||
"hotkey.reset": "Ripristina predefiniti"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Annulla",
|
||||
"messageModal.confirm": "Conferma",
|
||||
"messageModal.edit": "Modifica"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Comprimi barra laterale",
|
||||
"sideNav.demoActiveLabel": "Attivo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Trascina sotto la soglia per comprimere automaticamente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Comprimere automaticamente",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Nessuna animazione per prestazioni migliori",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Prestazioni",
|
||||
"sideNav.demoFeatureResizeDesc": "Trascina per regolare la larghezza del pannello",
|
||||
"sideNav.demoFeatureResizeTitle": "Ridimensionamento flessibile",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Passa il mouse per mostrare il pulsante di attivazione",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Gestione intelligente",
|
||||
"sideNav.demoFeaturesTitle": "Funzionalità",
|
||||
"sideNav.demoHint": "Prova a trascinare il bordo del pannello e a usare il pulsante di attivazione ->",
|
||||
"sideNav.demoSubtitle": "Un pannello laterale in stile workspace con ridimensionamento trascinabile",
|
||||
"sideNav.demoTitle": "Demo DraggableSideNav",
|
||||
"sideNav.expand": "Espandi barra laterale"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Annulla",
|
||||
"common.confirm": "Conferma",
|
||||
"common.delete": "Elimina",
|
||||
"common.edit": "Modifica",
|
||||
"editableMessage.addProps": "Aggiungi Proprietà",
|
||||
"editableMessage.delete": "Elimina",
|
||||
"editableMessage.input": "Input",
|
||||
"editableMessage.inputPlaceholder": "Inserisci un contenuto di input di esempio",
|
||||
"editableMessage.output": "Output",
|
||||
"editableMessage.outputPlaceholder": "Inserisci un contenuto di output di esempio",
|
||||
"editableMessage.system": "Sistema",
|
||||
"emojiPicker.delete": "Elimina",
|
||||
"emojiPicker.draggerDesc": "Clicca o trascina un'immagine in quest'area per caricarla",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Puoi caricare solo file immagine!",
|
||||
"emojiPicker.upload": "Carica",
|
||||
"emojiPicker.uploadBtn": "Ritaglia e carica",
|
||||
"form.reset": "Reimposta",
|
||||
"form.submit": "Invia",
|
||||
"form.unsavedChanges": "Modifiche non salvate",
|
||||
"form.unsavedWarning": "Hai modifiche non salvate. Sei sicuro di voler uscire?",
|
||||
"hotkey.conflict": "Questa scorciatoia è in conflitto con un'altra esistente.",
|
||||
"hotkey.invalidCombination": "La scorciatoia deve includere un tasto modificatore (Ctrl, Alt, Shift) e solo un tasto normale.",
|
||||
"hotkey.placeholder": "Premi i tasti per registrare la scorciatoia",
|
||||
"hotkey.reset": "Ripristina predefiniti",
|
||||
"messageModal.cancel": "Annulla",
|
||||
"messageModal.confirm": "Conferma",
|
||||
"messageModal.edit": "Modifica",
|
||||
"sideNav.collapse": "Comprimi barra laterale",
|
||||
"sideNav.demoActiveLabel": "Attivo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Trascina sotto la soglia per comprimere automaticamente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Comprimere automaticamente",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Nessuna animazione per prestazioni migliori",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Prestazioni",
|
||||
"sideNav.demoFeatureResizeDesc": "Trascina per regolare la larghezza del pannello",
|
||||
"sideNav.demoFeatureResizeTitle": "Ridimensionamento flessibile",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Passa il mouse per mostrare il pulsante di attivazione",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Gestione intelligente",
|
||||
"sideNav.demoFeaturesTitle": "Funzionalità",
|
||||
"sideNav.demoHint": "Prova a trascinare il bordo del pannello e a usare il pulsante di attivazione ->",
|
||||
"sideNav.demoSubtitle": "Un pannello laterale in stile workspace con ridimensionamento trascinabile",
|
||||
"sideNav.demoTitle": "Demo DraggableSideNav",
|
||||
"sideNav.expand": "Espandi barra laterale",
|
||||
"tokenTag.overload": "Sovraccarico",
|
||||
"tokenTag.remained": "Rimanenti",
|
||||
"tokenTag.used": "Utilizzati"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "ホームに戻る",
|
||||
"error.desc": "ページが一時的に利用できません。ホームに戻るか、時間を置いて再試行してください(設定は失われません)",
|
||||
"error.retry": "再読み込み",
|
||||
"error.stack": "エラースタック",
|
||||
"error.title": "ページが一時的に利用できません",
|
||||
"fetchError.detail": "詳細を見る",
|
||||
"fetchError.title": "リクエストが完了できませんでした",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "アバター",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "超過",
|
||||
"tokenTag.remained": "残り",
|
||||
"tokenTag.used": "使用"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "キャンセル",
|
||||
"common.confirm": "確認",
|
||||
"common.delete": "削除",
|
||||
"common.edit": "編集"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "プロパティを追加",
|
||||
"editableMessage.delete": "削除",
|
||||
"editableMessage.input": "入力",
|
||||
"editableMessage.inputPlaceholder": "サンプル入力内容を入力してください",
|
||||
"editableMessage.output": "出力",
|
||||
"editableMessage.outputPlaceholder": "サンプル出力内容を入力してください",
|
||||
"editableMessage.system": "システム"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "削除",
|
||||
"emojiPicker.draggerDesc": "クリックまたは画像をこの領域にドラッグしてアップロード",
|
||||
"emojiPicker.emoji": "絵文字",
|
||||
"emojiPicker.fileTypeError": "画像ファイルのみアップロードできます!",
|
||||
"emojiPicker.upload": "アップロード",
|
||||
"emojiPicker.uploadBtn": "切り抜いてアップロード"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "リセット",
|
||||
"form.submit": "送信",
|
||||
"form.unsavedChanges": "未保存の変更",
|
||||
"form.unsavedWarning": "未保存の変更があります。移動してもよろしいですか?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "このショートカットは既存のものと競合しています。",
|
||||
"hotkey.invalidCombination": "ショートカットは修飾キー(Ctrl、Alt、Shift)を含み、通常キーは1つのみです。",
|
||||
"hotkey.placeholder": "キーを押してショートカットを記録",
|
||||
"hotkey.reset": "既定にリセット"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "キャンセル",
|
||||
"messageModal.confirm": "確認",
|
||||
"messageModal.edit": "編集"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "サイドバーを折りたたむ",
|
||||
"sideNav.demoActiveLabel": "アクティブ",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "しきい値以下にドラッグすると自動的に折りたたみ",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自動折りたたみ",
|
||||
"sideNav.demoFeaturePerformanceDesc": "アニメーション負荷なしで高性能",
|
||||
"sideNav.demoFeaturePerformanceTitle": "パフォーマンス",
|
||||
"sideNav.demoFeatureResizeDesc": "ドラッグしてパネル幅を調整",
|
||||
"sideNav.demoFeatureResizeTitle": "柔軟なサイズ変更",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "ホバーで切替ボタンを表示",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "スマートハンドル",
|
||||
"sideNav.demoFeaturesTitle": "機能",
|
||||
"sideNav.demoHint": "パネルの端をドラッグし、切替ボタンを試してください ->",
|
||||
"sideNav.demoSubtitle": "ドラッグでサイズ変更できるワークスペース風サイドパネル",
|
||||
"sideNav.demoTitle": "DraggableSideNav デモ",
|
||||
"sideNav.expand": "サイドバーを展開"
|
||||
}
|
||||
"chat.avatar": "アバター",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "キャンセル",
|
||||
"common.confirm": "確認",
|
||||
"common.delete": "削除",
|
||||
"common.edit": "編集",
|
||||
"editableMessage.addProps": "プロパティを追加",
|
||||
"editableMessage.delete": "削除",
|
||||
"editableMessage.input": "入力",
|
||||
"editableMessage.inputPlaceholder": "サンプル入力内容を入力してください",
|
||||
"editableMessage.output": "出力",
|
||||
"editableMessage.outputPlaceholder": "サンプル出力内容を入力してください",
|
||||
"editableMessage.system": "システム",
|
||||
"emojiPicker.delete": "削除",
|
||||
"emojiPicker.draggerDesc": "クリックまたは画像をこの領域にドラッグしてアップロード",
|
||||
"emojiPicker.emoji": "絵文字",
|
||||
"emojiPicker.fileTypeError": "画像ファイルのみアップロードできます!",
|
||||
"emojiPicker.upload": "アップロード",
|
||||
"emojiPicker.uploadBtn": "切り抜いてアップロード",
|
||||
"form.reset": "リセット",
|
||||
"form.submit": "送信",
|
||||
"form.unsavedChanges": "未保存の変更",
|
||||
"form.unsavedWarning": "未保存の変更があります。移動してもよろしいですか?",
|
||||
"hotkey.conflict": "このショートカットは既存のものと競合しています。",
|
||||
"hotkey.invalidCombination": "ショートカットは修飾キー(Ctrl、Alt、Shift)を含み、通常キーは1つのみです。",
|
||||
"hotkey.placeholder": "キーを押してショートカットを記録",
|
||||
"hotkey.reset": "既定にリセット",
|
||||
"messageModal.cancel": "キャンセル",
|
||||
"messageModal.confirm": "確認",
|
||||
"messageModal.edit": "編集",
|
||||
"sideNav.collapse": "サイドバーを折りたたむ",
|
||||
"sideNav.demoActiveLabel": "アクティブ",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "しきい値以下にドラッグすると自動的に折りたたみ",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自動折りたたみ",
|
||||
"sideNav.demoFeaturePerformanceDesc": "アニメーション負荷なしで高性能",
|
||||
"sideNav.demoFeaturePerformanceTitle": "パフォーマンス",
|
||||
"sideNav.demoFeatureResizeDesc": "ドラッグしてパネル幅を調整",
|
||||
"sideNav.demoFeatureResizeTitle": "柔軟なサイズ変更",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "ホバーで切替ボタンを表示",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "スマートハンドル",
|
||||
"sideNav.demoFeaturesTitle": "機能",
|
||||
"sideNav.demoHint": "パネルの端をドラッグし、切替ボタンを試してください ->",
|
||||
"sideNav.demoSubtitle": "ドラッグでサイズ変更できるワークスペース風サイドパネル",
|
||||
"sideNav.demoTitle": "DraggableSideNav デモ",
|
||||
"sideNav.expand": "サイドバーを展開",
|
||||
"tokenTag.overload": "超過",
|
||||
"tokenTag.remained": "残り",
|
||||
"tokenTag.used": "使用"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "홈으로 돌아가기",
|
||||
"error.desc": "페이지를 일시적으로 사용할 수 없습니다. 홈으로 돌아가거나 잠시 후 다시 시도하세요(설정은 손실되지 않습니다)",
|
||||
"error.retry": "다시 로드",
|
||||
"error.stack": "오류 스택",
|
||||
"error.title": "페이지를 일시적으로 사용할 수 없습니다",
|
||||
"fetchError.detail": "자세히 보기",
|
||||
"fetchError.title": "요청을 완료할 수 없습니다",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "아바타",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "초과",
|
||||
"tokenTag.remained": "남음",
|
||||
"tokenTag.used": "사용"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "취소",
|
||||
"common.confirm": "확인",
|
||||
"common.delete": "삭제",
|
||||
"common.edit": "편집"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "속성 추가",
|
||||
"editableMessage.delete": "삭제",
|
||||
"editableMessage.input": "입력",
|
||||
"editableMessage.inputPlaceholder": "예시 입력 내용을 입력하세요",
|
||||
"editableMessage.output": "출력",
|
||||
"editableMessage.outputPlaceholder": "예시 출력 내용을 입력하세요",
|
||||
"editableMessage.system": "시스템"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "삭제",
|
||||
"emojiPicker.draggerDesc": "클릭하거나 이미지를 이 영역으로 끌어다 놓아 업로드하세요",
|
||||
"emojiPicker.emoji": "이모지",
|
||||
"emojiPicker.fileTypeError": "이미지 파일만 업로드할 수 있습니다!",
|
||||
"emojiPicker.upload": "업로드",
|
||||
"emojiPicker.uploadBtn": "자르고 업로드"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "재설정",
|
||||
"form.submit": "제출",
|
||||
"form.unsavedChanges": "저장되지 않은 변경사항",
|
||||
"form.unsavedWarning": "저장되지 않은 변경사항이 있습니다. 정말 나가시겠습니까?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "이 단축키는 기존 단축키와 충돌합니다.",
|
||||
"hotkey.invalidCombination": "단축키는 수정 키(Ctrl, Alt, Shift)를 포함해야 하며 일반 키는 하나만 포함할 수 있습니다.",
|
||||
"hotkey.placeholder": "키를 눌러 단축키를 기록하세요",
|
||||
"hotkey.reset": "기본값으로 재설정"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "취소",
|
||||
"messageModal.confirm": "확인",
|
||||
"messageModal.edit": "편집"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "사이드바 접기",
|
||||
"sideNav.demoActiveLabel": "활성",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "임계값 아래로 드래그하면 스마트하게 접힘",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "자동 접기",
|
||||
"sideNav.demoFeaturePerformanceDesc": "애니메이션 오버헤드 없이 더 나은 성능",
|
||||
"sideNav.demoFeaturePerformanceTitle": "성능",
|
||||
"sideNav.demoFeatureResizeDesc": "드래그하여 패널 너비 조정",
|
||||
"sideNav.demoFeatureResizeTitle": "유연한 크기 조정",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "호버하면 토글 버튼 표시",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "스마트 핸들",
|
||||
"sideNav.demoFeaturesTitle": "기능",
|
||||
"sideNav.demoHint": "패널 가장자리를 드래그하고 토글 버튼을 사용해 보세요 ->",
|
||||
"sideNav.demoSubtitle": "드래그로 크기를 조절하는 워크스페이스 스타일 사이드 패널",
|
||||
"sideNav.demoTitle": "DraggableSideNav 데모",
|
||||
"sideNav.expand": "사이드바 펼치기"
|
||||
}
|
||||
"chat.avatar": "아바타",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "취소",
|
||||
"common.confirm": "확인",
|
||||
"common.delete": "삭제",
|
||||
"common.edit": "편집",
|
||||
"editableMessage.addProps": "속성 추가",
|
||||
"editableMessage.delete": "삭제",
|
||||
"editableMessage.input": "입력",
|
||||
"editableMessage.inputPlaceholder": "예시 입력 내용을 입력하세요",
|
||||
"editableMessage.output": "출력",
|
||||
"editableMessage.outputPlaceholder": "예시 출력 내용을 입력하세요",
|
||||
"editableMessage.system": "시스템",
|
||||
"emojiPicker.delete": "삭제",
|
||||
"emojiPicker.draggerDesc": "클릭하거나 이미지를 이 영역으로 끌어다 놓아 업로드하세요",
|
||||
"emojiPicker.emoji": "이모지",
|
||||
"emojiPicker.fileTypeError": "이미지 파일만 업로드할 수 있습니다!",
|
||||
"emojiPicker.upload": "업로드",
|
||||
"emojiPicker.uploadBtn": "자르고 업로드",
|
||||
"form.reset": "재설정",
|
||||
"form.submit": "제출",
|
||||
"form.unsavedChanges": "저장되지 않은 변경사항",
|
||||
"form.unsavedWarning": "저장되지 않은 변경사항이 있습니다. 정말 나가시겠습니까?",
|
||||
"hotkey.conflict": "이 단축키는 기존 단축키와 충돌합니다.",
|
||||
"hotkey.invalidCombination": "단축키는 수정 키(Ctrl, Alt, Shift)를 포함해야 하며 일반 키는 하나만 포함할 수 있습니다.",
|
||||
"hotkey.placeholder": "키를 눌러 단축키를 기록하세요",
|
||||
"hotkey.reset": "기본값으로 재설정",
|
||||
"messageModal.cancel": "취소",
|
||||
"messageModal.confirm": "확인",
|
||||
"messageModal.edit": "편집",
|
||||
"sideNav.collapse": "사이드바 접기",
|
||||
"sideNav.demoActiveLabel": "활성",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "임계값 아래로 드래그하면 스마트하게 접힘",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "자동 접기",
|
||||
"sideNav.demoFeaturePerformanceDesc": "애니메이션 오버헤드 없이 더 나은 성능",
|
||||
"sideNav.demoFeaturePerformanceTitle": "성능",
|
||||
"sideNav.demoFeatureResizeDesc": "드래그하여 패널 너비 조정",
|
||||
"sideNav.demoFeatureResizeTitle": "유연한 크기 조정",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "호버하면 토글 버튼 표시",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "스마트 핸들",
|
||||
"sideNav.demoFeaturesTitle": "기능",
|
||||
"sideNav.demoHint": "패널 가장자리를 드래그하고 토글 버튼을 사용해 보세요 ->",
|
||||
"sideNav.demoSubtitle": "드래그로 크기를 조절하는 워크스페이스 스타일 사이드 패널",
|
||||
"sideNav.demoTitle": "DraggableSideNav 데모",
|
||||
"sideNav.expand": "사이드바 펼치기",
|
||||
"tokenTag.overload": "초과",
|
||||
"tokenTag.remained": "남음",
|
||||
"tokenTag.used": "사용"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Terug naar home",
|
||||
"error.desc": "Probeer het later opnieuw of keer terug naar de bekende wereld.",
|
||||
"error.retry": "Opnieuw laden",
|
||||
"error.stack": "Foutstack",
|
||||
"error.title": "Oeps, er ging iets mis..",
|
||||
"fetchError.detail": "Foutdetails",
|
||||
"fetchError.title": "Verzoek mislukt",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Overbelasting",
|
||||
"tokenTag.remained": "Resterend",
|
||||
"tokenTag.used": "Gebruikt"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Annuleren",
|
||||
"common.confirm": "Bevestigen",
|
||||
"common.delete": "Verwijderen",
|
||||
"common.edit": "Bewerken"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Eigenschappen toevoegen",
|
||||
"editableMessage.delete": "Verwijderen",
|
||||
"editableMessage.input": "Invoer",
|
||||
"editableMessage.inputPlaceholder": "Voer voorbeeldinhoud in",
|
||||
"editableMessage.output": "Uitvoer",
|
||||
"editableMessage.outputPlaceholder": "Voer voorbeelduitvoer in",
|
||||
"editableMessage.system": "Systeem"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Verwijderen",
|
||||
"emojiPicker.draggerDesc": "Klik of sleep een afbeelding naar dit gebied om te uploaden",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Je kunt alleen afbeeldingsbestanden uploaden!",
|
||||
"emojiPicker.upload": "Uploaden",
|
||||
"emojiPicker.uploadBtn": "Bijsnijden en uploaden"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Resetten",
|
||||
"form.submit": "Verzenden",
|
||||
"form.unsavedChanges": "Niet-opgeslagen wijzigingen",
|
||||
"form.unsavedWarning": "Je hebt niet-opgeslagen wijzigingen. Weet je zeker dat je wilt verlaten?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Deze sneltoets conflicteert met een bestaande.",
|
||||
"hotkey.invalidCombination": "De sneltoets moet een modificatietoets (Ctrl, Alt, Shift) en slechts één normale toets bevatten.",
|
||||
"hotkey.placeholder": "Druk op toetsen om sneltoets op te nemen",
|
||||
"hotkey.reset": "Herstellen naar standaard"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Annuleren",
|
||||
"messageModal.confirm": "Bevestigen",
|
||||
"messageModal.edit": "Bewerken"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Zijbalk samenvouwen",
|
||||
"sideNav.demoActiveLabel": "Actief",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Sleep onder de drempel om slim samen te vouwen",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatisch samenvouwen",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Geen animatiebelasting voor betere prestaties",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Prestaties",
|
||||
"sideNav.demoFeatureResizeDesc": "Sleep om de paneelbreedte aan te passen",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexibel formaat aanpassen",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Beweeg erover om de schakelknop te tonen",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Slimme bediening",
|
||||
"sideNav.demoFeaturesTitle": "Functies",
|
||||
"sideNav.demoHint": "Probeer de rand van het paneel te slepen en gebruik de schakelknop ->",
|
||||
"sideNav.demoSubtitle": "Een zijpaneel in werkruimte-stijl met versleepbare grootte",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Zijbalk uitvouwen"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Annuleren",
|
||||
"common.confirm": "Bevestigen",
|
||||
"common.delete": "Verwijderen",
|
||||
"common.edit": "Bewerken",
|
||||
"editableMessage.addProps": "Eigenschappen toevoegen",
|
||||
"editableMessage.delete": "Verwijderen",
|
||||
"editableMessage.input": "Invoer",
|
||||
"editableMessage.inputPlaceholder": "Voer voorbeeldinhoud in",
|
||||
"editableMessage.output": "Uitvoer",
|
||||
"editableMessage.outputPlaceholder": "Voer voorbeelduitvoer in",
|
||||
"editableMessage.system": "Systeem",
|
||||
"emojiPicker.delete": "Verwijderen",
|
||||
"emojiPicker.draggerDesc": "Klik of sleep een afbeelding naar dit gebied om te uploaden",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Je kunt alleen afbeeldingsbestanden uploaden!",
|
||||
"emojiPicker.upload": "Uploaden",
|
||||
"emojiPicker.uploadBtn": "Bijsnijden en uploaden",
|
||||
"form.reset": "Resetten",
|
||||
"form.submit": "Verzenden",
|
||||
"form.unsavedChanges": "Niet-opgeslagen wijzigingen",
|
||||
"form.unsavedWarning": "Je hebt niet-opgeslagen wijzigingen. Weet je zeker dat je wilt verlaten?",
|
||||
"hotkey.conflict": "Deze sneltoets conflicteert met een bestaande.",
|
||||
"hotkey.invalidCombination": "De sneltoets moet een modificatietoets (Ctrl, Alt, Shift) en slechts één normale toets bevatten.",
|
||||
"hotkey.placeholder": "Druk op toetsen om sneltoets op te nemen",
|
||||
"hotkey.reset": "Herstellen naar standaard",
|
||||
"messageModal.cancel": "Annuleren",
|
||||
"messageModal.confirm": "Bevestigen",
|
||||
"messageModal.edit": "Bewerken",
|
||||
"sideNav.collapse": "Zijbalk samenvouwen",
|
||||
"sideNav.demoActiveLabel": "Actief",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Sleep onder de drempel om slim samen te vouwen",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatisch samenvouwen",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Geen animatiebelasting voor betere prestaties",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Prestaties",
|
||||
"sideNav.demoFeatureResizeDesc": "Sleep om de paneelbreedte aan te passen",
|
||||
"sideNav.demoFeatureResizeTitle": "Flexibel formaat aanpassen",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Beweeg erover om de schakelknop te tonen",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Slimme bediening",
|
||||
"sideNav.demoFeaturesTitle": "Functies",
|
||||
"sideNav.demoHint": "Probeer de rand van het paneel te slepen en gebruik de schakelknop ->",
|
||||
"sideNav.demoSubtitle": "Een zijpaneel in werkruimte-stijl met versleepbare grootte",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Zijbalk uitvouwen",
|
||||
"tokenTag.overload": "Overbelasting",
|
||||
"tokenTag.remained": "Resterend",
|
||||
"tokenTag.used": "Gebruikt"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Powrót do strony głównej",
|
||||
"error.desc": "Spróbuj ponownie później lub wróć do znanego świata.",
|
||||
"error.retry": "Odśwież",
|
||||
"error.stack": "Stos błędów",
|
||||
"error.title": "Ups, coś poszło nie tak...",
|
||||
"fetchError.detail": "Szczegóły błędu",
|
||||
"fetchError.title": "Żądanie nie powiodło się",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Awatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Przeciążenie",
|
||||
"tokenTag.remained": "Pozostało",
|
||||
"tokenTag.used": "Użyto"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Anuluj",
|
||||
"common.confirm": "Potwierdź",
|
||||
"common.delete": "Usuń",
|
||||
"common.edit": "Edytuj"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Dodaj właściwości",
|
||||
"editableMessage.delete": "Usuń",
|
||||
"editableMessage.input": "Wejście",
|
||||
"editableMessage.inputPlaceholder": "Wprowadź przykładową treść wejściową",
|
||||
"editableMessage.output": "Wyjście",
|
||||
"editableMessage.outputPlaceholder": "Wprowadź przykładową treść wyjściową",
|
||||
"editableMessage.system": "System"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Usuń",
|
||||
"emojiPicker.draggerDesc": "Kliknij lub przeciągnij obraz tutaj, aby go przesłać",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Można przesyłać tylko pliki graficzne!",
|
||||
"emojiPicker.upload": "Prześlij",
|
||||
"emojiPicker.uploadBtn": "Przytnij i prześlij"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Resetuj",
|
||||
"form.submit": "Zatwierdź",
|
||||
"form.unsavedChanges": "Niezapisane zmiany",
|
||||
"form.unsavedWarning": "Masz niezapisane zmiany. Czy na pewno chcesz opuścić stronę?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Ten skrót klawiszowy koliduje z istniejącym.",
|
||||
"hotkey.invalidCombination": "Skrót musi zawierać klawisz modyfikujący (Ctrl, Alt, Shift) i tylko jeden zwykły klawisz.",
|
||||
"hotkey.placeholder": "Naciśnij klawisze, aby nagrać skrót",
|
||||
"hotkey.reset": "Przywróć domyślne"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Anuluj",
|
||||
"messageModal.confirm": "Potwierdź",
|
||||
"messageModal.edit": "Edytuj"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Zwiń pasek boczny",
|
||||
"sideNav.demoActiveLabel": "Aktywny",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Przeciągnij poniżej progu, aby inteligentnie zwinąć",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatyczne zwijanie",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Brak animacji dla lepszej wydajności",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Wydajność",
|
||||
"sideNav.demoFeatureResizeDesc": "Przeciągnij, aby dostosować szerokość panelu",
|
||||
"sideNav.demoFeatureResizeTitle": "Elastyczne skalowanie",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Najedź, aby pokazać przycisk przełączania",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Inteligentny uchwyt",
|
||||
"sideNav.demoFeaturesTitle": "Funkcje",
|
||||
"sideNav.demoHint": "Spróbuj przeciągnąć krawędź panelu i użyć przycisku przełączania ->",
|
||||
"sideNav.demoSubtitle": "Panel boczny w stylu przestrzeni roboczej z możliwością zmiany rozmiaru przez przeciąganie",
|
||||
"sideNav.demoTitle": "Demo DraggableSideNav",
|
||||
"sideNav.expand": "Rozwiń pasek boczny"
|
||||
}
|
||||
"chat.avatar": "Awatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Anuluj",
|
||||
"common.confirm": "Potwierdź",
|
||||
"common.delete": "Usuń",
|
||||
"common.edit": "Edytuj",
|
||||
"editableMessage.addProps": "Dodaj właściwości",
|
||||
"editableMessage.delete": "Usuń",
|
||||
"editableMessage.input": "Wejście",
|
||||
"editableMessage.inputPlaceholder": "Wprowadź przykładową treść wejściową",
|
||||
"editableMessage.output": "Wyjście",
|
||||
"editableMessage.outputPlaceholder": "Wprowadź przykładową treść wyjściową",
|
||||
"editableMessage.system": "System",
|
||||
"emojiPicker.delete": "Usuń",
|
||||
"emojiPicker.draggerDesc": "Kliknij lub przeciągnij obraz tutaj, aby go przesłać",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Można przesyłać tylko pliki graficzne!",
|
||||
"emojiPicker.upload": "Prześlij",
|
||||
"emojiPicker.uploadBtn": "Przytnij i prześlij",
|
||||
"form.reset": "Resetuj",
|
||||
"form.submit": "Zatwierdź",
|
||||
"form.unsavedChanges": "Niezapisane zmiany",
|
||||
"form.unsavedWarning": "Masz niezapisane zmiany. Czy na pewno chcesz opuścić stronę?",
|
||||
"hotkey.conflict": "Ten skrót klawiszowy koliduje z istniejącym.",
|
||||
"hotkey.invalidCombination": "Skrót musi zawierać klawisz modyfikujący (Ctrl, Alt, Shift) i tylko jeden zwykły klawisz.",
|
||||
"hotkey.placeholder": "Naciśnij klawisze, aby nagrać skrót",
|
||||
"hotkey.reset": "Przywróć domyślne",
|
||||
"messageModal.cancel": "Anuluj",
|
||||
"messageModal.confirm": "Potwierdź",
|
||||
"messageModal.edit": "Edytuj",
|
||||
"sideNav.collapse": "Zwiń pasek boczny",
|
||||
"sideNav.demoActiveLabel": "Aktywny",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Przeciągnij poniżej progu, aby inteligentnie zwinąć",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Automatyczne zwijanie",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Brak animacji dla lepszej wydajności",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Wydajność",
|
||||
"sideNav.demoFeatureResizeDesc": "Przeciągnij, aby dostosować szerokość panelu",
|
||||
"sideNav.demoFeatureResizeTitle": "Elastyczne skalowanie",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Najedź, aby pokazać przycisk przełączania",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Inteligentny uchwyt",
|
||||
"sideNav.demoFeaturesTitle": "Funkcje",
|
||||
"sideNav.demoHint": "Spróbuj przeciągnąć krawędź panelu i użyć przycisku przełączania ->",
|
||||
"sideNav.demoSubtitle": "Panel boczny w stylu przestrzeni roboczej z możliwością zmiany rozmiaru przez przeciąganie",
|
||||
"sideNav.demoTitle": "Demo DraggableSideNav",
|
||||
"sideNav.expand": "Rozwiń pasek boczny",
|
||||
"tokenTag.overload": "Przeciążenie",
|
||||
"tokenTag.remained": "Pozostało",
|
||||
"tokenTag.used": "Użyto"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Voltar para a Página Inicial",
|
||||
"error.desc": "Tente novamente mais tarde ou volte para o mundo conhecido.",
|
||||
"error.retry": "Recarregar",
|
||||
"error.stack": "Stack de erros",
|
||||
"error.title": "Ops, algo deu errado...",
|
||||
"fetchError.detail": "Detalhes do erro",
|
||||
"fetchError.title": "Falha na solicitação",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Sobrecarga",
|
||||
"tokenTag.remained": "Restante",
|
||||
"tokenTag.used": "Usado"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Cancelar",
|
||||
"common.confirm": "Confirmar",
|
||||
"common.delete": "Excluir",
|
||||
"common.edit": "Editar"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Adicionar Propriedades",
|
||||
"editableMessage.delete": "Excluir",
|
||||
"editableMessage.input": "Entrada",
|
||||
"editableMessage.inputPlaceholder": "Por favor, insira o conteúdo de entrada de exemplo",
|
||||
"editableMessage.output": "Saída",
|
||||
"editableMessage.outputPlaceholder": "Por favor, insira o conteúdo de saída de exemplo",
|
||||
"editableMessage.system": "Sistema"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Excluir",
|
||||
"emojiPicker.draggerDesc": "Clique ou arraste a imagem para esta área para fazer o upload",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Você só pode enviar arquivos de imagem!",
|
||||
"emojiPicker.upload": "Enviar",
|
||||
"emojiPicker.uploadBtn": "Cortar e enviar"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Redefinir",
|
||||
"form.submit": "Enviar",
|
||||
"form.unsavedChanges": "Alterações não salvas",
|
||||
"form.unsavedWarning": "Você tem alterações não salvas. Tem certeza de que deseja sair?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Este atalho entra em conflito com um já existente.",
|
||||
"hotkey.invalidCombination": "O atalho deve incluir uma tecla modificadora (Ctrl, Alt, Shift) e apenas uma tecla normal.",
|
||||
"hotkey.placeholder": "Pressione as teclas para gravar o atalho",
|
||||
"hotkey.reset": "Redefinir para padrão"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Cancelar",
|
||||
"messageModal.confirm": "Confirmar",
|
||||
"messageModal.edit": "Editar"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Recolher barra lateral",
|
||||
"sideNav.demoActiveLabel": "Ativo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Arraste abaixo do limite para recolhimento inteligente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Recolhimento automático",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Sem animações para melhor desempenho",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Desempenho",
|
||||
"sideNav.demoFeatureResizeDesc": "Arraste para ajustar a largura do painel",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionamento flexível",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Passe o mouse para mostrar o botão de alternância",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Manipulador inteligente",
|
||||
"sideNav.demoFeaturesTitle": "Recursos",
|
||||
"sideNav.demoHint": "Tente arrastar a borda do painel e usar o botão de alternância ->",
|
||||
"sideNav.demoSubtitle": "Um painel lateral estilo área de trabalho com redimensionamento arrastável",
|
||||
"sideNav.demoTitle": "Demonstração do DraggableSideNav",
|
||||
"sideNav.expand": "Expandir barra lateral"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Cancelar",
|
||||
"common.confirm": "Confirmar",
|
||||
"common.delete": "Excluir",
|
||||
"common.edit": "Editar",
|
||||
"editableMessage.addProps": "Adicionar Propriedades",
|
||||
"editableMessage.delete": "Excluir",
|
||||
"editableMessage.input": "Entrada",
|
||||
"editableMessage.inputPlaceholder": "Por favor, insira o conteúdo de entrada de exemplo",
|
||||
"editableMessage.output": "Saída",
|
||||
"editableMessage.outputPlaceholder": "Por favor, insira o conteúdo de saída de exemplo",
|
||||
"editableMessage.system": "Sistema",
|
||||
"emojiPicker.delete": "Excluir",
|
||||
"emojiPicker.draggerDesc": "Clique ou arraste a imagem para esta área para fazer o upload",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Você só pode enviar arquivos de imagem!",
|
||||
"emojiPicker.upload": "Enviar",
|
||||
"emojiPicker.uploadBtn": "Cortar e enviar",
|
||||
"form.reset": "Redefinir",
|
||||
"form.submit": "Enviar",
|
||||
"form.unsavedChanges": "Alterações não salvas",
|
||||
"form.unsavedWarning": "Você tem alterações não salvas. Tem certeza de que deseja sair?",
|
||||
"hotkey.conflict": "Este atalho entra em conflito com um já existente.",
|
||||
"hotkey.invalidCombination": "O atalho deve incluir uma tecla modificadora (Ctrl, Alt, Shift) e apenas uma tecla normal.",
|
||||
"hotkey.placeholder": "Pressione as teclas para gravar o atalho",
|
||||
"hotkey.reset": "Redefinir para padrão",
|
||||
"messageModal.cancel": "Cancelar",
|
||||
"messageModal.confirm": "Confirmar",
|
||||
"messageModal.edit": "Editar",
|
||||
"sideNav.collapse": "Recolher barra lateral",
|
||||
"sideNav.demoActiveLabel": "Ativo",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Arraste abaixo do limite para recolhimento inteligente",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Recolhimento automático",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Sem animações para melhor desempenho",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Desempenho",
|
||||
"sideNav.demoFeatureResizeDesc": "Arraste para ajustar a largura do painel",
|
||||
"sideNav.demoFeatureResizeTitle": "Redimensionamento flexível",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Passe o mouse para mostrar o botão de alternância",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Manipulador inteligente",
|
||||
"sideNav.demoFeaturesTitle": "Recursos",
|
||||
"sideNav.demoHint": "Tente arrastar a borda do painel e usar o botão de alternância ->",
|
||||
"sideNav.demoSubtitle": "Um painel lateral estilo área de trabalho com redimensionamento arrastável",
|
||||
"sideNav.demoTitle": "Demonstração do DraggableSideNav",
|
||||
"sideNav.expand": "Expandir barra lateral",
|
||||
"tokenTag.overload": "Sobrecarga",
|
||||
"tokenTag.remained": "Restante",
|
||||
"tokenTag.used": "Usado"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Вернуться на главную",
|
||||
"error.desc": "Попробуйте позже или вернитесь в знакомый мир.",
|
||||
"error.retry": "Перезагрузить",
|
||||
"error.stack": "Стек ошибок",
|
||||
"error.title": "Упс, что-то пошло не так...",
|
||||
"fetchError.detail": "Детали ошибки",
|
||||
"fetchError.title": "Запрос не выполнен",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Аватар",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Перегрузка",
|
||||
"tokenTag.remained": "Осталось",
|
||||
"tokenTag.used": "Использовано"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Отмена",
|
||||
"common.confirm": "Подтвердить",
|
||||
"common.delete": "Удалить",
|
||||
"common.edit": "Редактировать"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Добавить свойства",
|
||||
"editableMessage.delete": "Удалить",
|
||||
"editableMessage.input": "Ввод",
|
||||
"editableMessage.inputPlaceholder": "Пожалуйста, введите пример входных данных",
|
||||
"editableMessage.output": "Вывод",
|
||||
"editableMessage.outputPlaceholder": "Пожалуйста, введите пример выходных данных",
|
||||
"editableMessage.system": "Система"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Удалить",
|
||||
"emojiPicker.draggerDesc": "Кликните или перетащите изображение в эту область для загрузки",
|
||||
"emojiPicker.emoji": "Эмодзи",
|
||||
"emojiPicker.fileTypeError": "Можно загружать только файлы изображений!",
|
||||
"emojiPicker.upload": "Загрузить",
|
||||
"emojiPicker.uploadBtn": "Обрезать и загрузить"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Сбросить",
|
||||
"form.submit": "Отправить",
|
||||
"form.unsavedChanges": "Несохранённые изменения",
|
||||
"form.unsavedWarning": "У вас есть несохранённые изменения. Вы уверены, что хотите уйти?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Эта горячая клавиша конфликтует с уже существующей.",
|
||||
"hotkey.invalidCombination": "Горячая клавиша должна включать модификатор (Ctrl, Alt, Shift) и только одну обычную клавишу.",
|
||||
"hotkey.placeholder": "Нажмите клавиши для записи горячей клавиши",
|
||||
"hotkey.reset": "Сбросить по умолчанию"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Отмена",
|
||||
"messageModal.confirm": "Подтвердить",
|
||||
"messageModal.edit": "Редактировать"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Свернуть боковую панель",
|
||||
"sideNav.demoActiveLabel": "Активно",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Перетащите ниже порога для умного сворачивания",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Автосворачивание",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Без анимации для лучшей производительности",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Производительность",
|
||||
"sideNav.demoFeatureResizeDesc": "Перетащите для изменения ширины панели",
|
||||
"sideNav.demoFeatureResizeTitle": "Гибкое изменение размера",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Наведите курсор, чтобы показать кнопку переключения",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Умная ручка",
|
||||
"sideNav.demoFeaturesTitle": "Функции",
|
||||
"sideNav.demoHint": "Попробуйте перетащить край панели и использовать кнопку переключения ->",
|
||||
"sideNav.demoSubtitle": "Боковая панель в стиле рабочего пространства с возможностью изменения размера",
|
||||
"sideNav.demoTitle": "Демонстрация DraggableSideNav",
|
||||
"sideNav.expand": "Развернуть боковую панель"
|
||||
}
|
||||
"chat.avatar": "Аватар",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Отмена",
|
||||
"common.confirm": "Подтвердить",
|
||||
"common.delete": "Удалить",
|
||||
"common.edit": "Редактировать",
|
||||
"editableMessage.addProps": "Добавить свойства",
|
||||
"editableMessage.delete": "Удалить",
|
||||
"editableMessage.input": "Ввод",
|
||||
"editableMessage.inputPlaceholder": "Пожалуйста, введите пример входных данных",
|
||||
"editableMessage.output": "Вывод",
|
||||
"editableMessage.outputPlaceholder": "Пожалуйста, введите пример выходных данных",
|
||||
"editableMessage.system": "Система",
|
||||
"emojiPicker.delete": "Удалить",
|
||||
"emojiPicker.draggerDesc": "Кликните или перетащите изображение в эту область для загрузки",
|
||||
"emojiPicker.emoji": "Эмодзи",
|
||||
"emojiPicker.fileTypeError": "Можно загружать только файлы изображений!",
|
||||
"emojiPicker.upload": "Загрузить",
|
||||
"emojiPicker.uploadBtn": "Обрезать и загрузить",
|
||||
"form.reset": "Сбросить",
|
||||
"form.submit": "Отправить",
|
||||
"form.unsavedChanges": "Несохранённые изменения",
|
||||
"form.unsavedWarning": "У вас есть несохранённые изменения. Вы уверены, что хотите уйти?",
|
||||
"hotkey.conflict": "Эта горячая клавиша конфликтует с уже существующей.",
|
||||
"hotkey.invalidCombination": "Горячая клавиша должна включать модификатор (Ctrl, Alt, Shift) и только одну обычную клавишу.",
|
||||
"hotkey.placeholder": "Нажмите клавиши для записи горячей клавиши",
|
||||
"hotkey.reset": "Сбросить по умолчанию",
|
||||
"messageModal.cancel": "Отмена",
|
||||
"messageModal.confirm": "Подтвердить",
|
||||
"messageModal.edit": "Редактировать",
|
||||
"sideNav.collapse": "Свернуть боковую панель",
|
||||
"sideNav.demoActiveLabel": "Активно",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Перетащите ниже порога для умного сворачивания",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Автосворачивание",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Без анимации для лучшей производительности",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Производительность",
|
||||
"sideNav.demoFeatureResizeDesc": "Перетащите для изменения ширины панели",
|
||||
"sideNav.demoFeatureResizeTitle": "Гибкое изменение размера",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Наведите курсор, чтобы показать кнопку переключения",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Умная ручка",
|
||||
"sideNav.demoFeaturesTitle": "Функции",
|
||||
"sideNav.demoHint": "Попробуйте перетащить край панели и использовать кнопку переключения ->",
|
||||
"sideNav.demoSubtitle": "Боковая панель в стиле рабочего пространства с возможностью изменения размера",
|
||||
"sideNav.demoTitle": "Демонстрация DraggableSideNav",
|
||||
"sideNav.expand": "Развернуть боковую панель",
|
||||
"tokenTag.overload": "Перегрузка",
|
||||
"tokenTag.remained": "Осталось",
|
||||
"tokenTag.used": "Использовано"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Ana Sayfaya Dön",
|
||||
"error.desc": "Daha sonra tekrar deneyin ya da bilinen dünyaya geri dönün.",
|
||||
"error.retry": "Yeniden Yükle",
|
||||
"error.stack": "Hata Yığını",
|
||||
"error.title": "Hata oluştu..",
|
||||
"fetchError.detail": "Hata detayları",
|
||||
"fetchError.title": "İstek başarısız oldu",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Aşım",
|
||||
"tokenTag.remained": "Kalan",
|
||||
"tokenTag.used": "Kullanılan"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "İptal",
|
||||
"common.confirm": "Onayla",
|
||||
"common.delete": "Sil",
|
||||
"common.edit": "Düzenle"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Özellik Ekle",
|
||||
"editableMessage.delete": "Sil",
|
||||
"editableMessage.input": "Girdi",
|
||||
"editableMessage.inputPlaceholder": "Lütfen örnek girdi içeriği girin",
|
||||
"editableMessage.output": "Çıktı",
|
||||
"editableMessage.outputPlaceholder": "Lütfen örnek çıktı içeriği girin",
|
||||
"editableMessage.system": "Sistem"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Sil",
|
||||
"emojiPicker.draggerDesc": "Yüklemek için tıklayın veya görseli bu alana sürükleyin",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Yalnızca görsel dosyaları yükleyebilirsiniz!",
|
||||
"emojiPicker.upload": "Yükle",
|
||||
"emojiPicker.uploadBtn": "Kırp ve yükle"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Sıfırla",
|
||||
"form.submit": "Gönder",
|
||||
"form.unsavedChanges": "Kaydedilmemiş değişiklikler",
|
||||
"form.unsavedWarning": "Kaydedilmemiş değişiklikleriniz var. Çıkmak istediğinizden emin misiniz?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Bu kısayol mevcut bir kısayolla çakışıyor.",
|
||||
"hotkey.invalidCombination": "Kısayol bir değiştirici tuş (Ctrl, Alt, Shift) ve yalnızca bir normal tuş içermelidir.",
|
||||
"hotkey.placeholder": "Kısayolu kaydetmek için tuşlara basın",
|
||||
"hotkey.reset": "Varsayılana sıfırla"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "İptal",
|
||||
"messageModal.confirm": "Onayla",
|
||||
"messageModal.edit": "Düzenle"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Kenar çubuğunu daralt",
|
||||
"sideNav.demoActiveLabel": "Aktif",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Akıllı daraltma için eşiğin altına sürükleyin",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Otomatik daraltma",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Daha iyi performans için animasyon yükü yok",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performans",
|
||||
"sideNav.demoFeatureResizeDesc": "Panel genişliğini ayarlamak için sürükleyin",
|
||||
"sideNav.demoFeatureResizeTitle": "Esnek yeniden boyutlandırma",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Geçiş düğmesini göstermek için üzerine gelin",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Akıllı tutma yeri",
|
||||
"sideNav.demoFeaturesTitle": "Özellikler",
|
||||
"sideNav.demoHint": "Panel kenarını sürüklemeyi ve geçiş düğmesini kullanmayı deneyin ->",
|
||||
"sideNav.demoSubtitle": "Sürüklenebilir yeniden boyutlandırma ile çalışma alanı tarzı yan panel",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Kenar çubuğunu genişlet"
|
||||
}
|
||||
"chat.avatar": "Avatar",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "İptal",
|
||||
"common.confirm": "Onayla",
|
||||
"common.delete": "Sil",
|
||||
"common.edit": "Düzenle",
|
||||
"editableMessage.addProps": "Özellik Ekle",
|
||||
"editableMessage.delete": "Sil",
|
||||
"editableMessage.input": "Girdi",
|
||||
"editableMessage.inputPlaceholder": "Lütfen örnek girdi içeriği girin",
|
||||
"editableMessage.output": "Çıktı",
|
||||
"editableMessage.outputPlaceholder": "Lütfen örnek çıktı içeriği girin",
|
||||
"editableMessage.system": "Sistem",
|
||||
"emojiPicker.delete": "Sil",
|
||||
"emojiPicker.draggerDesc": "Yüklemek için tıklayın veya görseli bu alana sürükleyin",
|
||||
"emojiPicker.emoji": "Emoji",
|
||||
"emojiPicker.fileTypeError": "Yalnızca görsel dosyaları yükleyebilirsiniz!",
|
||||
"emojiPicker.upload": "Yükle",
|
||||
"emojiPicker.uploadBtn": "Kırp ve yükle",
|
||||
"form.reset": "Sıfırla",
|
||||
"form.submit": "Gönder",
|
||||
"form.unsavedChanges": "Kaydedilmemiş değişiklikler",
|
||||
"form.unsavedWarning": "Kaydedilmemiş değişiklikleriniz var. Çıkmak istediğinizden emin misiniz?",
|
||||
"hotkey.conflict": "Bu kısayol mevcut bir kısayolla çakışıyor.",
|
||||
"hotkey.invalidCombination": "Kısayol bir değiştirici tuş (Ctrl, Alt, Shift) ve yalnızca bir normal tuş içermelidir.",
|
||||
"hotkey.placeholder": "Kısayolu kaydetmek için tuşlara basın",
|
||||
"hotkey.reset": "Varsayılana sıfırla",
|
||||
"messageModal.cancel": "İptal",
|
||||
"messageModal.confirm": "Onayla",
|
||||
"messageModal.edit": "Düzenle",
|
||||
"sideNav.collapse": "Kenar çubuğunu daralt",
|
||||
"sideNav.demoActiveLabel": "Aktif",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Akıllı daraltma için eşiğin altına sürükleyin",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Otomatik daraltma",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Daha iyi performans için animasyon yükü yok",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Performans",
|
||||
"sideNav.demoFeatureResizeDesc": "Panel genişliğini ayarlamak için sürükleyin",
|
||||
"sideNav.demoFeatureResizeTitle": "Esnek yeniden boyutlandırma",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Geçiş düğmesini göstermek için üzerine gelin",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Akıllı tutma yeri",
|
||||
"sideNav.demoFeaturesTitle": "Özellikler",
|
||||
"sideNav.demoHint": "Panel kenarını sürüklemeyi ve geçiş düğmesini kullanmayı deneyin ->",
|
||||
"sideNav.demoSubtitle": "Sürüklenebilir yeniden boyutlandırma ile çalışma alanı tarzı yan panel",
|
||||
"sideNav.demoTitle": "DraggableSideNav Demo",
|
||||
"sideNav.expand": "Kenar çubuğunu genişlet",
|
||||
"tokenTag.overload": "Aşım",
|
||||
"tokenTag.remained": "Kalan",
|
||||
"tokenTag.used": "Kullanılan"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "Quay về Trang chủ",
|
||||
"error.desc": "Hãy thử lại sau, hoặc quay về thế giới quen thuộc.",
|
||||
"error.retry": "Tải lại",
|
||||
"error.stack": "Ngăn xếp lỗi",
|
||||
"error.title": "Ôi, đã xảy ra lỗi..",
|
||||
"fetchError.detail": "Chi tiết lỗi",
|
||||
"fetchError.title": "Yêu cầu thất bại",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "Ảnh đại diện",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "Quá tải",
|
||||
"tokenTag.remained": "Còn lại",
|
||||
"tokenTag.used": "Đã sử dụng"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "Hủy",
|
||||
"common.confirm": "Xác nhận",
|
||||
"common.delete": "Xóa",
|
||||
"common.edit": "Chỉnh sửa"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "Thêm thuộc tính",
|
||||
"editableMessage.delete": "Xóa",
|
||||
"editableMessage.input": "Đầu vào",
|
||||
"editableMessage.inputPlaceholder": "Vui lòng nhập nội dung mẫu đầu vào",
|
||||
"editableMessage.output": "Đầu ra",
|
||||
"editableMessage.outputPlaceholder": "Vui lòng nhập nội dung mẫu đầu ra",
|
||||
"editableMessage.system": "Hệ thống"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "Xóa",
|
||||
"emojiPicker.draggerDesc": "Nhấp hoặc kéo hình ảnh vào khu vực này để tải lên",
|
||||
"emojiPicker.emoji": "Biểu tượng cảm xúc",
|
||||
"emojiPicker.fileTypeError": "Bạn chỉ có thể tải lên các tệp hình ảnh!",
|
||||
"emojiPicker.upload": "Tải lên",
|
||||
"emojiPicker.uploadBtn": "Cắt và tải lên"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "Đặt lại",
|
||||
"form.submit": "Gửi",
|
||||
"form.unsavedChanges": "Thay đổi chưa được lưu",
|
||||
"form.unsavedWarning": "Bạn có thay đổi chưa được lưu. Bạn có chắc chắn muốn rời đi không?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "Phím tắt này bị trùng với một phím tắt khác.",
|
||||
"hotkey.invalidCombination": "Phím tắt phải bao gồm một phím điều khiển (Ctrl, Alt, Shift) và chỉ một phím thường.",
|
||||
"hotkey.placeholder": "Nhấn phím để ghi lại phím tắt",
|
||||
"hotkey.reset": "Khôi phục mặc định"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "Hủy",
|
||||
"messageModal.confirm": "Xác nhận",
|
||||
"messageModal.edit": "Chỉnh sửa"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "Thu gọn thanh bên",
|
||||
"sideNav.demoActiveLabel": "Đang hoạt động",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Kéo dưới ngưỡng để tự động thu gọn",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Tự động thu gọn",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Không có hiệu ứng động để tăng hiệu suất",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Hiệu suất",
|
||||
"sideNav.demoFeatureResizeDesc": "Kéo để điều chỉnh độ rộng bảng",
|
||||
"sideNav.demoFeatureResizeTitle": "Thay đổi kích thước linh hoạt",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Di chuột để hiển thị nút chuyển đổi",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Tay cầm thông minh",
|
||||
"sideNav.demoFeaturesTitle": "Tính năng",
|
||||
"sideNav.demoHint": "Thử kéo cạnh bảng và sử dụng nút chuyển đổi ->",
|
||||
"sideNav.demoSubtitle": "Bảng bên kiểu không gian làm việc với khả năng thay đổi kích thước bằng cách kéo",
|
||||
"sideNav.demoTitle": "Bản demo DraggableSideNav",
|
||||
"sideNav.expand": "Mở rộng thanh bên"
|
||||
}
|
||||
"chat.avatar": "Ảnh đại diện",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "Hủy",
|
||||
"common.confirm": "Xác nhận",
|
||||
"common.delete": "Xóa",
|
||||
"common.edit": "Chỉnh sửa",
|
||||
"editableMessage.addProps": "Thêm thuộc tính",
|
||||
"editableMessage.delete": "Xóa",
|
||||
"editableMessage.input": "Đầu vào",
|
||||
"editableMessage.inputPlaceholder": "Vui lòng nhập nội dung mẫu đầu vào",
|
||||
"editableMessage.output": "Đầu ra",
|
||||
"editableMessage.outputPlaceholder": "Vui lòng nhập nội dung mẫu đầu ra",
|
||||
"editableMessage.system": "Hệ thống",
|
||||
"emojiPicker.delete": "Xóa",
|
||||
"emojiPicker.draggerDesc": "Nhấp hoặc kéo hình ảnh vào khu vực này để tải lên",
|
||||
"emojiPicker.emoji": "Biểu tượng cảm xúc",
|
||||
"emojiPicker.fileTypeError": "Bạn chỉ có thể tải lên các tệp hình ảnh!",
|
||||
"emojiPicker.upload": "Tải lên",
|
||||
"emojiPicker.uploadBtn": "Cắt và tải lên",
|
||||
"form.reset": "Đặt lại",
|
||||
"form.submit": "Gửi",
|
||||
"form.unsavedChanges": "Thay đổi chưa được lưu",
|
||||
"form.unsavedWarning": "Bạn có thay đổi chưa được lưu. Bạn có chắc chắn muốn rời đi không?",
|
||||
"hotkey.conflict": "Phím tắt này bị trùng với một phím tắt khác.",
|
||||
"hotkey.invalidCombination": "Phím tắt phải bao gồm một phím điều khiển (Ctrl, Alt, Shift) và chỉ một phím thường.",
|
||||
"hotkey.placeholder": "Nhấn phím để ghi lại phím tắt",
|
||||
"hotkey.reset": "Khôi phục mặc định",
|
||||
"messageModal.cancel": "Hủy",
|
||||
"messageModal.confirm": "Xác nhận",
|
||||
"messageModal.edit": "Chỉnh sửa",
|
||||
"sideNav.collapse": "Thu gọn thanh bên",
|
||||
"sideNav.demoActiveLabel": "Đang hoạt động",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "Kéo dưới ngưỡng để tự động thu gọn",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "Tự động thu gọn",
|
||||
"sideNav.demoFeaturePerformanceDesc": "Không có hiệu ứng động để tăng hiệu suất",
|
||||
"sideNav.demoFeaturePerformanceTitle": "Hiệu suất",
|
||||
"sideNav.demoFeatureResizeDesc": "Kéo để điều chỉnh độ rộng bảng",
|
||||
"sideNav.demoFeatureResizeTitle": "Thay đổi kích thước linh hoạt",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "Di chuột để hiển thị nút chuyển đổi",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "Tay cầm thông minh",
|
||||
"sideNav.demoFeaturesTitle": "Tính năng",
|
||||
"sideNav.demoHint": "Thử kéo cạnh bảng và sử dụng nút chuyển đổi ->",
|
||||
"sideNav.demoSubtitle": "Bảng bên kiểu không gian làm việc với khả năng thay đổi kích thước bằng cách kéo",
|
||||
"sideNav.demoTitle": "Bản demo DraggableSideNav",
|
||||
"sideNav.expand": "Mở rộng thanh bên",
|
||||
"tokenTag.overload": "Quá tải",
|
||||
"tokenTag.remained": "Còn lại",
|
||||
"tokenTag.used": "Đã sử dụng"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "返回首页",
|
||||
"error.desc": "页面暂时不可用。你可以返回首页,或稍后重试(你的设置不会因此丢失)",
|
||||
"error.retry": "重新加载",
|
||||
"error.stack": "错误堆栈",
|
||||
"error.title": "页面暂时不可用",
|
||||
"fetchError.detail": "查看详情",
|
||||
"fetchError.title": "请求未能完成",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "头像",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "超额",
|
||||
"tokenTag.remained": "剩余",
|
||||
"tokenTag.used": "已用"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "确认",
|
||||
"common.delete": "删除",
|
||||
"common.edit": "编辑"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "添加属性",
|
||||
"editableMessage.delete": "删除",
|
||||
"editableMessage.input": "输入",
|
||||
"editableMessage.inputPlaceholder": "请输入示例输入内容",
|
||||
"editableMessage.output": "输出",
|
||||
"editableMessage.outputPlaceholder": "请输入示例输出内容",
|
||||
"editableMessage.system": "系统"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "删除",
|
||||
"emojiPicker.draggerDesc": "点击或拖动图片到此区域上传",
|
||||
"emojiPicker.emoji": "表情",
|
||||
"emojiPicker.fileTypeError": "只能上传图片文件!",
|
||||
"emojiPicker.upload": "上传",
|
||||
"emojiPicker.uploadBtn": "裁剪并上传"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "重置",
|
||||
"form.submit": "提交",
|
||||
"form.unsavedChanges": "未保存的更改",
|
||||
"form.unsavedWarning": "您有未保存的更改。确定要离开吗?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "此快捷键与现有快捷键冲突。",
|
||||
"hotkey.invalidCombination": "快捷键必须包含修饰键(Ctrl、Alt、Shift)且只能有一个普通键。",
|
||||
"hotkey.placeholder": "按键录制快捷键",
|
||||
"hotkey.reset": "重置为默认"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "取消",
|
||||
"messageModal.confirm": "确认",
|
||||
"messageModal.edit": "编辑"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "折叠侧边栏",
|
||||
"sideNav.demoActiveLabel": "启用",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "拖动到阈值以下智能折叠",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自动折叠",
|
||||
"sideNav.demoFeaturePerformanceDesc": "无动画开销,性能更佳",
|
||||
"sideNav.demoFeaturePerformanceTitle": "性能优化",
|
||||
"sideNav.demoFeatureResizeDesc": "拖动调整面板宽度",
|
||||
"sideNav.demoFeatureResizeTitle": "弹性调整",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "悬停显示切换按钮",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "智能手柄",
|
||||
"sideNav.demoFeaturesTitle": "功能特色",
|
||||
"sideNav.demoHint": "尝试拖动面板边缘并使用切换按钮 ->",
|
||||
"sideNav.demoSubtitle": "可拖动调整的工作区风格侧边面板",
|
||||
"sideNav.demoTitle": "DraggableSideNav 示范",
|
||||
"sideNav.expand": "展开侧边栏"
|
||||
}
|
||||
"chat.avatar": "头像",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "确认",
|
||||
"common.delete": "删除",
|
||||
"common.edit": "编辑",
|
||||
"editableMessage.addProps": "添加属性",
|
||||
"editableMessage.delete": "删除",
|
||||
"editableMessage.input": "输入",
|
||||
"editableMessage.inputPlaceholder": "请输入示例输入内容",
|
||||
"editableMessage.output": "输出",
|
||||
"editableMessage.outputPlaceholder": "请输入示例输出内容",
|
||||
"editableMessage.system": "系统",
|
||||
"emojiPicker.delete": "删除",
|
||||
"emojiPicker.draggerDesc": "点击或拖动图片到此区域上传",
|
||||
"emojiPicker.emoji": "表情",
|
||||
"emojiPicker.fileTypeError": "只能上传图片文件!",
|
||||
"emojiPicker.upload": "上传",
|
||||
"emojiPicker.uploadBtn": "裁剪并上传",
|
||||
"form.reset": "重置",
|
||||
"form.submit": "提交",
|
||||
"form.unsavedChanges": "未保存的更改",
|
||||
"form.unsavedWarning": "您有未保存的更改。确定要离开吗?",
|
||||
"hotkey.conflict": "此快捷键与现有快捷键冲突。",
|
||||
"hotkey.invalidCombination": "快捷键必须包含修饰键(Ctrl、Alt、Shift)且只能有一个普通键。",
|
||||
"hotkey.placeholder": "按键录制快捷键",
|
||||
"hotkey.reset": "重置为默认",
|
||||
"messageModal.cancel": "取消",
|
||||
"messageModal.confirm": "确认",
|
||||
"messageModal.edit": "编辑",
|
||||
"sideNav.collapse": "折叠侧边栏",
|
||||
"sideNav.demoActiveLabel": "启用",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "拖动到阈值以下智能折叠",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自动折叠",
|
||||
"sideNav.demoFeaturePerformanceDesc": "无动画开销,性能更佳",
|
||||
"sideNav.demoFeaturePerformanceTitle": "性能优化",
|
||||
"sideNav.demoFeatureResizeDesc": "拖动调整面板宽度",
|
||||
"sideNav.demoFeatureResizeTitle": "弹性调整",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "悬停显示切换按钮",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "智能手柄",
|
||||
"sideNav.demoFeaturesTitle": "功能特色",
|
||||
"sideNav.demoHint": "尝试拖动面板边缘并使用切换按钮 ->",
|
||||
"sideNav.demoSubtitle": "可拖动调整的工作区风格侧边面板",
|
||||
"sideNav.demoTitle": "DraggableSideNav 示范",
|
||||
"sideNav.expand": "展开侧边栏",
|
||||
"tokenTag.overload": "超额",
|
||||
"tokenTag.remained": "剩余",
|
||||
"tokenTag.used": "已用"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"error.backHome": "返回首頁",
|
||||
"error.desc": "待會再試試,或者回到已知的世界",
|
||||
"error.retry": "重新加載",
|
||||
"error.stack": "錯誤堆疊",
|
||||
"error.title": "頁面遇到一點問題..",
|
||||
"fetchError.detail": "錯誤詳情",
|
||||
"fetchError.title": "請求失敗",
|
||||
|
|
|
|||
|
|
@ -1,66 +1,50 @@
|
|||
{
|
||||
"chat": {
|
||||
"chat.avatar": "頭像",
|
||||
"chat.placeholder": "...",
|
||||
"tokenTag.overload": "超額",
|
||||
"tokenTag.remained": "剩餘",
|
||||
"tokenTag.used": "已用"
|
||||
},
|
||||
"common": {
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "確認",
|
||||
"common.delete": "刪除",
|
||||
"common.edit": "編輯"
|
||||
},
|
||||
"editableMessage": {
|
||||
"editableMessage.addProps": "添加屬性",
|
||||
"editableMessage.delete": "刪除",
|
||||
"editableMessage.input": "輸入",
|
||||
"editableMessage.inputPlaceholder": "請輸入示例輸入內容",
|
||||
"editableMessage.output": "輸出",
|
||||
"editableMessage.outputPlaceholder": "請輸入示例輸出內容",
|
||||
"editableMessage.system": "系統"
|
||||
},
|
||||
"emojiPicker": {
|
||||
"emojiPicker.delete": "刪除",
|
||||
"emojiPicker.draggerDesc": "點擊或拖動圖片到此處上傳",
|
||||
"emojiPicker.emoji": "表情",
|
||||
"emojiPicker.fileTypeError": "只能上傳圖片檔案!",
|
||||
"emojiPicker.upload": "上傳",
|
||||
"emojiPicker.uploadBtn": "裁剪並上傳"
|
||||
},
|
||||
"form": {
|
||||
"form.reset": "重置",
|
||||
"form.submit": "提交",
|
||||
"form.unsavedChanges": "未儲存的變更",
|
||||
"form.unsavedWarning": "您有未儲存的變更。確定要離開嗎?"
|
||||
},
|
||||
"hotkey": {
|
||||
"hotkey.conflict": "此快捷鍵與現有快捷鍵衝突。",
|
||||
"hotkey.invalidCombination": "快捷鍵必須包含修飾鍵(Ctrl、Alt、Shift)且只能有一個一般鍵。",
|
||||
"hotkey.placeholder": "按鍵錄製快捷鍵",
|
||||
"hotkey.reset": "重置為預設"
|
||||
},
|
||||
"messageModal": {
|
||||
"messageModal.cancel": "取消",
|
||||
"messageModal.confirm": "確認",
|
||||
"messageModal.edit": "編輯"
|
||||
},
|
||||
"sideNav": {
|
||||
"sideNav.collapse": "折疊側邊欄",
|
||||
"sideNav.demoActiveLabel": "啟用",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "拖動到閾值以下智慧折疊",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自動折疊",
|
||||
"sideNav.demoFeaturePerformanceDesc": "無動畫開銷,效能更佳",
|
||||
"sideNav.demoFeaturePerformanceTitle": "效能最佳化",
|
||||
"sideNav.demoFeatureResizeDesc": "拖動調整面板寬度",
|
||||
"sideNav.demoFeatureResizeTitle": "彈性調整",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "懸停顯示切換按鈕",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "智慧手柄",
|
||||
"sideNav.demoFeaturesTitle": "功能特色",
|
||||
"sideNav.demoHint": "嘗試拖動面板邊緣並使用切換按鈕 ->",
|
||||
"sideNav.demoSubtitle": "可拖動調整的工作區風格側邊面板",
|
||||
"sideNav.demoTitle": "DraggableSideNav 示範",
|
||||
"sideNav.expand": "展開側邊欄"
|
||||
}
|
||||
"chat.avatar": "頭像",
|
||||
"chat.placeholder": "...",
|
||||
"common.cancel": "取消",
|
||||
"common.confirm": "確認",
|
||||
"common.delete": "刪除",
|
||||
"common.edit": "編輯",
|
||||
"editableMessage.addProps": "添加屬性",
|
||||
"editableMessage.delete": "刪除",
|
||||
"editableMessage.input": "輸入",
|
||||
"editableMessage.inputPlaceholder": "請輸入示例輸入內容",
|
||||
"editableMessage.output": "輸出",
|
||||
"editableMessage.outputPlaceholder": "請輸入示例輸出內容",
|
||||
"editableMessage.system": "系統",
|
||||
"emojiPicker.delete": "刪除",
|
||||
"emojiPicker.draggerDesc": "點擊或拖動圖片到此處上傳",
|
||||
"emojiPicker.emoji": "表情",
|
||||
"emojiPicker.fileTypeError": "只能上傳圖片檔案!",
|
||||
"emojiPicker.upload": "上傳",
|
||||
"emojiPicker.uploadBtn": "裁剪並上傳",
|
||||
"form.reset": "重置",
|
||||
"form.submit": "提交",
|
||||
"form.unsavedChanges": "未儲存的變更",
|
||||
"form.unsavedWarning": "您有未儲存的變更。確定要離開嗎?",
|
||||
"hotkey.conflict": "此快捷鍵與現有快捷鍵衝突。",
|
||||
"hotkey.invalidCombination": "快捷鍵必須包含修飾鍵(Ctrl、Alt、Shift)且只能有一個一般鍵。",
|
||||
"hotkey.placeholder": "按鍵錄製快捷鍵",
|
||||
"hotkey.reset": "重置為預設",
|
||||
"messageModal.cancel": "取消",
|
||||
"messageModal.confirm": "確認",
|
||||
"messageModal.edit": "編輯",
|
||||
"sideNav.collapse": "折疊側邊欄",
|
||||
"sideNav.demoActiveLabel": "啟用",
|
||||
"sideNav.demoFeatureAutoCollapseDesc": "拖動到閾值以下智慧折疊",
|
||||
"sideNav.demoFeatureAutoCollapseTitle": "自動折疊",
|
||||
"sideNav.demoFeaturePerformanceDesc": "無動畫開銷,效能更佳",
|
||||
"sideNav.demoFeaturePerformanceTitle": "效能最佳化",
|
||||
"sideNav.demoFeatureResizeDesc": "拖動調整面板寬度",
|
||||
"sideNav.demoFeatureResizeTitle": "彈性調整",
|
||||
"sideNav.demoFeatureSmartHandleDesc": "懸停顯示切換按鈕",
|
||||
"sideNav.demoFeatureSmartHandleTitle": "智慧手柄",
|
||||
"sideNav.demoFeaturesTitle": "功能特色",
|
||||
"sideNav.demoHint": "嘗試拖動面板邊緣並使用切換按鈕 ->",
|
||||
"sideNav.demoSubtitle": "可拖動調整的工作區風格側邊面板",
|
||||
"sideNav.demoTitle": "DraggableSideNav 示範",
|
||||
"sideNav.expand": "展開側邊欄",
|
||||
"tokenTag.overload": "超額",
|
||||
"tokenTag.remained": "剩餘",
|
||||
"tokenTag.used": "已用"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@
|
|||
},
|
||||
"license": "MIT",
|
||||
"author": "LobeHub <i@lobehub.com>",
|
||||
"sideEffects": false,
|
||||
"sideEffects": [
|
||||
"./src/initialize.ts"
|
||||
],
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"packages/business/*",
|
||||
|
|
@ -37,7 +39,7 @@
|
|||
"build:docker": "pnpm run build:spa && pnpm run build:spa:mobile && pnpm run build:spa:copy && cross-env NODE_OPTIONS=--max-old-space-size=8192 DOCKER=true next build && pnpm run build-sitemap",
|
||||
"build:next": "cross-env NODE_OPTIONS=--max-old-space-size=6144 next build",
|
||||
"build:spa": "rm -rf public/spa && cross-env NODE_OPTIONS=--max-old-space-size=6144 vite build",
|
||||
"build:spa:copy": "mkdir -p public/spa && cp -r dist/desktop/assets public/spa/ && ([ -d dist/mobile/assets ] && cp -r dist/mobile/assets public/spa/ || true) && tsx scripts/generateSpaTemplates.mts",
|
||||
"build:spa:copy": "mkdir -p public/spa && for dir in assets i18n vendor; do ([ -d dist/desktop/$dir ] && cp -r dist/desktop/$dir public/spa/ || true) && ([ -d dist/mobile/$dir ] && cp -r dist/mobile/$dir public/spa/ || true); done && tsx scripts/generateSpaTemplates.mts",
|
||||
"build:spa:mobile": "cross-env NODE_OPTIONS=--max-old-space-size=8192 MOBILE=true vite build",
|
||||
"build:vercel": "bun run build && bun run db:migrate",
|
||||
"build-migrate-db": "bun run db:migrate",
|
||||
|
|
|
|||
|
|
@ -80,9 +80,6 @@ function sharedManualChunks(id: string): string | undefined {
|
|||
if (locale) return `i18n-${locale}`;
|
||||
}
|
||||
|
||||
// dayjs core — keep out of locale chunks so entry doesn't statically pull i18n-ar
|
||||
if (id.includes('/dayjs/') && !id.includes('/dayjs/locale/')) return 'vendor-dayjs';
|
||||
|
||||
// dayjs locale → merge into i18n-{locale}
|
||||
const dayjsMatch = id.match(/dayjs\/locale\/([^/.]+)\.js/);
|
||||
if (dayjsMatch) {
|
||||
|
|
@ -104,6 +101,12 @@ function sharedManualChunks(id: string): string | undefined {
|
|||
}
|
||||
|
||||
export const sharedRollupOutput = {
|
||||
chunkFileNames: (chunkInfo: { name: string }) => {
|
||||
const { name } = chunkInfo;
|
||||
if (name.startsWith('i18n-')) return 'i18n/[name]-[hash].js';
|
||||
if (name.startsWith('vendor-')) return 'vendor/[name]-[hash].js';
|
||||
return 'assets/[name]-[hash].js';
|
||||
},
|
||||
manualChunks: sharedManualChunks,
|
||||
};
|
||||
|
||||
|
|
@ -142,6 +145,7 @@ export function sharedRendererDefine(options: { isElectron: boolean; isMobile: b
|
|||
);
|
||||
|
||||
return {
|
||||
'__CI__': process.env.CI === 'true' ? 'true' : 'false',
|
||||
'__ELECTRON__': JSON.stringify(options.isElectron),
|
||||
'__MOBILE__': JSON.stringify(options.isMobile),
|
||||
...nextPublicDefine,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { Button, Flexbox, FluentEmoji, Highlighter, Text } from '@lobehub/ui';
|
|||
import { Result } from 'antd';
|
||||
import Link from 'next/link';
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const FailedPage = memo(() => {
|
||||
const FailedPage = () => {
|
||||
const { t } = useTranslation('oauth');
|
||||
const [reason] = useQueryState('reason');
|
||||
const [errorMessage] = useQueryState<string>('errorMessage', parseAsString);
|
||||
|
|
@ -40,8 +39,6 @@ const FailedPage = memo(() => {
|
|||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
FailedPage.displayName = 'FailedPage';
|
||||
};
|
||||
|
||||
export default FailedPage;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type RouteConfig } from '@/utils/router';
|
||||
import { type RouteObject } from 'react-router-dom';
|
||||
|
||||
export const BusinessDesktopRoutesWithMainLayout: RouteConfig[] = [];
|
||||
export const BusinessDesktopRoutesWithSettingsLayout: RouteConfig[] = [];
|
||||
export const BusinessDesktopRoutesWithoutMainLayout: RouteConfig[] = [];
|
||||
export const BusinessDesktopRoutesWithMainLayout: RouteObject[] = [];
|
||||
export const BusinessDesktopRoutesWithSettingsLayout: RouteObject[] = [];
|
||||
export const BusinessDesktopRoutesWithoutMainLayout: RouteObject[] = [];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type RouteConfig } from '@/utils/router';
|
||||
import { type RouteObject } from 'react-router-dom';
|
||||
|
||||
export const BusinessMobileRoutesWithMainLayout: RouteConfig[] = [];
|
||||
export const BusinessMobileRoutesWithSettingsLayout: RouteConfig[] = [];
|
||||
export const BusinessMobileRoutesWithoutMainLayout: RouteConfig[] = [];
|
||||
export const BusinessMobileRoutesWithMainLayout: RouteObject[] = [];
|
||||
export const BusinessMobileRoutesWithSettingsLayout: RouteObject[] = [];
|
||||
export const BusinessMobileRoutesWithoutMainLayout: RouteObject[] = [];
|
||||
|
|
|
|||
|
|
@ -2,14 +2,11 @@
|
|||
|
||||
import { Icon, Tag } from '@lobehub/ui';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { CloudIcon, Loader2Icon } from 'lucide-react';
|
||||
import { type CSSProperties } from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface AutoSaveHintProps {
|
||||
lastUpdatedTime?: string | Date | null;
|
||||
saveStatus: 'idle' | 'saving' | 'saved';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { Button, Flexbox, FluentEmoji } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { Button, Collapse, Flexbox, FluentEmoji, Highlighter } from '@lobehub/ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { MAX_WIDTH } from '@/const/layoutTokens';
|
||||
|
|
@ -13,8 +12,9 @@ interface ErrorCaptureProps {
|
|||
reset: () => void;
|
||||
}
|
||||
|
||||
const ErrorCapture = memo<ErrorCaptureProps>(({ reset }) => {
|
||||
const ErrorCapture = ({ error, reset }: ErrorCaptureProps) => {
|
||||
const { t } = useTranslation('error');
|
||||
const hasStack = !!error?.stack;
|
||||
|
||||
return (
|
||||
<Flexbox align={'center'} justify={'center'} style={{ minHeight: '100%', width: '100%' }}>
|
||||
|
|
@ -36,6 +36,26 @@ const ErrorCapture = memo<ErrorCaptureProps>(({ reset }) => {
|
|||
{t('error.title')}
|
||||
</h2>
|
||||
<p style={{ marginBottom: '2em' }}>{t('error.desc')}</p>
|
||||
{hasStack && (
|
||||
<Collapse
|
||||
defaultActiveKey={__CI__ ? ['stack'] : []}
|
||||
expandIconPlacement={'end'}
|
||||
size={'small'}
|
||||
style={{ marginBottom: '1em', maxWidth: '90vw', width: 560 }}
|
||||
variant={'borderless'}
|
||||
items={[
|
||||
{
|
||||
children: (
|
||||
<Highlighter language={'plaintext'} padding={8} variant={'borderless'}>
|
||||
{error.stack!}
|
||||
</Highlighter>
|
||||
),
|
||||
key: 'stack',
|
||||
label: t('error.stack'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<Flexbox horizontal gap={12} style={{ marginBottom: '1em' }}>
|
||||
<Button onClick={() => reset()}>{t('error.retry')}</Button>
|
||||
<Button type={'primary'} onClick={() => (window.location.href = '/')}>
|
||||
|
|
@ -44,8 +64,6 @@ const ErrorCapture = memo<ErrorCaptureProps>(({ reset }) => {
|
|||
</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
ErrorCapture.displayName = 'ErrorCapture';
|
||||
};
|
||||
|
||||
export default ErrorCapture;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { type DropdownMenuCheckboxItem } from '@lobehub/ui';
|
|||
import { ActionIcon, DropdownMenu, Flexbox } from '@lobehub/ui';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { Clock3Icon, PlusIcon } from 'lucide-react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
|
@ -12,8 +11,6 @@ import NavHeader from '@/features/NavHeader';
|
|||
import { useChatStore } from '@/store/chat';
|
||||
import { topicSelectors } from '@/store/chat/slices/topic/selectors';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
time: css`
|
||||
margin-inline-start: 6px;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { type IEditor } from '@lobehub/editor';
|
||||
import { Alert, Skeleton } from '@lobehub/ui';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createStoreUpdater } from 'zustand-utils';
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ import { editorSelectors } from '@/store/document/slices/editor';
|
|||
|
||||
import { type EditorCanvasProps } from './EditorCanvas';
|
||||
import InternalEditor from './InternalEditor';
|
||||
import UnsavedChangesGuard from './UnsavedChangesGuard';
|
||||
|
||||
/**
|
||||
* Loading skeleton for the editor
|
||||
|
|
@ -54,19 +55,25 @@ const DocumentIdMode = memo<DocumentIdModeProps>(
|
|||
autoSave = true,
|
||||
sourceType = 'page',
|
||||
onContentChange,
|
||||
unsavedChangesGuard,
|
||||
style,
|
||||
...editorProps
|
||||
}) => {
|
||||
const { t } = useTranslation('file');
|
||||
const { t } = useTranslation(['file', 'ui']);
|
||||
|
||||
const storeUpdater = createStoreUpdater(useDocumentStore);
|
||||
storeUpdater('activeDocumentId', documentId);
|
||||
storeUpdater('editor', editor);
|
||||
|
||||
// Get document store actions
|
||||
const [onEditorInit, handleContentChangeStore, useFetchDocument, flushSave] = useDocumentStore(
|
||||
(s) => [s.onEditorInit, s.handleContentChange, s.useFetchDocument, s.flushSave],
|
||||
);
|
||||
const [onEditorInit, handleContentChangeStore, useFetchDocument, performSave, flushSave] =
|
||||
useDocumentStore((s) => [
|
||||
s.onEditorInit,
|
||||
s.handleContentChange,
|
||||
s.useFetchDocument,
|
||||
s.performSave,
|
||||
s.flushSave,
|
||||
]);
|
||||
|
||||
useSaveDocumentHotkey(flushSave);
|
||||
|
||||
|
|
@ -75,6 +82,27 @@ const DocumentIdMode = memo<DocumentIdModeProps>(
|
|||
|
||||
// Check loading state via selector (document not yet in store)
|
||||
const isLoading = useDocumentStore(editorSelectors.isDocumentLoading(documentId));
|
||||
const isDirty = useDocumentStore(editorSelectors.isDirty(documentId));
|
||||
const shouldGuardUnsavedChanges = unsavedChangesGuard?.enabled ?? false;
|
||||
|
||||
const handleAutoSaveBeforeLeave = useCallback(async () => {
|
||||
if (!shouldGuardUnsavedChanges) return true;
|
||||
|
||||
handleContentChangeStore();
|
||||
await performSave(documentId);
|
||||
|
||||
const latestDocument = useDocumentStore.getState().documents[documentId];
|
||||
return latestDocument ? !latestDocument.isDirty : true;
|
||||
}, [documentId, handleContentChangeStore, performSave, shouldGuardUnsavedChanges]);
|
||||
|
||||
const unsavedGuardNode = (
|
||||
<UnsavedChangesGuard
|
||||
isDirty={shouldGuardUnsavedChanges && isDirty}
|
||||
message={unsavedChangesGuard?.message || t('form.unsavedWarning', { ns: 'ui' })}
|
||||
title={unsavedChangesGuard?.title || t('form.unsavedChanges', { ns: 'ui' })}
|
||||
onAutoSave={handleAutoSaveBeforeLeave}
|
||||
/>
|
||||
);
|
||||
|
||||
// Handle content change
|
||||
const handleChange = () => {
|
||||
|
|
@ -83,6 +111,8 @@ const DocumentIdMode = memo<DocumentIdModeProps>(
|
|||
};
|
||||
|
||||
const isEditorInitialized = !!editor?.getLexicalEditor();
|
||||
const contentChangeLockRef = useRef(false);
|
||||
const initRunIdRef = useRef(0);
|
||||
|
||||
// 追踪已经为哪个 documentId 调用过 onEditorInit
|
||||
const initializedDocIdRef = useRef<string | null>(null);
|
||||
|
|
@ -97,22 +127,40 @@ const DocumentIdMode = memo<DocumentIdModeProps>(
|
|||
!isLoading &&
|
||||
initializedDocIdRef.current !== documentId
|
||||
) {
|
||||
const runId = ++initRunIdRef.current;
|
||||
initializedDocIdRef.current = documentId;
|
||||
onEditorInit(editor);
|
||||
|
||||
// Lock content-change callback while hydrating document content into editor.
|
||||
contentChangeLockRef.current = true;
|
||||
|
||||
void onEditorInit(editor).finally(() => {
|
||||
queueMicrotask(() => {
|
||||
if (initRunIdRef.current === runId) {
|
||||
contentChangeLockRef.current = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [documentId, editor, isEditorInitialized, isLoading, onEditorInit]);
|
||||
|
||||
// Show loading state
|
||||
if (isLoading) {
|
||||
return <EditorSkeleton />;
|
||||
return (
|
||||
<>
|
||||
{unsavedGuardNode}
|
||||
<EditorSkeleton />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!editor) return null;
|
||||
if (!editor) return unsavedGuardNode;
|
||||
|
||||
return (
|
||||
<>
|
||||
{unsavedGuardNode}
|
||||
{error && <EditorError error={error as Error} />}
|
||||
<InternalEditor
|
||||
contentChangeLockRef={contentChangeLockRef}
|
||||
editor={editor}
|
||||
placeholder={editorProps.placeholder || t('pageEditor.editorPlaceholder')}
|
||||
style={style}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ describe('EditorCanvas', () => {
|
|||
editor={mockEditor}
|
||||
placeholder="Custom placeholder"
|
||||
sourceType="notebook"
|
||||
unsavedChangesGuard={{ enabled: true, message: 'unsaved', title: 'Unsaved' }}
|
||||
onContentChange={onContentChange}
|
||||
onInit={onInit}
|
||||
/>,
|
||||
|
|
@ -124,6 +125,7 @@ describe('EditorCanvas', () => {
|
|||
onInit,
|
||||
placeholder: 'Custom placeholder',
|
||||
sourceType: 'notebook',
|
||||
unsavedChangesGuard: { enabled: true, message: 'unsaved', title: 'Unsaved' },
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,24 @@ import InternalEditor from './InternalEditor';
|
|||
*/
|
||||
type EditorPlugins = Parameters<typeof Editor>[0]['plugins'];
|
||||
|
||||
interface UnsavedChangesGuardOptions {
|
||||
/**
|
||||
* Whether to enable unsaved-changes guard for route navigation and browser unload.
|
||||
* Defaults to false.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
|
||||
/**
|
||||
* Custom message shown in leave confirmation.
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* Custom title shown in leave confirmation.
|
||||
*/
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface EditorCanvasProps {
|
||||
/**
|
||||
* Whether to enable auto-save in DocumentStore. Defaults to true.
|
||||
|
|
@ -96,6 +114,11 @@ export interface EditorCanvasProps {
|
|||
* Extra items to add to the floating toolbar (e.g., "Ask Copilot" button)
|
||||
*/
|
||||
toolbarExtraItems?: ChatInputActionsProps['items'];
|
||||
|
||||
/**
|
||||
* Unsaved changes guard for documentId mode.
|
||||
*/
|
||||
unsavedChangesGuard?: UnsavedChangesGuardOptions;
|
||||
}
|
||||
|
||||
export interface EditorCanvasWithEditorProps extends EditorCanvasProps {
|
||||
|
|
|
|||
|
|
@ -247,6 +247,48 @@ describe('InternalEditor', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should call onContentChange when formatting changes but text stays the same', async () => {
|
||||
const onContentChange = vi.fn();
|
||||
let editorInstance: IEditor | undefined;
|
||||
|
||||
render(
|
||||
<MinimalTestWrapper
|
||||
onContentChange={onContentChange}
|
||||
onEditorReady={(e) => {
|
||||
editorInstance = e;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await moment();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(editorInstance).toBeDefined();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
editorInstance!.setDocument('text', 'Hello');
|
||||
await moment();
|
||||
});
|
||||
|
||||
onContentChange.mockClear();
|
||||
|
||||
await act(async () => {
|
||||
// Keep the same plain text but change formatting structure.
|
||||
editorInstance!.setDocument('markdown', '**Hello**');
|
||||
await moment();
|
||||
});
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onContentChange).toHaveBeenCalled();
|
||||
},
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
});
|
||||
|
||||
it('should track multiple content changes', async () => {
|
||||
const onContentChange = vi.fn();
|
||||
let editorInstance: IEditor | undefined;
|
||||
|
|
@ -294,6 +336,60 @@ describe('InternalEditor', () => {
|
|||
{ timeout: 2000 },
|
||||
);
|
||||
});
|
||||
|
||||
it('should not call onContentChange for programmatic hydration while lock is active', async () => {
|
||||
const onContentChange = vi.fn();
|
||||
const contentChangeLockRef = { current: false };
|
||||
let editorInstance: IEditor | undefined;
|
||||
|
||||
render(
|
||||
<MinimalTestWrapper
|
||||
contentChangeLockRef={contentChangeLockRef}
|
||||
onContentChange={onContentChange}
|
||||
onEditorReady={(e) => {
|
||||
editorInstance = e;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await moment();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(editorInstance).toBeDefined();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
editorInstance!.setDocument('text', 'Doc A');
|
||||
await moment();
|
||||
});
|
||||
|
||||
onContentChange.mockClear();
|
||||
|
||||
contentChangeLockRef.current = true;
|
||||
|
||||
await act(async () => {
|
||||
editorInstance!.setDocument('text', 'Doc B');
|
||||
await moment();
|
||||
});
|
||||
|
||||
expect(onContentChange).not.toHaveBeenCalled();
|
||||
|
||||
contentChangeLockRef.current = false;
|
||||
|
||||
await act(async () => {
|
||||
editorInstance!.setDocument('text', 'Doc B edited');
|
||||
await moment();
|
||||
});
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onContentChange).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('editor content methods', () => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ import {
|
|||
ReactToolbarPlugin,
|
||||
} from '@lobehub/editor';
|
||||
import { Editor, useEditorState } from '@lobehub/editor/react';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { memo, type RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { type EditorCanvasProps } from './EditorCanvas';
|
||||
|
|
@ -41,6 +42,11 @@ const STATIC_PLUGINS = [
|
|||
];
|
||||
|
||||
export interface InternalEditorProps extends EditorCanvasProps {
|
||||
/**
|
||||
* Optional lock ref to suppress content-change callback during programmatic document hydration.
|
||||
*/
|
||||
contentChangeLockRef?: RefObject<boolean>;
|
||||
|
||||
/**
|
||||
* Editor instance (required)
|
||||
*/
|
||||
|
|
@ -52,6 +58,7 @@ export interface InternalEditorProps extends EditorCanvasProps {
|
|||
*/
|
||||
const InternalEditor = memo<InternalEditorProps>(
|
||||
({
|
||||
contentChangeLockRef,
|
||||
editor,
|
||||
extraPlugins,
|
||||
floatingToolbar = true,
|
||||
|
|
@ -136,7 +143,7 @@ const InternalEditor = memo<InternalEditorProps>(
|
|||
}, [editor]);
|
||||
|
||||
// Use refs for stable references across re-renders
|
||||
const previousContentRef = useRef<string | undefined>(undefined);
|
||||
const previousDocumentSnapshotRef = useRef<unknown>(undefined);
|
||||
const onContentChangeRef = useRef(onContentChange);
|
||||
onContentChangeRef.current = onContentChange;
|
||||
|
||||
|
|
@ -148,18 +155,22 @@ const InternalEditor = memo<InternalEditorProps>(
|
|||
const lexicalEditor = editor.getLexicalEditor?.();
|
||||
if (!lexicalEditor) return;
|
||||
|
||||
// Initialize previousContent with current content before registering listener
|
||||
previousContentRef.current = JSON.stringify(editor.getDocument('text'));
|
||||
// Initialize snapshot before registering listener
|
||||
previousDocumentSnapshotRef.current = editor.getDocument('json');
|
||||
|
||||
const unregister = lexicalEditor.registerUpdateListener(({ dirtyElements, dirtyLeaves }) => {
|
||||
// Only process when there are actual content changes
|
||||
// Skip selection-only / caret-movement updates — no content was mutated.
|
||||
if (dirtyElements.size === 0 && dirtyLeaves.size === 0) return;
|
||||
|
||||
const currentContent = JSON.stringify(editor.getDocument('text'));
|
||||
const currentDocumentSnapshot = editor.getDocument('json');
|
||||
|
||||
if (!isEqual(currentDocumentSnapshot, previousDocumentSnapshotRef.current)) {
|
||||
previousDocumentSnapshotRef.current = currentDocumentSnapshot;
|
||||
|
||||
// During document hydration (e.g. route switch), we only advance snapshot
|
||||
// and skip external change callback to avoid false dirty checks.
|
||||
if (contentChangeLockRef?.current) return;
|
||||
|
||||
if (currentContent !== previousContentRef.current) {
|
||||
// Content actually changed
|
||||
previousContentRef.current = currentContent;
|
||||
onContentChangeRef.current?.();
|
||||
}
|
||||
});
|
||||
|
|
@ -167,7 +178,7 @@ const InternalEditor = memo<InternalEditorProps>(
|
|||
return () => {
|
||||
unregister();
|
||||
};
|
||||
}, [editor]); // Only depend on editor, use ref for onContentChange
|
||||
}, [contentChangeLockRef, editor]); // Only depend on stable refs and editor
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
144
src/features/EditorCanvas/UnsavedChangesGuard.test.tsx
Normal file
144
src/features/EditorCanvas/UnsavedChangesGuard.test.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import UnsavedChangesGuard from './UnsavedChangesGuard';
|
||||
|
||||
const useBlockerMock = vi.hoisted(() => vi.fn());
|
||||
const messageLoadingMock = vi.hoisted(() => vi.fn());
|
||||
const messageDestroyMock = vi.hoisted(() => vi.fn());
|
||||
const messageErrorMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('antd', () => ({
|
||||
App: {
|
||||
useApp: () => ({
|
||||
message: {
|
||||
destroy: messageDestroyMock,
|
||||
error: messageErrorMock,
|
||||
loading: messageLoadingMock,
|
||||
},
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('react-router-dom', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const actual = (await vi.importActual('react-router-dom')) as typeof import('react-router-dom');
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useBlocker: (shouldBlock: boolean) => useBlockerMock(shouldBlock),
|
||||
};
|
||||
});
|
||||
|
||||
interface MockBlocker {
|
||||
proceed: ReturnType<typeof vi.fn>;
|
||||
reset: ReturnType<typeof vi.fn>;
|
||||
state: 'blocked' | 'proceeding' | 'unblocked';
|
||||
}
|
||||
|
||||
const createMockBlocker = (state: MockBlocker['state']): MockBlocker => ({
|
||||
proceed: vi.fn(),
|
||||
reset: vi.fn(),
|
||||
state,
|
||||
});
|
||||
|
||||
const createBeforeUnloadEvent = () => {
|
||||
const event = new Event('beforeunload', { cancelable: true }) as BeforeUnloadEvent;
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
configurable: true,
|
||||
value: undefined,
|
||||
writable: true,
|
||||
});
|
||||
return event;
|
||||
};
|
||||
|
||||
describe('UnsavedChangesGuard', () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
useBlockerMock.mockReset();
|
||||
messageLoadingMock.mockReset();
|
||||
messageDestroyMock.mockReset();
|
||||
messageErrorMock.mockReset();
|
||||
useBlockerMock.mockReturnValue(createMockBlocker('unblocked'));
|
||||
});
|
||||
|
||||
it('should auto-save and proceed when route is blocked', async () => {
|
||||
const blocker = createMockBlocker('blocked');
|
||||
const onAutoSave = vi.fn().mockResolvedValue(true);
|
||||
useBlockerMock.mockReturnValue(blocker);
|
||||
|
||||
render(<UnsavedChangesGuard isDirty={true} message="unsaved" onAutoSave={onAutoSave} />);
|
||||
|
||||
expect(useBlockerMock).toHaveBeenCalledWith(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onAutoSave).toHaveBeenCalledTimes(1);
|
||||
expect(messageLoadingMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ content: 'pageEditor.saving', duration: 0 }),
|
||||
);
|
||||
expect(messageDestroyMock).toHaveBeenCalled();
|
||||
expect(blocker.proceed).toHaveBeenCalledTimes(1);
|
||||
expect(blocker.reset).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset navigation when auto-save fails', async () => {
|
||||
const blocker = createMockBlocker('blocked');
|
||||
const onAutoSave = vi.fn().mockResolvedValue(false);
|
||||
useBlockerMock.mockReturnValue(blocker);
|
||||
|
||||
render(<UnsavedChangesGuard isDirty={true} message="unsaved" onAutoSave={onAutoSave} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onAutoSave).toHaveBeenCalledTimes(1);
|
||||
expect(messageErrorMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ content: 'networkError' }),
|
||||
);
|
||||
expect(blocker.reset).toHaveBeenCalledTimes(1);
|
||||
expect(blocker.proceed).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset navigation and show error when auto-save throws', async () => {
|
||||
const blocker = createMockBlocker('blocked');
|
||||
const onAutoSave = vi.fn().mockRejectedValue(new Error('save failed'));
|
||||
useBlockerMock.mockReturnValue(blocker);
|
||||
|
||||
render(<UnsavedChangesGuard isDirty={true} message="unsaved" onAutoSave={onAutoSave} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(messageErrorMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ content: 'save failed' }),
|
||||
);
|
||||
expect(blocker.reset).toHaveBeenCalledTimes(1);
|
||||
expect(blocker.proceed).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should only trigger beforeunload warning when dirty', () => {
|
||||
render(<UnsavedChangesGuard isDirty={true} message="unsaved" />);
|
||||
|
||||
const dirtyEvent = createBeforeUnloadEvent();
|
||||
window.dispatchEvent(dirtyEvent);
|
||||
expect(dirtyEvent.defaultPrevented).toBe(true);
|
||||
expect(dirtyEvent.returnValue).toBe('unsaved');
|
||||
});
|
||||
|
||||
it('should not trigger beforeunload warning when clean', () => {
|
||||
render(<UnsavedChangesGuard isDirty={false} message="unsaved" />);
|
||||
|
||||
const cleanEvent = createBeforeUnloadEvent();
|
||||
window.dispatchEvent(cleanEvent);
|
||||
expect(cleanEvent.defaultPrevented).toBe(false);
|
||||
expect(cleanEvent.returnValue).toBeUndefined();
|
||||
});
|
||||
});
|
||||
94
src/features/EditorCanvas/UnsavedChangesGuard.tsx
Normal file
94
src/features/EditorCanvas/UnsavedChangesGuard.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
'use client';
|
||||
|
||||
import { App } from 'antd';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useBlocker } from 'react-router-dom';
|
||||
|
||||
interface UnsavedChangesGuardProps {
|
||||
isDirty: boolean;
|
||||
message: string;
|
||||
onAutoSave?: () => Promise<boolean>;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const UnsavedChangesGuard = memo<UnsavedChangesGuardProps>(
|
||||
({ isDirty, message, onAutoSave, title: _title }) => {
|
||||
void _title;
|
||||
const { t } = useTranslation('file');
|
||||
const { message: messageApi } = App.useApp();
|
||||
const blocker = useBlocker(isDirty);
|
||||
|
||||
const blockerRef = useRef(blocker);
|
||||
const isSavingRef = useRef(false);
|
||||
blockerRef.current = blocker;
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state !== 'blocked') return;
|
||||
if (isSavingRef.current) return;
|
||||
|
||||
isSavingRef.current = true;
|
||||
const messageKey = `editor-leave-auto-save-${Date.now()}`;
|
||||
|
||||
const leaveWithAutoSave = async () => {
|
||||
messageApi.loading({
|
||||
content: t('pageEditor.saving'),
|
||||
duration: 0,
|
||||
key: messageKey,
|
||||
});
|
||||
|
||||
try {
|
||||
const saved = (await onAutoSave?.()) ?? true;
|
||||
|
||||
if (!saved) {
|
||||
messageApi.error({
|
||||
content: t('networkError'),
|
||||
duration: 2,
|
||||
key: messageKey,
|
||||
});
|
||||
blockerRef.current?.reset?.();
|
||||
return;
|
||||
}
|
||||
|
||||
messageApi.destroy(messageKey);
|
||||
blockerRef.current?.proceed?.();
|
||||
} catch (error) {
|
||||
const content =
|
||||
error instanceof Error && error.message ? error.message : t('networkError');
|
||||
|
||||
messageApi.error({
|
||||
content,
|
||||
duration: 2,
|
||||
key: messageKey,
|
||||
});
|
||||
blockerRef.current?.reset?.();
|
||||
} finally {
|
||||
isSavingRef.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
void leaveWithAutoSave();
|
||||
}, [blocker.state, message, messageApi, onAutoSave, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDirty) return;
|
||||
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = message;
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [isDirty, message]);
|
||||
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
UnsavedChangesGuard.displayName = 'UnsavedChangesGuard';
|
||||
|
||||
export default UnsavedChangesGuard;
|
||||
|
|
@ -16,7 +16,7 @@ interface EditorCanvasProps {
|
|||
}
|
||||
|
||||
const EditorCanvas = memo<EditorCanvasProps>(({ placeholder, style }) => {
|
||||
const { t } = useTranslation(['file', 'editor']);
|
||||
const { t } = useTranslation(['file', 'ui']);
|
||||
|
||||
const editor = usePageEditorStore((s) => s.editor);
|
||||
const documentId = usePageEditorStore((s) => s.documentId);
|
||||
|
|
@ -32,6 +32,11 @@ const EditorCanvas = memo<EditorCanvasProps>(({ placeholder, style }) => {
|
|||
slashItems={slashItems}
|
||||
style={style}
|
||||
toolbarExtraItems={askCopilotItem}
|
||||
unsavedChangesGuard={{
|
||||
enabled: true,
|
||||
message: t('form.unsavedWarning', { ns: 'ui' }),
|
||||
title: t('form.unsavedChanges', { ns: 'ui' }),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { EditorProvider } from '@lobehub/editor/react';
|
|||
import { Flexbox } from '@lobehub/ui';
|
||||
import { cssVar } from 'antd-style';
|
||||
import type { FC } from 'react';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import Loading from '@/components/Loading/BrandTextLoading';
|
||||
import DiffAllToolbar from '@/features/EditorCanvas/DiffAllToolbar';
|
||||
|
|
@ -13,8 +13,6 @@ import WideScreenContainer from '@/features/WideScreenContainer';
|
|||
import { useRegisterFilesHotkeys } from '@/hooks/useHotkeys';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { builtinAgentSelectors } from '@/store/agent/selectors';
|
||||
import { useDocumentStore } from '@/store/document';
|
||||
import { editorSelectors } from '@/store/document/slices/editor';
|
||||
import { usePageStore } from '@/store/page';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
|
|
@ -60,32 +58,9 @@ const PageEditorCanvas = memo(() => {
|
|||
const editor = usePageEditorStore((s) => s.editor);
|
||||
const documentId = usePageEditorStore((s) => s.documentId);
|
||||
|
||||
// Get isDirty from DocumentStore
|
||||
const isDirty = useDocumentStore((s) =>
|
||||
documentId ? editorSelectors.isDirty(documentId)(s) : false,
|
||||
);
|
||||
|
||||
// Register Files scope and save document hotkey
|
||||
useRegisterFilesHotkeys();
|
||||
|
||||
// Warn user before leaving page with unsaved changes
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (isDirty) {
|
||||
// Prevent default and show browser confirmation dialog
|
||||
e.preventDefault();
|
||||
// Most modern browsers require returnValue to be set
|
||||
e.returnValue = '';
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [isDirty]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTitle />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
import { App, Input } from 'antd';
|
||||
import { createStaticStyles, cssVar, cx } from 'antd-style';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { isNull } from 'es-toolkit/compat';
|
||||
import { FileBoxIcon, FileText, FolderIcon } from 'lucide-react';
|
||||
import { type DragEvent } from 'react';
|
||||
|
|
@ -38,9 +37,6 @@ import { useFileItemDropdown } from '../../ItemDropdown/useFileItemDropdown';
|
|||
import ChunksBadge from './ChunkTag';
|
||||
import TruncatedFileName from './TruncatedFileName';
|
||||
|
||||
// Initialize dayjs plugin once at module level
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export const FILE_DATE_WIDTH = 160;
|
||||
export const FILE_SIZE_WIDTH = 140;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
import dayjs from 'dayjs';
|
||||
import isToday from 'dayjs/plugin/isToday';
|
||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { enableMapSet } from 'immer';
|
||||
|
||||
enableMapSet();
|
||||
|
||||
// Dayjs plugins - extend once at app init to avoid duplicate extensions in components
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use client';
|
||||
|
||||
import { ConfigProvider } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { memo, type PropsWithChildren, useEffect, useState } from 'react';
|
||||
|
|
@ -10,20 +8,28 @@ import { createI18nNext } from '@/locales/create';
|
|||
import { getAntdLocale } from '@/utils/locale';
|
||||
|
||||
const dayjsLocaleLoaders = import.meta.glob<{ default: ILocale }>(
|
||||
'/node_modules/dayjs/locale/{ar,bg,de,en,es,fa,fr,it,ja,ko,nl,pl,pt-br,ru,tr,vi,zh-cn,zh-tw}.js',
|
||||
'/node_modules/dayjs/esm/locale/{ar,bg,de,en,es,fa,fr,it,ja,ko,nl,pl,pt-br,ru,tr,vi,zh-cn,zh-tw}.js',
|
||||
);
|
||||
|
||||
const dayjsLocaleAliases: Record<string, string> = {
|
||||
'en-us': 'en',
|
||||
'zh': 'zh-cn',
|
||||
};
|
||||
|
||||
const updateDayjs = async (lang: string) => {
|
||||
const locale = lang.toLowerCase() === 'en-us' ? 'en' : lang.toLowerCase();
|
||||
const key = `/node_modules/dayjs/locale/${locale}.js`;
|
||||
const loader = dayjsLocaleLoaders[key] ?? dayjsLocaleLoaders['/node_modules/dayjs/locale/en.js'];
|
||||
const locale = dayjsLocaleAliases[lang.toLowerCase()] ?? lang.toLowerCase();
|
||||
const key = `/node_modules/dayjs/esm/locale/${locale}.js`;
|
||||
const loader =
|
||||
dayjsLocaleLoaders[key] ?? dayjsLocaleLoaders['/node_modules/dayjs/esm/locale/en.js'];
|
||||
|
||||
try {
|
||||
const mod = await loader!();
|
||||
const mod = await loader();
|
||||
|
||||
dayjs.locale(mod.default);
|
||||
} catch {
|
||||
console.warn(`dayjs locale for ${lang} not found, fallback to en`);
|
||||
const fallback = await dayjsLocaleLoaders['/node_modules/dayjs/locale/en.js']!();
|
||||
} catch (error) {
|
||||
console.error('error', error);
|
||||
console.error(`dayjs locale for ${lang} not found, fallback to en`);
|
||||
const fallback = await dayjsLocaleLoaders['/node_modules/dayjs/esm/locale/en.js']!();
|
||||
dayjs.locale(fallback.default);
|
||||
}
|
||||
};
|
||||
|
|
@ -38,16 +44,21 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
|
|||
const [lang, setLang] = useState(defaultLang);
|
||||
const [locale, setLocale] = useState(antdLocale);
|
||||
|
||||
// Set dayjs locale immediately on mount (don't wait for i18n init) to avoid
|
||||
// "a few seconds ago" showing in English when UI is already in Chinese
|
||||
useEffect(() => {
|
||||
if (defaultLang) updateDayjs(defaultLang);
|
||||
}, [defaultLang]);
|
||||
|
||||
if (!i18n.instance.isInitialized)
|
||||
i18n.init().then(async () => {
|
||||
if (!lang) return;
|
||||
await updateDayjs(lang);
|
||||
const resolvedLang = i18n.instance.language || defaultLang;
|
||||
if (resolvedLang) await updateDayjs(resolvedLang);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleLang = async (lng: string) => {
|
||||
setLang(lng);
|
||||
if (lang === lng) return;
|
||||
const newLocale = await getAntdLocale(lng);
|
||||
setLocale(newLocale);
|
||||
await updateDayjs(lng);
|
||||
|
|
@ -57,7 +68,7 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
|
|||
return () => {
|
||||
i18n.instance.off('languageChanged', handleLang);
|
||||
};
|
||||
}, [i18n, lang]);
|
||||
}, [i18n]);
|
||||
|
||||
const documentDir = isRtlLang(lang!) ? 'rtl' : 'ltr';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
'error.backHome': 'Back to Home',
|
||||
'error.desc': 'Give it a try later, or go back to the known world.',
|
||||
'error.stack': 'Error Stack',
|
||||
'error.retry': 'Reload',
|
||||
'error.title': 'Oops, something went wrong..',
|
||||
'fetchError.detail': 'Error details',
|
||||
|
|
|
|||
|
|
@ -1,67 +1,51 @@
|
|||
export default {
|
||||
chat: {
|
||||
'chat.avatar': 'Avatar',
|
||||
'chat.placeholder': '...',
|
||||
'tokenTag.overload': 'Overload',
|
||||
'tokenTag.remained': 'Remained',
|
||||
'tokenTag.used': 'Used',
|
||||
},
|
||||
common: {
|
||||
'common.cancel': 'Cancel',
|
||||
'common.confirm': 'Confirm',
|
||||
'common.delete': 'Delete',
|
||||
'common.edit': 'Edit',
|
||||
},
|
||||
editableMessage: {
|
||||
'editableMessage.addProps': 'Add Props',
|
||||
'editableMessage.delete': 'Delete',
|
||||
'editableMessage.input': 'Input',
|
||||
'editableMessage.inputPlaceholder': 'Please enter sample input content',
|
||||
'editableMessage.output': 'Output',
|
||||
'editableMessage.outputPlaceholder': 'Please enter sample output content',
|
||||
'editableMessage.system': 'System',
|
||||
},
|
||||
emojiPicker: {
|
||||
'emojiPicker.delete': 'Delete',
|
||||
'emojiPicker.draggerDesc': 'Click or drag image to this area to upload',
|
||||
'emojiPicker.emoji': 'Emoji',
|
||||
'emojiPicker.fileTypeError': 'You can only upload image files!',
|
||||
'emojiPicker.upload': 'Upload',
|
||||
'emojiPicker.uploadBtn': 'Crop and upload',
|
||||
},
|
||||
form: {
|
||||
'form.reset': 'Reset',
|
||||
'form.submit': 'Submit',
|
||||
'form.unsavedChanges': 'Unsaved changes',
|
||||
'form.unsavedWarning': 'You have unsaved changes. Are you sure you want to leave?',
|
||||
},
|
||||
hotkey: {
|
||||
'hotkey.conflict': 'This hotkey conflicts with an existing one.',
|
||||
'hotkey.invalidCombination':
|
||||
'The hotkey must include a modifier key (Ctrl, Alt, Shift) and only one normal key.',
|
||||
'hotkey.placeholder': 'Press keys to record hotkey',
|
||||
'hotkey.reset': 'Reset to default',
|
||||
},
|
||||
messageModal: {
|
||||
'messageModal.cancel': 'Cancel',
|
||||
'messageModal.confirm': 'Confirm',
|
||||
'messageModal.edit': 'Edit',
|
||||
},
|
||||
sideNav: {
|
||||
'sideNav.collapse': 'Collapse sidebar',
|
||||
'sideNav.demoActiveLabel': 'Active',
|
||||
'sideNav.demoFeatureAutoCollapseDesc': 'Drag below threshold to smart collapse',
|
||||
'sideNav.demoFeatureAutoCollapseTitle': 'Auto collapse',
|
||||
'sideNav.demoFeaturePerformanceDesc': 'No animation overhead for better performance',
|
||||
'sideNav.demoFeaturePerformanceTitle': 'Performance',
|
||||
'sideNav.demoFeatureResizeDesc': 'Drag to adjust panel width',
|
||||
'sideNav.demoFeatureResizeTitle': 'Flexible resize',
|
||||
'sideNav.demoFeatureSmartHandleDesc': 'Hover to show toggle button',
|
||||
'sideNav.demoFeatureSmartHandleTitle': 'Smart handle',
|
||||
'sideNav.demoFeaturesTitle': 'Features',
|
||||
'sideNav.demoHint': 'Try dragging the panel edge and using the toggle button ->',
|
||||
'sideNav.demoSubtitle': 'A workspace style side panel with draggable resize',
|
||||
'sideNav.demoTitle': 'DraggableSideNav Demo',
|
||||
'sideNav.expand': 'Expand sidebar',
|
||||
},
|
||||
'chat.avatar': 'Avatar',
|
||||
'chat.placeholder': '...',
|
||||
'common.cancel': 'Cancel',
|
||||
'common.confirm': 'Confirm',
|
||||
'common.delete': 'Delete',
|
||||
'common.edit': 'Edit',
|
||||
'editableMessage.addProps': 'Add Props',
|
||||
'editableMessage.delete': 'Delete',
|
||||
'editableMessage.input': 'Input',
|
||||
'editableMessage.inputPlaceholder': 'Please enter sample input content',
|
||||
'editableMessage.output': 'Output',
|
||||
'editableMessage.outputPlaceholder': 'Please enter sample output content',
|
||||
'editableMessage.system': 'System',
|
||||
'emojiPicker.delete': 'Delete',
|
||||
'emojiPicker.draggerDesc': 'Click or drag image to this area to upload',
|
||||
'emojiPicker.emoji': 'Emoji',
|
||||
'emojiPicker.fileTypeError': 'You can only upload image files!',
|
||||
'emojiPicker.upload': 'Upload',
|
||||
'emojiPicker.uploadBtn': 'Crop and upload',
|
||||
'form.reset': 'Reset',
|
||||
'form.submit': 'Submit',
|
||||
'form.unsavedChanges': 'Unsaved changes',
|
||||
'form.unsavedWarning': 'You have unsaved changes. Are you sure you want to leave?',
|
||||
'hotkey.conflict': 'This hotkey conflicts with an existing one.',
|
||||
'hotkey.invalidCombination':
|
||||
'The hotkey must include a modifier key (Ctrl, Alt, Shift) and only one normal key.',
|
||||
'hotkey.placeholder': 'Press keys to record hotkey',
|
||||
'hotkey.reset': 'Reset to default',
|
||||
'messageModal.cancel': 'Cancel',
|
||||
'messageModal.confirm': 'Confirm',
|
||||
'messageModal.edit': 'Edit',
|
||||
'sideNav.collapse': 'Collapse sidebar',
|
||||
'sideNav.demoActiveLabel': 'Active',
|
||||
'sideNav.demoFeatureAutoCollapseDesc': 'Drag below threshold to smart collapse',
|
||||
'sideNav.demoFeatureAutoCollapseTitle': 'Auto collapse',
|
||||
'sideNav.demoFeaturePerformanceDesc': 'No animation overhead for better performance',
|
||||
'sideNav.demoFeaturePerformanceTitle': 'Performance',
|
||||
'sideNav.demoFeatureResizeDesc': 'Drag to adjust panel width',
|
||||
'sideNav.demoFeatureResizeTitle': 'Flexible resize',
|
||||
'sideNav.demoFeatureSmartHandleDesc': 'Hover to show toggle button',
|
||||
'sideNav.demoFeatureSmartHandleTitle': 'Smart handle',
|
||||
'sideNav.demoFeaturesTitle': 'Features',
|
||||
'sideNav.demoHint': 'Try dragging the panel edge and using the toggle button ->',
|
||||
'sideNav.demoSubtitle': 'A workspace style side panel with draggable resize',
|
||||
'sideNav.demoTitle': 'DraggableSideNav Demo',
|
||||
'sideNav.expand': 'Expand sidebar',
|
||||
'tokenTag.overload': 'Overload',
|
||||
'tokenTag.remained': 'Remained',
|
||||
'tokenTag.used': 'Used',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { Text } from '@lobehub/ui';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { memo } from 'react';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
export const Time = memo<{ date: string | number | Date }>(({ date }) => {
|
||||
return (
|
||||
<Text fontSize={12} style={{ flex: 'none' }} type={'secondary'}>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { ActionIconGroup, Block, Flexbox, Grid, Markdown, Tag, Text } from '@lob
|
|||
import { App } from 'antd';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { omit } from 'es-toolkit/compat';
|
||||
import { CopyIcon, RotateCcwSquareIcon, Trash2 } from 'lucide-react';
|
||||
import { type RuntimeImageGenParams } from 'model-bank';
|
||||
|
|
@ -55,9 +54,6 @@ const styles = createStaticStyles(({ css, cssVar, cx }) => ({
|
|||
`,
|
||||
}));
|
||||
|
||||
// 扩展 dayjs 插件
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface GenerationBatchItemProps {
|
||||
batch: GenerationBatch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { type ExperienceListItem } from '@lobechat/types';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
|
@ -9,8 +7,6 @@ import ProgressIcon from '@/routes/(main)/memory/features/ProgressIcon';
|
|||
|
||||
import ExperienceDropdown from '../../ExperienceDropdown';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface ExperienceCardProps {
|
||||
experience: ExperienceListItem;
|
||||
onClick: (experience: ExperienceListItem) => void;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { Text } from '@lobehub/ui';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { memo } from 'react';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface TimeProps {
|
||||
capturedAt?: Date | number | string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
|
@ -9,8 +7,6 @@ import ProgressIcon from '@/routes/(main)/memory/features/ProgressIcon';
|
|||
|
||||
import PreferenceDropdown from '../../PreferenceDropdown';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface PreferenceCardProps {
|
||||
onClick?: () => void;
|
||||
preference: DisplayPreferenceMemory;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import isToday from 'dayjs/plugin/isToday';
|
||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
|
@ -15,10 +12,6 @@ import { formatNumber } from '@/utils/format';
|
|||
|
||||
import { type UsageChartProps } from '../../../types';
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
|
||||
const computeSpend = (
|
||||
data: UsageLog[],
|
||||
): {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
'use client';
|
||||
|
||||
import type { RouteObject } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
BusinessDesktopRoutesWithMainLayout,
|
||||
BusinessDesktopRoutesWithoutMainLayout,
|
||||
|
|
@ -65,11 +67,10 @@ import VideoPage from '@/routes/(main)/video';
|
|||
import DesktopVideoLayout from '@/routes/(main)/video/_layout';
|
||||
import ShareTopicPage from '@/routes/share/t/[id]';
|
||||
import ShareTopicLayout from '@/routes/share/t/[id]/_layout';
|
||||
import { type RouteConfig } from '@/utils/router';
|
||||
import { ErrorBoundary, redirectElement } from '@/utils/router';
|
||||
|
||||
// Desktop router configuration — all sync imports for Electron local build
|
||||
export const desktopRoutes: RouteConfig[] = [
|
||||
export const desktopRoutes: RouteObject[] = [
|
||||
{
|
||||
children: [
|
||||
// Chat routes (agent)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import { type RouteObject } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
BusinessDesktopRoutesWithMainLayout,
|
||||
BusinessDesktopRoutesWithoutMainLayout,
|
||||
} from '@/business/client/BusinessDesktopRoutes';
|
||||
import { type RouteConfig } from '@/utils/router';
|
||||
import { dynamicElement, dynamicLayout, ErrorBoundary, redirectElement } from '@/utils/router';
|
||||
|
||||
// Desktop router configuration (declarative mode)
|
||||
export const desktopRoutes: RouteConfig[] = [
|
||||
export const desktopRoutes: RouteObject[] = [
|
||||
{
|
||||
children: [
|
||||
// Chat routes (agent)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
'use client';
|
||||
|
||||
import { type RouteObject } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
BusinessMobileRoutesWithMainLayout,
|
||||
BusinessMobileRoutesWithoutMainLayout,
|
||||
} from '@/business/client/BusinessMobileRoutes';
|
||||
import { type RouteConfig } from '@/utils/router';
|
||||
import { dynamicElement, dynamicLayout, ErrorBoundary, redirectElement } from '@/utils/router';
|
||||
|
||||
// Mobile router configuration (declarative mode)
|
||||
export const mobileRoutes: RouteConfig[] = [
|
||||
export const mobileRoutes: RouteObject[] = [
|
||||
{
|
||||
children: [
|
||||
// Chat routes
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ export class DocumentActionImpl {
|
|||
content: content ?? undefined,
|
||||
editorData,
|
||||
lastSavedContent: content ?? undefined,
|
||||
lastSavedEditorData: editorData,
|
||||
sourceType,
|
||||
topicId,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -278,6 +278,42 @@ describe('DocumentStore - Editor Actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('handleContentChange', () => {
|
||||
it('should mark document dirty when editorData changes even if markdown is unchanged', () => {
|
||||
const { result } = renderHook(() => useDocumentStore());
|
||||
const mockEditor = {
|
||||
getDocument: vi.fn((type: string) => {
|
||||
if (type === 'markdown') return '# Test';
|
||||
if (type === 'json') return { type: 'doc', version: 2 };
|
||||
return null;
|
||||
}),
|
||||
setDocument: vi.fn(),
|
||||
} as any;
|
||||
|
||||
act(() => {
|
||||
result.current.initDocumentWithEditor({
|
||||
content: '# Test',
|
||||
documentId: 'doc-1',
|
||||
editor: mockEditor,
|
||||
editorData: { type: 'doc', version: 1 },
|
||||
sourceType: 'page',
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.documents['doc-1'].isDirty).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.handleContentChange();
|
||||
});
|
||||
|
||||
expect(result.current.documents['doc-1']).toMatchObject({
|
||||
content: '# Test',
|
||||
editorData: { type: 'doc', version: 2 },
|
||||
isDirty: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setEditorState', () => {
|
||||
it('should set editor state', () => {
|
||||
const { result } = renderHook(() => useDocumentStore());
|
||||
|
|
|
|||
|
|
@ -62,8 +62,9 @@ export class EditorActionImpl {
|
|||
const markdown = (editor.getDocument('markdown') as unknown as string) || '';
|
||||
const editorData = editor.getDocument('json');
|
||||
|
||||
// Check if content actually changed
|
||||
const contentChanged = markdown !== doc.lastSavedContent;
|
||||
const markdownChanged = markdown !== doc.lastSavedContent;
|
||||
const editorDataChanged = !isEqual(editorData, doc.lastSavedEditorData);
|
||||
const contentChanged = markdownChanged || editorDataChanged;
|
||||
|
||||
internal_dispatchDocument(
|
||||
{
|
||||
|
|
@ -177,6 +178,7 @@ export class EditorActionImpl {
|
|||
editorData: structuredClone(currentEditorData),
|
||||
isDirty: false,
|
||||
lastSavedContent: currentContent,
|
||||
lastSavedEditorData: structuredClone(currentEditorData),
|
||||
lastUpdatedTime: new Date(),
|
||||
saveStatus: 'saved',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ export interface EditorContentState {
|
|||
* Last saved content for comparison
|
||||
*/
|
||||
lastSavedContent: string;
|
||||
/**
|
||||
* Last saved editor JSON for comparison
|
||||
*/
|
||||
lastSavedEditorData?: any;
|
||||
/**
|
||||
* Last updated time
|
||||
*/
|
||||
|
|
@ -85,6 +89,7 @@ export const createInitialEditorContentState = (
|
|||
editorData: null,
|
||||
isDirty: false,
|
||||
lastSavedContent: '',
|
||||
lastSavedEditorData: null,
|
||||
lastUpdatedTime: null,
|
||||
saveStatus: 'idle',
|
||||
sourceType,
|
||||
|
|
|
|||
3
src/types/global.d.ts
vendored
3
src/types/global.d.ts
vendored
|
|
@ -28,6 +28,9 @@ declare global {
|
|||
};
|
||||
}
|
||||
|
||||
/** Vite define: running in CI environment (e.g. CI=true) */
|
||||
const __CI__: boolean;
|
||||
|
||||
/** Vite define: current bundle is mobile variant */
|
||||
const __MOBILE__: boolean;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { toast } from '@lobehub/ui';
|
||||
import { type ComponentType, type ReactElement } from 'react';
|
||||
import { createElement, lazy, memo, Suspense, useCallback, useEffect } from 'react';
|
||||
import { createElement, lazy, memo, Suspense, useCallback, useEffect, useRef } from 'react';
|
||||
import type { RouteObject } from 'react-router-dom';
|
||||
import {
|
||||
createBrowserRouter,
|
||||
|
|
@ -101,11 +102,36 @@ export interface ErrorBoundaryProps {
|
|||
|
||||
export const ErrorBoundary = ({ resetPath }: ErrorBoundaryProps) => {
|
||||
const error = useRouteError() as Error;
|
||||
const reloadRef = useRef(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const reset = useCallback(() => {
|
||||
navigate(resetPath);
|
||||
}, [navigate, resetPath]);
|
||||
let message = '';
|
||||
|
||||
if (error instanceof Error) {
|
||||
message = error.message;
|
||||
} else if (typeof error === 'string') {
|
||||
message = error;
|
||||
} else if (error && typeof error === 'object' && 'statusText' in error) {
|
||||
const statusText = (error as { statusText?: unknown }).statusText;
|
||||
if (typeof statusText === 'string') message = statusText;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
message?.startsWith('Failed to fetch dynamically imported module') &&
|
||||
window.sessionStorage.getItem('reload') !== '1'
|
||||
) {
|
||||
if (reloadRef.current) return null;
|
||||
|
||||
toast.info('Web app has been updated so it needs to be reloaded.');
|
||||
window.sessionStorage.setItem('reload', '1');
|
||||
window.location.reload();
|
||||
reloadRef.current = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createElement(ErrorCapture, { error, reset });
|
||||
};
|
||||
|
|
@ -138,20 +164,6 @@ export const NavigatorRegistrar = memo(() => {
|
|||
return null;
|
||||
});
|
||||
|
||||
/**
|
||||
* Route configuration object type (RouteObject-style for createBrowserRouter)
|
||||
*/
|
||||
export interface RouteConfig {
|
||||
children?: RouteConfig[];
|
||||
element?: ReactElement;
|
||||
errorElement?: ReactElement;
|
||||
// HydrateFallback is ignored in declarative mode
|
||||
HydrateFallback?: ComponentType;
|
||||
index?: boolean;
|
||||
loader?: (args: { params: Record<string, string | undefined> }) => unknown;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface CreateAppRouterOptions {
|
||||
basename?: string;
|
||||
}
|
||||
|
|
@ -168,11 +180,11 @@ export interface CreateAppRouterOptions {
|
|||
* </SPAGlobalProvider>
|
||||
* );
|
||||
*/
|
||||
export function createAppRouter(routes: RouteConfig[], options?: CreateAppRouterOptions) {
|
||||
export function createAppRouter(routes: RouteObject[], options?: CreateAppRouterOptions) {
|
||||
return createBrowserRouter(
|
||||
[
|
||||
{
|
||||
children: routes as RouteObject[],
|
||||
children: routes,
|
||||
element: (
|
||||
<BusinessGlobalProvider>
|
||||
<Outlet />
|
||||
|
|
|
|||
|
|
@ -39,18 +39,23 @@ export default defineConfig({
|
|||
name: 'lobe-dev-proxy-print',
|
||||
configureServer(server: ViteDevServer) {
|
||||
const ONLINE_HOST = 'https://app.lobehub.com';
|
||||
server.httpServer?.once('listening', () => {
|
||||
const address = server.httpServer?.address();
|
||||
const port = typeof address === 'object' && address ? address.port : 9876;
|
||||
const localHost = `http://localhost:${port}`;
|
||||
const proxyUrl = `${ONLINE_HOST}/_dangerous_local_dev_proxy?debug-host=${encodeURIComponent(localHost)}`;
|
||||
|
||||
setTimeout(() => {
|
||||
console.info();
|
||||
console.info(` \x1B[1m\x1B[35mDebug Proxy:\x1B[0m \x1B[36m${proxyUrl}\x1B[0m`);
|
||||
console.info();
|
||||
}, 100);
|
||||
});
|
||||
const c = {
|
||||
green: (s: string) => `\x1B[32m${s}\x1B[0m`,
|
||||
bold: (s: string) => `\x1B[1m${s}\x1B[0m`,
|
||||
cyan: (s: string) => `\x1B[36m${s}\x1B[0m`,
|
||||
};
|
||||
const { info } = server.config.logger;
|
||||
return () => {
|
||||
server.printUrls = () => {
|
||||
const urls = server.resolvedUrls;
|
||||
if (!urls?.local?.[0]) return;
|
||||
const localHost = urls.local[0].replace(/\/$/, '');
|
||||
const proxyUrl = `${ONLINE_HOST}/_dangerous_local_dev_proxy?debug-host=${encodeURIComponent(localHost)}`;
|
||||
const colorUrl = (url: string) =>
|
||||
c.cyan(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`));
|
||||
info(` ${c.green('➜')} ${c.bold('Debug Proxy')}: ${colorUrl(proxyUrl)}`);
|
||||
};
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue