diff --git a/i18n.config.ts b/i18n.config.ts index ca46e0fa..8cc2e932 100644 --- a/i18n.config.ts +++ b/i18n.config.ts @@ -65,6 +65,7 @@ const config = { "meta", "about", "team", + "donate", ] as const, namespacedRoutes: { "*": ["common", "entities", "meta", "reader"], @@ -72,6 +73,7 @@ const config = { "/t/*": ["reader"], "/about": ["about"], "/team": ["team"], + "/donate": ["donate"], // "/chat/*": ["reader"], }, }; diff --git a/locales/ar/common.json b/locales/ar/common.json index 032105fd..81e27b15 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "ساهم", + "donate": { + "title": "تبرع", + "description": "ادعم Usul" + }, "add-text": { "title": "إضافة نص", "description": "تحميل كتاب أو PDF" diff --git a/locales/ar/donate.json b/locales/ar/donate.json new file mode 100644 index 00000000..6d0efe45 --- /dev/null +++ b/locales/ar/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "مستقبل المعرفة الإسلامية، بدعم من الذكاء الاصطناعي", + "subtitle": "ساعدنا في جعل المعرفة الإسلامية أكثر وصولاً واسترجاعها أسهل بكثير.", + "description": "🎉 الحمد لله، بفضل دعمكم، وصل وصول إلى آلاف الباحثين عن المعرفة، مما يجسر الفجوة بين التقليد والحداثة.", + "donate-widget": { + "month-raised": "جمع", + "goal": "الهدف ${goal, number} شهرياً", + "active-monthly-donors": "{donors, number} من المتبرعين النشطين شهرياً", + "become-a-donor": "أصبح متبرعاً" + } + }, + "achievements": { + "title": "إنجازات", + "one": { + "title": "وصول لا مثيل له إلى النصوص الإسلامية.", + "features": { + "0": "أكثر من 10,000 نص إسلامي كلاسيكي وحديث متاحة مجانًا.", + "1": "أدوات بحث متقدمة لمساعدة الباحثين والمتعلمين على اكتشاف الأفكار بسهولة." + } + }, + "two": { + "title": "تمكين العلماء والمتعلمين", + "features": { + "0": "أدوات الذكاء الاصطناعي المتقدمة التي تتيح للمستخدمين العثور بسرعة على المعلومات واستخراجها من النصوص الكلاسيكية.", + "1": "إرفاق PDF تحميل على العديد من الكتب لتمكين المستخدمين من التحقق من المصدر." + } + }, + "three": { + "title": "تجربة مستخدم جميلة", + "description": "نسعى جاهدين لجعل عملنا جميلًا ومحبوبًا من قبل طلاب المعرفة المبتدئين والعلماء المتقدمين على حد سواء. من تجربة القراءة، إلى واجهة البحث، إلى تجربة الجوال." + } + }, + "roadmap": { + "title": "خارطة الطريق الخاصة بنا، رؤية وصول للمستقبل", + "description": "تبرعاتكم ستساعدنا في تحقيق الأهداف التالية، إن شاء الله:", + "items": { + "0": { + "title": "توسيع مكتبة النصوص", + "features": { + "0": "رقمنة آلاف الكتب الإضافية وإضافتها إلى المنصة.", + "1": "ضمان تمثيل شامل للتخصصات الإسلامية المختلفة والمدارس الفكرية." + } + }, + "1": { + "title": "تحسين أدوات البحث", + "features": { + "0": "إدخال قدرات جديدة مدعومة بالذكاء الاصطناعي تسمح للمستخدمين بالعثور على الأفكار عبر النصوص أو مجموعة كاملة.", + "1": "تطوير ميزات مثل مقارنة النصوص جنباً إلى جنب والبحث عن مصادر الاقتباسات." + } + }, + "2": { + "title": "الوصول العالمي", + "features": { + "0": "ترجمة النصوص باستخدام الذكاء الاصطناعي إلى الإنجليزية والعديد من اللغات الأخرى لفتح الوصول إلى المعرفة الإسلامية.", + "1": "تحسين أداء الموقع للمستخدمين في المناطق ذات الاتصال المحدود، لضمان عدم تخلف أي متعلم عن الركب." + } + } + } + }, + "be-part": { + "title": "كن جزءًا من رحلة وصول", + "description": " دعمك الشهري يقود الابتكار، يدعم نمونا، ويضمن بقاء وصول كمورد حيوي للأجيال القادمة.", + "why-support": { + "title": "لماذا تدعم وصول؟", + "features": { + "0": "تحديثات ربع سنوية شفافة حول كيفية إحداث فارق بفضل تبرعك", + "1": "المرونة في إلغاء أو تعديل تبرعك في أي وقت", + "2": "فرصة أن تكون جزءًا من مهمة ريادية للحفاظ على المعرفة الإسلامية ومشاركتها" + } + }, + "choose-amount": { + "title": "اختر مبلغ تبرعك:", + "interval": { + "one-time": "مرة واحدة", + "monthly": "شهرياً", + "yearly": "سنويًا" + }, + "note": "التبرعات تُجمع بواسطة مؤسسة وصول، وهي منظمة غير ربحية 501(c)(3). جميع التبرعات يمكن خصمها من الضرائب في الولايات المتحدة. لأي استفسارات، يرجى التواصل معنا على donors@usul.ai.", + "continue": "استمر" + } + }, + "success": { + "title": "شكرًا لك على تبرعك!", + "description": "كرمك يحدث فرقًا حقيقيًا. نحن نقدر دعمك في مهمتنا." + }, + "verification": { + "title": "ما هو بريدك الإلكتروني؟", + "description": "البريد الإلكتروني مطلوب لإرسال الإيصالات وتحديثات المتبرعين.", + "sent-code": "لقد أرسلنا رمزًا إلى بريدك الإلكتروني. يرجى إدخاله أدناه لإكمال التبرع.", + "verify": "تحقق", + "send-code": "أرسل الرمز" + }, + "already-donor": { + "title": "هل أنت متبرع بالفعل؟", + "manage": "إدارة تبرعك" + } +} \ No newline at end of file diff --git a/locales/ar/meta.json b/locales/ar/meta.json index b71dd7d6..db4d92ee 100644 --- a/locales/ar/meta.json +++ b/locales/ar/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "فريق العمل", "description": "تعرف على فريق أصول" + }, + "donate-page": { + "title": "تبرع", + "description": "تبرع لأوسول" } } \ No newline at end of file diff --git a/locales/bn/common.json b/locales/bn/common.json index efb10b4e..2e0b5a8d 100644 --- a/locales/bn/common.json +++ b/locales/bn/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "অবদান", + "donate": { + "title": "দান করুন", + "description": "Usul কে সমর্থন করুন" + }, "add-text": { "title": "টেক্সট যোগ করুন", "description": "বই বা PDF আপলোড করুন" diff --git a/locales/bn/donate.json b/locales/bn/donate.json new file mode 100644 index 00000000..d97af957 --- /dev/null +++ b/locales/bn/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "ইসলামী জ্ঞানের ভবিষ্যৎ, এআই দ্বারা সমর্থিত", + "subtitle": "ইসলামী জ্ঞান আরও অ্যাক্সেসযোগ্য করতে এবং এটি উদ্ধারের কাজকে সহজ করে তুলতে আমাদের সহায়তা করুন।", + "description": "🎉 আলহামদুলিল্লাহ, আপনার সহযোগিতায়, উসূল হাজার হাজার জ্ঞান সন্ধানীদের কাছে পৌঁছেছে, এবং ঐতিহ্য ও আধুনিকতার মধ্যে ফাঁক পূরণ করেছে।", + "donate-widget": { + "month-raised": "উত্থাপিত", + "goal": "লক্ষ্য ${goal, number} মাসিক", + "active-monthly-donors": "{donors, number} সক্রিয় মাসিক দাতা", + "become-a-donor": "দাতা হোন" + } + }, + "achievements": { + "title": "অর্জন", + "one": { + "title": "ইসলামী গ্রন্থে অদ্বিতীয় প্রবেশাধিকার।", + "features": { + "0": "১০,০০০ টিরও বেশি প্রাচীন এবং সমসাময়িক ইসলামী গ্রন্থ বিনামূল্যে উপলব্ধ।", + "1": "গবেষক এবং শিক্ষার্থীদের সহজে জ্ঞানের অনুসন্ধানে সুযোগ দেওয়ার জন্য উন্নত অনুসন্ধান সরঞ্জাম।" + } + }, + "two": { + "title": "শিক্ষার্থী ও শিক্ষার্থীদের ক্ষমতায়ন", + "features": { + "0": "ব্যবহারকারীদের প্রাচীন পাণ্ডুলিপি থেকে দ্রুত তথ্য সন্ধান ও আহরণ করতে সক্ষম করা অলৌকিক বুদ্ধিমান সরঞ্জাম।", + "1": "বইয়ের অনেকগুলিতে পিডিএফ সংযুক্তি আপলোড করা হয়েছে যাতে ব্যবহারকারীরা উৎস যাচাই করতে পারেন।" + } + }, + "three": { + "title": "সুন্দর ব্যবহারকারীর অভিজ্ঞতা", + "description": "আমরা আমাদের কাজকে সুন্দর এবং প্রিয় করে তুলতে চেষ্টা করি জ্ঞানের শিক্ষার্থীদের জন্য এবং অভিজ্ঞ পণ্ডিতদের জন্য সমানভাবে। পড়ার অভিজ্ঞতা থেকে, অনুসন্ধানের ইন্টারফেস, মোবাইল অভিজ্ঞতা সবই অন্তর্ভুক্ত।" + } + }, + "roadmap": { + "title": "আমাদের রোডম্যাপ, উসূলের ভবিষ্যতের দৃষ্টি", + "description": "আপনার দান আমাদের নিম্নলিখিত লক্ষ্য অর্জনে সাহায্য করবে, ইনশাআল্লাহঃ", + "items": { + "0": { + "title": "টেক্সট গ্রন্থাগার সম্প্রসারণ করুন", + "features": { + "0": "হাজার হাজার অতিরিক্ত বই ডিজিটাল করে প্ল্যাটফর্মে যোগ করুন।", + "1": "বিভিন্ন ইসলামী বিষয়শ্রেণীতে এবং চিন্তাধারার স্কুলগুলির বহুগুণী প্রতিনিধিত্ব নিশ্চিত করুন।" + } + }, + "1": { + "title": "গবেষণা সরঞ্জামগুলি পরিমার্জন করুন", + "features": { + "0": "ব্যবহারকারীদের গ্রন্থ বা একটি সম্পূর্ণ সংরক্ষণাগারের মধ্যে জ্ঞান খুঁজে পেতে সক্ষম করার জন্য আরও উন্নত এআই-চালিত ক্ষমতা প্রবর্তন করুন।", + "1": "পাশাপাশি টেক্সট তুলনা এবং উদ্ধৃতি উৎস খুঁজে বের করার মতো বৈশিষ্ট্যগুলি বিকাশ করুন।" + } + }, + "2": { + "title": "বিশ্বব্যাপী প্রবেশাধিকার", + "features": { + "0": "এআই ব্যবহার করে গ্রন্থগুলো ইংরেজি এবং অন্যান্য অনেক ভাষায় অনুবাদ করে ইসলামিক জ্ঞান উন্মুক্ত করুন।", + "1": "সীমিত সংযোগযুক্ত এলাকার ব্যবহারকারীদের জন্য ওয়েবসাইটের কার্যকারিতা উন্নত করুন, নিশ্চিত করুন কোনো শিক্ষার্থী পিছিয়ে নেই।" + } + } + } + }, + "be-part": { + "title": "উসূলের অভিযাত্রার অংশ হোন", + "description": " আপনার মাসিক সমর্থন উদ্ভাবন চালায়, আমাদের বৃদ্ধি বজায় রাখে, এবং নিশ্চিত করে যে উসূল ভবিষ্যতের প্রজন্মের জন্য একটি অমূল্য সম্পদ রয়ে যায়।", + "why-support": { + "title": "উসূলকে সমর্থন কেন করবেন?", + "features": { + "0": "আপনার অনুদান কীভাবে পরিবর্তন আনছে তার ওপর স্বচ্ছ ত্রৈমাসিক আপডেট", + "1": "আপনার সহযোগিতাকে যে কোনো সময় বাতিল বা সামঞ্জস্য করার নমনীয়তা", + "2": "ইসলামিক জ্ঞান সংরক্ষণ এবং ভাগ করা একটি অগ্রগামী মিশনের অংশ হওয়ার সুযোগ" + } + }, + "choose-amount": { + "title": "আপনার দান পরিমাণ নির্বাচন করুনঃ", + "interval": { + "one-time": "একবার", + "monthly": "মাসিক", + "yearly": "বার্ষিক" + }, + "note": "অনুদানগুলি উসূল ফাউন্ডেশন দ্বারা সংগৃহীত হয়, একটি 501(c)(3) অলাভজনক সংস্থা। সমস্ত অনুদান মার্কিন যুক্তরাষ্ট্রে কর-মুক্ত। কোনো প্রশ্নের জন্য, দয়া করে আমাদের সাথে donors@usul.ai এ যোগাযোগ করুন।", + "continue": "চালিয়ে যান" + } + }, + "success": { + "title": "আপনার অনুদানের জন্য ধন্যবাদ!", + "description": "আপনার উদারতা একটি বাস্তব পরিবর্তন আনে। আমাদের মিশনে আপনার সহায়তাকে আমরা মূল্যায়ন করি।" + }, + "verification": { + "title": "আপনার ইমেল কী?", + "description": "রসিদ এবং দাতা আপডেট পাঠানোর জন্য ইমেইল প্রয়োজন।", + "sent-code": "আমরা আপনার ইমেইলে একটি কোড পাঠিয়েছি। অনুদান সম্পন্ন করার জন্য দয়া করে এটি নিচে লিখুন।", + "verify": "যাচাই করুন", + "send-code": "কোড পাঠান" + }, + "already-donor": { + "title": "ইতিমধ্যে কি দাতা হয়েছেন?", + "manage": "আপনার অনুদান পরিচালনা করুন" + } +} \ No newline at end of file diff --git a/locales/bn/meta.json b/locales/bn/meta.json index 7d88801e..83cca3a0 100644 --- a/locales/bn/meta.json +++ b/locales/bn/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "দল", "description": "Usul দলের সাথে পরিচিত হন" + }, + "donate-page": { + "title": "দান করুন", + "description": "আমাদের উসুলে দান করুন" } } \ No newline at end of file diff --git a/locales/en/common.json b/locales/en/common.json index 5e1f941c..e9d1afca 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Contribute", + "donate": { + "title": "Donate", + "description": "Support Usul" + }, "add-text": { "title": "Add Text", "description": "Upload Book or PDF" diff --git a/locales/en/donate.json b/locales/en/donate.json new file mode 100644 index 00000000..80988418 --- /dev/null +++ b/locales/en/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "The Future of Islamic knowledge, powered by AI", + "subtitle": "Help us make Islamic knowledge more accessible and retrieving it much easier.", + "description": "🎉 Alhamdulillah, with your support, Usul has reached thousands of seekers of knowledge, bridging the gap between tradition and modernity.", + "donate-widget": { + "month-raised": "Raised", + "goal": "Goal ${goal, number} Monthly", + "active-monthly-donors": "{donors, number} active monthly donors", + "become-a-donor": "Become a Donor" + } + }, + "achievements": { + "title": "Achievements", + "one": { + "title": "Unmatched Access to Islamic Texts.", + "features": { + "0": "Over 10,000 classical and modern Islamic texts available for free.", + "1": "Advanced search tools to help researchers and learners uncover insights with ease." + } + }, + "two": { + "title": "Empowering Scholars and Learners", + "features": { + "0": "State-of-the-art AI tools that allows users to quickly find and extract information from classical texts.", + "1": "PDF Attachment uploaded to many books for users to validate the source." + } + }, + "three": { + "title": "Beautiful User Experience", + "description": "We strive to make our work beautiful and loved by beginner students of knowledge and advanced scholars alike. From the reading experience, to the search interface, to the mobile experience." + } + }, + "roadmap": { + "title": "Our Roadmap, Usul’s Vision for the Future", + "description": "Your donations will help us achieve the following goals, inshaAllah:", + "items": { + "0": { + "title": "Expand the Text Library", + "features": { + "0": "Digitize and additional thousands of additional books to the platform.", + "1": "Ensure comprehensive representation of various Islamic disciplines and schools of thought." + } + }, + "1": { + "title": "Refine Research Tools", + "features": { + "0": "Introduce more advanced AI-powered capabilities to allow users to find insights across texts or an entire corpus.", + "1": "Develop features such as side-by-side text comparisons and finding quote sources." + } + }, + "2": { + "title": "Global Accessibility", + "features": { + "0": "Translate texts using AI into English and many other languages to open-up access to Islamic knowledge.", + "1": "Improve website performance for users in areas with limited connectivity, ensuring no learner is left behind." + } + } + } + }, + "be-part": { + "title": "Be a Part of Usul’s Journey", + "description": " Your monthly support drives innovation, sustains our growth, and ensures that Usul remains a vital resource for generations to come.", + "why-support": { + "title": "Why support Usul?", + "features": { + "0": "Transparent quarterly updates on how your donation is making a difference", + "1": "Flexibility to cancel or adjust your contribution anytime", + "2": "A chance to be part of a groundbreaking mission to preserve and share Islamic knowledge" + } + }, + "choose-amount": { + "title": "Choose your donation amount:", + "interval": { + "one-time": "One Time", + "monthly": "Monthly", + "yearly": "Yearly" + }, + "note": "Donations are collected by Usul Foundation, a 501(c)(3) nonprofit organization. All donations are tax-deductible in the US. For any inquiries, please contact us at donors@usul.ai.", + "continue": "Continue" + } + }, + "success": { + "title": "Thank You for Your Donation!", + "description": "Your generosity makes a real difference. We appreciate your support in our mission." + }, + "verification": { + "title": "What's your email?", + "description": "Email is required to send you receipts and donor updates.", + "sent-code": "We've sent a code to your email. Please enter it below to complete the donation.", + "verify": "Verify", + "send-code": "Send Code" + }, + "already-donor": { + "title": "Already a donor?", + "manage": "Manage your donation" + } +} \ No newline at end of file diff --git a/locales/en/meta.json b/locales/en/meta.json index e675f4a6..512dc028 100644 --- a/locales/en/meta.json +++ b/locales/en/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Team", "description": "Meet the Usul team" + }, + "donate-page": { + "title": "Donate", + "description": "Donate to Usul" } } \ No newline at end of file diff --git a/locales/es/common.json b/locales/es/common.json index 5b4768f1..1949255b 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Contribuir", + "donate": { + "title": "Donar", + "description": "Apoya a Usul" + }, "add-text": { "title": "Añadir Texto", "description": "Subir libro o PDF" diff --git a/locales/es/donate.json b/locales/es/donate.json new file mode 100644 index 00000000..7abd42b1 --- /dev/null +++ b/locales/es/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "El futuro del conocimiento islámico, impulsado por IA", + "subtitle": "Ayúdanos a hacer el conocimiento islámico más accesible y su recuperación mucho más fácil.", + "description": "🎉 Alhamdulillah, con su apoyo, Usul ha llegado a miles de buscadores de conocimiento, cerrando la brecha entre la tradición y la modernidad.", + "donate-widget": { + "month-raised": "Recaudado", + "goal": "Meta ${goal, number} Mensual", + "active-monthly-donors": "{donors, number} donantes activos mensuales", + "become-a-donor": "Hazte Donante" + } + }, + "achievements": { + "title": "Logros", + "one": { + "title": "Acceso Inigualable a Textos Islámicos.", + "features": { + "0": "Más de 10.000 textos islámicos clásicos y modernos disponibles gratuitamente.", + "1": "Herramientas avanzadas de búsqueda para ayudar a los investigadores y alumnos a descubrir fácilmente conocimientos." + } + }, + "two": { + "title": "Empoderando a Académicos y Estudiantes", + "features": { + "0": "Herramientas de IA de última generación que permiten a los usuarios encontrar y extraer rápidamente información de textos clásicos.", + "1": "Adjunto PDF cargado en muchos libros para que los usuarios puedan validar la fuente." + } + }, + "three": { + "title": "Hermosa Experiencia de Usuario", + "description": "Nos esforzamos por hacer que nuestro trabajo sea hermoso y apreciado tanto por estudiantes principiantes del conocimiento como por académicos avanzados. Desde la experiencia de lectura, hasta la interfaz de búsqueda, hasta la experiencia móvil." + } + }, + "roadmap": { + "title": "Nuestra Hoja de Ruta, la Visión de Usul para el Futuro", + "description": "Sus donaciones nos ayudarán a alcanzar los siguientes objetivos, inshaAllah:", + "items": { + "0": { + "title": "Expandir la Biblioteca de Textos", + "features": { + "0": "Digitalizar y agregar miles de libros adicionales a la plataforma.", + "1": "Asegurar una representación integral de diversas disciplinas islámicas y escuelas de pensamiento." + } + }, + "1": { + "title": "Refinar Herramientas de Investigación", + "features": { + "0": "Introducir capacidades más avanzadas impulsadas por IA para permitir a los usuarios encontrar conocimientos en textos o en un corpus completo.", + "1": "Desarrollar funciones como comparaciones de textos lado a lado y encontrar fuentes de citas." + } + }, + "2": { + "title": "Accesibilidad Global", + "features": { + "0": "Traducir textos usando IA al inglés y a muchos otros idiomas para abrir el acceso al conocimiento islámico.", + "1": "Mejorar el rendimiento del sitio web para usuarios en áreas con conectividad limitada, asegurando que ningún estudiante se quede atrás." + } + } + } + }, + "be-part": { + "title": "Sé parte del Viaje de Usul", + "description": " Tu apoyo mensual impulsa la innovación, sostiene nuestro crecimiento y garantiza que Usul siga siendo un recurso vital para las generaciones venideras.", + "why-support": { + "title": "¿Por qué apoyar a Usul?", + "features": { + "0": "Actualizaciones trimestrales transparentes sobre cómo tu donación está haciendo la diferencia", + "1": "Flexibilidad para cancelar o ajustar tu contribución en cualquier momento", + "2": "Una oportunidad para ser parte de una misión innovadora para preservar y compartir el conocimiento islámico" + } + }, + "choose-amount": { + "title": "Elige el monto de tu donación:", + "interval": { + "one-time": "Una vez", + "monthly": "Mensual", + "yearly": "Anual" + }, + "note": "Las donaciones son recaudadas por Usul Foundation, una organización sin fines de lucro 501(c)(3). Todas las donaciones son deducibles de impuestos en los EE.UU. Para cualquier consulta, por favor contáctenos en donors@usul.ai.", + "continue": "Continuar" + } + }, + "success": { + "title": "¡Gracias por tu donación!", + "description": "Tu generosidad marca una verdadera diferencia. Apreciamos tu apoyo en nuestra misión." + }, + "verification": { + "title": "¿Cuál es tu correo electrónico?", + "description": "El correo electrónico es necesario para enviarle recibos y actualizaciones de donantes.", + "sent-code": "Hemos enviado un código a tu correo electrónico. Por favor, ingrésalo abajo para completar la donación.", + "verify": "Verificar", + "send-code": "Enviar Código" + }, + "already-donor": { + "title": "¿Ya eres donante?", + "manage": "Administra tu donación" + } +} \ No newline at end of file diff --git a/locales/es/meta.json b/locales/es/meta.json index aed5091e..e91a9677 100644 --- a/locales/es/meta.json +++ b/locales/es/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Equipo", "description": "Conoce al equipo de Usul" + }, + "donate-page": { + "title": "Donar", + "description": "Donar a Usul" } } \ No newline at end of file diff --git a/locales/fa/common.json b/locales/fa/common.json index 8f52cfda..b08145fa 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "مشارکت", + "donate": { + "title": "اهدا", + "description": "حمایت از Usul" + }, "add-text": { "title": "افزودن متن", "description": "کتاب یا PDF بارگذاری کنید" diff --git a/locales/fa/donate.json b/locales/fa/donate.json new file mode 100644 index 00000000..7d82f458 --- /dev/null +++ b/locales/fa/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "آینده دانش اسلامی، با قدرت هوش مصنوعی", + "subtitle": "به ما کمک کنید که دانش اسلامی را بیشتر در دسترس قرار دهیم و بازیابی آن را بسیاری آسان‌تر کنیم.", + "description": "🎉 الحمدلله، با حمایت شما، اصول به هزاران جوینده دانش رسیده است، و فاصله بین سنت و مدرنیته را پل زده است.", + "donate-widget": { + "month-raised": "جمع آوری شده", + "goal": "هدف ${goal, number} ماهانه", + "active-monthly-donors": "{donors, number} خیرین فعال ماهانه", + "become-a-donor": "کمک‌کننده شوید" + } + }, + "achievements": { + "title": "دستاوردها", + "one": { + "title": "دسترسی بی‌نظیر به متون اسلامی.", + "features": { + "0": "بیش از 10,000 متن کلاسیک و مدرن اسلامی رایگان در دسترس است.", + "1": "ابزارهای جستجوی پیشرفته به پژوهشگران و یادگیران کمک می‌کند تا به راحتی بینش‌ها را کشف کنند." + } + }, + "two": { + "title": "توانمندسازی دانشمندان و یادگیران", + "features": { + "0": "ابزارهای هوش مصنوعی پیشرفته که به کاربران امکان می‌دهد اطلاعات را سریعاً از متون کلاسیک پیدا و استخراج کنند.", + "1": "پیوست PDF در بسیاری از کتاب‌ها برای کاربران جهت اعتبارسنجی منبع بارگذاری شده است." + } + }, + "three": { + "title": "تجربه کاربری زیبا", + "description": "ما تلاش می‌کنیم کار خود را برای دانش‌پژوهان مبتدی و دانشمندان پیشرفته زیبا و محبوب کنیم. از تجربه خواندن، تا رابط جستجو، تا تجربه موبایل." + } + }, + "roadmap": { + "title": "نقشه راه ما، چشم‌انداز اصول برای آینده", + "description": "کمک‌های شما به ما کمک خواهد کرد تا به اهداف زیر برسیم، ان شاء الله:", + "items": { + "0": { + "title": "توسعه کتابخانه متون", + "features": { + "0": "هزاران کتاب دیگر را به صورت دیجیتال کرده و به پلتفرم اضافه کنید.", + "1": "اطمینان از نمایندگی جامع از رشته‌ها و مکاتب فکری مختلف اسلامی." + } + }, + "1": { + "title": "تصفیه ابزارهای تحقیق", + "features": { + "0": "قابلیت‌های پیشرفته مبتنی بر هوش مصنوعی معرفی کنید تا کاربران بتوانند بینش‌ها را در متون یا یک مجموعه کامل پیدا کنند.", + "1": "ویژگی‌هایی مانند مقایسه متن‌ها به صورت کنار به کنار و یافتن منابع نقل‌قول‌ها توسعه دهید." + } + }, + "2": { + "title": "دسترسی جهانی", + "features": { + "0": "متون را با استفاده از هوش مصنوعی به انگلیسی و بسیاری از زبان‌های دیگر ترجمه کنید تا دسترسی به دانش اسلامی باز شود.", + "1": "عملکرد وب‌سایت را برای کاربران در مناطق با اتصال محدود بهبود بخشید، تضمین کنید هیچ یادگیری عقب نماند." + } + } + } + }, + "be-part": { + "title": "عضوی از سفر اصول باشید", + "description": " حمایت ماهانه شما نوآوری را هدایت می‌کند، رشد ما را پایدار می‌کند و اطمینان می‌دهد که اصول به عنوان منبع حیاتی برای نسل‌های آینده باقی بماند.", + "why-support": { + "title": "چرا از اصول حمایت کنید?", + "features": { + "0": "به‌روزرسانی‌های سه‌ماهه شفاف در مورد اینکه چگونه کمک شما تغییر ایجاد می‌کند", + "1": "انعطاف‌پذیری برای لغو یا تنظیم کمک خود در هر زمان", + "2": "فرصتی برای بخشی از یک مأموریت نوآورانه برای حفظ و به اشتراک‌گذاری دانش اسلامی باشید" + } + }, + "choose-amount": { + "title": "مبلغ اهدا خود را انتخاب کنید:", + "interval": { + "one-time": "یکبار", + "monthly": "ماهانه", + "yearly": "سالانه" + }, + "note": "اهداءات توسط بنیاد اصول، یک سازمان غیر انتفاعی 501(c)(3) جمع‌آوری می‌شود. تمامی اهداءات در ایالات متحده معاف از مالیات هستند. برای هرگونه پرسش، لطفاً با ما در donors@usul.ai تماس بگیرید.", + "continue": "ادامه دهید" + } + }, + "success": { + "title": "از کمک شما سپاسگزاریم!", + "description": "سخاوت شما تفاوتی واقعی ایجاد می‌کند. ما از حمایت شما در مأموریت ما قدردانی می‌کنیم." + }, + "verification": { + "title": "ایمیل شما چیست?", + "description": "ایمیل برای ارسال رسیدها و به‌روزرسانی‌های خیرین نیاز است.", + "sent-code": "یک کد به ایمیل شما ارسال کرده‌ایم. لطفاً آن را در زیر وارد کنید تا اهدا کامل شود.", + "verify": "تأیید کنید", + "send-code": "ارسال کد" + }, + "already-donor": { + "title": "آیا قبلاً خیر بوده‌اید?", + "manage": "کمک خود را مدیریت کنید" + } +} \ No newline at end of file diff --git a/locales/fa/meta.json b/locales/fa/meta.json index 267ac5b1..8f6041a7 100644 --- a/locales/fa/meta.json +++ b/locales/fa/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "تیم", "description": "با تیم اصول آشنا شوید" + }, + "donate-page": { + "title": "اهدا", + "description": "به اصول اهدا کنید" } } \ No newline at end of file diff --git a/locales/fr/common.json b/locales/fr/common.json index 14cf82d5..a1e5c18f 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Contribuer", + "donate": { + "title": "Faire un don", + "description": "Soutenir Usul" + }, "add-text": { "title": "Ajouter Texte", "description": "Télécharger un livre ou PDF" diff --git a/locales/fr/donate.json b/locales/fr/donate.json new file mode 100644 index 00000000..c6a02a1a --- /dev/null +++ b/locales/fr/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "L'avenir du savoir islamique, propulsé par l'IA", + "subtitle": "Aidez-nous à rendre le savoir islamique plus accessible et sa récupération beaucoup plus facile.", + "description": "🎉 Alhamdoulillah, avec votre soutien, Usul a touché des milliers de chercheurs de savoir, comblant le fossé entre tradition et modernité.", + "donate-widget": { + "month-raised": "Collecté", + "goal": "Objectif ${goal, number} par mois", + "active-monthly-donors": "{donors, number} donateurs mensuels actifs", + "become-a-donor": "Devenir un Donateur" + } + }, + "achievements": { + "title": "Réalisations", + "one": { + "title": "Accès inégalé aux textes islamiques.", + "features": { + "0": "Plus de 10 000 textes islamiques classiques et modernes disponibles gratuitement.", + "1": "Outils de recherche avancés pour aider les chercheurs et les apprenants à découvrir facilement des informations." + } + }, + "two": { + "title": "Autonomiser les érudits et les apprenants", + "features": { + "0": "Outils d'IA à la pointe de la technologie qui permettent aux utilisateurs de trouver et d'extraire rapidement des informations à partir de textes classiques.", + "1": "Pièce jointe PDF téléchargée sur de nombreux livres pour permettre aux utilisateurs de valider la source." + } + }, + "three": { + "title": "Belle expérience utilisateur", + "description": "Nous nous efforçons de rendre notre travail beau et apprécié tant par les étudiants débutants en connaissance que par les érudits avancés. De l'expérience de lecture, à l'interface de recherche, à l'expérience mobile." + } + }, + "roadmap": { + "title": "Notre Feuille de Route, Vision d'Usul pour l'avenir", + "description": "Vos dons nous aideront à atteindre les objectifs suivants, inshaAllah :", + "items": { + "0": { + "title": "Étendre la bibliothèque de textes", + "features": { + "0": "Numériser et ajouter des milliers d'autres livres supplémentaires à la plateforme.", + "1": "Assurer une représentation complète des diverses disciplines islamiques et des écoles de pensée." + } + }, + "1": { + "title": "Affiner les outils de recherche", + "features": { + "0": "Introduire des capacités avancées alimentées par l'IA pour permettre aux utilisateurs de trouver des insights dans les textes ou un corpus entier.", + "1": "Développer des fonctionnalités telles que la comparaison de textes côte à côte et la recherche de sources de citations." + } + }, + "2": { + "title": "Accessibilité mondiale", + "features": { + "0": "Traduire les textes en utilisant l'IA en anglais et dans de nombreuses autres langues pour ouvrir l'accès au savoir islamique.", + "1": "Améliorer la performance du site pour les utilisateurs dans les zones avec une connectivité limitée, afin de garantir qu'aucun apprenant ne soit laissé pour compte." + } + } + } + }, + "be-part": { + "title": "Faites partie du voyage d'Usul", + "description": " Votre soutien mensuel stimule l'innovation, soutient notre croissance, et garantit qu'Usul reste une ressource vitale pour les générations futures.", + "why-support": { + "title": "Pourquoi soutenir Usul ?", + "features": { + "0": "Mises à jour trimestrielles transparentes sur la manière dont votre don fait la différence", + "1": "Flexibilité pour annuler ou ajuster votre contribution à tout moment", + "2": "Une occasion de faire partie d'une mission innovante pour préserver et partager le savoir islamique" + } + }, + "choose-amount": { + "title": "Choisissez le montant de votre don :", + "interval": { + "one-time": "Une seule fois", + "monthly": "Mensuel", + "yearly": "Annuel" + }, + "note": "Les dons sont collectés par Usul Foundation, une organisation à but non lucratif 501(c)(3). Tous les dons sont déductibles des impôts aux États-Unis. Pour toute question, veuillez nous contacter à donors@usul.ai.", + "continue": "Continuer" + } + }, + "success": { + "title": "Merci pour votre don !", + "description": "Votre générosité fait une réelle différence. Nous apprécions votre soutien dans notre mission." + }, + "verification": { + "title": "Quelle est votre adresse email ?", + "description": "L'email est requis pour vous envoyer des reçus et des mises à jour pour les donateurs.", + "sent-code": "Nous avons envoyé un code à votre email. Veuillez l'entrer ci-dessous pour compléter le don.", + "verify": "Vérifier", + "send-code": "Envoyer le code" + }, + "already-donor": { + "title": "Déjà donateur ?", + "manage": "Gérer votre don" + } +} \ No newline at end of file diff --git a/locales/fr/meta.json b/locales/fr/meta.json index 5c3f868d..8b65e0fb 100644 --- a/locales/fr/meta.json +++ b/locales/fr/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Équipe", "description": "Rencontrez l'équipe Usul" + }, + "donate-page": { + "title": "Faire un don", + "description": "Faire un don à Usul" } } \ No newline at end of file diff --git a/locales/ha/common.json b/locales/ha/common.json index 9fc64120..45138cf1 100644 --- a/locales/ha/common.json +++ b/locales/ha/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Gudumawa", + "donate": { + "title": "Ba da gudummawa", + "description": "Tallafa Usul" + }, "add-text": { "title": "Ƙara Rubutu", "description": "Loda Littafi ko PDF" diff --git a/locales/ha/donate.json b/locales/ha/donate.json new file mode 100644 index 00000000..54660a7e --- /dev/null +++ b/locales/ha/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "Makomar ilmin Musulunci, ƙarƙashin taimakon AI", + "subtitle": "Ka taimaka mana don sanya ilmin Musulunci ya fi samun sauƙi kuma a samu da sauƙi.", + "description": "🎉 Alhamdulillah, da goyon bayan ku, Usul ya kai dubbai na masu neman ilmi, yana haɗa tazara tsakanin al'ada da zamani.", + "donate-widget": { + "month-raised": "An tara", + "goal": "Manufa ${goal, number} Kowane wata", + "active-monthly-donors": "{donors, number} masu bayar da gudummawa na kowane wata", + "become-a-donor": "Kasance Mai Bayar da Gudummawa" + } + }, + "achievements": { + "title": "Nasarori", + "one": { + "title": "Samun damar karanta littattafan Musulunci marasa tamka.", + "features": { + "0": "Sama da littattafan Musulunci 10,000 na gargajiya da zamani ana samun su kyauta.", + "1": "Kayayyakin neman bayanai na ci gaba don taimakawa masu bincike da masu koyo su gano ilimi cikin sauƙi." + } + }, + "two": { + "title": "Karfafawa Malamai da Masu Koyo", + "features": { + "0": "Kayayyakin AI na zamani da suke baiwa masu amfani damar samun da fitar da bayanai daga abubuwan karatu tare da sauri.", + "1": "Mana'ikan PDF da aka ɗora wa littattafai da yawa don masu amfani su tabbatar da tushen." + } + }, + "three": { + "title": "Kwarewar Mai amfani Mai Kyau", + "description": "Muna ƙoƙari don sanya aikin mu mai kyau da kuma soyuwa ga ɗaliban ilmi na farko da ƙwararrun malamai ma. Daga kwarewar karatu, zuwa ga yanayin neman bayanai, zuwa kwarewar wayar hannu." + } + }, + "roadmap": { + "title": "Taswirar Hanyar mu, Vision na Usul domin Makomar", + "description": "Gudummawarku za ta taimaka mana wajen cimma waɗannan manufofin, inshaAllah:", + "items": { + "0": { + "title": "Faɗaɗa Makarantar Rubutu", + "features": { + "0": "Dijitaliza da ƙarin dubban littattafai zuwa dandalin.", + "1": "Tabbatar da cikakken wakilcin fannoni daban-daban na Musulunci da makaranto na tunani." + } + }, + "1": { + "title": "Inganta Kayan Bincike", + "features": { + "0": "Gabatar da karin fasali na AI da zai baiwa masu amfani damar samun bayanai tsakanin rubuce-rubucen ko dukkan corpus.", + "1": "Ƙirƙiri fasali irin su kwatancen rubutu bisa fuska da fuska da nemo tushe mai faɗar magana." + } + }, + "2": { + "title": "Samun Damar Duk Duniya", + "features": { + "0": "Fassara rubuce-rubucen da ke amfani da AI zuwa Ingilishi da yawancin ɗaiɗaikun harsuna don buɗe damar samun ilimin Musulunci.", + "1": "Inganta aiki na gidan yanar for masu amfani a wuraren da aka iyakance, yana tabbatar da babu wani mai koyo da aka bar a baya." + } + } + } + }, + "be-part": { + "title": "Kasance Sashin Tafiyar Usul", + "description": " Tallafin ku na kowane wata yana tsugar da kirkira, yana tallafawa ci gaban mu, kuma yana tabbatar da cewa Usul zai ci gaba da kasancewa muhimmiyar tushe ga tsararrakki masu zuwa.", + "why-support": { + "title": "Me ya sa za a tallafi Usul?", + "features": { + "0": "Bayani na kowane kwata akan yadda gudummawar ku ke kawo canji", + "1": "Yancin bayar da gudummawa tare da damar soke ko gyara duk lokacin da kake so", + "2": "Damar zama wani bangare na babban aiki na kiyaye da raba ilimin Musulunci" + } + }, + "choose-amount": { + "title": "Zaɓi adadin gudummawar ku:", + "interval": { + "one-time": "Sau ɗaya", + "monthly": "Kowanne wata", + "yearly": "Kowanne shekara" + }, + "note": "Gudummawa ana tara su ne ta Usul Foundation, wata kungiya mara riba 501(c)(3). Duk gudummawar tana iya samun rangwamen haraji a Amurka. Don kowanne tambaya, da fatan za a tuntube mu a donors@usul.ai.", + "continue": "Ci gaba" + } + }, + "success": { + "title": "Na gode da gudummawarku!", + "description": "Gatan ku yana kawo bambanci na hakika. Muna yaba tallafinku a aikin mu." + }, + "verification": { + "title": "Menene imel ɗinka?", + "description": "Ana buƙatar imel don aika maka da takaddun shaidar ku da sabunta gudummawa.", + "sent-code": "Mun aika da lambar zuwa imel ɗinka. Da fatan za a shigar da shi a ƙasa don kammala gudummawa.", + "verify": "Tabbatarwa", + "send-code": "Aika Lambar" + }, + "already-donor": { + "title": "Ka riga ka zama mai bayarwa?", + "manage": "Sarrafa gudummawarku" + } +} \ No newline at end of file diff --git a/locales/ha/meta.json b/locales/ha/meta.json index a0c70d55..18af828c 100644 --- a/locales/ha/meta.json +++ b/locales/ha/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Ƙungiya", "description": "Koyi da Ƙungiyar Usul" + }, + "donate-page": { + "title": "Ba da Gudunmawa", + "description": "Ba da gudunmawa ga Usul" } } \ No newline at end of file diff --git a/locales/hi/common.json b/locales/hi/common.json index ad10fb0d..f0094965 100644 --- a/locales/hi/common.json +++ b/locales/hi/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "योगदान दें", + "donate": { + "title": "दान करें", + "description": "Usul का समर्थन करें" + }, "add-text": { "title": "पाठ जोड़ें", "description": "पुस्तक या PDF अपलोड करें" diff --git a/locales/hi/donate.json b/locales/hi/donate.json new file mode 100644 index 00000000..32c3003f --- /dev/null +++ b/locales/hi/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "इस्लामी ज्ञान का भविष्य, AI द्वारा समर्थित", + "subtitle": "हमें इस्लामी ज्ञान को अधिक सुलभ बनाने और इसकी खोज को बहुत आसान बनाने में मदद करें।", + "description": "🎉 अल्हम्दुलिल्लाह, आपके समर्थन से, उसूल ने हजारों ज्ञान खोजी लोगों तक पहुँच बनाई, पारंपरिक और आधुनिकता के बीच सेतु बाँधते हुए।", + "donate-widget": { + "month-raised": "उठाया", + "goal": "लक्ष्य ${goal, number} मासिक", + "active-monthly-donors": "{donors, number} सक्रिय मासिक दानकर्ता", + "become-a-donor": "दानकर्ता बनें" + } + }, + "achievements": { + "title": "उपलब्धियां", + "one": { + "title": "इस्लामी आलेखों तक अद्वितीय पहुँच।", + "features": { + "0": "10,000 से अधिक शास्त्रीय और आधुनिक इस्लामी आलेख मुफ्त में उपलब्ध।", + "1": "शोधकर्ताओं और शिक्षार्थियों को आसानी से जानकारी हासिल करने में मदद करने के लिएउन्नत खोज उपकरण।" + } + }, + "two": { + "title": "विद्वानों और शिक्षार्थियों को सशक्त बनाना", + "features": { + "0": "आधुनिक AI उपकरण जो उपयोगकर्ताओं को शास्त्रीय आलेखों से जल्दी से जानकारी ढूंढने और निकालने की अनुमति देते हैं।", + "1": "PDF अनुबंध को स्रोत की पुष्टि करने के लिए कई पुस्तकों में अपलोड किया गया।" + } + }, + "three": { + "title": "सुंदर उपयोगकर्ता अनुभव", + "description": "हम अपनी मेहनत को सुंदर और प्रिय बनाने की कोशिश करते हैं, चाहे वह ज्ञान के शुरुआती छात्र हों या उन्नत विद्वान। पढ़ाई के अनुभव से लेकर खोज इंटरफेस तक, मोबाइल अनुभव तक।" + } + }, + "roadmap": { + "title": "हमारा पथचित्र, उसूल की भविष्य के लिए दृष्टि", + "description": "आपके दान हमें निम्नलिखित लक्ष्यों को प्राप्त करने में मदद करेंगे, इंशाअल्लाह:", + "items": { + "0": { + "title": "पाठ पुस्तकालय का विस्तार करें", + "features": { + "0": "हजारों अतिरिक्त किताबों को डिजिटाइज़ करें और इस मंच में जोड़ें।", + "1": "विभिन्न इस्लामी विषयों और विचारधाराओं की समग्र प्रतिनिधित्व सुनिश्चित करें।" + } + }, + "1": { + "title": "अनुसंधान उपकरणों को परिष्कृत करें", + "features": { + "0": "उपयोगकर्ताओं को आयतों या पूरे साहित्य में जानकारी खोजने की अनुमति देने के लिए अधिक उन्नत एआई-संचालित क्षमताएं पेश करें।", + "1": "साइड-बाई-साइड पाठ तुलनाओं और उद्धरण स्रोत खोजने जैसी सुविधाएँ विकसित करें।" + } + }, + "2": { + "title": "वैश्विक पहुँच", + "features": { + "0": "इस्लामी ज्ञान तक पहुँच खोलने के लिए एआई का उपयोग करके ग्रंथों का अनुवाद अंग्रेजी और कई अन्य भाषाओं में करें।", + "1": "सीमित कनेक्टिविटी वाले क्षेत्रों में उपयोगकर्ताओं के लिए वेबसाइट प्रदर्शन में सुधार करें, यह सुनिश्चित करके कि कोई भी शिक्षार्थी पीछे न रह जाये।" + } + } + } + }, + "be-part": { + "title": "हमारे सफर का हिस्सा बनें", + "description": " आपका मासिक समर्थन नवाचार को चलाता है, हमारी वृद्धि को बनाए रखता है, और यह सुनिश्चित करता है कि उसूल आने वाली पीढ़ियों के लिए एक महत्वपूर्ण संसाधन बना रहे।", + "why-support": { + "title": "उसूल का समर्थन क्यों?", + "features": { + "0": "आपके दान का प्रभाव डालने के तरीके पर पारदर्शी त्रैमासिक अपडेट", + "1": "कभी भी अपने योगदान को रद्द या समायोजित करने की लचीलापन", + "2": "इस्लामी ज्ञान को संरक्षित और साझा करने के एक अभूतपूर्व मिशन का हिस्सा बनने का एक अवसर" + } + }, + "choose-amount": { + "title": "अपने दान की राशि चुनें:", + "interval": { + "one-time": "एक बार", + "monthly": "मासिक", + "yearly": "वार्षिक" + }, + "note": "दान उसुल फाउंडेशन द्वारा एकत्र किए जाते हैं, जो एक 501(c)(3) गैर-लाभकारी संगठन है। सभी दान अमेरिका में कर-मुक्त होते हैं। किसी भी पूछताछ के लिए, कृपया हमें donors@usul.ai पर संपर्क करें।", + "continue": "जारी रखें" + } + }, + "success": { + "title": "आपके दान के लिए धन्यवाद!", + "description": "आपकी उदारता वास्तव में एक बड़ा अंतर लाती है। हमारे मिशन में आपके समर्थन की हम सराहना करते हैं।" + }, + "verification": { + "title": "आपका ईमेल क्या है?", + "description": "रसीदें और दानकर्ता अपडेट भेजने के लिए ईमेल आवश्यक है।", + "sent-code": "हमने आपके ईमेल पर एक कोड भेजा है। कृपया दान पूरा करने के लिए इसे नीचे दर्ज करें।", + "verify": "सत्यापित करें", + "send-code": "कोड भेजें" + }, + "already-donor": { + "title": "पहले से ही दानकर्ता हैं?", + "manage": "अपने दान का प्रबंधन करें" + } +} \ No newline at end of file diff --git a/locales/hi/meta.json b/locales/hi/meta.json index 8a221f0f..e4c00972 100644 --- a/locales/hi/meta.json +++ b/locales/hi/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "टीम", "description": "Usul टीम से मिलें" + }, + "donate-page": { + "title": "दान करें", + "description": "उसूल को दान करें" } } \ No newline at end of file diff --git a/locales/ms/common.json b/locales/ms/common.json index 20f2092e..bfd7f08c 100644 --- a/locales/ms/common.json +++ b/locales/ms/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Sumbang", + "donate": { + "title": "Derma", + "description": "Sokong Usul" + }, "add-text": { "title": "Tambah Teks", "description": "Muat Naik Buku atau PDF" diff --git a/locales/ms/donate.json b/locales/ms/donate.json new file mode 100644 index 00000000..32e195cb --- /dev/null +++ b/locales/ms/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "Masa depan pengetahuan Islam, dipacu oleh AI", + "subtitle": "Bantu kami menjadikan pengetahuan Islam lebih mudah diakses dan lebih mudah untuk diperoleh.", + "description": "🎉 Alhamdulillah, dengan sokongan anda, Usul telah mencapai ribuan pencari ilmu, merapatkan jurang antara tradisi dan kemodenan.", + "donate-widget": { + "month-raised": "Dikumpulkan", + "goal": "Matlamat ${goal, number} Bulanan", + "active-monthly-donors": "{donors, number} penderma aktif bulanan", + "become-a-donor": "Jadi Penderma" + } + }, + "achievements": { + "title": "Pencapaian", + "one": { + "title": "Akses Tidak Tertandingi kepada Teks Islam.", + "features": { + "0": "Lebih dari 10,000 teks Islam klasik dan moden tersedia secara percuma.", + "1": "Alat pencarian lanjutan untuk membantu penyelidik dan pelajar mendapatkan insight dengan mudah." + } + }, + "two": { + "title": "Memperkasa Cendekiawan dan Pelajar", + "features": { + "0": "Alat AI tercanggih yang membolehkan pengguna mencari dan mengekstrak maklumat dari teks klasik dengan cepat.", + "1": "PDF Lampiran dimuat naik ke banyak buku untuk membolehkan pengguna mengesahkan sumber." + } + }, + "three": { + "title": "Pengalaman Pengguna yang Menarik", + "description": "Kami berusaha untuk menjadikan kerja kami indah dan disukai oleh pelajar pengetahuan pemula dan cendekiawan mahir. Dari pengalaman membaca hingga antaramuka pencarian, hingga pengalaman mudah alih." + } + }, + "roadmap": { + "title": "Pelan Kami, Visi Usul untuk Masa Depan", + "description": "Derma anda akan membantu kami mencapai matlamat berikut, inshaAllah:", + "items": { + "0": { + "title": "Memperluas Perpustakaan Teks", + "features": { + "0": "Digitalisasi dan tambahan ribuan buku tambahan ke platform.", + "1": "Memastikan representasi komprehensif dari pelbagai disiplin dan aliran pemikiran Islam." + } + }, + "1": { + "title": "Perhalusi Alat Penyelidikan", + "features": { + "0": "Memperkenalkan lebih banyak keupayaan dikuasakan AI lanjutan untuk membolehkan pengguna mencari insight seluruh teks atau keseluruhan korpus.", + "1": "Membangunkan ciri seperti perbandingan teks sebelah menyebelah dan mencari sumber petikan." + } + }, + "2": { + "title": "Keteraksesan Global", + "features": { + "0": "Menterjemah teks menggunakan AI ke dalam Bahasa Inggeris dan banyak bahasa lain untuk membuka akses kepada pengetahuan Islam.", + "1": "Meningkatkan prestasi laman web untuk pengguna di kawasan dengan sambungan terhad, memastikan tiada pelajar tertinggal." + } + } + } + }, + "be-part": { + "title": "Jadi Sebahagian daripada Perjalanan Usul", + "description": " Sokongan bulanan anda mendorong inovasi, mengekalkan pertumbuhan kami, dan memastikan Usul kekal sebagai sumber penting untuk generasi akan datang.", + "why-support": { + "title": "Mengapa menyokong Usul?", + "features": { + "0": "Kemaskini suku tahunan yang telus mengenai bagaimana sumbangan anda memberikan perbezaan", + "1": "Fleksibiliti untuk membatalkan atau menyesuaikan sumbangan anda pada bila-bila masa", + "2": "Peluang untuk menjadi sebahagian daripada misi terobosan untuk memelihara dan berkongsi ilmu Islam" + } + }, + "choose-amount": { + "title": "Pilih jumlah derma anda:", + "interval": { + "one-time": "Sekali", + "monthly": "Bulanan", + "yearly": "Tahunan" + }, + "note": "Derma dikumpul oleh Usul Foundation, sebuah organisasi nirlaba 501(c)(3). Semua derma boleh ditolak cukai di AS. Untuk sebarang pertanyaan, sila hubungi kami di donors@usul.ai.", + "continue": "Teruskan" + } + }, + "success": { + "title": "Terima kasih atas derma anda!", + "description": "Kemurahan hati anda memberikan perbezaan yang sebenar. Kami menghargai sokongan anda dalam misi kami." + }, + "verification": { + "title": "Apakah emel anda?", + "description": "Emel diperlukan untuk menghantar resit dan kemas kini penderma kepada anda.", + "sent-code": "Kami telah menghantar kod ke emel anda. Sila masukkan ia di bawah untuk melengkapkan derma.", + "verify": "Sahkan", + "send-code": "Hantar Kod" + }, + "already-donor": { + "title": "Sudah menjadi penyumbang?", + "manage": "Uruskan derma anda" + } +} \ No newline at end of file diff --git a/locales/ms/meta.json b/locales/ms/meta.json index 9244cfec..598c09d6 100644 --- a/locales/ms/meta.json +++ b/locales/ms/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Pasukan", "description": "Berkenalan dengan pasukan Usul" + }, + "donate-page": { + "title": "Derma", + "description": "Derma kepada Usul" } } \ No newline at end of file diff --git a/locales/ps/common.json b/locales/ps/common.json index 0a5b3897..7895f85c 100644 --- a/locales/ps/common.json +++ b/locales/ps/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "ونډه ورکول", + "donate": { + "title": "بسپارئ", + "description": "له Usul سره ملاتړ وکړئ‎" + }, "add-text": { "title": "متن اضافه کول", "description": "کتاب یا پی‌ډی‌اف پورته کړئ" diff --git a/locales/ps/donate.json b/locales/ps/donate.json new file mode 100644 index 00000000..b1ca7c6d --- /dev/null +++ b/locales/ps/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "د اسلامي پوهې راتلونکی، د مصنوعي استخباراتو لخوا ځواکمن شوی", + "subtitle": "موږ سره مرسته وکړئ ترڅو اسلامي پوهه لا لاسرسی ولري او د هغې ترلاسه کول خورا اسانه وي.", + "description": "🎉 الحمد لله، ستاسو په ملاتړ سره، اصول د پوهې زرګونه غوښتونکي ته رسیدلی، د دود او عصریت تر مینځ پل جوړوي.", + "donate-widget": { + "month-raised": "راټول شوی", + "goal": "هدف ${goal, number} میاشتنی", + "active-monthly-donors": "{donors, number} فعال میاشتنې مرسته کوونکي", + "become-a-donor": "مرسته کوونکی شئ" + } + }, + "achievements": { + "title": "لاسته راوړنې", + "one": { + "title": "اسلامي متونو ته بې ساري لاسرسی.", + "features": { + "0": "له ۱۰٬۰۰۰ څخه زیات کلاسیکي او عصري اسلامي متنونه په وړیا توګه شتون لري.", + "1": "څیړونکو او زده کونکو سره د بشپړتیا لپاره د پرمختللي لټون وسیلې." + } + }, + "two": { + "title": "عالمانو او زده کوونکو ته د واک ورکول", + "features": { + "0": "د عصري AI وسیلې چې کاروونکو ته اجازه ورکوي چې کلاسیکو متنونو څخه په چټکۍ سره معلومات ومومي او استخراج کړي.", + "1": "ډیری کتابونو تهPDF مرفق پورته شوی ترڅو کارونکي د سرچینې اعتبار وکړئ." + } + }, + "three": { + "title": "ښکلی کارن تجربه", + "description": "موږ هڅه کوو چې زموږ کار ښکلی او محبوب کړو د پوهې د بتدیو زده کونکو او پرمختللو پوهانو لپاره په یو شان. د لوستلو تجربې څخه، د لټون انٹرفیس ته، د موبایل تجربې پورې." + } + }, + "roadmap": { + "title": "زموږ سړک نقشه، د اسول د راتلونکي نظر", + "description": "ستا د خیریه پیسې به موږ سره مرسته وکړي چې لاندې موخې ترلاسه کړو، ان شاء الله:", + "items": { + "0": { + "title": "د متن کتابتون پراخ کړئ", + "features": { + "0": "زرګونه اضافي کتابونه ديجټل او په پلیټ فارم کې اضافه کړئ.", + "1": "د مختلفو اسلامي څانګو او فکر ښوونځیو جامع استازیتوب یقیني کړئ." + } + }, + "1": { + "title": "د څیړنې وسایل اصلاح کړئ", + "features": { + "0": "د پرمختللو AI ځواکمن وړتیاوې معرفی کړئ چې کاروونکو ته اجازه ورکوي چې د متنونو یا ټولې کاپی څخه بینشونه ومومي.", + "1": "لکه د مخامخ متن پرتله کول او د اقتباس سرچینې موندل." + } + }, + "2": { + "title": "نړیوال لاسرسی", + "features": { + "0": "د اسلامي پوهې ته د لاسرسي د پرانستلو لپاره د AI په کارولو سره په انګلیسي او ډیرو نورو ژبو کې بېلابېل وژباړئ.", + "1": "د هغو ځایونو کاروونکو لپاره د ویب پا performanceې فعالیت ښه کړئ چې محدود ارتباط لري، ډاډ ترلاسه کړئ چې هیڅ زده کونکی شاته پاتې نه دی." + } + } + } + }, + "be-part": { + "title": "د اسول په سفر کې برخه واخلئ", + "description": " ستاسو میاشتنی ملاتړ نوښت رهبري کوي، زموږ وده په دوام ساتي، او دا یقیني کوي چې اسول د راتلونکو نسلونو لپاره یوه مهمه سرچینه پاتې شي.", + "why-support": { + "title": "ولې د اسول ملاتړ وکړئ؟", + "features": { + "0": "څنګه ستاسو مرسته توپیر رامنځته کوي په اړه ربع وار شفاف تازه خبرونه", + "1": "په هر وخت کې ستاسو د مرسته ردولو یا تنظیم کولو انعطاف", + "2": "د اسلامي پوهنې د ساتنې او شریکولو لپاره د یو مهم مأموریت برخه جوړیدو لپاره یوه موقعه" + } + }, + "choose-amount": { + "title": "ستاسو د مرستې اندازه وټاکئ:", + "interval": { + "one-time": "یو ځل", + "monthly": "میاشتنی", + "yearly": "کلنی" + }, + "note": "عطیې د اسول بنیاد لخوا راټولیږي، یوه ۵۰۱(c)(۳) غیر انتفاعي مؤسسه. ټول عطیې په امریکاکې د مالیې څخه بې برخې دي. د کومې پوښتنې په اړه، مهرباني وکړئ موږ سره donors@usul.ai کې اړیکه ونیسئ.", + "continue": "ادامه ورکړئ" + } + }, + "success": { + "title": "د خپلو مرسته مننه!", + "description": "ستاسو سخاوت واقعی توپیر رامنځته کوي. موږ په خپل مأموریت کې ستاسو ملاتړ ارزښت ورکړو." + }, + "verification": { + "title": "ستاسو برېښنالیک څه دی؟", + "description": "رسیدونه او د عطا کوونکو تازه خبرونه درته د لیږلو لپاره برېښنالیک ته اړتیا ده.", + "sent-code": "موږ ستاسو برېښنالیک ته یو کود ولیږلی. مهرباني وکړئ دا لاندې داخل کړئ ترڅو مرسته پوره کړئ.", + "verify": "تصدیق وکړئ", + "send-code": "کود واستوئ" + }, + "already-donor": { + "title": "آیا تاسو وړاندې یو مرسته کوونکی یاست؟", + "manage": "خپله مرسته مدیریت کړئ" + } +} \ No newline at end of file diff --git a/locales/ps/meta.json b/locales/ps/meta.json index e34e024d..2dad4a0f 100644 --- a/locales/ps/meta.json +++ b/locales/ps/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "ټیم", "description": "د اصول ټیم سره وګورئ" + }, + "donate-page": { + "title": "ډونیشن", + "description": "اوسول ته ډونیشن وکړئ" } } \ No newline at end of file diff --git a/locales/ru/common.json b/locales/ru/common.json index e3e11c6a..39e58f44 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Внести вклад", + "donate": { + "title": "Пожертвовать", + "description": "Поддержать Usul" + }, "add-text": { "title": "Добавить текст", "description": "Загрузите книгу или PDF" diff --git a/locales/ru/donate.json b/locales/ru/donate.json new file mode 100644 index 00000000..624a8a3e --- /dev/null +++ b/locales/ru/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "Будущее исламских знаний, поддерживаемое ИИ", + "subtitle": "Помогите нам сделать исламские знания более доступными и облегчить их получение.", + "description": "🎉 Альхамдулиллах, благодаря вашей поддержке, Usul достиг тысяч искателей знаний, преодолевая разрыв между традицией и современностью.", + "donate-widget": { + "month-raised": "Собрано", + "goal": "Цель ${goal, number} ежемесячно", + "active-monthly-donors": "{donors, number} активных ежемесячных доноров", + "become-a-donor": "Стать донором" + } + }, + "achievements": { + "title": "Достижения", + "one": { + "title": "Несравненный доступ к исламским текстам.", + "features": { + "0": "Более 10 000 классических и современных исламских текстов доступны бесплатно.", + "1": "Продвинутые инструменты поиска, которые помогают исследователям и ученикам с легкостью находить выводы." + } + }, + "two": { + "title": "Укрепление ученых и обучающихся", + "features": { + "0": "Современные инструменты ИИ, которые позволяют пользователям быстро находить и извлекать информацию из классических текстов.", + "1": "PDF вложения, загруженные в многие книги, чтобы пользователи могли подтвердить источник." + } + }, + "three": { + "title": "Прекрасный пользовательский опыт", + "description": "Мы стремимся сделать нашу работу прекрасной и любимой как начинающими студентами знаний, так и опытными учеными. От опыта чтения до интерфейса поиска и мобильного опыта." + } + }, + "roadmap": { + "title": "Наш план, Видение Usul на будущее", + "description": "Ваши пожертвования помогут нам достичь следующих целей, ин ша Аллах:", + "items": { + "0": { + "title": "Расширение библиотеки текстов", + "features": { + "0": "Оцифровать и добавить тысячи дополнительных книг на платформу.", + "1": "Обеспечить всестороннее представление различных исламских дисциплин и школ мысли." + } + }, + "1": { + "title": "Усовершенствование исследовательских инструментов", + "features": { + "0": "Внедрить более продвинутые возможности на базе ИИ, чтобы пользователи могли находить выводы в текстах или целом корпусе.", + "1": "Разработать функции, такие как сравнение текстов бок о бок и поиск источников цитат." + } + }, + "2": { + "title": "Глобальная доступность", + "features": { + "0": "Переводить тексты с помощью ИИ на английский и многие другие языки для открытия доступа к исламским знаниям.", + "1": "Улучшить производительность сайта для пользователей в зонах с ограниченной связью, обеспечив, чтобы ни один обучающийся не остался забытым." + } + } + } + }, + "be-part": { + "title": "Станьте частью пути Usul", + "description": " Ваш ежемесячный вклад стимулирует инновации, поддерживает наш рост и гарантирует, что Usul останется жизненно важным ресурсом для будущих поколений.", + "why-support": { + "title": "Почему поддерживать Usul?", + "features": { + "0": "Прозрачные ежеквартальные обновления о том, как ваше пожертвование оказывает влияние", + "1": "Возможность отменить или изменить ваше пожертвование в любое время", + "2": "Возможность стать частью прорывной миссии по сохранению и распространению исламских знаний" + } + }, + "choose-amount": { + "title": "Выберите сумму вашего пожертвования:", + "interval": { + "one-time": "Один раз", + "monthly": "Ежемесячно", + "yearly": "Ежегодно" + }, + "note": "Пожертвования собираются Фондом Usul, некоммерческой организацией 501(c)(3). Все пожертвования налоговые льготы в США. По любым вопросам, пожалуйста, свяжитесь с нами по адресу donors@usul.ai.", + "continue": "Продолжить" + } + }, + "success": { + "title": "Спасибо за ваше пожертвование!", + "description": "Ваша щедрость действительно оказывает влияние. Мы ценим вашу поддержку нашей миссии." + }, + "verification": { + "title": "Какой у вас адрес электронной почты?", + "description": "Электронная почта нужна для отправки вам квитанций и обновлений для доноров.", + "sent-code": "Мы отправили код на вашу электронную почту. Пожалуйста, введите его ниже, чтобы завершить пожертвование.", + "verify": "Проверить", + "send-code": "Отправить код" + }, + "already-donor": { + "title": "Уже донор?", + "manage": "Управляйте своим пожертвованием" + } +} \ No newline at end of file diff --git a/locales/ru/meta.json b/locales/ru/meta.json index 066cbcbb..53664a35 100644 --- a/locales/ru/meta.json +++ b/locales/ru/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Команда", "description": "Познакомьтесь с командой Usul" + }, + "donate-page": { + "title": "Пожертвовать", + "description": "Пожертвовать на Усуль" } } \ No newline at end of file diff --git a/locales/so/common.json b/locales/so/common.json index 69f4b2db..112db907 100644 --- a/locales/so/common.json +++ b/locales/so/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Ku Biiri", + "donate": { + "title": "Tabbaruca", + "description": "Nagu taageer" + }, "add-text": { "title": "Ku Dar Qoraal", "description": "Soo geli Buug ama PDF" diff --git a/locales/so/donate.json b/locales/so/donate.json new file mode 100644 index 00000000..cddc9aa7 --- /dev/null +++ b/locales/so/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "Mustaqbalka aqoonta Islaamka, oo ay taageerayaan AI", + "subtitle": "Naga caawi inaan ka dhigno aqoonta Islaamka mid si fudud loo heli karo oo fudud in dib loo soo celiyo.", + "description": "🎉 Alxamdulillah, taageeradaada awgeed, Usul waxay gaadhay kumanaan raadinaya aqoon, iyagoo buuxinaya farqiga udhaxeeya dhaqanka iyo casriyeynta.", + "donate-widget": { + "month-raised": "La ururiyey", + "goal": "Hadafka ${goal, number} Bil kasta", + "active-monthly-donors": "{donors, number} tabarucaad joogto ah oo bil walba ah", + "become-a-donor": "Noqo tabaruce" + } + }, + "achievements": { + "title": "Guulaha", + "one": { + "title": "Helitaan aan caadi ahayn oo loo maro qoraallo Islaami ah.", + "features": { + "0": "In ka badan 10,000 qoraallo Islaami caadi iyo casri ah ayaa si bilaash ah lagu heli karaa.", + "1": "Qalabaynta cilmi baaris casri ah si ay ugu sahlayso cilmi-baarayaasha iyo kuwa wax baranaya inay si fudud u soo bandhigaan aragtiyo." + } + }, + "two": { + "title": "Xoojinta Culimada iyo Ardayda", + "features": { + "0": "Qalab casri ah oo AI oo u suurtagelisa isticmaalayaasha inay si dhaqso leh u helaan oo uga soo saaraan macluumaadka qoraallada caadiga ah.", + "1": "Ku lifaaqyada PDF oo loo soo geliyay buugaag badan si ay isticmaalehu u xaqiijiyaan isha." + } + }, + "three": { + "title": "Khibrad isticmaale oo qurux badan", + "description": "Waxaan ku dadaalaynaa in shaqadeena ay ahaato qurux iyo jacayl oo ay jeclaadaan ardayda bilowga ah kuwa aqoonta iyo culimada sare Si lamid ah Khibrada akhriska, interface-ka raadinta, iyo khibrada mobilada." + } + }, + "roadmap": { + "title": "Khariidadda Wadadeena, Himilada Usul ee Mustaqbalka", + "description": "Ku deeq bixintaadu waxay naga caawin doontaa inaan gaarno yoolalka soo socda, inshaAllah:", + "items": { + "0": { + "title": "Kordhi Maktabadda Qoraalka", + "features": { + "0": "Dijitaan gareynta iyo kumanaan buugaagda kale ee dheeraadka ah ku dar madasha.", + "1": "Hubi matalaad dhameystiran oo loo dhigo culuumta kala duwan ee Islaamka iyo fikirka thoughtschools." + } + }, + "1": { + "title": "Soo Qurxin Qalabada Cilmi-baarista", + "features": { + "0": "Soo bandhig awoodaha AI ee horumarsan si ay u oggolaadaan isticmaalayaasha inay helaan aragtiyo qoraallada ama guud ahaan qoraalka.", + "1": "Horumarinta astaamo sida isbarbardhigga qoraallada dhinac-dhinac iyo helida ilaha sheegashada." + } + }, + "2": { + "title": "Helitaanka Caalamka", + "features": { + "0": "Tarjumaadda qoraallada adoo adeegsanaya AI luuqadaha Ingiriisiga iyo kuwo badan oo kale si aad uga faa'iidaysato aqoonta Islaamka.", + "1": "Sii deynta website gaar ahaan isticmaalayaasha meelo xitaa leh xariir xadidan, hubi inaan la hilmaanin." + } + } + } + }, + "be-part": { + "title": "Ka qayb ka noqo Safarka Usul", + "description": " Taageeradaada bil kasta waxay kordhisaa hal-abuurka, waxay sii wadaa koritaankayaga, waxayna hubisaa in Usul uu yahay il muhiim ah oo jiilalka soo socda.", + "why-support": { + "title": "Maxaad u taageeraysaa Usul?", + "features": { + "0": "Warbixinno furan oo saddex-biloodle ah oo ku saabsan sida tabarucaaddaadu u beddeshay", + "1": "Fudud u jignow inaad joojiso ama habbooniso tabarucaaddaada wakhti kasta", + "2": "Fursad inaad ka mid noqoto misiyaan cusub oo lagu ilaaliyo oo lagu wadaago aqoonta Islamka" + } + }, + "choose-amount": { + "title": "Dooro qadarka deeqdaada:", + "interval": { + "one-time": "Hal mar", + "monthly": "Bil kasta", + "yearly": "Sannad kasta" + }, + "note": "Deeqaha waxaa uruuriya Usul Foundation, oo ah hay'ad aan faa'iido doon ahayn oo 501(c)(3). Dhammaan deeqaha waxaa laga jarayaa cashuurta gudaha Maraykanka. Wixii su'aalo ah, fadlan nala soo xiriir donors@usul.ai.", + "continue": "Sii Soco" + } + }, + "success": { + "title": "Mahadsanid Deeqdaada!", + "description": "Deeqsinimadaan waxay keentaa farqi dhab ah. Waxaan ku qadarinayaa taageerada aad noogu siiyeeyso howsha." + }, + "verification": { + "title": "Waa maxay email-kaaga?", + "description": "Email waxaa loo baahan yahay in laguugu diro rasiidhada iyo cusbooneysiinta daneeyaha.", + "sent-code": "Waxaan kaaga soo dirnay kood email-kaaga. Fadlan geli hoose si aad u dhammaystirto deeqda.", + "verify": "Hubi", + "send-code": "Soo Dir Code" + }, + "already-donor": { + "title": "Hore ma u ahayd dhibbanaha?", + "manage": "Maamul Deeqdaada" + } +} \ No newline at end of file diff --git a/locales/so/meta.json b/locales/so/meta.json index 3a71014c..702a3c6b 100644 --- a/locales/so/meta.json +++ b/locales/so/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Kooxda", "description": "La kulan kooxda Usul" + }, + "donate-page": { + "title": "Ku deeq", + "description": "Ku deeq Usul" } } \ No newline at end of file diff --git a/locales/tr/common.json b/locales/tr/common.json index f1830db9..ed1a2083 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "Katkıda Bulun", + "donate": { + "title": "Bağış Yap", + "description": "Usul'e Destek Ol" + }, "add-text": { "title": "Metin Ekle", "description": "Kitap veya PDF Yükleyin" diff --git a/locales/tr/donate.json b/locales/tr/donate.json new file mode 100644 index 00000000..500fd550 --- /dev/null +++ b/locales/tr/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "İslam bilgisi geleceği, yapay zekâ ile destekleniyor", + "subtitle": "İslam bilgisini daha erişilebilir hale getirmemize ve daha kolay geri alınmasını sağlamamıza yardım edin.", + "description": "🎉 Elhamdülillah, desteğinizle Usul, binlerce bilgi arayışında olan kişiye ulaştı ve gelenek ile modernite arasında köprü kurdu.", + "donate-widget": { + "month-raised": "Toplam", + "goal": "Hedef ${goal, number} Aylık", + "active-monthly-donors": "{donors, number} aktif aylık bağışçı", + "become-a-donor": "Bağışçı Olun" + } + }, + "achievements": { + "title": "Başarılar", + "one": { + "title": "İslam Metinlerine Benzersiz Erişim.", + "features": { + "0": "'den fazla 10.000 klasik ve modern İslam metni ücretsiz olarak sunulmuştur.", + "1": "Araştırmacılar ve öğrenenlerin kolaylıkla içgörüler elde etmesine yardımcı olacak gelişmiş arama araçları." + } + }, + "two": { + "title": "Akademisyenleri ve Öğrenenleri Güçlendirme", + "features": { + "0": "Kullanıcıların klasik metinlerden hızlıca bilgi bulmalarını ve çıkarmalarını sağlayan en son yapay zekâ araçları.", + "1": "Birçok kitaba yüklenen kullanıcıların kaynağı doğrulaması için PDF Ekleri." + } + }, + "three": { + "title": "Güzel Kullanıcı Deneyimi", + "description": "Çalışmamızı, başlangıç seviyesindeki bilgi öğrenicilerinden ileri düzey akademisyenlere kadar güzel ve sevilen hale getirmeye çalışıyoruz. Okuma deneyiminden, arama arayüzüne, mobil deneyime kadar." + } + }, + "roadmap": { + "title": "Yol Haritamız, Usul'un Gelecek Vizyonu", + "description": "Bağışlarınız, inşaAllah aşağıdaki hedeflere ulaşmamıza yardımcı olacaktır:", + "items": { + "0": { + "title": "Metin Kütüphanesini Genişletin", + "features": { + "0": "Platforma ek olarak binlerce ek kitabı dijitalleştirin ve ekleyin.", + "1": "Çeşitli İslami disiplinlerin ve düşünce okullarının kapsamlı temsiliyetini sağlayın." + } + }, + "1": { + "title": "Araştırma Araçlarını İyileştirin", + "features": { + "0": "Kullanıcıların metinler veya tüm bir külliyat boyunca içgörü bulmalarını sağlamak için daha gelişmiş yapay zekâ destekli yetenekler ekleyin.", + "1": "Yan yana metin karşılaştırmaları ve alıntı kaynaklarını bulma gibi özellikler geliştirin." + } + }, + "2": { + "title": "Küresel Erişilebilirlik", + "features": { + "0": "İslam bilgisine erişimi açmak için yapay zekâ kullanarak metinleri İngilizce ve diğer birçok dile çevirin.", + "1": "Sınırlı bağlantılı alanlarda kullanıcılar için web sitesi performansını geliştirin, hiçbir öğrenicinin geride kalmamasını sağlayın." + } + } + } + }, + "be-part": { + "title": "Usul'un Yolculuğunun Bir Parçası Olun", + "description": " Aylık desteğiniz inovasyonu yönlendirir, büyümemizi sürdürülebilir kılar ve Usul'un gelecek nesiller için önemli bir kaynak olarak kalmasını sağlar.", + "why-support": { + "title": "Usul'a neden destek verilmeli?", + "features": { + "0": "Bağışınızın nasıl bir fark yarattığını gösteren şeffaf üç aylık güncellemeler", + "1": "Katkınızı iptal etme veya ayarlama esnekliği", + "2": "İslam bilgisini koruma ve paylaşma konusunda öncü bir misyona dahil olma fırsatı" + } + }, + "choose-amount": { + "title": "Bağış miktarınızı seçin:", + "interval": { + "one-time": "Tek Seferlik", + "monthly": "Aylık", + "yearly": "Yıllık" + }, + "note": "Bağışlar, 501(c)(3) kâr amacı gütmeyen bir kuruluş olan Usul Vakfı tarafından toplanır. Tüm bağışlar, ABD'de vergiden düşülebilir. Herhangi bir soru için lütfen bizimle donors@usul.ai adresinden iletişime geçin.", + "continue": "Devam Et" + } + }, + "success": { + "title": "Bağışınız İçin Teşekkür Ederiz!", + "description": "Cömertliğiniz gerçek bir fark yaratıyor. Misyonumuzu desteklediğiniz için teşekkür ederiz." + }, + "verification": { + "title": "E-posta adresiniz nedir?", + "description": "Makbuzları ve bağışçı güncellemelerini size göndermek için e-posta gereklidir.", + "sent-code": "E-posta adresinize bir kod gönderdik. Bağışı tamamlamak için lütfen aşağıya girin.", + "verify": "Doğrula", + "send-code": "Kod Gönder" + }, + "already-donor": { + "title": "Zaten bağışçı mısınız?", + "manage": "Bağışınızı yönetin" + } +} \ No newline at end of file diff --git a/locales/tr/meta.json b/locales/tr/meta.json index c57c267f..f7fb1b62 100644 --- a/locales/tr/meta.json +++ b/locales/tr/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "Ekip", "description": "Usul ekibiyle tanışın" + }, + "donate-page": { + "title": "Bağış Yap", + "description": "Usul'e Bağış Yap" } } \ No newline at end of file diff --git a/locales/ur/common.json b/locales/ur/common.json index 23b3f403..c46010f5 100644 --- a/locales/ur/common.json +++ b/locales/ur/common.json @@ -93,6 +93,10 @@ }, "contribute": { "title": "تعاون کریں", + "donate": { + "title": "عطیہ دیں", + "description": "Usul کی حمایت کریں" + }, "add-text": { "title": "متن شامل کریں", "description": "کتاب یا پی ڈی ایف اپ لوڈ کریں" diff --git a/locales/ur/donate.json b/locales/ur/donate.json new file mode 100644 index 00000000..6b3fc034 --- /dev/null +++ b/locales/ur/donate.json @@ -0,0 +1,98 @@ +{ + "hero": { + "title": "اسلامی علم کا مستقبل، مصنوعی ذہانت کے ذریعہ تقویت یافتہ", + "subtitle": "اسلامی علم کو مزید قابل رسائی بنانے اور اس کی بازیافت کو بہت آسان بنانے میں ہماری مدد کریں۔", + "description": "🎉 الحمد للہ، آپ کی حمایت سے، اصول ہزاروں علم کے طلباء تک پہنچ چکا ہے، جو روایت اور جدیدیت کے درمیان پل بناتا ہے۔", + "donate-widget": { + "month-raised": "اکٹھا", + "goal": "ہدف ${goal, number} ماہانہ", + "active-monthly-donors": "{donors, number} فعال ماہانہ عطیہ دہندگان", + "become-a-donor": "عطیہ دہندہ بنیں" + } + }, + "achievements": { + "title": "کارنامے", + "one": { + "title": "اسلامی متون تک بے مثال رسائی۔", + "features": { + "0": "10,000 سے زیادہ کلاسیکی اور جدید اسلامی متون مفت میں دستیاب ہیں۔", + "1": "محققین اور سیکھنے والوں کو آسانی سے معلومات کھولنے میں مدد کے لیےاعلی سطح کے تلاش کے آلات۔" + } + }, + "two": { + "title": "علماء اور سیکھنے والوں کو بااختیار بنانا", + "features": { + "0": "جدید AI کے اوزار جو صارفین کو کلاسیکی متون سے تیزی سے معلومات تلاش کرنے اور نکالنے کی اجازت دیتے ہیں۔", + "1": "PDF منسلکات کا متعدد کتابوں میں اپلوڈ کیا گیا ہے تاکہ صارفین ذریعہ کو تصدیق کر سکیں۔" + } + }, + "three": { + "title": "خوبصورت صارف تجربہ", + "description": "ہم اپنی محنت کو خوبصورت اور پسندیدہ بنانے کے لئے کوشاں ہیں، اس کا تجربہ مطالعہ کرنے والے نونہال طالب علموں اور پیشہ ور علماء دونوں کو۔ مطالعے کے تجربے سے، تلاش کی انٹرفیس تک، موبائل تجربے تک۔" + } + }, + "roadmap": { + "title": "ہمارا روڈ میپ، اصول کا مستقبل کے لئے وژن", + "description": "آپ کے عطیات ہمیں مندرجہ ذیل اہداف حاصل کرنے میں مدد دیں گے، انشاءاللہ:", + "items": { + "0": { + "title": "متن کی لائبریری کو وسعت دیں", + "features": { + "0": "لاکھوں اضافی کتب کو ڈیجیٹلائز کریں اور پلیٹ فارم میں شامل کریں۔", + "1": "مختلف اسلامی شعبوں اور فکر کے اسکولوں کی جامع نمائندگی کو یقینی بنائیں۔" + } + }, + "1": { + "title": "تحقیق کے اوزار بہتر کریں", + "features": { + "0": "مزید ترقی یافتہ AI پر مبنی امکانات کا تعارف کریں جو صارفین کو متون یا مکمل مجموعہ میں بصیرت حاصل کرنے کی اجازت دیتے ہیں۔", + "1": "ایسی خصوصیات تیار کریں جیسے ساتھ ساتھ متن کے موازنہ کریں اور اقتباسات کے ذرائع کو تلاش کریں۔" + } + }, + "2": { + "title": "عالمی رسائی", + "features": { + "0": "اسلامی علم تک رسائی کو کھولنے کے لئے متن کو AI کی مدد سے انگریزی اور دیگر زبانوں میں ترجمہ کریں۔", + "1": "محدود کنیکٹوٹی والے علاقوں میں صارفین کے لیے ویب سائٹ کی کارکردگی کو بہتر بنائیں، یہ یقینی بنائیں کہ کوئی بھی سیکھنے والا پیچھے نہ رہے۔" + } + } + } + }, + "be-part": { + "title": "اصول کی سفر کا حصہ بنیں", + "description": " آپ کی ماہانہ حمایت جدت کے لئے تحریک دیتی ہے، ہماری نشونما میں معاون ہے، اور اس بات کو یقینی بناتی ہے کہ اصول آنے والی نسلوں کے لئے ایک اہم وسیلہ بنی رہے۔", + "why-support": { + "title": "Why support Usul?", + "features": { + "0": "آپ کے عطیہ کے اثر ڈالنے کی شفاف سہ ماہی اپ ڈیٹس", + "1": "اپنے عطیہ کو کسی بھی وقت منسوخ یا ایڈجسٹ کرنے کی لچک", + "2": "اسلامی علم کو محفوظ کرنے اور بانٹنے کی ایک جدید مشن کا حصہ بننے کا موقع" + } + }, + "choose-amount": { + "title": "اپنے عطیہ کی رقم منتخب کریں:", + "interval": { + "one-time": "ایک بار", + "monthly": "ماہانہ", + "yearly": "سالانہ" + }, + "note": "عطیات اصول فاؤنڈیشن کے ذریعہ اکٹھے کئے جاتے ہیں، جو ایک غیر منافع بخش تنظیم 501(c)(3) ہے۔ تمام عطیات امریکہ میں ٹیکس سے کٹوتی کے قابل ہیں۔ کسی بھی استفسار کے لئے، براہ کرم ہم سے donors@usul.ai پر رابطہ کریں۔", + "continue": "جاری رکھیں" + } + }, + "success": { + "title": "آپ کے عطیہ کا شکریہ!", + "description": "آپ کی سخاوت حقیقی فرق پیدا کرتی ہے۔ ہم اپنی مشن میں آپ کے تعاون کی قدر کرتے ہیں۔" + }, + "verification": { + "title": "What's your email?", + "description": "آپ کے میلنگ کی انوائسز اور عطیہ دہندگان کے اپ ڈیٹس بھیجنے کے لئے ای میل ضروری ہے۔", + "sent-code": "ہم نے آپ کے ای میل پر ایک کوڈ بھیجا ہے۔ عطیہ مکمل کرنے کے لئے براہ کرم اسے نیچے درج کریں۔", + "verify": "تصدیق کریں", + "send-code": "کوڈ بھیجیں" + }, + "already-donor": { + "title": "Already a donor?", + "manage": "اپنے عطیہ کا انتظام کریں" + } +} \ No newline at end of file diff --git a/locales/ur/meta.json b/locales/ur/meta.json index 06c6c8dc..a9e72712 100644 --- a/locales/ur/meta.json +++ b/locales/ur/meta.json @@ -16,5 +16,9 @@ "team-page": { "title": "ٹیم", "description": "اسول کی ٹیم سے ملیں" + }, + "donate-page": { + "title": "عطیہ کریں", + "description": "اُصول کو عطیہ کریں" } } \ No newline at end of file diff --git a/package.json b/package.json index 5df96ed4..03a446d2 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.1.1", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", @@ -53,6 +54,7 @@ "@tanstack/react-query": "^5.22.2", "@tanstack/react-virtual": "^3.1.2", "@types/mdx": "^2.0.13", + "@upstash/redis": "^1.34.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "cmdk": "^0.2.1", @@ -80,6 +82,7 @@ "rehype-raw": "^7.0.0", "resend": "^3.2.0", "slugify": "^1.6.6", + "stripe": "^17.5.0", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "typesense": "^1.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6606cd79..984c2688 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: '@radix-ui/react-popover': specifier: ^1.0.7 version: 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-progress': + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-scroll-area': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -116,6 +119,9 @@ importers: '@types/mdx': specifier: ^2.0.13 version: 2.0.13 + '@upstash/redis': + specifier: ^1.34.3 + version: 1.34.3 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -197,6 +203,9 @@ importers: slugify: specifier: ^1.6.6 version: 1.6.6 + stripe: + specifier: ^17.5.0 + version: 17.5.0 tailwind-merge: specifier: ^2.2.1 version: 2.2.1 @@ -1781,6 +1790,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-context-menu@2.1.5': resolution: {integrity: sha512-R5XaDj06Xul1KGb+WP8qiOh7tKJNz2durpLBXAGZjSVtctcRFCuEvy2gtMwRJGePwQQE5nV77gs4FwRi8T+r2g==} peerDependencies: @@ -1817,6 +1835,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dialog@1.0.0': resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} peerDependencies: @@ -2137,13 +2164,26 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.0.3': - resolution: {integrity: sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==} + '@radix-ui/react-primitive@2.0.1': + resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.1': + resolution: {integrity: sha512-6diOawA84f/eMxFHcWut0aE1C2kyE9dOyCTQOMRR2C/qPiXz/X0SaiA/RLbapQaXUCmy0/hLMf9meSccD1N0pA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@types/react': optional: true @@ -2251,6 +2291,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.1.1': + resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-switch@1.0.3': resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: @@ -3005,6 +3054,9 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@upstash/redis@1.34.3': + resolution: {integrity: sha512-VT25TyODGy/8ljl7GADnJoMmtmJ1F8d84UXfGonRRF8fWYJz7+2J6GzW+a6ETGtk4OyuRTt7FRSvFG5GvrfSdQ==} + '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} @@ -3643,6 +3695,9 @@ packages: crypto-browserify@3.12.0: resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-line-break@2.1.0: resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} @@ -6299,6 +6354,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stripe@17.5.0: + resolution: {integrity: sha512-kcyeAkDFjGsVl17FqnG7q/+xIjt0ZjOo9Dm+q8deAvs2Xe4iAHrhxyoP4etUVFc+/LZJANjIPVR+ZOnt9hr/Ug==} + engines: {node: '>=12.*'} + style-loader@3.3.4: resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} engines: {node: '>= 12.13.0'} @@ -8540,6 +8599,12 @@ snapshots: optionalDependencies: '@types/react': 18.2.57 + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.2.57)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.57 + '@radix-ui/react-context-menu@2.1.5(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.5 @@ -8573,6 +8638,12 @@ snapshots: optionalDependencies: '@types/react': 18.2.57 + '@radix-ui/react-context@1.1.1(@types/react@18.2.57)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.57 + '@radix-ui/react-dialog@1.0.0(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.5 @@ -8954,11 +9025,19 @@ snapshots: '@types/react': 18.2.57 '@types/react-dom': 18.2.19 - '@radix-ui/react-progress@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@radix-ui/react-primitive@2.0.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@babel/runtime': 7.24.5 - '@radix-ui/react-context': 1.0.1(@types/react@18.2.57)(react@18.2.0) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.2.57)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.57 + '@types/react-dom': 18.2.19 + + '@radix-ui/react-progress@1.1.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@18.2.57)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) optionalDependencies: @@ -9101,6 +9180,13 @@ snapshots: optionalDependencies: '@types/react': 18.2.57 + '@radix-ui/react-slot@1.1.1(@types/react@18.2.57)(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.57)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.57 + '@radix-ui/react-switch@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.5 @@ -9348,7 +9434,7 @@ snapshots: '@radix-ui/react-popover': 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@radix-ui/react-progress': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-progress': 1.1.1(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-radio-group': 1.1.3(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-scroll-area': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -10141,6 +10227,10 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@upstash/redis@1.34.3': + dependencies: + crypto-js: 4.2.0 + '@vitest/expect@2.0.5': dependencies: '@vitest/spy': 2.0.5 @@ -10899,6 +10989,8 @@ snapshots: randombytes: 2.1.0 randomfill: 1.0.4 + crypto-js@4.2.0: {} + css-line-break@2.1.0: dependencies: utrie: 1.0.2 @@ -11306,7 +11398,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0))(eslint@8.56.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) eslint-plugin-react: 7.33.2(eslint@8.56.0) @@ -11325,7 +11417,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0))(eslint@8.56.0): dependencies: debug: 4.3.4 enhanced-resolve: 5.15.0 @@ -11349,7 +11441,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0))(eslint@8.56.0) transitivePeerDependencies: - supports-color @@ -14089,6 +14181,11 @@ snapshots: strip-json-comments@3.1.1: {} + stripe@17.5.0: + dependencies: + '@types/node': 18.19.18 + qs: 6.13.0 + style-loader@3.3.4(webpack@5.90.3(esbuild@0.18.20)): dependencies: webpack: 5.90.3(esbuild@0.18.20) diff --git a/public/images/features-screenshot.png b/public/images/features-screenshot.png new file mode 100644 index 00000000..a0685dd4 Binary files /dev/null and b/public/images/features-screenshot.png differ diff --git a/src/app/[locale]/api/stripe/webhooks/route.ts b/src/app/[locale]/api/stripe/webhooks/route.ts new file mode 100644 index 00000000..d57cca42 --- /dev/null +++ b/src/app/[locale]/api/stripe/webhooks/route.ts @@ -0,0 +1,46 @@ +import { error } from "next/dist/build/output/log"; +import type { Stripe } from "stripe"; +import stripe from "@/server/stripe"; +import { env } from "@/env"; +import handleStripeEvent from "@/server/stripe/events"; +import { NextRequest } from "next/server"; + +/** + * Syncs Stripe's payment state to our database via their webhooks + */ +export async function POST(request: NextRequest) { + const sig = request.headers.get("stripe-signature") as string; + if (!sig) return; + + const body = await request.text(); + + let event: Stripe.Event; + + // Verify that this is a genuine Stripe request and not just somebody pinging us + try { + event = stripe.webhooks.constructEvent( + body, + sig, + env.STRIPE_WEBHOOK_SECRET, + ); + } catch (err: any) { + error(err); + return new Response(`Webhook Error: ${err.message}`, { status: 400 }); + } + + try { + const result = await handleStripeEvent(event); + if (result === false) { + return new Response("Event not handled", { + status: 500, + }); + } + } catch (error) { + console.log(error); + return new Response("Webhook handler failed", { + status: 500, + }); + } + + return Response.json({ received: true }, { status: 200 }); +} diff --git a/src/app/[locale]/donate/bento-card.tsx b/src/app/[locale]/donate/bento-card.tsx new file mode 100644 index 00000000..8d5dc136 --- /dev/null +++ b/src/app/[locale]/donate/bento-card.tsx @@ -0,0 +1,13 @@ +import { cn } from "@/lib/utils"; + +const BentoCard = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); + +export default BentoCard; diff --git a/src/app/[locale]/donate/donate-form.client.tsx b/src/app/[locale]/donate/donate-form.client.tsx new file mode 100644 index 00000000..9fe688b5 --- /dev/null +++ b/src/app/[locale]/donate/donate-form.client.tsx @@ -0,0 +1,238 @@ +"use client"; + +import BentoCard from "./bento-card"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { useFormatter, useTranslations } from "next-intl"; + +import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; +import { + createCheckoutSession, + generateAndSendDonationCode, +} from "@/server/services/donations"; +import { toast } from "@/components/ui/use-toast"; +import { cn } from "@/lib/utils"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +type Frequency = "one-time" | "monthly" | "yearly"; +const presetAmounts = [25, 50, 100, 500]; + +function DonateForm() { + const t = useTranslations("donate"); + const formatter = useFormatter(); + const [frequency, setFrequency] = useState("one-time"); + const [amount, setAmount] = useState(100); + const [showEmailVerification, setShowEmailVerification] = useState(false); + + const { mutateAsync: createCheckoutUrl, isPending } = useMutation({ + mutationFn: (data: { + email: string; + code: string; + amount: number; + frequency: Frequency; + }) => { + return createCheckoutSession( + data.email, + data.code, + data.amount, + data.frequency, + ); + }, + onError: (error) => { + console.error(error); + toast({ + title: "Failed to verify!", + variant: "destructive", + }); + }, + }); + + const formatCurrency = (amount: number) => + formatter.number(amount, { + style: "currency", + currency: "USD", + maximumFractionDigits: 0, + }); + + const handleSubmit = () => { + setShowEmailVerification(true); + }; + + const handleVerify = async (email: string, code: string) => { + const url = await createCheckoutUrl({ email, code, amount, frequency }); + + if (url) { + window.location.href = url; + } + }; + + return ( + + ); +} + +const EmailVerification = ({ + open, + onClose, + onVerify, +}: { + open: boolean; + onClose: () => void; + onVerify: (email: string, code: string) => void; +}) => { + const [step, setStep] = useState<"email" | "code">("email"); + const t = useTranslations("donate.verification"); + + const { mutateAsync: sendCode, isPending: isSendingCode } = useMutation({ + mutationFn: (email: string) => generateAndSendDonationCode(email), + onError: (error) => { + console.log(error); + toast({ + title: "Failed to send code!", + variant: "destructive", + }); + }, + }); + + const [email, setEmail] = useState(""); + const [code, setCode] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (step === "email") { + if (!email) return; + + await sendCode(email); + setStep("code"); + + return; + } + + onVerify(email, code.trim()); + }; + + return ( + + + + {t("title")} + + {t("description")} + +
+ {step === "email" ? ( + setEmail(e.target.value)} + /> + ) : ( + <> +

{t("sent-code")}

+ + setCode(e.target.value)} + /> + + )} + + +
+
+
+ ); +}; + +export default DonateForm; diff --git a/src/app/[locale]/donate/features-list.tsx b/src/app/[locale]/donate/features-list.tsx new file mode 100644 index 00000000..ea8133cf --- /dev/null +++ b/src/app/[locale]/donate/features-list.tsx @@ -0,0 +1,38 @@ +import { cn } from "@/lib/utils"; +import { CheckCircleIcon } from "@heroicons/react/24/solid"; + +const FeaturesList = ({ + className, + features, + style = "check", +}: { + features: React.ReactNode[]; + style?: "list" | "check"; + className?: string; +}) => ( +
    + {features.map((feature, idx) => ( +
  • + {style === "check" ? ( + <> + +

    {feature}

    + + ) : ( + feature + )} +
  • + ))} +
+); + +export default FeaturesList; diff --git a/src/app/[locale]/donate/layout.tsx b/src/app/[locale]/donate/layout.tsx new file mode 100644 index 00000000..438333d5 --- /dev/null +++ b/src/app/[locale]/donate/layout.tsx @@ -0,0 +1,18 @@ +import Footer from "@/app/_components/footer"; +import Navbar from "@/app/_components/navbar"; + +export default function DonateLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ + + {children} + +
+
+ ); +} diff --git a/src/app/[locale]/donate/page.tsx b/src/app/[locale]/donate/page.tsx new file mode 100644 index 00000000..cf89d1d7 --- /dev/null +++ b/src/app/[locale]/donate/page.tsx @@ -0,0 +1,204 @@ +import Container from "@/components/ui/container"; +import { navigation } from "@/lib/urls"; +import { getTranslations, unstable_setRequestLocale } from "next-intl/server"; +import { getMetadata } from "@/lib/seo"; +import { type AppLocale } from "~/i18n.config"; +import { cn } from "@/lib/utils"; + +import Image from "next/image"; +import { MoonStarIcon } from "lucide-react"; +import DonateForm from "./donate-form.client"; +import BentoCard from "./bento-card"; +import FeaturesList from "./features-list"; +import dynamicImport from "next/dynamic"; +import { DonationStatsCard } from "./stats-card.client"; + +const SuccessModal = dynamicImport(() => import("./success-modal.client"), { + ssr: false, +}); + +export const generateMetadata = async ({ + params: { locale }, +}: { + params: { locale: AppLocale }; +}) => { + const t = await getTranslations({ locale, namespace: "meta" }); + + return getMetadata({ + pagePath: navigation.donate(), + locale, + title: t("donate-page.title"), + description: t("donate-page.description"), + }); +}; + +export default async function HomePage({ + params: { locale }, +}: { + params: { locale: AppLocale }; +}) { + unstable_setRequestLocale(locale); + + const t = await getTranslations({ locale, namespace: "donate" }); + + const getMarkup = ( + key: Parameters[0], + options?: Parameters[1], + ) => + t.rich(key, { + strong: (chunks) => {chunks}, + ...(options ?? {}), + }); + + const roadmap = [ + { + title: t("roadmap.items.0.title"), + features: [ + t("roadmap.items.0.features.0"), + t("roadmap.items.0.features.1"), + ], + }, + { + title: t("roadmap.items.1.title"), + features: [ + t("roadmap.items.1.features.0"), + t("roadmap.items.1.features.1"), + ], + }, + { + title: t("roadmap.items.2.title"), + features: [ + t("roadmap.items.2.features.0"), + t("roadmap.items.2.features.1"), + ], + }, + ]; + + return ( + <> + + +
+
+ + +
+

+ {t("hero.title")} +

+ +

{t("hero.subtitle")}

+ +

{t("hero.description")}

+
+ +
+ +
+
+
+ + +
+

{t("achievements.title")}

+ +
+ +

+ {t("achievements.one.title")} +

+ + +
+ + +

+ {t("achievements.two.title")} +

+ + +
+ + +
+ + +

+ {t("achievements.three.title")} +

+ +

+ {getMarkup("achievements.three.description")} +

+
+ +
+ + Features screenshot + +
+
+ +
+

{getMarkup("roadmap.title")}

+

{t("roadmap.description")}

+
+ {roadmap.map((item, idx) => ( + +

{item.title}

+ +
+ ))} +
+
+ +
+

{t("be-part.title")}

+

{t("be-part.description")}

+
+ +

+ {t("be-part.why-support.title")} +

+ + +
+ + +
+
+ + + ); +} diff --git a/src/app/[locale]/donate/stats-card.client.tsx b/src/app/[locale]/donate/stats-card.client.tsx new file mode 100644 index 00000000..ca8b71c6 --- /dev/null +++ b/src/app/[locale]/donate/stats-card.client.tsx @@ -0,0 +1,130 @@ +"use client"; + +import { useTranslations, useFormatter } from "next-intl"; +import { Button } from "@/components/ui/button"; +import { Progress } from "@/components/ui/progress"; +import { useQuery } from "@tanstack/react-query"; +import { getMonthlyStats } from "@/server/services/donations"; +import { Skeleton } from "@/components/ui/skeleton"; +import { useSearchParams } from "next/navigation"; +import { useEffect } from "react"; +import { HandCoinsIcon } from "lucide-react"; +import { env } from "@/env"; + +const GOAL = 75_000; + +export function DonationStatsCard() { + const t = useTranslations("donate"); + const formatter = useFormatter(); + const isSuccess = useSearchParams().get("status") === "success"; + + const { + data: monthlyStats, + isLoading, + refetch, + } = useQuery({ + queryKey: ["monthly-stats"], + queryFn: () => getMonthlyStats(), + }); + + useEffect(() => { + if (isSuccess) { + let count = 0; + // refetch 3 times after 3 seconds + const interval = setInterval(() => { + refetch(); + count++; + if (count === 3) { + clearInterval(interval); + } + }, 3000); + + return () => clearInterval(interval); + } + }, [isSuccess]); + + const getMarkup = ( + key: Parameters[0], + options?: Parameters[1], + ) => + t.rich(key, { + strong: (chunks) => {chunks}, + ...(options ?? {}), + }); + + const currentMonthDonations = monthlyStats?.total ?? 0; + const currentMonthDonors = monthlyStats?.donors ?? 0; + const formattedDonations = monthlyStats + ? formatter.number(currentMonthDonations, { + maximumFractionDigits: 2, + minimumFractionDigits: 2, + style: "currency", + currency: "USD", + currencyDisplay: "narrowSymbol", + }) + : 0; + + return ( +
+
+ {isLoading ? ( + + ) : ( +

+ {formattedDonations} +

+ )} + +
+

+ {t("hero.donate-widget.goal", { goal: GOAL })} +

+
+ +
+ +
+
+ +
+ +
+ {isLoading ? ( + + ) : ( +

+ {getMarkup("hero.donate-widget.active-monthly-donors", { + donors: currentMonthDonors, + })} +

+ )} + + + +

+ {t("already-donor.title")}{" "} + + {t("already-donor.manage")} + +

+
+
+ ); +} diff --git a/src/app/[locale]/donate/success-modal.client.tsx b/src/app/[locale]/donate/success-modal.client.tsx new file mode 100644 index 00000000..d472a736 --- /dev/null +++ b/src/app/[locale]/donate/success-modal.client.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { HeartIcon } from "lucide-react"; +import { useSearchParams } from "next/navigation"; +import { useTranslations } from "next-intl"; + +export default function SuccessModal() { + const success = useSearchParams().get("status") === "success"; + const t = useTranslations("donate.success"); + + return ( + + + + +
+ +
+ {t("title")} +
+
+ +
+

{t("description")}

+
+
+
+ ); +} diff --git a/src/app/_components/navbar/links.ts b/src/app/_components/navbar/links.ts index 0e79247c..b6b8ce9a 100644 --- a/src/app/_components/navbar/links.ts +++ b/src/app/_components/navbar/links.ts @@ -10,6 +10,7 @@ import { BookOpenIcon, CirclePlusIcon, FileTextIcon, + HandCoinsIcon, HeartHandshakeIcon, ListIcon, MailIcon, @@ -84,6 +85,12 @@ export const aboutItems: NavItem[] = [ ]; export const contributeItems: NavItem[] = [ + { + title: "navigation.contribute.donate.title", + description: "navigation.contribute.donate.description", + href: navigation.donate(), + icon: HandCoinsIcon, + }, { title: "navigation.contribute.add-text.title", description: "navigation.contribute.add-text.description", diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 5b331b10..4dbf343c 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -57,6 +57,8 @@ export default async function sitemap( generateEntryFromUrl("/about"), // team page generateEntryFromUrl("/team"), + // donate page + generateEntryFromUrl("/donate"), // root entities ...rootEntityPages.map((entityUrl) => generateEntryFromUrl(entityUrl)), ...authors.map((author) => diff --git a/src/components/newsletter-form.tsx b/src/components/newsletter-form.tsx index 84b27713..7734490e 100644 --- a/src/components/newsletter-form.tsx +++ b/src/components/newsletter-form.tsx @@ -9,6 +9,8 @@ import Spinner from "./ui/spinner"; import { AlertTitle } from "./ui/alert"; import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline"; import { useTranslations } from "next-intl"; +import { Link } from "@/navigation"; +import { navigation } from "@/lib/urls"; export default function NewsletterForm() { const t = useTranslations("common"); @@ -43,22 +45,35 @@ export default function NewsletterForm() { return (
- + - setEmail(e.target.value)} - /> +
+ -
- + setEmail(e.target.value)} + className="w-full flex-1 rounded-none border border-none border-border bg-background" + /> + +
+ +
); diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx new file mode 100644 index 00000000..529a0870 --- /dev/null +++ b/src/components/ui/progress.tsx @@ -0,0 +1,28 @@ +"use client"; + +import * as React from "react"; +import * as ProgressPrimitive from "@radix-ui/react-progress"; + +import { cn } from "@/lib/utils"; + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)); +Progress.displayName = ProgressPrimitive.Root.displayName; + +export { Progress }; diff --git a/src/components/ui/skeleton/index.tsx b/src/components/ui/skeleton/index.tsx index d7e45f7b..279daae1 100644 --- a/src/components/ui/skeleton/index.tsx +++ b/src/components/ui/skeleton/index.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Skeleton({ className, @@ -6,10 +6,10 @@ function Skeleton({ }: React.HTMLAttributes) { return (
- ) + ); } -export { Skeleton } +export { Skeleton }; diff --git a/src/env.js b/src/env.js index 94d383e3..683b2c29 100644 --- a/src/env.js +++ b/src/env.js @@ -10,9 +10,17 @@ export const env = createEnv({ TYPESENSE_API_KEY: z.string().min(1), DATABASE_URL: z.string().url().min(1), RESEND_AUDIENCE_ID: z.string().min(1), + // RESEND_DONORS_AUDIENCE_ID: z.string().min(1), RESEND_API_KEY: z.string().min(1), + STRIPE_API_KEY: z.string().min(1), + STRIPE_WEBHOOK_SECRET: z.string().min(1), + UPSTASH_REDIS_REST_URL: z.string().url().min(1), + UPSTASH_REDIS_REST_TOKEN: z.string().min(1), + VERIFY_RIGHT_API_KEY: z.string().min(1), + }, + client: { + NEXT_PUBLIC_STRIPE_PORTAL_URL: z.string().url().min(1), }, - client: {}, shared: { NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT: z.string().url().min(1), NEXT_PUBLIC_PDF_EXPRESS_LICENSE_KEY: z.string().min(1), @@ -31,6 +39,7 @@ export const env = createEnv({ TYPESENSE_API_KEY: process.env.TYPESENSE_API_KEY, DATABASE_URL: process.env.DATABASE_URL, RESEND_AUDIENCE_ID: process.env.RESEND_AUDIENCE_ID, + // RESEND_DONORS_AUDIENCE_ID: process.env.RESEND_DONORS_AUDIENCE_ID, RESEND_API_KEY: process.env.RESEND_API_KEY, NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT: process.env.NEXT_PUBLIC_AXIOM_INGEST_ENDPOINT, @@ -45,6 +54,12 @@ export const env = createEnv({ NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID: process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID, VERCEL_ENV: process.env.VERCEL_ENV, + STRIPE_API_KEY: process.env.STRIPE_API_KEY, + STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET, + UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL, + UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN, + VERIFY_RIGHT_API_KEY: process.env.VERIFY_RIGHT_API_KEY, + NEXT_PUBLIC_STRIPE_PORTAL_URL: process.env.NEXT_PUBLIC_STRIPE_PORTAL_URL, }, skipValidation: !!process.env.SKIP_ENV_VALIDATION, emptyStringAsUndefined: true, diff --git a/src/lib/date.ts b/src/lib/date.ts index ab363ff9..e4b9329e 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -136,3 +136,10 @@ export const formatDeathYear = ( return `d. ${isUnknown ? "Unknown" : year}${format}`; }; + +export const secondsToMsDate = (seconds: number) => new Date(seconds * 1000); + +export const getCurrentMonth = () => { + const now = new Date(); + return `${now.getFullYear()}-${now.getMonth() + 1}`; +}; diff --git a/src/lib/upstash.ts b/src/lib/upstash.ts new file mode 100644 index 00000000..1f32806f --- /dev/null +++ b/src/lib/upstash.ts @@ -0,0 +1,75 @@ +import { revalidateTag } from "next/cache"; +import { env } from "@/env"; +import { Redis } from "@upstash/redis"; +import { getCurrentMonth } from "./date"; + +export const redis = new Redis({ + url: env.UPSTASH_REDIS_REST_URL, + token: env.UPSTASH_REDIS_REST_TOKEN, +}); + +export type DonationRecord = { + email: string | null; + amount: number; // in dollars + interval: "one-time" | "monthly" | "yearly" | null; + timestamp: number; // in milliseconds +}; + +export const makeCurrentMonthTotalKey = () => + `donations:totals:${getCurrentMonth()}`; +export const makeCurrentMonthDonorsKey = () => + `donations:donors:${getCurrentMonth()}`; + +export async function storeDonation( + obj: { sessionId: string } | { invoiceId: string }, + amountInCents: number | null | undefined, + email: string | null | undefined, + interval: "one-time" | "monthly" | "yearly" | null, +) { + // on staging environments, don't store anything to redis and just return + if (env.VERCEL_ENV !== "production") { + return true; + } + + if (!amountInCents) return false; + + // store in dollars + const amount = amountInCents / 100; + + const donationRecord: DonationRecord = { + ...obj, + email: email ?? null, + amount, + interval, + timestamp: Date.now(), + }; + + // check if donations array exists + const donations = await redis.exists("donations"); + if (!donations) { + await redis.json.set("donations", "$", [donationRecord]); + } else { + await redis.json.arrappend("donations", "$", donationRecord); + } + + // Increment the current month's total + const monthKey = makeCurrentMonthTotalKey(); + await redis.incrbyfloat(monthKey, amount); + + // Increment the current month's donors count + const donorsKey = makeCurrentMonthDonorsKey(); + await redis.incr(donorsKey); + + invalidateMonthlyStats(); + + return true; +} + +// export async function getDonations(): Promise { +// const data = await redis.json.get("donations", "items"); +// return data ?? []; +// } + +export const invalidateMonthlyStats = () => { + revalidateTag("monthly-stats"); +}; diff --git a/src/lib/urls.ts b/src/lib/urls.ts index f7edaec7..ac91ec3b 100644 --- a/src/lib/urls.ts +++ b/src/lib/urls.ts @@ -32,6 +32,7 @@ export const navigation = { }, about: () => "/about", team: () => "/team", + donate: () => "/donate", }; export const booksSorts = [ diff --git a/src/server/services/donations.ts b/src/server/services/donations.ts new file mode 100644 index 00000000..be933854 --- /dev/null +++ b/src/server/services/donations.ts @@ -0,0 +1,244 @@ +"use server"; + +import type Stripe from "stripe"; +import stripe from "../stripe"; +import { unstable_cache } from "next/cache"; +import { getCurrentMonth } from "@/lib/date"; +import { + makeCurrentMonthDonorsKey, + makeCurrentMonthTotalKey, + redis, +} from "@/lib/upstash"; +import { env } from "@/env"; +import { z } from "zod"; +import { nanoid } from "nanoid"; +import { resend } from "@/lib/resend"; + +const presetDonations = [ + { + amount: 25, + monthlyPriceId: { + production: "price_1QiegZHQeTWInv1ysgB6Nr2M", + test: "price_1QiexFHQeTWInv1ygbhnq8s1", + }, + yearlyPriceId: { + production: "price_1QieiBHQeTWInv1yDQNie2M6", + test: "price_1QieyKHQeTWInv1yjhgCqi5W", + }, + }, + { + amount: 50, + monthlyPriceId: { + production: "price_1QiejVHQeTWInv1yItufSfDO", + test: "price_1QieySHQeTWInv1yXM9zjRmr", + }, + yearlyPriceId: { + production: "price_1QiejeHQeTWInv1yF469JTxh", + test: "price_1QieycHQeTWInv1yNoQMcgMa", + }, + }, + { + amount: 100, + monthlyPriceId: { + production: "price_1QiejtHQeTWInv1ySDzqqaeX", + test: "price_1QieyiHQeTWInv1yikQ2kNuI", + }, + yearlyPriceId: { + production: "price_1Qiek3HQeTWInv1yKqMEz3Et", + test: "price_1QieyvHQeTWInv1ywjZPbQFJ", + }, + }, + { + amount: 500, + monthlyPriceId: { + production: "price_1QiekDHQeTWInv1yleqkvfZK", + test: "price_1Qiez6HQeTWInv1yNawlKMKh", + }, + yearlyPriceId: { + production: "price_1QiekMHQeTWInv1yJD71pdSk", + test: "price_1QiezGHQeTWInv1yTfQk8icK", + }, + }, +]; + +const productEnv = env.VERCEL_ENV === "production" ? "production" : "test"; + +export const generateAndSendDonationCode = async (email: string) => { + const response = await fetch( + `https://verifyright.co/verify/${email}?token=${env.VERIFY_RIGHT_API_KEY}`, + ); + const data = (await response.json()) as { + status: boolean; + }; + + if (!data.status) { + throw new Error("Failed to verify email"); + } + + const code = nanoid(12); + + await resend.emails.send({ + from: "usul-donations@digitalseem.org", + to: email, + subject: "Usul - Verify your email", + html: ` +

Your verification code is ${code}

+
+

This code is valid for 10 minutes.

+

+ +

If you didn't request this, you can safely ignore this email.

`, + }); + + await redis.set(`code:${email}`, code, { ex: 60 * 10 }); // 10 minutes + + return code; +}; + +const schema = z.object({ + email: z.string().email().toLowerCase().trim(), + code: z.string(), + amountInUsd: z.number().min(1), + frequency: z.enum(["monthly", "yearly", "one-time"]), +}); + +export const createCheckoutSession = async ( + email: string, + code: string, + amountInUsd: number, + frequency: "monthly" | "yearly" | "one-time", +) => { + const data = schema.safeParse({ email, code, amountInUsd, frequency }); + if (!data.success) { + throw new Error("Invalid input"); + } + + const validatedData = data.data; + + // check code + const codeInRedis = await redis.get(`code:${validatedData.email}`); + if (!codeInRedis || codeInRedis !== validatedData.code) { + throw new Error("Invalid code"); + } + + // delete code + await redis.del(`code:${validatedData.email}`); + + let customerId: string; + const customerInRedis = await redis.get( + `customer:${validatedData.email}`, + ); + if (customerInRedis) { + customerId = customerInRedis; + } else { + const customer = await stripe.customers.create({ + email: validatedData.email, + }); + customerId = customer.id; + await redis.set(`customer:${validatedData.email}`, customerId); + } + + // Convert amount (in USD) to cents + const amountInCents = validatedData.amountInUsd * 100; + + const baseUrl = process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : "http://localhost:3000"; + + let session: Stripe.Checkout.Session; + + try { + if (validatedData.frequency === "one-time") { + // One-time payment + session = await stripe.checkout.sessions.create({ + mode: "payment", + line_items: [ + { + price_data: { + currency: "usd", + product_data: { + name: "Donation", + }, + unit_amount: amountInCents, + }, + quantity: 1, + }, + ], + success_url: `${baseUrl}/donate?status=success`, + cancel_url: `${baseUrl}/donate`, + customer: customerId, + }); + } else { + let priceId: string; + + const presetDonation = presetDonations.find( + (d) => d.amount === validatedData.amountInUsd, + ); + + if (presetDonation) { + priceId = + frequency === "monthly" + ? presetDonation.monthlyPriceId[productEnv] + : presetDonation.yearlyPriceId[productEnv]; + } else { + // Recurring donation (monthly or yearly) + // You may want to create a Price in Stripe based on `amount` and `frequency`. + // For example, create a price on the fly: + const price = await stripe.prices.create({ + currency: "usd", + unit_amount: amountInCents, + recurring: { + interval: validatedData.frequency === "monthly" ? "month" : "year", + }, + product_data: { + name: "Recurring Donation", + }, + }); + priceId = price.id; + } + + session = await stripe.checkout.sessions.create({ + mode: "subscription", + line_items: [ + { + price: priceId, + quantity: 1, + }, + ], + success_url: `${baseUrl}/donate?status=success`, + cancel_url: `${baseUrl}/donate`, + customer: customerId, + }); + } + } catch (error) { + console.error("Error creating checkout session", error); + throw error; + } + + if (!session.url) { + throw new Error("No checkout URL found"); + } + + return session.url; +}; + +export const getMonthlyStats = async () => { + const currentMonth = getCurrentMonth(); + + return unstable_cache( + async () => { + const monthKey = makeCurrentMonthTotalKey(); + const total = (await redis.get(monthKey)) ?? 0; + + const donorsKey = makeCurrentMonthDonorsKey(); + const donors = (await redis.get(donorsKey)) ?? 0; + + return { + total, + donors, + }; + }, + [`monthly-stats-${currentMonth}`], + { revalidate: 60 * 60 * 24, tags: ["monthly-stats"] }, + )(); +}; diff --git a/src/server/stripe/events/checkout-completed.event.ts b/src/server/stripe/events/checkout-completed.event.ts new file mode 100644 index 00000000..65c96d4e --- /dev/null +++ b/src/server/stripe/events/checkout-completed.event.ts @@ -0,0 +1,31 @@ +import type Stripe from "stripe"; +// import { resend } from "@/lib/resend"; +import { env } from "@/env"; +import { storeDonation } from "@/lib/upstash"; + +export const handleCheckoutCompleted = async (event: Stripe.Event) => { + const session = event.data.object as Stripe.Checkout.Session; + + const email = session.customer_details?.email ?? session.customer_email; + + // Only store one-time donations in this webhook event. Recurring donations are handled in the invoice.paid webhook event. + if (session.mode === "payment" && session.payment_status === "paid") { + const amount = session.amount_total; // in cents + await storeDonation({ sessionId: session.id }, amount, email, "one-time"); + } + + // Add donor to Resend Audience + if (email) { + if (env.NODE_ENV !== "production") { + console.log(`[DEV] Received donation from: ${email}`); + } else { + // TODO: Uncomment this once we have a donors audience + // await resend.contacts.create({ + // email: email, + // audienceId: env.RESEND_DONORS_AUDIENCE_ID, + // }); + } + } + + // TODO: Send email to donor +}; diff --git a/src/server/stripe/events/index.ts b/src/server/stripe/events/index.ts new file mode 100644 index 00000000..946a8061 --- /dev/null +++ b/src/server/stripe/events/index.ts @@ -0,0 +1,14 @@ +import Stripe from "stripe"; +import { handleCheckoutCompleted } from "./checkout-completed.event"; +import { handleInvoicePaid } from "./invoice.paid.event"; + +const events: Record Promise> = { + "checkout.session.completed": handleCheckoutCompleted, + "invoice.paid": handleInvoicePaid, +}; + +export default async function handleStripeEvent(event: Stripe.Event) { + const handler = events[event.type]; + if (!handler) return false; + return handler(event); +} diff --git a/src/server/stripe/events/invoice.paid.event.ts b/src/server/stripe/events/invoice.paid.event.ts new file mode 100644 index 00000000..3e845977 --- /dev/null +++ b/src/server/stripe/events/invoice.paid.event.ts @@ -0,0 +1,22 @@ +import { storeDonation } from "@/lib/upstash"; +import type Stripe from "stripe"; + +export const handleInvoicePaid = async (event: Stripe.Event) => { + const invoice = event.data.object as Stripe.Invoice; + + const amount = invoice.amount_paid; // in cents + const email = invoice.customer_email; // or retrieve from invoice.customer + let interval: "monthly" | "yearly" | null = null; + + // If it’s a subscription invoice, check the subscription’s plan + if (invoice.lines.data.length > 0) { + const line = invoice.lines.data[0]; + const recurring = line?.price?.recurring; + + if (recurring?.interval === "month") interval = "monthly"; + else if (recurring?.interval === "year") interval = "yearly"; + } + + // Store in Redis + await storeDonation({ invoiceId: invoice.id }, amount, email, interval); +}; diff --git a/src/server/stripe/index.ts b/src/server/stripe/index.ts new file mode 100644 index 00000000..1b8687a6 --- /dev/null +++ b/src/server/stripe/index.ts @@ -0,0 +1,12 @@ +import { env } from "@/env"; +import Stripe from "stripe"; + +const stripe = new Stripe(env.STRIPE_API_KEY, { + // @ts-ignore The Stripe docs state that null denotes the Stripe account's default version and to use ts-ignore + apiVersion: null, + appInfo: { + name: "Usul", + }, +}); + +export default stripe; diff --git a/src/styles/globals.css b/src/styles/globals.css index 70c73f1b..919bcb6f 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -137,7 +137,7 @@ } .reader-page { - @apply text-2xl leading-10; + @apply text-2xl leading-[3rem]; a { @apply text-primary underline; diff --git a/src/types/types.d.ts b/src/types/types.d.ts index f3a7ed68..2e4ad199 100644 --- a/src/types/types.d.ts +++ b/src/types/types.d.ts @@ -8,6 +8,7 @@ declare interface IntlMessages { meta: typeof import("../../locales/en/meta.json"); about: typeof import("../../locales/en/about.json"); team: typeof import("../../locales/en/team.json"); + donate: typeof import("../../locales/en/donate.json"); } declare module "*.svg" { diff --git a/tailwind.config.ts b/tailwind.config.ts index b492222e..32dbe00d 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -92,6 +92,9 @@ const config = { "accordion-up": "accordion-up 0.2s ease-out", gradient: "gradient 8s linear infinite", }, + screens: { + xs: "480px", + }, }, }, plugins: [