diff --git a/apps/bot/locales/ar.json b/apps/bot/locales/ar.json new file mode 100644 index 0000000..5a0802e --- /dev/null +++ b/apps/bot/locales/ar.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "المعامل {{arg}} مفقود. الاستخدام: {{usage}}", + "invalidInt": "يجب أن يكون المعامل عدداً صحيحاً. تم استلام: {{value}}", + "invalidNumber": "يجب أن يكون المعامل رقماً. تم استلام: {{value}}", + "invalidBoolean": "يجب أن يكون المعامل قيمة منطقية (صواب/خطأ). تم استلام: {{value}}", + "invalidUser": "تعذر العثور على مستخدم من {{value}}", + "invalidChannel": "تعذر العثور على قناة من {{value}}", + "invalidRole": "تعذر العثور على دور من {{value}}", + "tooMany": "عدد كبير جداً من المعاملات ({{count}}) تم اكتشافها: {{extras}}. الاستخدام: {{usage}}" + }, + "permissions": { + "user": "ليس لديك الصلاحيات التالية: {{permissions}}" + }, + "execution": "حدث خطأ غير متوقع أثناء التنفيذ.", + "cooldown": "يرجى الانتظار {{seconds}} ثانية قبل إعادة استخدام هذا الأمر.", + "rateLimit": "عدد طلبات كبير جداً. يرجى الانتظار {{seconds}} ثانية قبل المحاولة مرة أخرى.", + "executionQueued": "تم قبول الأمر وإضافته إلى قائمة التنفيذ." + }, + "categories": { + "fun": "ترفيه", + "utility": "أدوات", + "core": "أساسي" + }, + "commands": { + "kiss": { + "name": "قبلة", + "description": "إرسال قبلة إلى مستخدم.", + "args": { + "user": "المستخدم المستهدف" + }, + "examples": { + "basic": "إرسال قبلة إلى عضو" + }, + "responses": { + "success": "{{from}} يرسل قبلة إلى {{to}}." + } + }, + "ping": { + "name": "بينغ", + "description": "التحقق من زمن استجابة البوت.", + "examples": { + "basic": "فحص الحالة" + }, + "responses": { + "pong": "بونغ. زمن الاستجابة: {{latency}}ms." + } + }, + "presence": { + "name": "الحالة", + "description": "تكوين حالة البوت عبر لوحة تفاعلية.", + "examples": { + "slash": "فتح لوحة الحالة التفاعلية" + }, + "responses": { + "panel": "لوحة الحالة\nالحالة: {{status}}\nالنشاط: {{activityType}}\nالتدوير: كل {{rotationIntervalSeconds}} ثانية\nالنص الحالي ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nالمعاينة: {{activityPreview}}\nالنصوص: {{activityTexts}}\nالمتغيرات ({{doubleBracesHint}}): {{variables}}", + "notOwner": "فقط صاحب الأمر يمكنه استخدام هذه اللوحة.", + "invalidSelection": "اختيار غير صالح.", + "invalidInterval": "فاصل غير صالح. استخدم عدداً بين {{minSeconds}} و {{maxSeconds}} ثانية.", + "modalTimeout": "انتهى الوقت المخصص للتعديل. يرجى المحاولة مرة أخرى." + }, + "ui": { + "status": { + "placeholder": "اختر الحالة", + "options": { + "online": { + "label": "متصل", + "description": "عرض البوت كمتصل" + }, + "idle": { + "label": "خامل", + "description": "عرض البوت كخامل" + }, + "dnd": { + "label": "عدم الإزعاج", + "description": "عرض البوت في وضع عدم الإزعاج" + }, + "invisible": { + "label": "غير مرئي", + "description": "عرض البوت كغير متصل" + }, + "streaming": { + "label": "بث مباشر", + "description": "عرض البوت في وضع البث" + } + } + }, + "activity": { + "placeholder": "اختر نوع النشاط", + "options": { + "PLAYING": { + "label": "يلعب", + "description": "يعرض يلعب ..." + }, + "STREAMING": { + "label": "بث", + "description": "يعرض مباشر على ..." + }, + "WATCHING": { + "label": "يشاهد", + "description": "يعرض يشاهد ..." + }, + "LISTENING": { + "label": "يستمع", + "description": "يعرض يستمع ..." + }, + "COMPETING": { + "label": "منافسة", + "description": "يعرض منافسة ..." + }, + "CUSTOM": { + "label": "حالة مخصصة", + "description": "يعرض حالة مخصصة" + } + } + }, + "embed": { + "title": "إعداد الحالة", + "description": "تغيير الحالة ونوع النشاط والنصوص وتدويرها.", + "footer": "لوحة تفاعلية /presence", + "fields": { + "status": "الحالة", + "activity": "نوع النشاط", + "text": "النص" + } + }, + "textButton": "📝 تعديل النصوص", + "intervalButton": "⏱️ الفاصل الزمني", + "modal": { + "title": "نصوص الحالة", + "label": "كل سطر نص (استخدم {{var}})", + "placeholder": "صيانة\n{{guild_count}} خوادم | {{prefix}}مساعدة" + }, + "intervalModal": { + "title": "فاصل التدوير", + "label": "الثواني بين كل نص", + "placeholder": "مثال: 30" + } + } + }, + "logs": { + "name": "السجلات", + "description": "تكوين سجلات أحداث ديسكورد عبر لوحة تفاعلية.", + "examples": { + "slash": "فتح لوحة السجلات التفاعلية" + }, + "responses": { + "slashOnly": "استخدم /logs لفتح هذه اللوحة.", + "guildOnly": "يجب استخدام هذا الأمر داخل سيرفر.", + "notOwner": "فقط صاحب الأمر يمكنه استخدام هذه اللوحة.", + "invalidSelection": "اختيار غير صالح.", + "allEnabled": "تم تفعيل جميع سجلات الأحداث.", + "allDisabled": "تم تعطيل جميع سجلات الأحداث.", + "eventEnabled": "تم تفعيل الحدث {{event}}.", + "eventDisabled": "تم تعطيل الحدث {{event}}.", + "channelsCreated": "تم تجهيز قنوات السجلات. تم إنشاء: {{created}}، إعادة استخدام: {{reused}}.", + "channelsPartial": "تم تجهيز قنوات السجلات جزئياً. تم إنشاء: {{created}}، إعادة استخدام: {{reused}}، فشل في: {{failed}}.", + "channelsFailed": "تعذر إنشاء قنوات السجلات. فشل في: {{failed}}." + }, + "ui": { + "embed": { + "title": "إعداد السجلات", + "description": "تفعيل أو تعطيل أحداث ديسكورد وتوجيه السجلات إلى القنوات." + }, + "status": { + "enabled": "مفعل", + "disabled": "معطل" + }, + "pageLabel": "صفحة {{page}}/{{pageCount}}", + "enabledCountLabel": "الأحداث المفعلة: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "الأحداث", + "channelLabel": "القناة", + "channelNotConfigured": "#غير-محدد", + "feedbackLabel": "آخر إجراء", + "buttons": { + "enableAll": "تفعيل جميع السجلات", + "disableAll": "تعطيل جميع السجلات", + "createChannels": "إنشاء قنوات السجلات", + "previousPage": "السابق", + "nextPage": "التالي", + "enableEvent": "تفعيل {{event}}", + "disableEvent": "تعطيل {{event}}" + }, + "events": { + "messageCreate": "تم إرسال رسالة", + "messageDelete": "تم حذف رسالة", + "messageUpdate": "تم تعديل رسالة", + "messageBulkDelete": "حذف جماعي للرسائل", + "guildMemberAdd": "انضم عضو", + "guildMemberRemove": "غادر عضو", + "guildMemberUpdate": "تم تحديث عضو", + "interactionCreate": "تفاعل", + "channelCreate": "تم إنشاء قناة", + "channelDelete": "تم حذف قناة", + "channelUpdate": "تم تحديث قناة", + "roleCreate": "تم إنشاء دور", + "roleDelete": "تم حذف دور", + "roleUpdate": "تم تحديث دور", + "threadCreate": "تم إنشاء خيط", + "threadDelete": "تم حذف خيط", + "threadUpdate": "تم تحديث خيط", + "emojiCreate": "تم إنشاء إيموجي", + "emojiDelete": "تم حذف إيموجي", + "emojiUpdate": "تم تحديث إيموجي", + "guildUpdate": "تم تحديث السيرفر", + "guildUnavailable": "السيرفر غير متاح", + "guildBanAdd": "تم حظر عضو", + "guildBanRemove": "تم إلغاء حظر عضو", + "inviteCreate": "تم إنشاء دعوة", + "inviteDelete": "تم حذف دعوة" + } + } + }, + "welcome": { + "name": "ترحيب", + "description": "تكوين رسائل الترحيب عبر لوحة تفاعلية.", + "examples": { + "slash": "فتح لوحة إعداد الترحيب" + }, + "responses": { + "guildOnly": "يجب استخدام هذا الأمر داخل سيرفر.", + "notOwner": "فقط صاحب الأمر يمكنه استخدام هذه اللوحة.", + "invalidSelection": "اختيار غير صالح.", + "testSuccess": "تم إرسال رسالة اختبار في {{channel}}.", + "testDisabled": "وحدة الترحيب معطلة.", + "testMissingChannel": "لا توجد قناة مهيأة. استخدم زر القناة.", + "testChannelUnavailable": "القناة غير موجودة أو غير صالحة.", + "testMissingPermissions": "البوت لا يملك صلاحية إرسال الرسائل في هذه القناة.", + "testFailed": "تعذر إرسال رسالة الاختبار. حاول لاحقاً." + }, + "ui": { + "embed": { + "title": "إعداد الترحيب", + "description": "تعديل الحالة والقناة ونوع الرسالة وإجراء اختبار.", + "fields": { + "status": "الحالة", + "channel": "القناة", + "type": "النوع", + "channelPicker": "اختيار القناة", + "autoRoles": "الأدوار التلقائية" + } + }, + "status": { + "enabled": "🟢 مفعل", + "disabled": "🔴 معطل" + }, + "channelNotConfigured": "#غير-محدد", + "autoRolesNotConfigured": "لا توجد أدوار تلقائية", + "channelPickerPlaceholder": "اختر قناة الترحيب", + "channelPickerHint": "اختر القناة من القائمة.", + "rolePickerPlaceholder": "اختر أدوار تلقائية", + "rolePickerHint": "اختر الأدوار لإضافتها تلقائياً.", + "buttons": { + "toggle": "تبديل الحالة", + "channel": "تحديد القناة", + "channelCancel": "إغلاق اختيار القناة", + "roles": "إضافة أدوار تلقائية", + "rolesCancel": "إغلاق اختيار الأدوار", + "rolesClear": "مسح الأدوار التلقائية", + "test": "اختبار" + }, + "type": { + "placeholder": "اختر نوع الرسالة", + "options": { + "simple": { + "label": "رسالة بسيطة", + "description": "إرسال رسالة نصية عادية" + }, + "embed": { + "label": "Embed", + "description": "إرسال رسالة ترحيب بشكل embed" + }, + "container": { + "label": "حاوية (V2)", + "description": "إرسال Container Components V2" + }, + "image": { + "label": "صورة", + "description": "إرسال رسالة مع صورة" + } + } + } + }, + "templates": { + "simple": "🎉 مرحباً {{user}} في **{{guild}}**!", + "embedTitle": "مرحباً!", + "embedDescription": "{{user}} انضم إلى **{{guild}}**.", + "containerTitle": "مرحباً", + "containerDescription": "{{user}} انضم إلى **{{guild}}**.", + "imageTitle": "مرحباً", + "imageDescription": "في سيرفر ديسكورد {{guild}}" + } + }, + "goodbye": { + "name": "وداعاً", + "description": "تكوين رسائل المغادرة عبر لوحة تفاعلية.", + "examples": { + "slash": "فتح لوحة إعداد رسائل المغادرة" + }, + "responses": { + "guildOnly": "يجب استخدام هذا الأمر داخل سيرفر.", + "notOwner": "فقط صاحب الأمر يمكنه استخدام هذه اللوحة.", + "invalidSelection": "اختيار غير صالح.", + "testSuccess": "تم إرسال رسالة اختبار في {{channel}}.", + "testDisabled": "وحدة المغادرة معطلة.", + "testMissingChannel": "لا توجد قناة مهيأة. استخدم زر القناة.", + "testChannelUnavailable": "القناة غير موجودة أو غير صالحة.", + "testMissingPermissions": "البوت لا يملك صلاحية إرسال الرسائل في هذه القناة.", + "testFailed": "تعذر إرسال رسالة الاختبار. حاول لاحقاً." + }, + "ui": { + "embed": { + "title": "إعداد المغادرة", + "description": "تعديل الحالة والقناة ونوع الرسالة وإجراء اختبار.", + "fields": { + "status": "الحالة", + "channel": "القناة", + "type": "النوع", + "channelPicker": "اختيار القناة" + } + }, + "status": { + "enabled": "🟢 مفعل", + "disabled": "🔴 معطل" + }, + "channelNotConfigured": "#غير-محدد", + "channelPickerPlaceholder": "اختر قناة المغادرة", + "channelPickerHint": "اختر القناة من القائمة.", + "buttons": { + "toggle": "تبديل الحالة", + "channel": "تحديد القناة", + "channelCancel": "إغلاق اختيار القناة", + "test": "اختبار" + }, + "type": { + "placeholder": "اختر نوع الرسالة", + "options": { + "simple": { + "label": "رسالة بسيطة", + "description": "إرسال رسالة نصية عادية" + }, + "embed": { + "label": "Embed", + "description": "إرسال رسالة مغادرة بشكل embed" + }, + "container": { + "label": "حاوية (V2)", + "description": "إرسال Container Components V2" + }, + "image": { + "label": "صورة", + "description": "إرسال رسالة مع صورة" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} غادر **{{guild}}**.", + "embedTitle": "وداعاً", + "embedDescription": "{{user}} غادر **{{guild}}**.", + "containerTitle": "وداعاً", + "containerDescription": "{{user}} غادر **{{guild}}**.", + "imageTitle": "وداعاً", + "imageDescription": "من سيرفر ديسكورد {{guild}}" + } + }, + "help": { + "name": "مساعدة", + "description": "عرض قائمة الأوامر أو تفاصيل أمر معين.", + "args": { + "command": "اسم الأمر أو اختياري" + }, + "examples": { + "basic": "عرض جميع الأوامر", + "single": "عرض تفاصيل أمر" + }, + "errors": { + "notFound": "لا يوجد أمر يطابق {{query}}." + }, + "embed": { + "title": "مساعدة الأوامر", + "description": "استخدم {{usage}} لفحص أمر.", + "categoryEmpty": "لا توجد أوامر في هذه الفئة.", + "detailsTitle": "الأمر: {{name}}", + "detailsDescription": "{{description}}", + "footer": "الفئة: {{source}}", + "fields": { + "usage": "الاستخدام", + "arguments": "المعاملات", + "examples": "أمثلة" + } + }, + "labels": { + "prefix": "بادئة", + "slash": "سلاش", + "required": "إجباري", + "optional": "اختياري", + "noArgs": "لا توجد معاملات.", + "noExamples": "لا توجد أمثلة." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(لا يوجد محتوى)", + "unknown": "غير معروف", + "none": "لا شيء" + }, + "embed": { + "title": "حدث: {{event}}", + "detailsField": "التفاصيل" + }, + "summaries": { + "messageCreate": "تم إرسال رسالة بواسطة {{user}} في {{channel}}", + "messageDelete": "تم حذف رسالة في {{channel}}", + "messageUpdate": "تم تعديل رسالة في {{channel}}", + "messageBulkDelete": "حذف جماعي: {{count}} رسالة", + "guildMemberAdd": "انضم عضو: {{user}}", + "guildMemberRemove": "غادر عضو: {{user}}", + "guildMemberUpdate": "تم تحديث عضو: {{user}}", + "interactionGeneric": "تفاعل بواسطة {{user}}", + "interactionSlash": "أمر /{{command}} بواسطة {{user}}", + "interactionButton": "تفاعل زر بواسطة {{user}}", + "interactionSelect": "تفاعل قائمة بواسطة {{user}}", + "interactionModal": "إرسال نموذج بواسطة {{user}}", + "channelCreate": "تم إنشاء قناة: {{channel}}", + "channelDelete": "تم حذف قناة: {{channel}}", + "channelUpdate": "تم تحديث قناة: {{channel}}", + "roleCreate": "تم إنشاء دور: {{role}}", + "roleDelete": "تم حذف دور: {{role}}", + "roleUpdate": "تم تحديث دور: {{role}}", + "threadCreate": "تم إنشاء خيط: {{thread}}", + "threadDelete": "تم حذف خيط: {{thread}}", + "threadUpdate": "تم تحديث خيط: {{thread}}", + "emojiCreate": "تم إنشاء إيموجي: {{emoji}}", + "emojiDelete": "تم حذف إيموجي: {{emoji}}", + "emojiUpdate": "تم تحديث إيموجي: {{emoji}}", + "guildUpdate": "تم تحديث السيرفر: {{guild}}", + "guildUnavailable": "السيرفر غير متاح مؤقتاً: {{guild}}", + "guildBanAdd": "تم حظر مستخدم: {{user}}", + "guildBanRemove": "تم إلغاء حظر مستخدم: {{user}}", + "inviteCreate": "تم إنشاء دعوة: {{code}}", + "inviteDelete": "تم حذف دعوة: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/bn.json b/apps/bot/locales/bn.json new file mode 100644 index 0000000..39061fb --- /dev/null +++ b/apps/bot/locales/bn.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "আর্গুমেন্ট {{arg}} অনুপস্থিত। ব্যবহার: {{usage}}", + "invalidInt": "আর্গুমেন্টটি একটি পূর্ণসংখ্যা হতে হবে। প্রাপ্ত: {{value}}", + "invalidNumber": "আর্গুমেন্টটি একটি সংখ্যা হতে হবে। প্রাপ্ত: {{value}}", + "invalidBoolean": "আর্গুমেন্টটি একটি বুলিয়ান হতে হবে। প্রাপ্ত: {{value}}", + "invalidUser": "{{value}} থেকে ব্যবহারকারী খুঁজে পাওয়া যায়নি", + "invalidChannel": "{{value}} থেকে চ্যানেল খুঁজে পাওয়া যায়নি", + "invalidRole": "{{value}} থেকে রোল খুঁজে পাওয়া যায়নি", + "tooMany": "অতিরিক্ত আর্গুমেন্ট ({{count}}) সনাক্ত হয়েছে: {{extras}}। ব্যবহার: {{usage}}" + }, + "permissions": { + "user": "আপনার অনুমতি নেই: {{permissions}}" + }, + "execution": "কার্য সম্পাদনের সময় একটি অপ্রত্যাশিত ত্রুটি ঘটেছে।", + "cooldown": "কমান্ডটি আবার ব্যবহার করার আগে {{seconds}} সেকেন্ড অপেক্ষা করুন।", + "rateLimit": "অনেক বেশি অনুরোধ। আবার চেষ্টা করার আগে {{seconds}} সেকেন্ড অপেক্ষা করুন।", + "executionQueued": "কমান্ড গ্রহণ করা হয়েছে এবং সারিতে যুক্ত হয়েছে।" + }, + "categories": { + "fun": "মজা", + "utility": "ইউটিলিটি", + "core": "মূল" + }, + "commands": { + "kiss": { + "name": "চুমু", + "description": "একজন ব্যবহারকারীকে চুমু পাঠান।", + "args": { + "user": "টার্গেট ব্যবহারকারী" + }, + "examples": { + "basic": "একজন সদস্যকে চুমু পাঠানো" + }, + "responses": { + "success": "{{from}} {{to}}-কে চুমু পাঠিয়েছে।" + } + }, + "ping": { + "name": "ping", + "description": "বটের ওয়েবসকেট ল্যাটেন্সি পরীক্ষা করুন।", + "examples": { + "basic": "স্বাস্থ্য পরীক্ষা" + }, + "responses": { + "pong": "Pong. ওয়েবসকেট ল্যাটেন্সি: {{latency}}ms." + } + }, + "presence": { + "name": "উপস্থিতি", + "description": "ইন্টারঅ্যাকটিভ প্যানেলের মাধ্যমে বটের উপস্থিতি কনফিগার করুন।", + "examples": { + "slash": "উপস্থিতি ইন্টারঅ্যাকটিভ প্যানেল খুলুন" + }, + "responses": { + "panel": "উপস্থিতি প্যানেল\nস্ট্যাটাস: {{status}}\nঅ্যাক্টিভিটি: {{activityType}}\nরোটেশন: প্রতি {{rotationIntervalSeconds}} সেকেন্ডে\nবর্তমান টেক্সট ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nপ্রিভিউ: {{activityPreview}}\nটেক্সটসমূহ: {{activityTexts}}\nভেরিয়েবল ({{doubleBracesHint}}): {{variables}}", + "notOwner": "শুধুমাত্র কমান্ডের লেখক এই প্যানেল ব্যবহার করতে পারবেন।", + "invalidSelection": "অবৈধ নির্বাচন।", + "invalidInterval": "অবৈধ ইন্টারভ্যাল। {{minSeconds}} থেকে {{maxSeconds}} সেকেন্ডের মধ্যে একটি পূর্ণসংখ্যা ব্যবহার করুন।", + "modalTimeout": "টেক্সট পরিবর্তনের সময় শেষ হয়েছে। আবার বোতামে ক্লিক করুন।" + }, + "ui": { + "status": { + "placeholder": "একটি স্ট্যাটাস নির্বাচন করুন", + "options": { + "online": { + "label": "অনলাইন", + "description": "বটকে অনলাইন দেখায়" + }, + "idle": { + "label": "নিষ্ক্রিয়", + "description": "বটকে নিষ্ক্রিয় দেখায়" + }, + "dnd": { + "label": "ডু নট ডিস্টার্ব", + "description": "বটকে বিরক্ত করবেন না মোডে দেখায়" + }, + "invisible": { + "label": "অফলাইন", + "description": "বটকে অফলাইন দেখায়" + }, + "streaming": { + "label": "স্ট্রিমিং", + "description": "বটকে স্ট্রিমিং মোডে দেখায়" + } + } + }, + "activity": { + "placeholder": "অ্যাক্টিভিটির ধরন নির্বাচন করুন", + "options": { + "PLAYING": { + "label": "খেলছে", + "description": "দেখায় খেলছে ..." + }, + "STREAMING": { + "label": "স্ট্রিমিং", + "description": "দেখায় লাইভে ..." + }, + "WATCHING": { + "label": "দেখছে", + "description": "দেখায় দেখছে ..." + }, + "LISTENING": { + "label": "শুনছে", + "description": "দেখায় শুনছে ..." + }, + "COMPETING": { + "label": "প্রতিযোগিতা", + "description": "দেখায় প্রতিযোগিতা ..." + }, + "CUSTOM": { + "label": "কাস্টম স্ট্যাটাস", + "description": "একটি কাস্টম স্ট্যাটাস দেখায়" + } + } + }, + "embed": { + "title": "উপস্থিতি কনফিগারেশন", + "description": "স্ট্যাটাস, অ্যাক্টিভিটির ধরন, একাধিক টেক্সট এবং তাদের রোটেশন পরিবর্তন করুন।", + "footer": "ইন্টারঅ্যাকটিভ প্যানেল /presence", + "fields": { + "status": "স্ট্যাটাস", + "activity": "অ্যাক্টিভিটির ধরন", + "text": "টেক্সট" + } + }, + "textButton": "📝 টেক্সট পরিবর্তন করুন", + "intervalButton": "⏱️ ইন্টারভ্যাল", + "modal": { + "title": "উপস্থিতি টেক্সটসমূহ", + "label": "প্রতি লাইনে একটি টেক্সট ({{var}} ব্যবহার করুন)", + "placeholder": "মেইনটেন্যান্স\n{{guild_count}} সার্ভার | {{prefix}}help" + }, + "intervalModal": { + "title": "রোটেশন ইন্টারভ্যাল", + "label": "প্রতি টেক্সটের মাঝে সেকেন্ড", + "placeholder": "উদাহরণ: 30" + } + } + }, + "logs": { + "name": "লগস", + "description": "ডিসকর্ড ইভেন্ট লগ কনফিগার করুন ইন্টারঅ্যাকটিভ প্যানেলের মাধ্যমে।", + "examples": { + "slash": "লগস ইন্টারঅ্যাকটিভ প্যানেল খুলুন" + }, + "responses": { + "slashOnly": "এই প্যানেল খুলতে /logs ব্যবহার করুন।", + "guildOnly": "এই কমান্ড শুধুমাত্র সার্ভারে ব্যবহার করা যাবে।", + "notOwner": "শুধুমাত্র কমান্ডের লেখক এই প্যানেল ব্যবহার করতে পারবেন।", + "invalidSelection": "অবৈধ নির্বাচন।", + "allEnabled": "সব ইভেন্ট লগ সক্রিয় করা হয়েছে।", + "allDisabled": "সব ইভেন্ট লগ নিষ্ক্রিয় করা হয়েছে।", + "eventEnabled": "ইভেন্ট {{event}} সক্রিয় করা হয়েছে।", + "eventDisabled": "ইভেন্ট {{event}} নিষ্ক্রিয় করা হয়েছে।", + "channelsCreated": "লগ চ্যানেল প্রস্তুত। তৈরি হয়েছে: {{created}}, পুনরায় ব্যবহার: {{reused}}।", + "channelsPartial": "আংশিকভাবে লগ চ্যানেল তৈরি হয়েছে। তৈরি: {{created}}, পুনরায় ব্যবহার: {{reused}}, ব্যর্থ: {{failed}}।", + "channelsFailed": "লগ চ্যানেল তৈরি করা যায়নি। ব্যর্থ: {{failed}}।" + }, + "ui": { + "embed": { + "title": "লগ কনফিগারেশন", + "description": "প্রতিটি ডিসকর্ড ইভেন্ট চালু/বন্ধ করুন এবং ক্যাটাগরি অনুযায়ী চ্যানেলে লগ পাঠান।" + }, + "status": { + "enabled": "সক্রিয়", + "disabled": "নিষ্ক্রিয়" + }, + "pageLabel": "পৃষ্ঠা {{page}}/{{pageCount}}", + "enabledCountLabel": "সক্রিয় ইভেন্ট: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "ইভেন্টসমূহ", + "channelLabel": "চ্যানেল", + "channelNotConfigured": "#কনফিগার-নেই", + "feedbackLabel": "শেষ কার্যক্রম", + "buttons": { + "enableAll": "✅ সব লগ চালু করুন", + "disableAll": "❌ সব লগ বন্ধ করুন", + "createChannels": "📁 লগ চ্যানেল তৈরি করুন", + "previousPage": "আগের", + "nextPage": "পরের", + "enableEvent": "{{event}} চালু করুন", + "disableEvent": "{{event}} বন্ধ করুন" + }, + "events": { + "messageCreate": "বার্তা তৈরি হয়েছে", + "messageDelete": "বার্তা মুছে ফেলা হয়েছে", + "messageUpdate": "বার্তা আপডেট হয়েছে", + "messageBulkDelete": "একাধিক বার্তা মুছে ফেলা হয়েছে", + "guildMemberAdd": "সদস্য যোগ দিয়েছে", + "guildMemberRemove": "সদস্য চলে গেছে", + "guildMemberUpdate": "সদস্য আপডেট হয়েছে", + "interactionCreate": "ইন্টারঅ্যাকশন", + "channelCreate": "চ্যানেল তৈরি হয়েছে", + "channelDelete": "চ্যানেল মুছে ফেলা হয়েছে", + "channelUpdate": "চ্যানেল আপডেট হয়েছে", + "roleCreate": "রোল তৈরি হয়েছে", + "roleDelete": "রোল মুছে ফেলা হয়েছে", + "roleUpdate": "রোল আপডেট হয়েছে", + "threadCreate": "থ্রেড তৈরি হয়েছে", + "threadDelete": "থ্রেড মুছে ফেলা হয়েছে", + "threadUpdate": "থ্রেড আপডেট হয়েছে", + "emojiCreate": "ইমোজি তৈরি হয়েছে", + "emojiDelete": "ইমোজি মুছে ফেলা হয়েছে", + "emojiUpdate": "ইমোজি আপডেট হয়েছে", + "guildUpdate": "সার্ভার আপডেট হয়েছে", + "guildUnavailable": "সার্ভার অনুপলব্ধ", + "guildBanAdd": "সদস্য ব্যান করা হয়েছে", + "guildBanRemove": "ব্যান তুলে নেওয়া হয়েছে", + "inviteCreate": "ইনভাইট তৈরি হয়েছে", + "inviteDelete": "ইনভাইট মুছে ফেলা হয়েছে" + } + } + }, + "welcome": { + "name": "স্বাগতম", + "description": "ইন্টারঅ্যাকটিভ প্যানেলের মাধ্যমে স্বাগতম বার্তা কনফিগার করুন।", + "examples": { + "slash": "স্বাগতম কনফিগারেশন প্যানেল খুলুন" + }, + "responses": { + "guildOnly": "এই কমান্ড শুধুমাত্র সার্ভারে ব্যবহার করা যাবে।", + "notOwner": "শুধুমাত্র কমান্ডের লেখক এই প্যানেল ব্যবহার করতে পারবেন।", + "invalidSelection": "অবৈধ নির্বাচন।", + "testSuccess": "{{channel}}-এ টেস্ট বার্তা পাঠানো হয়েছে।", + "testDisabled": "ওয়েলকাম মডিউল নিষ্ক্রিয়।", + "testMissingChannel": "কোনো চ্যানেল কনফিগার করা নেই। চ্যানেল বোতাম ব্যবহার করুন।", + "testChannelUnavailable": "কনফিগার করা চ্যানেল পাওয়া যায়নি বা সমর্থিত নয়।", + "testMissingPermissions": "এই চ্যানেলে বার্তা পাঠানোর অনুমতি বটের নেই।", + "testFailed": "টেস্ট বার্তা পাঠানো যায়নি। পরে আবার চেষ্টা করুন।" + }, + "ui": { + "embed": { + "title": "ওয়েলকাম কনফিগারেশন", + "description": "স্ট্যাটাস, চ্যানেল, বার্তার ধরন সেট করুন এবং টেস্ট চালান।", + "fields": { + "status": "স্ট্যাটাস", + "channel": "চ্যানেল", + "type": "ধরন", + "channelPicker": "চ্যানেল নির্বাচন", + "autoRoles": "অটো রোল" + } + }, + "status": { + "enabled": "🟢 সক্রিয়", + "disabled": "🔴 নিষ্ক্রিয়" + }, + "channelNotConfigured": "#কনফিগার-নেই", + "autoRolesNotConfigured": "কোনো অটো রোল কনফিগার নেই", + "channelPickerPlaceholder": "একটি ওয়েলকাম চ্যানেল নির্বাচন করুন", + "channelPickerHint": "নিচের সিলেক্ট মেনু থেকে চ্যানেল বেছে নিন।", + "rolePickerPlaceholder": "এক বা একাধিক অটো রোল নির্বাচন করুন", + "rolePickerHint": "অটো তালিকায় যোগ করতে নিচের মেনু থেকে রোল বেছে নিন।", + "buttons": { + "toggle": "স্ট্যাটাস পরিবর্তন", + "channel": "চ্যানেল কনফিগার করুন", + "channelCancel": "চ্যানেল সিলেক্ট বন্ধ করুন", + "roles": "অটো রোল যোগ করুন", + "rolesCancel": "রোল সিলেক্ট বন্ধ করুন", + "rolesClear": "অটো রোল মুছে ফেলুন", + "test": "টেস্ট" + }, + "type": { + "placeholder": "বার্তার ধরন নির্বাচন করুন", + "options": { + "simple": { + "label": "সাধারণ বার্তা", + "description": "একটি সাধারণ টেক্সট বার্তা পাঠায়" + }, + "embed": { + "label": "এম্বেড", + "description": "একটি এম্বেড স্বাগতম বার্তা পাঠায়" + }, + "container": { + "label": "কন্টেইনার (V2)", + "description": "Components V2 কন্টেইনার পাঠায়" + }, + "image": { + "label": "ছবি", + "description": "ছবিসহ এম্বেড পাঠায়" + } + } + } + }, + "templates": { + "simple": "🎉 স্বাগতম {{user}} **{{guild}}**-এ!", + "embedTitle": "স্বাগতম!", + "embedDescription": "{{user}} **{{guild}}**-এ যোগ দিয়েছে।", + "containerTitle": "স্বাগতম", + "containerDescription": "{{user}} **{{guild}}**-এ যোগ দিয়েছে।", + "imageTitle": "স্বাগতম", + "imageDescription": "Discord সার্ভার {{guild}}-এ" + } + }, + "goodbye": { + "name": "বিদায়", + "description": "ইন্টারঅ্যাকটিভ প্যানেলের মাধ্যমে বিদায় বার্তা কনফিগার করুন।", + "examples": { + "slash": "বিদায় কনফিগারেশন প্যানেল খুলুন" + }, + "responses": { + "guildOnly": "এই কমান্ড শুধুমাত্র সার্ভারে ব্যবহার করা যাবে।", + "notOwner": "শুধুমাত্র কমান্ডের লেখক এই প্যানেল ব্যবহার করতে পারবেন।", + "invalidSelection": "অবৈধ নির্বাচন।", + "testSuccess": "{{channel}}-এ টেস্ট বার্তা পাঠানো হয়েছে।", + "testDisabled": "গুডবাই মডিউল নিষ্ক্রিয়।", + "testMissingChannel": "কোনো চ্যানেল কনফিগার করা নেই। চ্যানেল বোতাম ব্যবহার করুন।", + "testChannelUnavailable": "কনফিগার করা চ্যানেল পাওয়া যায়নি বা সমর্থিত নয়।", + "testMissingPermissions": "এই চ্যানেলে বার্তা পাঠানোর অনুমতি বটের নেই।", + "testFailed": "টেস্ট বার্তা পাঠানো যায়নি। পরে আবার চেষ্টা করুন।" + }, + "ui": { + "embed": { + "title": "গুডবাই কনফিগারেশন", + "description": "স্ট্যাটাস, চ্যানেল, বার্তার ধরন সেট করুন এবং টেস্ট চালান।", + "fields": { + "status": "স্ট্যাটাস", + "channel": "চ্যানেল", + "type": "ধরন", + "channelPicker": "চ্যানেল নির্বাচন" + } + }, + "status": { + "enabled": "🟢 সক্রিয়", + "disabled": "🔴 নিষ্ক্রিয়" + }, + "channelNotConfigured": "#কনফিগার-নেই", + "channelPickerPlaceholder": "একটি বিদায় চ্যানেল নির্বাচন করুন", + "channelPickerHint": "নিচের সিলেক্ট মেনু থেকে চ্যানেল বেছে নিন।", + "buttons": { + "toggle": "স্ট্যাটাস পরিবর্তন", + "channel": "চ্যানেল কনফিগার করুন", + "channelCancel": "চ্যানেল সিলেক্ট বন্ধ করুন", + "test": "টেস্ট" + }, + "type": { + "placeholder": "বার্তার ধরন নির্বাচন করুন", + "options": { + "simple": { + "label": "সাধারণ বার্তা", + "description": "একটি সাধারণ টেক্সট বার্তা পাঠায়" + }, + "embed": { + "label": "এম্বেড", + "description": "একটি বিদায় এম্বেড পাঠায়" + }, + "container": { + "label": "কন্টেইনার (V2)", + "description": "Components V2 কন্টেইনার পাঠায়" + }, + "image": { + "label": "ছবি", + "description": "ছবিসহ এম্বেড পাঠায়" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} **{{guild}}** ছেড়ে গেছে।", + "embedTitle": "বিদায়", + "embedDescription": "{{user}} **{{guild}}** ছেড়ে গেছে।", + "containerTitle": "বিদায়", + "containerDescription": "{{user}} **{{guild}}** ছেড়ে গেছে।", + "imageTitle": "বিদায়", + "imageDescription": "Discord সার্ভার {{guild}} থেকে" + } + }, + "help": { + "name": "সহায়তা", + "description": "সব কমান্ডের তালিকা বা একটি নির্দিষ্ট কমান্ডের বিস্তারিত দেখুন।", + "args": { + "command": "ঐচ্ছিক কমান্ড নাম বা ট্রিগার" + }, + "examples": { + "basic": "সব কমান্ড দেখান", + "single": "একটি কমান্ডের বিস্তারিত দেখান" + }, + "errors": { + "notFound": "{{query}} এর সাথে মিলে এমন কোনো কমান্ড পাওয়া যায়নি।" + }, + "embed": { + "title": "কমান্ড সহায়তা", + "description": "একটি কমান্ড দেখতে {{usage}} ব্যবহার করুন।", + "categoryEmpty": "এই ক্যাটাগরিতে কোনো কমান্ড নেই।", + "detailsTitle": "কমান্ড: {{name}}", + "detailsDescription": "{{description}}", + "footer": "ক্যাটাগরি: {{source}}", + "fields": { + "usage": "ব্যবহার", + "arguments": "আর্গুমেন্ট", + "examples": "উদাহরণ" + } + }, + "labels": { + "prefix": "প্রিফিক্স", + "slash": "স্ল্যাশ", + "required": "আবশ্যক", + "optional": "ঐচ্ছিক", + "noArgs": "কোনো আর্গুমেন্ট নেই।", + "noExamples": "কোনো উদাহরণ নেই।" + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(কোনো কনটেন্ট নেই)", + "unknown": "অজানা", + "none": "কিছুই নেই" + }, + "embed": { + "title": "ইভেন্ট: {{event}}", + "detailsField": "বিস্তারিত" + }, + "summaries": { + "messageCreate": "{{user}} {{channel}}-এ বার্তা পাঠিয়েছে", + "messageDelete": "{{channel}}-এ বার্তা মুছে ফেলা হয়েছে", + "messageUpdate": "{{channel}}-এ বার্তা আপডেট হয়েছে", + "messageBulkDelete": "একাধিক মুছে ফেলা: {{count}} টি বার্তা অপসারণ করা হয়েছে", + "guildMemberAdd": "সদস্য যোগ দিয়েছে: {{user}}", + "guildMemberRemove": "সদস্য চলে গেছে: {{user}}", + "guildMemberUpdate": "সদস্য আপডেট হয়েছে: {{user}}", + "interactionGeneric": "{{user}} দ্বারা ইন্টারঅ্যাকশন", + "interactionSlash": "{{user}} দ্বারা স্ল্যাশ কমান্ড /{{command}}", + "interactionButton": "{{user}} দ্বারা বোতাম ইন্টারঅ্যাকশন", + "interactionSelect": "{{user}} দ্বারা সিলেক্ট মেনু ইন্টারঅ্যাকশন", + "interactionModal": "{{user}} দ্বারা মডাল সাবমিট", + "channelCreate": "চ্যানেল তৈরি হয়েছে: {{channel}}", + "channelDelete": "চ্যানেল মুছে ফেলা হয়েছে: {{channel}}", + "channelUpdate": "চ্যানেল আপডেট হয়েছে: {{channel}}", + "roleCreate": "রোল তৈরি হয়েছে: {{role}}", + "roleDelete": "রোল মুছে ফেলা হয়েছে: {{role}}", + "roleUpdate": "রোল আপডেট হয়েছে: {{role}}", + "threadCreate": "থ্রেড তৈরি হয়েছে: {{thread}}", + "threadDelete": "থ্রেড মুছে ফেলা হয়েছে: {{thread}}", + "threadUpdate": "থ্রেড আপডেট হয়েছে: {{thread}}", + "emojiCreate": "ইমোজি তৈরি হয়েছে: {{emoji}}", + "emojiDelete": "ইমোজি মুছে ফেলা হয়েছে: {{emoji}}", + "emojiUpdate": "ইমোজি আপডেট হয়েছে: {{emoji}}", + "guildUpdate": "সার্ভার আপডেট হয়েছে: {{guild}}", + "guildUnavailable": "সার্ভার অস্থায়ীভাবে অনুপলব্ধ: {{guild}}", + "guildBanAdd": "ব্যবহারকারী ব্যান করা হয়েছে: {{user}}", + "guildBanRemove": "ব্যবহারকারীর ব্যান তুলে নেওয়া হয়েছে: {{user}}", + "inviteCreate": "ইনভাইট তৈরি হয়েছে: {{code}}", + "inviteDelete": "ইনভাইট মুছে ফেলা হয়েছে: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/de.json b/apps/bot/locales/de.json new file mode 100644 index 0000000..3bac467 --- /dev/null +++ b/apps/bot/locales/de.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Fehlendes Argument {{arg}}. Verwendung: {{usage}}", + "invalidInt": "Das Argument muss eine ganze Zahl sein. Erhalten: {{value}}", + "invalidNumber": "Das Argument muss eine Zahl sein. Erhalten: {{value}}", + "invalidBoolean": "Das Argument muss ein Boolescher Wert sein. Erhalten: {{value}}", + "invalidUser": "Benutzer konnte aus {{value}} nicht gefunden werden", + "invalidChannel": "Kanal konnte aus {{value}} nicht gefunden werden", + "invalidRole": "Rolle konnte aus {{value}} nicht gefunden werden", + "tooMany": "Zu viele Argumente ({{count}}) erkannt: {{extras}}. Verwendung: {{usage}}" + }, + "permissions": { + "user": "Du hast nicht die Berechtigungen: {{permissions}}" + }, + "execution": "Beim Ausführen ist ein unerwarteter Fehler aufgetreten.", + "cooldown": "Bitte warte {{seconds}}s, bevor du diesen Befehl erneut verwendest.", + "rateLimit": "Zu viele Anfragen. Bitte warte {{seconds}}s, bevor du es erneut versuchst.", + "executionQueued": "Befehl akzeptiert und in die Warteschlange aufgenommen." + }, + "categories": { + "fun": "Spaß", + "utility": "Hilfsfunktionen", + "core": "Kern" + }, + "commands": { + "kiss": { + "name": "kuss", + "description": "Einem Benutzer einen Kuss senden.", + "args": { + "user": "Zielbenutzer" + }, + "examples": { + "basic": "Einem Mitglied einen Kuss senden" + }, + "responses": { + "success": "{{from}} sendet {{to}} einen Kuss." + } + }, + "ping": { + "name": "ping", + "description": "WebSocket-Latenz des Bots prüfen.", + "examples": { + "basic": "Gesundheitsprüfung" + }, + "responses": { + "pong": "Pong. WebSocket-Latenz: {{latency}}ms." + } + }, + "presence": { + "name": "präsenz", + "description": "Bot-Präsenz über ein interaktives Panel konfigurieren.", + "examples": { + "slash": "Interaktives Präsenz-Panel öffnen" + }, + "responses": { + "panel": "Präsenz-Panel\nStatus: {{status}}\nAktivität: {{activityType}}\nRotation: alle {{rotationIntervalSeconds}}s\nAktiver Text ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nVorschau: {{activityPreview}}\nTexte: {{activityTexts}}\nVariablen ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Nur der Befehlsersteller kann dieses Panel verwenden.", + "invalidSelection": "Ungültige Auswahl.", + "invalidInterval": "Ungültiges Intervall. Verwende eine ganze Zahl zwischen {{minSeconds}} und {{maxSeconds}} Sekunden.", + "modalTimeout": "Zeitüberschreitung bei der Textbearbeitung. Klicke erneut auf den Button." + }, + "ui": { + "status": { + "placeholder": "Status auswählen", + "options": { + "online": { + "label": "Online", + "description": "Bot als online anzeigen" + }, + "idle": { + "label": "Inaktiv", + "description": "Bot als inaktiv anzeigen" + }, + "dnd": { + "label": "Nicht stören", + "description": "Bot im Nicht-stören-Modus anzeigen" + }, + "invisible": { + "label": "Offline", + "description": "Bot als offline anzeigen" + }, + "streaming": { + "label": "Streaming", + "description": "Bot im Streaming-Modus anzeigen" + } + } + }, + "activity": { + "placeholder": "Aktivitätstyp auswählen", + "options": { + "PLAYING": { + "label": "Spielt", + "description": "Zeigt: Spielt ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Zeigt: Live auf ..." + }, + "WATCHING": { + "label": "Schaut", + "description": "Zeigt: Schaut ..." + }, + "LISTENING": { + "label": "Hört", + "description": "Zeigt: Hört ..." + }, + "COMPETING": { + "label": "Wettbewerb", + "description": "Zeigt: Wettbewerb ..." + }, + "CUSTOM": { + "label": "Benutzerstatus", + "description": "Zeigt einen benutzerdefinierten Status" + } + } + }, + "embed": { + "title": "Präsenz-Konfiguration", + "description": "Status, Aktivitätstyp, mehrere Texte und Rotation ändern.", + "footer": "Interaktives Panel /presence", + "fields": { + "status": "Status", + "activity": "Aktivitätstyp", + "text": "Text" + } + }, + "textButton": "📝 Texte bearbeiten", + "intervalButton": "⏱️ Intervall", + "modal": { + "title": "Präsenztexte", + "label": "Ein Text pro Zeile (nutze {{var}})", + "placeholder": "Wartung\n{{guild_count}} Server | {{prefix}}hilfe" + }, + "intervalModal": { + "title": "Rotationsintervall", + "label": "Sekunden zwischen jedem Text", + "placeholder": "Beispiel: 30" + } + } + }, + "logs": { + "name": "logs", + "description": "Discord-Event-Logs mit einem interaktiven Panel konfigurieren.", + "examples": { + "slash": "Interaktives Logs-Panel öffnen" + }, + "responses": { + "slashOnly": "Verwende /logs, um dieses interaktive Panel zu öffnen.", + "guildOnly": "Dieser Befehl muss in einem Server verwendet werden.", + "notOwner": "Nur der Befehlsersteller kann dieses Panel verwenden.", + "invalidSelection": "Ungültige Auswahl.", + "allEnabled": "Alle Event-Logs sind aktiviert.", + "allDisabled": "Alle Event-Logs sind deaktiviert.", + "eventEnabled": "Event {{event}} aktiviert.", + "eventDisabled": "Event {{event}} deaktiviert.", + "channelsCreated": "Log-Kanäle bereit. Erstellt: {{created}}, wiederverwendet: {{reused}}.", + "channelsPartial": "Log-Kanäle teilweise bereit. Erstellt: {{created}}, wiederverwendet: {{reused}}, fehlgeschlagen: {{failed}}.", + "channelsFailed": "Log-Kanäle konnten nicht erstellt werden. Fehlgeschlagen: {{failed}}." + }, + "ui": { + "embed": { + "title": "Log-Konfiguration", + "description": "Aktiviere oder deaktiviere Discord-Events und leite Logs in Kategorien weiter." + }, + "status": { + "enabled": "Aktiv", + "disabled": "Deaktiviert" + }, + "pageLabel": "Seite {{page}}/{{pageCount}}", + "enabledCountLabel": "Aktive Events: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Events", + "channelLabel": "Kanal", + "channelNotConfigured": "#nicht-konfiguriert", + "feedbackLabel": "Letzte Aktion", + "buttons": { + "enableAll": "✅ Alle Logs aktivieren", + "disableAll": "❌ Alle Logs deaktivieren", + "createChannels": "📁 Log-Kanäle erstellen", + "previousPage": "Zurück", + "nextPage": "Weiter", + "enableEvent": "{{event}} aktivieren", + "disableEvent": "{{event}} deaktivieren" + }, + "events": { + "messageCreate": "Nachricht erstellt", + "messageDelete": "Nachricht gelöscht", + "messageUpdate": "Nachricht aktualisiert", + "messageBulkDelete": "Mehrfachlöschung", + "guildMemberAdd": "Mitglied beigetreten", + "guildMemberRemove": "Mitglied verlassen", + "guildMemberUpdate": "Mitglied aktualisiert", + "interactionCreate": "Interaktion", + "channelCreate": "Kanal erstellt", + "channelDelete": "Kanal gelöscht", + "channelUpdate": "Kanal aktualisiert", + "roleCreate": "Rolle erstellt", + "roleDelete": "Rolle gelöscht", + "roleUpdate": "Rolle aktualisiert", + "threadCreate": "Thread erstellt", + "threadDelete": "Thread gelöscht", + "threadUpdate": "Thread aktualisiert", + "emojiCreate": "Emoji erstellt", + "emojiDelete": "Emoji gelöscht", + "emojiUpdate": "Emoji aktualisiert", + "guildUpdate": "Server aktualisiert", + "guildUnavailable": "Server nicht verfügbar", + "guildBanAdd": "Mitglied gebannt", + "guildBanRemove": "Mitglied entbannt", + "inviteCreate": "Einladung erstellt", + "inviteDelete": "Einladung gelöscht" + } + } + }, + "welcome": { + "name": "willkommen", + "description": "Willkommensnachrichten mit einem interaktiven Panel konfigurieren.", + "examples": { + "slash": "Willkommens-Konfigurationspanel öffnen" + }, + "responses": { + "guildOnly": "Dieser Befehl muss in einem Server verwendet werden.", + "notOwner": "Nur der Befehlsersteller kann dieses Panel verwenden.", + "invalidSelection": "Ungültige Auswahl.", + "testSuccess": "Testnachricht in {{channel}} gesendet.", + "testDisabled": "Willkommensmodul ist deaktiviert.", + "testMissingChannel": "Kein Kanal konfiguriert. Nutze den Kanal-Button.", + "testChannelUnavailable": "Der konfigurierte Kanal ist nicht verfügbar.", + "testMissingPermissions": "Der Bot hat keine Berechtigung, Nachrichten in diesem Kanal zu senden.", + "testFailed": "Testnachricht konnte nicht gesendet werden." + }, + "ui": { + "embed": { + "title": "Willkommens-Konfiguration", + "description": "Status, Kanal, Nachrichtentyp und Test konfigurieren.", + "fields": { + "status": "Status", + "channel": "Kanal", + "type": "Typ", + "channelPicker": "Kanal auswählen", + "autoRoles": "Auto-Rollen" + } + }, + "status": { + "enabled": "🟢 Aktiv", + "disabled": "🔴 Deaktiviert" + }, + "channelNotConfigured": "#nicht-konfiguriert", + "autoRolesNotConfigured": "Keine Auto-Rollen konfiguriert", + "channelPickerPlaceholder": "Willkommenskanal auswählen", + "channelPickerHint": "Wähle den Kanal im Menü aus.", + "rolePickerPlaceholder": "Auto-Rollen auswählen", + "rolePickerHint": "Wähle Rollen aus dem Menü.", + "buttons": { + "toggle": "Status umschalten", + "channel": "Kanal konfigurieren", + "channelCancel": "Kanal-Auswahl schließen", + "roles": "Auto-Rollen hinzufügen", + "rolesCancel": "Rollen-Auswahl schließen", + "rolesClear": "Auto-Rollen löschen", + "test": "Testen" + }, + "type": { + "placeholder": "Nachrichtentyp auswählen", + "options": { + "simple": { + "label": "Einfache Nachricht", + "description": "Sendet eine Textnachricht" + }, + "embed": { + "label": "Embed", + "description": "Sendet ein Willkommens-Embed" + }, + "container": { + "label": "Container (V2)", + "description": "Sendet einen Components V2 Container" + }, + "image": { + "label": "Bild", + "description": "Sendet ein Embed mit Bild" + } + } + } + }, + "templates": { + "simple": "🎉 Willkommen {{user}} auf **{{guild}}**!", + "embedTitle": "Willkommen!", + "embedDescription": "{{user}} ist **{{guild}}** beigetreten.", + "containerTitle": "Willkommen", + "containerDescription": "{{user}} ist **{{guild}}** beigetreten.", + "imageTitle": "Willkommen", + "imageDescription": "auf dem Discord-Server {{guild}}" + } + }, + "goodbye": { + "name": "aufwiedersehen", + "description": "Abschiedsnachrichten mit einem interaktiven Panel konfigurieren.", + "examples": { + "slash": "Abschieds-Konfigurationspanel öffnen" + }, + "responses": { + "guildOnly": "Dieser Befehl muss in einem Server verwendet werden.", + "notOwner": "Nur der Befehlsersteller kann dieses Panel verwenden.", + "invalidSelection": "Ungültige Auswahl.", + "testSuccess": "Testnachricht in {{channel}} gesendet.", + "testDisabled": "Abschiedsmodul ist deaktiviert.", + "testMissingChannel": "Kein Kanal konfiguriert. Nutze den Kanal-Button.", + "testChannelUnavailable": "Der konfigurierte Kanal ist nicht verfügbar.", + "testMissingPermissions": "Der Bot hat keine Berechtigung, Nachrichten in diesem Kanal zu senden.", + "testFailed": "Testnachricht konnte nicht gesendet werden." + }, + "ui": { + "embed": { + "title": "Abschieds-Konfiguration", + "description": "Status, Kanal, Nachrichtentyp und Test konfigurieren.", + "fields": { + "status": "Status", + "channel": "Kanal", + "type": "Typ", + "channelPicker": "Kanal auswählen" + } + }, + "status": { + "enabled": "🟢 Aktiv", + "disabled": "🔴 Deaktiviert" + }, + "channelNotConfigured": "#nicht-konfiguriert", + "channelPickerPlaceholder": "Abschiedskanal auswählen", + "channelPickerHint": "Wähle den Kanal im Menü aus.", + "buttons": { + "toggle": "Status umschalten", + "channel": "Kanal konfigurieren", + "channelCancel": "Kanal-Auswahl schließen", + "test": "Testen" + }, + "type": { + "placeholder": "Nachrichtentyp auswählen", + "options": { + "simple": { + "label": "Einfache Nachricht", + "description": "Sendet eine Textnachricht" + }, + "embed": { + "label": "Embed", + "description": "Sendet ein Abschieds-Embed" + }, + "container": { + "label": "Container (V2)", + "description": "Sendet einen Components V2 Container" + }, + "image": { + "label": "Bild", + "description": "Sendet ein Embed mit Bild" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} hat **{{guild}}** verlassen.", + "embedTitle": "Auf Wiedersehen", + "embedDescription": "{{user}} hat **{{guild}}** verlassen.", + "containerTitle": "Auf Wiedersehen", + "containerDescription": "{{user}} hat **{{guild}}** verlassen.", + "imageTitle": "Auf Wiedersehen", + "imageDescription": "vom Discord-Server {{guild}}" + } + }, + "help": { + "name": "hilfe", + "description": "Liste der Befehle oder Details eines Befehls anzeigen.", + "args": { + "command": "Optionaler Befehlsname oder Trigger" + }, + "examples": { + "basic": "Alle Befehle anzeigen", + "single": "Details eines Befehls anzeigen" + }, + "errors": { + "notFound": "Kein Befehl entspricht {{query}}." + }, + "embed": { + "title": "Befehls-Hilfe", + "description": "Nutze {{usage}}, um einen Befehl zu prüfen.", + "categoryEmpty": "Keine Befehle in dieser Kategorie.", + "detailsTitle": "Befehl: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Kategorie: {{source}}", + "fields": { + "usage": "Verwendung", + "arguments": "Argumente", + "examples": "Beispiele" + } + }, + "labels": { + "prefix": "Prefix", + "slash": "Slash", + "required": "erforderlich", + "optional": "optional", + "noArgs": "Keine Argumente.", + "noExamples": "Keine Beispiele." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(kein Inhalt)", + "unknown": "unbekannt", + "none": "keine" + }, + "embed": { + "title": "Event: {{event}}", + "detailsField": "Details" + }, + "summaries": { + "messageCreate": "Nachricht von {{user}} in {{channel}} gesendet", + "messageDelete": "Nachricht in {{channel}} gelöscht", + "messageUpdate": "Nachricht in {{channel}} aktualisiert", + "messageBulkDelete": "Mehrfachlöschung: {{count}} Nachrichten entfernt", + "guildMemberAdd": "Mitglied beigetreten: {{user}}", + "guildMemberRemove": "Mitglied verlassen: {{user}}", + "guildMemberUpdate": "Mitglied aktualisiert: {{user}}", + "interactionGeneric": "Interaktion von {{user}}", + "interactionSlash": "Slash-Befehl /{{command}} von {{user}}", + "interactionButton": "Button-Interaktion von {{user}}", + "interactionSelect": "Auswahlmenü-Interaktion von {{user}}", + "interactionModal": "Modal gesendet von {{user}}", + "channelCreate": "Kanal erstellt: {{channel}}", + "channelDelete": "Kanal gelöscht: {{channel}}", + "channelUpdate": "Kanal aktualisiert: {{channel}}", + "roleCreate": "Rolle erstellt: {{role}}", + "roleDelete": "Rolle gelöscht: {{role}}", + "roleUpdate": "Rolle aktualisiert: {{role}}", + "threadCreate": "Thread erstellt: {{thread}}", + "threadDelete": "Thread gelöscht: {{thread}}", + "threadUpdate": "Thread aktualisiert: {{thread}}", + "emojiCreate": "Emoji erstellt: {{emoji}}", + "emojiDelete": "Emoji gelöscht: {{emoji}}", + "emojiUpdate": "Emoji aktualisiert: {{emoji}}", + "guildUpdate": "Server aktualisiert: {{guild}}", + "guildUnavailable": "Server vorübergehend nicht verfügbar: {{guild}}", + "guildBanAdd": "Benutzer gebannt: {{user}}", + "guildBanRemove": "Benutzer entbannt: {{user}}", + "inviteCreate": "Einladung erstellt: {{code}}", + "inviteDelete": "Einladung gelöscht: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/src/legacy/i18n/en.json b/apps/bot/locales/en.json similarity index 50% rename from apps/bot/src/legacy/i18n/en.json rename to apps/bot/locales/en.json index 6c0b6f2..e27fdbe 100644 --- a/apps/bot/src/legacy/i18n/en.json +++ b/apps/bot/locales/en.json @@ -2,20 +2,20 @@ "errors": { "args": { "missing": "Missing argument {{arg}}. Usage: {{usage}}", - "invalidInt": "Argument must be an integer. Received: {{value}}", - "invalidNumber": "Argument must be a number. Received: {{value}}", - "invalidBoolean": "Argument must be a boolean value. Received: {{value}}", - "invalidUser": "Could not resolve a user from {{value}}", - "invalidChannel": "Could not resolve a channel from {{value}}", - "invalidRole": "Could not resolve a role from {{value}}", - "tooMany": "Too many arguments detected ({{count}}): {{extras}}. Usage: {{usage}}" + "invalidInt": "The argument must be an integer. Received: {{value}}", + "invalidNumber": "The argument must be a number. Received: {{value}}", + "invalidBoolean": "The argument must be a boolean. Received: {{value}}", + "invalidUser": "Unable to find a user from {{value}}", + "invalidChannel": "Unable to find a channel from {{value}}", + "invalidRole": "Unable to find a role from {{value}}", + "tooMany": "Too many arguments ({{count}}) detected: {{extras}}. Usage: {{usage}}" }, "permissions": { - "user": "You are missing permissions: {{permissions}}" + "user": "You do not have the permissions: {{permissions}}" }, - "execution": "An unexpected error happened while running this command.", - "cooldown": "Please wait {{seconds}}s before using this command again.", - "rateLimit": "Too many requests. Please wait {{seconds}}s before retrying.", + "execution": "An unexpected error occurred during execution.", + "cooldown": "Please wait {{seconds}}s before reusing this command.", + "rateLimit": "Too many requests. Please wait {{seconds}}s before trying again.", "executionQueued": "Command accepted and queued for processing." }, "categories": { @@ -26,7 +26,7 @@ "commands": { "kiss": { "name": "kiss", - "description": "Send a kiss to another user.", + "description": "Send a kiss to a user.", "args": { "user": "Target user" }, @@ -39,9 +39,9 @@ }, "ping": { "name": "ping", - "description": "Check bot websocket latency.", + "description": "Check the bot's websocket latency.", "examples": { - "basic": "Basic health check" + "basic": "Health check" }, "responses": { "pong": "Pong. Websocket latency: {{latency}}ms." @@ -49,16 +49,16 @@ }, "presence": { "name": "presence", - "description": "Configure bot presence with an interactive panel.", + "description": "Configure the bot presence with an interactive panel.", "examples": { "slash": "Open the interactive presence panel" }, "responses": { "panel": "Presence panel\nStatus: {{status}}\nActivity: {{activityType}}\nRotation: every {{rotationIntervalSeconds}}s\nActive text ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nRendered preview: {{activityPreview}}\nTexts: {{activityTexts}}\nVariables ({{doubleBracesHint}}): {{variables}}", "notOwner": "Only the command author can use this panel.", - "invalidSelection": "Invalid selection received.", + "invalidSelection": "Invalid selection.", "invalidInterval": "Invalid interval. Use an integer between {{minSeconds}} and {{maxSeconds}} seconds.", - "modalTimeout": "Text update timed out. Click the button again." + "modalTimeout": "Time expired for text editing. Click the button again." }, "ui": { "status": { @@ -66,23 +66,23 @@ "options": { "online": { "label": "Online", - "description": "Displayed as online" + "description": "Shows the bot as online" }, "idle": { "label": "Idle", - "description": "Displayed as idle" + "description": "Shows the bot as idle" }, "dnd": { "label": "Do Not Disturb", - "description": "Displayed as do not disturb" + "description": "Shows the bot in do not disturb mode" }, "invisible": { "label": "Invisible", - "description": "Displayed as offline" + "description": "Shows the bot as offline" }, "streaming": { "label": "Streaming", - "description": "Displayed in streaming mode" + "description": "Shows the bot in streaming mode" } } }, @@ -91,34 +91,34 @@ "options": { "PLAYING": { "label": "Playing", - "description": "Shows as Playing ..." + "description": "Shows Playing ..." }, "STREAMING": { "label": "Streaming", - "description": "Shows as Streaming ..." + "description": "Shows Live on ..." }, "WATCHING": { "label": "Watching", - "description": "Shows as Watching ..." + "description": "Shows Watching ..." }, "LISTENING": { "label": "Listening", - "description": "Shows as Listening to ..." + "description": "Shows Listening to ..." }, "COMPETING": { "label": "Competing", - "description": "Shows as Competing in ..." + "description": "Shows Competing ..." }, "CUSTOM": { - "label": "CustomStatus", + "label": "Custom Status", "description": "Shows a custom status" } } }, "embed": { "title": "Presence Configuration", - "description": "Change status, activity type, multiple texts, and rotation interval in real time.", - "footer": "Interactive /presence panel", + "description": "Edit status, activity type, multiple texts and rotation.", + "footer": "Interactive panel /presence", "fields": { "status": "Status", "activity": "Activity type", @@ -143,20 +143,20 @@ "name": "logs", "description": "Configure Discord event logs with an interactive panel.", "examples": { - "slash": "Open the interactive logs panel" + "slash": "Open the logs interactive panel" }, "responses": { "slashOnly": "Use /logs to open this interactive panel.", - "guildOnly": "This command can only be used inside a server.", + "guildOnly": "This command must be used in a server.", "notOwner": "Only the command author can use this panel.", "invalidSelection": "Invalid selection.", - "allEnabled": "All log events are now enabled.", - "allDisabled": "All log events are now disabled.", + "allEnabled": "All event logs are enabled.", + "allDisabled": "All event logs are disabled.", "eventEnabled": "Event {{event}} enabled.", "eventDisabled": "Event {{event}} disabled.", "channelsCreated": "Log channels ready. Created: {{created}}, reused: {{reused}}.", "channelsPartial": "Log channels partially ready. Created: {{created}}, reused: {{reused}}, failed categories: {{failed}}.", - "channelsFailed": "Could not create log channels. Failed categories: {{failed}}." + "channelsFailed": "Unable to create log channels. Failed categories: {{failed}}." }, "ui": { "embed": { @@ -181,6 +181,34 @@ "nextPage": "Next", "enableEvent": "Enable {{event}}", "disableEvent": "Disable {{event}}" + }, + "events": { + "messageCreate": "Message created", + "messageDelete": "Message deleted", + "messageUpdate": "Message updated", + "messageBulkDelete": "Bulk message delete", + "guildMemberAdd": "Member joined", + "guildMemberRemove": "Member left", + "guildMemberUpdate": "Member updated", + "interactionCreate": "Interaction", + "channelCreate": "Channel created", + "channelDelete": "Channel deleted", + "channelUpdate": "Channel updated", + "roleCreate": "Role created", + "roleDelete": "Role deleted", + "roleUpdate": "Role updated", + "threadCreate": "Thread created", + "threadDelete": "Thread deleted", + "threadUpdate": "Thread updated", + "emojiCreate": "Emoji created", + "emojiDelete": "Emoji deleted", + "emojiUpdate": "Emoji updated", + "guildUpdate": "Server updated", + "guildUnavailable": "Server unavailable", + "guildBanAdd": "Member banned", + "guildBanRemove": "Member unbanned", + "inviteCreate": "Invite created", + "inviteDelete": "Invite deleted" } } }, @@ -188,28 +216,28 @@ "name": "welcome", "description": "Configure welcome messages with an interactive panel.", "examples": { - "slash": "Open the welcome message configuration panel" + "slash": "Open the welcome configuration panel" }, "responses": { - "guildOnly": "This command can only be used inside a server.", + "guildOnly": "This command must be used in a server.", "notOwner": "Only the command author can use this panel.", "invalidSelection": "Invalid selection.", - "testSuccess": "Test message sent in {{channel}}.", - "testDisabled": "Welcome module is disabled.", - "testMissingChannel": "No channel configured yet. Use the channel button first.", - "testChannelUnavailable": "Configured channel was not found or is not supported.", - "testMissingPermissions": "Bot is missing permission to send messages in that channel.", - "testFailed": "Failed to send test message. Please try again later." + "testSuccess": "Test message sent to {{channel}}.", + "testDisabled": "The welcome module is disabled.", + "testMissingChannel": "No channel configured. Use the Channel button.", + "testChannelUnavailable": "The configured channel is unavailable or incompatible.", + "testMissingPermissions": "The bot does not have permission to send messages in this channel.", + "testFailed": "Unable to send test message. Please try again later." }, "ui": { "embed": { "title": "Welcome Configuration", - "description": "Configure status, channel, message type, and run a test send.", + "description": "Set status, channel, message type and run a test.", "fields": { "status": "Status", "channel": "Channel", "type": "Type", - "channelPicker": "Channel picker", + "channelPicker": "Channel selection", "autoRoles": "Auto roles" } }, @@ -218,38 +246,38 @@ "disabled": "🔴 Disabled" }, "channelNotConfigured": "#not-configured", - "autoRolesNotConfigured": "No auto role configured", + "autoRolesNotConfigured": "No auto roles configured", "channelPickerPlaceholder": "Select a welcome channel", - "channelPickerHint": "Use the select menu below to choose the destination channel.", + "channelPickerHint": "Choose the channel in the select menu below.", "rolePickerPlaceholder": "Select one or more auto roles", - "rolePickerHint": "Use the role select menu below to append roles to the auto-role list.", + "rolePickerHint": "Choose roles in the select menu below to add them.", "buttons": { "toggle": "Toggle status", "channel": "Set channel", - "channelCancel": "Close channel picker", + "channelCancel": "Close channel select", "roles": "Add auto roles", - "rolesCancel": "Close role picker", + "rolesCancel": "Close roles select", "rolesClear": "Clear auto roles", "test": "Test" }, "type": { - "placeholder": "Choose a message type", + "placeholder": "Choose message type", "options": { "simple": { "label": "Simple message", - "description": "Send a classic text message" + "description": "Sends a plain text message" }, "embed": { "label": "Embed", - "description": "Send a rich embed message" + "description": "Sends a welcome embed" }, "container": { "label": "Container (V2)", - "description": "Send a Components V2 container" + "description": "Sends a Components V2 container" }, "image": { "label": "Image", - "description": "Send an embed with an image" + "description": "Sends an embed with image" } } } @@ -261,35 +289,35 @@ "containerTitle": "Welcome", "containerDescription": "{{user}} just joined **{{guild}}**.", "imageTitle": "Welcome", - "imageDescription": "on the Discord server {{guild}}" + "imageDescription": "to the Discord server {{guild}}" } }, "goodbye": { "name": "goodbye", "description": "Configure goodbye messages with an interactive panel.", "examples": { - "slash": "Open the goodbye message configuration panel" + "slash": "Open the goodbye configuration panel" }, "responses": { - "guildOnly": "This command can only be used inside a server.", + "guildOnly": "This command must be used in a server.", "notOwner": "Only the command author can use this panel.", "invalidSelection": "Invalid selection.", - "testSuccess": "Test message sent in {{channel}}.", - "testDisabled": "Goodbye module is disabled.", - "testMissingChannel": "No channel configured yet. Use the channel button first.", - "testChannelUnavailable": "Configured channel was not found or is not supported.", - "testMissingPermissions": "Bot is missing permission to send messages in that channel.", - "testFailed": "Failed to send test message. Please try again later." + "testSuccess": "Test message sent to {{channel}}.", + "testDisabled": "The goodbye module is disabled.", + "testMissingChannel": "No channel configured. Use the Channel button.", + "testChannelUnavailable": "The configured channel is unavailable or incompatible.", + "testMissingPermissions": "The bot does not have permission to send messages in this channel.", + "testFailed": "Unable to send test message. Please try again later." }, "ui": { "embed": { "title": "Goodbye Configuration", - "description": "Configure status, channel, message type, and run a test send.", + "description": "Set status, channel, message type and run a test.", "fields": { "status": "Status", "channel": "Channel", "type": "Type", - "channelPicker": "Channel picker" + "channelPicker": "Channel selection" } }, "status": { @@ -298,31 +326,31 @@ }, "channelNotConfigured": "#not-configured", "channelPickerPlaceholder": "Select a goodbye channel", - "channelPickerHint": "Use the select menu below to choose the destination channel.", + "channelPickerHint": "Choose the channel in the select menu below.", "buttons": { "toggle": "Toggle status", "channel": "Set channel", - "channelCancel": "Close channel picker", + "channelCancel": "Close channel select", "test": "Test" }, "type": { - "placeholder": "Choose a message type", + "placeholder": "Choose message type", "options": { "simple": { "label": "Simple message", - "description": "Send a classic text message" + "description": "Sends a plain text message" }, "embed": { "label": "Embed", - "description": "Send a rich embed message" + "description": "Sends a goodbye embed" }, "container": { "label": "Container (V2)", - "description": "Send a Components V2 container" + "description": "Sends a Components V2 container" }, "image": { "label": "Image", - "description": "Send an embed with an image" + "description": "Sends an embed with image" } } } @@ -330,29 +358,29 @@ "templates": { "simple": "👋 {{user}} left **{{guild}}**.", "embedTitle": "Goodbye", - "embedDescription": "{{user}} has left **{{guild}}**.", + "embedDescription": "{{user}} left **{{guild}}**.", "containerTitle": "Goodbye", - "containerDescription": "{{user}} has left **{{guild}}**.", + "containerDescription": "{{user}} left **{{guild}}**.", "imageTitle": "Goodbye", "imageDescription": "from the Discord server {{guild}}" } }, "help": { "name": "help", - "description": "Display command list or details for one command.", + "description": "Show the command list or details of a command.", "args": { "command": "Optional command name or trigger" }, "examples": { - "basic": "List all commands", - "single": "Show details for one command" + "basic": "Show all commands", + "single": "Show command details" }, "errors": { "notFound": "No command matches {{query}}." }, "embed": { "title": "Command Help", - "description": "Use {{usage}} to inspect one command.", + "description": "Use {{usage}} to inspect a command.", "categoryEmpty": "No commands in this category.", "detailsTitle": "Command: {{name}}", "detailsDescription": "{{description}}", @@ -372,5 +400,69 @@ "noExamples": "No examples." } } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(no content)", + "unknown": "unknown", + "none": "none" + }, + "embed": { + "title": "Event: {{event}}", + "detailsField": "Details" + }, + "summaries": { + "messageCreate": "Message sent by {{user}} in {{channel}}", + "messageDelete": "Message deleted in {{channel}}", + "messageUpdate": "Message updated in {{channel}}", + "messageBulkDelete": "Bulk delete: {{count}} messages removed", + "guildMemberAdd": "Member joined: {{user}}", + "guildMemberRemove": "Member left: {{user}}", + "guildMemberUpdate": "Member updated: {{user}}", + "interactionGeneric": "Interaction by {{user}}", + "interactionSlash": "Slash command /{{command}} by {{user}}", + "interactionButton": "Button interaction by {{user}}", + "interactionSelect": "Select menu interaction by {{user}}", + "interactionModal": "Modal submit by {{user}}", + "channelCreate": "Channel created: {{channel}}", + "channelDelete": "Channel deleted: {{channel}}", + "channelUpdate": "Channel updated: {{channel}}", + "roleCreate": "Role created: {{role}}", + "roleDelete": "Role deleted: {{role}}", + "roleUpdate": "Role updated: {{role}}", + "threadCreate": "Thread created: {{thread}}", + "threadDelete": "Thread deleted: {{thread}}", + "threadUpdate": "Thread updated: {{thread}}", + "emojiCreate": "Emoji created: {{emoji}}", + "emojiDelete": "Emoji deleted: {{emoji}}", + "emojiUpdate": "Emoji updated: {{emoji}}", + "guildUpdate": "Server updated: {{guild}}", + "guildUnavailable": "Server temporarily unavailable: {{guild}}", + "guildBanAdd": "User banned: {{user}}", + "guildBanRemove": "User unbanned: {{user}}", + "inviteCreate": "Invite created: {{code}}", + "inviteDelete": "Invite deleted: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } } -} +} \ No newline at end of file diff --git a/apps/bot/locales/es.json b/apps/bot/locales/es.json new file mode 100644 index 0000000..47c126a --- /dev/null +++ b/apps/bot/locales/es.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Argumento faltante {{arg}}. Uso: {{usage}}", + "invalidInt": "El argumento debe ser un entero. Recibido: {{value}}", + "invalidNumber": "El argumento debe ser un número. Recibido: {{value}}", + "invalidBoolean": "El argumento debe ser un booleano. Recibido: {{value}}", + "invalidUser": "No se pudo encontrar un usuario desde {{value}}", + "invalidChannel": "No se pudo encontrar un canal desde {{value}}", + "invalidRole": "No se pudo encontrar un rol desde {{value}}", + "tooMany": "Demasiados argumentos ({{count}}) detectados: {{extras}}. Uso: {{usage}}" + }, + "permissions": { + "user": "No tienes los permisos: {{permissions}}" + }, + "execution": "Ocurrió un error inesperado durante la ejecución.", + "cooldown": "Espera {{seconds}}s antes de reutilizar este comando.", + "rateLimit": "Demasiadas solicitudes. Espera {{seconds}}s antes de volver a intentarlo.", + "executionQueued": "Comando aceptado y puesto en cola de procesamiento." + }, + "categories": { + "fun": "Diversión", + "utility": "Utilidad", + "core": "Base" + }, + "commands": { + "kiss": { + "name": "beso", + "description": "Enviar un beso a un usuario.", + "args": { + "user": "Usuario objetivo" + }, + "examples": { + "basic": "Enviar un beso a un miembro" + }, + "responses": { + "success": "{{from}} envía un beso a {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Verificar la latencia websocket del bot.", + "examples": { + "basic": "Verificación de estado" + }, + "responses": { + "pong": "Pong. Latencia websocket: {{latency}}ms." + } + }, + "presence": { + "name": "presencia", + "description": "Configurar la presencia del bot con un panel interactivo.", + "examples": { + "slash": "Abrir el panel interactivo de presencia" + }, + "responses": { + "panel": "Panel de presencia\nEstado: {{status}}\nActividad: {{activityType}}\nRotación: cada {{rotationIntervalSeconds}}s\nTexto activo ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nVista previa: {{activityPreview}}\nTextos: {{activityTexts}}\nVariables ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Solo el autor del comando puede usar este panel.", + "invalidSelection": "Selección inválida.", + "invalidInterval": "Intervalo inválido. Usa un entero entre {{minSeconds}} y {{maxSeconds}} segundos.", + "modalTimeout": "Tiempo agotado para modificar el texto. Haz clic nuevamente en el botón." + }, + "ui": { + "status": { + "placeholder": "Elegir un estado", + "options": { + "online": { + "label": "En línea", + "description": "Muestra el bot en línea" + }, + "idle": { + "label": "Inactivo", + "description": "Muestra el bot inactivo" + }, + "dnd": { + "label": "No molestar", + "description": "Muestra el bot en modo no molestar" + }, + "invisible": { + "label": "Desconectado", + "description": "Muestra el bot desconectado" + }, + "streaming": { + "label": "Streaming", + "description": "Muestra el bot en modo streaming" + } + } + }, + "activity": { + "placeholder": "Elegir tipo de actividad", + "options": { + "PLAYING": { + "label": "Jugando a", + "description": "Muestra Jugando a ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Muestra En directo en ..." + }, + "WATCHING": { + "label": "Viendo", + "description": "Muestra Viendo ..." + }, + "LISTENING": { + "label": "Escuchando", + "description": "Muestra Escuchando ..." + }, + "COMPETING": { + "label": "Competición", + "description": "Muestra Competición ..." + }, + "CUSTOM": { + "label": "Estado personalizado", + "description": "Muestra un estado personalizado" + } + } + }, + "embed": { + "title": "Configuración de la presencia", + "description": "Modifica el estado, tipo de actividad, múltiples textos y su rotación.", + "footer": "Panel interactivo /presence", + "fields": { + "status": "Estado", + "activity": "Tipo de actividad", + "text": "Texto" + } + }, + "textButton": "📝 Editar textos", + "intervalButton": "⏱️ Intervalo", + "modal": { + "title": "Textos de presencia", + "label": "Un texto por línea (usa {{var}})", + "placeholder": "Mantenimiento\n{{guild_count}} servidores | {{prefix}}ayuda" + }, + "intervalModal": { + "title": "Intervalo de rotación", + "label": "Segundos entre cada texto", + "placeholder": "Ejemplo: 30" + } + } + }, + "logs": { + "name": "registros", + "description": "Configurar los registros de eventos de Discord con un panel interactivo.", + "examples": { + "slash": "Abrir el panel interactivo de registros" + }, + "responses": { + "slashOnly": "Usa /logs para abrir este panel interactivo.", + "guildOnly": "Este comando debe usarse en un servidor.", + "notOwner": "Solo el autor del comando puede usar este panel.", + "invalidSelection": "Selección inválida.", + "allEnabled": "Todos los registros de eventos están activados.", + "allDisabled": "Todos los registros de eventos están desactivados.", + "eventEnabled": "Evento {{event}} activado.", + "eventDisabled": "Evento {{event}} desactivado.", + "channelsCreated": "Canales de registros listos. Creados: {{created}}, reutilizados: {{reused}}.", + "channelsPartial": "Canales de registros parcialmente listos. Creados: {{created}}, reutilizados: {{reused}}, categorías fallidas: {{failed}}.", + "channelsFailed": "No se pudieron crear los canales de registros. Categorías fallidas: {{failed}}." + }, + "ui": { + "embed": { + "title": "Configuración de registros", + "description": "Activa o desactiva cada evento de Discord y redirige los registros por canales de categoría." + }, + "status": { + "enabled": "Activo", + "disabled": "Desactivado" + }, + "pageLabel": "Página {{page}}/{{pageCount}}", + "enabledCountLabel": "Eventos activos: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Eventos", + "channelLabel": "Canal", + "channelNotConfigured": "#sin-configurar", + "feedbackLabel": "Última acción", + "buttons": { + "enableAll": "✅ Activar todos los registros", + "disableAll": "❌ Desactivar todos los registros", + "createChannels": "📁 Crear canales de registros", + "previousPage": "Anterior", + "nextPage": "Siguiente", + "enableEvent": "Activar {{event}}", + "disableEvent": "Desactivar {{event}}" + }, + "events": { + "messageCreate": "Mensaje creado", + "messageDelete": "Mensaje eliminado", + "messageUpdate": "Mensaje actualizado", + "messageBulkDelete": "Eliminación masiva de mensajes", + "guildMemberAdd": "Miembro se unió", + "guildMemberRemove": "Miembro salió", + "guildMemberUpdate": "Miembro actualizado", + "interactionCreate": "Interacción", + "channelCreate": "Canal creado", + "channelDelete": "Canal eliminado", + "channelUpdate": "Canal actualizado", + "roleCreate": "Rol creado", + "roleDelete": "Rol eliminado", + "roleUpdate": "Rol actualizado", + "threadCreate": "Hilo creado", + "threadDelete": "Hilo eliminado", + "threadUpdate": "Hilo actualizado", + "emojiCreate": "Emoji creado", + "emojiDelete": "Emoji eliminado", + "emojiUpdate": "Emoji actualizado", + "guildUpdate": "Servidor actualizado", + "guildUnavailable": "Servidor no disponible", + "guildBanAdd": "Miembro baneado", + "guildBanRemove": "Miembro desbaneado", + "inviteCreate": "Invitación creada", + "inviteDelete": "Invitación eliminada" + } + } + }, + "welcome": { + "name": "bienvenida", + "description": "Configurar mensajes de bienvenida con un panel interactivo.", + "examples": { + "slash": "Abrir el panel de configuración de bienvenida" + }, + "responses": { + "guildOnly": "Este comando debe usarse en un servidor.", + "notOwner": "Solo el autor del comando puede usar este panel.", + "invalidSelection": "Selección inválida.", + "testSuccess": "Mensaje de prueba enviado en {{channel}}.", + "testDisabled": "El módulo de bienvenida está desactivado.", + "testMissingChannel": "No hay canal configurado. Usa el botón Canal.", + "testChannelUnavailable": "El canal configurado no existe o no es compatible.", + "testMissingPermissions": "El bot no tiene permisos para enviar mensajes en este canal.", + "testFailed": "No se pudo enviar el mensaje de prueba. Inténtalo más tarde." + }, + "ui": { + "embed": { + "title": "Configuración Welcome", + "description": "Ajusta estado, canal, tipo de mensaje y ejecuta una prueba.", + "fields": { + "status": "Estado", + "channel": "Canal", + "type": "Tipo", + "channelPicker": "Selección de canal", + "autoRoles": "Roles automáticos" + } + }, + "status": { + "enabled": "🟢 Activado", + "disabled": "🔴 Desactivado" + }, + "channelNotConfigured": "#sin-configurar", + "autoRolesNotConfigured": "Sin roles automáticos configurados", + "channelPickerPlaceholder": "Selecciona un canal de bienvenida", + "channelPickerHint": "Elige el canal en el menú de selección.", + "rolePickerPlaceholder": "Selecciona uno o varios roles automáticos", + "rolePickerHint": "Elige los roles en el menú para agregarlos a la lista automática.", + "buttons": { + "toggle": "Cambiar estado", + "channel": "Configurar canal", + "channelCancel": "Cerrar selector de canal", + "roles": "Añadir roles automáticos", + "rolesCancel": "Cerrar selector de roles", + "rolesClear": "Vaciar roles automáticos", + "test": "Probar" + }, + "type": { + "placeholder": "Elegir tipo de mensaje", + "options": { + "simple": { + "label": "Mensaje simple", + "description": "Envía un mensaje de texto clásico" + }, + "embed": { + "label": "Embed", + "description": "Envía un embed de bienvenida" + }, + "container": { + "label": "Container (V2)", + "description": "Envía un container Components V2" + }, + "image": { + "label": "Imagen", + "description": "Envía un embed con imagen" + } + } + } + }, + "templates": { + "simple": "🎉 ¡Bienvenido {{user}} a **{{guild}}**!", + "embedTitle": "¡Bienvenido!", + "embedDescription": "{{user}} se ha unido a **{{guild}}**.", + "containerTitle": "Bienvenido", + "containerDescription": "{{user}} se ha unido a **{{guild}}**.", + "imageTitle": "Bienvenido", + "imageDescription": "al servidor Discord {{guild}}" + } + }, + "goodbye": { + "name": "despedida", + "description": "Configurar mensajes de despedida con un panel interactivo.", + "examples": { + "slash": "Abrir el panel de configuración de despedida" + }, + "responses": { + "guildOnly": "Este comando debe usarse en un servidor.", + "notOwner": "Solo el autor del comando puede usar este panel.", + "invalidSelection": "Selección inválida.", + "testSuccess": "Mensaje de prueba enviado en {{channel}}.", + "testDisabled": "El módulo de despedida está desactivado.", + "testMissingChannel": "No hay canal configurado. Usa el botón Canal.", + "testChannelUnavailable": "El canal configurado no existe o no es compatible.", + "testMissingPermissions": "El bot no tiene permisos para enviar mensajes en este canal.", + "testFailed": "No se pudo enviar el mensaje de prueba. Inténtalo más tarde." + }, + "ui": { + "embed": { + "title": "Configuración Goodbye", + "description": "Ajusta estado, canal, tipo de mensaje y ejecuta una prueba.", + "fields": { + "status": "Estado", + "channel": "Canal", + "type": "Tipo", + "channelPicker": "Selección de canal" + } + }, + "status": { + "enabled": "🟢 Activado", + "disabled": "🔴 Desactivado" + }, + "channelNotConfigured": "#sin-configurar", + "channelPickerPlaceholder": "Selecciona un canal de despedida", + "channelPickerHint": "Elige el canal en el menú de selección.", + "buttons": { + "toggle": "Cambiar estado", + "channel": "Configurar canal", + "channelCancel": "Cerrar selector de canal", + "test": "Probar" + }, + "type": { + "placeholder": "Elegir tipo de mensaje", + "options": { + "simple": { + "label": "Mensaje simple", + "description": "Envía un mensaje de texto clásico" + }, + "embed": { + "label": "Embed", + "description": "Envía un embed de despedida" + }, + "container": { + "label": "Container (V2)", + "description": "Envía un container Components V2" + }, + "image": { + "label": "Imagen", + "description": "Envía un embed con imagen" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} ha salido de **{{guild}}**.", + "embedTitle": "Adiós", + "embedDescription": "{{user}} ha salido de **{{guild}}**.", + "containerTitle": "Adiós", + "containerDescription": "{{user}} ha salido de **{{guild}}**.", + "imageTitle": "Adiós", + "imageDescription": "del servidor Discord {{guild}}" + } + }, + "help": { + "name": "ayuda", + "description": "Mostrar la lista de comandos o los detalles de un comando.", + "args": { + "command": "Nombre o trigger opcional del comando" + }, + "examples": { + "basic": "Mostrar todos los comandos", + "single": "Mostrar los detalles de un comando" + }, + "errors": { + "notFound": "No se encontró ningún comando que coincida con {{query}}." + }, + "embed": { + "title": "Ayuda de comandos", + "description": "Usa {{usage}} para inspeccionar un comando.", + "categoryEmpty": "No hay comandos en esta categoría.", + "detailsTitle": "Comando: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Categoría: {{source}}", + "fields": { + "usage": "Uso", + "arguments": "Argumentos", + "examples": "Ejemplos" + } + }, + "labels": { + "prefix": "Prefijo", + "slash": "Slash", + "required": "obligatorio", + "optional": "opcional", + "noArgs": "Sin argumentos.", + "noExamples": "Sin ejemplos." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(sin contenido)", + "unknown": "desconocido", + "none": "ninguno" + }, + "embed": { + "title": "Evento: {{event}}", + "detailsField": "Detalles" + }, + "summaries": { + "messageCreate": "Mensaje enviado por {{user}} en {{channel}}", + "messageDelete": "Mensaje eliminado en {{channel}}", + "messageUpdate": "Mensaje actualizado en {{channel}}", + "messageBulkDelete": "Eliminación masiva: {{count}} mensajes eliminados", + "guildMemberAdd": "Miembro unido: {{user}}", + "guildMemberRemove": "Miembro salió: {{user}}", + "guildMemberUpdate": "Miembro actualizado: {{user}}", + "interactionGeneric": "Interacción de {{user}}", + "interactionSlash": "Comando /{{command}} de {{user}}", + "interactionButton": "Interacción de botón de {{user}}", + "interactionSelect": "Interacción de menú de selección de {{user}}", + "interactionModal": "Envío de modal por {{user}}", + "channelCreate": "Canal creado: {{channel}}", + "channelDelete": "Canal eliminado: {{channel}}", + "channelUpdate": "Canal actualizado: {{channel}}", + "roleCreate": "Rol creado: {{role}}", + "roleDelete": "Rol eliminado: {{role}}", + "roleUpdate": "Rol actualizado: {{role}}", + "threadCreate": "Hilo creado: {{thread}}", + "threadDelete": "Hilo eliminado: {{thread}}", + "threadUpdate": "Hilo actualizado: {{thread}}", + "emojiCreate": "Emoji creado: {{emoji}}", + "emojiDelete": "Emoji eliminado: {{emoji}}", + "emojiUpdate": "Emoji actualizado: {{emoji}}", + "guildUpdate": "Servidor actualizado: {{guild}}", + "guildUnavailable": "Servidor temporalmente no disponible: {{guild}}", + "guildBanAdd": "Usuario baneado: {{user}}", + "guildBanRemove": "Usuario desbaneado: {{user}}", + "inviteCreate": "Invitación creada: {{code}}", + "inviteDelete": "Invitación eliminada: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/fr.json b/apps/bot/locales/fr.json new file mode 100644 index 0000000..1c889dd --- /dev/null +++ b/apps/bot/locales/fr.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Argument manquant {{arg}}. Utilisation : {{usage}}", + "invalidInt": "L’argument doit être un entier. Reçu : {{value}}", + "invalidNumber": "L’argument doit être un nombre. Reçu : {{value}}", + "invalidBoolean": "L’argument doit être un booléen. Reçu : {{value}}", + "invalidUser": "Impossible de trouver un utilisateur à partir de {{value}}", + "invalidChannel": "Impossible de trouver un salon à partir de {{value}}", + "invalidRole": "Impossible de trouver un rôle à partir de {{value}}", + "tooMany": "Trop d’arguments ({{count}}) détectés : {{extras}}. Utilisation : {{usage}}" + }, + "permissions": { + "user": "Tu n’as pas les permissions : {{permissions}}" + }, + "execution": "Une erreur inattendue est survenue pendant l’exécution.", + "cooldown": "Patiente {{seconds}} s avant de réutiliser cette commande.", + "rateLimit": "Trop de requêtes. Patiente {{seconds}} s avant de réessayer.", + "executionQueued": "Commande acceptée et mise en file d’attente." + }, + "categories": { + "fun": "Divertissement", + "utility": "Utilitaire", + "core": "Base" + }, + "commands": { + "kiss": { + "name": "bisou", + "description": "Envoyer un bisou à un utilisateur.", + "args": { + "user": "Utilisateur cible" + }, + "examples": { + "basic": "Envoyer un bisou à un membre" + }, + "responses": { + "success": "{{from}} envoie un bisou à {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Vérifier la latence WebSocket du bot.", + "examples": { + "basic": "Vérification de l’état du bot" + }, + "responses": { + "pong": "Pong. Latence WebSocket : {{latency}} ms." + } + }, + "presence": { + "name": "presence", + "description": "Configurer la présence du bot avec un panneau interactif.", + "examples": { + "slash": "Ouvrir le panneau interactif de présence" + }, + "responses": { + "panel": "Panneau de présence\nStatut : {{status}}\nActivité : {{activityType}}\nRotation : toutes les {{rotationIntervalSeconds}} s\nTexte actuel ({{currentTextIndex}}/{{textCount}}) : {{activityText}}\nAperçu rendu : {{activityPreview}}\nTextes : {{activityTexts}}\nVariables ({{doubleBracesHint}}) : {{variables}}", + "notOwner": "Seul l’auteur de la commande peut utiliser ce panneau.", + "invalidSelection": "Sélection invalide.", + "invalidInterval": "Intervalle invalide. Utilise un entier entre {{minSeconds}} et {{maxSeconds}} secondes.", + "modalTimeout": "Temps écoulé pour la modification du texte. Clique à nouveau sur le bouton." + }, + "ui": { + "status": { + "placeholder": "Choisir un statut", + "options": { + "online": { + "label": "En ligne", + "description": "Affiche le bot en ligne" + }, + "idle": { + "label": "Inactif", + "description": "Affiche le bot inactif" + }, + "dnd": { + "label": "Ne pas déranger", + "description": "Affiche le bot en mode Ne pas déranger" + }, + "invisible": { + "label": "Hors ligne", + "description": "Affiche le bot hors ligne" + }, + "streaming": { + "label": "Streaming", + "description": "Affiche le bot en mode streaming" + } + } + }, + "activity": { + "placeholder": "Choisir un type d’activité", + "options": { + "PLAYING": { + "label": "Joue à", + "description": "Affiche « Joue à ... »" + }, + "STREAMING": { + "label": "Streaming", + "description": "Affiche « En direct sur ... »" + }, + "WATCHING": { + "label": "Regarde", + "description": "Affiche « Regarde ... »" + }, + "LISTENING": { + "label": "Écoute", + "description": "Affiche « Écoute ... »" + }, + "COMPETING": { + "label": "Compétition", + "description": "Affiche « Compétition ... »" + }, + "CUSTOM": { + "label": "Statut personnalisé", + "description": "Affiche un statut personnalisé" + } + } + }, + "embed": { + "title": "Configuration de la présence", + "description": "Modifie le statut, le type d’activité, plusieurs textes et leur rotation.", + "footer": "Panneau interactif /presence", + "fields": { + "status": "Statut", + "activity": "Type d’activité", + "text": "Texte" + } + }, + "textButton": "📝 Modifier les textes", + "intervalButton": "⏱️ Intervalle", + "modal": { + "title": "Textes de présence", + "label": "Un texte par ligne (utilise {{var}})", + "placeholder": "Maintenance\n{{guild_count}} serveurs | {{prefix}}aide" + }, + "intervalModal": { + "title": "Intervalle de rotation", + "label": "Secondes entre chaque texte", + "placeholder": "Exemple : 30" + } + } + }, + "logs": { + "name": "logs", + "description": "Configurer les logs d’événements Discord avec un panneau interactif.", + "examples": { + "slash": "Ouvrir le panneau interactif des logs" + }, + "responses": { + "slashOnly": "Utilise /logs pour ouvrir ce panneau interactif.", + "guildOnly": "Cette commande doit être utilisée dans un serveur.", + "notOwner": "Seul l’auteur de la commande peut utiliser ce panneau.", + "invalidSelection": "Sélection invalide.", + "allEnabled": "Tous les logs d’événements sont activés.", + "allDisabled": "Tous les logs d’événements sont désactivés.", + "eventEnabled": "Événement {{event}} activé.", + "eventDisabled": "Événement {{event}} désactivé.", + "channelsCreated": "Salons de logs prêts. Créés : {{created}}, réutilisés : {{reused}}.", + "channelsPartial": "Salons de logs partiellement prêts. Créés : {{created}}, réutilisés : {{reused}}, catégories en échec : {{failed}}.", + "channelsFailed": "Impossible de créer les salons de logs. Catégories en échec : {{failed}}." + }, + "ui": { + "embed": { + "title": "Configuration des logs", + "description": "Active ou désactive chaque événement Discord et route les logs par salons de catégorie." + }, + "status": { + "enabled": "Activé", + "disabled": "Désactivé" + }, + "pageLabel": "Page {{page}}/{{pageCount}}", + "enabledCountLabel": "Événements activés : {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Événements", + "channelLabel": "Salon", + "channelNotConfigured": "#non-configuré", + "feedbackLabel": "Dernière action", + "buttons": { + "enableAll": "✅ Activer tous les logs", + "disableAll": "❌ Désactiver tous les logs", + "createChannels": "📁 Créer les salons de logs", + "previousPage": "Précédent", + "nextPage": "Suivant", + "enableEvent": "Activer {{event}}", + "disableEvent": "Désactiver {{event}}" + }, + "events": { + "messageCreate": "Message créé", + "messageDelete": "Message supprimé", + "messageUpdate": "Message modifié", + "messageBulkDelete": "Suppression massive de messages", + "guildMemberAdd": "Membre rejoint", + "guildMemberRemove": "Membre parti", + "guildMemberUpdate": "Membre modifié", + "interactionCreate": "Interaction", + "channelCreate": "Salon créé", + "channelDelete": "Salon supprimé", + "channelUpdate": "Salon modifié", + "roleCreate": "Rôle créé", + "roleDelete": "Rôle supprimé", + "roleUpdate": "Rôle modifié", + "threadCreate": "Fil créé", + "threadDelete": "Fil supprimé", + "threadUpdate": "Fil modifié", + "emojiCreate": "Emoji créé", + "emojiDelete": "Emoji supprimé", + "emojiUpdate": "Emoji modifié", + "guildUpdate": "Serveur modifié", + "guildUnavailable": "Serveur indisponible", + "guildBanAdd": "Membre banni", + "guildBanRemove": "Membre débanni", + "inviteCreate": "Invitation créée", + "inviteDelete": "Invitation supprimée" + } + } + }, + "welcome": { + "name": "bienvenue", + "description": "Configurer les messages de bienvenue avec un panneau interactif.", + "examples": { + "slash": "Ouvrir le panneau de configuration des messages de bienvenue" + }, + "responses": { + "guildOnly": "Cette commande doit être utilisée dans un serveur.", + "notOwner": "Seul l’auteur de la commande peut utiliser ce panneau.", + "invalidSelection": "Sélection invalide.", + "testSuccess": "Message de test envoyé dans {{channel}}.", + "testDisabled": "Le module de bienvenue est désactivé.", + "testMissingChannel": "Aucun salon configuré. Utilise le bouton Salon.", + "testChannelUnavailable": "Le salon configuré est introuvable ou non compatible.", + "testMissingPermissions": "Le bot n’a pas la permission d’envoyer des messages dans ce salon.", + "testFailed": "Impossible d’envoyer le message de test. Réessaie plus tard." + }, + "ui": { + "embed": { + "title": "Configuration du message de bienvenue", + "description": "Règle le statut, le salon, le type de message et lance un test.", + "fields": { + "status": "Statut", + "channel": "Salon", + "type": "Type", + "channelPicker": "Sélection du salon", + "autoRoles": "Rôles automatiques" + } + }, + "status": { + "enabled": "🟢 Activé", + "disabled": "🔴 Désactivé" + }, + "channelNotConfigured": "#non-configuré", + "autoRolesNotConfigured": "Aucun rôle automatique configuré", + "channelPickerPlaceholder": "Sélectionne un salon de bienvenue", + "channelPickerHint": "Choisis le salon dans le menu de sélection ci-dessous.", + "rolePickerPlaceholder": "Sélectionne un ou plusieurs rôles automatiques", + "rolePickerHint": "Choisis les rôles dans le menu de sélection ci-dessous pour les ajouter à la liste automatique.", + "buttons": { + "toggle": "Basculer le statut", + "channel": "Configurer le salon", + "channelCancel": "Fermer la sélection du salon", + "roles": "Ajouter des rôles automatiques", + "rolesCancel": "Fermer la sélection des rôles", + "rolesClear": "Vider les rôles automatiques", + "test": "Tester" + }, + "type": { + "placeholder": "Choisir un type de message", + "options": { + "simple": { + "label": "Message simple", + "description": "Envoie un message texte classique" + }, + "embed": { + "label": "Embed", + "description": "Envoie un embed de bienvenue" + }, + "container": { + "label": "Conteneur (V2)", + "description": "Envoie un conteneur Components V2" + }, + "image": { + "label": "Image", + "description": "Envoie un embed avec image" + } + } + } + }, + "templates": { + "simple": "🎉 Bienvenue {{user}} sur **{{guild}}** !", + "embedTitle": "Bienvenue !", + "embedDescription": "{{user}} vient de rejoindre **{{guild}}**.", + "containerTitle": "Bienvenue", + "containerDescription": "{{user}} vient de rejoindre **{{guild}}**.", + "imageTitle": "Bienvenue", + "imageDescription": "sur le serveur Discord {{guild}}" + } + }, + "goodbye": { + "name": "au_revoir", + "description": "Configurer les messages d’au revoir avec un panneau interactif.", + "examples": { + "slash": "Ouvrir le panneau de configuration des messages d’au revoir" + }, + "responses": { + "guildOnly": "Cette commande doit être utilisée dans un serveur.", + "notOwner": "Seul l’auteur de la commande peut utiliser ce panneau.", + "invalidSelection": "Sélection invalide.", + "testSuccess": "Message de test envoyé dans {{channel}}.", + "testDisabled": "Le module d’au revoir est désactivé.", + "testMissingChannel": "Aucun salon configuré. Utilise le bouton Salon.", + "testChannelUnavailable": "Le salon configuré est introuvable ou non compatible.", + "testMissingPermissions": "Le bot n’a pas la permission d’envoyer des messages dans ce salon.", + "testFailed": "Impossible d’envoyer le message de test. Réessaie plus tard." + }, + "ui": { + "embed": { + "title": "Configuration du message d’au revoir", + "description": "Règle le statut, le salon, le type de message et lance un test.", + "fields": { + "status": "Statut", + "channel": "Salon", + "type": "Type", + "channelPicker": "Sélection du salon" + } + }, + "status": { + "enabled": "🟢 Activé", + "disabled": "🔴 Désactivé" + }, + "channelNotConfigured": "#non-configuré", + "channelPickerPlaceholder": "Sélectionne un salon d’au revoir", + "channelPickerHint": "Choisis le salon dans le menu de sélection ci-dessous.", + "buttons": { + "toggle": "Basculer le statut", + "channel": "Configurer le salon", + "channelCancel": "Fermer la sélection du salon", + "test": "Tester" + }, + "type": { + "placeholder": "Choisir un type de message", + "options": { + "simple": { + "label": "Message simple", + "description": "Envoie un message texte classique" + }, + "embed": { + "label": "Embed", + "description": "Envoie un embed d’au revoir" + }, + "container": { + "label": "Conteneur (V2)", + "description": "Envoie un conteneur Components V2" + }, + "image": { + "label": "Image", + "description": "Envoie un embed avec image" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} a quitté **{{guild}}**.", + "embedTitle": "Au revoir", + "embedDescription": "{{user}} a quitté **{{guild}}**.", + "containerTitle": "Au revoir", + "containerDescription": "{{user}} a quitté **{{guild}}**.", + "imageTitle": "Au revoir", + "imageDescription": "du serveur Discord {{guild}}" + } + }, + "help": { + "name": "aide", + "description": "Afficher la liste des commandes ou les détails d’une commande.", + "args": { + "command": "Nom ou déclencheur de commande facultatif" + }, + "examples": { + "basic": "Afficher toutes les commandes", + "single": "Afficher les détails d’une commande" + }, + "errors": { + "notFound": "Aucune commande ne correspond à {{query}}." + }, + "embed": { + "title": "Aide sur les commandes", + "description": "Utilise {{usage}} pour inspecter une commande.", + "categoryEmpty": "Aucune commande dans cette catégorie.", + "detailsTitle": "Commande : {{name}}", + "detailsDescription": "{{description}}", + "footer": "Catégorie : {{source}}", + "fields": { + "usage": "Utilisation", + "arguments": "Arguments", + "examples": "Exemples" + } + }, + "labels": { + "prefix": "Préfixe", + "slash": "Slash", + "required": "requis", + "optional": "facultatif", + "noArgs": "Aucun argument.", + "noExamples": "Aucun exemple." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(aucun contenu)", + "unknown": "inconnu", + "none": "aucun" + }, + "embed": { + "title": "Événement : {{event}}", + "detailsField": "Détails" + }, + "summaries": { + "messageCreate": "Message envoyé par {{user}} dans {{channel}}", + "messageDelete": "Message supprimé dans {{channel}}", + "messageUpdate": "Message modifié dans {{channel}}", + "messageBulkDelete": "Suppression massive : {{count}} messages supprimés", + "guildMemberAdd": "Membre rejoint : {{user}}", + "guildMemberRemove": "Membre parti : {{user}}", + "guildMemberUpdate": "Membre modifié : {{user}}", + "interactionGeneric": "Interaction par {{user}}", + "interactionSlash": "Commande slash /{{command}} par {{user}}", + "interactionButton": "Interaction sur bouton par {{user}}", + "interactionSelect": "Interaction sur menu de sélection par {{user}}", + "interactionModal": "Formulaire envoyé par {{user}}", + "channelCreate": "Salon créé : {{channel}}", + "channelDelete": "Salon supprimé : {{channel}}", + "channelUpdate": "Salon modifié : {{channel}}", + "roleCreate": "Rôle créé : {{role}}", + "roleDelete": "Rôle supprimé : {{role}}", + "roleUpdate": "Rôle modifié : {{role}}", + "threadCreate": "Fil créé : {{thread}}", + "threadDelete": "Fil supprimé : {{thread}}", + "threadUpdate": "Fil modifié : {{thread}}", + "emojiCreate": "Emoji créé : {{emoji}}", + "emojiDelete": "Emoji supprimé : {{emoji}}", + "emojiUpdate": "Emoji modifié : {{emoji}}", + "guildUpdate": "Serveur modifié : {{guild}}", + "guildUnavailable": "Serveur temporairement indisponible : {{guild}}", + "guildBanAdd": "Utilisateur banni : {{user}}", + "guildBanRemove": "Utilisateur débanni : {{user}}", + "inviteCreate": "Invitation créée : {{code}}", + "inviteDelete": "Invitation supprimée : {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "auteur={{tag}} ({{id}})", + "content": "contenu={{value}}", + "before": "avant={{value}}", + "after": "après={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "utilisateur={{tag}} ({{id}})", + "oldNick": "ancienPseudo={{value}}", + "newNick": "nouveauPseudo={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "ancienNom={{value}}", + "newName": "nouveauNom={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviteur={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/hi.json b/apps/bot/locales/hi.json new file mode 100644 index 0000000..a116f8b --- /dev/null +++ b/apps/bot/locales/hi.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "आर्गुमेंट गायब है {{arg}}. उपयोग: {{usage}}", + "invalidInt": "आर्गुमेंट एक पूर्णांक होना चाहिए। प्राप्त हुआ: {{value}}", + "invalidNumber": "आर्गुमेंट एक संख्या होना चाहिए। प्राप्त हुआ: {{value}}", + "invalidBoolean": "आर्गुमेंट एक बूलियन होना चाहिए। प्राप्त हुआ: {{value}}", + "invalidUser": "{{value}} से कोई उपयोगकर्ता नहीं मिला", + "invalidChannel": "{{value}} से कोई चैनल नहीं मिला", + "invalidRole": "{{value}} से कोई भूमिका नहीं मिली", + "tooMany": "बहुत अधिक आर्गुमेंट्स ({{count}}) मिले: {{extras}}. उपयोग: {{usage}}" + }, + "permissions": { + "user": "आपके पास ये अनुमतियाँ नहीं हैं: {{permissions}}" + }, + "execution": "कार्य निष्पादन के दौरान एक अप्रत्याशित त्रुटि हुई।", + "cooldown": "इस कमांड को फिर से उपयोग करने से पहले {{seconds}} सेकंड प्रतीक्षा करें।", + "rateLimit": "बहुत अधिक अनुरोध। फिर से प्रयास करने से पहले {{seconds}} सेकंड प्रतीक्षा करें।", + "executionQueued": "कमांड स्वीकार कर ली गई है और प्रोसेसिंग कतार में डाल दी गई है।" + }, + "categories": { + "fun": "मनोरंजन", + "utility": "उपयोगिता", + "core": "मुख्य" + }, + "commands": { + "kiss": { + "name": "चुंबन", + "description": "किसी उपयोगकर्ता को चुंबन भेजें।", + "args": { + "user": "लक्षित उपयोगकर्ता" + }, + "examples": { + "basic": "किसी सदस्य को चुंबन भेजें" + }, + "responses": { + "success": "{{from}} ने {{to}} को चुंबन भेजा।" + } + }, + "ping": { + "name": "पिंग", + "description": "बॉट की websocket latency जाँचें।", + "examples": { + "basic": "स्वास्थ्य जाँच" + }, + "responses": { + "pong": "पोंग। websocket latency: {{latency}}ms." + } + }, + "presence": { + "name": "उपस्थिति", + "description": "एक इंटरैक्टिव पैनल के साथ बॉट की उपस्थिति कॉन्फ़िगर करें।", + "examples": { + "slash": "उपस्थिति का इंटरैक्टिव पैनल खोलें" + }, + "responses": { + "panel": "उपस्थिति पैनल\nस्थिति: {{status}}\nगतिविधि: {{activityType}}\nरोटेशन: हर {{rotationIntervalSeconds}}s\nसक्रिय टेक्स्ट ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nरेंडर पूर्वावलोकन: {{activityPreview}}\nटेक्स्ट: {{activityTexts}}\nवेरिएबल्स ({{doubleBracesHint}}): {{variables}}", + "notOwner": "केवल कमांड का लेखक ही इस पैनल का उपयोग कर सकता है।", + "invalidSelection": "अमान्य चयन।", + "invalidInterval": "अमान्य अंतराल। {{minSeconds}} और {{maxSeconds}} सेकंड के बीच एक पूर्णांक उपयोग करें।", + "modalTimeout": "टेक्स्ट संशोधन का समय समाप्त हो गया। कृपया बटन पर फिर से क्लिक करें।" + }, + "ui": { + "status": { + "placeholder": "एक स्थिति चुनें", + "options": { + "online": { + "label": "ऑनलाइन", + "description": "बॉट को ऑनलाइन दिखाता है" + }, + "idle": { + "label": "निष्क्रिय", + "description": "बॉट को निष्क्रिय दिखाता है" + }, + "dnd": { + "label": "व्यस्त न करें", + "description": "बॉट को डू नॉट डिस्टर्ब मोड में दिखाता है" + }, + "invisible": { + "label": "ऑफ़लाइन", + "description": "बॉट को ऑफ़लाइन दिखाता है" + }, + "streaming": { + "label": "स्ट्रीमिंग", + "description": "बॉट को स्ट्रीमिंग मोड में दिखाता है" + } + } + }, + "activity": { + "placeholder": "एक गतिविधि प्रकार चुनें", + "options": { + "PLAYING": { + "label": "खेल रहा है", + "description": "दिखाता है: खेल रहा है ..." + }, + "STREAMING": { + "label": "स्ट्रीमिंग", + "description": "दिखाता है: लाइव पर ..." + }, + "WATCHING": { + "label": "देख रहा है", + "description": "दिखाता है: देख रहा है ..." + }, + "LISTENING": { + "label": "सुन रहा है", + "description": "दिखाता है: सुन रहा है ..." + }, + "COMPETING": { + "label": "प्रतिस्पर्धा", + "description": "दिखाता है: प्रतिस्पर्धा ..." + }, + "CUSTOM": { + "label": "कस्टम स्टेटस", + "description": "एक कस्टम स्टेटस दिखाता है" + } + } + }, + "embed": { + "title": "उपस्थिति कॉन्फ़िगरेशन", + "description": "स्थिति, गतिविधि प्रकार, कई टेक्स्ट और उनका रोटेशन बदलें।", + "footer": "इंटरैक्टिव पैनल /presence", + "fields": { + "status": "स्थिति", + "activity": "गतिविधि प्रकार", + "text": "टेक्स्ट" + } + }, + "textButton": "📝 टेक्स्ट बदलें", + "intervalButton": "⏱️ अंतराल", + "modal": { + "title": "उपस्थिति टेक्स्ट", + "label": "प्रति पंक्ति एक टेक्स्ट ({{var}} का उपयोग करें)", + "placeholder": "मेंटेनेंस\n{{guild_count}} सर्वर | {{prefix}}मदद" + }, + "intervalModal": { + "title": "रोटेशन अंतराल", + "label": "हर टेक्स्ट के बीच सेकंड", + "placeholder": "उदाहरण: 30" + } + } + }, + "logs": { + "name": "लॉग्स", + "description": "एक इंटरैक्टिव पैनल के साथ Discord इवेंट लॉग्स कॉन्फ़िगर करें।", + "examples": { + "slash": "लॉग्स का इंटरैक्टिव पैनल खोलें" + }, + "responses": { + "slashOnly": "इस इंटरैक्टिव पैनल को खोलने के लिए /logs का उपयोग करें।", + "guildOnly": "इस कमांड का उपयोग एक सर्वर में किया जाना चाहिए।", + "notOwner": "केवल कमांड का लेखक ही इस पैनल का उपयोग कर सकता है।", + "invalidSelection": "अमान्य चयन।", + "allEnabled": "सभी इवेंट लॉग्स सक्रिय हैं।", + "allDisabled": "सभी इवेंट लॉग्स निष्क्रिय हैं।", + "eventEnabled": "इवेंट {{event}} सक्रिय किया गया।", + "eventDisabled": "इवेंट {{event}} निष्क्रिय किया गया।", + "channelsCreated": "लॉग चैनल तैयार हैं। बनाए गए: {{created}}, पुन: उपयोग किए गए: {{reused}}।", + "channelsPartial": "लॉग चैनल आंशिक रूप से तैयार हैं। बनाए गए: {{created}}, पुन: उपयोग किए गए: {{reused}}, विफल श्रेणियाँ: {{failed}}।", + "channelsFailed": "लॉग चैनल नहीं बनाए जा सके। विफल श्रेणियाँ: {{failed}}।" + }, + "ui": { + "embed": { + "title": "लॉग्स कॉन्फ़िगरेशन", + "description": "हर Discord इवेंट को सक्रिय या निष्क्रिय करें और श्रेणी के अनुसार लॉग्स को चैनलों में भेजें।" + }, + "status": { + "enabled": "सक्रिय", + "disabled": "निष्क्रिय" + }, + "pageLabel": "पृष्ठ {{page}}/{{pageCount}}", + "enabledCountLabel": "सक्रिय इवेंट: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "इवेंट", + "channelLabel": "चैनल", + "channelNotConfigured": "#कॉन्फ़िगर_नहीं", + "feedbackLabel": "अंतिम कार्रवाई", + "buttons": { + "enableAll": "✅ सभी लॉग्स सक्रिय करें", + "disableAll": "❌ सभी लॉग्स निष्क्रिय करें", + "createChannels": "📁 लॉग चैनल बनाएँ", + "previousPage": "पिछला", + "nextPage": "अगला", + "enableEvent": "{{event}} सक्रिय करें", + "disableEvent": "{{event}} निष्क्रिय करें" + }, + "events": { + "messageCreate": "संदेश बनाया गया", + "messageDelete": "संदेश हटाया गया", + "messageUpdate": "संदेश अपडेट किया गया", + "messageBulkDelete": "थोक संदेश हटाना", + "guildMemberAdd": "सदस्य शामिल हुआ", + "guildMemberRemove": "सदस्य बाहर गया", + "guildMemberUpdate": "सदस्य अपडेट हुआ", + "interactionCreate": "इंटरैक्शन", + "channelCreate": "चैनल बनाया गया", + "channelDelete": "चैनल हटाया गया", + "channelUpdate": "चैनल अपडेट किया गया", + "roleCreate": "भूमिका बनाई गई", + "roleDelete": "भूमिका हटाई गई", + "roleUpdate": "भूमिका अपडेट की गई", + "threadCreate": "थ्रेड बनाया गया", + "threadDelete": "थ्रेड हटाया गया", + "threadUpdate": "थ्रेड अपडेट किया गया", + "emojiCreate": "इमोजी बनाई गई", + "emojiDelete": "इमोजी हटाई गई", + "emojiUpdate": "इमोजी अपडेट की गई", + "guildUpdate": "सर्वर अपडेट हुआ", + "guildUnavailable": "सर्वर अनुपलब्ध", + "guildBanAdd": "सदस्य प्रतिबंधित किया गया", + "guildBanRemove": "सदस्य का प्रतिबंध हटाया गया", + "inviteCreate": "आमंत्रण बनाया गया", + "inviteDelete": "आमंत्रण हटाया गया" + } + } + }, + "welcome": { + "name": "स्वागत", + "description": "एक इंटरैक्टिव पैनल के साथ स्वागत संदेश कॉन्फ़िगर करें।", + "examples": { + "slash": "स्वागत संदेशों का कॉन्फ़िगरेशन पैनल खोलें" + }, + "responses": { + "guildOnly": "इस कमांड का उपयोग एक सर्वर में किया जाना चाहिए।", + "notOwner": "केवल कमांड का लेखक ही इस पैनल का उपयोग कर सकता है।", + "invalidSelection": "अमान्य चयन।", + "testSuccess": "परीक्षण संदेश {{channel}} में भेज दिया गया।", + "testDisabled": "स्वागत मॉड्यूल निष्क्रिय है।", + "testMissingChannel": "कोई चैनल कॉन्फ़िगर नहीं है। चैनल बटन का उपयोग करें।", + "testChannelUnavailable": "कॉन्फ़िगर किया गया चैनल नहीं मिला या संगत नहीं है।", + "testMissingPermissions": "इस चैनल में संदेश भेजने की अनुमति बॉट के पास नहीं है।", + "testFailed": "परीक्षण संदेश भेजा नहीं जा सका। बाद में पुनः प्रयास करें।" + }, + "ui": { + "embed": { + "title": "स्वागत कॉन्फ़िगरेशन", + "description": "स्थिति, चैनल, संदेश प्रकार सेट करें और परीक्षण चलाएँ।", + "fields": { + "status": "स्थिति", + "channel": "चैनल", + "type": "प्रकार", + "channelPicker": "चैनल चयन", + "autoRoles": "स्वचालित भूमिकाएँ" + } + }, + "status": { + "enabled": "🟢 सक्रिय", + "disabled": "🔴 निष्क्रिय" + }, + "channelNotConfigured": "#कॉन्फ़िगर_नहीं", + "autoRolesNotConfigured": "कोई स्वचालित भूमिका कॉन्फ़िगर नहीं है", + "channelPickerPlaceholder": "एक स्वागत चैनल चुनें", + "channelPickerHint": "नीचे दिए गए चयन मेनू से चैनल चुनें।", + "rolePickerPlaceholder": "एक या अधिक स्वचालित भूमिकाएँ चुनें", + "rolePickerHint": "सूची में जोड़ने के लिए नीचे दिए गए चयन मेनू से भूमिकाएँ चुनें।", + "buttons": { + "toggle": "स्थिति टॉगल करें", + "channel": "चैनल कॉन्फ़िगर करें", + "channelCancel": "चैनल चयन बंद करें", + "roles": "स्वचालित भूमिकाएँ जोड़ें", + "rolesCancel": "भूमिका चयन बंद करें", + "rolesClear": "स्वचालित भूमिकाएँ साफ़ करें", + "test": "परीक्षण करें" + }, + "type": { + "placeholder": "एक संदेश प्रकार चुनें", + "options": { + "simple": { + "label": "साधारण संदेश", + "description": "एक सामान्य टेक्स्ट संदेश भेजता है" + }, + "embed": { + "label": "एंबेड", + "description": "एक स्वागत एंबेड भेजता है" + }, + "container": { + "label": "कंटेनर (V2)", + "description": "एक Components V2 कंटेनर भेजता है" + }, + "image": { + "label": "छवि", + "description": "छवि सहित एक एंबेड भेजता है" + } + } + } + }, + "templates": { + "simple": "🎉 स्वागत है {{user}} **{{guild}}** में!", + "embedTitle": "स्वागत है!", + "embedDescription": "{{user}} अभी-अभी **{{guild}}** में शामिल हुआ है।", + "containerTitle": "स्वागत है", + "containerDescription": "{{user}} अभी-अभी **{{guild}}** में शामिल हुआ है।", + "imageTitle": "स्वागत है", + "imageDescription": "Discord सर्वर {{guild}} पर" + } + }, + "goodbye": { + "name": "विदाई", + "description": "एक इंटरैक्टिव पैनल के साथ विदाई संदेश कॉन्फ़िगर करें।", + "examples": { + "slash": "विदाई संदेशों का कॉन्फ़िगरेशन पैनल खोलें" + }, + "responses": { + "guildOnly": "इस कमांड का उपयोग एक सर्वर में किया जाना चाहिए।", + "notOwner": "केवल कमांड का लेखक ही इस पैनल का उपयोग कर सकता है।", + "invalidSelection": "अमान्य चयन।", + "testSuccess": "परीक्षण संदेश {{channel}} में भेज दिया गया।", + "testDisabled": "विदाई मॉड्यूल निष्क्रिय है।", + "testMissingChannel": "कोई चैनल कॉन्फ़िगर नहीं है। चैनल बटन का उपयोग करें।", + "testChannelUnavailable": "कॉन्फ़िगर किया गया चैनल नहीं मिला या संगत नहीं है।", + "testMissingPermissions": "इस चैनल में संदेश भेजने की अनुमति बॉट के पास नहीं है।", + "testFailed": "परीक्षण संदेश भेजा नहीं जा सका। बाद में पुनः प्रयास करें।" + }, + "ui": { + "embed": { + "title": "विदाई कॉन्फ़िगरेशन", + "description": "स्थिति, चैनल, संदेश प्रकार सेट करें और परीक्षण चलाएँ।", + "fields": { + "status": "स्थिति", + "channel": "चैनल", + "type": "प्रकार", + "channelPicker": "चैनल चयन" + } + }, + "status": { + "enabled": "🟢 सक्रिय", + "disabled": "🔴 निष्क्रिय" + }, + "channelNotConfigured": "#कॉन्फ़िगर_नहीं", + "channelPickerPlaceholder": "एक विदाई चैनल चुनें", + "channelPickerHint": "नीचे दिए गए चयन मेनू से चैनल चुनें।", + "buttons": { + "toggle": "स्थिति टॉगल करें", + "channel": "चैनल कॉन्फ़िगर करें", + "channelCancel": "चैनल चयन बंद करें", + "test": "परीक्षण करें" + }, + "type": { + "placeholder": "एक संदेश प्रकार चुनें", + "options": { + "simple": { + "label": "साधारण संदेश", + "description": "एक सामान्य टेक्स्ट संदेश भेजता है" + }, + "embed": { + "label": "एंबेड", + "description": "एक विदाई एंबेड भेजता है" + }, + "container": { + "label": "कंटेनर (V2)", + "description": "एक Components V2 कंटेनर भेजता है" + }, + "image": { + "label": "छवि", + "description": "छवि सहित एक एंबेड भेजता है" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} **{{guild}}** से चला गया।", + "embedTitle": "विदाई", + "embedDescription": "{{user}} **{{guild}}** से चला गया।", + "containerTitle": "विदाई", + "containerDescription": "{{user}} **{{guild}}** से चला गया।", + "imageTitle": "विदाई", + "imageDescription": "Discord सर्वर {{guild}} से" + } + }, + "help": { + "name": "मदद", + "description": "कमांड्स की सूची या किसी कमांड का विवरण दिखाएँ।", + "args": { + "command": "वैकल्पिक कमांड नाम या ट्रिगर" + }, + "examples": { + "basic": "सभी कमांड दिखाएँ", + "single": "एक कमांड का विवरण दिखाएँ" + }, + "errors": { + "notFound": "{{query}} से मेल खाने वाली कोई कमांड नहीं मिली।" + }, + "embed": { + "title": "कमांड सहायता", + "description": "किसी कमांड की जाँच करने के लिए {{usage}} का उपयोग करें।", + "categoryEmpty": "इस श्रेणी में कोई कमांड नहीं है।", + "detailsTitle": "कमांड: {{name}}", + "detailsDescription": "{{description}}", + "footer": "श्रेणी: {{source}}", + "fields": { + "usage": "उपयोग", + "arguments": "आर्गुमेंट्स", + "examples": "उदाहरण" + } + }, + "labels": { + "prefix": "प्रिफ़िक्स", + "slash": "स्लैश", + "required": "आवश्यक", + "optional": "वैकल्पिक", + "noArgs": "कोई आर्गुमेंट नहीं।", + "noExamples": "कोई उदाहरण नहीं।" + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(कोई सामग्री नहीं)", + "unknown": "अज्ञात", + "none": "कोई नहीं" + }, + "embed": { + "title": "इवेंट: {{event}}", + "detailsField": "विवरण" + }, + "summaries": { + "messageCreate": "{{channel}} में {{user}} द्वारा भेजा गया संदेश", + "messageDelete": "{{channel}} में संदेश हटाया गया", + "messageUpdate": "{{channel}} में संदेश अपडेट किया गया", + "messageBulkDelete": "थोक हटाना: {{count}} संदेश हटाए गए", + "guildMemberAdd": "सदस्य शामिल हुआ: {{user}}", + "guildMemberRemove": "सदस्य बाहर गया: {{user}}", + "guildMemberUpdate": "सदस्य अपडेट हुआ: {{user}}", + "interactionGeneric": "{{user}} द्वारा इंटरैक्शन", + "interactionSlash": "{{user}} द्वारा स्लैश कमांड /{{command}}", + "interactionButton": "{{user}} द्वारा बटन इंटरैक्शन", + "interactionSelect": "{{user}} द्वारा चयन मेनू इंटरैक्शन", + "interactionModal": "{{user}} द्वारा मोडल सबमिट", + "channelCreate": "चैनल बनाया गया: {{channel}}", + "channelDelete": "चैनल हटाया गया: {{channel}}", + "channelUpdate": "चैनल अपडेट किया गया: {{channel}}", + "roleCreate": "भूमिका बनाई गई: {{role}}", + "roleDelete": "भूमिका हटाई गई: {{role}}", + "roleUpdate": "भूमिका अपडेट की गई: {{role}}", + "threadCreate": "थ्रेड बनाया गया: {{thread}}", + "threadDelete": "थ्रेड हटाया गया: {{thread}}", + "threadUpdate": "थ्रेड अपडेट किया गया: {{thread}}", + "emojiCreate": "इमोजी बनाई गई: {{emoji}}", + "emojiDelete": "इमोजी हटाई गई: {{emoji}}", + "emojiUpdate": "इमोजी अपडेट की गई: {{emoji}}", + "guildUpdate": "सर्वर अपडेट हुआ: {{guild}}", + "guildUnavailable": "सर्वर अस्थायी रूप से अनुपलब्ध: {{guild}}", + "guildBanAdd": "उपयोगकर्ता प्रतिबंधित: {{user}}", + "guildBanRemove": "उपयोगकर्ता का प्रतिबंध हटाया गया: {{user}}", + "inviteCreate": "आमंत्रण बनाया गया: {{code}}", + "inviteDelete": "आमंत्रण हटाया गया: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/id.json b/apps/bot/locales/id.json new file mode 100644 index 0000000..8daaaeb --- /dev/null +++ b/apps/bot/locales/id.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Argumen {{arg}} hilang. Penggunaan: {{usage}}", + "invalidInt": "Argumen harus berupa bilangan bulat. Diterima: {{value}}", + "invalidNumber": "Argumen harus berupa angka. Diterima: {{value}}", + "invalidBoolean": "Argumen harus berupa boolean. Diterima: {{value}}", + "invalidUser": "Tidak dapat menemukan pengguna dari {{value}}", + "invalidChannel": "Tidak dapat menemukan channel dari {{value}}", + "invalidRole": "Tidak dapat menemukan peran dari {{value}}", + "tooMany": "Terlalu banyak argumen ({{count}}) terdeteksi: {{extras}}. Penggunaan: {{usage}}" + }, + "permissions": { + "user": "Kamu tidak memiliki izin: {{permissions}}" + }, + "execution": "Terjadi kesalahan tak terduga saat eksekusi.", + "cooldown": "Tunggu {{seconds}} detik sebelum menggunakan perintah ini lagi.", + "rateLimit": "Terlalu banyak permintaan. Tunggu {{seconds}} detik sebelum mencoba lagi.", + "executionQueued": "Perintah diterima dan dimasukkan ke antrean pemrosesan." + }, + "categories": { + "fun": "Fun", + "utility": "Utilitas", + "core": "Dasar" + }, + "commands": { + "kiss": { + "name": "ciuman", + "description": "Mengirim ciuman ke seorang pengguna.", + "args": { + "user": "Pengguna target" + }, + "examples": { + "basic": "Mengirim ciuman ke anggota" + }, + "responses": { + "success": "{{from}} mengirim ciuman ke {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Memeriksa latensi websocket bot.", + "examples": { + "basic": "Pemeriksaan kesehatan" + }, + "responses": { + "pong": "Pong. Latensi websocket: {{latency}}ms." + } + }, + "presence": { + "name": "presence", + "description": "Mengatur status bot dengan panel interaktif.", + "examples": { + "slash": "Buka panel interaktif presence" + }, + "responses": { + "panel": "Panel presence\nStatus: {{status}}\nAktivitas: {{activityType}}\nRotasi: setiap {{rotationIntervalSeconds}}s\nTeks aktif ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nPratinjau: {{activityPreview}}\nTeks: {{activityTexts}}\nVariabel ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Hanya pembuat perintah yang dapat menggunakan panel ini.", + "invalidSelection": "Pilihan tidak valid.", + "invalidInterval": "Interval tidak valid. Gunakan angka antara {{minSeconds}} dan {{maxSeconds}} detik.", + "modalTimeout": "Waktu habis untuk mengubah teks. Klik tombol lagi." + }, + "ui": { + "status": { + "placeholder": "Pilih status", + "options": { + "online": { + "label": "Online", + "description": "Menampilkan bot online" + }, + "idle": { + "label": "Tidak aktif", + "description": "Menampilkan bot tidak aktif" + }, + "dnd": { + "label": "Jangan ganggu", + "description": "Menampilkan mode jangan ganggu" + }, + "invisible": { + "label": "Offline", + "description": "Menampilkan bot offline" + }, + "streaming": { + "label": "Streaming", + "description": "Menampilkan bot dalam mode streaming" + } + } + }, + "activity": { + "placeholder": "Pilih jenis aktivitas", + "options": { + "PLAYING": { + "label": "Bermain", + "description": "Menampilkan Bermain ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Menampilkan Sedang live di ..." + }, + "WATCHING": { + "label": "Menonton", + "description": "Menampilkan Menonton ..." + }, + "LISTENING": { + "label": "Mendengarkan", + "description": "Menampilkan Mendengarkan ..." + }, + "COMPETING": { + "label": "Kompetisi", + "description": "Menampilkan Kompetisi ..." + }, + "CUSTOM": { + "label": "Status Kustom", + "description": "Menampilkan status kustom" + } + } + }, + "embed": { + "title": "Konfigurasi Presence", + "description": "Ubah status, jenis aktivitas, beberapa teks, dan rotasinya.", + "footer": "Panel interaktif /presence", + "fields": { + "status": "Status", + "activity": "Jenis aktivitas", + "text": "Teks" + } + }, + "textButton": "📝 Ubah teks", + "intervalButton": "⏱️ Interval", + "modal": { + "title": "Teks presence", + "label": "Satu teks per baris (gunakan {{var}})", + "placeholder": "Maintenance\n{{guild_count}} server | {{prefix}}help" + }, + "intervalModal": { + "title": "Interval rotasi", + "label": "Detik antar setiap teks", + "placeholder": "Contoh: 30" + } + } + }, + "logs": { + "name": "logs", + "description": "Mengatur log event Discord dengan panel interaktif.", + "examples": { + "slash": "Buka panel interaktif logs" + }, + "responses": { + "slashOnly": "Gunakan /logs untuk membuka panel ini.", + "guildOnly": "Perintah ini harus digunakan di server.", + "notOwner": "Hanya pembuat perintah yang dapat menggunakan panel ini.", + "invalidSelection": "Pilihan tidak valid.", + "allEnabled": "Semua log event diaktifkan.", + "allDisabled": "Semua log event dinonaktifkan.", + "eventEnabled": "Event {{event}} diaktifkan.", + "eventDisabled": "Event {{event}} dinonaktifkan.", + "channelsCreated": "Channel log siap. Dibuat: {{created}}, digunakan ulang: {{reused}}.", + "channelsPartial": "Channel log sebagian siap. Dibuat: {{created}}, digunakan ulang: {{reused}}, kategori gagal: {{failed}}.", + "channelsFailed": "Gagal membuat channel log. Kategori gagal: {{failed}}." + }, + "ui": { + "embed": { + "title": "Konfigurasi logs", + "description": "Aktifkan atau nonaktifkan setiap event Discord dan arahkan log per kategori channel." + }, + "status": { + "enabled": "Aktif", + "disabled": "Nonaktif" + }, + "pageLabel": "Halaman {{page}}/{{pageCount}}", + "enabledCountLabel": "Event aktif: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Event", + "channelLabel": "Channel", + "channelNotConfigured": "#tidak-terkonfigurasi", + "feedbackLabel": "Aksi terakhir", + "buttons": { + "enableAll": "✅ Aktifkan semua log", + "disableAll": "❌ Nonaktifkan semua log", + "createChannels": "📁 Buat channel log", + "previousPage": "Sebelumnya", + "nextPage": "Selanjutnya", + "enableEvent": "Aktifkan {{event}}", + "disableEvent": "Nonaktifkan {{event}}" + }, + "events": { + "messageCreate": "Pesan dibuat", + "messageDelete": "Pesan dihapus", + "messageUpdate": "Pesan diperbarui", + "messageBulkDelete": "Hapus pesan massal", + "guildMemberAdd": "Anggota bergabung", + "guildMemberRemove": "Anggota keluar", + "guildMemberUpdate": "Anggota diperbarui", + "interactionCreate": "Interaksi", + "channelCreate": "Channel dibuat", + "channelDelete": "Channel dihapus", + "channelUpdate": "Channel diperbarui", + "roleCreate": "Peran dibuat", + "roleDelete": "Peran dihapus", + "roleUpdate": "Peran diperbarui", + "threadCreate": "Thread dibuat", + "threadDelete": "Thread dihapus", + "threadUpdate": "Thread diperbarui", + "emojiCreate": "Emoji dibuat", + "emojiDelete": "Emoji dihapus", + "emojiUpdate": "Emoji diperbarui", + "guildUpdate": "Server diperbarui", + "guildUnavailable": "Server tidak tersedia", + "guildBanAdd": "Anggota dibanned", + "guildBanRemove": "Ban dicabut", + "inviteCreate": "Undangan dibuat", + "inviteDelete": "Undangan dihapus" + } + } + }, + "welcome": { + "name": "welcome", + "description": "Mengatur pesan sambutan dengan panel interaktif.", + "examples": { + "slash": "Buka panel konfigurasi welcome" + }, + "responses": { + "guildOnly": "Perintah ini harus digunakan di server.", + "notOwner": "Hanya pembuat perintah yang dapat menggunakan panel ini.", + "invalidSelection": "Pilihan tidak valid.", + "testSuccess": "Pesan tes dikirim ke {{channel}}.", + "testDisabled": "Modul welcome dinonaktifkan.", + "testMissingChannel": "Tidak ada channel yang dikonfigurasi. Gunakan tombol Channel.", + "testChannelUnavailable": "Channel tidak ditemukan atau tidak kompatibel.", + "testMissingPermissions": "Bot tidak memiliki izin untuk mengirim pesan di channel ini.", + "testFailed": "Gagal mengirim pesan tes. Coba lagi nanti." + }, + "ui": { + "embed": { + "title": "Konfigurasi Welcome", + "description": "Atur status, channel, jenis pesan, dan lakukan tes.", + "fields": { + "status": "Status", + "channel": "Channel", + "type": "Jenis", + "channelPicker": "Pemilihan channel", + "autoRoles": "Role otomatis" + } + }, + "status": { + "enabled": "🟢 Aktif", + "disabled": "🔴 Nonaktif" + }, + "channelNotConfigured": "#tidak-terkonfigurasi", + "autoRolesNotConfigured": "Tidak ada role otomatis", + "channelPickerPlaceholder": "Pilih channel welcome", + "channelPickerHint": "Pilih channel dari menu di bawah.", + "rolePickerPlaceholder": "Pilih satu atau beberapa role otomatis", + "rolePickerHint": "Pilih role dari menu di bawah untuk ditambahkan.", + "buttons": { + "toggle": "Toggle status", + "channel": "Atur channel", + "channelCancel": "Tutup pilihan channel", + "roles": "Tambah role otomatis", + "rolesCancel": "Tutup pilihan role", + "rolesClear": "Hapus role otomatis", + "test": "Tes" + }, + "type": { + "placeholder": "Pilih jenis pesan", + "options": { + "simple": { + "label": "Pesan sederhana", + "description": "Mengirim pesan teks biasa" + }, + "embed": { + "label": "Embed", + "description": "Mengirim embed welcome" + }, + "container": { + "label": "Container (V2)", + "description": "Mengirim container Components V2" + }, + "image": { + "label": "Gambar", + "description": "Mengirim embed dengan gambar" + } + } + } + }, + "templates": { + "simple": "🎉 Selamat datang {{user}} di **{{guild}}**!", + "embedTitle": "Selamat datang!", + "embedDescription": "{{user}} baru saja bergabung di **{{guild}}**.", + "containerTitle": "Selamat datang", + "containerDescription": "{{user}} baru saja bergabung di **{{guild}}**.", + "imageTitle": "Selamat datang", + "imageDescription": "di server Discord {{guild}}" + } + }, + "goodbye": { + "name": "goodbye", + "description": "Mengatur pesan perpisahan dengan panel interaktif.", + "examples": { + "slash": "Buka panel konfigurasi pesan perpisahan" + }, + "responses": { + "guildOnly": "Perintah ini harus digunakan di server.", + "notOwner": "Hanya pembuat perintah yang dapat menggunakan panel ini.", + "invalidSelection": "Pilihan tidak valid.", + "testSuccess": "Pesan tes dikirim ke {{channel}}.", + "testDisabled": "Modul goodbye dinonaktifkan.", + "testMissingChannel": "Tidak ada channel yang dikonfigurasi. Gunakan tombol Channel.", + "testChannelUnavailable": "Channel tidak ditemukan atau tidak kompatibel.", + "testMissingPermissions": "Bot tidak memiliki izin untuk mengirim pesan di channel ini.", + "testFailed": "Gagal mengirim pesan tes. Coba lagi nanti." + }, + "ui": { + "embed": { + "title": "Konfigurasi Goodbye", + "description": "Atur status, channel, jenis pesan, dan lakukan tes.", + "fields": { + "status": "Status", + "channel": "Channel", + "type": "Jenis", + "channelPicker": "Pemilihan channel" + } + }, + "status": { + "enabled": "🟢 Aktif", + "disabled": "🔴 Nonaktif" + }, + "channelNotConfigured": "#tidak-terkonfigurasi", + "channelPickerPlaceholder": "Pilih channel goodbye", + "channelPickerHint": "Pilih channel dari menu di bawah.", + "buttons": { + "toggle": "Toggle status", + "channel": "Atur channel", + "channelCancel": "Tutup pilihan channel", + "test": "Tes" + }, + "type": { + "placeholder": "Pilih jenis pesan", + "options": { + "simple": { + "label": "Pesan sederhana", + "description": "Mengirim pesan teks biasa" + }, + "embed": { + "label": "Embed", + "description": "Mengirim embed goodbye" + }, + "container": { + "label": "Container (V2)", + "description": "Mengirim container Components V2" + }, + "image": { + "label": "Gambar", + "description": "Mengirim embed dengan gambar" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} telah meninggalkan **{{guild}}**.", + "embedTitle": "Sampai jumpa", + "embedDescription": "{{user}} telah meninggalkan **{{guild}}**.", + "containerTitle": "Sampai jumpa", + "containerDescription": "{{user}} telah meninggalkan **{{guild}}**.", + "imageTitle": "Sampai jumpa", + "imageDescription": "dari server Discord {{guild}}" + } + }, + "help": { + "name": "bantuan", + "description": "Menampilkan daftar perintah atau detail sebuah perintah.", + "args": { + "command": "Nama atau trigger perintah opsional" + }, + "examples": { + "basic": "Menampilkan semua perintah", + "single": "Menampilkan detail satu perintah" + }, + "errors": { + "notFound": "Tidak ada perintah yang cocok dengan {{query}}." + }, + "embed": { + "title": "Bantuan Perintah", + "description": "Gunakan {{usage}} untuk melihat detail perintah.", + "categoryEmpty": "Tidak ada perintah di kategori ini.", + "detailsTitle": "Perintah: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Kategori: {{source}}", + "fields": { + "usage": "Penggunaan", + "arguments": "Argumen", + "examples": "Contoh" + } + }, + "labels": { + "prefix": "Prefix", + "slash": "Slash", + "required": "wajib", + "optional": "opsional", + "noArgs": "Tidak ada argumen.", + "noExamples": "Tidak ada contoh." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(tidak ada konten)", + "unknown": "tidak diketahui", + "none": "tidak ada" + }, + "embed": { + "title": "Event: {{event}}", + "detailsField": "Detail" + }, + "summaries": { + "messageCreate": "Pesan dikirim oleh {{user}} di {{channel}}", + "messageDelete": "Pesan dihapus di {{channel}}", + "messageUpdate": "Pesan diperbarui di {{channel}}", + "messageBulkDelete": "Hapus massal: {{count}} pesan dihapus", + "guildMemberAdd": "Anggota bergabung: {{user}}", + "guildMemberRemove": "Anggota keluar: {{user}}", + "guildMemberUpdate": "Anggota diperbarui: {{user}}", + "interactionGeneric": "Interaksi oleh {{user}}", + "interactionSlash": "Perintah slash /{{command}} oleh {{user}}", + "interactionButton": "Interaksi tombol oleh {{user}}", + "interactionSelect": "Interaksi menu oleh {{user}}", + "interactionModal": "Pengiriman modal oleh {{user}}", + "channelCreate": "Channel dibuat: {{channel}}", + "channelDelete": "Channel dihapus: {{channel}}", + "channelUpdate": "Channel diperbarui: {{channel}}", + "roleCreate": "Role dibuat: {{role}}", + "roleDelete": "Role dihapus: {{role}}", + "roleUpdate": "Role diperbarui: {{role}}", + "threadCreate": "Thread dibuat: {{thread}}", + "threadDelete": "Thread dihapus: {{thread}}", + "threadUpdate": "Thread diperbarui: {{thread}}", + "emojiCreate": "Emoji dibuat: {{emoji}}", + "emojiDelete": "Emoji dihapus: {{emoji}}", + "emojiUpdate": "Emoji diperbarui: {{emoji}}", + "guildUpdate": "Server diperbarui: {{guild}}", + "guildUnavailable": "Server sementara tidak tersedia: {{guild}}", + "guildBanAdd": "Pengguna dibanned: {{user}}", + "guildBanRemove": "Ban dicabut: {{user}}", + "inviteCreate": "Undangan dibuat: {{code}}", + "inviteDelete": "Undangan dihapus: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/it.json b/apps/bot/locales/it.json new file mode 100644 index 0000000..00181c7 --- /dev/null +++ b/apps/bot/locales/it.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Argomento mancante {{arg}}. Uso: {{usage}}", + "invalidInt": "L'argomento deve essere un intero. Ricevuto: {{value}}", + "invalidNumber": "L'argomento deve essere un numero. Ricevuto: {{value}}", + "invalidBoolean": "L'argomento deve essere un booleano. Ricevuto: {{value}}", + "invalidUser": "Impossibile trovare un utente da {{value}}", + "invalidChannel": "Impossibile trovare un canale da {{value}}", + "invalidRole": "Impossibile trovare un ruolo da {{value}}", + "tooMany": "Troppi argomenti ({{count}}) rilevati: {{extras}}. Uso: {{usage}}" + }, + "permissions": { + "user": "Non hai i permessi: {{permissions}}" + }, + "execution": "Si è verificato un errore imprevisto durante l'esecuzione.", + "cooldown": "Attendi {{seconds}}s prima di riutilizzare questo comando.", + "rateLimit": "Troppe richieste. Attendi {{seconds}}s prima di riprovare.", + "executionQueued": "Comando accettato e inserito in coda di elaborazione." + }, + "categories": { + "fun": "Divertimento", + "utility": "Utilità", + "core": "Base" + }, + "commands": { + "kiss": { + "name": "bacio", + "description": "Invia un bacio a un utente.", + "args": { + "user": "Utente bersaglio" + }, + "examples": { + "basic": "Invia un bacio a un membro" + }, + "responses": { + "success": "{{from}} invia un bacio a {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Verifica la latenza websocket del bot.", + "examples": { + "basic": "Controllo di stato" + }, + "responses": { + "pong": "Pong. Latenza websocket: {{latency}}ms." + } + }, + "presence": { + "name": "presenza", + "description": "Configura la presenza del bot con un pannello interattivo.", + "examples": { + "slash": "Apri il pannello interattivo della presenza" + }, + "responses": { + "panel": "Pannello presenza\nStato: {{status}}\nAttività: {{activityType}}\nRotazione: ogni {{rotationIntervalSeconds}}s\nTesto attivo ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nAnteprima: {{activityPreview}}\nTesti: {{activityTexts}}\nVariabili ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Solo l'autore del comando può usare questo pannello.", + "invalidSelection": "Selezione non valida.", + "invalidInterval": "Intervallo non valido. Usa un intero tra {{minSeconds}} e {{maxSeconds}} secondi.", + "modalTimeout": "Tempo scaduto per la modifica del testo. Clicca di nuovo il pulsante." + }, + "ui": { + "status": { + "placeholder": "Scegli uno stato", + "options": { + "online": { + "label": "Online", + "description": "Mostra il bot online" + }, + "idle": { + "label": "Inattivo", + "description": "Mostra il bot inattivo" + }, + "dnd": { + "label": "Non disturbare", + "description": "Mostra il bot in modalità non disturbare" + }, + "invisible": { + "label": "Offline", + "description": "Mostra il bot offline" + }, + "streaming": { + "label": "Streaming", + "description": "Mostra il bot in streaming" + } + } + }, + "activity": { + "placeholder": "Scegli un tipo di attività", + "options": { + "PLAYING": { + "label": "Gioca a", + "description": "Mostra Gioca a ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Mostra In diretta su ..." + }, + "WATCHING": { + "label": "Guarda", + "description": "Mostra Guarda ..." + }, + "LISTENING": { + "label": "Ascolta", + "description": "Mostra Ascolta ..." + }, + "COMPETING": { + "label": "Competizione", + "description": "Mostra Competizione ..." + }, + "CUSTOM": { + "label": "Stato personalizzato", + "description": "Mostra uno stato personalizzato" + } + } + }, + "embed": { + "title": "Configurazione presenza", + "description": "Modifica stato, tipo di attività, testi e rotazione.", + "footer": "Pannello interattivo /presence", + "fields": { + "status": "Stato", + "activity": "Tipo di attività", + "text": "Testo" + } + }, + "textButton": "📝 Modifica testi", + "intervalButton": "⏱️ Intervallo", + "modal": { + "title": "Testi presenza", + "label": "Un testo per riga (usa {{var}})", + "placeholder": "Manutenzione\n{{guild_count}} server | {{prefix}}aiuto" + }, + "intervalModal": { + "title": "Intervallo di rotazione", + "label": "Secondi tra ogni testo", + "placeholder": "Esempio: 30" + } + } + }, + "logs": { + "name": "log", + "description": "Configura i log degli eventi Discord con un pannello interattivo.", + "examples": { + "slash": "Apri il pannello interattivo dei log" + }, + "responses": { + "slashOnly": "Usa /logs per aprire questo pannello interattivo.", + "guildOnly": "Questo comando deve essere usato in un server.", + "notOwner": "Solo l'autore del comando può usare questo pannello.", + "invalidSelection": "Selezione non valida.", + "allEnabled": "Tutti i log degli eventi sono attivi.", + "allDisabled": "Tutti i log degli eventi sono disattivati.", + "eventEnabled": "Evento {{event}} attivato.", + "eventDisabled": "Evento {{event}} disattivato.", + "channelsCreated": "Canali log pronti. Creati: {{created}}, riutilizzati: {{reused}}.", + "channelsPartial": "Canali log parzialmente pronti. Creati: {{created}}, riutilizzati: {{reused}}, categorie fallite: {{failed}}.", + "channelsFailed": "Impossibile creare i canali log. Categorie fallite: {{failed}}." + }, + "ui": { + "embed": { + "title": "Configurazione log", + "description": "Attiva o disattiva ogni evento Discord e instrada i log nei canali." + }, + "status": { + "enabled": "Attivo", + "disabled": "Disattivo" + }, + "pageLabel": "Pagina {{page}}/{{pageCount}}", + "enabledCountLabel": "Eventi attivi: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Eventi", + "channelLabel": "Canale", + "channelNotConfigured": "#non-configurato", + "feedbackLabel": "Ultima azione", + "buttons": { + "enableAll": "✅ Attiva tutti i log", + "disableAll": "❌ Disattiva tutti i log", + "createChannels": "📁 Crea canali log", + "previousPage": "Precedente", + "nextPage": "Successivo", + "enableEvent": "Attiva {{event}}", + "disableEvent": "Disattiva {{event}}" + }, + "events": { + "messageCreate": "Messaggio creato", + "messageDelete": "Messaggio eliminato", + "messageUpdate": "Messaggio aggiornato", + "messageBulkDelete": "Eliminazione massiva messaggi", + "guildMemberAdd": "Membro entrato", + "guildMemberRemove": "Membro uscito", + "guildMemberUpdate": "Membro aggiornato", + "interactionCreate": "Interazione", + "channelCreate": "Canale creato", + "channelDelete": "Canale eliminato", + "channelUpdate": "Canale aggiornato", + "roleCreate": "Ruolo creato", + "roleDelete": "Ruolo eliminato", + "roleUpdate": "Ruolo aggiornato", + "threadCreate": "Thread creato", + "threadDelete": "Thread eliminato", + "threadUpdate": "Thread aggiornato", + "emojiCreate": "Emoji creata", + "emojiDelete": "Emoji eliminata", + "emojiUpdate": "Emoji aggiornata", + "guildUpdate": "Server aggiornato", + "guildUnavailable": "Server non disponibile", + "guildBanAdd": "Utente bannato", + "guildBanRemove": "Utente sbannato", + "inviteCreate": "Invito creato", + "inviteDelete": "Invito eliminato" + } + } + }, + "welcome": { + "name": "benvenuto", + "description": "Configura i messaggi di benvenuto con un pannello interattivo.", + "examples": { + "slash": "Apri il pannello di configurazione benvenuto" + }, + "responses": { + "guildOnly": "Questo comando deve essere usato in un server.", + "notOwner": "Solo l'autore del comando può usare questo pannello.", + "invalidSelection": "Selezione non valida.", + "testSuccess": "Messaggio di test inviato in {{channel}}.", + "testDisabled": "Il modulo benvenuto è disattivato.", + "testMissingChannel": "Nessun canale configurato. Usa il pulsante Canale.", + "testChannelUnavailable": "Il canale configurato non è disponibile o compatibile.", + "testMissingPermissions": "Il bot non ha il permesso di inviare messaggi in questo canale.", + "testFailed": "Impossibile inviare il messaggio di test. Riprova più tardi." + }, + "ui": { + "embed": { + "title": "Configurazione Welcome", + "description": "Imposta stato, canale, tipo di messaggio e test.", + "fields": { + "status": "Stato", + "channel": "Canale", + "type": "Tipo", + "channelPicker": "Selezione canale", + "autoRoles": "Ruoli automatici" + } + }, + "status": { + "enabled": "🟢 Attivo", + "disabled": "🔴 Disattivo" + }, + "channelNotConfigured": "#non-configurato", + "autoRolesNotConfigured": "Nessun ruolo automatico configurato", + "channelPickerPlaceholder": "Seleziona un canale di benvenuto", + "channelPickerHint": "Scegli il canale dal menu.", + "rolePickerPlaceholder": "Seleziona uno o più ruoli automatici", + "rolePickerHint": "Scegli i ruoli dal menu per aggiungerli.", + "buttons": { + "toggle": "Attiva/Disattiva", + "channel": "Configura canale", + "channelCancel": "Chiudi selezione canale", + "roles": "Aggiungi ruoli auto", + "rolesCancel": "Chiudi selezione ruoli", + "rolesClear": "Svuota ruoli auto", + "test": "Test" + }, + "type": { + "placeholder": "Scegli un tipo di messaggio", + "options": { + "simple": { + "label": "Messaggio semplice", + "description": "Invia un messaggio di testo" + }, + "embed": { + "label": "Embed", + "description": "Invia un embed di benvenuto" + }, + "container": { + "label": "Container (V2)", + "description": "Invia un container Components V2" + }, + "image": { + "label": "Immagine", + "description": "Invia un embed con immagine" + } + } + } + }, + "templates": { + "simple": "🎉 Benvenuto {{user}} su **{{guild}}** !", + "embedTitle": "Benvenuto!", + "embedDescription": "{{user}} è appena entrato in **{{guild}}**.", + "containerTitle": "Benvenuto", + "containerDescription": "{{user}} è appena entrato in **{{guild}}**.", + "imageTitle": "Benvenuto", + "imageDescription": "nel server Discord {{guild}}" + } + }, + "goodbye": { + "name": "addio", + "description": "Configura i messaggi di addio con un pannello interattivo.", + "examples": { + "slash": "Apri il pannello di configurazione addio" + }, + "responses": { + "guildOnly": "Questo comando deve essere usato in un server.", + "notOwner": "Solo l'autore del comando può usare questo pannello.", + "invalidSelection": "Selezione non valida.", + "testSuccess": "Messaggio di test inviato in {{channel}}.", + "testDisabled": "Il modulo addio è disattivato.", + "testMissingChannel": "Nessun canale configurato. Usa il pulsante Canale.", + "testChannelUnavailable": "Il canale configurato non è disponibile o compatibile.", + "testMissingPermissions": "Il bot non ha il permesso di inviare messaggi in questo canale.", + "testFailed": "Impossibile inviare il messaggio di test. Riprova più tardi." + }, + "ui": { + "embed": { + "title": "Configurazione Goodbye", + "description": "Imposta stato, canale, tipo di messaggio e test.", + "fields": { + "status": "Stato", + "channel": "Canale", + "type": "Tipo", + "channelPicker": "Selezione canale" + } + }, + "status": { + "enabled": "🟢 Attivo", + "disabled": "🔴 Disattivo" + }, + "channelNotConfigured": "#non-configurato", + "channelPickerPlaceholder": "Seleziona un canale di addio", + "channelPickerHint": "Scegli il canale dal menu.", + "buttons": { + "toggle": "Attiva/Disattiva", + "channel": "Configura canale", + "channelCancel": "Chiudi selezione canale", + "test": "Test" + }, + "type": { + "placeholder": "Scegli un tipo di messaggio", + "options": { + "simple": { + "label": "Messaggio semplice", + "description": "Invia un messaggio di testo" + }, + "embed": { + "label": "Embed", + "description": "Invia un embed di addio" + }, + "container": { + "label": "Container (V2)", + "description": "Invia un container Components V2" + }, + "image": { + "label": "Immagine", + "description": "Invia un embed con immagine" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} ha lasciato **{{guild}}**.", + "embedTitle": "Addio", + "embedDescription": "{{user}} ha lasciato **{{guild}}**.", + "containerTitle": "Addio", + "containerDescription": "{{user}} ha lasciato **{{guild}}**.", + "imageTitle": "Addio", + "imageDescription": "dal server Discord {{guild}}" + } + }, + "help": { + "name": "aiuto", + "description": "Mostra la lista dei comandi o i dettagli di un comando.", + "args": { + "command": "Nome o trigger del comando opzionale" + }, + "examples": { + "basic": "Mostra tutti i comandi", + "single": "Mostra i dettagli di un comando" + }, + "errors": { + "notFound": "Nessun comando corrisponde a {{query}}." + }, + "embed": { + "title": "Aiuto Comandi", + "description": "Usa {{usage}} per vedere un comando.", + "categoryEmpty": "Nessun comando in questa categoria.", + "detailsTitle": "Comando: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Categoria: {{source}}", + "fields": { + "usage": "Uso", + "arguments": "Argomenti", + "examples": "Esempi" + } + }, + "labels": { + "prefix": "Prefisso", + "slash": "Slash", + "required": "obbligatorio", + "optional": "opzionale", + "noArgs": "Nessun argomento.", + "noExamples": "Nessun esempio." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(nessun contenuto)", + "unknown": "sconosciuto", + "none": "nessuno" + }, + "embed": { + "title": "Evento: {{event}}", + "detailsField": "Dettagli" + }, + "summaries": { + "messageCreate": "Messaggio inviato da {{user}} in {{channel}}", + "messageDelete": "Messaggio eliminato in {{channel}}", + "messageUpdate": "Messaggio aggiornato in {{channel}}", + "messageBulkDelete": "Eliminazione massiva: {{count}} messaggi rimossi", + "guildMemberAdd": "Membro entrato: {{user}}", + "guildMemberRemove": "Membro uscito: {{user}}", + "guildMemberUpdate": "Membro aggiornato: {{user}}", + "interactionGeneric": "Interazione di {{user}}", + "interactionSlash": "Comando /{{command}} di {{user}}", + "interactionButton": "Interazione bottone di {{user}}", + "interactionSelect": "Menu selezione di {{user}}", + "interactionModal": "Invio modulo di {{user}}", + "channelCreate": "Canale creato: {{channel}}", + "channelDelete": "Canale eliminato: {{channel}}", + "channelUpdate": "Canale aggiornato: {{channel}}", + "roleCreate": "Ruolo creato: {{role}}", + "roleDelete": "Ruolo eliminato: {{role}}", + "roleUpdate": "Ruolo aggiornato: {{role}}", + "threadCreate": "Thread creato: {{thread}}", + "threadDelete": "Thread eliminato: {{thread}}", + "threadUpdate": "Thread aggiornato: {{thread}}", + "emojiCreate": "Emoji creata: {{emoji}}", + "emojiDelete": "Emoji eliminata: {{emoji}}", + "emojiUpdate": "Emoji aggiornata: {{emoji}}", + "guildUpdate": "Server aggiornato: {{guild}}", + "guildUnavailable": "Server temporaneamente non disponibile: {{guild}}", + "guildBanAdd": "Utente bannato: {{user}}", + "guildBanRemove": "Utente sbannato: {{user}}", + "inviteCreate": "Invito creato: {{code}}", + "inviteDelete": "Invito eliminato: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/ja.json b/apps/bot/locales/ja.json new file mode 100644 index 0000000..847ecd6 --- /dev/null +++ b/apps/bot/locales/ja.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "引数 {{arg}} が不足しています。使用方法: {{usage}}", + "invalidInt": "引数は整数である必要があります。受け取った値: {{value}}", + "invalidNumber": "引数は数値である必要があります。受け取った値: {{value}}", + "invalidBoolean": "引数は真偽値である必要があります。受け取った値: {{value}}", + "invalidUser": "{{value}} からユーザーを見つけられません", + "invalidChannel": "{{value}} からチャンネルを見つけられません", + "invalidRole": "{{value}} からロールを見つけられません", + "tooMany": "引数が多すぎます({{count}}):{{extras}}。使用方法: {{usage}}" + }, + "permissions": { + "user": "あなたには権限がありません: {{permissions}}" + }, + "execution": "実行中に予期しないエラーが発生しました。", + "cooldown": "このコマンドを再使用するまで {{seconds}} 秒待ってください。", + "rateLimit": "リクエストが多すぎます。{{seconds}} 秒後に再試行してください。", + "executionQueued": "コマンドは受け付けられ、キューに追加されました。" + }, + "categories": { + "fun": "楽しみ", + "utility": "ユーティリティ", + "core": "基本" + }, + "commands": { + "kiss": { + "name": "キス", + "description": "ユーザーにキスを送る。", + "args": { + "user": "対象ユーザー" + }, + "examples": { + "basic": "メンバーにキスを送る" + }, + "responses": { + "success": "{{from}} が {{to}} にキスを送りました。" + } + }, + "ping": { + "name": "ピング", + "description": "ボットのWebSocketレイテンシを確認する。", + "examples": { + "basic": "ヘルスチェック" + }, + "responses": { + "pong": "Pong。WebSocket遅延: {{latency}}ms。" + } + }, + "presence": { + "name": "プレゼンス", + "description": "インタラクティブパネルでボットのステータスを設定する。", + "examples": { + "slash": "プレゼンスパネルを開く" + }, + "responses": { + "panel": "プレゼンスパネル\nステータス: {{status}}\nアクティビティ: {{activityType}}\nローテーション: {{rotationIntervalSeconds}}秒ごと\nアクティブテキスト({{currentTextIndex}}/{{textCount}}): {{activityText}}\nプレビュー: {{activityPreview}}\nテキスト: {{activityTexts}}\n変数({{doubleBracesHint}}): {{variables}}", + "notOwner": "このパネルはコマンド実行者のみ使用できます。", + "invalidSelection": "無効な選択です。", + "invalidInterval": "無効な間隔です。{{minSeconds}}〜{{maxSeconds}}秒の整数を使用してください。", + "modalTimeout": "テキスト編集の制限時間が切れました。もう一度ボタンをクリックしてください。" + }, + "ui": { + "status": { + "placeholder": "ステータスを選択", + "options": { + "online": { + "label": "オンライン", + "description": "ボットをオンライン表示" + }, + "idle": { + "label": "アイドル", + "description": "ボットをアイドル表示" + }, + "dnd": { + "label": "取り込み中", + "description": "ボットを取り込み中表示" + }, + "invisible": { + "label": "オフライン", + "description": "ボットをオフライン表示" + }, + "streaming": { + "label": "ストリーミング", + "description": "ボットをストリーミング表示" + } + } + }, + "activity": { + "placeholder": "アクティビティタイプを選択", + "options": { + "PLAYING": { + "label": "プレイ中", + "description": "プレイ中..." + }, + "STREAMING": { + "label": "ストリーミング", + "description": "配信中..." + }, + "WATCHING": { + "label": "視聴中", + "description": "見ている..." + }, + "LISTENING": { + "label": "リスニング", + "description": "聴いている..." + }, + "COMPETING": { + "label": "競技中", + "description": "競技中..." + }, + "CUSTOM": { + "label": "カスタムステータス", + "description": "カスタムステータスを表示" + } + } + }, + "embed": { + "title": "プレゼンス設定", + "description": "ステータス、アクティビティタイプ、複数テキストとローテーションを編集します。", + "footer": "/presence インタラクティブパネル", + "fields": { + "status": "ステータス", + "activity": "アクティビティタイプ", + "text": "テキスト" + } + }, + "textButton": "📝 テキスト編集", + "intervalButton": "⏱️ 間隔", + "modal": { + "title": "プレゼンステキスト", + "label": "1行に1テキスト({{var}}使用可)", + "placeholder": "メンテナンス\n{{guild_count}} サーバー | {{prefix}}help" + }, + "intervalModal": { + "title": "ローテーション間隔", + "label": "各テキストの秒数", + "placeholder": "例: 30" + } + } + }, + "logs": { + "name": "ログ", + "description": "Discordイベントログをインタラクティブに設定する。", + "examples": { + "slash": "ログパネルを開く" + }, + "responses": { + "slashOnly": "/logs を使用してこのパネルを開いてください。", + "guildOnly": "このコマンドはサーバーでのみ使用できます。", + "notOwner": "このパネルはコマンド実行者のみ使用できます。", + "invalidSelection": "無効な選択です。", + "allEnabled": "すべてのイベントログが有効になりました。", + "allDisabled": "すべてのイベントログが無効になりました。", + "eventEnabled": "イベント {{event}} を有効化しました。", + "eventDisabled": "イベント {{event}} を無効化しました。", + "channelsCreated": "ログチャンネル準備完了。作成: {{created}}、再利用: {{reused}}。", + "channelsPartial": "ログチャンネルは一部のみ準備されました。作成: {{created}}、再利用: {{reused}}、失敗: {{failed}}。", + "channelsFailed": "ログチャンネルの作成に失敗しました。失敗カテゴリ: {{failed}}。" + }, + "ui": { + "embed": { + "title": "ログ設定", + "description": "各Discordイベントのログを有効/無効化し、カテゴリ別チャンネルに送信します。" + }, + "status": { + "enabled": "有効", + "disabled": "無効" + }, + "pageLabel": "ページ {{page}}/{{pageCount}}", + "enabledCountLabel": "有効イベント: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "イベント", + "channelLabel": "チャンネル", + "channelNotConfigured": "#未設定", + "feedbackLabel": "最後の操作", + "buttons": { + "enableAll": "✅ すべて有効化", + "disableAll": "❌ すべて無効化", + "createChannels": "📁 チャンネル作成", + "previousPage": "前へ", + "nextPage": "次へ", + "enableEvent": "{{event}} を有効化", + "disableEvent": "{{event}} を無効化" + }, + "events": { + "messageCreate": "メッセージ作成", + "messageDelete": "メッセージ削除", + "messageUpdate": "メッセージ更新", + "messageBulkDelete": "一括メッセージ削除", + "guildMemberAdd": "メンバー参加", + "guildMemberRemove": "メンバー退出", + "guildMemberUpdate": "メンバー更新", + "interactionCreate": "インタラクション", + "channelCreate": "チャンネル作成", + "channelDelete": "チャンネル削除", + "channelUpdate": "チャンネル更新", + "roleCreate": "ロール作成", + "roleDelete": "ロール削除", + "roleUpdate": "ロール更新", + "threadCreate": "スレッド作成", + "threadDelete": "スレッド削除", + "threadUpdate": "スレッド更新", + "emojiCreate": "絵文字作成", + "emojiDelete": "絵文字削除", + "emojiUpdate": "絵文字更新", + "guildUpdate": "サーバー更新", + "guildUnavailable": "サーバー利用不可", + "guildBanAdd": "BAN追加", + "guildBanRemove": "BAN解除", + "inviteCreate": "招待作成", + "inviteDelete": "招待削除" + } + } + }, + "welcome": { + "name": "ウェルカム", + "description": "ウェルカムメッセージをインタラクティブに設定する。", + "examples": { + "slash": "ウェルカム設定パネルを開く" + }, + "responses": { + "guildOnly": "このコマンドはサーバーでのみ使用できます。", + "notOwner": "このパネルはコマンド実行者のみ使用できます。", + "invalidSelection": "無効な選択です。", + "testSuccess": "テストメッセージを {{channel}} に送信しました。", + "testDisabled": "ウェルカム機能は無効です。", + "testMissingChannel": "チャンネルが設定されていません。チャンネルボタンを使用してください。", + "testChannelUnavailable": "設定されたチャンネルが見つからないか無効です。", + "testMissingPermissions": "このチャンネルにメッセージを送信する権限がありません。", + "testFailed": "テストメッセージの送信に失敗しました。後でもう一度試してください。" + }, + "ui": { + "embed": { + "title": "ウェルカム設定", + "description": "ステータス、チャンネル、メッセージタイプを設定し、テストを実行します。", + "fields": { + "status": "ステータス", + "channel": "チャンネル", + "type": "タイプ", + "channelPicker": "チャンネル選択", + "autoRoles": "自動ロール" + } + }, + "status": { + "enabled": "🟢 有効", + "disabled": "🔴 無効" + }, + "channelNotConfigured": "#未設定", + "autoRolesNotConfigured": "自動ロールは設定されていません", + "channelPickerPlaceholder": "ウェルカムチャンネルを選択", + "channelPickerHint": "下のメニューからチャンネルを選択してください。", + "rolePickerPlaceholder": "自動ロールを選択", + "rolePickerHint": "追加するロールを選択してください。", + "buttons": { + "toggle": "ステータス切替", + "channel": "チャンネル設定", + "channelCancel": "チャンネル選択を閉じる", + "roles": "自動ロール追加", + "rolesCancel": "ロール選択を閉じる", + "rolesClear": "自動ロールを削除", + "test": "テスト" + }, + "type": { + "placeholder": "メッセージタイプを選択", + "options": { + "simple": { + "label": "シンプルメッセージ", + "description": "通常のテキストメッセージを送信" + }, + "embed": { + "label": "埋め込み", + "description": "埋め込みメッセージを送信" + }, + "container": { + "label": "コンテナ (V2)", + "description": "Components V2 コンテナを送信" + }, + "image": { + "label": "画像", + "description": "画像付き埋め込みを送信" + } + } + } + }, + "templates": { + "simple": "🎉 {{user}} さん、**{{guild}}** へようこそ!", + "embedTitle": "ようこそ!", + "embedDescription": "{{user}} さんが **{{guild}}** に参加しました。", + "containerTitle": "ようこそ", + "containerDescription": "{{user}} さんが **{{guild}}** に参加しました。", + "imageTitle": "ようこそ", + "imageDescription": "{{guild}} のDiscordサーバーへようこそ" + } + }, + "goodbye": { + "name": "お別れ", + "description": "お別れメッセージをインタラクティブに設定する。", + "examples": { + "slash": "お別れ設定パネルを開く" + }, + "responses": { + "guildOnly": "このコマンドはサーバーでのみ使用できます。", + "notOwner": "このパネルはコマンド実行者のみ使用できます。", + "invalidSelection": "無効な選択です。", + "testSuccess": "テストメッセージを {{channel}} に送信しました。", + "testDisabled": "お別れ機能は無効です。", + "testMissingChannel": "チャンネルが設定されていません。チャンネルボタンを使用してください。", + "testChannelUnavailable": "設定されたチャンネルが見つからないか無効です。", + "testMissingPermissions": "このチャンネルにメッセージを送信する権限がありません。", + "testFailed": "テストメッセージの送信に失敗しました。後でもう一度試してください。" + }, + "ui": { + "embed": { + "title": "お別れ設定", + "description": "ステータス、チャンネル、メッセージタイプを設定し、テストを実行します。", + "fields": { + "status": "ステータス", + "channel": "チャンネル", + "type": "タイプ", + "channelPicker": "チャンネル選択" + } + }, + "status": { + "enabled": "🟢 有効", + "disabled": "🔴 無効" + }, + "channelNotConfigured": "#未設定", + "channelPickerPlaceholder": "お別れチャンネルを選択", + "channelPickerHint": "下のメニューからチャンネルを選択してください。", + "buttons": { + "toggle": "ステータス切替", + "channel": "チャンネル設定", + "channelCancel": "チャンネル選択を閉じる", + "test": "テスト" + }, + "type": { + "placeholder": "メッセージタイプを選択", + "options": { + "simple": { + "label": "シンプルメッセージ", + "description": "通常のテキストメッセージを送信" + }, + "embed": { + "label": "埋め込み", + "description": "お別れ埋め込みを送信" + }, + "container": { + "label": "コンテナ (V2)", + "description": "Components V2 コンテナを送信" + }, + "image": { + "label": "画像", + "description": "画像付き埋め込みを送信" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} さんが **{{guild}}** から退出しました。", + "embedTitle": "お別れ", + "embedDescription": "{{user}} さんが **{{guild}}** から退出しました。", + "containerTitle": "お別れ", + "containerDescription": "{{user}} さんが **{{guild}}** から退出しました。", + "imageTitle": "お別れ", + "imageDescription": "{{guild}} のDiscordサーバーから" + } + }, + "help": { + "name": "ヘルプ", + "description": "コマンド一覧または詳細を表示する。", + "args": { + "command": "コマンド名(任意)" + }, + "examples": { + "basic": "すべてのコマンドを表示", + "single": "コマンド詳細を表示" + }, + "errors": { + "notFound": "{{query}} に一致するコマンドはありません。" + }, + "embed": { + "title": "コマンドヘルプ", + "description": "{{usage}} を使ってコマンドを確認できます。", + "categoryEmpty": "このカテゴリにはコマンドがありません。", + "detailsTitle": "コマンド: {{name}}", + "detailsDescription": "{{description}}", + "footer": "カテゴリ: {{source}}", + "fields": { + "usage": "使用方法", + "arguments": "引数", + "examples": "例" + } + }, + "labels": { + "prefix": "プレフィックス", + "slash": "スラッシュ", + "required": "必須", + "optional": "任意", + "noArgs": "引数なし", + "noExamples": "例なし" + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(内容なし)", + "unknown": "不明", + "none": "なし" + }, + "embed": { + "title": "イベント: {{event}}", + "detailsField": "詳細" + }, + "summaries": { + "messageCreate": "{{user}} が {{channel}} にメッセージを送信", + "messageDelete": "{{channel}} でメッセージが削除されました", + "messageUpdate": "{{channel}} でメッセージが更新されました", + "messageBulkDelete": "{{count}} 件のメッセージが一括削除されました", + "guildMemberAdd": "{{user}} が参加しました", + "guildMemberRemove": "{{user}} が退出しました", + "guildMemberUpdate": "{{user}} が更新されました", + "interactionGeneric": "{{user}} によるインタラクション", + "interactionSlash": "{{user}} によるスラッシュコマンド /{{command}}", + "interactionButton": "{{user}} によるボタン操作", + "interactionSelect": "{{user}} によるセレクト操作", + "interactionModal": "{{user}} によるモーダル送信", + "channelCreate": "チャンネル作成: {{channel}}", + "channelDelete": "チャンネル削除: {{channel}}", + "channelUpdate": "チャンネル更新: {{channel}}", + "roleCreate": "ロール作成: {{role}}", + "roleDelete": "ロール削除: {{role}}", + "roleUpdate": "ロール更新: {{role}}", + "threadCreate": "スレッド作成: {{thread}}", + "threadDelete": "スレッド削除: {{thread}}", + "threadUpdate": "スレッド更新: {{thread}}", + "emojiCreate": "絵文字作成: {{emoji}}", + "emojiDelete": "絵文字削除: {{emoji}}", + "emojiUpdate": "絵文字更新: {{emoji}}", + "guildUpdate": "サーバー更新: {{guild}}", + "guildUnavailable": "サーバー一時的に利用不可: {{guild}}", + "guildBanAdd": "ユーザーBAN: {{user}}", + "guildBanRemove": "ユーザーBAN解除: {{user}}", + "inviteCreate": "招待作成: {{code}}", + "inviteDelete": "招待削除: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/nl.json b/apps/bot/locales/nl.json new file mode 100644 index 0000000..80aa294 --- /dev/null +++ b/apps/bot/locales/nl.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Ontbrekend argument {{arg}}. Gebruik: {{usage}}", + "invalidInt": "Het argument moet een geheel getal zijn. Ontvangen: {{value}}", + "invalidNumber": "Het argument moet een getal zijn. Ontvangen: {{value}}", + "invalidBoolean": "Het argument moet een booleaanse waarde zijn. Ontvangen: {{value}}", + "invalidUser": "Kan geen gebruiker vinden voor {{value}}", + "invalidChannel": "Kan geen kanaal vinden voor {{value}}", + "invalidRole": "Kan geen rol vinden voor {{value}}", + "tooMany": "Te veel argumenten ({{count}}) gedetecteerd: {{extras}}. Gebruik: {{usage}}" + }, + "permissions": { + "user": "Je hebt niet de volgende rechten: {{permissions}}" + }, + "execution": "Er is een onverwachte fout opgetreden tijdens de uitvoering.", + "cooldown": "Wacht {{seconds}}s voordat je dit commando opnieuw gebruikt.", + "rateLimit": "Te veel verzoeken. Wacht {{seconds}}s voordat je opnieuw probeert.", + "executionQueued": "Commando geaccepteerd en in de wachtrij geplaatst." + }, + "categories": { + "fun": "Fun", + "utility": "Hulpmiddelen", + "core": "Basis" + }, + "commands": { + "kiss": { + "name": "kus", + "description": "Stuur een kus naar een gebruiker.", + "args": { + "user": "Doelgebruiker" + }, + "examples": { + "basic": "Stuur een kus naar een lid" + }, + "responses": { + "success": "{{from}} geeft {{to}} een kus." + } + }, + "ping": { + "name": "ping", + "description": "Controleer de websocket-latentie van de bot.", + "examples": { + "basic": "Gezondheidscontrole" + }, + "responses": { + "pong": "Pong. Websocket-latentie: {{latency}}ms." + } + }, + "presence": { + "name": "presence", + "description": "Configureer de aanwezigheid van de bot met een interactief paneel.", + "examples": { + "slash": "Open het interactieve aanwezigheidspaneel" + }, + "responses": { + "panel": "Aanwezigheidspaneel\nStatus: {{status}}\nActiviteit: {{activityType}}\nRotatie: elke {{rotationIntervalSeconds}}s\nActieve tekst ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nVoorbeeldweergave: {{activityPreview}}\nTeksten: {{activityTexts}}\nVariabelen ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Alleen de auteur van het commando kan dit paneel gebruiken.", + "invalidSelection": "Ongeldige selectie.", + "invalidInterval": "Ongeldig interval. Gebruik een geheel getal tussen {{minSeconds}} en {{maxSeconds}} seconden.", + "modalTimeout": "Tijd verstreken om de tekst te wijzigen. Klik opnieuw op de knop." + }, + "ui": { + "status": { + "placeholder": "Kies een status", + "options": { + "online": { + "label": "Online", + "description": "Toont de bot als online" + }, + "idle": { + "label": "Inactief", + "description": "Toont de bot als inactief" + }, + "dnd": { + "label": "Niet storen", + "description": "Toont de bot in niet storen-modus" + }, + "invisible": { + "label": "Offline", + "description": "Toont de bot als offline" + }, + "streaming": { + "label": "Streaming", + "description": "Toont de bot als streaming" + } + } + }, + "activity": { + "placeholder": "Kies een activiteitstype", + "options": { + "PLAYING": { + "label": "Speelt", + "description": "Toont Speelt ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Toont Live op ..." + }, + "WATCHING": { + "label": "Kijkt", + "description": "Toont Kijkt ..." + }, + "LISTENING": { + "label": "Luistert", + "description": "Toont Luistert ..." + }, + "COMPETING": { + "label": "Competitie", + "description": "Toont Competitie ..." + }, + "CUSTOM": { + "label": "Aangepaste status", + "description": "Toont een aangepaste status" + } + } + }, + "embed": { + "title": "Aanwezigheidsconfiguratie", + "description": "Wijzig status, activiteitstype, meerdere teksten en rotatie.", + "footer": "Interactief paneel /presence", + "fields": { + "status": "Status", + "activity": "Activiteitstype", + "text": "Tekst" + } + }, + "textButton": "📝 Teksten bewerken", + "intervalButton": "⏱️ Interval", + "modal": { + "title": "Aanwezigheidsteksten", + "label": "Één tekst per regel (gebruik {{var}})", + "placeholder": "Onderhoud\n{{guild_count}} servers | {{prefix}}help" + }, + "intervalModal": { + "title": "Rotatie-interval", + "label": "Seconden tussen elke tekst", + "placeholder": "Voorbeeld: 30" + } + } + }, + "logs": { + "name": "logs", + "description": "Configureer Discord event-logs met een interactief paneel.", + "examples": { + "slash": "Open het interactieve logpaneel" + }, + "responses": { + "slashOnly": "Gebruik /logs om dit interactieve paneel te openen.", + "guildOnly": "Dit commando moet in een server worden gebruikt.", + "notOwner": "Alleen de auteur van het commando kan dit paneel gebruiken.", + "invalidSelection": "Ongeldige selectie.", + "allEnabled": "Alle event-logs zijn ingeschakeld.", + "allDisabled": "Alle event-logs zijn uitgeschakeld.", + "eventEnabled": "Event {{event}} ingeschakeld.", + "eventDisabled": "Event {{event}} uitgeschakeld.", + "channelsCreated": "Logkanalen klaar. Gemaakt: {{created}}, hergebruikt: {{reused}}.", + "channelsPartial": "Logkanalen gedeeltelijk klaar. Gemaakt: {{created}}, hergebruikt: {{reused}}, gefaalde categorieën: {{failed}}.", + "channelsFailed": "Kan geen logkanalen maken. Gefaalde categorieën: {{failed}}." + }, + "ui": { + "embed": { + "title": "Logconfiguratie", + "description": "Schakel Discord-events in of uit en routeer logs per categoriekanaal." + }, + "status": { + "enabled": "Ingeschakeld", + "disabled": "Uitgeschakeld" + }, + "pageLabel": "Pagina {{page}}/{{pageCount}}", + "enabledCountLabel": "Ingeschakelde events: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Events", + "channelLabel": "Kanaal", + "channelNotConfigured": "#niet-geconfigureerd", + "feedbackLabel": "Laatste actie", + "buttons": { + "enableAll": "✅ Alles inschakelen", + "disableAll": "❌ Alles uitschakelen", + "createChannels": "📁 Logkanalen maken", + "previousPage": "Vorige", + "nextPage": "Volgende", + "enableEvent": "Activeer {{event}}", + "disableEvent": "Deactiveer {{event}}" + }, + "events": { + "messageCreate": "Bericht aangemaakt", + "messageDelete": "Bericht verwijderd", + "messageUpdate": "Bericht bewerkt", + "messageBulkDelete": "Bulkverwijdering berichten", + "guildMemberAdd": "Lid toegetreden", + "guildMemberRemove": "Lid verlaten", + "guildMemberUpdate": "Lid bijgewerkt", + "interactionCreate": "Interactie", + "channelCreate": "Kanaal aangemaakt", + "channelDelete": "Kanaal verwijderd", + "channelUpdate": "Kanaal bijgewerkt", + "roleCreate": "Rol aangemaakt", + "roleDelete": "Rol verwijderd", + "roleUpdate": "Rol bijgewerkt", + "threadCreate": "Thread aangemaakt", + "threadDelete": "Thread verwijderd", + "threadUpdate": "Thread bijgewerkt", + "emojiCreate": "Emoji aangemaakt", + "emojiDelete": "Emoji verwijderd", + "emojiUpdate": "Emoji bijgewerkt", + "guildUpdate": "Server bijgewerkt", + "guildUnavailable": "Server niet beschikbaar", + "guildBanAdd": "Lid verbannen", + "guildBanRemove": "Verbanning opgeheven", + "inviteCreate": "Uitnodiging aangemaakt", + "inviteDelete": "Uitnodiging verwijderd" + } + } + }, + "welcome": { + "name": "welkom", + "description": "Configureer welkomstberichten met een interactief paneel.", + "examples": { + "slash": "Open het welkomstconfiguratiepaneel" + }, + "responses": { + "guildOnly": "Dit commando moet in een server worden gebruikt.", + "notOwner": "Alleen de auteur van het commando kan dit paneel gebruiken.", + "invalidSelection": "Ongeldige selectie.", + "testSuccess": "Testbericht verzonden naar {{channel}}.", + "testDisabled": "Welkomstmodule is uitgeschakeld.", + "testMissingChannel": "Geen kanaal geconfigureerd. Gebruik de knop Kanaal.", + "testChannelUnavailable": "Het geconfigureerde kanaal is niet beschikbaar of ongeldig.", + "testMissingPermissions": "De bot heeft geen toestemming om berichten te sturen in dit kanaal.", + "testFailed": "Kon testbericht niet verzenden. Probeer later opnieuw." + }, + "ui": { + "embed": { + "title": "Welcome-configuratie", + "description": "Stel status, kanaal, berichttype en test in.", + "fields": { + "status": "Status", + "channel": "Kanaal", + "type": "Type", + "channelPicker": "Kanaalkeuze", + "autoRoles": "Auto-rollen" + } + }, + "status": { + "enabled": "🟢 Ingeschakeld", + "disabled": "🔴 Uitgeschakeld" + }, + "channelNotConfigured": "#niet-geconfigureerd", + "autoRolesNotConfigured": "Geen auto-rollen geconfigureerd", + "channelPickerPlaceholder": "Kies een welkomstkanaal", + "channelPickerHint": "Kies een kanaal in het selectiemenu hieronder.", + "rolePickerPlaceholder": "Kies één of meerdere auto-rollen", + "rolePickerHint": "Kies rollen in het selectiemenu hieronder om ze toe te voegen.", + "buttons": { + "toggle": "Status schakelen", + "channel": "Kanaal instellen", + "channelCancel": "Kanaalkeuze sluiten", + "roles": "Auto-rollen toevoegen", + "rolesCancel": "Rollenkeuze sluiten", + "rolesClear": "Auto-rollen wissen", + "test": "Testen" + }, + "type": { + "placeholder": "Kies een berichttype", + "options": { + "simple": { + "label": "Eenvoudig bericht", + "description": "Verstuurt een standaard tekstbericht" + }, + "embed": { + "label": "Embed", + "description": "Verstuurt een welkomst-embed" + }, + "container": { + "label": "Container (V2)", + "description": "Verstuurt een Components V2 container" + }, + "image": { + "label": "Afbeelding", + "description": "Verstuurt een embed met afbeelding" + } + } + } + }, + "templates": { + "simple": "🎉 Welkom {{user}} op **{{guild}}**!", + "embedTitle": "Welkom!", + "embedDescription": "{{user}} is zojuist **{{guild}}** binnengekomen.", + "containerTitle": "Welkom", + "containerDescription": "{{user}} is zojuist **{{guild}}** binnengekomen.", + "imageTitle": "Welkom", + "imageDescription": "op de Discord-server {{guild}}" + } + }, + "goodbye": { + "name": "afscheid", + "description": "Configureer afscheid-berichten met een interactief paneel.", + "examples": { + "slash": "Open het afscheid-configuratiepaneel" + }, + "responses": { + "guildOnly": "Dit commando moet in een server worden gebruikt.", + "notOwner": "Alleen de auteur van het commando kan dit paneel gebruiken.", + "invalidSelection": "Ongeldige selectie.", + "testSuccess": "Testbericht verzonden naar {{channel}}.", + "testDisabled": "Afscheid-module is uitgeschakeld.", + "testMissingChannel": "Geen kanaal geconfigureerd. Gebruik de knop Kanaal.", + "testChannelUnavailable": "Het geconfigureerde kanaal is niet beschikbaar of ongeldig.", + "testMissingPermissions": "De bot heeft geen toestemming om berichten te sturen in dit kanaal.", + "testFailed": "Kon testbericht niet verzenden. Probeer later opnieuw." + }, + "ui": { + "embed": { + "title": "Goodbye-configuratie", + "description": "Stel status, kanaal, berichttype en test in.", + "fields": { + "status": "Status", + "channel": "Kanaal", + "type": "Type", + "channelPicker": "Kanaalkeuze" + } + }, + "status": { + "enabled": "🟢 Ingeschakeld", + "disabled": "🔴 Uitgeschakeld" + }, + "channelNotConfigured": "#niet-geconfigureerd", + "channelPickerPlaceholder": "Kies een afscheid-kanaal", + "channelPickerHint": "Kies een kanaal in het selectiemenu hieronder.", + "buttons": { + "toggle": "Status schakelen", + "channel": "Kanaal instellen", + "channelCancel": "Kanaalkeuze sluiten", + "test": "Testen" + }, + "type": { + "placeholder": "Kies een berichttype", + "options": { + "simple": { + "label": "Eenvoudig bericht", + "description": "Verstuurt een standaard tekstbericht" + }, + "embed": { + "label": "Embed", + "description": "Verstuurt een afscheid-embed" + }, + "container": { + "label": "Container (V2)", + "description": "Verstuurt een Components V2 container" + }, + "image": { + "label": "Afbeelding", + "description": "Verstuurt een embed met afbeelding" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} heeft **{{guild}}** verlaten.", + "embedTitle": "Afscheid", + "embedDescription": "{{user}} heeft **{{guild}}** verlaten.", + "containerTitle": "Afscheid", + "containerDescription": "{{user}} heeft **{{guild}}** verlaten.", + "imageTitle": "Afscheid", + "imageDescription": "van de Discord-server {{guild}}" + } + }, + "help": { + "name": "help", + "description": "Toon de lijst met commando’s of details van een commando.", + "args": { + "command": "Optionele commando-naam of trigger" + }, + "examples": { + "basic": "Toon alle commando’s", + "single": "Toon details van een commando" + }, + "errors": { + "notFound": "Geen commando gevonden voor {{query}}." + }, + "embed": { + "title": "Commando-hulp", + "description": "Gebruik {{usage}} om een commando te bekijken.", + "categoryEmpty": "Geen commando’s in deze categorie.", + "detailsTitle": "Commando: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Categorie: {{source}}", + "fields": { + "usage": "Gebruik", + "arguments": "Argumenten", + "examples": "Voorbeelden" + } + }, + "labels": { + "prefix": "Prefix", + "slash": "Slash", + "required": "verplicht", + "optional": "optioneel", + "noArgs": "Geen argumenten.", + "noExamples": "Geen voorbeelden." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(geen inhoud)", + "unknown": "onbekend", + "none": "geen" + }, + "embed": { + "title": "Event: {{event}}", + "detailsField": "Details" + }, + "summaries": { + "messageCreate": "Bericht verzonden door {{user}} in {{channel}}", + "messageDelete": "Bericht verwijderd in {{channel}}", + "messageUpdate": "Bericht bijgewerkt in {{channel}}", + "messageBulkDelete": "Bulkverwijdering: {{count}} berichten verwijderd", + "guildMemberAdd": "Lid toegetreden: {{user}}", + "guildMemberRemove": "Lid verlaten: {{user}}", + "guildMemberUpdate": "Lid bijgewerkt: {{user}}", + "interactionGeneric": "Interactie door {{user}}", + "interactionSlash": "Slash-commando /{{command}} door {{user}}", + "interactionButton": "Knopinteractie door {{user}}", + "interactionSelect": "Selectiemenu-interactie door {{user}}", + "interactionModal": "Formulier ingediend door {{user}}", + "channelCreate": "Kanaal aangemaakt: {{channel}}", + "channelDelete": "Kanaal verwijderd: {{channel}}", + "channelUpdate": "Kanaal bijgewerkt: {{channel}}", + "roleCreate": "Rol aangemaakt: {{role}}", + "roleDelete": "Rol verwijderd: {{role}}", + "roleUpdate": "Rol bijgewerkt: {{role}}", + "threadCreate": "Thread aangemaakt: {{thread}}", + "threadDelete": "Thread verwijderd: {{thread}}", + "threadUpdate": "Thread bijgewerkt: {{thread}}", + "emojiCreate": "Emoji aangemaakt: {{emoji}}", + "emojiDelete": "Emoji verwijderd: {{emoji}}", + "emojiUpdate": "Emoji bijgewerkt: {{emoji}}", + "guildUpdate": "Server bijgewerkt: {{guild}}", + "guildUnavailable": "Server tijdelijk niet beschikbaar: {{guild}}", + "guildBanAdd": "Gebruiker verbannen: {{user}}", + "guildBanRemove": "Verbanning opgeheven: {{user}}", + "inviteCreate": "Uitnodiging aangemaakt: {{code}}", + "inviteDelete": "Uitnodiging verwijderd: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/pl.json b/apps/bot/locales/pl.json new file mode 100644 index 0000000..e43fb63 --- /dev/null +++ b/apps/bot/locales/pl.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Brakujący argument {{arg}}. Użycie: {{usage}}", + "invalidInt": "Argument musi być liczbą całkowitą. Otrzymano: {{value}}", + "invalidNumber": "Argument musi być liczbą. Otrzymano: {{value}}", + "invalidBoolean": "Argument musi być wartością logiczną. Otrzymano: {{value}}", + "invalidUser": "Nie można znaleźć użytkownika z {{value}}", + "invalidChannel": "Nie można znaleźć kanału z {{value}}", + "invalidRole": "Nie można znaleźć roli z {{value}}", + "tooMany": "Zbyt wiele argumentów ({{count}}) wykryto: {{extras}}. Użycie: {{usage}}" + }, + "permissions": { + "user": "Nie masz uprawnień: {{permissions}}" + }, + "execution": "Wystąpił nieoczekiwany błąd podczas wykonywania.", + "cooldown": "Odczekaj {{seconds}}s przed ponownym użyciem tej komendy.", + "rateLimit": "Zbyt wiele żądań. Odczekaj {{seconds}}s przed ponowną próbą.", + "executionQueued": "Komenda zaakceptowana i dodana do kolejki." + }, + "categories": { + "fun": "Zabawne", + "utility": "Narzędzia", + "core": "Podstawowe" + }, + "commands": { + "kiss": { + "name": "pocałuj", + "description": "Wyślij buziaka użytkownikowi.", + "args": { + "user": "Użytkownik docelowy" + }, + "examples": { + "basic": "Wyślij buziaka do członka" + }, + "responses": { + "success": "{{from}} wysyła buziaka do {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Sprawdź opóźnienie websocket bota.", + "examples": { + "basic": "Sprawdzenie stanu" + }, + "responses": { + "pong": "Pong. Opóźnienie websocket: {{latency}}ms." + } + }, + "presence": { + "name": "status", + "description": "Konfiguruj status bota za pomocą interaktywnego panelu.", + "examples": { + "slash": "Otwórz panel statusu" + }, + "responses": { + "panel": "Panel statusu\nStatus: {{status}}\nAktywność: {{activityType}}\nRotacja: co {{rotationIntervalSeconds}}s\nAktywny tekst ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nPodgląd: {{activityPreview}}\nTeksty: {{activityTexts}}\nZmienne ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Tylko autor komendy może używać tego panelu.", + "invalidSelection": "Nieprawidłowy wybór.", + "invalidInterval": "Nieprawidłowy interwał. Użyj liczby między {{minSeconds}} a {{maxSeconds}} sekund.", + "modalTimeout": "Czas na edycję tekstu minął. Kliknij ponownie przycisk." + }, + "ui": { + "status": { + "placeholder": "Wybierz status", + "options": { + "online": { + "label": "Online", + "description": "Pokazuje bota jako online" + }, + "idle": { + "label": "Nieaktywny", + "description": "Pokazuje bota jako nieaktywny" + }, + "dnd": { + "label": "Nie przeszkadzać", + "description": "Pokazuje tryb Nie przeszkadzać" + }, + "invisible": { + "label": "Offline", + "description": "Pokazuje bota jako offline" + }, + "streaming": { + "label": "Streaming", + "description": "Pokazuje bota w trybie streamingu" + } + } + }, + "activity": { + "placeholder": "Wybierz typ aktywności", + "options": { + "PLAYING": { + "label": "Gra w", + "description": "Pokazuje Gra w ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Pokazuje Na żywo na ..." + }, + "WATCHING": { + "label": "Ogląda", + "description": "Pokazuje Ogląda ..." + }, + "LISTENING": { + "label": "Słucha", + "description": "Pokazuje Słucha ..." + }, + "COMPETING": { + "label": "Rywalizacja", + "description": "Pokazuje Rywalizacja ..." + }, + "CUSTOM": { + "label": "Niestandardowy status", + "description": "Pokazuje własny status" + } + } + }, + "embed": { + "title": "Konfiguracja statusu", + "description": "Zmień status, typ aktywności, teksty i rotację.", + "footer": "Panel interaktywny /presence", + "fields": { + "status": "Status", + "activity": "Typ aktywności", + "text": "Tekst" + } + }, + "textButton": "📝 Edytuj teksty", + "intervalButton": "⏱️ Interwał", + "modal": { + "title": "Teksty statusu", + "label": "Jeden tekst na linię (użyj {{var}})", + "placeholder": "Konserwacja\n{{guild_count}} serwerów | {{prefix}}pomoc" + }, + "intervalModal": { + "title": "Interwał rotacji", + "label": "Sekundy między tekstami", + "placeholder": "Przykład: 30" + } + } + }, + "logs": { + "name": "logi", + "description": "Konfiguruj logi zdarzeń Discorda za pomocą interaktywnego panelu.", + "examples": { + "slash": "Otwórz panel logów" + }, + "responses": { + "slashOnly": "Użyj /logs, aby otworzyć panel.", + "guildOnly": "Ta komenda działa tylko na serwerze.", + "notOwner": "Tylko autor komendy może używać tego panelu.", + "invalidSelection": "Nieprawidłowy wybór.", + "allEnabled": "Wszystkie logi zdarzeń zostały włączone.", + "allDisabled": "Wszystkie logi zdarzeń zostały wyłączone.", + "eventEnabled": "Zdarzenie {{event}} włączone.", + "eventDisabled": "Zdarzenie {{event}} wyłączone.", + "channelsCreated": "Kanały logów gotowe. Utworzone: {{created}}, ponownie użyte: {{reused}}.", + "channelsPartial": "Kanały logów częściowo gotowe. Utworzone: {{created}}, ponownie użyte: {{reused}}, błędy: {{failed}}.", + "channelsFailed": "Nie udało się utworzyć kanałów logów. Błędy: {{failed}}." + }, + "ui": { + "embed": { + "title": "Konfiguracja logów", + "description": "Włącz lub wyłącz zdarzenia Discord i przypisz je do kanałów." + }, + "status": { + "enabled": "Włączone", + "disabled": "Wyłączone" + }, + "pageLabel": "Strona {{page}}/{{pageCount}}", + "enabledCountLabel": "Aktywne: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Zdarzenia", + "channelLabel": "Kanał", + "channelNotConfigured": "#nie-skonfigurowano", + "feedbackLabel": "Ostatnia akcja", + "buttons": { + "enableAll": "✅ Włącz wszystkie logi", + "disableAll": "❌ Wyłącz wszystkie logi", + "createChannels": "📁 Utwórz kanały logów", + "previousPage": "Poprzednia", + "nextPage": "Następna", + "enableEvent": "Włącz {{event}}", + "disableEvent": "Wyłącz {{event}}" + }, + "events": { + "messageCreate": "Wiadomość utworzona", + "messageDelete": "Wiadomość usunięta", + "messageUpdate": "Wiadomość zaktualizowana", + "messageBulkDelete": "Masowe usunięcie wiadomości", + "guildMemberAdd": "Dołączenie członka", + "guildMemberRemove": "Odejście członka", + "guildMemberUpdate": "Aktualizacja członka", + "interactionCreate": "Interakcja", + "channelCreate": "Kanał utworzony", + "channelDelete": "Kanał usunięty", + "channelUpdate": "Kanał zaktualizowany", + "roleCreate": "Rola utworzona", + "roleDelete": "Rola usunięta", + "roleUpdate": "Rola zaktualizowana", + "threadCreate": "Wątek utworzony", + "threadDelete": "Wątek usunięty", + "threadUpdate": "Wątek zaktualizowany", + "emojiCreate": "Emoji utworzone", + "emojiDelete": "Emoji usunięte", + "emojiUpdate": "Emoji zaktualizowane", + "guildUpdate": "Serwer zaktualizowany", + "guildUnavailable": "Serwer niedostępny", + "guildBanAdd": "Użytkownik zbanowany", + "guildBanRemove": "Ban zdjęty", + "inviteCreate": "Zaproszenie utworzone", + "inviteDelete": "Zaproszenie usunięte" + } + } + }, + "welcome": { + "name": "powitanie", + "description": "Konfiguruj wiadomości powitalne za pomocą panelu.", + "examples": { + "slash": "Otwórz panel powitań" + }, + "responses": { + "guildOnly": "Ta komenda działa tylko na serwerze.", + "notOwner": "Tylko autor komendy może używać tego panelu.", + "invalidSelection": "Nieprawidłowy wybór.", + "testSuccess": "Wysłano wiadomość testową do {{channel}}.", + "testDisabled": "Moduł powitań jest wyłączony.", + "testMissingChannel": "Brak skonfigurowanego kanału.", + "testChannelUnavailable": "Kanał nie istnieje lub jest niedostępny.", + "testMissingPermissions": "Brak uprawnień do wysyłania wiadomości.", + "testFailed": "Nie udało się wysłać wiadomości testowej." + }, + "ui": { + "embed": { + "title": "Konfiguracja powitań", + "description": "Ustaw status, kanał, typ wiadomości i test.", + "fields": { + "status": "Status", + "channel": "Kanał", + "type": "Typ", + "channelPicker": "Wybór kanału", + "autoRoles": "Auto role" + } + }, + "status": { + "enabled": "🟢 Włączone", + "disabled": "🔴 Wyłączone" + }, + "channelNotConfigured": "#nie-skonfigurowano", + "autoRolesNotConfigured": "Brak auto ról", + "channelPickerPlaceholder": "Wybierz kanał powitalny", + "channelPickerHint": "Wybierz kanał z menu.", + "rolePickerPlaceholder": "Wybierz role automatyczne", + "rolePickerHint": "Wybierz role z menu.", + "buttons": { + "toggle": "Przełącz status", + "channel": "Ustaw kanał", + "channelCancel": "Zamknij wybór kanału", + "roles": "Dodaj role", + "rolesCancel": "Zamknij wybór ról", + "rolesClear": "Wyczyść role", + "test": "Test" + }, + "type": { + "placeholder": "Wybierz typ wiadomości", + "options": { + "simple": { + "label": "Prosta wiadomość", + "description": "Zwykła wiadomość tekstowa" + }, + "embed": { + "label": "Embed", + "description": "Wiadomość embed" + }, + "container": { + "label": "Kontener (V2)", + "description": "Components V2 container" + }, + "image": { + "label": "Obraz", + "description": "Embed z obrazem" + } + } + } + }, + "templates": { + "simple": "🎉 Witaj {{user}} na **{{guild}}**!", + "embedTitle": "Witaj!", + "embedDescription": "{{user}} dołączył do **{{guild}}**.", + "containerTitle": "Witaj", + "containerDescription": "{{user}} dołączył do **{{guild}}**.", + "imageTitle": "Witaj", + "imageDescription": "na serwerze Discord {{guild}}" + } + }, + "goodbye": { + "name": "pożegnanie", + "description": "Konfiguruj wiadomości pożegnalne za pomocą panelu.", + "examples": { + "slash": "Otwórz panel pożegnań" + }, + "responses": { + "guildOnly": "Ta komenda działa tylko na serwerze.", + "notOwner": "Tylko autor komendy może używać tego panelu.", + "invalidSelection": "Nieprawidłowy wybór.", + "testSuccess": "Wysłano wiadomość testową do {{channel}}.", + "testDisabled": "Moduł pożegnań jest wyłączony.", + "testMissingChannel": "Brak skonfigurowanego kanału.", + "testChannelUnavailable": "Kanał nie istnieje lub jest niedostępny.", + "testMissingPermissions": "Brak uprawnień do wysyłania wiadomości.", + "testFailed": "Nie udało się wysłać wiadomości testowej." + }, + "ui": { + "embed": { + "title": "Konfiguracja pożegnań", + "description": "Ustaw status, kanał, typ wiadomości i test.", + "fields": { + "status": "Status", + "channel": "Kanał", + "type": "Typ", + "channelPicker": "Wybór kanału" + } + }, + "status": { + "enabled": "🟢 Włączone", + "disabled": "🔴 Wyłączone" + }, + "channelNotConfigured": "#nie-skonfigurowano", + "channelPickerPlaceholder": "Wybierz kanał pożegnań", + "channelPickerHint": "Wybierz kanał z menu.", + "buttons": { + "toggle": "Przełącz status", + "channel": "Ustaw kanał", + "channelCancel": "Zamknij wybór kanału", + "test": "Test" + }, + "type": { + "placeholder": "Wybierz typ wiadomości", + "options": { + "simple": { + "label": "Prosta wiadomość", + "description": "Zwykła wiadomość tekstowa" + }, + "embed": { + "label": "Embed", + "description": "Wiadomość pożegnalna embed" + }, + "container": { + "label": "Kontener (V2)", + "description": "Components V2 container" + }, + "image": { + "label": "Obraz", + "description": "Embed z obrazem" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} opuścił **{{guild}}**.", + "embedTitle": "Do widzenia", + "embedDescription": "{{user}} opuścił **{{guild}}**.", + "containerTitle": "Do widzenia", + "containerDescription": "{{user}} opuścił **{{guild}}**.", + "imageTitle": "Do widzenia", + "imageDescription": "z serwera Discord {{guild}}" + } + }, + "help": { + "name": "pomoc", + "description": "Pokaż listę komend lub szczegóły komendy.", + "args": { + "command": "Opcjonalna nazwa komendy" + }, + "examples": { + "basic": "Pokaż wszystkie komendy", + "single": "Pokaż szczegóły komendy" + }, + "errors": { + "notFound": "Nie znaleziono komendy {{query}}." + }, + "embed": { + "title": "Pomoc - Komendy", + "description": "Użyj {{usage}} aby sprawdzić komendę.", + "categoryEmpty": "Brak komend w tej kategorii.", + "detailsTitle": "Komenda: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Kategoria: {{source}}", + "fields": { + "usage": "Użycie", + "arguments": "Argumenty", + "examples": "Przykłady" + } + }, + "labels": { + "prefix": "Prefix", + "slash": "Slash", + "required": "wymagane", + "optional": "opcjonalne", + "noArgs": "Brak argumentów.", + "noExamples": "Brak przykładów." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(brak treści)", + "unknown": "nieznane", + "none": "brak" + }, + "embed": { + "title": "Zdarzenie: {{event}}", + "detailsField": "Szczegóły" + }, + "summaries": { + "messageCreate": "Wiadomość wysłana przez {{user}} na {{channel}}", + "messageDelete": "Wiadomość usunięta na {{channel}}", + "messageUpdate": "Wiadomość zaktualizowana na {{channel}}", + "messageBulkDelete": "Usunięto {{count}} wiadomości", + "guildMemberAdd": "Dołączył użytkownik: {{user}}", + "guildMemberRemove": "Odszedł użytkownik: {{user}}", + "guildMemberUpdate": "Zaktualizowano użytkownika: {{user}}", + "interactionGeneric": "Interakcja od {{user}}", + "interactionSlash": "Komenda /{{command}} od {{user}}", + "interactionButton": "Kliknięto przycisk przez {{user}}", + "interactionSelect": "Wybór menu przez {{user}}", + "interactionModal": "Wysłano formularz przez {{user}}", + "channelCreate": "Utworzono kanał: {{channel}}", + "channelDelete": "Usunięto kanał: {{channel}}", + "channelUpdate": "Zaktualizowano kanał: {{channel}}", + "roleCreate": "Utworzono rolę: {{role}}", + "roleDelete": "Usunięto rolę: {{role}}", + "roleUpdate": "Zaktualizowano rolę: {{role}}", + "threadCreate": "Utworzono wątek: {{thread}}", + "threadDelete": "Usunięto wątek: {{thread}}", + "threadUpdate": "Zaktualizowano wątek: {{thread}}", + "emojiCreate": "Utworzono emoji: {{emoji}}", + "emojiDelete": "Usunięto emoji: {{emoji}}", + "emojiUpdate": "Zaktualizowano emoji: {{emoji}}", + "guildUpdate": "Zaktualizowano serwer: {{guild}}", + "guildUnavailable": "Serwer chwilowo niedostępny: {{guild}}", + "guildBanAdd": "Zbanowano użytkownika: {{user}}", + "guildBanRemove": "Odbanowano użytkownika: {{user}}", + "inviteCreate": "Utworzono zaproszenie: {{code}}", + "inviteDelete": "Usunięto zaproszenie: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "autor={{tag}} ({{id}})", + "content": "treść={{value}}", + "before": "przed={{value}}", + "after": "po={{value}}", + "channelId": "channelId={{value}}", + "count": "liczba={{value}}", + "user": "użytkownik={{tag}} ({{id}})", + "oldNick": "staryNick={{value}}", + "newNick": "nowyNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "typ={{value}}", + "oldName": "staraNazwa={{value}}", + "newName": "nowaNazwa={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "zapraszający={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/pt.json b/apps/bot/locales/pt.json new file mode 100644 index 0000000..2e38c53 --- /dev/null +++ b/apps/bot/locales/pt.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Argumento ausente {{arg}}. Uso: {{usage}}", + "invalidInt": "O argumento deve ser um inteiro. Recebido: {{value}}", + "invalidNumber": "O argumento deve ser um número. Recebido: {{value}}", + "invalidBoolean": "O argumento deve ser um booleano. Recebido: {{value}}", + "invalidUser": "Não foi possível encontrar um usuário a partir de {{value}}", + "invalidChannel": "Não foi possível encontrar um canal a partir de {{value}}", + "invalidRole": "Não foi possível encontrar um cargo a partir de {{value}}", + "tooMany": "Muitos argumentos ({{count}}) detectados: {{extras}}. Uso: {{usage}}" + }, + "permissions": { + "user": "Você não tem as permissões: {{permissions}}" + }, + "execution": "Ocorreu um erro inesperado durante a execução.", + "cooldown": "Aguarde {{seconds}}s antes de usar este comando novamente.", + "rateLimit": "Muitas requisições. Aguarde {{seconds}}s antes de tentar novamente.", + "executionQueued": "Comando aceito e colocado na fila de processamento." + }, + "categories": { + "fun": "Diversão", + "utility": "Utilitário", + "core": "Base" + }, + "commands": { + "kiss": { + "name": "beijo", + "description": "Enviar um beijo para um usuário.", + "args": { + "user": "Usuário alvo" + }, + "examples": { + "basic": "Enviar um beijo para um membro" + }, + "responses": { + "success": "{{from}} envia um beijo para {{to}}." + } + }, + "ping": { + "name": "ping", + "description": "Verificar a latência websocket do bot.", + "examples": { + "basic": "Verificação de saúde" + }, + "responses": { + "pong": "Pong. Latência websocket: {{latency}}ms." + } + }, + "presence": { + "name": "presença", + "description": "Configurar a presença do bot com um painel interativo.", + "examples": { + "slash": "Abrir o painel interativo de presença" + }, + "responses": { + "panel": "Painel de presença\nStatus: {{status}}\nAtividade: {{activityType}}\nRotação: a cada {{rotationIntervalSeconds}}s\nTexto ativo ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nPrévia renderizada: {{activityPreview}}\nTextos: {{activityTexts}}\nVariáveis ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Apenas o autor do comando pode usar este painel.", + "invalidSelection": "Seleção inválida.", + "invalidInterval": "Intervalo inválido. Use um inteiro entre {{minSeconds}} e {{maxSeconds}} segundos.", + "modalTimeout": "Tempo expirado para edição do texto. Clique no botão novamente." + }, + "ui": { + "status": { + "placeholder": "Escolher um status", + "options": { + "online": { + "label": "Online", + "description": "Exibe o bot online" + }, + "idle": { + "label": "Ausente", + "description": "Exibe o bot ausente" + }, + "dnd": { + "label": "Não perturbar", + "description": "Exibe o bot em não perturbar" + }, + "invisible": { + "label": "Offline", + "description": "Exibe o bot offline" + }, + "streaming": { + "label": "Transmitindo", + "description": "Exibe o bot em modo streaming" + } + } + }, + "activity": { + "placeholder": "Escolher um tipo de atividade", + "options": { + "PLAYING": { + "label": "Jogando", + "description": "Exibe Jogando ..." + }, + "STREAMING": { + "label": "Streaming", + "description": "Exibe Ao vivo em ..." + }, + "WATCHING": { + "label": "Assistindo", + "description": "Exibe Assistindo ..." + }, + "LISTENING": { + "label": "Ouvindo", + "description": "Exibe Ouvindo ..." + }, + "COMPETING": { + "label": "Competindo", + "description": "Exibe Competindo ..." + }, + "CUSTOM": { + "label": "Status personalizado", + "description": "Exibe um status personalizado" + } + } + }, + "embed": { + "title": "Configuração de presença", + "description": "Altere status, tipo de atividade, textos e rotação.", + "footer": "Painel interativo /presence", + "fields": { + "status": "Status", + "activity": "Tipo de atividade", + "text": "Texto" + } + }, + "textButton": "📝 Editar textos", + "intervalButton": "⏱️ Intervalo", + "modal": { + "title": "Textos de presença", + "label": "Um texto por linha (use {{var}})", + "placeholder": "Manutenção\n{{guild_count}} servidores | {{prefix}}ajuda" + }, + "intervalModal": { + "title": "Intervalo de rotação", + "label": "Segundos entre cada texto", + "placeholder": "Exemplo: 30" + } + } + }, + "logs": { + "name": "logs", + "description": "Configurar logs de eventos do Discord com um painel interativo.", + "examples": { + "slash": "Abrir o painel interativo de logs" + }, + "responses": { + "slashOnly": "Use /logs para abrir este painel interativo.", + "guildOnly": "Este comando deve ser usado em um servidor.", + "notOwner": "Apenas o autor do comando pode usar este painel.", + "invalidSelection": "Seleção inválida.", + "allEnabled": "Todos os logs de eventos estão ativados.", + "allDisabled": "Todos os logs de eventos estão desativados.", + "eventEnabled": "Evento {{event}} ativado.", + "eventDisabled": "Evento {{event}} desativado.", + "channelsCreated": "Canais de logs prontos. Criados: {{created}}, reutilizados: {{reused}}.", + "channelsPartial": "Canais de logs parcialmente prontos. Criados: {{created}}, reutilizados: {{reused}}, categorias com falha: {{failed}}.", + "channelsFailed": "Não foi possível criar os canais de logs. Categorias com falha: {{failed}}." + }, + "ui": { + "embed": { + "title": "Configuração de logs", + "description": "Ative ou desative eventos do Discord e direcione logs por canais de categoria." + }, + "status": { + "enabled": "Ativo", + "disabled": "Desativado" + }, + "pageLabel": "Página {{page}}/{{pageCount}}", + "enabledCountLabel": "Eventos ativos: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Eventos", + "channelLabel": "Canal", + "channelNotConfigured": "#não-configurado", + "feedbackLabel": "Última ação", + "buttons": { + "enableAll": "✅ Ativar todos os logs", + "disableAll": "❌ Desativar todos os logs", + "createChannels": "📁 Criar canais de logs", + "previousPage": "Anterior", + "nextPage": "Próximo", + "enableEvent": "Ativar {{event}}", + "disableEvent": "Desativar {{event}}" + }, + "events": { + "messageCreate": "Mensagem criada", + "messageDelete": "Mensagem excluída", + "messageUpdate": "Mensagem atualizada", + "messageBulkDelete": "Exclusão em massa de mensagens", + "guildMemberAdd": "Membro entrou", + "guildMemberRemove": "Membro saiu", + "guildMemberUpdate": "Membro atualizado", + "interactionCreate": "Interação", + "channelCreate": "Canal criado", + "channelDelete": "Canal excluído", + "channelUpdate": "Canal atualizado", + "roleCreate": "Cargo criado", + "roleDelete": "Cargo excluído", + "roleUpdate": "Cargo atualizado", + "threadCreate": "Thread criada", + "threadDelete": "Thread excluída", + "threadUpdate": "Thread atualizada", + "emojiCreate": "Emoji criado", + "emojiDelete": "Emoji excluído", + "emojiUpdate": "Emoji atualizado", + "guildUpdate": "Servidor atualizado", + "guildUnavailable": "Servidor indisponível", + "guildBanAdd": "Membro banido", + "guildBanRemove": "Banimento removido", + "inviteCreate": "Convite criado", + "inviteDelete": "Convite excluído" + } + } + }, + "welcome": { + "name": "boas-vindas", + "description": "Configurar mensagens de boas-vindas com painel interativo.", + "examples": { + "slash": "Abrir o painel de configuração de boas-vindas" + }, + "responses": { + "guildOnly": "Este comando deve ser usado em um servidor.", + "notOwner": "Apenas o autor do comando pode usar este painel.", + "invalidSelection": "Seleção inválida.", + "testSuccess": "Mensagem de teste enviada em {{channel}}.", + "testDisabled": "O módulo de boas-vindas está desativado.", + "testMissingChannel": "Nenhum canal configurado. Use o botão Canal.", + "testChannelUnavailable": "O canal configurado não foi encontrado ou não é compatível.", + "testMissingPermissions": "O bot não tem permissão para enviar mensagens neste canal.", + "testFailed": "Não foi possível enviar a mensagem de teste. Tente novamente mais tarde." + }, + "ui": { + "embed": { + "title": "Configuração Welcome", + "description": "Configure status, canal, tipo de mensagem e teste.", + "fields": { + "status": "Status", + "channel": "Canal", + "type": "Tipo", + "channelPicker": "Seleção de canal", + "autoRoles": "Cargos automáticos" + } + }, + "status": { + "enabled": "🟢 Ativo", + "disabled": "🔴 Desativado" + }, + "channelNotConfigured": "#não-configurado", + "autoRolesNotConfigured": "Nenhum cargo automático configurado", + "channelPickerPlaceholder": "Selecione um canal de boas-vindas", + "channelPickerHint": "Escolha o canal no menu abaixo.", + "rolePickerPlaceholder": "Selecione um ou mais cargos automáticos", + "rolePickerHint": "Escolha os cargos no menu abaixo para adicioná-los automaticamente.", + "buttons": { + "toggle": "Alternar status", + "channel": "Configurar canal", + "channelCancel": "Fechar seleção de canal", + "roles": "Adicionar cargos automáticos", + "rolesCancel": "Fechar seleção de cargos", + "rolesClear": "Limpar cargos automáticos", + "test": "Testar" + }, + "type": { + "placeholder": "Escolher tipo de mensagem", + "options": { + "simple": { + "label": "Mensagem simples", + "description": "Envia uma mensagem de texto" + }, + "embed": { + "label": "Embed", + "description": "Envia um embed de boas-vindas" + }, + "container": { + "label": "Container (V2)", + "description": "Envia um container Components V2" + }, + "image": { + "label": "Imagem", + "description": "Envia um embed com imagem" + } + } + } + }, + "templates": { + "simple": "🎉 Bem-vindo {{user}} ao **{{guild}}**!", + "embedTitle": "Bem-vindo!", + "embedDescription": "{{user}} acabou de entrar no **{{guild}}**.", + "containerTitle": "Bem-vindo", + "containerDescription": "{{user}} acabou de entrar no **{{guild}}**.", + "imageTitle": "Bem-vindo", + "imageDescription": "no servidor Discord {{guild}}" + } + }, + "goodbye": { + "name": "adeus", + "description": "Configurar mensagens de despedida com painel interativo.", + "examples": { + "slash": "Abrir o painel de configuração de despedidas" + }, + "responses": { + "guildOnly": "Este comando deve ser usado em um servidor.", + "notOwner": "Apenas o autor do comando pode usar este painel.", + "invalidSelection": "Seleção inválida.", + "testSuccess": "Mensagem de teste enviada em {{channel}}.", + "testDisabled": "O módulo de despedida está desativado.", + "testMissingChannel": "Nenhum canal configurado. Use o botão Canal.", + "testChannelUnavailable": "O canal configurado não foi encontrado ou não é compatível.", + "testMissingPermissions": "O bot não tem permissão para enviar mensagens neste canal.", + "testFailed": "Não foi possível enviar a mensagem de teste. Tente novamente mais tarde." + }, + "ui": { + "embed": { + "title": "Configuração Goodbye", + "description": "Configure status, canal, tipo de mensagem e teste.", + "fields": { + "status": "Status", + "channel": "Canal", + "type": "Tipo", + "channelPicker": "Seleção de canal" + } + }, + "status": { + "enabled": "🟢 Ativo", + "disabled": "🔴 Desativado" + }, + "channelNotConfigured": "#não-configurado", + "channelPickerPlaceholder": "Selecione um canal de despedida", + "channelPickerHint": "Escolha o canal no menu abaixo.", + "buttons": { + "toggle": "Alternar status", + "channel": "Configurar canal", + "channelCancel": "Fechar seleção de canal", + "test": "Testar" + }, + "type": { + "placeholder": "Escolher tipo de mensagem", + "options": { + "simple": { + "label": "Mensagem simples", + "description": "Envia uma mensagem de texto" + }, + "embed": { + "label": "Embed", + "description": "Envia um embed de despedida" + }, + "container": { + "label": "Container (V2)", + "description": "Envia um container Components V2" + }, + "image": { + "label": "Imagem", + "description": "Envia um embed com imagem" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} saiu de **{{guild}}**.", + "embedTitle": "Adeus", + "embedDescription": "{{user}} saiu de **{{guild}}**.", + "containerTitle": "Adeus", + "containerDescription": "{{user}} saiu de **{{guild}}**.", + "imageTitle": "Adeus", + "imageDescription": "do servidor Discord {{guild}}" + } + }, + "help": { + "name": "ajuda", + "description": "Mostrar lista de comandos ou detalhes de um comando.", + "args": { + "command": "Nome ou comando opcional" + }, + "examples": { + "basic": "Mostrar todos os comandos", + "single": "Mostrar detalhes de um comando" + }, + "errors": { + "notFound": "Nenhum comando corresponde a {{query}}." + }, + "embed": { + "title": "Ajuda de Comandos", + "description": "Use {{usage}} para inspecionar um comando.", + "categoryEmpty": "Nenhum comando nesta categoria.", + "detailsTitle": "Comando: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Categoria: {{source}}", + "fields": { + "usage": "Uso", + "arguments": "Argumentos", + "examples": "Exemplos" + } + }, + "labels": { + "prefix": "Prefixo", + "slash": "Slash", + "required": "obrigatório", + "optional": "opcional", + "noArgs": "Sem argumentos.", + "noExamples": "Sem exemplos." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(sem conteúdo)", + "unknown": "desconhecido", + "none": "nenhum" + }, + "embed": { + "title": "Evento: {{event}}", + "detailsField": "Detalhes" + }, + "summaries": { + "messageCreate": "Mensagem enviada por {{user}} em {{channel}}", + "messageDelete": "Mensagem excluída em {{channel}}", + "messageUpdate": "Mensagem atualizada em {{channel}}", + "messageBulkDelete": "Exclusão em massa: {{count}} mensagens removidas", + "guildMemberAdd": "Membro entrou: {{user}}", + "guildMemberRemove": "Membro saiu: {{user}}", + "guildMemberUpdate": "Membro atualizado: {{user}}", + "interactionGeneric": "Interação de {{user}}", + "interactionSlash": "Comando /{{command}} por {{user}}", + "interactionButton": "Interação de botão por {{user}}", + "interactionSelect": "Interação de menu por {{user}}", + "interactionModal": "Envio de modal por {{user}}", + "channelCreate": "Canal criado: {{channel}}", + "channelDelete": "Canal excluído: {{channel}}", + "channelUpdate": "Canal atualizado: {{channel}}", + "roleCreate": "Cargo criado: {{role}}", + "roleDelete": "Cargo excluído: {{role}}", + "roleUpdate": "Cargo atualizado: {{role}}", + "threadCreate": "Thread criada: {{thread}}", + "threadDelete": "Thread excluída: {{thread}}", + "threadUpdate": "Thread atualizada: {{thread}}", + "emojiCreate": "Emoji criado: {{emoji}}", + "emojiDelete": "Emoji excluído: {{emoji}}", + "emojiUpdate": "Emoji atualizado: {{emoji}}", + "guildUpdate": "Servidor atualizado: {{guild}}", + "guildUnavailable": "Servidor temporariamente indisponível: {{guild}}", + "guildBanAdd": "Usuário banido: {{user}}", + "guildBanRemove": "Banimento removido: {{user}}", + "inviteCreate": "Convite criado: {{code}}", + "inviteDelete": "Convite excluído: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "autor={{tag}} ({{id}})", + "content": "conteúdo={{value}}", + "before": "antes={{value}}", + "after": "depois={{value}}", + "channelId": "channelId={{value}}", + "count": "quantidade={{value}}", + "user": "usuário={{tag}} ({{id}})", + "oldNick": "apelidoAntigo={{value}}", + "newNick": "apelidoNovo={{value}}", + "interactionId": "interactionId={{value}}", + "type": "tipo={{value}}", + "oldName": "nomeAntigo={{value}}", + "newName": "nomeNovo={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "convidador={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/ru.json b/apps/bot/locales/ru.json new file mode 100644 index 0000000..78aad79 --- /dev/null +++ b/apps/bot/locales/ru.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Отсутствует аргумент {{arg}}. Использование: {{usage}}", + "invalidInt": "Аргумент должен быть целым числом. Получено: {{value}}", + "invalidNumber": "Аргумент должен быть числом. Получено: {{value}}", + "invalidBoolean": "Аргумент должен быть логическим значением. Получено: {{value}}", + "invalidUser": "Не удалось найти пользователя по {{value}}", + "invalidChannel": "Не удалось найти канал по {{value}}", + "invalidRole": "Не удалось найти роль по {{value}}", + "tooMany": "Слишком много аргументов ({{count}}): {{extras}}. Использование: {{usage}}" + }, + "permissions": { + "user": "У вас нет разрешений: {{permissions}}" + }, + "execution": "Во время выполнения произошла непредвиденная ошибка.", + "cooldown": "Подождите {{seconds}}с перед повторным использованием команды.", + "rateLimit": "Слишком много запросов. Подождите {{seconds}}с перед повторной попыткой.", + "executionQueued": "Команда принята и поставлена в очередь выполнения." + }, + "categories": { + "fun": "Развлечения", + "utility": "Утилиты", + "core": "Основные" + }, + "commands": { + "kiss": { + "name": "поцелуй", + "description": "Отправить поцелуй пользователю.", + "args": { + "user": "Целевой пользователь" + }, + "examples": { + "basic": "Отправить поцелуй участнику" + }, + "responses": { + "success": "{{from}} отправляет поцелуй {{to}}." + } + }, + "ping": { + "name": "пинг", + "description": "Проверить задержку вебсокета бота.", + "examples": { + "basic": "Проверка работоспособности" + }, + "responses": { + "pong": "Понг. Задержка вебсокета: {{latency}}мс." + } + }, + "presence": { + "name": "статус", + "description": "Настроить статус бота с помощью интерактивной панели.", + "examples": { + "slash": "Открыть интерактивную панель статуса" + }, + "responses": { + "panel": "Панель статуса\nСтатус: {{status}}\nАктивность: {{activityType}}\nРотация: каждые {{rotationIntervalSeconds}}с\nАктивный текст ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nПредпросмотр: {{activityPreview}}\nТексты: {{activityTexts}}\nПеременные ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Только автор команды может использовать эту панель.", + "invalidSelection": "Неверный выбор.", + "invalidInterval": "Неверный интервал. Используйте целое число от {{minSeconds}} до {{maxSeconds}} секунд.", + "modalTimeout": "Время на редактирование текста истекло. Нажмите кнопку снова." + }, + "ui": { + "status": { + "placeholder": "Выберите статус", + "options": { + "online": { + "label": "В сети", + "description": "Показывает бота как онлайн" + }, + "idle": { + "label": "Неактивен", + "description": "Показывает бота неактивным" + }, + "dnd": { + "label": "Не беспокоить", + "description": "Показывает режим не беспокоить" + }, + "invisible": { + "label": "Невидимый", + "description": "Показывает бота оффлайн" + }, + "streaming": { + "label": "Стриминг", + "description": "Показывает режим трансляции" + } + } + }, + "activity": { + "placeholder": "Выберите тип активности", + "options": { + "PLAYING": { + "label": "Играет в", + "description": "Показывает «Играет в ...»" + }, + "STREAMING": { + "label": "Стриминг", + "description": "Показывает «В прямом эфире ...»" + }, + "WATCHING": { + "label": "Смотрит", + "description": "Показывает «Смотрит ...»" + }, + "LISTENING": { + "label": "Слушает", + "description": "Показывает «Слушает ...»" + }, + "COMPETING": { + "label": "Соревнование", + "description": "Показывает «Соревнование ...»" + }, + "CUSTOM": { + "label": "Пользовательский статус", + "description": "Показывает кастомный статус" + } + } + }, + "embed": { + "title": "Настройка статуса", + "description": "Изменяйте статус, тип активности, тексты и их ротацию.", + "footer": "Интерактивная панель /presence", + "fields": { + "status": "Статус", + "activity": "Тип активности", + "text": "Текст" + } + }, + "textButton": "📝 Изменить тексты", + "intervalButton": "⏱️ Интервал", + "modal": { + "title": "Тексты статуса", + "label": "По одной строке на текст (используйте {{var}})", + "placeholder": "Обслуживание\n{{guild_count}} серверов | {{prefix}}help" + }, + "intervalModal": { + "title": "Интервал ротации", + "label": "Секунды между текстами", + "placeholder": "Пример: 30" + } + } + }, + "logs": { + "name": "логи", + "description": "Настроить логи событий Discord через интерактивную панель.", + "examples": { + "slash": "Открыть панель логов" + }, + "responses": { + "slashOnly": "Используйте /logs чтобы открыть эту панель.", + "guildOnly": "Эта команда должна использоваться на сервере.", + "notOwner": "Только автор команды может использовать эту панель.", + "invalidSelection": "Неверный выбор.", + "allEnabled": "Все логи событий включены.", + "allDisabled": "Все логи событий отключены.", + "eventEnabled": "Событие {{event}} включено.", + "eventDisabled": "Событие {{event}} отключено.", + "channelsCreated": "Каналы логов готовы. Создано: {{created}}, переиспользовано: {{reused}}.", + "channelsPartial": "Частично готовы каналы логов. Создано: {{created}}, переиспользовано: {{reused}}, категории с ошибкой: {{failed}}.", + "channelsFailed": "Не удалось создать каналы логов. Категории с ошибкой: {{failed}}." + }, + "ui": { + "embed": { + "title": "Настройка логов", + "description": "Включайте или отключайте события Discord и распределяйте логи по каналам." + }, + "status": { + "enabled": "Активно", + "disabled": "Отключено" + }, + "pageLabel": "Страница {{page}}/{{pageCount}}", + "enabledCountLabel": "Активные события: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "События", + "channelLabel": "Канал", + "channelNotConfigured": "#не-настроен", + "feedbackLabel": "Последнее действие", + "buttons": { + "enableAll": "✅ Включить все логи", + "disableAll": "❌ Отключить все логи", + "createChannels": "📁 Создать каналы логов", + "previousPage": "Назад", + "nextPage": "Вперед", + "enableEvent": "Включить {{event}}", + "disableEvent": "Отключить {{event}}" + }, + "events": { + "messageCreate": "Сообщение создано", + "messageDelete": "Сообщение удалено", + "messageUpdate": "Сообщение обновлено", + "messageBulkDelete": "Массовое удаление сообщений", + "guildMemberAdd": "Участник присоединился", + "guildMemberRemove": "Участник покинул сервер", + "guildMemberUpdate": "Участник обновлён", + "interactionCreate": "Взаимодействие", + "channelCreate": "Канал создан", + "channelDelete": "Канал удалён", + "channelUpdate": "Канал обновлён", + "roleCreate": "Роль создана", + "roleDelete": "Роль удалена", + "roleUpdate": "Роль обновлена", + "threadCreate": "Тред создан", + "threadDelete": "Тред удалён", + "threadUpdate": "Тред обновлён", + "emojiCreate": "Эмодзи создано", + "emojiDelete": "Эмодзи удалено", + "emojiUpdate": "Эмодзи обновлено", + "guildUpdate": "Сервер обновлён", + "guildUnavailable": "Сервер недоступен", + "guildBanAdd": "Участник забанен", + "guildBanRemove": "Участник разбанен", + "inviteCreate": "Приглашение создано", + "inviteDelete": "Приглашение удалено" + } + } + }, + "welcome": { + "name": "приветствие", + "description": "Настроить сообщения приветствия через интерактивную панель.", + "examples": { + "slash": "Открыть панель настроек приветствий" + }, + "responses": { + "guildOnly": "Эта команда должна использоваться на сервере.", + "notOwner": "Только автор команды может использовать эту панель.", + "invalidSelection": "Неверный выбор.", + "testSuccess": "Тестовое сообщение отправлено в {{channel}}.", + "testDisabled": "Модуль приветствий отключён.", + "testMissingChannel": "Канал не настроен. Используйте кнопку Канал.", + "testChannelUnavailable": "Канал не найден или недоступен.", + "testMissingPermissions": "У бота нет прав на отправку сообщений в этот канал.", + "testFailed": "Не удалось отправить тестовое сообщение. Попробуйте позже." + }, + "ui": { + "embed": { + "title": "Настройка приветствий", + "description": "Настройка статуса, канала, типа сообщения и тестирования.", + "fields": { + "status": "Статус", + "channel": "Канал", + "type": "Тип", + "channelPicker": "Выбор канала", + "autoRoles": "Авто-роли" + } + }, + "status": { + "enabled": "🟢 Включено", + "disabled": "🔴 Отключено" + }, + "channelNotConfigured": "#не-настроен", + "autoRolesNotConfigured": "Авто-роли не настроены", + "channelPickerPlaceholder": "Выберите канал приветствия", + "channelPickerHint": "Выберите канал в меню ниже.", + "rolePickerPlaceholder": "Выберите роли", + "rolePickerHint": "Выберите роли для авто-добавления.", + "buttons": { + "toggle": "Переключить статус", + "channel": "Настроить канал", + "channelCancel": "Закрыть выбор канала", + "roles": "Добавить авто-роли", + "rolesCancel": "Закрыть выбор ролей", + "rolesClear": "Очистить авто-роли", + "test": "Тест" + }, + "type": { + "placeholder": "Выберите тип сообщения", + "options": { + "simple": { + "label": "Простое сообщение", + "description": "Обычное текстовое сообщение" + }, + "embed": { + "label": "Embed", + "description": "Embed приветствие" + }, + "container": { + "label": "Контейнер (V2)", + "description": "Components V2 контейнер" + }, + "image": { + "label": "Изображение", + "description": "Embed с изображением" + } + } + } + }, + "templates": { + "simple": "🎉 Добро пожаловать {{user}} на **{{guild}}**!", + "embedTitle": "Добро пожаловать!", + "embedDescription": "{{user}} присоединился к **{{guild}}**.", + "containerTitle": "Добро пожаловать", + "containerDescription": "{{user}} присоединился к **{{guild}}**.", + "imageTitle": "Добро пожаловать", + "imageDescription": "на сервер Discord {{guild}}" + } + }, + "goodbye": { + "name": "прощание", + "description": "Настроить сообщения прощания через интерактивную панель.", + "examples": { + "slash": "Открыть панель настроек прощаний" + }, + "responses": { + "guildOnly": "Эта команда должна использоваться на сервере.", + "notOwner": "Только автор команды может использовать эту панель.", + "invalidSelection": "Неверный выбор.", + "testSuccess": "Тестовое сообщение отправлено в {{channel}}.", + "testDisabled": "Модуль прощаний отключён.", + "testMissingChannel": "Канал не настроен. Используйте кнопку Канал.", + "testChannelUnavailable": "Канал не найден или недоступен.", + "testMissingPermissions": "У бота нет прав на отправку сообщений в этот канал.", + "testFailed": "Не удалось отправить тестовое сообщение. Попробуйте позже." + }, + "ui": { + "embed": { + "title": "Настройка прощаний", + "description": "Настройка статуса, канала, типа сообщения и тестирования.", + "fields": { + "status": "Статус", + "channel": "Канал", + "type": "Тип", + "channelPicker": "Выбор канала" + } + }, + "status": { + "enabled": "🟢 Включено", + "disabled": "🔴 Отключено" + }, + "channelNotConfigured": "#не-настроен", + "channelPickerPlaceholder": "Выберите канал прощаний", + "channelPickerHint": "Выберите канал в меню ниже.", + "buttons": { + "toggle": "Переключить статус", + "channel": "Настроить канал", + "channelCancel": "Закрыть выбор канала", + "test": "Тест" + }, + "type": { + "placeholder": "Выберите тип сообщения", + "options": { + "simple": { + "label": "Простое сообщение", + "description": "Обычное текстовое сообщение" + }, + "embed": { + "label": "Embed", + "description": "Embed прощания" + }, + "container": { + "label": "Контейнер (V2)", + "description": "Components V2 контейнер" + }, + "image": { + "label": "Изображение", + "description": "Embed с изображением" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} покинул **{{guild}}**.", + "embedTitle": "Прощание", + "embedDescription": "{{user}} покинул **{{guild}}**.", + "containerTitle": "Прощание", + "containerDescription": "{{user}} покинул **{{guild}}**.", + "imageTitle": "Прощание", + "imageDescription": "с сервера Discord {{guild}}" + } + }, + "help": { + "name": "помощь", + "description": "Показать список команд или информацию о команде.", + "args": { + "command": "Необязательное имя команды" + }, + "examples": { + "basic": "Показать все команды", + "single": "Показать информацию о команде" + }, + "errors": { + "notFound": "Команда {{query}} не найдена." + }, + "embed": { + "title": "Справка по командам", + "description": "Используйте {{usage}} для просмотра команды.", + "categoryEmpty": "В этой категории нет команд.", + "detailsTitle": "Команда: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Категория: {{source}}", + "fields": { + "usage": "Использование", + "arguments": "Аргументы", + "examples": "Примеры" + } + }, + "labels": { + "prefix": "Префикс", + "slash": "Слэш", + "required": "обязательно", + "optional": "опционально", + "noArgs": "Нет аргументов.", + "noExamples": "Нет примеров." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(нет содержимого)", + "unknown": "неизвестно", + "none": "нет" + }, + "embed": { + "title": "Событие: {{event}}", + "detailsField": "Детали" + }, + "summaries": { + "messageCreate": "Сообщение отправлено {{user}} в {{channel}}", + "messageDelete": "Сообщение удалено в {{channel}}", + "messageUpdate": "Сообщение обновлено в {{channel}}", + "messageBulkDelete": "Массовое удаление: {{count}} сообщений удалено", + "guildMemberAdd": "Участник присоединился: {{user}}", + "guildMemberRemove": "Участник покинул: {{user}}", + "guildMemberUpdate": "Участник обновлён: {{user}}", + "interactionGeneric": "Взаимодействие от {{user}}", + "interactionSlash": "Слэш-команда /{{command}} от {{user}}", + "interactionButton": "Нажатие кнопки от {{user}}", + "interactionSelect": "Выбор в меню от {{user}}", + "interactionModal": "Отправка формы от {{user}}", + "channelCreate": "Канал создан: {{channel}}", + "channelDelete": "Канал удалён: {{channel}}", + "channelUpdate": "Канал обновлён: {{channel}}", + "roleCreate": "Роль создана: {{role}}", + "roleDelete": "Роль удалена: {{role}}", + "roleUpdate": "Роль обновлена: {{role}}", + "threadCreate": "Тред создан: {{thread}}", + "threadDelete": "Тред удалён: {{thread}}", + "threadUpdate": "Тред обновлён: {{thread}}", + "emojiCreate": "Эмодзи создано: {{emoji}}", + "emojiDelete": "Эмодзи удалено: {{emoji}}", + "emojiUpdate": "Эмодзи обновлено: {{emoji}}", + "guildUpdate": "Сервер обновлён: {{guild}}", + "guildUnavailable": "Сервер временно недоступен: {{guild}}", + "guildBanAdd": "Пользователь забанен: {{user}}", + "guildBanRemove": "Пользователь разбанен: {{user}}", + "inviteCreate": "Приглашение создано: {{code}}", + "inviteDelete": "Приглашение удалено: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/tr.json b/apps/bot/locales/tr.json new file mode 100644 index 0000000..065419c --- /dev/null +++ b/apps/bot/locales/tr.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "Eksik argüman {{arg}}. Kullanım: {{usage}}", + "invalidInt": "Argüman bir tam sayı olmalı. Alınan: {{value}}", + "invalidNumber": "Argüman bir sayı olmalı. Alınan: {{value}}", + "invalidBoolean": "Argüman bir boolean olmalı. Alınan: {{value}}", + "invalidUser": "{{value}} üzerinden kullanıcı bulunamadı", + "invalidChannel": "{{value}} üzerinden kanal bulunamadı", + "invalidRole": "{{value}} üzerinden rol bulunamadı", + "tooMany": "Çok fazla argüman ({{count}}) tespit edildi: {{extras}}. Kullanım: {{usage}}" + }, + "permissions": { + "user": "Bu izinlere sahip değilsin: {{permissions}}" + }, + "execution": "Komut çalıştırılırken beklenmeyen bir hata oluştu.", + "cooldown": "Bu komutu tekrar kullanmak için {{seconds}} saniye bekle.", + "rateLimit": "Çok fazla istek. Tekrar denemeden önce {{seconds}} saniye bekle.", + "executionQueued": "Komut kabul edildi ve sıraya alındı." + }, + "categories": { + "fun": "Eğlence", + "utility": "Araçlar", + "core": "Temel" + }, + "commands": { + "kiss": { + "name": "öpücük", + "description": "Bir kullanıcıya öpücük gönder.", + "args": { + "user": "Hedef kullanıcı" + }, + "examples": { + "basic": "Bir üyeye öpücük gönder" + }, + "responses": { + "success": "{{from}} {{to}} kişisine bir öpücük gönderdi." + } + }, + "ping": { + "name": "ping", + "description": "Botun websocket gecikmesini kontrol et.", + "examples": { + "basic": "Sağlık kontrolü" + }, + "responses": { + "pong": "Pong. Websocket gecikmesi: {{latency}}ms." + } + }, + "presence": { + "name": "durum", + "description": "Botun durumunu etkileşimli panel ile yapılandır.", + "examples": { + "slash": "Durum panelini aç" + }, + "responses": { + "panel": "Durum paneli\nDurum: {{status}}\nAktivite: {{activityType}}\nDöngü: her {{rotationIntervalSeconds}} saniye\nAktif metin ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nÖnizleme: {{activityPreview}}\nMetinler: {{activityTexts}}\nDeğişkenler ({{doubleBracesHint}}): {{variables}}", + "notOwner": "Bu paneli sadece komutu başlatan kişi kullanabilir.", + "invalidSelection": "Geçersiz seçim.", + "invalidInterval": "Geçersiz aralık. {{minSeconds}} ile {{maxSeconds}} saniye arasında bir tam sayı kullan.", + "modalTimeout": "Metin düzenleme süresi doldu. Tekrar tıkla." + }, + "ui": { + "status": { + "placeholder": "Durum seç", + "options": { + "online": { + "label": "Çevrimiçi", + "description": "Botu çevrimiçi gösterir" + }, + "idle": { + "label": "Boşta", + "description": "Botu boşta gösterir" + }, + "dnd": { + "label": "Rahatsız etmeyin", + "description": "Botu rahatsız etmeyin modunda gösterir" + }, + "invisible": { + "label": "Görünmez", + "description": "Botu çevrimdışı gösterir" + }, + "streaming": { + "label": "Yayın", + "description": "Botu yayın yapıyor olarak gösterir" + } + } + }, + "activity": { + "placeholder": "Aktivite türü seç", + "options": { + "PLAYING": { + "label": "Oynuyor", + "description": "Oynuyor ..." + }, + "STREAMING": { + "label": "Yayın", + "description": "Canlı yayın ..." + }, + "WATCHING": { + "label": "İzliyor", + "description": "İzliyor ..." + }, + "LISTENING": { + "label": "Dinliyor", + "description": "Dinliyor ..." + }, + "COMPETING": { + "label": "Yarışıyor", + "description": "Yarışıyor ..." + }, + "CUSTOM": { + "label": "Özel durum", + "description": "Özel durum gösterir" + } + } + }, + "embed": { + "title": "Durum yapılandırması", + "description": "Durum, aktivite türü ve metinleri düzenle.", + "footer": "Panel /presence", + "fields": { + "status": "Durum", + "activity": "Aktivite türü", + "text": "Metin" + } + }, + "textButton": "📝 Metinleri düzenle", + "intervalButton": "⏱️ Aralık", + "modal": { + "title": "Durum metinleri", + "label": "Her satıra bir metin ({{var}} kullan)", + "placeholder": "Bakım\n{{guild_count}} sunucu | {{prefix}}yardım" + }, + "intervalModal": { + "title": "Döngü aralığı", + "label": "Metinler arası saniye", + "placeholder": "Örnek: 30" + } + } + }, + "logs": { + "name": "loglar", + "description": "Discord olay loglarını etkileşimli panel ile yapılandır.", + "examples": { + "slash": "Log panelini aç" + }, + "responses": { + "slashOnly": "Bu paneli açmak için /logs kullan.", + "guildOnly": "Bu komut sadece bir sunucuda kullanılabilir.", + "notOwner": "Bu paneli sadece komutu başlatan kişi kullanabilir.", + "invalidSelection": "Geçersiz seçim.", + "allEnabled": "Tüm olay logları etkinleştirildi.", + "allDisabled": "Tüm olay logları devre dışı bırakıldı.", + "eventEnabled": "{{event}} etkinleştirildi.", + "eventDisabled": "{{event}} devre dışı bırakıldı.", + "channelsCreated": "Log kanalları hazır. Oluşturulan: {{created}}, yeniden kullanılan: {{reused}}.", + "channelsPartial": "Log kanalları kısmen hazır. Oluşturulan: {{created}}, yeniden kullanılan: {{reused}}, hatalı kategoriler: {{failed}}.", + "channelsFailed": "Log kanalları oluşturulamadı. Hatalı kategoriler: {{failed}}." + }, + "ui": { + "embed": { + "title": "Log yapılandırması", + "description": "Discord olaylarını aç/kapat ve kanallara yönlendir." + }, + "status": { + "enabled": "Aktif", + "disabled": "Pasif" + }, + "pageLabel": "Sayfa {{page}}/{{pageCount}}", + "enabledCountLabel": "Aktif olaylar: {{enabledCount}}/{{totalCount}}", + "eventsHeader": "Olaylar", + "channelLabel": "Kanal", + "channelNotConfigured": "#yapılandırılmadı", + "feedbackLabel": "Son işlem", + "buttons": { + "enableAll": "✅ Tüm logları aç", + "disableAll": "❌ Tüm logları kapat", + "createChannels": "📁 Log kanallarını oluştur", + "previousPage": "Önceki", + "nextPage": "Sonraki", + "enableEvent": "{{event}} etkinleştir", + "disableEvent": "{{event}} devre dışı bırak" + }, + "events": { + "messageCreate": "Mesaj oluşturuldu", + "messageDelete": "Mesaj silindi", + "messageUpdate": "Mesaj güncellendi", + "messageBulkDelete": "Toplu mesaj silme", + "guildMemberAdd": "Üye katıldı", + "guildMemberRemove": "Üye ayrıldı", + "guildMemberUpdate": "Üye güncellendi", + "interactionCreate": "Etkileşim", + "channelCreate": "Kanal oluşturuldu", + "channelDelete": "Kanal silindi", + "channelUpdate": "Kanal güncellendi", + "roleCreate": "Rol oluşturuldu", + "roleDelete": "Rol silindi", + "roleUpdate": "Rol güncellendi", + "threadCreate": "Konu oluşturuldu", + "threadDelete": "Konu silindi", + "threadUpdate": "Konu güncellendi", + "emojiCreate": "Emoji oluşturuldu", + "emojiDelete": "Emoji silindi", + "emojiUpdate": "Emoji güncellendi", + "guildUpdate": "Sunucu güncellendi", + "guildUnavailable": "Sunucu kullanılamıyor", + "guildBanAdd": "Üye yasaklandı", + "guildBanRemove": "Yasak kaldırıldı", + "inviteCreate": "Davet oluşturuldu", + "inviteDelete": "Davet silindi" + } + } + }, + "welcome": { + "name": "karşılama", + "description": "Hoş geldin mesajlarını etkileşimli panel ile yapılandır.", + "examples": { + "slash": "Karşılama panelini aç" + }, + "responses": { + "guildOnly": "Bu komut sadece bir sunucuda kullanılabilir.", + "notOwner": "Bu paneli sadece komutu başlatan kişi kullanabilir.", + "invalidSelection": "Geçersiz seçim.", + "testSuccess": "Test mesajı {{channel}} kanalına gönderildi.", + "testDisabled": "Karşılama sistemi devre dışı.", + "testMissingChannel": "Kanal yapılandırılmamış. Kanal butonunu kullan.", + "testChannelUnavailable": "Kanal bulunamadı veya kullanılamıyor.", + "testMissingPermissions": "Botun bu kanala mesaj gönderme izni yok.", + "testFailed": "Test mesajı gönderilemedi. Daha sonra tekrar dene." + }, + "ui": { + "embed": { + "title": "Karşılama yapılandırması", + "description": "Durum, kanal ve mesaj türünü ayarla.", + "fields": { + "status": "Durum", + "channel": "Kanal", + "type": "Tür", + "channelPicker": "Kanal seçimi", + "autoRoles": "Otomatik roller" + } + }, + "status": { + "enabled": "🟢 Aktif", + "disabled": "🔴 Pasif" + }, + "channelNotConfigured": "#yapılandırılmadı", + "autoRolesNotConfigured": "Otomatik rol yok", + "channelPickerPlaceholder": "Karşılama kanalı seç", + "channelPickerHint": "Aşağıdan kanal seç.", + "rolePickerPlaceholder": "Otomatik roller seç", + "rolePickerHint": "Listeye eklemek için roller seç.", + "buttons": { + "toggle": "Durum değiştir", + "channel": "Kanal ayarla", + "channelCancel": "Kanal seçimini kapat", + "roles": "Rol ekle", + "rolesCancel": "Rol seçimini kapat", + "rolesClear": "Rolleri temizle", + "test": "Test et" + }, + "type": { + "placeholder": "Mesaj türü seç", + "options": { + "simple": { + "label": "Basit mesaj", + "description": "Normal metin mesajı gönderir" + }, + "embed": { + "label": "Embed", + "description": "Embed karşılama mesajı" + }, + "container": { + "label": "Container (V2)", + "description": "Components V2 container gönderir" + }, + "image": { + "label": "Görsel", + "description": "Görselli embed gönderir" + } + } + } + }, + "templates": { + "simple": "🎉 {{user}} **{{guild}}** sunucusuna hoş geldin!", + "embedTitle": "Hoş geldin!", + "embedDescription": "{{user}} **{{guild}}** sunucusuna katıldı.", + "containerTitle": "Hoş geldin", + "containerDescription": "{{user}} **{{guild}}** sunucusuna katıldı.", + "imageTitle": "Hoş geldin", + "imageDescription": "{{guild}} Discord sunucusuna hoş geldin" + } + }, + "goodbye": { + "name": "veda", + "description": "Ayrılma mesajlarını etkileşimli panel ile yapılandır.", + "examples": { + "slash": "Veda panelini aç" + }, + "responses": { + "guildOnly": "Bu komut sadece bir sunucuda kullanılabilir.", + "notOwner": "Bu paneli sadece komutu başlatan kişi kullanabilir.", + "invalidSelection": "Geçersiz seçim.", + "testSuccess": "Test mesajı {{channel}} kanalına gönderildi.", + "testDisabled": "Veda sistemi devre dışı.", + "testMissingChannel": "Kanal yapılandırılmamış. Kanal butonunu kullan.", + "testChannelUnavailable": "Kanal bulunamadı veya kullanılamıyor.", + "testMissingPermissions": "Botun bu kanala mesaj gönderme izni yok.", + "testFailed": "Test mesajı gönderilemedi. Daha sonra tekrar dene." + }, + "ui": { + "embed": { + "title": "Veda yapılandırması", + "description": "Durum, kanal ve mesaj türünü ayarla.", + "fields": { + "status": "Durum", + "channel": "Kanal", + "type": "Tür", + "channelPicker": "Kanal seçimi" + } + }, + "status": { + "enabled": "🟢 Aktif", + "disabled": "🔴 Pasif" + }, + "channelNotConfigured": "#yapılandırılmadı", + "channelPickerPlaceholder": "Veda kanalı seç", + "channelPickerHint": "Aşağıdan kanal seç.", + "buttons": { + "toggle": "Durum değiştir", + "channel": "Kanal ayarla", + "channelCancel": "Kanal seçimini kapat", + "test": "Test et" + }, + "type": { + "placeholder": "Mesaj türü seç", + "options": { + "simple": { + "label": "Basit mesaj", + "description": "Normal metin mesajı gönderir" + }, + "embed": { + "label": "Embed", + "description": "Veda embed mesajı" + }, + "container": { + "label": "Container (V2)", + "description": "Components V2 container gönderir" + }, + "image": { + "label": "Görsel", + "description": "Görselli embed gönderir" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} **{{guild}}** sunucusundan ayrıldı.", + "embedTitle": "Güle güle", + "embedDescription": "{{user}} **{{guild}}** sunucusundan ayrıldı.", + "containerTitle": "Güle güle", + "containerDescription": "{{user}} **{{guild}}** sunucusundan ayrıldı.", + "imageTitle": "Güle güle", + "imageDescription": "{{guild}} Discord sunucusundan ayrıldı" + } + }, + "help": { + "name": "yardım", + "description": "Komut listesini veya detaylarını göster.", + "args": { + "command": "İsteğe bağlı komut adı" + }, + "examples": { + "basic": "Tüm komutları göster", + "single": "Komut detaylarını göster" + }, + "errors": { + "notFound": "{{query}} ile eşleşen komut bulunamadı." + }, + "embed": { + "title": "Komut Yardımı", + "description": "{{usage}} kullanarak bir komutu incele.", + "categoryEmpty": "Bu kategoride komut yok.", + "detailsTitle": "Komut: {{name}}", + "detailsDescription": "{{description}}", + "footer": "Kategori: {{source}}", + "fields": { + "usage": "Kullanım", + "arguments": "Argümanlar", + "examples": "Örnekler" + } + }, + "labels": { + "prefix": "Prefix", + "slash": "Slash", + "required": "zorunlu", + "optional": "isteğe bağlı", + "noArgs": "Argüman yok.", + "noExamples": "Örnek yok." + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(içerik yok)", + "unknown": "bilinmiyor", + "none": "yok" + }, + "embed": { + "title": "Olay: {{event}}", + "detailsField": "Detaylar" + }, + "summaries": { + "messageCreate": "Mesaj gönderildi: {{user}} - {{channel}}", + "messageDelete": "Mesaj silindi: {{channel}}", + "messageUpdate": "Mesaj güncellendi: {{channel}}", + "messageBulkDelete": "{{count}} mesaj silindi", + "guildMemberAdd": "Üye katıldı: {{user}}", + "guildMemberRemove": "Üye ayrıldı: {{user}}", + "guildMemberUpdate": "Üye güncellendi: {{user}}", + "interactionGeneric": "Etkileşim: {{user}}", + "interactionSlash": "/{{command}} komutu: {{user}}", + "interactionButton": "Buton etkileşimi: {{user}}", + "interactionSelect": "Seçim menüsü: {{user}}", + "interactionModal": "Form gönderimi: {{user}}", + "channelCreate": "Kanal oluşturuldu: {{channel}}", + "channelDelete": "Kanal silindi: {{channel}}", + "channelUpdate": "Kanal güncellendi: {{channel}}", + "roleCreate": "Rol oluşturuldu: {{role}}", + "roleDelete": "Rol silindi: {{role}}", + "roleUpdate": "Rol güncellendi: {{role}}", + "threadCreate": "Konu oluşturuldu: {{thread}}", + "threadDelete": "Konu silindi: {{thread}}", + "threadUpdate": "Konu güncellendi: {{thread}}", + "emojiCreate": "Emoji oluşturuldu: {{emoji}}", + "emojiDelete": "Emoji silindi: {{emoji}}", + "emojiUpdate": "Emoji güncellendi: {{emoji}}", + "guildUpdate": "Sunucu güncellendi: {{guild}}", + "guildUnavailable": "Sunucu geçici olarak kullanılamıyor: {{guild}}", + "guildBanAdd": "Kullanıcı yasaklandı: {{user}}", + "guildBanRemove": "Yasak kaldırıldı: {{user}}", + "inviteCreate": "Davet oluşturuldu: {{code}}", + "inviteDelete": "Davet silindi: {{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/locales/zh.json b/apps/bot/locales/zh.json new file mode 100644 index 0000000..0fa2734 --- /dev/null +++ b/apps/bot/locales/zh.json @@ -0,0 +1,468 @@ +{ + "errors": { + "args": { + "missing": "缺少参数 {{arg}}。用法:{{usage}}", + "invalidInt": "参数必须是整数。收到:{{value}}", + "invalidNumber": "参数必须是数字。收到:{{value}}", + "invalidBoolean": "参数必须是布尔值。收到:{{value}}", + "invalidUser": "无法根据 {{value}} 找到用户", + "invalidChannel": "无法根据 {{value}} 找到频道", + "invalidRole": "无法根据 {{value}} 找到角色", + "tooMany": "检测到过多参数({{count}}):{{extras}}。用法:{{usage}}" + }, + "permissions": { + "user": "你没有权限:{{permissions}}" + }, + "execution": "执行过程中发生未知错误。", + "cooldown": "请等待 {{seconds}} 秒后再使用该命令。", + "rateLimit": "请求过于频繁。请等待 {{seconds}} 秒后重试。", + "executionQueued": "命令已接受并加入队列处理。" + }, + "categories": { + "fun": "娱乐", + "utility": "工具", + "core": "基础" + }, + "commands": { + "kiss": { + "name": "亲吻", + "description": "向用户发送一个亲吻。", + "args": { + "user": "目标用户" + }, + "examples": { + "basic": "向一名成员发送亲吻" + }, + "responses": { + "success": "{{from}} 向 {{to}} 发送了一个亲吻。" + } + }, + "ping": { + "name": "ping", + "description": "检查机器人的 WebSocket 延迟。", + "examples": { + "basic": "健康检查" + }, + "responses": { + "pong": "Pong。WebSocket 延迟:{{latency}}ms。" + } + }, + "presence": { + "name": "状态", + "description": "使用交互面板配置机器人的状态。", + "examples": { + "slash": "打开状态交互面板" + }, + "responses": { + "panel": "状态面板\n状态:{{status}}\n活动:{{activityType}}\n轮换:每 {{rotationIntervalSeconds}} 秒\n当前文本({{currentTextIndex}}/{{textCount}}):{{activityText}}\n预览:{{activityPreview}}\n文本列表:{{activityTexts}}\n变量({{doubleBracesHint}}):{{variables}}", + "notOwner": "只有命令作者可以使用此面板。", + "invalidSelection": "无效选择。", + "invalidInterval": "无效间隔。请使用 {{minSeconds}} 到 {{maxSeconds}} 秒之间的整数。", + "modalTimeout": "文本编辑超时。请重新点击按钮。" + }, + "ui": { + "status": { + "placeholder": "选择状态", + "options": { + "online": { + "label": "在线", + "description": "显示机器人在线" + }, + "idle": { + "label": "离开", + "description": "显示机器人空闲" + }, + "dnd": { + "label": "请勿打扰", + "description": "显示机器人请勿打扰" + }, + "invisible": { + "label": "隐身", + "description": "显示机器人离线" + }, + "streaming": { + "label": "直播", + "description": "显示机器人正在直播" + } + } + }, + "activity": { + "placeholder": "选择活动类型", + "options": { + "PLAYING": { + "label": "正在玩", + "description": "显示 正在玩..." + }, + "STREAMING": { + "label": "直播", + "description": "显示 正在直播..." + }, + "WATCHING": { + "label": "正在观看", + "description": "显示 正在观看..." + }, + "LISTENING": { + "label": "正在听", + "description": "显示 正在听..." + }, + "COMPETING": { + "label": "比赛中", + "description": "显示 比赛中..." + }, + "CUSTOM": { + "label": "自定义状态", + "description": "显示自定义状态" + } + } + }, + "embed": { + "title": "状态配置", + "description": "修改状态、活动类型、多条文本及其轮换。", + "footer": "交互面板 /presence", + "fields": { + "status": "状态", + "activity": "活动类型", + "text": "文本" + } + }, + "textButton": "📝 修改文本", + "intervalButton": "⏱️ 间隔", + "modal": { + "title": "状态文本", + "label": "每行一个文本(使用 {{var}})", + "placeholder": "维护中\n{{guild_count}} 个服务器 | {{prefix}}帮助" + }, + "intervalModal": { + "title": "轮换间隔", + "label": "每个文本之间的秒数", + "placeholder": "例如:30" + } + } + }, + "logs": { + "name": "日志", + "description": "使用交互面板配置 Discord 事件日志。", + "examples": { + "slash": "打开日志交互面板" + }, + "responses": { + "slashOnly": "使用 /logs 打开此交互面板。", + "guildOnly": "此命令只能在服务器中使用。", + "notOwner": "只有命令作者可以使用此面板。", + "invalidSelection": "无效选择。", + "allEnabled": "所有事件日志已启用。", + "allDisabled": "所有事件日志已禁用。", + "eventEnabled": "事件 {{event}} 已启用。", + "eventDisabled": "事件 {{event}} 已禁用。", + "channelsCreated": "日志频道已准备好。新建:{{created}},复用:{{reused}}。", + "channelsPartial": "日志频道部分完成。新建:{{created}},复用:{{reused}},失败分类:{{failed}}。", + "channelsFailed": "无法创建日志频道。失败分类:{{failed}}。" + }, + "ui": { + "embed": { + "title": "日志配置", + "description": "启用或禁用 Discord 事件,并按分类路由日志频道。" + }, + "status": { + "enabled": "已启用", + "disabled": "已禁用" + }, + "pageLabel": "第 {{page}}/{{pageCount}} 页", + "enabledCountLabel": "已启用事件:{{enabledCount}}/{{totalCount}}", + "eventsHeader": "事件", + "channelLabel": "频道", + "channelNotConfigured": "#未配置", + "feedbackLabel": "最近操作", + "buttons": { + "enableAll": "✅ 启用所有日志", + "disableAll": "❌ 禁用所有日志", + "createChannels": "📁 创建日志频道", + "previousPage": "上一页", + "nextPage": "下一页", + "enableEvent": "启用 {{event}}", + "disableEvent": "禁用 {{event}}" + }, + "events": { + "messageCreate": "消息创建", + "messageDelete": "消息删除", + "messageUpdate": "消息更新", + "messageBulkDelete": "批量删除消息", + "guildMemberAdd": "成员加入", + "guildMemberRemove": "成员离开", + "guildMemberUpdate": "成员更新", + "interactionCreate": "交互", + "channelCreate": "频道创建", + "channelDelete": "频道删除", + "channelUpdate": "频道更新", + "roleCreate": "角色创建", + "roleDelete": "角色删除", + "roleUpdate": "角色更新", + "threadCreate": "线程创建", + "threadDelete": "线程删除", + "threadUpdate": "线程更新", + "emojiCreate": "表情创建", + "emojiDelete": "表情删除", + "emojiUpdate": "表情更新", + "guildUpdate": "服务器更新", + "guildUnavailable": "服务器不可用", + "guildBanAdd": "成员被封禁", + "guildBanRemove": "成员解封", + "inviteCreate": "邀请创建", + "inviteDelete": "邀请删除" + } + } + }, + "welcome": { + "name": "欢迎", + "description": "使用交互面板配置欢迎消息。", + "examples": { + "slash": "打开欢迎消息配置面板" + }, + "responses": { + "guildOnly": "此命令只能在服务器中使用。", + "notOwner": "只有命令作者可以使用此面板。", + "invalidSelection": "无效选择。", + "testSuccess": "测试消息已发送到 {{channel}}。", + "testDisabled": "欢迎模块已禁用。", + "testMissingChannel": "未配置频道。请使用频道按钮。", + "testChannelUnavailable": "已配置频道不可用或不存在。", + "testMissingPermissions": "机器人没有权限在该频道发送消息。", + "testFailed": "无法发送测试消息,请稍后重试。" + }, + "ui": { + "embed": { + "title": "欢迎配置", + "description": "设置状态、频道、消息类型并进行测试。", + "fields": { + "status": "状态", + "channel": "频道", + "type": "类型", + "channelPicker": "频道选择", + "autoRoles": "自动角色" + } + }, + "status": { + "enabled": "🟢 已启用", + "disabled": "🔴 已禁用" + }, + "channelNotConfigured": "#未配置", + "autoRolesNotConfigured": "未配置自动角色", + "channelPickerPlaceholder": "选择欢迎频道", + "channelPickerHint": "在下方选择菜单中选择频道。", + "rolePickerPlaceholder": "选择一个或多个自动角色", + "rolePickerHint": "在下方选择角色以加入自动列表。", + "buttons": { + "toggle": "切换状态", + "channel": "配置频道", + "channelCancel": "关闭频道选择", + "roles": "添加自动角色", + "rolesCancel": "关闭角色选择", + "rolesClear": "清空自动角色", + "test": "测试" + }, + "type": { + "placeholder": "选择消息类型", + "options": { + "simple": { + "label": "简单消息", + "description": "发送普通文本消息" + }, + "embed": { + "label": "嵌入消息", + "description": "发送欢迎嵌入消息" + }, + "container": { + "label": "容器 (V2)", + "description": "发送 Components V2 容器" + }, + "image": { + "label": "图片", + "description": "发送带图片的嵌入消息" + } + } + } + }, + "templates": { + "simple": "🎉 欢迎 {{user}} 来到 **{{guild}}**!", + "embedTitle": "欢迎!", + "embedDescription": "{{user}} 刚刚加入了 **{{guild}}**。", + "containerTitle": "欢迎", + "containerDescription": "{{user}} 刚刚加入了 **{{guild}}**。", + "imageTitle": "欢迎", + "imageDescription": "加入 Discord 服务器 {{guild}}" + } + }, + "goodbye": { + "name": "再见", + "description": "使用交互面板配置离开消息。", + "examples": { + "slash": "打开离开消息配置面板" + }, + "responses": { + "guildOnly": "此命令只能在服务器中使用。", + "notOwner": "只有命令作者可以使用此面板。", + "invalidSelection": "无效选择。", + "testSuccess": "测试消息已发送到 {{channel}}。", + "testDisabled": "离开模块已禁用。", + "testMissingChannel": "未配置频道。请使用频道按钮。", + "testChannelUnavailable": "已配置频道不可用或不存在。", + "testMissingPermissions": "机器人没有权限在该频道发送消息。", + "testFailed": "无法发送测试消息,请稍后重试。" + }, + "ui": { + "embed": { + "title": "离开配置", + "description": "设置状态、频道、消息类型并进行测试。", + "fields": { + "status": "状态", + "channel": "频道", + "type": "类型", + "channelPicker": "频道选择" + } + }, + "status": { + "enabled": "🟢 已启用", + "disabled": "🔴 已禁用" + }, + "channelNotConfigured": "#未配置", + "channelPickerPlaceholder": "选择离开频道", + "channelPickerHint": "在下方选择菜单中选择频道。", + "buttons": { + "toggle": "切换状态", + "channel": "配置频道", + "channelCancel": "关闭频道选择", + "test": "测试" + }, + "type": { + "placeholder": "选择消息类型", + "options": { + "simple": { + "label": "简单消息", + "description": "发送普通文本消息" + }, + "embed": { + "label": "嵌入消息", + "description": "发送离开嵌入消息" + }, + "container": { + "label": "容器 (V2)", + "description": "发送 Components V2 容器" + }, + "image": { + "label": "图片", + "description": "发送带图片的嵌入消息" + } + } + } + }, + "templates": { + "simple": "👋 {{user}} 离开了 **{{guild}}**。", + "embedTitle": "再见", + "embedDescription": "{{user}} 离开了 **{{guild}}**。", + "containerTitle": "再见", + "containerDescription": "{{user}} 离开了 **{{guild}}**。", + "imageTitle": "再见", + "imageDescription": "离开 Discord 服务器 {{guild}}" + } + }, + "help": { + "name": "帮助", + "description": "显示命令列表或某个命令的详细信息。", + "args": { + "command": "可选的命令名称或触发词" + }, + "examples": { + "basic": "显示所有命令", + "single": "显示某个命令的详细信息" + }, + "errors": { + "notFound": "没有找到匹配 {{query}} 的命令。" + }, + "embed": { + "title": "命令帮助", + "description": "使用 {{usage}} 查看某个命令。", + "categoryEmpty": "该分类中没有命令。", + "detailsTitle": "命令:{{name}}", + "detailsDescription": "{{description}}", + "footer": "分类:{{source}}", + "fields": { + "usage": "用法", + "arguments": "参数", + "examples": "示例" + } + }, + "labels": { + "prefix": "前缀", + "slash": "斜杠", + "required": "必填", + "optional": "可选", + "noArgs": "无参数。", + "noExamples": "无示例。" + } + } + }, + "logsRuntime": { + "placeholders": { + "noContent": "(无内容)", + "unknown": "未知", + "none": "无" + }, + "embed": { + "title": "事件:{{event}}", + "detailsField": "详情" + }, + "summaries": { + "messageCreate": "{{user}} 在 {{channel}} 发送了消息", + "messageDelete": "{{channel}} 中的消息已删除", + "messageUpdate": "{{channel}} 中的消息已更新", + "messageBulkDelete": "批量删除:{{count}} 条消息被移除", + "guildMemberAdd": "成员加入:{{user}}", + "guildMemberRemove": "成员离开:{{user}}", + "guildMemberUpdate": "成员更新:{{user}}", + "interactionGeneric": "{{user}} 进行了交互", + "interactionSlash": "{{user}} 使用了斜杠命令 /{{command}}", + "interactionButton": "{{user}} 点击了按钮", + "interactionSelect": "{{user}} 使用了选择菜单", + "interactionModal": "{{user}} 提交了表单", + "channelCreate": "频道创建:{{channel}}", + "channelDelete": "频道删除:{{channel}}", + "channelUpdate": "频道更新:{{channel}}", + "roleCreate": "角色创建:{{role}}", + "roleDelete": "角色删除:{{role}}", + "roleUpdate": "角色更新:{{role}}", + "threadCreate": "线程创建:{{thread}}", + "threadDelete": "线程删除:{{thread}}", + "threadUpdate": "线程更新:{{thread}}", + "emojiCreate": "表情创建:{{emoji}}", + "emojiDelete": "表情删除:{{emoji}}", + "emojiUpdate": "表情更新:{{emoji}}", + "guildUpdate": "服务器更新:{{guild}}", + "guildUnavailable": "服务器暂时不可用:{{guild}}", + "guildBanAdd": "用户被封禁:{{user}}", + "guildBanRemove": "用户解除封禁:{{user}}", + "inviteCreate": "邀请创建:{{code}}", + "inviteDelete": "邀请删除:{{code}}" + }, + "details": { + "messageId": "messageId={{value}}", + "author": "author={{tag}} ({{id}})", + "content": "content={{value}}", + "before": "before={{value}}", + "after": "after={{value}}", + "channelId": "channelId={{value}}", + "count": "count={{value}}", + "user": "user={{tag}} ({{id}})", + "oldNick": "oldNick={{value}}", + "newNick": "newNick={{value}}", + "interactionId": "interactionId={{value}}", + "type": "type={{value}}", + "oldName": "oldName={{value}}", + "newName": "newName={{value}}", + "roleId": "roleId={{value}}", + "threadId": "threadId={{value}}", + "emojiId": "emojiId={{value}}", + "guildId": "guildId={{value}}", + "inviter": "inviter={{value}}" + } + } +} \ No newline at end of file diff --git a/apps/bot/scripts/copyLegacyLocales.mjs b/apps/bot/scripts/copyLegacyLocales.mjs index a773bfb..b4a56b0 100644 --- a/apps/bot/scripts/copyLegacyLocales.mjs +++ b/apps/bot/scripts/copyLegacyLocales.mjs @@ -4,19 +4,26 @@ import { fileURLToPath } from "node:url"; const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url)); const BOT_DIR = path.resolve(SCRIPT_DIR, ".."); -const SOURCE_DIR = path.join(BOT_DIR, "src", "legacy", "i18n"); -const TARGET_DIR = path.join(BOT_DIR, "dist", "legacy", "i18n"); +const SOURCE_DIR = path.join(BOT_DIR, "locales"); +const TARGET_DIRECTORIES = [ + path.join(BOT_DIR, "dist", "locales"), + path.join(BOT_DIR, "dist", "legacy", "i18n"), +]; if (!existsSync(SOURCE_DIR)) { throw new Error(`[i18n] source locale directory not found: ${SOURCE_DIR}`); } -mkdirSync(TARGET_DIR, { recursive: true }); +for (const targetDirectory of TARGET_DIRECTORIES) { + mkdirSync(targetDirectory, { recursive: true }); +} for (const fileName of readdirSync(SOURCE_DIR)) { if (!fileName.endsWith(".json")) { continue; } - copyFileSync(path.join(SOURCE_DIR, fileName), path.join(TARGET_DIR, fileName)); + for (const targetDirectory of TARGET_DIRECTORIES) { + copyFileSync(path.join(SOURCE_DIR, fileName), path.join(targetDirectory, fileName)); + } } \ No newline at end of file diff --git a/apps/bot/src/legacy/commands/ping.ts b/apps/bot/src/legacy/commands/ping.ts index dbc3134..e8f5d31 100644 --- a/apps/bot/src/legacy/commands/ping.ts +++ b/apps/bot/src/legacy/commands/ping.ts @@ -19,11 +19,8 @@ export const pingCommand = defineCommand({ }, ], execute: async (ctx) => { - const responses = (ctx.commandText.responses as Record | undefined) ?? {}; - const template = typeof responses.pong === "string" ? responses.pong : "Pong {{latency}}ms"; - await ctx.reply({ - content: ctx.format(template, { + content: ctx.ct("responses.pong", { latency: ctx.client.ws.ping, }), flags: [MessageFlags.Ephemeral], diff --git a/apps/bot/src/legacy/core/commands/slashBuilder.ts b/apps/bot/src/legacy/core/commands/slashBuilder.ts index 488247b..b94dfcd 100644 --- a/apps/bot/src/legacy/core/commands/slashBuilder.ts +++ b/apps/bot/src/legacy/core/commands/slashBuilder.ts @@ -4,23 +4,69 @@ import { type RESTPostAPIChatInputApplicationCommandsJSONBody, } from "discord.js"; -import type { BotCommand, CommandArgument, SupportedLang } from "../../types/command.js"; +import { SUPPORTED_LANGS, type BotCommand, type CommandArgument, type SupportedLang } from "../../types/command.js"; import type { I18nService } from "../../i18n/index.js"; -const LANG_TO_DISCORD_LOCALE: Record = { +const LANG_TO_DISCORD_LOCALE: Partial> = { en: "en-US", - fr: "fr", es: "es-ES", + de: "de", + ja: "ja", + fr: "fr", + pt: "pt-BR", + ru: "ru", + it: "it", + nl: "nl", + pl: "pl", + zh: "zh-CN", + hi: "hi", + id: "id", + tr: "tr", }; const toLocalizationMap = (source: Partial>): Record => { const entries = Object.entries(source) - .filter(([, value]) => Boolean(value)) - .map(([lang, value]) => [LANG_TO_DISCORD_LOCALE[lang as SupportedLang], value as string]); + .map(([lang, value]) => { + const discordLocale = LANG_TO_DISCORD_LOCALE[lang as SupportedLang]; + if (!discordLocale || !value) { + return null; + } + + return [discordLocale, value] as const; + }) + .filter((entry): entry is readonly [string, string] => entry !== null); return Object.fromEntries(entries); }; +const buildCommandNameLocalizationSource = (command: BotCommand, i18n: I18nService): Partial> => { + const localizations: Partial> = {}; + + for (const lang of SUPPORTED_LANGS) { + if (!LANG_TO_DISCORD_LOCALE[lang]) { + continue; + } + + localizations[lang] = i18n.commandName(lang, command.meta.name); + } + + return localizations; +}; + +const buildDescriptionLocalizationSource = (descriptionKey: string, i18n: I18nService): Partial> => { + const localizations: Partial> = {}; + + for (const lang of SUPPORTED_LANGS) { + if (!LANG_TO_DISCORD_LOCALE[lang]) { + continue; + } + + localizations[lang] = i18n.t(lang, descriptionKey); + } + + return localizations; +}; + const argDescriptionKey = (command: BotCommand, arg: CommandArgument): string => { return `commands.${command.meta.name}.${arg.descriptionKey}`; }; @@ -49,11 +95,7 @@ const applyOption = ( ): void => { const descriptionKey = argDescriptionKey(command, arg); const descriptionEn = i18n.t("en", descriptionKey); - const descriptionLocalizations = toLocalizationMap({ - en: i18n.t("en", descriptionKey), - fr: i18n.t("fr", descriptionKey), - es: i18n.t("es", descriptionKey), - }); + const descriptionLocalizations = toLocalizationMap(buildDescriptionLocalizationSource(descriptionKey, i18n)); if (arg.type === "user") { builder.addUserOption((opt) => @@ -148,21 +190,13 @@ export const buildSlashPayload = ( return commands.map((command) => { const descriptionKey = commandDescriptionKey(command); - const slashLocalizations = toLocalizationMap({ - fr: i18n.commandName("fr", command.meta.name), - es: i18n.commandName("es", command.meta.name), - }); + const slashLocalizations = toLocalizationMap(buildCommandNameLocalizationSource(command, i18n)); + const descriptionLocalizations = toLocalizationMap(buildDescriptionLocalizationSource(descriptionKey, i18n)); const slashBuilder = new SlashCommandBuilder() .setName(command.meta.name) .setDescription(i18n.t("en", descriptionKey)) - .setDescriptionLocalizations( - toLocalizationMap({ - en: i18n.t("en", descriptionKey), - fr: i18n.t("fr", descriptionKey), - es: i18n.t("es", descriptionKey), - }), - ) + .setDescriptionLocalizations(descriptionLocalizations) .setNameLocalizations(slashLocalizations); for (const arg of command.args) { diff --git a/apps/bot/src/legacy/events/index.ts b/apps/bot/src/legacy/events/index.ts index d574145..cacd9ac 100644 --- a/apps/bot/src/legacy/events/index.ts +++ b/apps/bot/src/legacy/events/index.ts @@ -28,7 +28,7 @@ export const registerEvents = ( ): void => { registerMessageCreate(client, handlers.onPrefixMessage); registerInteractionCreate(client, handlers.onSlashInteraction); - registerLogRuntimeEvents(client, services.logEventService); + registerLogRuntimeEvents(client, services.logEventService, i18n); registerGuildMemberAdd(client, i18n, services.memberMessageService); registerGuildMemberRemove(client, i18n, services.memberMessageService); diff --git a/apps/bot/src/legacy/events/logsRuntime.ts b/apps/bot/src/legacy/events/logsRuntime.ts index 105829c..7990ad9 100644 --- a/apps/bot/src/legacy/events/logsRuntime.ts +++ b/apps/bot/src/legacy/events/logsRuntime.ts @@ -1,9 +1,12 @@ import { Events, type Client, + type Guild, + type InviteGuild, } from "discord.js"; import { createScopedLogger } from "../core/logging/logger.js"; +import type { I18nService } from "../i18n/index.js"; import type { LogEventKey } from "../types/logs.js"; import type { LogEventService } from "../modules/logs/index.js"; @@ -17,153 +20,243 @@ const clamp = (value: string, maxLength: number): string => { return `${value.slice(0, maxLength - 3)}...`; }; -const formatContent = (content: string | null | undefined): string => { +const tForGuild = ( + i18n: I18nService, + guild: Guild | InviteGuild | null | undefined, + key: string, + vars: Record = {}, +): string => { + const preferredLocale = guild && "preferredLocale" in guild ? (guild.preferredLocale ?? null) : null; + const lang = i18n.resolveLang(preferredLocale); + return i18n.t(lang, key, vars); +}; + +const detailLine = ( + i18n: I18nService, + guild: Guild | InviteGuild | null | undefined, + key: string, + vars: Record = {}, +): string => { + return tForGuild(i18n, guild, `logsRuntime.details.${key}`, vars); +}; + +const formatContent = ( + i18n: I18nService, + guild: Guild | InviteGuild | null | undefined, + content: string | null | undefined, +): string => { + const noContent = tForGuild(i18n, guild, "logsRuntime.placeholders.noContent"); + if (typeof content !== "string") { - return "(no content)"; + return noContent; } const trimmed = content.trim(); if (trimmed.length === 0) { - return "(no content)"; + return noContent; } return clamp(trimmed, 400); }; +interface EmitRuntimeLogInput { + eventKey: LogEventKey; + guild?: Guild | InviteGuild | null | undefined; + guildId?: string | null | undefined; + summary: string; + details?: string[]; + color?: number; +} + const emitRuntimeLog = ( client: Client, logEventService: LogEventService, - eventKey: LogEventKey, - guildId: string | null | undefined, - summary: string, - details: string[] = [], - color?: number, + i18n: I18nService, + input: EmitRuntimeLogInput, ): void => { + const guildId = input.guild?.id ?? input.guildId; if (!guildId) { return; } + const title = tForGuild(i18n, input.guild, "logsRuntime.embed.title", { + event: input.eventKey, + }); + const detailsTitle = tForGuild(i18n, input.guild, "logsRuntime.embed.detailsField"); + void logEventService.dispatchEvent(client, { - eventKey, + eventKey: input.eventKey, guildId, - summary, - ...(details.length > 0 ? { details } : {}), - ...(color !== undefined ? { color } : {}), + summary: input.summary, + ...(input.details && input.details.length > 0 ? { details: input.details } : {}), + ...(input.color !== undefined ? { color: input.color } : {}), + title, + detailsTitle, }).catch((error) => { - logger.error({ eventKey, guildId, err: error }, "failed to dispatch runtime log event"); + logger.error({ eventKey: input.eventKey, guildId, err: error }, "failed to dispatch runtime log event"); }); }; -export const registerLogRuntimeEvents = (client: Client, logEventService: LogEventService): void => { +export const registerLogRuntimeEvents = ( + client: Client, + logEventService: LogEventService, + i18n: I18nService, +): void => { client.on(Events.MessageCreate, (message) => { if (!message.guild || message.author.bot) { return; } - emitRuntimeLog( - client, - logEventService, - "messageCreate", - message.guild.id, - `Message sent by <@${message.author.id}> in <#${message.channelId}>`, - [ - `messageId=${message.id}`, - `author=${message.author.tag} (${message.author.id})`, - `content=${formatContent(message.content)}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "messageCreate", + guild: message.guild, + summary: tForGuild(i18n, message.guild, "logsRuntime.summaries.messageCreate", { + user: `<@${message.author.id}>`, + channel: `<#${message.channelId}>`, + }), + details: [ + detailLine(i18n, message.guild, "messageId", { value: message.id }), + detailLine(i18n, message.guild, "author", { + tag: message.author.tag, + id: message.author.id, + }), + detailLine(i18n, message.guild, "content", { + value: formatContent(i18n, message.guild, message.content), + }), ], - 0x57f287, - ); + color: 0x57f287, + }); }); client.on(Events.MessageDelete, (message) => { - const authorId = message.author?.id ?? "unknown"; - const authorTag = message.author?.tag ?? "unknown"; + const guild = message.guild; + const unknown = tForGuild(i18n, guild, "logsRuntime.placeholders.unknown"); + const authorId = message.author?.id ?? unknown; + const authorTag = message.author?.tag ?? unknown; - emitRuntimeLog( - client, - logEventService, - "messageDelete", - message.guild?.id, - `Message deleted in <#${message.channelId}>`, - [ - `messageId=${message.id}`, - `author=${authorTag} (${authorId})`, - `content=${formatContent(message.content)}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "messageDelete", + guild, + guildId: guild?.id, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.messageDelete", { + channel: `<#${message.channelId}>`, + }), + details: [ + detailLine(i18n, guild, "messageId", { value: message.id }), + detailLine(i18n, guild, "author", { + tag: authorTag, + id: authorId, + }), + detailLine(i18n, guild, "content", { + value: formatContent(i18n, guild, message.content), + }), ], - 0xed4245, - ); + color: 0xed4245, + }); }); client.on(Events.MessageUpdate, (oldMessage, newMessage) => { - emitRuntimeLog( - client, - logEventService, - "messageUpdate", - newMessage.guild?.id, - `Message updated in <#${newMessage.channelId}>`, - [ - `messageId=${newMessage.id}`, - `before=${formatContent(oldMessage.content)}`, - `after=${formatContent(newMessage.content)}`, + const guild = newMessage.guild; + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "messageUpdate", + guild, + guildId: guild?.id, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.messageUpdate", { + channel: `<#${newMessage.channelId}>`, + }), + details: [ + detailLine(i18n, guild, "messageId", { value: newMessage.id }), + detailLine(i18n, guild, "before", { + value: formatContent(i18n, guild, oldMessage.content), + }), + detailLine(i18n, guild, "after", { + value: formatContent(i18n, guild, newMessage.content), + }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.MessageBulkDelete, (messages) => { const first = messages.first(); - emitRuntimeLog( - client, - logEventService, - "messageBulkDelete", - first?.guild?.id, - `Bulk delete: ${messages.size} messages removed`, - [ - `channelId=${first?.channelId ?? "unknown"}`, - `count=${messages.size}`, + const guild = first?.guild; + const unknown = tForGuild(i18n, guild, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "messageBulkDelete", + guild, + guildId: guild?.id, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.messageBulkDelete", { + count: messages.size, + }), + details: [ + detailLine(i18n, guild, "channelId", { + value: first?.channelId ?? unknown, + }), + detailLine(i18n, guild, "count", { value: messages.size }), ], - 0xed4245, - ); + color: 0xed4245, + }); }); client.on(Events.GuildMemberAdd, (member) => { - emitRuntimeLog( - client, - logEventService, - "guildMemberAdd", - member.guild.id, - `Member joined: <@${member.user.id}>`, - [`user=${member.user.tag} (${member.user.id})`], - 0x57f287, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildMemberAdd", + guild: member.guild, + summary: tForGuild(i18n, member.guild, "logsRuntime.summaries.guildMemberAdd", { + user: `<@${member.user.id}>`, + }), + details: [ + detailLine(i18n, member.guild, "user", { + tag: member.user.tag, + id: member.user.id, + }), + ], + color: 0x57f287, + }); }); client.on(Events.GuildMemberRemove, (member) => { - emitRuntimeLog( - client, - logEventService, - "guildMemberRemove", - member.guild.id, - `Member left: <@${member.user.id}>`, - [`user=${member.user.tag} (${member.user.id})`], - 0xed4245, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildMemberRemove", + guild: member.guild, + summary: tForGuild(i18n, member.guild, "logsRuntime.summaries.guildMemberRemove", { + user: `<@${member.user.id}>`, + }), + details: [ + detailLine(i18n, member.guild, "user", { + tag: member.user.tag, + id: member.user.id, + }), + ], + color: 0xed4245, + }); }); client.on(Events.GuildMemberUpdate, (oldMember, newMember) => { - emitRuntimeLog( - client, - logEventService, - "guildMemberUpdate", - newMember.guild.id, - `Member updated: <@${newMember.user.id}>`, - [ - `user=${newMember.user.tag} (${newMember.user.id})`, - `oldNick=${oldMember.nickname ?? "none"}`, - `newNick=${newMember.nickname ?? "none"}`, + const none = tForGuild(i18n, newMember.guild, "logsRuntime.placeholders.none"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildMemberUpdate", + guild: newMember.guild, + summary: tForGuild(i18n, newMember.guild, "logsRuntime.summaries.guildMemberUpdate", { + user: `<@${newMember.user.id}>`, + }), + details: [ + detailLine(i18n, newMember.guild, "user", { + tag: newMember.user.tag, + id: newMember.user.id, + }), + detailLine(i18n, newMember.guild, "oldNick", { + value: oldMember.nickname ?? none, + }), + detailLine(i18n, newMember.guild, "newNick", { + value: newMember.nickname ?? none, + }), ], - 0x5865f2, - ); + color: 0x5865f2, + }); }); client.on(Events.InteractionCreate, (interaction) => { @@ -171,29 +264,45 @@ export const registerLogRuntimeEvents = (client: Client, logEventService: LogEve return; } - let summary = `Interaction by <@${interaction.user.id}>`; + const guild = interaction.guild; + const unknown = tForGuild(i18n, guild, "logsRuntime.placeholders.unknown"); + + let summary = tForGuild(i18n, guild, "logsRuntime.summaries.interactionGeneric", { + user: `<@${interaction.user.id}>`, + }); + if (interaction.isChatInputCommand()) { - summary = `Slash command /${interaction.commandName} by <@${interaction.user.id}>`; + summary = tForGuild(i18n, guild, "logsRuntime.summaries.interactionSlash", { + command: interaction.commandName, + user: `<@${interaction.user.id}>`, + }); } else if (interaction.isButton()) { - summary = `Button interaction by <@${interaction.user.id}>`; + summary = tForGuild(i18n, guild, "logsRuntime.summaries.interactionButton", { + user: `<@${interaction.user.id}>`, + }); } else if (interaction.isStringSelectMenu()) { - summary = `Select menu interaction by <@${interaction.user.id}>`; + summary = tForGuild(i18n, guild, "logsRuntime.summaries.interactionSelect", { + user: `<@${interaction.user.id}>`, + }); } else if (interaction.isModalSubmit()) { - summary = `Modal submit by <@${interaction.user.id}>`; + summary = tForGuild(i18n, guild, "logsRuntime.summaries.interactionModal", { + user: `<@${interaction.user.id}>`, + }); } - emitRuntimeLog( - client, - logEventService, - "interactionCreate", - interaction.guildId, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "interactionCreate", + guild, + guildId: interaction.guildId, summary, - [ - `interactionId=${interaction.id}`, - `channelId=${interaction.channelId ?? "unknown"}`, + details: [ + detailLine(i18n, guild, "interactionId", { value: interaction.id }), + detailLine(i18n, guild, "channelId", { + value: interaction.channelId ?? unknown, + }), ], - 0x5865f2, - ); + color: 0x5865f2, + }); }); client.on(Events.ChannelCreate, (channel) => { @@ -201,18 +310,18 @@ export const registerLogRuntimeEvents = (client: Client, logEventService: LogEve return; } - emitRuntimeLog( - client, - logEventService, - "channelCreate", - channel.guild.id, - `Channel created: #${channel.name}`, - [ - `channelId=${channel.id}`, - `type=${channel.type}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "channelCreate", + guild: channel.guild, + summary: tForGuild(i18n, channel.guild, "logsRuntime.summaries.channelCreate", { + channel: `#${channel.name}`, + }), + details: [ + detailLine(i18n, channel.guild, "channelId", { value: channel.id }), + detailLine(i18n, channel.guild, "type", { value: channel.type }), ], - 0x57f287, - ); + color: 0x57f287, + }); }); client.on(Events.ChannelDelete, (channel) => { @@ -220,18 +329,18 @@ export const registerLogRuntimeEvents = (client: Client, logEventService: LogEve return; } - emitRuntimeLog( - client, - logEventService, - "channelDelete", - channel.guild.id, - `Channel deleted: #${channel.name}`, - [ - `channelId=${channel.id}`, - `type=${channel.type}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "channelDelete", + guild: channel.guild, + summary: tForGuild(i18n, channel.guild, "logsRuntime.summaries.channelDelete", { + channel: `#${channel.name}`, + }), + details: [ + detailLine(i18n, channel.guild, "channelId", { value: channel.id }), + detailLine(i18n, channel.guild, "type", { value: channel.type }), ], - 0xed4245, - ); + color: 0xed4245, + }); }); client.on(Events.ChannelUpdate, (oldChannel, newChannel) => { @@ -239,227 +348,274 @@ export const registerLogRuntimeEvents = (client: Client, logEventService: LogEve return; } - const oldName = "name" in oldChannel ? oldChannel.name : "unknown"; + const unknown = tForGuild(i18n, newChannel.guild, "logsRuntime.placeholders.unknown"); + const oldName = "name" in oldChannel ? oldChannel.name : unknown; - emitRuntimeLog( - client, - logEventService, - "channelUpdate", - newChannel.guild.id, - `Channel updated: #${newChannel.name}`, - [ - `channelId=${newChannel.id}`, - `oldName=${oldName}`, - `newName=${newChannel.name}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "channelUpdate", + guild: newChannel.guild, + summary: tForGuild(i18n, newChannel.guild, "logsRuntime.summaries.channelUpdate", { + channel: `#${newChannel.name}`, + }), + details: [ + detailLine(i18n, newChannel.guild, "channelId", { value: newChannel.id }), + detailLine(i18n, newChannel.guild, "oldName", { value: oldName }), + detailLine(i18n, newChannel.guild, "newName", { value: newChannel.name }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.GuildRoleCreate, (role) => { - emitRuntimeLog( - client, - logEventService, - "roleCreate", - role.guild.id, - `Role created: @${role.name}`, - [`roleId=${role.id}`], - 0x57f287, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "roleCreate", + guild: role.guild, + summary: tForGuild(i18n, role.guild, "logsRuntime.summaries.roleCreate", { + role: `@${role.name}`, + }), + details: [detailLine(i18n, role.guild, "roleId", { value: role.id })], + color: 0x57f287, + }); }); client.on(Events.GuildRoleDelete, (role) => { - emitRuntimeLog( - client, - logEventService, - "roleDelete", - role.guild.id, - `Role deleted: @${role.name}`, - [`roleId=${role.id}`], - 0xed4245, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "roleDelete", + guild: role.guild, + summary: tForGuild(i18n, role.guild, "logsRuntime.summaries.roleDelete", { + role: `@${role.name}`, + }), + details: [detailLine(i18n, role.guild, "roleId", { value: role.id })], + color: 0xed4245, + }); }); client.on(Events.GuildRoleUpdate, (oldRole, newRole) => { - emitRuntimeLog( - client, - logEventService, - "roleUpdate", - newRole.guild.id, - `Role updated: @${newRole.name}`, - [ - `roleId=${newRole.id}`, - `oldName=${oldRole.name}`, - `newName=${newRole.name}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "roleUpdate", + guild: newRole.guild, + summary: tForGuild(i18n, newRole.guild, "logsRuntime.summaries.roleUpdate", { + role: `@${newRole.name}`, + }), + details: [ + detailLine(i18n, newRole.guild, "roleId", { value: newRole.id }), + detailLine(i18n, newRole.guild, "oldName", { value: oldRole.name }), + detailLine(i18n, newRole.guild, "newName", { value: newRole.name }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.ThreadCreate, (thread) => { - emitRuntimeLog( - client, - logEventService, - "threadCreate", - thread.guild?.id, - `Thread created: ${thread.name}`, - [`threadId=${thread.id}`], - 0x57f287, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "threadCreate", + guild: thread.guild ?? null, + guildId: thread.guild?.id, + summary: tForGuild(i18n, thread.guild ?? null, "logsRuntime.summaries.threadCreate", { + thread: thread.name, + }), + details: [detailLine(i18n, thread.guild ?? null, "threadId", { value: thread.id })], + color: 0x57f287, + }); }); client.on(Events.ThreadDelete, (thread) => { - emitRuntimeLog( - client, - logEventService, - "threadDelete", - thread.guild?.id, - `Thread deleted: ${thread.name}`, - [`threadId=${thread.id}`], - 0xed4245, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "threadDelete", + guild: thread.guild ?? null, + guildId: thread.guild?.id, + summary: tForGuild(i18n, thread.guild ?? null, "logsRuntime.summaries.threadDelete", { + thread: thread.name, + }), + details: [detailLine(i18n, thread.guild ?? null, "threadId", { value: thread.id })], + color: 0xed4245, + }); }); client.on(Events.ThreadUpdate, (oldThread, newThread) => { - emitRuntimeLog( - client, - logEventService, - "threadUpdate", - newThread.guild?.id, - `Thread updated: ${newThread.name}`, - [ - `threadId=${newThread.id}`, - `oldName=${oldThread.name}`, - `newName=${newThread.name}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "threadUpdate", + guild: newThread.guild ?? null, + guildId: newThread.guild?.id, + summary: tForGuild(i18n, newThread.guild ?? null, "logsRuntime.summaries.threadUpdate", { + thread: newThread.name, + }), + details: [ + detailLine(i18n, newThread.guild ?? null, "threadId", { value: newThread.id }), + detailLine(i18n, newThread.guild ?? null, "oldName", { value: oldThread.name }), + detailLine(i18n, newThread.guild ?? null, "newName", { value: newThread.name }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.GuildEmojiCreate, (emoji) => { - emitRuntimeLog( - client, - logEventService, - "emojiCreate", - emoji.guild?.id, - `Emoji created: ${emoji.name ?? "unknown"}`, - [`emojiId=${emoji.id ?? "unknown"}`], - 0x57f287, - ); + const unknown = tForGuild(i18n, emoji.guild ?? null, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "emojiCreate", + guild: emoji.guild ?? null, + guildId: emoji.guild?.id, + summary: tForGuild(i18n, emoji.guild ?? null, "logsRuntime.summaries.emojiCreate", { + emoji: emoji.name ?? unknown, + }), + details: [ + detailLine(i18n, emoji.guild ?? null, "emojiId", { + value: emoji.id ?? unknown, + }), + ], + color: 0x57f287, + }); }); client.on(Events.GuildEmojiDelete, (emoji) => { - emitRuntimeLog( - client, - logEventService, - "emojiDelete", - emoji.guild?.id, - `Emoji deleted: ${emoji.name ?? "unknown"}`, - [`emojiId=${emoji.id ?? "unknown"}`], - 0xed4245, - ); + const unknown = tForGuild(i18n, emoji.guild ?? null, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "emojiDelete", + guild: emoji.guild ?? null, + guildId: emoji.guild?.id, + summary: tForGuild(i18n, emoji.guild ?? null, "logsRuntime.summaries.emojiDelete", { + emoji: emoji.name ?? unknown, + }), + details: [ + detailLine(i18n, emoji.guild ?? null, "emojiId", { + value: emoji.id ?? unknown, + }), + ], + color: 0xed4245, + }); }); client.on(Events.GuildEmojiUpdate, (oldEmoji, newEmoji) => { - emitRuntimeLog( - client, - logEventService, - "emojiUpdate", - newEmoji.guild?.id, - `Emoji updated: ${newEmoji.name ?? "unknown"}`, - [ - `emojiId=${newEmoji.id ?? "unknown"}`, - `oldName=${oldEmoji.name ?? "unknown"}`, - `newName=${newEmoji.name ?? "unknown"}`, + const unknown = tForGuild(i18n, newEmoji.guild ?? null, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "emojiUpdate", + guild: newEmoji.guild ?? null, + guildId: newEmoji.guild?.id, + summary: tForGuild(i18n, newEmoji.guild ?? null, "logsRuntime.summaries.emojiUpdate", { + emoji: newEmoji.name ?? unknown, + }), + details: [ + detailLine(i18n, newEmoji.guild ?? null, "emojiId", { + value: newEmoji.id ?? unknown, + }), + detailLine(i18n, newEmoji.guild ?? null, "oldName", { + value: oldEmoji.name ?? unknown, + }), + detailLine(i18n, newEmoji.guild ?? null, "newName", { + value: newEmoji.name ?? unknown, + }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.GuildUpdate, (oldGuild, newGuild) => { - emitRuntimeLog( - client, - logEventService, - "guildUpdate", - newGuild.id, - `Guild updated: ${newGuild.name}`, - [ - `guildId=${newGuild.id}`, - `oldName=${oldGuild.name}`, - `newName=${newGuild.name}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildUpdate", + guild: newGuild, + summary: tForGuild(i18n, newGuild, "logsRuntime.summaries.guildUpdate", { + guild: newGuild.name, + }), + details: [ + detailLine(i18n, newGuild, "guildId", { value: newGuild.id }), + detailLine(i18n, newGuild, "oldName", { value: oldGuild.name }), + detailLine(i18n, newGuild, "newName", { value: newGuild.name }), ], - 0xfee75c, - ); + color: 0xfee75c, + }); }); client.on(Events.GuildUnavailable, (guild) => { - emitRuntimeLog( - client, - logEventService, - "guildUnavailable", - guild.id, - `Guild temporarily unavailable: ${guild.name}`, - [`guildId=${guild.id}`], - 0xed4245, - ); + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildUnavailable", + guild, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.guildUnavailable", { + guild: guild.name, + }), + details: [detailLine(i18n, guild, "guildId", { value: guild.id })], + color: 0xed4245, + }); }); client.on(Events.GuildBanAdd, (ban) => { - emitRuntimeLog( - client, - logEventService, - "guildBanAdd", - ban.guild.id, - `User banned: <@${ban.user.id}>`, - [ - `user=${ban.user.tag} (${ban.user.id})`, - `guildId=${ban.guild.id}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildBanAdd", + guild: ban.guild, + summary: tForGuild(i18n, ban.guild, "logsRuntime.summaries.guildBanAdd", { + user: `<@${ban.user.id}>`, + }), + details: [ + detailLine(i18n, ban.guild, "user", { + tag: ban.user.tag, + id: ban.user.id, + }), + detailLine(i18n, ban.guild, "guildId", { value: ban.guild.id }), ], - 0xed4245, - ); + color: 0xed4245, + }); }); client.on(Events.GuildBanRemove, (ban) => { - emitRuntimeLog( - client, - logEventService, - "guildBanRemove", - ban.guild.id, - `User unbanned: <@${ban.user.id}>`, - [ - `user=${ban.user.tag} (${ban.user.id})`, - `guildId=${ban.guild.id}`, + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "guildBanRemove", + guild: ban.guild, + summary: tForGuild(i18n, ban.guild, "logsRuntime.summaries.guildBanRemove", { + user: `<@${ban.user.id}>`, + }), + details: [ + detailLine(i18n, ban.guild, "user", { + tag: ban.user.tag, + id: ban.user.id, + }), + detailLine(i18n, ban.guild, "guildId", { value: ban.guild.id }), ], - 0x57f287, - ); + color: 0x57f287, + }); }); client.on(Events.InviteCreate, (invite) => { - emitRuntimeLog( - client, - logEventService, - "inviteCreate", - invite.guild?.id, - `Invite created: ${invite.code}`, - [ - `channelId=${invite.channelId ?? "unknown"}`, - `inviter=${invite.inviter?.tag ?? "unknown"}`, + const guild = invite.guild ?? null; + const unknown = tForGuild(i18n, guild, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "inviteCreate", + guild, + guildId: guild?.id, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.inviteCreate", { + code: invite.code, + }), + details: [ + detailLine(i18n, guild, "channelId", { + value: invite.channelId ?? unknown, + }), + detailLine(i18n, guild, "inviter", { + value: invite.inviter?.tag ?? unknown, + }), ], - 0x57f287, - ); + color: 0x57f287, + }); }); client.on(Events.InviteDelete, (invite) => { - emitRuntimeLog( - client, - logEventService, - "inviteDelete", - invite.guild?.id, - `Invite deleted: ${invite.code}`, - [ - `channelId=${invite.channelId ?? "unknown"}`, + const guild = invite.guild ?? null; + const unknown = tForGuild(i18n, guild, "logsRuntime.placeholders.unknown"); + + emitRuntimeLog(client, logEventService, i18n, { + eventKey: "inviteDelete", + guild, + guildId: guild?.id, + summary: tForGuild(i18n, guild, "logsRuntime.summaries.inviteDelete", { + code: invite.code, + }), + details: [ + detailLine(i18n, guild, "channelId", { + value: invite.channelId ?? unknown, + }), ], - 0xed4245, - ); + color: 0xed4245, + }); }); -}; \ No newline at end of file +}; diff --git a/apps/bot/src/legacy/features/logs/commandPanel.ts b/apps/bot/src/legacy/features/logs/commandPanel.ts index 3eb8437..6809f8a 100644 --- a/apps/bot/src/legacy/features/logs/commandPanel.ts +++ b/apps/bot/src/legacy/features/logs/commandPanel.ts @@ -83,6 +83,10 @@ const eventStatusLabel = (ctx: CommandExecutionContext, enabled: boolean): strin return enabled ? ctx.ct("ui.status.enabled") : ctx.ct("ui.status.disabled"); }; +const eventLabel = (ctx: CommandExecutionContext, eventKey: LogEventKey): string => { + return ctx.ct(`ui.events.${eventKey}`); +}; + const panelContent = ( ctx: CommandExecutionContext, state: LogEventStateByKey, @@ -105,7 +109,7 @@ const panelContent = ( for (const definition of currentEvents) { const eventConfig = state[definition.key]; const channelDisplay = eventConfig.channelId ? `<#${eventConfig.channelId}>` : ctx.ct("ui.channelNotConfigured"); - lines.push(`- ${definition.key} | ${eventStatusLabel(ctx, eventConfig.enabled)} | ${ctx.ct("ui.channelLabel")}: ${channelDisplay}`); + lines.push(`- ${eventLabel(ctx, definition.key)} | ${eventStatusLabel(ctx, eventConfig.enabled)} | ${ctx.ct("ui.channelLabel")}: ${channelDisplay}`); } if (uiState.feedback) { @@ -164,9 +168,10 @@ const buildContainer = ( for (const definitionChunk of chunk(currentEvents, 5)) { const rowButtons = definitionChunk.map((definition) => { const eventConfig = state[definition.key]; + const localizedEvent = eventLabel(ctx, definition.key); const label = eventConfig.enabled - ? ctx.ct("ui.buttons.disableEvent", { event: definition.key }) - : ctx.ct("ui.buttons.enableEvent", { event: definition.key }); + ? ctx.ct("ui.buttons.disableEvent", { event: localizedEvent }) + : ctx.ct("ui.buttons.enableEvent", { event: localizedEvent }); return new ButtonBuilder() .setCustomId(customIds.toggleButtonsByEvent[definition.key]) @@ -331,9 +336,10 @@ export const createLogsCommandExecute = (logEventService: LogEventService) => { if (eventKey) { state[eventKey].enabled = !state[eventKey].enabled; await logEventService.persistGuildState(ctx.client, guild.id, state); + const localizedEvent = eventLabel(ctx, eventKey); uiState.feedback = state[eventKey].enabled - ? ctx.ct("responses.eventEnabled", { event: eventKey }) - : ctx.ct("responses.eventDisabled", { event: eventKey }); + ? ctx.ct("responses.eventEnabled", { event: localizedEvent }) + : ctx.ct("responses.eventDisabled", { event: localizedEvent }); await interaction.update({ flags: MessageFlags.IsComponentsV2, diff --git a/apps/bot/src/legacy/features/logs/service.ts b/apps/bot/src/legacy/features/logs/service.ts index 4db081d..93ef2d1 100644 --- a/apps/bot/src/legacy/features/logs/service.ts +++ b/apps/bot/src/legacy/features/logs/service.ts @@ -30,8 +30,9 @@ import { } from "../../validators/logs.js"; const logger = createScopedLogger("feature:logs"); -const LOG_CHANNEL_CATEGORY_NAME = "📁 ➜ Espace Logs"; -const LOG_CHANNEL_NAME_PREFIX = "📁・"; +const LOG_CHANNEL_CATEGORY_NAME = "logs"; +const LOG_CHANNEL_NAME_PREFIX = ""; +const LEGACY_LOG_CHANNEL_NAME_PREFIX = "📁・"; const hasSendMethod = (value: unknown): value is { send: (payload: unknown) => Promise } => { if (!value || typeof value !== "object") { @@ -176,7 +177,10 @@ export class LogEventService { return false; } - return channel.type === ChannelType.GuildText && channel.name === legacyName; + return channel.type === ChannelType.GuildText && ( + channel.name === legacyName + || channel.name === `${LEGACY_LOG_CHANNEL_NAME_PREFIX}${legacyName}` + ); }); const existing = existingByExpectedName ?? existingByLegacyName; @@ -224,7 +228,6 @@ export class LogEventService { name: expectedName, type: ChannelType.GuildText, parent: logCategory.id, - topic: `Logs for ${category} events`, permissionOverwrites, }); @@ -295,7 +298,7 @@ export class LogEventService { const embed = new EmbedBuilder() .setColor(input.color ?? 0x5865f2) - .setTitle(`Event: ${input.eventKey}`) + .setTitle(input.title ?? input.eventKey) .setDescription(clampField(input.summary, 4096)) .setTimestamp(new Date()); @@ -303,7 +306,7 @@ export class LogEventService { const detailsValue = clampField(input.details.map((line) => `- ${line}`).join("\n"), 1024); if (detailsValue.length > 0) { embed.addFields({ - name: "Details", + name: input.detailsTitle ?? "details", value: detailsValue, }); } diff --git a/apps/bot/src/legacy/features/memberMessages/service.ts b/apps/bot/src/legacy/features/memberMessages/service.ts index b2d3b44..8ab2b36 100644 --- a/apps/bot/src/legacy/features/memberMessages/service.ts +++ b/apps/bot/src/legacy/features/memberMessages/service.ts @@ -59,70 +59,19 @@ const messageColor = (kind: MemberMessageKind): number => { return kind === "welcome" ? 0x57f287 : 0xed4245; }; -const defaultTemplate = ( - kind: MemberMessageKind, - suffix: TemplateSuffix, - vars: Record, -): string => { - if (kind === "welcome") { - switch (suffix) { - case "simple": - return `🎉 Welcome ${vars.user} to **${vars.guild}**!`; - case "embedTitle": - return "Welcome!"; - case "embedDescription": - return `${vars.user} just joined **${vars.guild}**.`; - case "containerTitle": - return "Welcome"; - case "containerDescription": - return `${vars.user} just joined **${vars.guild}**.`; - case "imageTitle": - return "New member"; - case "imageDescription": - return `Glad to have you here, ${vars.user}!`; - default: - return ""; - } - } - - switch (suffix) { - case "simple": - return `👋 ${vars.user} left **${vars.guild}**.`; - case "embedTitle": - return "Goodbye"; - case "embedDescription": - return `${vars.user} has left **${vars.guild}**.`; - case "containerTitle": - return "Goodbye"; - case "containerDescription": - return `${vars.user} has left **${vars.guild}**.`; - case "imageTitle": - return "Member left"; - case "imageDescription": - return `${vars.user} has left the server.`; - default: - return ""; - } -}; - const resolveTemplate = ( - i18n: I18nService | undefined, + i18n: I18nService, lang: SupportedLang, kind: MemberMessageKind, suffix: TemplateSuffix, vars: Record, ): string => { - if (!i18n) { - return defaultTemplate(kind, suffix, vars); - } - const key = `commands.${kind}.templates.${suffix}`; - const translated = i18n.t(lang, key, vars); - return translated === key ? defaultTemplate(kind, suffix, vars) : translated; + return i18n.t(lang, key, vars); }; const resolveNonEmptyTemplate = ( - i18n: I18nService | undefined, + i18n: I18nService, lang: SupportedLang, kind: MemberMessageKind, suffix: TemplateSuffix, @@ -133,8 +82,9 @@ const resolveNonEmptyTemplate = ( return resolved; } - const fallback = defaultTemplate(kind, suffix, vars).trim(); - return fallback.length > 0 ? fallback : suffix; + const key = `commands.${kind}.templates.${suffix}`; + const fallback = i18n.t("en", key, vars).trim(); + return fallback.length > 0 ? fallback : key; }; const resolveAssignableRoleId = async (member: GuildMember, roleId: string): Promise => { @@ -151,7 +101,7 @@ const resolveAssignableRoleId = async (member: GuildMember, roleId: string): Pro }; const buildMemberMessagePayload = async ( - i18n: I18nService | undefined, + i18n: I18nService, lang: SupportedLang, kind: MemberMessageKind, renderType: MemberMessageRenderType, @@ -392,7 +342,7 @@ export class MemberMessageService { } } - const lang = input.i18n?.resolveLang(input.guild.preferredLocale ?? null) ?? "en"; + const lang = input.i18n.resolveLang(input.guild.preferredLocale ?? null); const payload = await buildMemberMessagePayload(input.i18n, lang, input.kind, config.messageType, input.guild, input.user); try { diff --git a/apps/bot/src/legacy/features/presence/service.ts b/apps/bot/src/legacy/features/presence/service.ts index 9c57d99..867f62b 100644 --- a/apps/bot/src/legacy/features/presence/service.ts +++ b/apps/bot/src/legacy/features/presence/service.ts @@ -182,7 +182,7 @@ export class PresenceService { activities: [ { type: ActivityType.Custom, - name: "Custom Status", + name: text, state: text, }, ], diff --git a/apps/bot/src/legacy/i18n/es.json b/apps/bot/src/legacy/i18n/es.json deleted file mode 100644 index d646e32..0000000 --- a/apps/bot/src/legacy/i18n/es.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "errors": { - "args": { - "missing": "Falta el argumento {{arg}}. Uso: {{usage}}", - "invalidInt": "El argumento debe ser un entero. Recibido: {{value}}", - "invalidNumber": "El argumento debe ser un numero. Recibido: {{value}}", - "invalidBoolean": "El argumento debe ser un valor booleano. Recibido: {{value}}", - "invalidUser": "No se pudo resolver un usuario desde {{value}}", - "invalidChannel": "No se pudo resolver un canal desde {{value}}", - "invalidRole": "No se pudo resolver un rol desde {{value}}", - "tooMany": "Se detectaron demasiados argumentos ({{count}}): {{extras}}. Uso: {{usage}}" - }, - "permissions": { - "user": "Te faltan permisos: {{permissions}}" - }, - "execution": "Ocurrio un error inesperado al ejecutar este comando.", - "cooldown": "Espera {{seconds}}s antes de volver a usar este comando.", - "rateLimit": "Demasiadas solicitudes. Espera {{seconds}}s antes de reintentar.", - "executionQueued": "Comando aceptado y encolado para procesamiento." - }, - "categories": { - "fun": "Diversion", - "utility": "Utilidad", - "core": "Nucleo" - }, - "commands": { - "kiss": { - "name": "beso", - "description": "Enviar un beso a otro usuario.", - "args": { - "user": "Usuario objetivo" - }, - "examples": { - "basic": "Enviar un beso a un miembro" - }, - "responses": { - "success": "{{from}} envia un beso a {{to}}." - } - }, - "ping": { - "name": "ping", - "description": "Revisar latencia websocket del bot.", - "examples": { - "basic": "Chequeo basico" - }, - "responses": { - "pong": "Pong. Latencia websocket: {{latency}}ms." - } - }, - "presence": { - "name": "presence", - "description": "Configurar la presencia del bot con un panel interactivo.", - "examples": { - "slash": "Abrir el panel interactivo de presencia" - }, - "responses": { - "panel": "Panel de presencia\nEstado: {{status}}\nActividad: {{activityType}}\nRotacion: cada {{rotationIntervalSeconds}}s\nTexto activo ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nVista previa: {{activityPreview}}\nTextos: {{activityTexts}}\nVariables ({{doubleBracesHint}}): {{variables}}", - "notOwner": "Solo el autor del comando puede usar este panel.", - "invalidSelection": "Seleccion invalida.", - "invalidInterval": "Intervalo invalido. Usa un entero entre {{minSeconds}} y {{maxSeconds}} segundos.", - "modalTimeout": "Tiempo agotado para editar el texto. Pulsa el boton otra vez." - }, - "ui": { - "status": { - "placeholder": "Elegir un estado", - "options": { - "online": { - "label": "En linea", - "description": "Muestra el bot en linea" - }, - "idle": { - "label": "Inactivo", - "description": "Muestra el bot inactivo" - }, - "dnd": { - "label": "No molestar", - "description": "Muestra el bot en no molestar" - }, - "invisible": { - "label": "Desconectado", - "description": "Muestra el bot fuera de linea" - }, - "streaming": { - "label": "Streaming", - "description": "Muestra el bot en modo streaming" - } - } - }, - "activity": { - "placeholder": "Elegir un tipo de actividad", - "options": { - "PLAYING": { - "label": "Jugando", - "description": "Muestra Jugando a ..." - }, - "STREAMING": { - "label": "Streaming", - "description": "Muestra En directo en ..." - }, - "WATCHING": { - "label": "Viendo", - "description": "Muestra Viendo ..." - }, - "LISTENING": { - "label": "Escuchando", - "description": "Muestra Escuchando ..." - }, - "COMPETING": { - "label": "Compitiendo", - "description": "Muestra Compitiendo en ..." - }, - "CUSTOM": { - "label": "CustomStatus", - "description": "Muestra un estado personalizado" - } - } - }, - "embed": { - "title": "Configuracion de Presencia", - "description": "Cambia el estado, el tipo de actividad, varios textos y el intervalo de rotacion en tiempo real.", - "footer": "Panel interactivo /presence", - "fields": { - "status": "Estado", - "activity": "Tipo de actividad", - "text": "Texto" - } - }, - "textButton": "📝 Editar textos", - "intervalButton": "⏱️ Intervalo", - "modal": { - "title": "Textos de presencia", - "label": "Un texto por linea (usa {{var}})", - "placeholder": "Mantenimiento\n{{guild_count}} servidores | {{prefix}}ayuda" - }, - "intervalModal": { - "title": "Intervalo de rotacion", - "label": "Segundos entre cada texto", - "placeholder": "Ejemplo: 30" - } - } - }, - "logs": { - "name": "logs", - "description": "Configurar logs de eventos Discord con un panel interactivo.", - "examples": { - "slash": "Abrir el panel interactivo de logs" - }, - "responses": { - "slashOnly": "Usa /logs para abrir este panel interactivo.", - "guildOnly": "Este comando solo puede usarse dentro de un servidor.", - "notOwner": "Solo el autor del comando puede usar este panel.", - "invalidSelection": "Seleccion invalida.", - "allEnabled": "Todos los logs de eventos estan activados.", - "allDisabled": "Todos los logs de eventos estan desactivados.", - "eventEnabled": "Evento {{event}} activado.", - "eventDisabled": "Evento {{event}} desactivado.", - "channelsCreated": "Canales de logs listos. Creados: {{created}}, reutilizados: {{reused}}.", - "channelsPartial": "Canales de logs parcialmente listos. Creados: {{created}}, reutilizados: {{reused}}, categorias fallidas: {{failed}}.", - "channelsFailed": "No se pudieron crear los canales de logs. Categorias fallidas: {{failed}}." - }, - "ui": { - "embed": { - "title": "Configuracion de Logs", - "description": "Activa o desactiva cada evento de Discord y enruta logs por canales de categoria." - }, - "status": { - "enabled": "Activado", - "disabled": "Desactivado" - }, - "pageLabel": "Pagina {{page}}/{{pageCount}}", - "enabledCountLabel": "Eventos activados: {{enabledCount}}/{{totalCount}}", - "eventsHeader": "Eventos", - "channelLabel": "Canal", - "channelNotConfigured": "#sin-configurar", - "feedbackLabel": "Ultima accion", - "buttons": { - "enableAll": "✅ Activar todos los logs", - "disableAll": "❌ Desactivar todos los logs", - "createChannels": "📁 Crear canales de logs", - "previousPage": "Anterior", - "nextPage": "Siguiente", - "enableEvent": "Activar {{event}}", - "disableEvent": "Desactivar {{event}}" - } - } - }, - "welcome": { - "name": "bienvenida", - "description": "Configurar mensajes de bienvenida con un panel interactivo.", - "examples": { - "slash": "Abrir el panel de configuracion de bienvenida" - }, - "responses": { - "guildOnly": "Este comando solo puede usarse dentro de un servidor.", - "notOwner": "Solo el autor del comando puede usar este panel.", - "invalidSelection": "Seleccion invalida.", - "testSuccess": "Mensaje de prueba enviado en {{channel}}.", - "testDisabled": "El modulo de bienvenida esta desactivado.", - "testMissingChannel": "No hay canal configurado. Usa primero el boton de canal.", - "testChannelUnavailable": "El canal configurado no existe o no es compatible.", - "testMissingPermissions": "El bot no tiene permiso para enviar mensajes en ese canal.", - "testFailed": "No se pudo enviar el mensaje de prueba. Intentalo mas tarde." - }, - "ui": { - "embed": { - "title": "Configuracion Welcome", - "description": "Ajusta estado, canal, tipo de mensaje y ejecuta una prueba.", - "fields": { - "status": "Estado", - "channel": "Canal", - "type": "Tipo", - "channelPicker": "Selector de canal", - "autoRoles": "Roles automaticos" - } - }, - "status": { - "enabled": "🟢 Activado", - "disabled": "🔴 Desactivado" - }, - "channelNotConfigured": "#sin-configurar", - "autoRolesNotConfigured": "Ningun rol automatico configurado", - "channelPickerPlaceholder": "Selecciona un canal de bienvenida", - "channelPickerHint": "Usa el menu de seleccion de abajo para elegir el canal destino.", - "rolePickerPlaceholder": "Selecciona uno o varios roles automaticos", - "rolePickerHint": "Usa el menu de roles de abajo para agregarlos a la lista automatica.", - "buttons": { - "toggle": "Alternar estado", - "channel": "Configurar canal", - "channelCancel": "Cerrar selector", - "roles": "Agregar roles auto", - "rolesCancel": "Cerrar selector de roles", - "rolesClear": "Limpiar roles auto", - "test": "Probar" - }, - "type": { - "placeholder": "Elige un tipo de mensaje", - "options": { - "simple": { - "label": "Mensaje simple", - "description": "Enviar un mensaje de texto clasico" - }, - "embed": { - "label": "Embed", - "description": "Enviar un embed de bienvenida" - }, - "container": { - "label": "Container (V2)", - "description": "Enviar un container Components V2" - }, - "image": { - "label": "Imagen", - "description": "Enviar un embed con imagen" - } - } - } - }, - "templates": { - "simple": "🎉 Bienvenido {{user}} a **{{guild}}**!", - "embedTitle": "Bienvenido", - "embedDescription": "{{user}} acaba de entrar en **{{guild}}**.", - "containerTitle": "Bienvenido", - "containerDescription": "{{user}} acaba de entrar en **{{guild}}**.", - "imageTitle": "Bienvenido", - "imageDescription": "al servidor de Discord {{guild}}" - } - }, - "goodbye": { - "name": "despedida", - "description": "Configurar mensajes de despedida con un panel interactivo.", - "examples": { - "slash": "Abrir el panel de configuracion de despedida" - }, - "responses": { - "guildOnly": "Este comando solo puede usarse dentro de un servidor.", - "notOwner": "Solo el autor del comando puede usar este panel.", - "invalidSelection": "Seleccion invalida.", - "testSuccess": "Mensaje de prueba enviado en {{channel}}.", - "testDisabled": "El modulo de despedida esta desactivado.", - "testMissingChannel": "No hay canal configurado. Usa primero el boton de canal.", - "testChannelUnavailable": "El canal configurado no existe o no es compatible.", - "testMissingPermissions": "El bot no tiene permiso para enviar mensajes en ese canal.", - "testFailed": "No se pudo enviar el mensaje de prueba. Intentalo mas tarde." - }, - "ui": { - "embed": { - "title": "Configuracion Goodbye", - "description": "Ajusta estado, canal, tipo de mensaje y ejecuta una prueba.", - "fields": { - "status": "Estado", - "channel": "Canal", - "type": "Tipo", - "channelPicker": "Selector de canal" - } - }, - "status": { - "enabled": "🟢 Activado", - "disabled": "🔴 Desactivado" - }, - "channelNotConfigured": "#sin-configurar", - "channelPickerPlaceholder": "Selecciona un canal de despedida", - "channelPickerHint": "Usa el menu de seleccion de abajo para elegir el canal destino.", - "buttons": { - "toggle": "Alternar estado", - "channel": "Configurar canal", - "channelCancel": "Cerrar selector", - "test": "Probar" - }, - "type": { - "placeholder": "Elige un tipo de mensaje", - "options": { - "simple": { - "label": "Mensaje simple", - "description": "Enviar un mensaje de texto clasico" - }, - "embed": { - "label": "Embed", - "description": "Enviar un embed de despedida" - }, - "container": { - "label": "Container (V2)", - "description": "Enviar un container Components V2" - }, - "image": { - "label": "Imagen", - "description": "Enviar un embed con imagen" - } - } - } - }, - "templates": { - "simple": "👋 {{user}} salio de **{{guild}}**.", - "embedTitle": "Despedida", - "embedDescription": "{{user}} salio de **{{guild}}**.", - "containerTitle": "Despedida", - "containerDescription": "{{user}} salio de **{{guild}}**.", - "imageTitle": "Hasta luego", - "imageDescription": "del servidor de Discord {{guild}}" - } - }, - "help": { - "name": "ayuda", - "description": "Mostrar lista de comandos o detalles de un comando.", - "args": { - "command": "Nombre o trigger opcional del comando" - }, - "examples": { - "basic": "Listar todos los comandos", - "single": "Mostrar detalles de un comando" - }, - "errors": { - "notFound": "Ningun comando coincide con {{query}}." - }, - "embed": { - "title": "Ayuda de Comandos", - "description": "Usa {{usage}} para inspeccionar un comando.", - "categoryEmpty": "No hay comandos en esta categoria.", - "detailsTitle": "Comando: {{name}}", - "detailsDescription": "{{description}}", - "footer": "Categoria: {{source}}", - "fields": { - "usage": "Uso", - "arguments": "Argumentos", - "examples": "Ejemplos" - } - }, - "labels": { - "prefix": "Prefijo", - "slash": "Slash", - "required": "requerido", - "optional": "opcional", - "noArgs": "Sin argumentos.", - "noExamples": "Sin ejemplos." - } - } - } -} diff --git a/apps/bot/src/legacy/i18n/fr.json b/apps/bot/src/legacy/i18n/fr.json deleted file mode 100644 index 119f2e9..0000000 --- a/apps/bot/src/legacy/i18n/fr.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "errors": { - "args": { - "missing": "Argument manquant {{arg}}. Usage: {{usage}}", - "invalidInt": "L argument doit etre un entier. Recu: {{value}}", - "invalidNumber": "L argument doit etre un nombre. Recu: {{value}}", - "invalidBoolean": "L argument doit etre un booleen. Recu: {{value}}", - "invalidUser": "Impossible de trouver un utilisateur depuis {{value}}", - "invalidChannel": "Impossible de trouver un salon depuis {{value}}", - "invalidRole": "Impossible de trouver un role depuis {{value}}", - "tooMany": "Trop d arguments ({{count}}) detectes: {{extras}}. Usage: {{usage}}" - }, - "permissions": { - "user": "Tu n as pas les permissions: {{permissions}}" - }, - "execution": "Une erreur inattendue est survenue pendant l execution.", - "cooldown": "Patiente {{seconds}}s avant de reutiliser cette commande.", - "rateLimit": "Trop de requetes. Patiente {{seconds}}s avant de reessayer.", - "executionQueued": "Commande acceptee et mise en file de traitement." - }, - "categories": { - "fun": "Fun", - "utility": "Utilitaire", - "core": "Base" - }, - "commands": { - "kiss": { - "name": "bisou", - "description": "Envoyer un bisou a un utilisateur.", - "args": { - "user": "Utilisateur cible" - }, - "examples": { - "basic": "Envoyer un bisou a un membre" - }, - "responses": { - "success": "{{from}} envoie un bisou a {{to}}." - } - }, - "ping": { - "name": "ping", - "description": "Verifier la latence websocket du bot.", - "examples": { - "basic": "Verification de sante" - }, - "responses": { - "pong": "Pong. Latence websocket: {{latency}}ms." - } - }, - "presence": { - "name": "presence", - "description": "Configurer la presence du bot avec un panneau interactif.", - "examples": { - "slash": "Ouvrir le panneau interactif de presence" - }, - "responses": { - "panel": "Panneau presence\nStatut: {{status}}\nActivite: {{activityType}}\nRotation: toutes les {{rotationIntervalSeconds}}s\nTexte actif ({{currentTextIndex}}/{{textCount}}): {{activityText}}\nApercu rendu: {{activityPreview}}\nTextes: {{activityTexts}}\nVariables ({{doubleBracesHint}}): {{variables}}", - "notOwner": "Seul l auteur de la commande peut utiliser ce panneau.", - "invalidSelection": "Selection invalide.", - "invalidInterval": "Intervalle invalide. Utilise un entier entre {{minSeconds}} et {{maxSeconds}} secondes.", - "modalTimeout": "Temps depasse pour la modification du texte. Clique a nouveau sur le bouton." - }, - "ui": { - "status": { - "placeholder": "Choisir un statut", - "options": { - "online": { - "label": "En ligne", - "description": "Affiche le bot en ligne" - }, - "idle": { - "label": "Inactif", - "description": "Affiche le bot inactif" - }, - "dnd": { - "label": "Ne pas deranger", - "description": "Affiche le bot en mode ne pas deranger" - }, - "invisible": { - "label": "Hors ligne", - "description": "Affiche le bot hors ligne" - }, - "streaming": { - "label": "Streaming", - "description": "Affiche le bot en mode streaming" - } - } - }, - "activity": { - "placeholder": "Choisir un type d activite", - "options": { - "PLAYING": { - "label": "Joue a", - "description": "Affiche Joue a ..." - }, - "STREAMING": { - "label": "Streaming", - "description": "Affiche En direct sur ..." - }, - "WATCHING": { - "label": "Regarde", - "description": "Affiche Regarde ..." - }, - "LISTENING": { - "label": "Ecoute", - "description": "Affiche Ecoute ..." - }, - "COMPETING": { - "label": "Competition", - "description": "Affiche Competition ..." - }, - "CUSTOM": { - "label": "CustomStatus", - "description": "Affiche un statut personnalise" - } - } - }, - "embed": { - "title": "Configuration de la presence", - "description": "Modifie le statut, le type d activite, plusieurs textes et leur rotation.", - "footer": "Panneau interactif /presence", - "fields": { - "status": "Statut", - "activity": "Type d activite", - "text": "Texte" - } - }, - "textButton": "📝 Modifier les textes", - "intervalButton": "⏱️ Intervalle", - "modal": { - "title": "Textes de presence", - "label": "Un texte par ligne (utilise {{var}})", - "placeholder": "Maintenance\n{{guild_count}} serveurs | {{prefix}}aide" - }, - "intervalModal": { - "title": "Intervalle de rotation", - "label": "Secondes entre chaque texte", - "placeholder": "Exemple: 30" - } - } - }, - "logs": { - "name": "logs", - "description": "Configurer les logs d events Discord avec un panneau interactif.", - "examples": { - "slash": "Ouvrir le panneau interactif des logs" - }, - "responses": { - "slashOnly": "Utilise /logs pour ouvrir ce panneau interactif.", - "guildOnly": "Cette commande doit etre utilisee dans un serveur.", - "notOwner": "Seul l auteur de la commande peut utiliser ce panneau.", - "invalidSelection": "Selection invalide.", - "allEnabled": "Tous les logs d events sont actives.", - "allDisabled": "Tous les logs d events sont desactives.", - "eventEnabled": "Event {{event}} active.", - "eventDisabled": "Event {{event}} desactive.", - "channelsCreated": "Salons de logs prets. Crees: {{created}}, reutilises: {{reused}}.", - "channelsPartial": "Salons de logs partiellement prets. Crees: {{created}}, reutilises: {{reused}}, categories en echec: {{failed}}.", - "channelsFailed": "Impossible de creer les salons de logs. Categories en echec: {{failed}}." - }, - "ui": { - "embed": { - "title": "Configuration des logs", - "description": "Active ou desactive chaque event Discord et route les logs par salons de categorie." - }, - "status": { - "enabled": "Active", - "disabled": "Desactive" - }, - "pageLabel": "Page {{page}}/{{pageCount}}", - "enabledCountLabel": "Events actives: {{enabledCount}}/{{totalCount}}", - "eventsHeader": "Events", - "channelLabel": "Salon", - "channelNotConfigured": "#non-configure", - "feedbackLabel": "Derniere action", - "buttons": { - "enableAll": "✅ Activer tous les logs", - "disableAll": "❌ Desactiver tous les logs", - "createChannels": "📁 Creer les salons de logs", - "previousPage": "Precedent", - "nextPage": "Suivant", - "enableEvent": "Activer {{event}}", - "disableEvent": "Desactiver {{event}}" - } - } - }, - "welcome": { - "name": "bienvenue", - "description": "Configurer les messages de bienvenue avec un panneau interactif.", - "examples": { - "slash": "Ouvrir le panneau de configuration des messages de bienvenue" - }, - "responses": { - "guildOnly": "Cette commande doit etre utilisee dans un serveur.", - "notOwner": "Seul l auteur de la commande peut utiliser ce panneau.", - "invalidSelection": "Selection invalide.", - "testSuccess": "Message de test envoye dans {{channel}}.", - "testDisabled": "Le module de bienvenue est desactive.", - "testMissingChannel": "Aucun salon configure. Utilise le bouton Salon.", - "testChannelUnavailable": "Le salon configure est introuvable ou non compatible.", - "testMissingPermissions": "Le bot n a pas la permission d envoyer des messages dans ce salon.", - "testFailed": "Impossible d envoyer le message de test. Reessaie plus tard." - }, - "ui": { - "embed": { - "title": "Configuration Welcome", - "description": "Regle le statut, le salon, le type de message et lance un test.", - "fields": { - "status": "Statut", - "channel": "Salon", - "type": "Type", - "channelPicker": "Selection du salon", - "autoRoles": "Roles auto" - } - }, - "status": { - "enabled": "🟢 Active", - "disabled": "🔴 Desactive" - }, - "channelNotConfigured": "#non-configure", - "autoRolesNotConfigured": "Aucun role auto configure", - "channelPickerPlaceholder": "Selectionne un salon de bienvenue", - "channelPickerHint": "Choisis le salon dans le select menu ci-dessous.", - "rolePickerPlaceholder": "Selectionne un ou plusieurs roles auto", - "rolePickerHint": "Choisis les roles dans le select menu ci-dessous pour les ajouter a la liste auto.", - "buttons": { - "toggle": "Toggle statut", - "channel": "Configurer salon", - "channelCancel": "Fermer select salon", - "roles": "Ajouter roles auto", - "rolesCancel": "Fermer select roles", - "rolesClear": "Vider roles auto", - "test": "Tester" - }, - "type": { - "placeholder": "Choisir un type de message", - "options": { - "simple": { - "label": "Message simple", - "description": "Envoie un message texte classique" - }, - "embed": { - "label": "Embed", - "description": "Envoie un embed de bienvenue" - }, - "container": { - "label": "Container (V2)", - "description": "Envoie un container Components V2" - }, - "image": { - "label": "Image", - "description": "Envoie un embed avec image" - } - } - } - }, - "templates": { - "simple": "🎉 Bienvenue {{user}} sur **{{guild}}** !", - "embedTitle": "Bienvenue !", - "embedDescription": "{{user}} vient de rejoindre **{{guild}}**.", - "containerTitle": "Bienvenue", - "containerDescription": "{{user}} vient de rejoindre **{{guild}}**.", - "imageTitle": "Bienvenue", - "imageDescription": "sur le serveur Discord {{guild}}" - } - }, - "goodbye": { - "name": "aurevoir", - "description": "Configurer les messages d au revoir avec un panneau interactif.", - "examples": { - "slash": "Ouvrir le panneau de configuration des messages d au revoir" - }, - "responses": { - "guildOnly": "Cette commande doit etre utilisee dans un serveur.", - "notOwner": "Seul l auteur de la commande peut utiliser ce panneau.", - "invalidSelection": "Selection invalide.", - "testSuccess": "Message de test envoye dans {{channel}}.", - "testDisabled": "Le module d au revoir est desactive.", - "testMissingChannel": "Aucun salon configure. Utilise le bouton Salon.", - "testChannelUnavailable": "Le salon configure est introuvable ou non compatible.", - "testMissingPermissions": "Le bot n a pas la permission d envoyer des messages dans ce salon.", - "testFailed": "Impossible d envoyer le message de test. Reessaie plus tard." - }, - "ui": { - "embed": { - "title": "Configuration Goodbye", - "description": "Regle le statut, le salon, le type de message et lance un test.", - "fields": { - "status": "Statut", - "channel": "Salon", - "type": "Type", - "channelPicker": "Selection du salon" - } - }, - "status": { - "enabled": "🟢 Active", - "disabled": "🔴 Desactive" - }, - "channelNotConfigured": "#non-configure", - "channelPickerPlaceholder": "Selectionne un salon d au revoir", - "channelPickerHint": "Choisis le salon dans le select menu ci-dessous.", - "buttons": { - "toggle": "Toggle statut", - "channel": "Configurer salon", - "channelCancel": "Fermer select salon", - "test": "Tester" - }, - "type": { - "placeholder": "Choisir un type de message", - "options": { - "simple": { - "label": "Message simple", - "description": "Envoie un message texte classique" - }, - "embed": { - "label": "Embed", - "description": "Envoie un embed d au revoir" - }, - "container": { - "label": "Container (V2)", - "description": "Envoie un container Components V2" - }, - "image": { - "label": "Image", - "description": "Envoie un embed avec image" - } - } - } - }, - "templates": { - "simple": "👋 {{user}} a quitte **{{guild}}**.", - "embedTitle": "Au revoir", - "embedDescription": "{{user}} a quitte **{{guild}}**.", - "containerTitle": "Au revoir", - "containerDescription": "{{user}} a quitte **{{guild}}**.", - "imageTitle": "Au revoir", - "imageDescription": "du serveur Discord {{guild}}" - } - }, - "help": { - "name": "aide", - "description": "Afficher la liste des commandes ou les details d une commande.", - "args": { - "command": "Nom ou trigger de commande optionnel" - }, - "examples": { - "basic": "Afficher toutes les commandes", - "single": "Afficher les details d une commande" - }, - "errors": { - "notFound": "Aucune commande ne correspond a {{query}}." - }, - "embed": { - "title": "Aide Commandes", - "description": "Utilise {{usage}} pour inspecter une commande.", - "categoryEmpty": "Aucune commande dans cette categorie.", - "detailsTitle": "Commande: {{name}}", - "detailsDescription": "{{description}}", - "footer": "Categorie: {{source}}", - "fields": { - "usage": "Usage", - "arguments": "Arguments", - "examples": "Exemples" - } - }, - "labels": { - "prefix": "Prefix", - "slash": "Slash", - "required": "requis", - "optional": "optionnel", - "noArgs": "Aucun argument.", - "noExamples": "Aucun exemple." - } - } - } -} diff --git a/apps/bot/src/legacy/i18n/index.ts b/apps/bot/src/legacy/i18n/index.ts index 4720808..1d5255d 100644 --- a/apps/bot/src/legacy/i18n/index.ts +++ b/apps/bot/src/legacy/i18n/index.ts @@ -9,18 +9,52 @@ const DISCORD_LOCALE_MAP: Record = { en: "en", "en-us": "en", "en-gb": "en", - fr: "fr", - "fr-fr": "fr", es: "es", "es-es": "es", "es-419": "es", + de: "de", + "de-de": "de", + ja: "ja", + "ja-jp": "ja", + fr: "fr", + "fr-fr": "fr", + pt: "pt", + "pt-br": "pt", + "pt-pt": "pt", + ru: "ru", + "ru-ru": "ru", + it: "it", + "it-it": "it", + nl: "nl", + "nl-nl": "nl", + pl: "pl", + "pl-pl": "pl", + zh: "zh", + "zh-cn": "zh", + "zh-hans": "zh", + "zh-tw": "zh", + "zh-hant": "zh", + hi: "hi", + "hi-in": "hi", + ar: "ar", + "ar-sa": "ar", + bn: "bn", + "bn-bd": "bn", + "bn-in": "bn", + id: "id", + "id-id": "id", + tr: "tr", + "tr-tr": "tr", }; const CURRENT_DIR = path.dirname(fileURLToPath(import.meta.url)); const LOCALE_DIR_CANDIDATES = [ + path.resolve(CURRENT_DIR, "..", "..", "..", "locales"), + path.resolve(process.cwd(), "apps", "bot", "locales"), + path.resolve(process.cwd(), "locales"), CURRENT_DIR, - path.resolve(process.cwd(), "src", "i18n"), - path.resolve(process.cwd(), "build", "i18n"), + path.resolve(process.cwd(), "src", "legacy", "i18n"), + path.resolve(process.cwd(), "dist", "legacy", "i18n"), ]; const resolveLocaleFilePath = (lang: SupportedLang): string => { @@ -36,6 +70,7 @@ const resolveLocaleFilePath = (lang: SupportedLang): string => { export class I18nService { private readonly dictionaries: Record; + private readonly fallbackLang: SupportedLang = "en"; public constructor(private readonly defaultLang: SupportedLang) { this.dictionaries = this.loadDictionaries(); @@ -43,7 +78,7 @@ export class I18nService { public resolveLang(input?: string | null): SupportedLang { if (!input) { - return this.defaultLang; + return this.fallbackLang; } const normalized = input.toLowerCase(); @@ -59,14 +94,22 @@ export class I18nService { return fromShort; } - return this.defaultLang; + return this.fallbackLang; } public t(lang: SupportedLang, key: string, vars: TranslationVars = {}): string { const fromLang = this.lookup(this.dictionaries[lang], key); + const fromFallback = this.lookup(this.dictionaries[this.fallbackLang], key); const fromDefault = this.lookup(this.dictionaries[this.defaultLang], key); - const template = typeof fromLang === "string" ? fromLang : typeof fromDefault === "string" ? fromDefault : key; + const template = + typeof fromLang === "string" + ? fromLang + : typeof fromFallback === "string" + ? fromFallback + : typeof fromDefault === "string" + ? fromDefault + : key; return this.format(template, vars); } @@ -82,6 +125,11 @@ export class I18nService { return fromLang; } + const fromFallback = this.lookup(this.dictionaries[this.fallbackLang], key); + if (typeof fromFallback === "string" && fromFallback.length > 0) { + return fromFallback; + } + const fromDefault = this.lookup(this.dictionaries[this.defaultLang], key); if (typeof fromDefault === "string" && fromDefault.length > 0) { return fromDefault; @@ -101,6 +149,11 @@ export class I18nService { return fromLang; } + const fromFallback = this.lookup(this.dictionaries[this.fallbackLang], key); + if (this.isObject(fromFallback)) { + return fromFallback; + } + const fromDefault = this.lookup(this.dictionaries[this.defaultLang], key); if (this.isObject(fromDefault)) { return fromDefault; diff --git a/apps/bot/src/legacy/types/command.ts b/apps/bot/src/legacy/types/command.ts index a439949..82db8ea 100644 --- a/apps/bot/src/legacy/types/command.ts +++ b/apps/bot/src/legacy/types/command.ts @@ -15,7 +15,24 @@ import type { User, } from "discord.js"; -export const SUPPORTED_LANGS = ["en", "fr", "es"] as const; +export const SUPPORTED_LANGS = [ + "en", + "es", + "de", + "ja", + "fr", + "pt", + "ru", + "it", + "nl", + "pl", + "zh", + "hi", + "ar", + "bn", + "id", + "tr", +] as const; export type SupportedLang = (typeof SUPPORTED_LANGS)[number]; export type CommandSource = "prefix" | "slash"; diff --git a/apps/bot/src/legacy/types/logs.ts b/apps/bot/src/legacy/types/logs.ts index 75b91ac..a8bd6bf 100644 --- a/apps/bot/src/legacy/types/logs.ts +++ b/apps/bot/src/legacy/types/logs.ts @@ -69,6 +69,8 @@ export interface LogRuntimeDispatchInput { summary: string; details?: string[]; color?: number; + title?: string; + detailsTitle?: string; } export interface LogPanelCustomIds { diff --git a/apps/bot/src/legacy/types/memberMessages.ts b/apps/bot/src/legacy/types/memberMessages.ts index 6f4be7c..f15a607 100644 --- a/apps/bot/src/legacy/types/memberMessages.ts +++ b/apps/bot/src/legacy/types/memberMessages.ts @@ -36,7 +36,7 @@ export interface DispatchMemberMessageResult { export interface DispatchMemberMessageInput { client: Client; - i18n?: I18nService; + i18n: I18nService; guild: Guild; user: User; kind: MemberMessageKind; diff --git a/apps/web/app/[locale]/layout.tsx b/apps/web/app/[locale]/layout.tsx index e7aaaba..c7fbc78 100644 --- a/apps/web/app/[locale]/layout.tsx +++ b/apps/web/app/[locale]/layout.tsx @@ -1,12 +1,10 @@ import type { Metadata } from "next"; import { IBM_Plex_Mono, Space_Grotesk } from "next/font/google"; import { hasLocale, NextIntlClientProvider } from "next-intl"; -import { getMessages } from "next-intl/server"; +import { getMessages, getTranslations } from "next-intl/server"; import { notFound } from "next/navigation"; -import { routing, type AppLocale } from "../../i18n/routing"; -import enMessages from "../../messages/en.json"; -import frMessages from "../../messages/fr.json"; +import { RTL_LOCALES, routing } from "../../i18n/routing"; import "../globals.css"; const headingFont = Space_Grotesk({ @@ -21,11 +19,6 @@ const monoFont = IBM_Plex_Mono({ weight: ["400", "500"], }); -const metadataByLocale: Record = { - en: enMessages.metadata, - fr: frMessages.metadata, -}; - export function generateStaticParams() { return routing.locales.map((locale) => ({ locale })); } @@ -39,7 +32,7 @@ export async function generateMetadata({ const locale = hasLocale(routing.locales, candidateLocale) ? candidateLocale : routing.defaultLocale; - const metadata = metadataByLocale[locale]; + const t = await getTranslations({ locale, namespace: "metadata" }); const languages = routing.locales.reduce>( (accumulator, currentLocale) => { @@ -50,8 +43,8 @@ export async function generateMetadata({ ); return { - title: metadata.title, - description: metadata.description, + title: t("title"), + description: t("description"), alternates: { canonical: `/${locale}`, languages, @@ -73,9 +66,10 @@ export default async function LocaleLayout({ } const messages = await getMessages(); + const direction = RTL_LOCALES.includes(locale as (typeof RTL_LOCALES)[number]) ? "rtl" : "ltr"; return ( - + {children} diff --git a/apps/web/components/LanguageSwitcher.tsx b/apps/web/components/LanguageSwitcher.tsx index 09c47a8..5acd02b 100644 --- a/apps/web/components/LanguageSwitcher.tsx +++ b/apps/web/components/LanguageSwitcher.tsx @@ -7,8 +7,8 @@ import { useTransition } from "react"; import { useT } from "../i18n/client"; import { usePathname, useRouter } from "../i18n/navigation"; import { routing, type AppLocale } from "../i18n/routing"; -import { Switch } from "./ui/Switch"; -import { cn } from "./ui/cn"; + +type LocaleLabelKey = `localeSwitcher.locales.${AppLocale}`; export default function LanguageSwitcher() { const t = useT(); @@ -18,10 +18,12 @@ export default function LanguageSwitcher() { const searchParams = useSearchParams(); const [isPending, startTransition] = useTransition(); - const localeLabels: Record = { - en: t("localeSwitcher.locales.en"), - fr: t("localeSwitcher.locales.fr"), - }; + const localeLabels = Object.fromEntries( + routing.locales.map((availableLocale) => [ + availableLocale, + t(`localeSwitcher.locales.${availableLocale}` as LocaleLabelKey), + ]), + ) as Record; const handleLocaleChange = (nextLocale: AppLocale) => { if (nextLocale === locale) { @@ -35,57 +37,22 @@ export default function LanguageSwitcher() { }); }; - if (routing.locales.length !== 2) { - return ( - - ); - } - - const [leftLocale, rightLocale] = routing.locales; - const checked = locale === rightLocale; - return ( -
- - {localeLabels[leftLocale]} - - + {t("localeSwitcher.label")} + + ); } diff --git a/apps/web/i18n/next-intl.d.ts b/apps/web/i18n/next-intl.d.ts index b6f9695..15e27ae 100644 --- a/apps/web/i18n/next-intl.d.ts +++ b/apps/web/i18n/next-intl.d.ts @@ -1,6 +1,6 @@ import "next-intl"; -import enMessages from "../messages/en.json"; +import enMessages from "../locales/en.json"; import { routing } from "./routing"; diff --git a/apps/web/i18n/request.ts b/apps/web/i18n/request.ts index 048fdbc..d92265d 100644 --- a/apps/web/i18n/request.ts +++ b/apps/web/i18n/request.ts @@ -6,8 +6,22 @@ import { routing, type AppLocale } from "./routing"; type Messages = Record; const messageLoaders: Record Promise> = { - en: async () => (await import("../messages/en.json")).default as Messages, - fr: async () => (await import("../messages/fr.json")).default as Messages, + en: async () => (await import("../locales/en.json")).default as Messages, + es: async () => (await import("../locales/es.json")).default as Messages, + de: async () => (await import("../locales/de.json")).default as Messages, + ja: async () => (await import("../locales/ja.json")).default as Messages, + fr: async () => (await import("../locales/fr.json")).default as Messages, + pt: async () => (await import("../locales/pt.json")).default as Messages, + ru: async () => (await import("../locales/ru.json")).default as Messages, + it: async () => (await import("../locales/it.json")).default as Messages, + nl: async () => (await import("../locales/nl.json")).default as Messages, + pl: async () => (await import("../locales/pl.json")).default as Messages, + zh: async () => (await import("../locales/zh.json")).default as Messages, + hi: async () => (await import("../locales/hi.json")).default as Messages, + ar: async () => (await import("../locales/ar.json")).default as Messages, + bn: async () => (await import("../locales/bn.json")).default as Messages, + id: async () => (await import("../locales/id.json")).default as Messages, + tr: async () => (await import("../locales/tr.json")).default as Messages, }; const isObject = (value: unknown): value is Record => { diff --git a/apps/web/i18n/routing.ts b/apps/web/i18n/routing.ts index 88696e2..cec2dce 100644 --- a/apps/web/i18n/routing.ts +++ b/apps/web/i18n/routing.ts @@ -1,9 +1,31 @@ import { defineRouting } from "next-intl/routing"; +export const APP_LOCALES = [ + "en", + "es", + "de", + "ja", + "fr", + "pt", + "ru", + "it", + "nl", + "pl", + "zh", + "hi", + "ar", + "bn", + "id", + "tr", +] as const; + +export const RTL_LOCALES = ["ar"] as const; + export const routing = defineRouting({ - locales: ["en", "fr"], + locales: [...APP_LOCALES], defaultLocale: "en", localePrefix: "always", + localeDetection: true, }); export type AppLocale = (typeof routing.locales)[number]; \ No newline at end of file diff --git a/apps/web/locales/ar.json b/apps/web/locales/ar.json new file mode 100644 index 0000000..15b5b3c --- /dev/null +++ b/apps/web/locales/ar.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - استضافة بوتات Discord (ابتداءً من 2 يورو/شهريًا لكل بوت)", + "description": "منصة SaaS متعددة المستخدمين لاستضافة وإدارة بوتات Discord الخاصة بك مع أوامر تلقائية، components v2، دعم متعدد اللغات ومتغيرات ديناميكية." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "الرئيسية", + "dashboard": "لوحة التحكم", + "login": "تسجيل الدخول" + }, + "localeSwitcher": { + "label": "اللغة", + "locales": { + "en": "الإنجليزية", + "es": "الإسبانية", + "de": "الألمانية", + "ja": "اليابانية", + "fr": "الفرنسية", + "pt": "البرتغالية", + "ru": "الروسية", + "it": "الإيطالية", + "nl": "الهولندية", + "pl": "البولندية", + "zh": "الصينية", + "hi": "الهندية", + "ar": "العربية", + "bn": "البنغالية", + "id": "الإندونيسية", + "tr": "التركية" + } + }, + "hero": { + "kicker": "منصة SaaS لبوتات Discord", + "title": "Shadow - استضف بوتات Discord الخاصة بك (ابتداءً من 2 يورو/شهريًا لكل بوت)", + "description": "استضف وأدر بوتاتك باستخدام أوامر تلقائية (prefix و slash)، components v2، مساعدة تلقائية، دعم متعدد اللغات ومتغيرات ديناميكية.", + "ctaStart": "ابدأ - 2 يورو/شهريًا", + "ctaLogin": "تسجيل الدخول", + "bots": { + "moderation": "الإشراف", + "welcome": "ترحيب", + "analytics": "تحليلات" + }, + "status": { + "online": "متصل", + "stopped": "متوقف" + }, + "card": { + "title": "عرض مباشر للحالة", + "subtitle": "تابع كل نسخة من البوت من خلال واجهة تشغيل موحدة.", + "instanceMetrics": "النسخ: 1 | المعالج 35%", + "manage": "إدارة", + "preview": "عرض مباشر لحالة بوتاتك" + } + }, + "features": { + "title": "الميزات الرئيسية", + "subtitle": "كل ما تحتاجه لإدارة بوتاتك في بيئة الإنتاج.", + "items": { + "autoCommands": { + "title": "أوامر تلقائية", + "description": "يتم إنشاء أوامر prefix و slash ومزامنتها تلقائيًا لكل بوت." + }, + "componentsV2": { + "title": "Components v2", + "description": "دعم كامل لـ components v2 والتفاعلات المتقدمة والموثوقة." + }, + "autoHelp": { + "title": "مساعدة تلقائية", + "description": "مساعدة ووثائق للأوامر يتم إنشاؤها تلقائيًا." + }, + "multiLanguage": { + "title": "متعدد اللغات", + "description": "قم بترجمة الرسائل وعناوين الأوامر ثم فعّل عدة لغات بسهولة." + }, + "dynamicVariables": { + "title": "متغيرات ديناميكية", + "description": "قوالب ديناميكية مع متغيرات مثل bot_latency و guild_count وغيرها." + }, + "hosting": { + "title": "الاستضافة والتوسع", + "description": "خطط بسيطة تبدأ من 2 يورو/شهريًا لكل بوت مع المراقبة وقابلية التوسع." + } + } + }, + "howItWorks": { + "title": "كيف يعمل", + "subtitle": "ثلاث خطوات بسيطة للتحكم في بوتاتك.", + "steps": { + "addBot": { + "title": "إضافة بوت", + "description": "قم بربط تطبيق Discord الخاص بك عبر OAuth وقم بتسجيله في Shadow." + }, + "dashboard": { + "title": "الإدارة من لوحة التحكم", + "description": "الوصول إلى الأوامر والسجلات والإعدادات من واجهة مركزية." + }, + "realtime": { + "title": "تحكم في الوقت الحقيقي", + "description": "ابدأ، أوقف، وقم بتوسيع بوتاتك فورًا حسب الطلب." + } + } + }, + "footer": { + "tagline": "استضافة بوتات Discord - خطط تبدأ من 2 يورو/شهريًا لكل بوت", + "github": "GitHub", + "copyright": "حقوق النشر {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - استضافة بوتات Discord", + "title": "قم بربط حساب Discord الخاص بك", + "description": "قم بتسجيل الدخول عبر OAuth2 للوصول إلى مساحتك واستضافة بوتاتك.", + "cta": "المتابعة باستخدام Discord" + }, + "dashboard": { + "loading": "جارٍ تحميل لوحة التحكم...", + "sessionRequired": "جلسة مطلوبة", + "loginRequired": "تسجيل الدخول مطلوب", + "loginDescription": "لوحة التحكم الخاصة بك محمية. قم بتسجيل الدخول عبر Discord لإدارة بوتاتك.", + "loginCta": "تسجيل الدخول", + "tenant": "Tenant {tenantId}", + "role": "الدور: {role}", + "roles": { + "owner": "المالك", + "member": "عضو" + }, + "logout": "تسجيل الخروج", + "addBot": { + "title": "إضافة بوت", + "description": "يتم التحقق من التوكن عبر API ثم تشفيره قبل التخزين.", + "tokenLabel": "توكن بوت Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "اسم العرض (اختياري)", + "displayNamePlaceholder": "بوت دعم EU", + "submit": "إضافة البوت", + "submitPending": "جارٍ التحقق..." + }, + "bots": { + "title": "بوتاتي", + "refresh": "تحديث", + "empty": "لا يوجد أي بوت لهذا tenant.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "آخر خطأ: {message}" + }, + "status": { + "stopped": "متوقف", + "starting": "جارٍ التشغيل", + "running": "متصل", + "stopping": "جارٍ الإيقاف", + "error": "خطأ" + }, + "actions": { + "start": "تشغيل", + "stop": "إيقاف", + "restart": "إعادة تشغيل" + }, + "errors": { + "fetchSession": "تعذر جلب جلسة المستخدم", + "fetchBots": "تعذر جلب قائمة البوتات", + "unexpected": "خطأ غير متوقع", + "addBot": "تعذر إضافة البوت", + "actionFailed": "فشل الإجراء: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/bn.json b/apps/web/locales/bn.json new file mode 100644 index 0000000..ec34fdc --- /dev/null +++ b/apps/web/locales/bn.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - ডিসকর্ড বট হোস্টিং (প্রতি বট প্রতি মাসে ২ ইউরো থেকে)", + "description": "আপনার ডিসকর্ড বট হোস্ট এবং পরিচালনা করার জন্য মাল্টি-টেন্যান্ট SaaS প্ল্যাটফর্ম, যেখানে স্বয়ংক্রিয় কমান্ড, components v2, বহু-ভাষা এবং ডায়নামিক ভেরিয়েবল রয়েছে।" + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "হোম", + "dashboard": "ড্যাশবোর্ড", + "login": "লগইন" + }, + "localeSwitcher": { + "label": "ভাষা", + "locales": { + "en": "ইংরেজি", + "es": "স্প্যানিশ", + "de": "জার্মান", + "ja": "জাপানি", + "fr": "ফরাসি", + "pt": "পর্তুগিজ", + "ru": "রাশিয়ান", + "it": "ইতালীয়", + "nl": "ডাচ", + "pl": "পোলিশ", + "zh": "চীনা", + "hi": "হিন্দি", + "ar": "আরবি", + "bn": "বাংলা", + "id": "ইন্দোনেশীয়", + "tr": "তুর্কি" + } + }, + "hero": { + "kicker": "ডিসকর্ড বটের জন্য SaaS প্ল্যাটফর্ম", + "title": "Shadow - আপনার ডিসকর্ড বট হোস্ট করুন (প্রতি বট প্রতি মাসে ২ ইউরো থেকে)", + "description": "স্বয়ংক্রিয় কমান্ড (prefix এবং slash), components v2, স্বয়ংক্রিয় সাহায্য, বহু-ভাষা সমর্থন এবং ডায়নামিক ভেরিয়েবলসহ আপনার বট হোস্ট এবং পরিচালনা করুন।", + "ctaStart": "শুরু করুন - ২ ইউরো/মাস", + "ctaLogin": "লগইন", + "bots": { + "moderation": "মডারেশন", + "welcome": "স্বাগতম", + "analytics": "অ্যানালিটিক্স" + }, + "status": { + "online": "অনলাইন", + "stopped": "বন্ধ" + }, + "card": { + "title": "লাইভ ফ্লিট ওভারভিউ", + "subtitle": "একটি অপারেশনাল ইন্টারফেস থেকে প্রতিটি বট ইনস্ট্যান্স পর্যবেক্ষণ করুন।", + "instanceMetrics": "ইনস্ট্যান্স: ১ | CPU ৩৫%", + "manage": "পরিচালনা করুন", + "preview": "আপনার বটের স্ট্যাটাসের রিয়েল-টাইম প্রিভিউ" + } + }, + "features": { + "title": "প্রধান বৈশিষ্ট্য", + "subtitle": "প্রোডাকশনে আপনার বট পরিচালনার জন্য যা কিছু দরকার।", + "items": { + "autoCommands": { + "title": "স্বয়ংক্রিয় কমান্ড", + "description": "প্রতিটি বটের জন্য prefix এবং slash কমান্ড স্বয়ংক্রিয়ভাবে তৈরি এবং সিঙ্ক করা হয়।" + }, + "componentsV2": { + "title": "Components v2", + "description": "components v2 এবং নির্ভরযোগ্য রিচ ইন্টারঅ্যাকশনের সম্পূর্ণ সমর্থন।" + }, + "autoHelp": { + "title": "স্বয়ংক্রিয় সাহায্য", + "description": "কমান্ডগুলোর জন্য স্বয়ংক্রিয়ভাবে তৈরি সাহায্য এবং ডকুমেন্টেশন।" + }, + "multiLanguage": { + "title": "বহু-ভাষা", + "description": "মেসেজ এবং কমান্ড লেবেল অনুবাদ করুন এবং সহজে একাধিক ভাষা সক্রিয় করুন।" + }, + "dynamicVariables": { + "title": "ডায়নামিক ভেরিয়েবল", + "description": "bot_latency, guild_count ইত্যাদি ভেরিয়েবলসহ ডায়নামিক টেমপ্লেট।" + }, + "hosting": { + "title": "হোস্টিং এবং স্কেলেবিলিটি", + "description": "প্রতি বট ২ ইউরো/মাস থেকে সহজ প্ল্যান, মনিটরিং এবং স্কেলিংসহ।" + } + } + }, + "howItWorks": { + "title": "কিভাবে কাজ করে", + "subtitle": "আপনার বট নিয়ন্ত্রণে আনতে তিনটি সহজ ধাপ।", + "steps": { + "addBot": { + "title": "একটি বট যোগ করুন", + "description": "OAuth এর মাধ্যমে আপনার Discord অ্যাপ সংযুক্ত করুন এবং Shadow-এ নিবন্ধন করুন।" + }, + "dashboard": { + "title": "ড্যাশবোর্ড থেকে পরিচালনা করুন", + "description": "একটি কেন্দ্রীভূত ইন্টারফেস থেকে কমান্ড, লগ এবং সেটিংস অ্যাক্সেস করুন।" + }, + "realtime": { + "title": "রিয়েল-টাইম নিয়ন্ত্রণ", + "description": "চাহিদা অনুযায়ী আপনার বট শুরু, বন্ধ এবং স্কেল করুন।" + } + } + }, + "footer": { + "tagline": "ডিসকর্ড বট হোস্টিং - প্রতি বট ২ ইউরো/মাস থেকে প্ল্যান", + "github": "GitHub", + "copyright": "কপিরাইট {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - ডিসকর্ড বট হোস্টিং", + "title": "আপনার Discord অ্যাকাউন্ট সংযুক্ত করুন", + "description": "OAuth2 এর মাধ্যমে লগইন করে আপনার ড্যাশবোর্ডে প্রবেশ করুন এবং বট হোস্ট করুন।", + "cta": "Discord দিয়ে চালিয়ে যান" + }, + "dashboard": { + "loading": "ড্যাশবোর্ড লোড হচ্ছে...", + "sessionRequired": "সেশন প্রয়োজন", + "loginRequired": "লগইন প্রয়োজন", + "loginDescription": "আপনার ড্যাশবোর্ড সুরক্ষিত। আপনার বট পরিচালনার জন্য Discord দিয়ে লগইন করুন।", + "loginCta": "লগইন", + "tenant": "টেন্যান্ট {tenantId}", + "role": "ভূমিকা: {role}", + "roles": { + "owner": "মালিক", + "member": "সদস্য" + }, + "logout": "লগআউট", + "addBot": { + "title": "একটি বট যোগ করুন", + "description": "টোকেন API পাশে যাচাই করা হয় এবং সংরক্ষণের আগে এনক্রিপ্ট করা হয়।", + "tokenLabel": "Discord বট টোকেন", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "প্রদর্শন নাম (ঐচ্ছিক)", + "displayNamePlaceholder": "EU সাপোর্ট বট", + "submit": "বট যোগ করুন", + "submitPending": "যাচাই চলছে..." + }, + "bots": { + "title": "আমার বটসমূহ", + "refresh": "রিফ্রেশ", + "empty": "এই টেন্যান্টের জন্য কোনো বট নেই।", + "discordId": "Discord ID: {discordBotId}", + "lastError": "সর্বশেষ ত্রুটি: {message}" + }, + "status": { + "stopped": "বন্ধ", + "starting": "শুরু হচ্ছে", + "running": "চলমান", + "stopping": "বন্ধ হচ্ছে", + "error": "ত্রুটি" + }, + "actions": { + "start": "শুরু করুন", + "stop": "বন্ধ করুন", + "restart": "পুনরায় চালু করুন" + }, + "errors": { + "fetchSession": "ব্যবহারকারীর সেশন আনতে ব্যর্থ", + "fetchBots": "বট তালিকা আনতে ব্যর্থ", + "unexpected": "অপ্রত্যাশিত ত্রুটি", + "addBot": "বট যোগ করা যায়নি", + "actionFailed": "অ্যাকশন ব্যর্থ: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/de.json b/apps/web/locales/de.json new file mode 100644 index 0000000..d9e3b7c --- /dev/null +++ b/apps/web/locales/de.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hosting von Discord-Bots (ab 2 EUR/Monat pro Bot)", + "description": "Multi-Tenant-SaaS-Plattform zum Hosten und Verwalten deiner Discord-Bots mit automatischen Befehlen, Components V2, Mehrsprachigkeit und dynamischen Variablen." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Startseite", + "dashboard": "Dashboard", + "login": "Anmelden" + }, + "localeSwitcher": { + "label": "Sprache", + "locales": { + "en": "Englisch", + "es": "Spanisch", + "de": "Deutsch", + "ja": "Japanisch", + "fr": "Französisch", + "pt": "Portugiesisch", + "ru": "Russisch", + "it": "Italienisch", + "nl": "Niederländisch", + "pl": "Polnisch", + "zh": "Chinesisch", + "hi": "Hindi", + "ar": "Arabisch", + "bn": "Bengalisch", + "id": "Indonesisch", + "tr": "Türkisch" + } + }, + "hero": { + "kicker": "SaaS-Plattform für Discord-Bots", + "title": "Shadow - Hoste deine Discord-Bots (ab 2 EUR/Monat pro Bot)", + "description": "Hoste und verwalte deine Bots mit automatischen Befehlen (Prefix und Slash), Components V2, automatischer Hilfe, Mehrsprachigkeit und dynamischen Variablen.", + "ctaStart": "Starten - 2 EUR/Monat", + "ctaLogin": "Anmelden", + "bots": { + "moderation": "Moderation", + "welcome": "Willkommen", + "analytics": "Analysen" + }, + "status": { + "online": "Online", + "stopped": "Gestoppt" + }, + "card": { + "title": "Live-Übersicht der Flotte", + "subtitle": "Überwache jede Bot-Instanz von einer zentralen Oberfläche aus.", + "instanceMetrics": "Instanzen: 1 | CPU 35%", + "manage": "Verwalten", + "preview": "Echtzeit-Übersicht des Bot-Status" + } + }, + "features": { + "title": "Hauptfunktionen", + "subtitle": "Alles, was du brauchst, um deine Bots produktiv zu betreiben.", + "items": { + "autoCommands": { + "title": "Automatische Befehle", + "description": "Prefix- und Slash-Befehle werden automatisch für jeden Bot generiert und synchronisiert." + }, + "componentsV2": { + "title": "Components V2", + "description": "Vollständige Unterstützung für Components V2 und zuverlässige, reichhaltige Interaktionen." + }, + "autoHelp": { + "title": "Automatische Hilfe", + "description": "Automatisch generierte Hilfe und Dokumentation für Befehle." + }, + "multiLanguage": { + "title": "Mehrsprachigkeit", + "description": "Übersetze Nachrichten und Befehlslabels und aktiviere einfach mehrere Sprachen." + }, + "dynamicVariables": { + "title": "Dynamische Variablen", + "description": "Dynamische Templates mit Variablen wie bot_latency, guild_count und mehr." + }, + "hosting": { + "title": "Hosting & Skalierbarkeit", + "description": "Einfache Pläne ab 2 EUR/Monat pro Bot mit integriertem Monitoring und Skalierung." + } + } + }, + "howItWorks": { + "title": "So funktioniert es", + "subtitle": "Drei einfache Schritte, um die Kontrolle über deine Bots zu übernehmen.", + "steps": { + "addBot": { + "title": "Bot hinzufügen", + "description": "Verbinde deine Discord-Anwendung über OAuth und registriere sie in Shadow." + }, + "dashboard": { + "title": "Im Dashboard verwalten", + "description": "Greife auf Befehle, Logs und Einstellungen über eine zentrale Oberfläche zu." + }, + "realtime": { + "title": "Echtzeit-Steuerung", + "description": "Starte, stoppe und skaliere deine Bots sofort nach Bedarf." + } + } + }, + "footer": { + "tagline": "Hosting von Discord-Bots - Pläne ab 2 EUR/Monat pro Bot", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hosting von Discord-Bots", + "title": "Verbinde dein Discord-Konto", + "description": "Authentifiziere dich über OAuth2, um auf deinen Bereich zuzugreifen und deine Bots zu hosten.", + "cta": "Mit Discord fortfahren" + }, + "dashboard": { + "loading": "Dashboard wird geladen...", + "sessionRequired": "Session erforderlich", + "loginRequired": "Anmeldung erforderlich", + "loginDescription": "Dein Dashboard ist geschützt. Melde dich mit Discord an, um deine Bots zu verwalten.", + "loginCta": "Anmelden", + "tenant": "Mandant {tenantId}", + "role": "Rolle: {role}", + "roles": { + "owner": "Besitzer", + "member": "Mitglied" + }, + "logout": "Abmelden", + "addBot": { + "title": "Bot hinzufügen", + "description": "Der Token wird serverseitig geprüft und vor der Speicherung verschlüsselt.", + "tokenLabel": "Discord Bot Token", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Anzeigename (optional)", + "displayNamePlaceholder": "Support Bot EU", + "submit": "Bot hinzufügen", + "submitPending": "Validierung läuft..." + }, + "bots": { + "title": "Meine Bots", + "refresh": "Aktualisieren", + "empty": "Keine Bots für diesen Mandanten vorhanden.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Letzter Fehler: {message}" + }, + "status": { + "stopped": "Gestoppt", + "starting": "Startet", + "running": "Online", + "stopping": "Wird gestoppt", + "error": "Fehler" + }, + "actions": { + "start": "Starten", + "stop": "Stoppen", + "restart": "Neustarten" + }, + "errors": { + "fetchSession": "Benutzersitzung konnte nicht geladen werden", + "fetchBots": "Bot-Liste konnte nicht geladen werden", + "unexpected": "Unerwarteter Fehler", + "addBot": "Bot konnte nicht hinzugefügt werden", + "actionFailed": "Aktion fehlgeschlagen: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/messages/en.json b/apps/web/locales/en.json similarity index 51% rename from apps/web/messages/en.json rename to apps/web/locales/en.json index c989aaa..aef6a02 100644 --- a/apps/web/messages/en.json +++ b/apps/web/locales/en.json @@ -1,7 +1,7 @@ { "metadata": { - "title": "Shadow - Discord bot hosting (EUR 2/month per bot)", - "description": "Multi-tenant SaaS platform to host and manage Discord bots with auto commands, components v2, multi-language support and dynamic variables." + "title": "Shadow - Discord bot hosting (from €2/month per bot)", + "description": "Multi-tenant SaaS platform to host and manage your Discord bots with automatic commands, components v2, multi-language support, and dynamic variables." }, "common": { "brand": "Shadow" @@ -9,21 +9,35 @@ "nav": { "home": "Home", "dashboard": "Dashboard", - "login": "Sign in" + "login": "Log in" }, "localeSwitcher": { "label": "Language", "locales": { "en": "English", - "fr": "Francais" + "es": "Spanish", + "de": "German", + "ja": "Japanese", + "fr": "French", + "pt": "Portuguese", + "ru": "Russian", + "it": "Italian", + "nl": "Dutch", + "pl": "Polish", + "zh": "Chinese", + "hi": "Hindi", + "ar": "Arabic", + "bn": "Bengali", + "id": "Indonesian", + "tr": "Turkish" } }, "hero": { - "kicker": "Discord bot hosting platform", - "title": "Shadow - Host your Discord bots (from EUR 2/month per bot)", - "description": "Host and manage your bots with auto commands (prefix and slash), components v2, automatic help, multi-language support and dynamic variables.", - "ctaStart": "Get started - EUR 2/month", - "ctaLogin": "Sign in", + "kicker": "SaaS platform for Discord bots", + "title": "Shadow - Host your Discord bots (from €2/month per bot)", + "description": "Host and manage your bots with automatic commands (prefix and slash), components v2, automatic help, multi-language support, and dynamic variables.", + "ctaStart": "Get started - €2/month", + "ctaLogin": "Log in", "bots": { "moderation": "Moderation", "welcome": "Welcome", @@ -34,20 +48,20 @@ "stopped": "Stopped" }, "card": { - "title": "Live fleet preview", - "subtitle": "Track every bot instance from one operational surface.", + "title": "Live fleet overview", + "subtitle": "Monitor every bot instance from a single operational dashboard.", "instanceMetrics": "Instances: 1 | CPU 35%", "manage": "Manage", - "preview": "Real-time preview of your bot status" + "preview": "Real-time preview of your bots' status" } }, "features": { - "title": "Main features", + "title": "Key features", "subtitle": "Everything you need to run your bots in production.", "items": { "autoCommands": { "title": "Automatic commands", - "description": "Prefix and slash commands are generated and synced automatically for each bot." + "description": "Prefix and slash commands are automatically generated and synchronized for each bot." }, "componentsV2": { "title": "Components v2", @@ -55,19 +69,19 @@ }, "autoHelp": { "title": "Automatic help", - "description": "Help and command documentation generated automatically." + "description": "Automatically generated command help and documentation." }, "multiLanguage": { "title": "Multi-language", - "description": "Translate messages and command labels, then enable multiple locales quickly." + "description": "Translate messages and command labels and easily enable multiple languages." }, "dynamicVariables": { "title": "Dynamic variables", - "description": "Dynamic templates with variables like bot_latency, guild_count and more." + "description": "Dynamic templates with variables like bot_latency, guild_count, and more." }, "hosting": { - "title": "Hosting and scalability", - "description": "Simple plans at EUR 2/month per bot with monitoring and scalability included." + "title": "Hosting & scalability", + "description": "Simple plans at €2/month per bot with built-in monitoring and scalability." } } }, @@ -77,56 +91,56 @@ "steps": { "addBot": { "title": "Add a bot", - "description": "Connect your Discord application with OAuth and register it in Shadow." + "description": "Connect your Discord application via OAuth and register it in Shadow." }, "dashboard": { - "title": "Manage from dashboard", - "description": "Access commands, logs and settings from one centralized interface." + "title": "Manage from the dashboard", + "description": "Access commands, logs, and settings from a centralized interface." }, "realtime": { "title": "Control in real time", - "description": "Start, stop and scale your bots instantly based on demand." + "description": "Start, stop, and scale your bots instantly based on demand." } } }, "footer": { - "tagline": "Discord bot hosting - plans from EUR 2/month per bot", + "tagline": "Discord bot hosting - plans from €2/month per bot", "github": "GitHub", "copyright": "Copyright {year} {brand}" }, "login": { "eyebrow": "Shadow - Discord bot hosting", "title": "Connect your Discord account", - "description": "Authenticate with OAuth2 to access your space and host your bots.", + "description": "Authenticate via OAuth2 to access your workspace and host your bots.", "cta": "Continue with Discord" }, "dashboard": { "loading": "Loading dashboard...", "sessionRequired": "Session required", - "loginRequired": "Sign in required", - "loginDescription": "Your dashboard is protected. Sign in with Discord to manage your bots.", - "loginCta": "Sign in", + "loginRequired": "Login required", + "loginDescription": "Your dashboard is protected. Log in with Discord to manage your bots.", + "loginCta": "Log in", "tenant": "Tenant {tenantId}", "role": "Role: {role}", "roles": { "owner": "Owner", "member": "Member" }, - "logout": "Sign out", + "logout": "Log out", "addBot": { "title": "Add a bot", - "description": "The token is validated by the API and encrypted before it is stored.", + "description": "The token is verified via the API then encrypted before storage.", "tokenLabel": "Discord bot token", "tokenPlaceholder": "MTIz...", "displayNameLabel": "Display name (optional)", - "displayNamePlaceholder": "Support bot EU", + "displayNamePlaceholder": "EU support bot", "submit": "Add bot", - "submitPending": "Validating..." + "submitPending": "Validation in progress..." }, "bots": { "title": "My bots", "refresh": "Refresh", - "empty": "No bot for this tenant.", + "empty": "No bots for this tenant.", "discordId": "Discord ID: {discordBotId}", "lastError": "Last error: {message}" }, @@ -143,11 +157,11 @@ "restart": "Restart" }, "errors": { - "fetchSession": "Unable to fetch user session", + "fetchSession": "Unable to retrieve user session", "fetchBots": "Unable to fetch bot list", "unexpected": "Unexpected error", - "addBot": "Unable to add bot", + "addBot": "Failed to add bot", "actionFailed": "Action failed: {action}" } } -} +} \ No newline at end of file diff --git a/apps/web/locales/es.json b/apps/web/locales/es.json new file mode 100644 index 0000000..0484c46 --- /dev/null +++ b/apps/web/locales/es.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Alojamiento de bots de Discord (desde 2 EUR/mes por bot)", + "description": "Plataforma SaaS multi-tenant para alojar y gestionar tus bots de Discord con comandos automáticos, components v2, multi-idioma y variables dinámicas." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Inicio", + "dashboard": "Panel", + "login": "Iniciar sesión" + }, + "localeSwitcher": { + "label": "Idioma", + "locales": { + "en": "Inglés", + "es": "Español", + "de": "Alemán", + "ja": "Japonés", + "fr": "Francés", + "pt": "Portugués", + "ru": "Ruso", + "it": "Italiano", + "nl": "Neerlandés", + "pl": "Polaco", + "zh": "Chino", + "hi": "Hindi", + "ar": "Árabe", + "bn": "Bengalí", + "id": "Indonesio", + "tr": "Turco" + } + }, + "hero": { + "kicker": "Plataforma SaaS para bots de Discord", + "title": "Shadow - Aloja tus bots de Discord (desde 2 EUR/mes por bot)", + "description": "Aloja y gestiona tus bots con comandos automáticos (prefix y slash), components v2, ayuda automática, soporte multi-idioma y variables dinámicas.", + "ctaStart": "Empezar - 2 EUR/mes", + "ctaLogin": "Iniciar sesión", + "bots": { + "moderation": "Moderación", + "welcome": "Bienvenida", + "analytics": "Analíticas" + }, + "status": { + "online": "En línea", + "stopped": "Detenido" + }, + "card": { + "title": "Vista en vivo de la flota", + "subtitle": "Sigue cada instancia de bot desde un panel operativo único.", + "instanceMetrics": "Instancias: 1 | CPU 35%", + "manage": "Gestionar", + "preview": "Vista en tiempo real del estado de tus bots" + } + }, + "features": { + "title": "Funciones principales", + "subtitle": "Todo lo necesario para gestionar tus bots en producción.", + "items": { + "autoCommands": { + "title": "Comandos automáticos", + "description": "Los comandos prefix y slash se generan y sincronizan automáticamente para cada bot." + }, + "componentsV2": { + "title": "Components v2", + "description": "Soporte completo de components v2 e interacciones ricas fiables." + }, + "autoHelp": { + "title": "Ayuda automática", + "description": "Ayuda y documentación de comandos generadas automáticamente." + }, + "multiLanguage": { + "title": "Multi-idioma", + "description": "Traduce mensajes y etiquetas de comandos y activa varios idiomas fácilmente." + }, + "dynamicVariables": { + "title": "Variables dinámicas", + "description": "Plantillas dinámicas con variables como bot_latency, guild_count y más." + }, + "hosting": { + "title": "Alojamiento y escalabilidad", + "description": "Planes simples desde 2 EUR/mes por bot con monitorización y escalabilidad incluidas." + } + } + }, + "howItWorks": { + "title": "Cómo funciona", + "subtitle": "Tres pasos simples para tomar el control de tus bots.", + "steps": { + "addBot": { + "title": "Añadir un bot", + "description": "Conecta tu aplicación de Discord mediante OAuth y regístrala en Shadow." + }, + "dashboard": { + "title": "Gestionar desde el panel", + "description": "Accede a comandos, registros y configuración desde una interfaz centralizada." + }, + "realtime": { + "title": "Control en tiempo real", + "description": "Inicia, detén y escala tus bots al instante según la demanda." + } + } + }, + "footer": { + "tagline": "Alojamiento de bots de Discord - planes desde 2 EUR/mes por bot", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Alojamiento de bots de Discord", + "title": "Conecta tu cuenta de Discord", + "description": "Autentícate mediante OAuth2 para acceder a tu espacio y alojar tus bots.", + "cta": "Continuar con Discord" + }, + "dashboard": { + "loading": "Cargando el panel...", + "sessionRequired": "Sesión requerida", + "loginRequired": "Inicio de sesión necesario", + "loginDescription": "Tu panel está protegido. Inicia sesión con Discord para administrar tus bots.", + "loginCta": "Iniciar sesión", + "tenant": "Tenant {tenantId}", + "role": "Rol: {role}", + "roles": { + "owner": "Propietario", + "member": "Miembro" + }, + "logout": "Cerrar sesión", + "addBot": { + "title": "Añadir un bot", + "description": "El token se verifica en la API y luego se cifra antes de almacenarse.", + "tokenLabel": "Token del bot de Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nombre visible (opcional)", + "displayNamePlaceholder": "Bot soporte EU", + "submit": "Añadir bot", + "submitPending": "Validando..." + }, + "bots": { + "title": "Mis bots", + "refresh": "Actualizar", + "empty": "No hay bots para este tenant.", + "discordId": "ID de Discord: {discordBotId}", + "lastError": "Último error: {message}" + }, + "status": { + "stopped": "Detenido", + "starting": "Iniciando", + "running": "En línea", + "stopping": "Deteniendo", + "error": "Error" + }, + "actions": { + "start": "Iniciar", + "stop": "Detener", + "restart": "Reiniciar" + }, + "errors": { + "fetchSession": "No se pudo obtener la sesión de usuario", + "fetchBots": "No se pudo obtener la lista de bots", + "unexpected": "Error inesperado", + "addBot": "No se pudo añadir el bot", + "actionFailed": "Acción fallida: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/fr.json b/apps/web/locales/fr.json new file mode 100644 index 0000000..e26e857 --- /dev/null +++ b/apps/web/locales/fr.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hébergement de bots Discord (à partir de 2 € / mois par bot)", + "description": "Plateforme SaaS multi-tenant pour héberger et gérer vos bots Discord avec commandes automatiques, components v2, multi-langue et variables dynamiques." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Accueil", + "dashboard": "Tableau de bord", + "login": "Se connecter" + }, + "localeSwitcher": { + "label": "Langue", + "locales": { + "en": "Anglais", + "es": "Espagnol", + "de": "Allemand", + "ja": "Japonais", + "fr": "Français", + "pt": "Portugais", + "ru": "Russe", + "it": "Italien", + "nl": "Néerlandais", + "pl": "Polonais", + "zh": "Chinois", + "hi": "Hindi", + "ar": "Arabe", + "bn": "Bengali", + "id": "Indonésien", + "tr": "Turc" + } + }, + "hero": { + "kicker": "Plateforme SaaS pour bots Discord", + "title": "Shadow - Hébergez vos bots Discord (à partir de 2 € / mois par bot)", + "description": "Hébergez et gérez vos bots avec commandes automatiques (préfixe et slash), components v2, aide automatique, support multi-langue et variables dynamiques.", + "ctaStart": "Commencer - 2 € / mois", + "ctaLogin": "Se connecter", + "bots": { + "moderation": "Modération", + "welcome": "Bienvenue", + "analytics": "Analytique" + }, + "status": { + "online": "En ligne", + "stopped": "Arrêté" + }, + "card": { + "title": "Aperçu en direct de la flotte", + "subtitle": "Suivez chaque instance de bot depuis une interface opérationnelle unique.", + "instanceMetrics": "Instances : 1 | CPU 35%", + "manage": "Gérer", + "preview": "Aperçu en temps réel du statut de vos bots" + } + }, + "features": { + "title": "Fonctionnalités principales", + "subtitle": "Tout ce qu’il faut pour piloter vos bots en production.", + "items": { + "autoCommands": { + "title": "Commandes automatiques", + "description": "Les commandes préfixe et slash sont générées et synchronisées automatiquement pour chaque bot." + }, + "componentsV2": { + "title": "Components v2", + "description": "Support complet des components v2 et des interactions riches et fiables." + }, + "autoHelp": { + "title": "Aide automatique", + "description": "Aide et documentation des commandes générées automatiquement." + }, + "multiLanguage": { + "title": "Multi-langue", + "description": "Traduisez les messages et les labels de commandes puis activez facilement plusieurs langues." + }, + "dynamicVariables": { + "title": "Variables dynamiques", + "description": "Templates dynamiques avec des variables comme bot_latency, guild_count et plus." + }, + "hosting": { + "title": "Hébergement et scalabilité", + "description": "Plans simples à 2 € / mois par bot avec monitoring et scalabilité inclus." + } + } + }, + "howItWorks": { + "title": "Comment ça marche", + "subtitle": "Trois étapes simples pour prendre le contrôle de vos bots.", + "steps": { + "addBot": { + "title": "Ajouter un bot", + "description": "Connectez votre application Discord via OAuth et enregistrez-la dans Shadow." + }, + "dashboard": { + "title": "Gérer depuis le tableau de bord", + "description": "Accédez aux commandes, aux logs et aux paramètres depuis une interface centralisée." + }, + "realtime": { + "title": "Contrôler en temps réel", + "description": "Démarrez, arrêtez et scalez vos bots instantanément selon la demande." + } + } + }, + "footer": { + "tagline": "Hébergement de bots Discord - plans à partir de 2 € / mois par bot", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hébergement de bots Discord", + "title": "Connectez votre compte Discord", + "description": "Authentifiez-vous via OAuth2 pour accéder à votre espace et héberger vos bots.", + "cta": "Continuer avec Discord" + }, + "dashboard": { + "loading": "Chargement du tableau de bord...", + "sessionRequired": "Session requise", + "loginRequired": "Connexion nécessaire", + "loginDescription": "Votre tableau de bord est protégé. Connectez-vous via Discord pour administrer vos bots.", + "loginCta": "Se connecter", + "tenant": "Tenant {tenantId}", + "role": "Rôle : {role}", + "roles": { + "owner": "Propriétaire", + "member": "Membre" + }, + "logout": "Se déconnecter", + "addBot": { + "title": "Ajouter un bot", + "description": "Le token est vérifié côté API puis chiffré avant stockage.", + "tokenLabel": "Token bot Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nom d’affichage (optionnel)", + "displayNamePlaceholder": "Bot support EU", + "submit": "Ajouter le bot", + "submitPending": "Validation en cours..." + }, + "bots": { + "title": "Mes bots", + "refresh": "Rafraîchir", + "empty": "Aucun bot pour ce tenant.", + "discordId": "ID Discord : {discordBotId}", + "lastError": "Dernière erreur : {message}" + }, + "status": { + "stopped": "Arrêté", + "starting": "Démarrage", + "running": "En ligne", + "stopping": "Arrêt", + "error": "Erreur" + }, + "actions": { + "start": "Démarrer", + "stop": "Arrêter", + "restart": "Redémarrer" + }, + "errors": { + "fetchSession": "Impossible de récupérer la session utilisateur", + "fetchBots": "Impossible de récupérer la liste des bots", + "unexpected": "Erreur inattendue", + "addBot": "Ajout du bot impossible", + "actionFailed": "Action impossible : {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/hi.json b/apps/web/locales/hi.json new file mode 100644 index 0000000..f249c1d --- /dev/null +++ b/apps/web/locales/hi.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Discord बॉट होस्टिंग (प्रति बॉट 2 EUR/माह से शुरू)", + "description": "अपने Discord बॉट्स को होस्ट और प्रबंधित करने के लिए मल्टी-टेनेंट SaaS प्लेटफ़ॉर्म, जिसमें ऑटोमैटिक कमांड्स, components v2, मल्टी-भाषा सपोर्ट और डायनामिक वेरिएबल्स शामिल हैं।" + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "मुखपृष्ठ", + "dashboard": "डैशबोर्ड", + "login": "लॉगिन करें" + }, + "localeSwitcher": { + "label": "भाषा", + "locales": { + "en": "अंग्रेज़ी", + "es": "स्पेनिश", + "de": "जर्मन", + "ja": "जापानी", + "fr": "फ़्रेंच", + "pt": "पुर्तगाली", + "ru": "रूसी", + "it": "इतालवी", + "nl": "डच", + "pl": "पोलिश", + "zh": "चीनी", + "hi": "हिंदी", + "ar": "अरबी", + "bn": "बांग्ला", + "id": "इंडोनेशियाई", + "tr": "तुर्की" + } + }, + "hero": { + "kicker": "Discord बॉट्स के लिए SaaS प्लेटफ़ॉर्म", + "title": "Shadow - अपने Discord बॉट्स होस्ट करें (प्रति बॉट 2 EUR/माह से शुरू)", + "description": "अपने बॉट्स को ऑटोमैटिक कमांड्स (prefix और slash), components v2, स्वचालित सहायता, मल्टी-भाषा सपोर्ट और डायनामिक वेरिएबल्स के साथ होस्ट और प्रबंधित करें।", + "ctaStart": "शुरू करें - 2 EUR/माह", + "ctaLogin": "लॉगिन करें", + "bots": { + "moderation": "मॉडरेशन", + "welcome": "स्वागत", + "analytics": "विश्लेषण" + }, + "status": { + "online": "ऑनलाइन", + "stopped": "रुका हुआ" + }, + "card": { + "title": "लाइव फ़्लीट का अवलोकन", + "subtitle": "एक ही ऑपरेशनल पैनल से हर बॉट इंस्टेंस को ट्रैक करें।", + "instanceMetrics": "इंस्टेंस: 1 | CPU 35%", + "manage": "प्रबंधन करें", + "preview": "अपने बॉट्स की स्थिति का रीयल-टाइम पूर्वावलोकन" + } + }, + "features": { + "title": "मुख्य विशेषताएँ", + "subtitle": "प्रोडक्शन में अपने बॉट्स को नियंत्रित करने के लिए सब कुछ।", + "items": { + "autoCommands": { + "title": "स्वचालित कमांड्स", + "description": "हर बॉट के लिए prefix और slash कमांड्स स्वतः जनरेट और सिंक किए जाते हैं।" + }, + "componentsV2": { + "title": "Components v2", + "description": "Components v2 और उन्नत इंटरैक्शन्स का पूर्ण सपोर्ट।" + }, + "autoHelp": { + "title": "स्वचालित सहायता", + "description": "कमांड्स के लिए स्वतः डॉक्यूमेंटेशन और हेल्प जनरेट होता है।" + }, + "multiLanguage": { + "title": "बहु-भाषा", + "description": "मैसेज और कमांड लेबल्स को अनुवाद करें और आसानी से कई भाषाएँ सक्षम करें।" + }, + "dynamicVariables": { + "title": "डायनामिक वेरिएबल्स", + "description": "bot_latency, guild_count जैसे वेरिएबल्स के साथ डायनामिक टेम्पलेट्स।" + }, + "hosting": { + "title": "होस्टिंग और स्केलेबिलिटी", + "description": "प्रति बॉट 2 EUR/माह के सरल प्लान, जिसमें मॉनिटरिंग और स्केलेबिलिटी शामिल है।" + } + } + }, + "howItWorks": { + "title": "यह कैसे काम करता है", + "subtitle": "अपने बॉट्स को नियंत्रित करने के लिए तीन सरल चरण।", + "steps": { + "addBot": { + "title": "बॉट जोड़ें", + "description": "OAuth के माध्यम से अपना Discord एप्लिकेशन कनेक्ट करें और Shadow में जोड़ें।" + }, + "dashboard": { + "title": "डैशबोर्ड से प्रबंधन करें", + "description": "एक केंद्रीकृत इंटरफ़ेस से कमांड्स, लॉग्स और सेटिंग्स एक्सेस करें।" + }, + "realtime": { + "title": "रीयल टाइम नियंत्रण करें", + "description": "मांग के अनुसार अपने बॉट्स को तुरंत शुरू, बंद और स्केल करें।" + } + } + }, + "footer": { + "tagline": "Discord बॉट होस्टिंग - प्रति बॉट 2 EUR/माह से शुरू", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Discord बॉट होस्टिंग", + "title": "अपने Discord खाते से लॉगिन करें", + "description": "अपने बॉट्स को होस्ट और प्रबंधित करने के लिए OAuth2 के माध्यम से लॉगिन करें।", + "cta": "Discord के साथ जारी रखें" + }, + "dashboard": { + "loading": "डैशबोर्ड लोड हो रहा है...", + "sessionRequired": "सेशन आवश्यक", + "loginRequired": "लॉगिन आवश्यक", + "loginDescription": "आपका डैशबोर्ड सुरक्षित है। अपने बॉट्स को प्रबंधित करने के लिए Discord से लॉगिन करें।", + "loginCta": "लॉगिन करें", + "tenant": "टेनेंट {tenantId}", + "role": "भूमिका: {role}", + "roles": { + "owner": "मालिक", + "member": "सदस्य" + }, + "logout": "लॉगआउट करें", + "addBot": { + "title": "बॉट जोड़ें", + "description": "टोकन API द्वारा सत्यापित किया जाता है और स्टोर करने से पहले एन्क्रिप्ट किया जाता है।", + "tokenLabel": "Discord बॉट टोकन", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "डिस्प्ले नाम (वैकल्पिक)", + "displayNamePlaceholder": "सपोर्ट बॉट EU", + "submit": "बॉट जोड़ें", + "submitPending": "सत्यापन जारी है..." + }, + "bots": { + "title": "मेरे बॉट्स", + "refresh": "रीफ्रेश करें", + "empty": "इस टेनेंट के लिए कोई बॉट नहीं है।", + "discordId": "Discord ID: {discordBotId}", + "lastError": "अंतिम त्रुटि: {message}" + }, + "status": { + "stopped": "रुका हुआ", + "starting": "शुरू हो रहा है", + "running": "ऑनलाइन", + "stopping": "बंद हो रहा है", + "error": "त्रुटि" + }, + "actions": { + "start": "शुरू करें", + "stop": "बंद करें", + "restart": "पुनः आरंभ करें" + }, + "errors": { + "fetchSession": "उपयोगकर्ता सत्र प्राप्त नहीं हो सका", + "fetchBots": "बॉट सूची प्राप्त नहीं हो सकी", + "unexpected": "अप्रत्याशित त्रुटि", + "addBot": "बॉट जोड़ना असंभव", + "actionFailed": "क्रिया असफल: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/id.json b/apps/web/locales/id.json new file mode 100644 index 0000000..cee218c --- /dev/null +++ b/apps/web/locales/id.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hosting bot Discord (mulai dari 2 EUR/bulan per bot)", + "description": "Platform SaaS multi-tenant untuk meng-host dan mengelola bot Discord Anda dengan perintah otomatis, components v2, multi-bahasa, dan variabel dinamis." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Beranda", + "dashboard": "Dasbor", + "login": "Masuk" + }, + "localeSwitcher": { + "label": "Bahasa", + "locales": { + "en": "Inggris", + "es": "Spanyol", + "de": "Jerman", + "ja": "Jepang", + "fr": "Prancis", + "pt": "Portugis", + "ru": "Rusia", + "it": "Italia", + "nl": "Belanda", + "pl": "Polandia", + "zh": "Tionghoa", + "hi": "Hindi", + "ar": "Arab", + "bn": "Bengali", + "id": "Indonesia", + "tr": "Turki" + } + }, + "hero": { + "kicker": "Platform SaaS untuk bot Discord", + "title": "Shadow - Hosting bot Discord Anda (mulai dari 2 EUR/bulan per bot)", + "description": "Hosting dan kelola bot Anda dengan perintah otomatis (prefix dan slash), components v2, bantuan otomatis, dukungan multi-bahasa, dan variabel dinamis.", + "ctaStart": "Mulai - 2 EUR/bulan", + "ctaLogin": "Masuk", + "bots": { + "moderation": "Moderasi", + "welcome": "Selamat datang", + "analytics": "Analitik" + }, + "status": { + "online": "Online", + "stopped": "Berhenti" + }, + "card": { + "title": "Pratinjau langsung fleet", + "subtitle": "Pantau setiap instance bot dari satu dashboard operasional.", + "instanceMetrics": "Instansi: 1 | CPU 35%", + "manage": "Kelola", + "preview": "Pratinjau real-time status bot Anda" + } + }, + "features": { + "title": "Fitur utama", + "subtitle": "Semua yang Anda butuhkan untuk mengelola bot dalam produksi.", + "items": { + "autoCommands": { + "title": "Perintah otomatis", + "description": "Perintah prefix dan slash dibuat serta disinkronkan secara otomatis untuk setiap bot." + }, + "componentsV2": { + "title": "Components v2", + "description": "Dukungan penuh untuk components v2 dan interaksi kaya yang andal." + }, + "autoHelp": { + "title": "Bantuan otomatis", + "description": "Dokumentasi dan bantuan perintah dibuat secara otomatis." + }, + "multiLanguage": { + "title": "Multi-bahasa", + "description": "Terjemahkan pesan dan label perintah lalu aktifkan beberapa bahasa dengan mudah." + }, + "dynamicVariables": { + "title": "Variabel dinamis", + "description": "Template dinamis dengan variabel seperti bot_latency, guild_count, dan lainnya." + }, + "hosting": { + "title": "Hosting dan skalabilitas", + "description": "Paket sederhana mulai 2 EUR/bulan per bot dengan monitoring dan skalabilitas." + } + } + }, + "howItWorks": { + "title": "Cara kerja", + "subtitle": "Tiga langkah sederhana untuk mengontrol bot Anda.", + "steps": { + "addBot": { + "title": "Tambah bot", + "description": "Hubungkan aplikasi Discord Anda melalui OAuth dan daftarkan di Shadow." + }, + "dashboard": { + "title": "Kelola dari dashboard", + "description": "Akses perintah, log, dan pengaturan dari satu antarmuka terpusat." + }, + "realtime": { + "title": "Kontrol real-time", + "description": "Jalankan, hentikan, dan skalakan bot Anda secara instan sesuai kebutuhan." + } + } + }, + "footer": { + "tagline": "Hosting bot Discord - mulai dari 2 EUR/bulan per bot", + "github": "GitHub", + "copyright": "Hak cipta {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hosting bot Discord", + "title": "Hubungkan akun Discord Anda", + "description": "Login melalui OAuth2 untuk mengakses dashboard dan meng-host bot Anda.", + "cta": "Lanjutkan dengan Discord" + }, + "dashboard": { + "loading": "Memuat dashboard...", + "sessionRequired": "Sesi diperlukan", + "loginRequired": "Login diperlukan", + "loginDescription": "Dashboard Anda dilindungi. Masuk melalui Discord untuk mengelola bot Anda.", + "loginCta": "Masuk", + "tenant": "Tenant {tenantId}", + "role": "Peran: {role}", + "roles": { + "owner": "Pemilik", + "member": "Anggota" + }, + "logout": "Keluar", + "addBot": { + "title": "Tambah bot", + "description": "Token diverifikasi di sisi API lalu dienkripsi sebelum disimpan.", + "tokenLabel": "Token bot Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nama tampilan (opsional)", + "displayNamePlaceholder": "Bot support EU", + "submit": "Tambah bot", + "submitPending": "Memvalidasi..." + }, + "bots": { + "title": "Bot saya", + "refresh": "Segarkan", + "empty": "Tidak ada bot untuk tenant ini.", + "discordId": "ID Discord: {discordBotId}", + "lastError": "Error terakhir: {message}" + }, + "status": { + "stopped": "Berhenti", + "starting": "Memulai", + "running": "Online", + "stopping": "Menghentikan", + "error": "Error" + }, + "actions": { + "start": "Mulai", + "stop": "Hentikan", + "restart": "Restart" + }, + "errors": { + "fetchSession": "Tidak dapat mengambil sesi pengguna", + "fetchBots": "Tidak dapat mengambil daftar bot", + "unexpected": "Kesalahan tidak terduga", + "addBot": "Gagal menambahkan bot", + "actionFailed": "Aksi gagal: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/it.json b/apps/web/locales/it.json new file mode 100644 index 0000000..f1ba927 --- /dev/null +++ b/apps/web/locales/it.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hosting di bot Discord (a partire da 2 EUR/mese per bot)", + "description": "Piattaforma SaaS multi-tenant per ospitare e gestire i tuoi bot Discord con comandi automatici, componenti v2, multi-lingua e variabili dinamiche." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Home", + "dashboard": "Dashboard", + "login": "Accedi" + }, + "localeSwitcher": { + "label": "Lingua", + "locales": { + "en": "Inglese", + "es": "Spagnolo", + "de": "Tedesco", + "ja": "Giapponese", + "fr": "Francese", + "pt": "Portoghese", + "ru": "Russo", + "it": "Italiano", + "nl": "Olandese", + "pl": "Polacco", + "zh": "Cinese", + "hi": "Hindi", + "ar": "Arabo", + "bn": "Bengalese", + "id": "Indonesiano", + "tr": "Turco" + } + }, + "hero": { + "kicker": "Piattaforma SaaS per bot Discord", + "title": "Shadow - Ospita i tuoi bot Discord (a partire da 2 EUR/mese per bot)", + "description": "Ospita e gestisci i tuoi bot con comandi automatici (prefix e slash), componenti v2, assistenza automatica, supporto multi-lingua e variabili dinamiche.", + "ctaStart": "Inizia - 2 EUR/mese", + "ctaLogin": "Accedi", + "bots": { + "moderation": "Moderazione", + "welcome": "Benvenuto", + "analytics": "Analisi" + }, + "status": { + "online": "Online", + "stopped": "Fermato" + }, + "card": { + "title": "Panoramica live della flotta", + "subtitle": "Monitora ogni istanza del bot da un’unica superficie operativa.", + "instanceMetrics": "Istanze: 1 | CPU 35%", + "manage": "Gestisci", + "preview": "Anteprima in tempo reale dello stato dei tuoi bot" + } + }, + "features": { + "title": "Funzionalità principali", + "subtitle": "Tutto ciò che serve per gestire i tuoi bot in produzione.", + "items": { + "autoCommands": { + "title": "Comandi automatici", + "description": "I comandi prefix e slash vengono generati e sincronizzati automaticamente per ogni bot." + }, + "componentsV2": { + "title": "Componenti v2", + "description": "Supporto completo per i componenti v2 e interazioni avanzate affidabili." + }, + "autoHelp": { + "title": "Assistenza automatica", + "description": "Assistenza e documentazione dei comandi generate automaticamente." + }, + "multiLanguage": { + "title": "Multi-lingua", + "description": "Traduci i messaggi e le etichette dei comandi e attiva facilmente più lingue." + }, + "dynamicVariables": { + "title": "Variabili dinamiche", + "description": "Template dinamici con variabili come bot_latency, guild_count e altro." + }, + "hosting": { + "title": "Hosting e scalabilità", + "description": "Piani semplici a 2 EUR/mese per bot con monitoraggio e scalabilità inclusi." + } + } + }, + "howItWorks": { + "title": "Come funziona", + "subtitle": "Tre semplici passaggi per prendere il controllo dei tuoi bot.", + "steps": { + "addBot": { + "title": "Aggiungi un bot", + "description": "Collega la tua applicazione Discord tramite OAuth e registrala in Shadow." + }, + "dashboard": { + "title": "Gestisci dal dashboard", + "description": "Accedi a comandi, log e impostazioni da un’interfaccia centralizzata." + }, + "realtime": { + "title": "Controllo in tempo reale", + "description": "Avvia, arresta e scala i tuoi bot istantaneamente in base alla domanda." + } + } + }, + "footer": { + "tagline": "Hosting di bot Discord - piani a partire da 2 EUR/mese per bot", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hosting di bot Discord", + "title": "Collega il tuo account Discord", + "description": "Autenticati tramite OAuth2 per accedere al tuo spazio e ospitare i tuoi bot.", + "cta": "Continua con Discord" + }, + "dashboard": { + "loading": "Caricamento della dashboard...", + "sessionRequired": "Sessione richiesta", + "loginRequired": "Accesso richiesto", + "loginDescription": "La tua dashboard è protetta. Accedi tramite Discord per gestire i tuoi bot.", + "loginCta": "Accedi", + "tenant": "Tenant {tenantId}", + "role": "Ruolo: {role}", + "roles": { + "owner": "Proprietario", + "member": "Membro" + }, + "logout": "Disconnetti", + "addBot": { + "title": "Aggiungi un bot", + "description": "Il token viene verificato lato API e poi cifrato prima della memorizzazione.", + "tokenLabel": "Token bot Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nome visualizzato (opzionale)", + "displayNamePlaceholder": "Bot support EU", + "submit": "Aggiungi bot", + "submitPending": "Validazione in corso..." + }, + "bots": { + "title": "I miei bot", + "refresh": "Aggiorna", + "empty": "Nessun bot per questo tenant.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Ultimo errore: {message}" + }, + "status": { + "stopped": "Fermato", + "starting": "Avvio", + "running": "Online", + "stopping": "Arresto", + "error": "Errore" + }, + "actions": { + "start": "Avviare", + "stop": "Arrestare", + "restart": "Riavviare" + }, + "errors": { + "fetchSession": "Impossibile recuperare la sessione utente", + "fetchBots": "Impossibile recuperare la lista dei bot", + "unexpected": "Errore imprevisto", + "addBot": "Impossibile aggiungere il bot", + "actionFailed": "Azione impossibile: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/ja.json b/apps/web/locales/ja.json new file mode 100644 index 0000000..44217c8 --- /dev/null +++ b/apps/web/locales/ja.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Discordボットホスティング(ボット1台あたり月額2EURから)", + "description": "Discordボットをホスティング・管理するためのマルチテナントSaaSプラットフォーム。自動コマンド、v2コンポーネント、多言語対応、動的変数をサポート。" + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "ホーム", + "dashboard": "ダッシュボード", + "login": "ログイン" + }, + "localeSwitcher": { + "label": "言語", + "locales": { + "en": "英語", + "es": "スペイン語", + "de": "ドイツ語", + "ja": "日本語", + "fr": "フランス語", + "pt": "ポルトガル語", + "ru": "ロシア語", + "it": "イタリア語", + "nl": "オランダ語", + "pl": "ポーランド語", + "zh": "中国語", + "hi": "ヒンディー語", + "ar": "アラビア語", + "bn": "ベンガル語", + "id": "インドネシア語", + "tr": "トルコ語" + } + }, + "hero": { + "kicker": "Discordボット向けSaaSプラットフォーム", + "title": "Shadow - Discordボットをホスティング(ボット1台あたり月額2EURから)", + "description": "自動コマンド(プレフィックス・スラッシュ)、v2コンポーネント、自動ヘルプ、多言語対応、動的変数でボットをホスティング・管理できます。", + "ctaStart": "開始 - 月額2EUR", + "ctaLogin": "ログイン", + "bots": { + "moderation": "モデレーション", + "welcome": "ウェルカム", + "analytics": "分析" + }, + "status": { + "online": "オンライン", + "stopped": "停止中" + }, + "card": { + "title": "フリートのライブ概要", + "subtitle": "すべてのボットインスタンスを単一の操作画面から監視。", + "instanceMetrics": "インスタンス: 1 | CPU 35%", + "manage": "管理", + "preview": "ボットのステータスをリアルタイムで表示" + } + }, + "features": { + "title": "主な機能", + "subtitle": "本番環境でボットを運用するために必要なすべて。", + "items": { + "autoCommands": { + "title": "自動コマンド", + "description": "プレフィックスおよびスラッシュコマンドは各ボットに対して自動生成・同期されます。" + }, + "componentsV2": { + "title": "v2コンポーネント", + "description": "v2コンポーネントとリッチなインタラクションを完全サポート。" + }, + "autoHelp": { + "title": "自動ヘルプ", + "description": "コマンドのヘルプとドキュメントを自動生成します。" + }, + "multiLanguage": { + "title": "多言語対応", + "description": "メッセージやコマンドラベルを翻訳し、簡単に複数言語を有効化できます。" + }, + "dynamicVariables": { + "title": "動的変数", + "description": "bot_latency、guild_countなどの変数を使った動的テンプレート。" + }, + "hosting": { + "title": "ホスティングとスケーラビリティ", + "description": "ボット1台あたり月額2EURのシンプルなプラン。監視とスケーリングを含む。" + } + } + }, + "howItWorks": { + "title": "使い方", + "subtitle": "ボットを簡単に管理するための3ステップ。", + "steps": { + "addBot": { + "title": "ボットを追加", + "description": "DiscordアプリをOAuthで接続し、Shadowに登録します。" + }, + "dashboard": { + "title": "ダッシュボードで管理", + "description": "統合されたUIからコマンド、ログ、設定にアクセスできます。" + }, + "realtime": { + "title": "リアルタイム制御", + "description": "必要に応じてボットを即時起動・停止・スケールできます。" + } + } + }, + "footer": { + "tagline": "Discordボットホスティング - ボット1台あたり月額2EURから", + "github": "GitHub", + "copyright": "著作権 {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Discordボットホスティング", + "title": "Discordアカウントに接続", + "description": "OAuth2で認証してダッシュボードにアクセスし、ボットをホスティングします。", + "cta": "Discordで続行" + }, + "dashboard": { + "loading": "ダッシュボードを読み込み中...", + "sessionRequired": "セッションが必要です", + "loginRequired": "ログインが必要です", + "loginDescription": "ダッシュボードは保護されています。ボット管理のためにDiscordでログインしてください。", + "loginCta": "ログイン", + "tenant": "テナント {tenantId}", + "role": "ロール: {role}", + "roles": { + "owner": "オーナー", + "member": "メンバー" + }, + "logout": "ログアウト", + "addBot": { + "title": "ボットを追加", + "description": "トークンはAPI側で検証され、保存前に暗号化されます。", + "tokenLabel": "Discordボットトークン", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "表示名(任意)", + "displayNamePlaceholder": "EUサポートボット", + "submit": "ボットを追加", + "submitPending": "検証中..." + }, + "bots": { + "title": "マイボット", + "refresh": "更新", + "empty": "このテナントにはボットがありません。", + "discordId": "Discord ID: {discordBotId}", + "lastError": "最新エラー: {message}" + }, + "status": { + "stopped": "停止中", + "starting": "起動中", + "running": "オンライン", + "stopping": "停止中", + "error": "エラー" + }, + "actions": { + "start": "起動", + "stop": "停止", + "restart": "再起動" + }, + "errors": { + "fetchSession": "ユーザーセッションの取得に失敗しました", + "fetchBots": "ボット一覧の取得に失敗しました", + "unexpected": "予期しないエラー", + "addBot": "ボットの追加に失敗しました", + "actionFailed": "操作失敗: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/nl.json b/apps/web/locales/nl.json new file mode 100644 index 0000000..f117821 --- /dev/null +++ b/apps/web/locales/nl.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hosting van Discord-bots (vanaf 2 EUR/maand per bot)", + "description": "Multi-tenant SaaS-platform om je Discord-bots te hosten en te beheren met automatische commando's, components v2, meertaligheid en dynamische variabelen." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Home", + "dashboard": "Dashboard", + "login": "Inloggen" + }, + "localeSwitcher": { + "label": "Taal", + "locales": { + "en": "Engels", + "es": "Spaans", + "de": "Duits", + "ja": "Japans", + "fr": "Frans", + "pt": "Portugees", + "ru": "Russisch", + "it": "Italiaans", + "nl": "Nederlands", + "pl": "Pools", + "zh": "Chinees", + "hi": "Hindi", + "ar": "Arabisch", + "bn": "Bengaals", + "id": "Indonesisch", + "tr": "Turks" + } + }, + "hero": { + "kicker": "SaaS-platform voor Discord-bots", + "title": "Shadow - Host je Discord-bots (vanaf 2 EUR/maand per bot)", + "description": "Host en beheer je bots met automatische commando's (prefix en slash), components v2, automatische hulp, meertalige ondersteuning en dynamische variabelen.", + "ctaStart": "Begin - 2 EUR/maand", + "ctaLogin": "Inloggen", + "bots": { + "moderation": "Moderatie", + "welcome": "Welkom", + "analytics": "Analytics" + }, + "status": { + "online": "Online", + "stopped": "Gestopt" + }, + "card": { + "title": "Live overzicht van de vloot", + "subtitle": "Volg elke bot-instantie vanuit één operationeel dashboard.", + "instanceMetrics": "Instanties: 1 | CPU 35%", + "manage": "Beheren", + "preview": "Realtime overzicht van de status van je bots" + } + }, + "features": { + "title": "Belangrijkste functies", + "subtitle": "Alles wat je nodig hebt om je bots in productie te beheren.", + "items": { + "autoCommands": { + "title": "Automatische commando's", + "description": "Prefix- en slash-commando's worden automatisch gegenereerd en gesynchroniseerd voor elke bot." + }, + "componentsV2": { + "title": "Components v2", + "description": "Volledige ondersteuning voor components v2 en betrouwbare interactieve elementen." + }, + "autoHelp": { + "title": "Automatische hulp", + "description": "Automatisch gegenereerde hulp en documentatie voor commando's." + }, + "multiLanguage": { + "title": "Meertalig", + "description": "Vertaal berichten en commandolabels en activeer eenvoudig meerdere talen." + }, + "dynamicVariables": { + "title": "Dynamische variabelen", + "description": "Dynamische templates met variabelen zoals bot_latency, guild_count en meer." + }, + "hosting": { + "title": "Hosting en schaalbaarheid", + "description": "Eenvoudige plannen vanaf 2 EUR/maand per bot met monitoring en schaalbaarheid inbegrepen." + } + } + }, + "howItWorks": { + "title": "Hoe het werkt", + "subtitle": "Drie eenvoudige stappen om je bots te beheren.", + "steps": { + "addBot": { + "title": "Bot toevoegen", + "description": "Koppel je Discord-app via OAuth en registreer deze in Shadow." + }, + "dashboard": { + "title": "Beheer via dashboard", + "description": "Toegang tot commando's, logs en instellingen via één centrale interface." + }, + "realtime": { + "title": "Realtime controle", + "description": "Start, stop en schaal je bots direct op basis van de vraag." + } + } + }, + "footer": { + "tagline": "Discord-bot hosting - plannen vanaf 2 EUR/maand per bot", + "github": "GitHub", + "copyright": "Copyright {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hosting van Discord-bots", + "title": "Verbind je Discord-account", + "description": "Authenticeer via OAuth2 om toegang te krijgen tot je omgeving en je bots te hosten.", + "cta": "Doorgaan met Discord" + }, + "dashboard": { + "loading": "Dashboard laden...", + "sessionRequired": "Sessie vereist", + "loginRequired": "Inloggen vereist", + "loginDescription": "Je dashboard is beveiligd. Log in via Discord om je bots te beheren.", + "loginCta": "Inloggen", + "tenant": "Tenant {tenantId}", + "role": "Rol: {role}", + "roles": { + "owner": "Eigenaar", + "member": "Lid" + }, + "logout": "Uitloggen", + "addBot": { + "title": "Bot toevoegen", + "description": "De token wordt aan de API-kant gecontroleerd en versleuteld opgeslagen.", + "tokenLabel": "Discord bot-token", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Weergavenaam (optioneel)", + "displayNamePlaceholder": "EU support bot", + "submit": "Bot toevoegen", + "submitPending": "Bezig met valideren..." + }, + "bots": { + "title": "Mijn bots", + "refresh": "Vernieuwen", + "empty": "Geen bots voor deze tenant.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Laatste fout: {message}" + }, + "status": { + "stopped": "Gestopt", + "starting": "Starten", + "running": "Online", + "stopping": "Stoppen", + "error": "Fout" + }, + "actions": { + "start": "Starten", + "stop": "Stoppen", + "restart": "Herstarten" + }, + "errors": { + "fetchSession": "Kan gebruikerssessie niet ophalen", + "fetchBots": "Kan botlijst niet ophalen", + "unexpected": "Onverwachte fout", + "addBot": "Bot toevoegen mislukt", + "actionFailed": "Actie mislukt: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/pl.json b/apps/web/locales/pl.json new file mode 100644 index 0000000..b02fd53 --- /dev/null +++ b/apps/web/locales/pl.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hosting botów Discord (od 2 EUR/miesiąc za bota)", + "description": "Wielodostępna platforma SaaS do hostowania i zarządzania botami Discord z automatycznymi komendami, komponentami v2, wielojęzycznością i dynamicznymi zmiennymi." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Strona główna", + "dashboard": "Panel", + "login": "Zaloguj się" + }, + "localeSwitcher": { + "label": "Język", + "locales": { + "en": "Angielski", + "es": "Hiszpański", + "de": "Niemiecki", + "ja": "Japoński", + "fr": "Francuski", + "pt": "Portugalski", + "ru": "Rosyjski", + "it": "Włoski", + "nl": "Holenderski", + "pl": "Polski", + "zh": "Chiński", + "hi": "Hinduski", + "ar": "Arabski", + "bn": "Bengalski", + "id": "Indonezyjski", + "tr": "Turecki" + } + }, + "hero": { + "kicker": "Platforma SaaS dla botów Discord", + "title": "Shadow - Hostuj swoje boty Discord (od 2 EUR/miesiąc za bota)", + "description": "Hostuj i zarządzaj swoimi botami z automatycznymi komendami (prefix i slash), komponentami v2, automatyczną pomocą, wsparciem wielojęzycznym i dynamicznymi zmiennymi.", + "ctaStart": "Rozpocznij - 2 EUR/miesiąc", + "ctaLogin": "Zaloguj się", + "bots": { + "moderation": "Moderacja", + "welcome": "Powitanie", + "analytics": "Analityka" + }, + "status": { + "online": "Online", + "stopped": "Zatrzymany" + }, + "card": { + "title": "Podgląd floty na żywo", + "subtitle": "Śledź każdą instancję bota z jednego panelu operacyjnego.", + "instanceMetrics": "Instancje: 1 | CPU 35%", + "manage": "Zarządzaj", + "preview": "Podgląd w czasie rzeczywistym statusu botów" + } + }, + "features": { + "title": "Główne funkcje", + "subtitle": "Wszystko czego potrzebujesz do zarządzania botami w produkcji.", + "items": { + "autoCommands": { + "title": "Automatyczne komendy", + "description": "Komendy prefix i slash są automatycznie generowane i synchronizowane dla każdego bota." + }, + "componentsV2": { + "title": "Komponenty v2", + "description": "Pełne wsparcie dla komponentów v2 i niezawodnych interakcji." + }, + "autoHelp": { + "title": "Automatyczna pomoc", + "description": "Automatycznie generowana pomoc i dokumentacja komend." + }, + "multiLanguage": { + "title": "Wielojęzyczność", + "description": "Tłumacz wiadomości i etykiety komend oraz łatwo aktywuj wiele języków." + }, + "dynamicVariables": { + "title": "Zmienne dynamiczne", + "description": "Dynamiczne szablony ze zmiennymi takimi jak bot_latency, guild_count i inne." + }, + "hosting": { + "title": "Hosting i skalowalność", + "description": "Proste plany od 2 EUR/miesiąc za bota z monitoringiem i skalowaniem w cenie." + } + } + }, + "howItWorks": { + "title": "Jak to działa", + "subtitle": "Trzy proste kroki, aby przejąć kontrolę nad botami.", + "steps": { + "addBot": { + "title": "Dodaj bota", + "description": "Połącz swoją aplikację Discord przez OAuth i zarejestruj ją w Shadow." + }, + "dashboard": { + "title": "Zarządzaj z panelu", + "description": "Uzyskaj dostęp do komend, logów i ustawień z jednego centralnego interfejsu." + }, + "realtime": { + "title": "Kontroluj w czasie rzeczywistym", + "description": "Uruchamiaj, zatrzymuj i skaluj boty natychmiast w zależności od potrzeb." + } + } + }, + "footer": { + "tagline": "Hosting botów Discord - plany od 2 EUR/miesiąc za bota", + "github": "GitHub", + "copyright": "Prawa autorskie {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hosting botów Discord", + "title": "Połącz swoje konto Discord", + "description": "Zaloguj się przez OAuth2, aby uzyskać dostęp do swojego panelu i hostować boty.", + "cta": "Kontynuuj z Discordem" + }, + "dashboard": { + "loading": "Ładowanie panelu...", + "sessionRequired": "Wymagana sesja", + "loginRequired": "Wymagane logowanie", + "loginDescription": "Twój panel jest chroniony. Zaloguj się przez Discord, aby zarządzać botami.", + "loginCta": "Zaloguj się", + "tenant": "Tenant {tenantId}", + "role": "Rola: {role}", + "roles": { + "owner": "Właściciel", + "member": "Członek" + }, + "logout": "Wyloguj się", + "addBot": { + "title": "Dodaj bota", + "description": "Token jest weryfikowany po stronie API i szyfrowany przed zapisaniem.", + "tokenLabel": "Token bota Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nazwa wyświetlana (opcjonalnie)", + "displayNamePlaceholder": "Bot wsparcia EU", + "submit": "Dodaj bota", + "submitPending": "Weryfikacja..." + }, + "bots": { + "title": "Moje boty", + "refresh": "Odśwież", + "empty": "Brak botów dla tego tenanta.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Ostatni błąd: {message}" + }, + "status": { + "stopped": "Zatrzymany", + "starting": "Uruchamianie", + "running": "Online", + "stopping": "Zatrzymywanie", + "error": "Błąd" + }, + "actions": { + "start": "Uruchom", + "stop": "Zatrzymaj", + "restart": "Uruchom ponownie" + }, + "errors": { + "fetchSession": "Nie można pobrać sesji użytkownika", + "fetchBots": "Nie można pobrać listy botów", + "unexpected": "Nieoczekiwany błąd", + "addBot": "Nie można dodać bota", + "actionFailed": "Akcja nie powiodła się: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/pt.json b/apps/web/locales/pt.json new file mode 100644 index 0000000..9bfb14a --- /dev/null +++ b/apps/web/locales/pt.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Hospedagem de bots Discord (a partir de 2 EUR/mês por bot)", + "description": "Plataforma SaaS multi-tenant para hospedar e gerenciar seus bots Discord com comandos automáticos, components v2, multi-idioma e variáveis dinâmicas." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Início", + "dashboard": "Painel", + "login": "Entrar" + }, + "localeSwitcher": { + "label": "Idioma", + "locales": { + "en": "Inglês", + "es": "Espanhol", + "de": "Alemão", + "ja": "Japonês", + "fr": "Francês", + "pt": "Português", + "ru": "Russo", + "it": "Italiano", + "nl": "Holandês", + "pl": "Polonês", + "zh": "Chinês", + "hi": "Hindi", + "ar": "Árabe", + "bn": "Bengali", + "id": "Indonésio", + "tr": "Turco" + } + }, + "hero": { + "kicker": "Plataforma SaaS para bots Discord", + "title": "Shadow - Hospede seus bots Discord (a partir de 2 EUR/mês por bot)", + "description": "Hospede e gerencie seus bots com comandos automáticos (prefixo e slash), components v2, ajuda automática, suporte multi-idioma e variáveis dinâmicas.", + "ctaStart": "Começar - 2 EUR/mês", + "ctaLogin": "Entrar", + "bots": { + "moderation": "Moderação", + "welcome": "Boas-vindas", + "analytics": "Análises" + }, + "status": { + "online": "Online", + "stopped": "Parado" + }, + "card": { + "title": "Visão geral da frota em tempo real", + "subtitle": "Acompanhe cada instância de bot a partir de um painel operacional único.", + "instanceMetrics": "Instâncias: 1 | CPU 35%", + "manage": "Gerenciar", + "preview": "Pré-visualização em tempo real do status dos seus bots" + } + }, + "features": { + "title": "Principais funcionalidades", + "subtitle": "Tudo o que você precisa para operar seus bots em produção.", + "items": { + "autoCommands": { + "title": "Comandos automáticos", + "description": "Comandos prefixo e slash são gerados e sincronizados automaticamente para cada bot." + }, + "componentsV2": { + "title": "Components v2", + "description": "Suporte completo para components v2 e interações ricas confiáveis." + }, + "autoHelp": { + "title": "Ajuda automática", + "description": "Ajuda e documentação dos comandos geradas automaticamente." + }, + "multiLanguage": { + "title": "Multi-idioma", + "description": "Traduza mensagens e rótulos de comandos e ative vários idiomas facilmente." + }, + "dynamicVariables": { + "title": "Variáveis dinâmicas", + "description": "Templates dinâmicos com variáveis como bot_latency, guild_count e mais." + }, + "hosting": { + "title": "Hospedagem e escalabilidade", + "description": "Planos simples a 2 EUR/mês por bot com monitoramento e escalabilidade incluídos." + } + } + }, + "howItWorks": { + "title": "Como funciona", + "subtitle": "Três etapas simples para assumir o controle dos seus bots.", + "steps": { + "addBot": { + "title": "Adicionar um bot", + "description": "Conecte sua aplicação Discord via OAuth e registre-a no Shadow." + }, + "dashboard": { + "title": "Gerenciar pelo painel", + "description": "Acesse comandos, logs e configurações a partir de uma interface centralizada." + }, + "realtime": { + "title": "Controle em tempo real", + "description": "Inicie, pare e escale seus bots instantaneamente conforme a demanda." + } + } + }, + "footer": { + "tagline": "Hospedagem de bots Discord - planos a partir de 2 EUR/mês por bot", + "github": "GitHub", + "copyright": "Direitos autorais {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Hospedagem de bots Discord", + "title": "Conecte sua conta Discord", + "description": "Autentique-se via OAuth2 para acessar seu espaço e hospedar seus bots.", + "cta": "Continuar com Discord" + }, + "dashboard": { + "loading": "Carregando painel...", + "sessionRequired": "Sessão obrigatória", + "loginRequired": "Login necessário", + "loginDescription": "Seu painel é protegido. Entre com Discord para gerenciar seus bots.", + "loginCta": "Entrar", + "tenant": "Tenant {tenantId}", + "role": "Função: {role}", + "roles": { + "owner": "Proprietário", + "member": "Membro" + }, + "logout": "Sair", + "addBot": { + "title": "Adicionar um bot", + "description": "O token é verificado pela API e depois criptografado antes do armazenamento.", + "tokenLabel": "Token do bot Discord", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Nome de exibição (opcional)", + "displayNamePlaceholder": "Bot suporte EU", + "submit": "Adicionar bot", + "submitPending": "Validando..." + }, + "bots": { + "title": "Meus bots", + "refresh": "Atualizar", + "empty": "Nenhum bot para este tenant.", + "discordId": "ID Discord: {discordBotId}", + "lastError": "Último erro: {message}" + }, + "status": { + "stopped": "Parado", + "starting": "Iniciando", + "running": "Online", + "stopping": "Parando", + "error": "Erro" + }, + "actions": { + "start": "Iniciar", + "stop": "Parar", + "restart": "Reiniciar" + }, + "errors": { + "fetchSession": "Não foi possível recuperar a sessão do usuário", + "fetchBots": "Não foi possível recuperar a lista de bots", + "unexpected": "Erro inesperado", + "addBot": "Não foi possível adicionar o bot", + "actionFailed": "Ação falhou: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/ru.json b/apps/web/locales/ru.json new file mode 100644 index 0000000..9acfbc2 --- /dev/null +++ b/apps/web/locales/ru.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Хостинг Discord-ботов (от 2 EUR/месяц за бота)", + "description": "Мультиарендная SaaS-платформа для хостинга и управления вашими Discord-ботами с автоматическими командами, компонентами v2, поддержкой нескольких языков и динамическими переменными." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Главная", + "dashboard": "Панель управления", + "login": "Войти" + }, + "localeSwitcher": { + "label": "Язык", + "locales": { + "en": "Английский", + "es": "Испанский", + "de": "Немецкий", + "ja": "Японский", + "fr": "Французский", + "pt": "Португальский", + "ru": "Русский", + "it": "Итальянский", + "nl": "Нидерландский", + "pl": "Польский", + "zh": "Китайский", + "hi": "Хинди", + "ar": "Арабский", + "bn": "Бенгальский", + "id": "Индонезийский", + "tr": "Турецкий" + } + }, + "hero": { + "kicker": "SaaS-платформа для Discord-ботов", + "title": "Shadow - размещайте своих Discord-ботов (от 2 EUR/месяц за бота)", + "description": "Размещайте и управляйте ботами с автоматическими командами (prefix и slash), компонентами v2, автоматической помощью, поддержкой нескольких языков и динамическими переменными.", + "ctaStart": "Начать - 2 EUR/месяц", + "ctaLogin": "Войти", + "bots": { + "moderation": "Модерация", + "welcome": "Приветствие", + "analytics": "Аналитика" + }, + "status": { + "online": "В сети", + "stopped": "Остановлен" + }, + "card": { + "title": "Живой обзор флота", + "subtitle": "Отслеживайте каждый экземпляр бота из единой панели управления.", + "instanceMetrics": "Экземпляры: 1 | CPU 35%", + "manage": "Управлять", + "preview": "Реальное отображение статуса ваших ботов" + } + }, + "features": { + "title": "Основные функции", + "subtitle": "Всё необходимое для управления ботами в продакшене.", + "items": { + "autoCommands": { + "title": "Автоматические команды", + "description": "Prefix и slash команды автоматически создаются и синхронизируются для каждого бота." + }, + "componentsV2": { + "title": "Компоненты v2", + "description": "Полная поддержка компонентов v2 и надёжных интерактивных элементов." + }, + "autoHelp": { + "title": "Автоматическая помощь", + "description": "Автоматическая генерация справки и документации команд." + }, + "multiLanguage": { + "title": "Многоязычность", + "description": "Перевод сообщений и командных меток с лёгкой активацией нескольких языков." + }, + "dynamicVariables": { + "title": "Динамические переменные", + "description": "Динамические шаблоны с переменными, такими как bot_latency, guild_count и другие." + }, + "hosting": { + "title": "Хостинг и масштабируемость", + "description": "Простые тарифы от 2 EUR/месяц за бота с мониторингом и масштабированием." + } + } + }, + "howItWorks": { + "title": "Как это работает", + "subtitle": "Три простых шага для управления вашими ботами.", + "steps": { + "addBot": { + "title": "Добавить бота", + "description": "Подключите Discord-приложение через OAuth и зарегистрируйте его в Shadow." + }, + "dashboard": { + "title": "Управление через панель", + "description": "Получайте доступ к командам, логам и настройкам через единый интерфейс." + }, + "realtime": { + "title": "Управление в реальном времени", + "description": "Запускайте, останавливайте и масштабируйте ботов мгновенно по необходимости." + } + } + }, + "footer": { + "tagline": "Хостинг Discord-ботов - тарифы от 2 EUR/месяц за бота", + "github": "GitHub", + "copyright": "Авторские права {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Хостинг Discord-ботов", + "title": "Подключите свой аккаунт Discord", + "description": "Войдите через OAuth2, чтобы получить доступ к панели и размещать ботов.", + "cta": "Продолжить с Discord" + }, + "dashboard": { + "loading": "Загрузка панели управления...", + "sessionRequired": "Требуется сессия", + "loginRequired": "Требуется вход", + "loginDescription": "Ваша панель защищена. Войдите через Discord для управления ботами.", + "loginCta": "Войти", + "tenant": "Арендатор {tenantId}", + "role": "Роль: {role}", + "roles": { + "owner": "Владелец", + "member": "Участник" + }, + "logout": "Выйти", + "addBot": { + "title": "Добавить бота", + "description": "Токен проверяется через API и шифруется перед сохранением.", + "tokenLabel": "Токен Discord-бота", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Отображаемое имя (опционально)", + "displayNamePlaceholder": "Бот поддержки EU", + "submit": "Добавить бота", + "submitPending": "Проверка..." + }, + "bots": { + "title": "Мои боты", + "refresh": "Обновить", + "empty": "У этого арендатора нет ботов.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Последняя ошибка: {message}" + }, + "status": { + "stopped": "Остановлен", + "starting": "Запуск", + "running": "В сети", + "stopping": "Остановка", + "error": "Ошибка" + }, + "actions": { + "start": "Запустить", + "stop": "Остановить", + "restart": "Перезапустить" + }, + "errors": { + "fetchSession": "Не удалось получить пользовательскую сессию", + "fetchBots": "Не удалось получить список ботов", + "unexpected": "Неожиданная ошибка", + "addBot": "Не удалось добавить бота", + "actionFailed": "Ошибка действия: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/tr.json b/apps/web/locales/tr.json new file mode 100644 index 0000000..911f39a --- /dev/null +++ b/apps/web/locales/tr.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Discord bot barındırma (bot başına ayda 2 EUR’dan itibaren)", + "description": "Discord botlarınızı barındırmak ve yönetmek için çok kiracılı SaaS platformu; otomatik komutlar, v2 bileşenleri, çoklu dil ve dinamik değişkenler içerir." + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "Ana sayfa", + "dashboard": "Panel", + "login": "Giriş yap" + }, + "localeSwitcher": { + "label": "Dil", + "locales": { + "en": "İngilizce", + "es": "İspanyolca", + "de": "Almanca", + "ja": "Japonca", + "fr": "Fransızca", + "pt": "Portekizce", + "ru": "Rusça", + "it": "İtalyanca", + "nl": "Flemenkçe", + "pl": "Lehçe", + "zh": "Çince", + "hi": "Hintçe", + "ar": "Arapça", + "bn": "Bengalce", + "id": "Endonezce", + "tr": "Türkçe" + } + }, + "hero": { + "kicker": "Discord botları için SaaS platformu", + "title": "Shadow - Discord botlarınızı barındırın (bot başına ayda 2 EUR’dan itibaren)", + "description": "Botlarınızı otomatik komutlar (prefix ve slash), v2 bileşenleri, otomatik yardım, çoklu dil desteği ve dinamik değişkenlerle barındırın ve yönetin.", + "ctaStart": "Başla - 2 EUR/ay", + "ctaLogin": "Giriş yap", + "bots": { + "moderation": "Moderasyon", + "welcome": "Hoş geldin", + "analytics": "Analitik" + }, + "status": { + "online": "Çevrimiçi", + "stopped": "Durduruldu" + }, + "card": { + "title": "Canlı filo görünümü", + "subtitle": "Tüm bot instance’larını tek bir operasyon panelinden izleyin.", + "instanceMetrics": "Instance: 1 | CPU %35", + "manage": "Yönet", + "preview": "Botlarınızın gerçek zamanlı durum önizlemesi" + } + }, + "features": { + "title": "Ana özellikler", + "subtitle": "Botlarınızı üretimde yönetmek için ihtiyacınız olan her şey.", + "items": { + "autoCommands": { + "title": "Otomatik komutlar", + "description": "Prefix ve slash komutları her bot için otomatik olarak oluşturulur ve senkronize edilir." + }, + "componentsV2": { + "title": "v2 bileşenleri", + "description": "v2 bileşenleri ve zengin etkileşimler için tam destek." + }, + "autoHelp": { + "title": "Otomatik yardım", + "description": "Komutlar için otomatik oluşturulan yardım ve dokümantasyon." + }, + "multiLanguage": { + "title": "Çoklu dil", + "description": "Mesajları ve komut etiketlerini çevirin, kolayca birden fazla dili etkinleştirin." + }, + "dynamicVariables": { + "title": "Dinamik değişkenler", + "description": "bot_latency, guild_count gibi değişkenlerle dinamik şablonlar." + }, + "hosting": { + "title": "Barındırma ve ölçeklenebilirlik", + "description": "Bot başına 2 EUR/ay basit planlar, izleme ve ölçeklenebilirlik dahil." + } + } + }, + "howItWorks": { + "title": "Nasıl çalışır", + "subtitle": "Botlarınızı kontrol altına almak için üç basit adım.", + "steps": { + "addBot": { + "title": "Bot ekle", + "description": "Discord uygulamanızı OAuth ile bağlayın ve Shadow’a kaydedin." + }, + "dashboard": { + "title": "Panelden yönetin", + "description": "Komutlara, loglara ve ayarlara merkezi bir arayüzden erişin." + }, + "realtime": { + "title": "Gerçek zamanlı kontrol", + "description": "Botlarınızı anında başlatın, durdurun ve ölçeklendirin." + } + } + }, + "footer": { + "tagline": "Discord bot barındırma - bot başına 2 EUR’dan başlayan planlar", + "github": "GitHub", + "copyright": "Telif hakkı {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Discord bot barındırma", + "title": "Discord hesabınızla giriş yapın", + "description": "Botlarınızı barındırmak ve panelinize erişmek için OAuth2 ile kimlik doğrulaması yapın.", + "cta": "Discord ile devam et" + }, + "dashboard": { + "loading": "Panel yükleniyor...", + "sessionRequired": "Oturum gerekli", + "loginRequired": "Giriş gerekli", + "loginDescription": "Paneliniz korunmaktadır. Botlarınızı yönetmek için Discord ile giriş yapın.", + "loginCta": "Giriş yap", + "tenant": "Tenant {tenantId}", + "role": "Rol: {role}", + "roles": { + "owner": "Sahip", + "member": "Üye" + }, + "logout": "Çıkış yap", + "addBot": { + "title": "Bot ekle", + "description": "Token API tarafında doğrulanır ve ardından şifrelenerek saklanır.", + "tokenLabel": "Discord bot token", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "Görünen ad (isteğe bağlı)", + "displayNamePlaceholder": "EU destek botu", + "submit": "Botu ekle", + "submitPending": "Doğrulanıyor..." + }, + "bots": { + "title": "Botlarım", + "refresh": "Yenile", + "empty": "Bu tenant için bot yok.", + "discordId": "Discord ID: {discordBotId}", + "lastError": "Son hata: {message}" + }, + "status": { + "stopped": "Durduruldu", + "starting": "Başlatılıyor", + "running": "Çevrimiçi", + "stopping": "Durduruluyor", + "error": "Hata" + }, + "actions": { + "start": "Başlat", + "stop": "Durdur", + "restart": "Yeniden başlat" + }, + "errors": { + "fetchSession": "Kullanıcı oturumu alınamadı", + "fetchBots": "Bot listesi alınamadı", + "unexpected": "Beklenmeyen hata", + "addBot": "Bot eklenemedi", + "actionFailed": "İşlem başarısız: {action}" + } + } +} \ No newline at end of file diff --git a/apps/web/locales/zh.json b/apps/web/locales/zh.json new file mode 100644 index 0000000..995c9d7 --- /dev/null +++ b/apps/web/locales/zh.json @@ -0,0 +1,167 @@ +{ + "metadata": { + "title": "Shadow - Discord 机器人托管(每个机器人每月 2 欧元起)", + "description": "多租户 SaaS 平台,用于托管和管理你的 Discord 机器人,支持自动命令、v2 组件、多语言和动态变量。" + }, + "common": { + "brand": "Shadow" + }, + "nav": { + "home": "首页", + "dashboard": "控制面板", + "login": "登录" + }, + "localeSwitcher": { + "label": "语言", + "locales": { + "en": "英语", + "es": "西班牙语", + "de": "德语", + "ja": "日语", + "fr": "法语", + "pt": "葡萄牙语", + "ru": "俄语", + "it": "意大利语", + "nl": "荷兰语", + "pl": "波兰语", + "zh": "中文", + "hi": "印地语", + "ar": "阿拉伯语", + "bn": "孟加拉语", + "id": "印尼语", + "tr": "土耳其语" + } + }, + "hero": { + "kicker": "Discord 机器人 SaaS 平台", + "title": "Shadow - 托管你的 Discord 机器人(每个机器人每月 2 欧元起)", + "description": "托管并管理你的机器人,支持自动命令(前缀与斜杠)、v2 组件、自动帮助、多语言支持和动态变量。", + "ctaStart": "开始使用 - 每月 2 欧元", + "ctaLogin": "登录", + "bots": { + "moderation": "管理", + "welcome": "欢迎", + "analytics": "分析" + }, + "status": { + "online": "在线", + "stopped": "已停止" + }, + "card": { + "title": "机器人集群实时概览", + "subtitle": "从统一的操作界面监控每个机器人实例。", + "instanceMetrics": "实例:1 | CPU 35%", + "manage": "管理", + "preview": "机器人状态实时预览" + } + }, + "features": { + "title": "核心功能", + "subtitle": "生产环境中管理机器人所需的一切。", + "items": { + "autoCommands": { + "title": "自动命令", + "description": "每个机器人都会自动生成并同步前缀命令与斜杠命令。" + }, + "componentsV2": { + "title": "v2 组件", + "description": "完全支持 v2 组件和丰富交互功能。" + }, + "autoHelp": { + "title": "自动帮助", + "description": "自动生成命令帮助与文档。" + }, + "multiLanguage": { + "title": "多语言", + "description": "轻松翻译消息与命令标签并启用多语言支持。" + }, + "dynamicVariables": { + "title": "动态变量", + "description": "支持模板变量,例如 bot_latency、guild_count 等。" + }, + "hosting": { + "title": "托管与扩展性", + "description": "每个机器人每月 2 欧元起的简单方案,包含监控与自动扩展。" + } + } + }, + "howItWorks": { + "title": "工作原理", + "subtitle": "三步即可掌控你的机器人。", + "steps": { + "addBot": { + "title": "添加机器人", + "description": "通过 OAuth 连接你的 Discord 应用并注册到 Shadow。" + }, + "dashboard": { + "title": "在控制面板管理", + "description": "通过统一界面访问命令、日志和设置。" + }, + "realtime": { + "title": "实时控制", + "description": "根据需求即时启动、停止或扩展你的机器人。" + } + } + }, + "footer": { + "tagline": "Discord 机器人托管 - 每个机器人每月 2 欧元起", + "github": "GitHub", + "copyright": "版权所有 {year} {brand}" + }, + "login": { + "eyebrow": "Shadow - Discord 机器人托管", + "title": "连接你的 Discord 账号", + "description": "通过 OAuth2 登录以访问你的空间并托管机器人。", + "cta": "使用 Discord 继续" + }, + "dashboard": { + "loading": "正在加载控制面板...", + "sessionRequired": "需要会话", + "loginRequired": "需要登录", + "loginDescription": "你的控制面板受保护,请通过 Discord 登录以管理机器人。", + "loginCta": "登录", + "tenant": "租户 {tenantId}", + "role": "角色:{role}", + "roles": { + "owner": "所有者", + "member": "成员" + }, + "logout": "退出登录", + "addBot": { + "title": "添加机器人", + "description": "Token 会在 API 端验证并在存储前加密。", + "tokenLabel": "Discord 机器人 Token", + "tokenPlaceholder": "MTIz...", + "displayNameLabel": "显示名称(可选)", + "displayNamePlaceholder": "EU 支持机器人", + "submit": "添加机器人", + "submitPending": "验证中..." + }, + "bots": { + "title": "我的机器人", + "refresh": "刷新", + "empty": "该租户暂无机器人。", + "discordId": "Discord ID:{discordBotId}", + "lastError": "最近错误:{message}" + }, + "status": { + "stopped": "已停止", + "starting": "启动中", + "running": "运行中", + "stopping": "停止中", + "error": "错误" + }, + "actions": { + "start": "启动", + "stop": "停止", + "restart": "重启" + }, + "errors": { + "fetchSession": "无法获取用户会话", + "fetchBots": "无法获取机器人列表", + "unexpected": "未知错误", + "addBot": "无法添加机器人", + "actionFailed": "操作失败:{action}" + } + } +} \ No newline at end of file diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json deleted file mode 100644 index 9be287c..0000000 --- a/apps/web/messages/fr.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "metadata": { - "title": "Shadow - Hebergement de bots Discord (a partir de 2 EUR/mois par bot)", - "description": "Plateforme SaaS multi-tenant pour heberger et gerer vos bots Discord avec commandes automatiques, components v2, multi-langue et variables dynamiques." - }, - "common": { - "brand": "Shadow" - }, - "nav": { - "home": "Accueil", - "dashboard": "Dashboard", - "login": "Se connecter" - }, - "localeSwitcher": { - "label": "Langue", - "locales": { - "en": "English", - "fr": "Francais" - } - }, - "hero": { - "kicker": "Plateforme SaaS pour bots Discord", - "title": "Shadow - Hebergez vos bots Discord (a partir de 2 EUR/mois par bot)", - "description": "Hebergez et gerez vos bots avec commandes automatiques (prefix et slash), components v2, aide automatique, support multi-langue et variables dynamiques.", - "ctaStart": "Commencer - 2 EUR/mois", - "ctaLogin": "Se connecter", - "bots": { - "moderation": "Moderation", - "welcome": "Bienvenue", - "analytics": "Analytics" - }, - "status": { - "online": "En ligne", - "stopped": "Arrete" - }, - "card": { - "title": "Apercu live de la flotte", - "subtitle": "Suivez chaque instance bot depuis une surface operationnelle unique.", - "instanceMetrics": "Instances: 1 | CPU 35%", - "manage": "Gerer", - "preview": "Apercu en temps reel du statut de vos bots" - } - }, - "features": { - "title": "Fonctionnalites principales", - "subtitle": "Tout ce quil faut pour piloter vos bots en production.", - "items": { - "autoCommands": { - "title": "Commandes automatiques", - "description": "Les commandes prefix et slash sont generees et synchronisees automatiquement pour chaque bot." - }, - "componentsV2": { - "title": "Components v2", - "description": "Support complet des components v2 et des interactions riches fiables." - }, - "autoHelp": { - "title": "Aide automatique", - "description": "Aide et documentation des commandes generees automatiquement." - }, - "multiLanguage": { - "title": "Multi-langue", - "description": "Traduisez les messages et les labels de commandes puis activez plusieurs langues facilement." - }, - "dynamicVariables": { - "title": "Variables dynamiques", - "description": "Templates dynamiques avec des variables comme bot_latency, guild_count et plus." - }, - "hosting": { - "title": "Hebergement et scalabilite", - "description": "Plans simples a 2 EUR/mois par bot avec monitoring et scalabilite inclus." - } - } - }, - "howItWorks": { - "title": "Comment ca marche", - "subtitle": "Trois etapes simples pour prendre le controle de vos bots.", - "steps": { - "addBot": { - "title": "Ajouter un bot", - "description": "Connectez votre application Discord via OAuth et enregistrez-la dans Shadow." - }, - "dashboard": { - "title": "Gerer depuis le dashboard", - "description": "Accedez aux commandes, aux logs et aux parametres depuis une interface centralisee." - }, - "realtime": { - "title": "Controler en temps reel", - "description": "Demarrez, arretez et scalez vos bots instantanement selon la demande." - } - } - }, - "footer": { - "tagline": "Hebergement de bots Discord - plans des 2 EUR/mois par bot", - "github": "GitHub", - "copyright": "Copyright {year} {brand}" - }, - "login": { - "eyebrow": "Shadow - Hebergement de bots Discord", - "title": "Connectez votre compte Discord", - "description": "Authentifiez-vous via OAuth2 pour acceder a votre espace et heberger vos bots.", - "cta": "Continuer avec Discord" - }, - "dashboard": { - "loading": "Chargement du dashboard...", - "sessionRequired": "Session requise", - "loginRequired": "Connexion necessaire", - "loginDescription": "Votre dashboard est protege. Connectez-vous via Discord pour administrer vos bots.", - "loginCta": "Se connecter", - "tenant": "Tenant {tenantId}", - "role": "Role: {role}", - "roles": { - "owner": "Proprietaire", - "member": "Membre" - }, - "logout": "Se deconnecter", - "addBot": { - "title": "Ajouter un bot", - "description": "Le token est verifie cote API puis chiffre avant stockage.", - "tokenLabel": "Token bot Discord", - "tokenPlaceholder": "MTIz...", - "displayNameLabel": "Nom daffichage (optionnel)", - "displayNamePlaceholder": "Bot support EU", - "submit": "Ajouter le bot", - "submitPending": "Validation en cours..." - }, - "bots": { - "title": "Mes bots", - "refresh": "Rafraichir", - "empty": "Aucun bot pour ce tenant.", - "discordId": "Discord ID: {discordBotId}", - "lastError": "Derniere erreur: {message}" - }, - "status": { - "stopped": "Arrete", - "starting": "Demarrage", - "running": "En ligne", - "stopping": "Arret", - "error": "Erreur" - }, - "actions": { - "start": "Demarrer", - "stop": "Arreter", - "restart": "Redemarrer" - }, - "errors": { - "fetchSession": "Impossible de recuperer la session utilisateur", - "fetchBots": "Impossible de recuperer la liste des bots", - "unexpected": "Erreur inattendue", - "addBot": "Ajout du bot impossible", - "actionFailed": "Action impossible: {action}" - } - } -} diff --git a/package.json b/package.json index 0f6cc3b..e0ee84b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "scripts": { "build": "npm run build --workspaces --if-present", "typecheck": "npm run typecheck --workspaces --if-present", + "i18n:sync": "node ./scripts/i18n/sync-locales.mjs", + "i18n:check": "node ./scripts/i18n/sync-locales.mjs --check", "dev:api": "npm run build -w @saas/shared && npm run dev -w @saas/api", "dev:bot": "npm run build -w @saas/shared && npm run dev -w @saas/bot", "dev:web": "npm run dev -w @saas/web", diff --git a/scripts/i18n/sync-locales.mjs b/scripts/i18n/sync-locales.mjs new file mode 100644 index 0000000..24100af --- /dev/null +++ b/scripts/i18n/sync-locales.mjs @@ -0,0 +1,139 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const SUPPORTED_LOCALES = [ + "en", + "es", + "de", + "ja", + "fr", + "pt", + "ru", + "it", + "nl", + "pl", + "zh", + "hi", + "ar", + "bn", + "id", + "tr", +]; + +const TARGETS = [ + { + name: "bot", + directory: "apps/bot/locales", + }, + { + name: "web", + directory: "apps/web/locales", + }, +]; + +const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = path.resolve(SCRIPT_DIR, "..", ".."); +const CHECK_ONLY = process.argv.includes("--check"); + +const isObject = (value) => { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +}; + +const readJsonFile = (filePath) => { + const raw = readFileSync(filePath, "utf-8"); + return JSON.parse(raw); +}; + +const writeJsonFile = (filePath, content) => { + writeFileSync(filePath, `${JSON.stringify(content, null, 2)}\n`, "utf-8"); +}; + +const shapeFromBase = (base, override) => { + if (Array.isArray(base)) { + return Array.isArray(override) ? override : base; + } + + if (isObject(base)) { + const result = {}; + const overrideObject = isObject(override) ? override : {}; + + for (const [key, value] of Object.entries(base)) { + result[key] = shapeFromBase(value, overrideObject[key]); + } + + return result; + } + + return typeof override === typeof base ? override : base; +}; + +const collectLeafPaths = (input, prefix = "", output = []) => { + if (isObject(input)) { + for (const [key, value] of Object.entries(input)) { + const nextPrefix = prefix.length > 0 ? `${prefix}.${key}` : key; + collectLeafPaths(value, nextPrefix, output); + } + return output; + } + + output.push(prefix); + return output; +}; + +let hasError = false; + +for (const target of TARGETS) { + const targetDirectory = path.resolve(REPO_ROOT, target.directory); + const defaultLocalePath = path.join(targetDirectory, "en.json"); + + if (!existsSync(defaultLocalePath)) { + console.error(`[i18n:${target.name}] missing default locale file: ${defaultLocalePath}`); + hasError = true; + continue; + } + + mkdirSync(targetDirectory, { recursive: true }); + + const baseDictionary = readJsonFile(defaultLocalePath); + const baseLeafPaths = new Set(collectLeafPaths(baseDictionary)); + + for (const locale of SUPPORTED_LOCALES) { + const localePath = path.join(targetDirectory, `${locale}.json`); + const localeExists = existsSync(localePath); + const currentDictionary = localeExists ? readJsonFile(localePath) : {}; + const normalizedDictionary = shapeFromBase(baseDictionary, currentDictionary); + + const normalizedText = JSON.stringify(normalizedDictionary, null, 2); + const currentText = JSON.stringify(currentDictionary, null, 2); + + if (!CHECK_ONLY && (!localeExists || normalizedText !== currentText)) { + writeJsonFile(localePath, normalizedDictionary); + console.log(`[i18n:${target.name}] synced ${locale}.json`); + } + + if (CHECK_ONLY) { + const localeLeafPaths = new Set(collectLeafPaths(currentDictionary)); + + const missingPaths = [...baseLeafPaths].filter((key) => !localeLeafPaths.has(key)); + const extraPaths = [...localeLeafPaths].filter((key) => !baseLeafPaths.has(key)); + + if (missingPaths.length > 0 || extraPaths.length > 0) { + hasError = true; + console.error(`[i18n:${target.name}] ${locale}.json is out of sync`); + + if (missingPaths.length > 0) { + console.error(` missing keys: ${missingPaths.join(", ")}`); + } + + if (extraPaths.length > 0) { + console.error(` extra keys: ${extraPaths.join(", ")}`); + } + } + } + } +} + +if (CHECK_ONLY && hasError) { + process.exitCode = 1; +}