diff --git a/src/locales/ar.json b/src/locales/ar.json index ebb7326c..bb2114a7 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "نقطة محفظة مطلوبة.", "errorPublishedInvalid": "فشل التحقق من الحدث المنشور. الرجاء التحديث والمحاولة مرة أخرى." }, + "campaignsDetail": { + "seoTitle": "{{title}} | حملات {{appName}}", + "seoDescriptionFallback": "ادعم {{title}} على {{appName}}.", + "deadlineEndedOn": "انتهى في {{date}}", + "deadlineEndsToday": "ينتهي اليوم", + "deadlineDaysLeft_one": "بقي {{count}} يوم", + "deadlineDaysLeft_other": "بقي {{count}} يومًا", + "deadlineEndsOn": "ينتهي في {{date}}", + "back": "رجوع", + "edit": "تعديل", + "delete": "حذف", + "deleting": "جارٍ الحذف…", + "byAuthor": "بواسطة <0>{{name}}", + "commentLabel": "تعليق", + "linkCopied": "تم نسخ الرابط إلى الحافظة", + "pinnedToast": "تم التثبيت على الحملة", + "unpinnedToast": "تم إلغاء التثبيت", + "pinFailed": "تعذّر تحديث المثبتات", + "deletedToast": "تم حذف الحملة", + "deletedToastDesc": "تم نشر طلب حذف. ستزيل المرحّلات المتعاونة الحملة من الخلاصات.", + "deleteErrorTitle": "تعذّر حذف الحملة", + "campaigner": "صاحب الحملة", + "repost_one": "<0>{{count}} إعادة نشر", + "repost_other": "<0>{{count}} إعادات نشر", + "quote_one": "<0>{{count}} اقتباس", + "quote_other": "<0>{{count}} اقتباسات", + "like_one": "<0>{{count}} إعجاب", + "like_other": "<0>{{count}} إعجابات", + "commentsAndDonations": "التعليقات والتبرعات", + "commentCount_one": "{{count}} تعليق", + "commentCount_other": "{{count}} تعليقات", + "noCommentsTitle": "لا توجد تعليقات بعد", + "noCommentsHint": "كن أول من يترك رسالة دعم.", + "deleteDialogTitle": "حذف هذه الحملة؟", + "deleteDialogBody": "هذا ينشر طلب حذف NIP-09. ستزيل المرحّلات المتعاونة الحملة من الخلاصات والروابط المباشرة. تبقى إيصالات التبرعات السابقة على السلسلة بغض النظر. لا يمكن التراجع عن هذا الإجراء — لمواصلة قبول التبرعات، عدّل الحملة بدلًا من ذلك.", + "storyHeading": "القصة", + "storyEmpty": "لم يكتب المنظم قصة هذه الحملة بعد.", + "campaignEnded": "انتهت الحملة", + "donate": "تبرّع", + "share": "مشاركة", + "target": "الهدف: {{amount}}", + "raised": "مجموع", + "ofGoal": "من هدف {{amount}}", + "donationCount_one": "{{count}} تبرع", + "donationCount_other": "{{count}} تبرعات", + "recentDonations": "التبرعات الأخيرة", + "seeAllDonations_one": "عرض {{count}} تبرع", + "seeAllDonations_other": "عرض جميع {{count}} تبرع" + }, "campaigns": { "home": { "seoTitle": "حملات تمويل", diff --git a/src/locales/en.json b/src/locales/en.json index 2ab58715..2716387b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "Wallet endpoint is required.", "errorPublishedInvalid": "Published event failed validation. Please refresh and try again." }, + "campaignsDetail": { + "seoTitle": "{{title}} | {{appName}} Fundraisers", + "seoDescriptionFallback": "Support {{title}} on {{appName}}.", + "deadlineEndedOn": "Ended {{date}}", + "deadlineEndsToday": "Ends today", + "deadlineDaysLeft_one": "{{count}} day left", + "deadlineDaysLeft_other": "{{count}} days left", + "deadlineEndsOn": "Ends {{date}}", + "back": "Back", + "edit": "Edit", + "delete": "Delete", + "deleting": "Deleting…", + "byAuthor": "by <0>{{name}}", + "commentLabel": "Comment", + "linkCopied": "Link copied to clipboard", + "pinnedToast": "Pinned to campaign", + "unpinnedToast": "Unpinned from campaign", + "pinFailed": "Failed to update campaign pins", + "deletedToast": "Campaign deleted", + "deletedToastDesc": "A deletion request was published. Well-behaved relays will drop the campaign from feeds.", + "deleteErrorTitle": "Could not delete campaign", + "campaigner": "Campaigner", + "repost_one": "<0>{{count}} Repost", + "repost_other": "<0>{{count}} Reposts", + "quote_one": "<0>{{count}} Quote", + "quote_other": "<0>{{count}} Quotes", + "like_one": "<0>{{count}} Like", + "like_other": "<0>{{count}} Likes", + "commentsAndDonations": "Comments & donations", + "commentCount_one": "{{count}} comment", + "commentCount_other": "{{count}} comments", + "noCommentsTitle": "No comments yet", + "noCommentsHint": "Be the first to leave a message of support.", + "deleteDialogTitle": "Delete this campaign?", + "deleteDialogBody": "This publishes a NIP-09 deletion request. Well-behaved relays will drop the campaign from feeds and direct links. Past donation receipts stay on-chain regardless. This action cannot be undone — to keep accepting donations, edit the campaign instead.", + "storyHeading": "The story", + "storyEmpty": "The organizer hasn't written a story for this campaign yet.", + "campaignEnded": "Campaign ended", + "donate": "Donate", + "share": "Share", + "target": "Target: {{amount}}", + "raised": "raised", + "ofGoal": "of {{amount}} goal", + "donationCount_one": "{{count}} donation", + "donationCount_other": "{{count}} donations", + "recentDonations": "Recent donations", + "seeAllDonations_one": "See all {{count}} donation", + "seeAllDonations_other": "See all {{count}} donations" + }, "campaigns": { "home": { "seoTitle": "Fundraisers", diff --git a/src/locales/es.json b/src/locales/es.json index 75859c78..c51e79df 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "Se requiere un punto de cartera.", "errorPublishedInvalid": "El evento publicado falló la validación. Recarga e inténtalo de nuevo." }, + "campaignsDetail": { + "seoTitle": "{{title}} | Recaudaciones de {{appName}}", + "seoDescriptionFallback": "Apoya {{title}} en {{appName}}.", + "deadlineEndedOn": "Finalizó el {{date}}", + "deadlineEndsToday": "Finaliza hoy", + "deadlineDaysLeft_one": "Queda {{count}} día", + "deadlineDaysLeft_other": "Quedan {{count}} días", + "deadlineEndsOn": "Finaliza el {{date}}", + "back": "Atrás", + "edit": "Editar", + "delete": "Eliminar", + "deleting": "Eliminando…", + "byAuthor": "por <0>{{name}}", + "commentLabel": "Comentar", + "linkCopied": "Enlace copiado al portapapeles", + "pinnedToast": "Fijado a la campaña", + "unpinnedToast": "Quitado de la campaña", + "pinFailed": "No se pudieron actualizar los fijados", + "deletedToast": "Campaña eliminada", + "deletedToastDesc": "Se publicó una solicitud de eliminación. Los relés que se comportan bien quitarán la campaña de los feeds.", + "deleteErrorTitle": "No se pudo eliminar la campaña", + "campaigner": "Organizador", + "repost_one": "<0>{{count}} Reposteo", + "repost_other": "<0>{{count}} Reposteos", + "quote_one": "<0>{{count}} Cita", + "quote_other": "<0>{{count}} Citas", + "like_one": "<0>{{count}} Me gusta", + "like_other": "<0>{{count}} Me gusta", + "commentsAndDonations": "Comentarios y donaciones", + "commentCount_one": "{{count}} comentario", + "commentCount_other": "{{count}} comentarios", + "noCommentsTitle": "Aún no hay comentarios", + "noCommentsHint": "Sé la primera persona en dejar un mensaje de apoyo.", + "deleteDialogTitle": "¿Eliminar esta campaña?", + "deleteDialogBody": "Esto publica una solicitud de eliminación NIP-09. Los relés que se comportan bien quitarán la campaña de los feeds y los enlaces directos. Los recibos de donaciones pasadas quedan en cadena de todos modos. Esta acción no se puede deshacer — para seguir aceptando donaciones, edita la campaña en su lugar.", + "storyHeading": "La historia", + "storyEmpty": "El organizador todavía no ha escrito la historia para esta campaña.", + "campaignEnded": "Campaña finalizada", + "donate": "Donar", + "share": "Compartir", + "target": "Meta: {{amount}}", + "raised": "recaudado", + "ofGoal": "de la meta de {{amount}}", + "donationCount_one": "{{count}} donación", + "donationCount_other": "{{count}} donaciones", + "recentDonations": "Donaciones recientes", + "seeAllDonations_one": "Ver la {{count}} donación", + "seeAllDonations_other": "Ver las {{count}} donaciones" + }, "campaigns": { "home": { "seoTitle": "Recaudaciones", diff --git a/src/locales/fa.json b/src/locales/fa.json index 0e00a13f..7bd28093 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "نقطهٔ کیف پول الزامی است.", "errorPublishedInvalid": "رویداد منتشرشده اعتبارسنجی نشد. لطفاً صفحه را به‌روز کن و دوباره تلاش کن." }, + "campaignsDetail": { + "seoTitle": "{{title}} | کمپین‌های {{appName}}", + "seoDescriptionFallback": "از {{title}} در {{appName}} حمایت کن.", + "deadlineEndedOn": "در {{date}} پایان یافت", + "deadlineEndsToday": "امروز پایان می‌یابد", + "deadlineDaysLeft_one": "{{count}} روز باقی", + "deadlineDaysLeft_other": "{{count}} روز باقی", + "deadlineEndsOn": "در {{date}} پایان می‌یابد", + "back": "بازگشت", + "edit": "ویرایش", + "delete": "حذف", + "deleting": "در حال حذف…", + "byAuthor": "توسط <0>{{name}}", + "commentLabel": "نظر", + "linkCopied": "پیوند در کلیپ‌بورد کپی شد", + "pinnedToast": "به کمپین سنجاق شد", + "unpinnedToast": "از کمپین جدا شد", + "pinFailed": "به‌روزرسانی سنجاق‌ها ناموفق بود", + "deletedToast": "کمپین حذف شد", + "deletedToastDesc": "درخواست حذف منتشر شد. رله‌های همکار کمپین را از فیدها حذف خواهند کرد.", + "deleteErrorTitle": "حذف کمپین ممکن نشد", + "campaigner": "میزبان کمپین", + "repost_one": "<0>{{count}} بازنشر", + "repost_other": "<0>{{count}} بازنشر", + "quote_one": "<0>{{count}} نقل‌قول", + "quote_other": "<0>{{count}} نقل‌قول", + "like_one": "<0>{{count}} پسند", + "like_other": "<0>{{count}} پسند", + "commentsAndDonations": "نظرها و کمک‌ها", + "commentCount_one": "{{count}} نظر", + "commentCount_other": "{{count}} نظر", + "noCommentsTitle": "هنوز نظری نیست", + "noCommentsHint": "اولین کسی باش که پیام حمایت می‌گذارد.", + "deleteDialogTitle": "این کمپین حذف شود؟", + "deleteDialogBody": "این یک درخواست حذف NIP-09 منتشر می‌کند. رله‌های همکار کمپین را از فیدها و پیوندهای مستقیم حذف خواهند کرد. رسیدهای کمک‌های گذشته در زنجیره باقی می‌مانند. این کار قابل بازگشت نیست — برای ادامهٔ دریافت کمک، به‌جای حذف کمپین را ویرایش کن.", + "storyHeading": "داستان", + "storyEmpty": "سازمان‌دهنده هنوز داستانی برای این کمپین ننوشته است.", + "campaignEnded": "کمپین پایان یافت", + "donate": "کمک کنید", + "share": "هم‌رسانی", + "target": "هدف: {{amount}}", + "raised": "جمع‌آوری‌شده", + "ofGoal": "از هدف {{amount}}", + "donationCount_one": "{{count}} کمک", + "donationCount_other": "{{count}} کمک", + "recentDonations": "کمک‌های اخیر", + "seeAllDonations_one": "نمایش همهٔ {{count}} کمک", + "seeAllDonations_other": "نمایش همهٔ {{count}} کمک" + }, "campaigns": { "home": { "seoTitle": "کمپین‌های جذب کمک مالی", diff --git a/src/locales/km.json b/src/locales/km.json index a0064748..8792b89d 100644 --- a/src/locales/km.json +++ b/src/locales/km.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "ត្រូវការចំណុចកាបូប។", "errorPublishedInvalid": "ព្រឹត្តិការណ៍ដែលបានផ្សព្វផ្សាយបរាជ័យក្នុងការវាយតម្លៃ។ សូមផ្ទុកឡើងវិញ ហើយព្យាយាមម្តងទៀត។" }, + "campaignsDetail": { + "seoTitle": "{{title}} | យុទ្ធនាការរបស់ {{appName}}", + "seoDescriptionFallback": "គាំទ្រ {{title}} នៅលើ {{appName}}។", + "deadlineEndedOn": "បានបញ្ចប់នៅ {{date}}", + "deadlineEndsToday": "បញ្ចប់នៅថ្ងៃនេះ", + "deadlineDaysLeft_one": "នៅសល់ {{count}} ថ្ងៃ", + "deadlineDaysLeft_other": "នៅសល់ {{count}} ថ្ងៃ", + "deadlineEndsOn": "បញ្ចប់នៅ {{date}}", + "back": "ត្រឡប់", + "edit": "កែសម្រួល", + "delete": "លុប", + "deleting": "កំពុងលុប…", + "byAuthor": "ដោយ <0>{{name}}", + "commentLabel": "មតិយោបល់", + "linkCopied": "បានចម្លងតំណទៅក្តារតម្បៀតខ្ទាស់", + "pinnedToast": "បានបោះខ្ទាស់ទៅយុទ្ធនាការ", + "unpinnedToast": "បានដកខ្ទាស់ចេញពីយុទ្ធនាការ", + "pinFailed": "មិនអាចធ្វើបច្ចុប្បន្នភាពខ្ទាស់យុទ្ធនាការ", + "deletedToast": "បានលុបយុទ្ធនាការ", + "deletedToastDesc": "សំណើលុបត្រូវបានផ្សព្វផ្សាយ។ Relay ដែលអនុលោមនឹងលុបយុទ្ធនាការចេញពី feed។", + "deleteErrorTitle": "មិនអាចលុបយុទ្ធនាការ", + "campaigner": "អ្នករៀបចំ", + "repost_one": "<0>{{count}} Repost", + "repost_other": "<0>{{count}} Reposts", + "quote_one": "<0>{{count}} ការដកស្រង់", + "quote_other": "<0>{{count}} ការដកស្រង់", + "like_one": "<0>{{count}} ចូលចិត្ត", + "like_other": "<0>{{count}} ចូលចិត្ត", + "commentsAndDonations": "មតិយោបល់ និងការបរិច្ចាគ", + "commentCount_one": "{{count}} មតិយោបល់", + "commentCount_other": "{{count}} មតិយោបល់", + "noCommentsTitle": "មិនទាន់មានមតិយោបល់", + "noCommentsHint": "ក្លាយជាមនុស្សដំបូងផ្ញើសារគាំទ្រ។", + "deleteDialogTitle": "លុបយុទ្ធនាការនេះមែនទេ?", + "deleteDialogBody": "នេះផ្សព្វផ្សាយសំណើលុប NIP-09។ Relay ដែលអនុលោមនឹងលុបយុទ្ធនាការចេញពី feed និងតំណផ្ទាល់។ បង្កាន់ដៃនៃការបរិច្ចាគកន្លងមកនៅតែស្ថិតលើខ្សែសង្វាក់។ សកម្មភាពនេះមិនអាចត្រឡប់វិញបានទេ — ដើម្បីបន្តទទួលការបរិច្ចាគ កែសម្រួលយុទ្ធនាការជំនួស។", + "storyHeading": "រឿង", + "storyEmpty": "អ្នករៀបចំមិនទាន់សរសេររឿងសម្រាប់យុទ្ធនាការនេះទេ។", + "campaignEnded": "យុទ្ធនាការបានបញ្ចប់", + "donate": "បរិច្ចាគ", + "share": "ចែករំលែក", + "target": "គោលដៅ៖ {{amount}}", + "raised": "បានរៃអង្គាស", + "ofGoal": "នៃគោលដៅ {{amount}}", + "donationCount_one": "{{count}} ការបរិច្ចាគ", + "donationCount_other": "{{count}} ការបរិច្ចាគ", + "recentDonations": "ការបរិច្ចាគថ្មីៗ", + "seeAllDonations_one": "មើលការបរិច្ចាគ {{count}} ទាំងអស់", + "seeAllDonations_other": "មើលការបរិច្ចាគ {{count}} ទាំងអស់" + }, "campaigns": { "home": { "seoTitle": "យុទ្ធនាការប្រមូលមូលនិធិ", diff --git a/src/locales/ps.json b/src/locales/ps.json index a02b26f4..1cb4ba6a 100644 --- a/src/locales/ps.json +++ b/src/locales/ps.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "د پاکټ نقطه اړینه ده.", "errorPublishedInvalid": "خپره شوې پیښه اعتبار نه ولاره. مهرباني وکړئ پاڼه تازه کړئ او بیا هڅه وکړئ." }, + "campaignsDetail": { + "seoTitle": "{{title}} | د {{appName}} کمپاینونه", + "seoDescriptionFallback": "په {{appName}} کې د {{title}} ملاتړ وکړئ.", + "deadlineEndedOn": "په {{date}} پای ته ورسیده", + "deadlineEndsToday": "نن پای ته رسي", + "deadlineDaysLeft_one": "{{count}} ورځ پاتې", + "deadlineDaysLeft_other": "{{count}} ورځې پاتې", + "deadlineEndsOn": "په {{date}} پای ته رسي", + "back": "شاته", + "edit": "سمول", + "delete": "ړنګول", + "deleting": "ړنګېږي…", + "byAuthor": "د <0>{{name}} له لوري", + "commentLabel": "تبصره", + "linkCopied": "لینک کلپ‌بورډ ته کاپي شو", + "pinnedToast": "کمپاین ته ونښلول شو", + "unpinnedToast": "له کمپاین جلا شو", + "pinFailed": "د نښلونو تازه کول پاتې راغلل", + "deletedToast": "کمپاین ړنګ شو", + "deletedToastDesc": "د ړنګولو غوښتنه خپره شوه. ښه چلند کوونکي ریلې به کمپاین له فیدونو څخه لرې کړي.", + "deleteErrorTitle": "کمپاین نه ړنګېدلای شي", + "campaigner": "کمپاینوال", + "repost_one": "<0>{{count}} بیا-خپرول", + "repost_other": "<0>{{count}} بیا-خپرونې", + "quote_one": "<0>{{count}} نقل", + "quote_other": "<0>{{count}} نقلونه", + "like_one": "<0>{{count}} خوښ", + "like_other": "<0>{{count}} خوښونه", + "commentsAndDonations": "تبصرې او مرستې", + "commentCount_one": "{{count}} تبصره", + "commentCount_other": "{{count}} تبصرې", + "noCommentsTitle": "تر اوسه تبصرې نشته", + "noCommentsHint": "د ملاتړ د پیغام لومړنی شه.", + "deleteDialogTitle": "دا کمپاین ړنګ کړئ؟", + "deleteDialogBody": "دا د NIP-09 د ړنګولو غوښتنه خپروي. ښه چلند کوونکي ریلې به کمپاین له فیدونو او مستقیمو لینکونو څخه لرې کړي. د تېرو مرستو رسیدونه پر چین پاتې کیږي. دا کار بیرته نه راګرځول کیږي — د مرستو د منلو لپاره، کمپاین سم کړئ.", + "storyHeading": "کیسه", + "storyEmpty": "تنظیم کوونکی تر اوسه د دې کمپاین لپاره کیسه نه ده لیکلې.", + "campaignEnded": "کمپاین پای ته ورسید", + "donate": "مرسته", + "share": "شریکول", + "target": "هدف: {{amount}}", + "raised": "راټول‌شوي", + "ofGoal": "د {{amount}} هدف څخه", + "donationCount_one": "{{count}} مرسته", + "donationCount_other": "{{count}} مرستې", + "recentDonations": "وروستۍ مرستې", + "seeAllDonations_one": "ټولې {{count}} مرستې وګورئ", + "seeAllDonations_other": "ټولې {{count}} مرستې وګورئ" + }, "campaigns": { "home": { "seoTitle": "د مرستو راټولولو کمپاینونه", diff --git a/src/locales/sn.json b/src/locales/sn.json index 89bbdbf4..5bf89c28 100644 --- a/src/locales/sn.json +++ b/src/locales/sn.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "Chinangwa chechikwama chinodikanwa.", "errorPublishedInvalid": "Chiitiko chakaburitswa chatadza chenjedzo. Tapota refresh moedza zvakare." }, + "campaignsDetail": { + "seoTitle": "{{title}} | Macampaign e{{appName}}", + "seoDescriptionFallback": "Tsigira {{title}} pa{{appName}}.", + "deadlineEndedOn": "Zvakapera pa{{date}}", + "deadlineEndsToday": "Zvinopera nhasi", + "deadlineDaysLeft_one": "{{count}} zuva rasara", + "deadlineDaysLeft_other": "Mazuva {{count}} asara", + "deadlineEndsOn": "Zvinopera pa{{date}}", + "back": "Dzokera", + "edit": "Gadzirisa", + "delete": "Bvisa", + "deleting": "Kubvisa…", + "byAuthor": "na <0>{{name}}", + "commentLabel": "Pindura", + "linkCopied": "Rink yakopirwa kuclipboard", + "pinnedToast": "Yakanamatidzwa kucampaign", + "unpinnedToast": "Yabviswa pacampaign", + "pinFailed": "Yatadza kuchinja zvakanamatidzwa", + "deletedToast": "Campaign yabviswa", + "deletedToastDesc": "Chikumbiro chekubvisa chakaburitswa. Marelay anozvibata zvakanaka achabvisa campaign mufeeds.", + "deleteErrorTitle": "Hatina kukwanisa kubvisa campaign", + "campaigner": "Murongi", + "repost_one": "<0>{{count}} Repost", + "repost_other": "<0>{{count}} maRepost", + "quote_one": "<0>{{count}} Quote", + "quote_other": "<0>{{count}} maQuote", + "like_one": "<0>{{count}} Like", + "like_other": "<0>{{count}} maLike", + "commentsAndDonations": "Mhinduro nezvipo", + "commentCount_one": "{{count}} mhinduro", + "commentCount_other": "{{count}} mhinduro", + "noCommentsTitle": "Hapana mhinduro parizvino", + "noCommentsHint": "Iva wekutanga kusiya shoko rerutsigiro.", + "deleteDialogTitle": "Bvisa campaign iyi?", + "deleteDialogBody": "Izvi zvinoburitsa chikumbiro chekubvisa cheNIP-09. Marelay anozvibata zvakanaka achabvisa campaign mufeeds nezvirink zvakananga. Rezvi dzezvipo dzapfuura dzinoramba dzakachengetwa pachain. Izvi hazvigoni kudzosererwa — kuti urambe uchigamuchira zvipo, gadzirisa campaign panzvimbo yokuti uibvise.", + "storyHeading": "Nyaya", + "storyEmpty": "Murongi haasati anyora nyaya yecampaign iyi.", + "campaignEnded": "Campaign yapera", + "donate": "Ipa", + "share": "Govera", + "target": "Chinangwa: {{amount}}", + "raised": "yakaunganidzwa", + "ofGoal": "ye{{amount}} chinangwa", + "donationCount_one": "{{count}} chipo", + "donationCount_other": "{{count}} zvipo", + "recentDonations": "Zvipo zvichangoitika", + "seeAllDonations_one": "Ona zvipo zvese {{count}}", + "seeAllDonations_other": "Ona zvipo zvese {{count}}" + }, "campaigns": { "home": { "seoTitle": "Mishandirapamwe yekuunganidza mari", diff --git a/src/locales/zh.json b/src/locales/zh.json index a0a8ed2c..9504732e 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -354,6 +354,55 @@ "errorWalletRequiredFallback": "需要钱包端点。", "errorPublishedInvalid": "已发布的事件未通过验证。请刷新并重试。" }, + "campaignsDetail": { + "seoTitle": "{{title}} | {{appName}} 募款", + "seoDescriptionFallback": "在 {{appName}} 上支持 {{title}}。", + "deadlineEndedOn": "{{date}} 已结束", + "deadlineEndsToday": "今天截止", + "deadlineDaysLeft_one": "还剩 {{count}} 天", + "deadlineDaysLeft_other": "还剩 {{count}} 天", + "deadlineEndsOn": "{{date}} 截止", + "back": "返回", + "edit": "编辑", + "delete": "删除", + "deleting": "删除中……", + "byAuthor": "由 <0>{{name}}", + "commentLabel": "评论", + "linkCopied": "已复制链接到剪贴板", + "pinnedToast": "已置顶到活动", + "unpinnedToast": "已取消置顶", + "pinFailed": "更新置顶失败", + "deletedToast": "活动已删除", + "deletedToastDesc": "删除请求已发布。行为良好的中继会将活动从信息流中移除。", + "deleteErrorTitle": "无法删除活动", + "campaigner": "发起人", + "repost_one": "<0>{{count}} 转发", + "repost_other": "<0>{{count}} 转发", + "quote_one": "<0>{{count}} 引用", + "quote_other": "<0>{{count}} 引用", + "like_one": "<0>{{count}} 点赞", + "like_other": "<0>{{count}} 点赞", + "commentsAndDonations": "评论与捐赠", + "commentCount_one": "{{count}} 条评论", + "commentCount_other": "{{count}} 条评论", + "noCommentsTitle": "暂无评论", + "noCommentsHint": "成为第一个留下支持留言的人。", + "deleteDialogTitle": "删除此活动?", + "deleteDialogBody": "这将发布一个 NIP-09 删除请求。行为良好的中继会将活动从信息流和直接链接中移除。过往的捐赠收据无论如何都会保留在链上。此操作无法撤销 — 若要继续接受捐赠,请改为编辑活动。", + "storyHeading": "故事", + "storyEmpty": "组织者尚未为此活动撰写故事。", + "campaignEnded": "活动已结束", + "donate": "捐赠", + "share": "分享", + "target": "目标:{{amount}}", + "raised": "已募", + "ofGoal": "目标 {{amount}}", + "donationCount_one": "{{count}} 笔捐赠", + "donationCount_other": "{{count}} 笔捐赠", + "recentDonations": "最近捐赠", + "seeAllDonations_one": "查看全部 {{count}} 笔捐赠", + "seeAllDonations_other": "查看全部 {{count}} 笔捐赠" + }, "campaigns": { "home": { "seoTitle": "众筹活动", diff --git a/src/pages/CampaignDetailPage.tsx b/src/pages/CampaignDetailPage.tsx index 70394dd5..ed4117d7 100644 --- a/src/pages/CampaignDetailPage.tsx +++ b/src/pages/CampaignDetailPage.tsx @@ -2,6 +2,8 @@ import { useMemo, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { useQueryClient } from '@tanstack/react-query'; +import { useTranslation, Trans } from 'react-i18next'; +import type { TFunction } from 'i18next'; import type { NostrEvent } from '@nostrify/nostrify'; import { CalendarClock, @@ -41,6 +43,7 @@ import { NoteMoreMenu } from '@/components/NoteMoreMenu'; import { Progress } from '@/components/ui/progress'; import { ThreadedReplyList, type ReplyNode } from '@/components/ThreadedReplyList'; import { useAuthor } from '@/hooks/useAuthor'; +import { useAppContext } from '@/hooks/useAppContext'; import { useBtcPrice } from '@/hooks/useBtcPrice'; import { useCampaign } from '@/hooks/useCampaign'; import { useCampaignDonations } from '@/hooks/useCampaignDonations'; @@ -80,16 +83,16 @@ function formatSatsFull(sats: number, btcPrice: number | undefined): string { return `${sats.toLocaleString()} sats`; } -function formatDeadline(unixSeconds: number): { label: string; isPast: boolean } { +function formatDeadline(unixSeconds: number, t: TFunction): { label: string; isPast: boolean } { const now = Math.floor(Date.now() / 1000); const diff = unixSeconds - now; if (diff <= 0) { - return { label: `Ended ${new Date(unixSeconds * 1000).toLocaleDateString()}`, isPast: true }; + return { label: t('campaignsDetail.deadlineEndedOn', { date: new Date(unixSeconds * 1000).toLocaleDateString() }), isPast: true }; } const days = Math.ceil(diff / 86_400); - if (days <= 1) return { label: 'Ends today', isPast: false }; - if (days < 60) return { label: `${days} days left`, isPast: false }; - return { label: `Ends ${new Date(unixSeconds * 1000).toLocaleDateString()}`, isPast: false }; + if (days <= 1) return { label: t('campaignsDetail.deadlineEndsToday'), isPast: false }; + if (days < 60) return { label: t('campaignsDetail.deadlineDaysLeft', { count: days }), isPast: false }; + return { label: t('campaignsDetail.deadlineEndsOn', { date: new Date(unixSeconds * 1000).toLocaleDateString() }), isPast: false }; } function collectReplyEvents(nodes: ReplyNode[], out = new Map()): Map { @@ -119,6 +122,8 @@ export function CampaignDetailPage({ pubkey, identifier, relays }: CampaignDetai } function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { + const { t } = useTranslation(); + const { config } = useAppContext(); const { user } = useCurrentUser(); const { data: btcPrice } = useBtcPrice(); const author = useAuthor(campaign.pubkey); @@ -241,7 +246,7 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { creatorMetadata?.display_name || creatorMetadata?.name || genUserName(campaign.pubkey); const creatorProfileUrl = useProfileUrl(campaign.pubkey, creatorMetadata); - const deadline = campaign.deadline ? formatDeadline(campaign.deadline) : null; + const deadline = campaign.deadline ? formatDeadline(campaign.deadline, t) : null; const countryLabel = getCampaignCountryLabel(campaign); const raisedSats = stats?.totalSats ?? 0; @@ -256,8 +261,8 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { ); useSeoMeta({ - title: `${campaign.title} | Agora Fundraisers`, - description: campaign.summary || `Support ${campaign.title} on Agora.`, + title: t('campaignsDetail.seoTitle', { title: campaign.title, appName: config.appName }), + description: campaign.summary || t('campaignsDetail.seoDescriptionFallback', { title: campaign.title, appName: config.appName }), ogImage: cover, }); @@ -269,7 +274,7 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { await nav.share({ title: campaign.title, text: campaign.summary, url }); } else if (nav?.clipboard) { await nav.clipboard.writeText(url); - toast({ title: 'Link copied to clipboard' }); + toast({ title: t('campaignsDetail.linkCopied') }); } } catch { // User likely cancelled the share sheet; nothing to do. @@ -287,9 +292,8 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { { onSuccess: () => { toast({ - title: 'Campaign deleted', - description: - 'A deletion request was published. Well-behaved relays will drop the campaign from feeds.', + title: t('campaignsDetail.deletedToast'), + description: t('campaignsDetail.deletedToastDesc'), }); setDeleteConfirmOpen(false); void queryClient.invalidateQueries({ queryKey: ['campaign', campaign.pubkey, campaign.identifier] }); @@ -312,7 +316,7 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { onError: (error: unknown) => { const msg = error instanceof Error ? error.message : String(error); toast({ - title: 'Could not delete campaign', + title: t('campaignsDetail.deleteErrorTitle'), description: msg, variant: 'destructive', }); @@ -416,10 +420,12 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { onClick={() => openInteractions('reposts')} className="hover:underline transition-colors" > - - {formatNumber(engagementStats.reposts)} - {' '} - Repost{engagementStats.reposts !== 1 ? 's' : ''} + }} + /> ) : null} {engagementStats?.quotes ? ( @@ -427,10 +433,12 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { onClick={() => openInteractions('quotes')} className="hover:underline transition-colors" > - - {formatNumber(engagementStats.quotes)} - {' '} - Quote{engagementStats.quotes !== 1 ? 's' : ''} + }} + /> ) : null} {engagementStats?.reactions ? ( @@ -438,10 +446,12 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { onClick={() => openInteractions('reactions')} className="hover:underline transition-colors" > - - {formatNumber(engagementStats.reactions)} - {' '} - Like{engagementStats.reactions !== 1 ? 's' : ''} + }} + /> ) : null} @@ -450,12 +460,11 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) {

- Comments & donations + {t('campaignsDetail.commentsAndDonations')}

{engagementStats?.replies ? ( - {formatNumber(engagementStats.replies)}{' '} - {engagementStats.replies === 1 ? 'comment' : 'comments'} + {t('campaignsDetail.commentCount', { count: engagementStats.replies })} ) : null}
@@ -492,10 +501,10 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { className="block w-full rounded-2xl border border-dashed border-border/80 bg-card/50 px-6 py-10 text-center hover:bg-card hover:border-primary/40 transition-colors" >

- No comments yet + {t('campaignsDetail.noCommentsTitle')}

- Be the first to leave a message of support. + {t('campaignsDetail.noCommentsHint')}

)} @@ -539,17 +548,13 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { - Delete this campaign? + {t('campaignsDetail.deleteDialogTitle')} - This publishes a NIP-09 deletion request. Well-behaved relays - will drop the campaign from feeds and direct links. Past - donation receipts stay on-chain regardless. This action cannot - be undone — to keep accepting donations, edit the campaign - instead. + {t('campaignsDetail.deleteDialogBody')} - Cancel + {t('common.cancel')} { e.preventDefault(); @@ -558,7 +563,7 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { disabled={deleteMutation.isPending} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > - {deleteMutation.isPending ? 'Deleting…' : 'Delete'} + {deleteMutation.isPending ? t('campaignsDetail.deleting') : t('campaignsDetail.delete')} @@ -570,10 +575,10 @@ function CampaignDetailContent({ campaign }: { campaign: ParsedCampaign }) { const wasPinned = isPinned(event.id); togglePin.mutate(event.id, { onSuccess: () => { - toast({ title: wasPinned ? 'Unpinned from campaign' : 'Pinned to campaign' }); + toast({ title: wasPinned ? t('campaignsDetail.unpinnedToast') : t('campaignsDetail.pinnedToast') }); }, onError: () => { - toast({ title: 'Failed to update campaign pins', variant: 'destructive' }); + toast({ title: t('campaignsDetail.pinFailed'), variant: 'destructive' }); }, }); } @@ -592,6 +597,7 @@ function CampaignPinHeader({ pinPending: boolean; onTogglePin: () => void; }) { + const { t } = useTranslation(); return ( - Campaigner + {t('campaignsDetail.campaigner')} )} @@ -650,6 +656,7 @@ function CampaignHero({ onReply, onMore, }: CampaignHeroProps) { + const { t } = useTranslation(); const initials = creatorName.slice(0, 2).toUpperCase(); return ( @@ -696,10 +703,10 @@ function CampaignHero({ {isCreator && ( @@ -711,7 +718,7 @@ function CampaignHero({ > - Edit + {t('campaignsDetail.edit')}
)} @@ -759,10 +766,11 @@ function CampaignHero({ - by{' '} - - {creatorName} - + }} + /> @@ -791,7 +799,7 @@ function CampaignHero({
); } @@ -853,8 +862,9 @@ function DonateColumn({ onShare, onSeeAll, }: DonateColumnProps) { + const { t } = useTranslation(); const ended = !!deadline?.isPast; - const endedLabel = ended ? 'Campaign ended' : null; + const endedLabel = ended ? t('campaignsDetail.campaignEnded') : null; const isSilentPayment = !campaign.wallets.onchain; return ( @@ -871,7 +881,7 @@ function DonateColumn({ {isSilentPayment ? ( campaign.goalUsd && campaign.goalUsd > 0 ? (
- Target: {formatUsdGoal(campaign.goalUsd)} + {t('campaignsDetail.target', { amount: formatUsdGoal(campaign.goalUsd) })}
) : null ) : statsLoading ? ( @@ -882,24 +892,22 @@ function DonateColumn({
{formatSatsFull(raisedSats, btcPrice)} - raised + {t('campaignsDetail.raised')}
{campaign.goalUsd ? (
- of {formatUsdGoal(campaign.goalUsd)} goal + {t('campaignsDetail.ofGoal', { amount: formatUsdGoal(campaign.goalUsd) })} {donations.length > 0 && ( <> {' · '} - {formatNumber(donations.length)}{' '} - {donations.length === 1 ? 'donation' : 'donations'} + {t('campaignsDetail.donationCount', { count: donations.length })} )}
) : donations.length > 0 ? (
- {formatNumber(donations.length)}{' '} - {donations.length === 1 ? 'donation' : 'donations'} + {t('campaignsDetail.donationCount', { count: donations.length })}
) : null}
@@ -920,11 +928,11 @@ function DonateColumn({
) : ( @@ -935,7 +943,7 @@ function DonateColumn({ )} @@ -945,7 +953,7 @@ function DonateColumn({ {!isSilentPayment && donations.length > 0 && (
- Recent donations + {t('campaignsDetail.recentDonations')}
)}