diff --git a/apps/admin/src/app/dashboard/site-config/page.tsx b/apps/admin/src/app/dashboard/site-config/page.tsx index 3214b19..0714095 100644 --- a/apps/admin/src/app/dashboard/site-config/page.tsx +++ b/apps/admin/src/app/dashboard/site-config/page.tsx @@ -94,12 +94,12 @@ export default function AdminSiteConfigPage() { }, fr: { title: 'Configuration du site', - description: 'Modifiez ici la homepage principale de la marketplace, ou ouvrez une entreprise pour gérer sa homepage publique et son menu.', - homepageTitle: 'Homepage du site principal', - homepageDescription: 'Cette section contrôle la homepage marketplace affichée sur le site principal RentalDriveGo.', - saveHomepage: 'Enregistrer la homepage', + description: 'Modifiez ici la page d’accueil principale de la marketplace, ou ouvrez une entreprise pour gérer sa page publique et son menu.', + homepageTitle: 'Page d’accueil du site principal', + homepageDescription: 'Cette section contrôle la page d’accueil marketplace affichée sur le site principal de RentalDriveGo.', + saveHomepage: 'Enregistrer la page d’accueil', savingHomepage: 'Enregistrement…', - homepageSaved: 'Homepage marketplace enregistrée.', + homepageSaved: 'Page d’accueil marketplace enregistrée.', search: 'Rechercher par entreprise ou slug…', loading: 'Chargement…', empty: 'Aucune entreprise trouvée', @@ -111,7 +111,7 @@ export default function AdminSiteConfigPage() { manageCompany: 'Ouvrir l’entreprise', companiesTitle: 'Sites de marque des entreprises', languageLabel: 'Langue', - hero: 'Hero', + hero: 'Bloc principal', surface: 'Bloc marketplace', companySection: 'Bloc opérateur', renterSection: 'Bloc client', @@ -121,8 +121,8 @@ export default function AdminSiteConfigPage() { closingSection: 'Appel à l’action final', previewTitle: 'Aperçu en direct', previewDescription: 'Les brouillons apparaissent ici avant l’enregistrement.', - homepageSections: 'Sections de la homepage', - homepageSectionsDescription: 'Ajoutez ou retirez des blocs de la homepage marketplace principale.', + homepageSections: 'Sections de la page d’accueil', + homepageSectionsDescription: 'Ajoutez ou retirez des blocs de la page d’accueil marketplace principale.', addSection: 'Ajouter', removeSection: 'Retirer', expand: 'Ouvrir', diff --git a/apps/admin/src/components/PublicFooter.tsx b/apps/admin/src/components/PublicFooter.tsx index fc5af98..00e993e 100644 --- a/apps/admin/src/components/PublicFooter.tsx +++ b/apps/admin/src/components/PublicFooter.tsx @@ -20,7 +20,7 @@ export default function PublicFooter() { preferences: 'Admin preferences', }, fr: { - preferences: 'Preferences admin', + preferences: 'Préférences d’administration', }, ar: { preferences: 'تفضيلات الإدارة', diff --git a/apps/api/src/data/platform-content.json b/apps/api/src/data/platform-content.json index c3933fe..54b1a04 100644 --- a/apps/api/src/data/platform-content.json +++ b/apps/api/src/data/platform-content.json @@ -99,11 +99,11 @@ "closing" ], "heroKicker": "RentalDriveGo", - "heroTitle": "Une vitrine marketplace plus nette et plus forte.", - "heroBody": "Les entreprises de location gèrent leurs opérations en privé, les clients parcourent une marketplace commune, et chaque réservation se termine sur le paiement de marque de la société.", + "heroTitle": "Une vitrine marketplace plus claire et plus percutante.", + "heroBody": "Les entreprises de location gèrent leurs opérations en privé, les clients parcourent une marketplace commune et chaque réservation se finalise sur le parcours de paiement aux couleurs de l'entreprise.", "startTrial": "Commencer l’essai gratuit", "exploreVehicles": "Explorer les véhicules", - "surfaceLabel": "Surface marketplace", + "surfaceLabel": "Vitrine marketplace", "surfaceTitle": "Pensée pour deux audiences à la fois.", "surfaceBody": "Les opérateurs veulent du contrôle, les clients veulent de la confiance. La marketplace doit montrer les deux sans ressembler à un modèle générique.", "liveLabel": "Réseau actif", @@ -111,15 +111,15 @@ "brandedFlows": "Parcours de marque", "multiTenant": "Opérations multi-tenant", "companyKicker": "Flux opérateur", - "companyTitle": "Pilotez inventaire, offres, facturation et équipe depuis une seule couche de commande.", + "companyTitle": "Pilotez l’inventaire, les offres, la facturation et l’équipe depuis un seul centre de contrôle.", "companyBody": "Publiez une seule fois, choisissez ce qui apparaît publiquement, et gardez contrats, paiements et rapports isolés par entreprise.", "renterKicker": "Expérience client", - "renterTitle": "Laissez les clients comparer rapidement puis dirigez-les vers le bon site entreprise.", - "renterBody": "La marketplace agit comme moteur de découverte, pas comme agrégateur sans suite. Recherche ici, réservation là-bas, paiement direct.", + "renterTitle": "Laissez les clients comparer rapidement, puis redirigez-les vers le bon site d’entreprise.", + "renterBody": "La marketplace sert de moteur de découverte, pas d’agrégateur sans suite. Recherche ici, réservation là-bas, paiement direct.", "pillars": [ { "title": "Publication unifiée", - "body": "Les photos, tarifs et offres circulent depuis un seul flux admin vers la découverte marketplace et les pages de réservation de marque." + "body": "Les photos, tarifs et offres circulent depuis un seul flux d’administration vers la découverte marketplace et les pages de réservation de marque." }, { "title": "Découverte qualifiée", @@ -127,7 +127,7 @@ }, { "title": "Revenus en direct", - "body": "Les réservations basculent vers le paiement propre à l’entreprise, pour garder la relation client et l’encaissement." + "body": "Les réservations basculent vers le paiement propre à l’entreprise afin de préserver la relation client et l’encaissement." } ], "metrics": [ @@ -141,12 +141,12 @@ }, { "value": "03", - "label": "Paiements détenus par la société" + "label": "Paiements gérés par l’entreprise" } ], "featureLabel": "Ce que couvre le produit", "features": [ - "Gestion de flotte avec téléversement multi-photos", + "Gestion de flotte avec téléversement de plusieurs photos", "Offres marketplace et redirection vers la réservation", "Site public de réservation par entreprise", "CRM client, analytics et contrôle de facturation" @@ -171,8 +171,8 @@ ], "stepLabel": "Étape", "readyKicker": "Prêt à démarrer", - "readyTitle": "Une homepage marketplace doit vendre le système en quelques secondes.", - "readyBody": "Utilisez la marketplace pour capter la demande, puis faites passer les clients vers une expérience de marque qui garde prix, paiements et confiance liés à votre entreprise.", + "readyTitle": "La page d’accueil marketplace doit présenter le produit en quelques secondes.", + "readyBody": "Utilisez la marketplace pour capter la demande, puis orientez les clients vers une expérience de marque où les prix, les paiements et la confiance restent attachés à votre entreprise.", "viewPricing": "Voir les tarifs", "createWorkspace": "Créer l’espace" }, @@ -187,8 +187,8 @@ "closing" ], "heroKicker": "RentalDriveGo", - "heroTitle": "واجهة سوق أوضح وأقوى.", - "heroBody": "شركات التأجير تدير عملياتها بشكل خاص، والمستأجرون يتصفحون سوقاً مشتركاً، وكل حجز ينتهي في صفحة دفع تحمل هوية الشركة نفسها.", + "heroTitle": "واجهة سوق أكثر وضوحاً وتأثيراً.", + "heroBody": "شركات التأجير تدير عملياتها بشكل خاص، والمستأجرون يتصفحون سوقاً مشتركاً، وكل حجز ينتهي في صفحة دفع تحمل هوية الشركة ذاتها.", "startTrial": "ابدأ التجربة المجانية", "exploreVehicles": "استكشف السيارات", "surfaceLabel": "واجهة السوق", @@ -198,16 +198,16 @@ "trustedFleets": "أساطيل موثوقة", "brandedFlows": "مسارات حجز مخصصة", "multiTenant": "عمليات متعددة الشركات", - "companyKicker": "تدفق المشغل", + "companyKicker": "سير عمل المشغل", "companyTitle": "تحكم في المخزون والعروض والفوترة والفريق من طبقة تشغيل واحدة.", "companyBody": "انشر المخزون مرة واحدة، وحدد ما يظهر للعامة، واحتفظ بالعقود والمدفوعات والتقارير معزولة لكل شركة.", "renterKicker": "تجربة المستأجر", - "renterTitle": "دع المستأجر يقارن بسرعة ثم انقله إلى موقع الشركة المناسب.", - "renterBody": "السوق هنا محرك اكتشاف وليس مجمعاً بلا نهاية. البحث هنا، الحجز هناك، والدفع مباشرة للشركة.", + "renterTitle": "دع المستأجرين يقارنون بسرعة ثم وجّههم إلى موقع الشركة المناسب.", + "renterBody": "هذه المنصة محرك لاكتشاف الخيارات، وليست مُجمِّعاً بلا مسار واضح. ابحث هنا، احجز هناك، وادفع مباشرة للشركة.", "pillars": [ { "title": "نشر موحد", - "body": "صور السيارات والأسعار والعروض تنتقل من تدفق إدارة واحد إلى السوق وصفحات الحجز ذات الهوية الخاصة." + "body": "صور السيارات والأسعار والعروض تنتقل من سير إدارة واحد إلى السوق وصفحات الحجز ذات الهوية الخاصة." }, { "title": "اكتشاف مؤهل", @@ -215,7 +215,7 @@ }, { "title": "مسار إيراد مباشر", - "body": "تنتقل الحجوزات إلى صفحة الدفع الخاصة بالشركة حتى تبقى الملكية المالية وعلاقة العميل مع النشاط نفسه." + "body": "تنتقل الحجوزات إلى صفحة الدفع الخاصة بالشركة حتى تبقى علاقة العميل والتحصيل المالي بيد الشركة نفسها." } ], "metrics": [ @@ -232,7 +232,7 @@ "label": "مدفوعات مملوكة للشركة" } ], - "featureLabel": "ما الذي يغطيه المنتج", + "featureLabel": "ما الذي يقدمه المنتج", "features": [ "إدارة الأسطول مع رفع عدة صور", "عروض السوق والتحويل إلى مسار الحجز", @@ -243,8 +243,8 @@ "steps": [ { "step": "1", - "title": "أنشئ مساحة شركتك", - "body": "اختر الخطة وابدأ تجربتك لمدة 14 يوماً ثم تحقق من حساب المالك." + "title": "أنشئ مساحة عمل شركتك", + "body": "اختر الخطة وابدأ تجربتك لمدة 14 يوماً ثم فعّل حساب المالك." }, { "step": "2", @@ -260,9 +260,9 @@ "stepLabel": "الخطوة", "readyKicker": "جاهز للانطلاق", "readyTitle": "يجب أن تشرح الصفحة الرئيسية قيمة المنصة خلال ثوانٍ.", - "readyBody": "استخدم السوق لجذب الطلب، ثم انقل المستأجرين إلى تجربة تحمل هوية شركتك وتحافظ على التسعير والمدفوعات والثقة داخل نشاطك.", + "readyBody": "استخدم السوق لجذب الطلب، ثم انقل المستأجرين إلى تجربة تحمل هوية شركتك، بحيث تبقى الأسعار والمدفوعات والثقة مرتبطة بنشاطك.", "viewPricing": "عرض الأسعار", "createWorkspace": "إنشاء المساحة" } } -} \ No newline at end of file +} diff --git a/apps/api/src/lib/emailTranslations.ts b/apps/api/src/lib/emailTranslations.ts index f683baa..81aee34 100644 --- a/apps/api/src/lib/emailTranslations.ts +++ b/apps/api/src/lib/emailTranslations.ts @@ -104,7 +104,7 @@ export const resetPasswordEmail = {

مرحباً ${firstName}،

طلبت إعادة تعيين كلمة مرور حساب RentalDriveGo الخاص بك.

إعادة تعيين كلمة المرور

-

ينتهي صلاحية هذا الرابط خلال ${expireMinutes} دقيقة. إذا لم تطلب ذلك، يمكنك تجاهل هذا البريد الإلكتروني بأمان.

+

تنتهي صلاحية هذا الرابط خلال ${expireMinutes} دقيقة. إذا لم تطلب ذلك، يمكنك تجاهل هذا البريد الإلكتروني بأمان.

RentalDriveGo

`, }, lang) @@ -255,7 +255,7 @@ export const reviewRequestEmail = { }, ar: { greeting: `مرحباً ${opts.firstName}،`, - body1: `شكراً لاستئجارك مع ${opts.companyName}. نأمل أنك استمتعت بـ ${opts.vehicleLabel}.`, + body1: `شكراً لاختيار ${opts.companyName}. نأمل أنك استمتعت بـ ${opts.vehicleLabel}.`, body2: 'تساعد ملاحظاتك الشركة على التحسين وتساعد العملاء الآخرين على اتخاذ قرارات مستنيرة. لا يستغرق الأمر سوى 30 ثانية.', cta: 'اترك تقييماً', disclaimer: 'هذا الرابط فريد لحجزك ولا يمكن استخدامه إلا مرة واحدة. إذا لم تستأجر مركبة مؤخراً، يمكنك تجاهل هذا البريد الإلكتروني.', @@ -285,6 +285,6 @@ export const reviewRequestEmail = { }, lang: Lang): string => t({ en: `Hi ${opts.firstName},\n\nThank you for renting with ${opts.companyName}. We hope you enjoyed your ${opts.vehicleLabel}.\n\nLeave a review here (one-time link):\n${opts.reviewUrl}\n\nThank you!`, fr: `Bonjour ${opts.firstName},\n\nMerci d'avoir loué chez ${opts.companyName}. Nous espérons que vous avez apprécié votre ${opts.vehicleLabel}.\n\nLaissez un avis ici (lien unique) :\n${opts.reviewUrl}\n\nMerci !`, - ar: `مرحباً ${opts.firstName}،\n\nشكراً لاستئجارك مع ${opts.companyName}. نأمل أنك استمتعت بـ ${opts.vehicleLabel}.\n\nاترك تقييماً هنا (رابط فريد):\n${opts.reviewUrl}\n\nشكراً!`, + ar: `مرحباً ${opts.firstName}،\n\nشكراً لاختيار ${opts.companyName}. نأمل أنك استمتعت بـ ${opts.vehicleLabel}.\n\nاترك تقييماً هنا (رابط فريد):\n${opts.reviewUrl}\n\nشكراً!`, }, lang), } diff --git a/apps/api/src/modules/companies/company.presenter.ts b/apps/api/src/modules/companies/company.presenter.ts index 54f0ddc..16d0498 100644 --- a/apps/api/src/modules/companies/company.presenter.ts +++ b/apps/api/src/modules/companies/company.presenter.ts @@ -3,5 +3,11 @@ export function presentCompany(company: any) { } export function presentBrand(brand: any) { - return brand + if (!brand) return brand + const { amanpaySecretKey, amanpayMerchantId, paypalEmail, paypalMerchantId, ...safe } = brand + return { + ...safe, + amanpayConfigured: !!(amanpayMerchantId && amanpaySecretKey), + paypalConfigured: !!(paypalEmail || paypalMerchantId), + } } diff --git a/apps/api/src/modules/payments/payment.routes.ts b/apps/api/src/modules/payments/payment.routes.ts index cb330d3..0b5df60 100644 --- a/apps/api/src/modules/payments/payment.routes.ts +++ b/apps/api/src/modules/payments/payment.routes.ts @@ -18,7 +18,7 @@ router.post('/webhooks/amanpay', async (req, res, next) => { try { const rawBody = JSON.stringify(req.body) const signature = (req.headers['x-amanpay-signature'] as string) ?? '' - if (amanpay.isConfigured() && !amanpay.verifyWebhookSignature(rawBody, signature)) { + if (!amanpay.isConfigured() || !amanpay.verifyWebhookSignature(rawBody, signature)) { return res.status(401).json({ error: 'invalid_signature' }) } await service.handleAmanpayWebhook(req.body) @@ -30,7 +30,7 @@ router.post('/webhooks/paypal', async (req, res, next) => { try { const rawBody = JSON.stringify(req.body) const isValid = await paypal.verifyWebhookEvent(req.headers as Record, rawBody) - if (paypal.isConfigured() && !isValid) return res.status(401).json({ error: 'invalid_signature' }) + if (!paypal.isConfigured() || !isValid) return res.status(401).json({ error: 'invalid_signature' }) await service.handlePaypalWebhook(req.body) res.json({ received: true }) } catch (err) { next(err) } diff --git a/apps/api/src/modules/subscriptions/subscription.routes.ts b/apps/api/src/modules/subscriptions/subscription.routes.ts index 8aba292..ecb43f9 100644 --- a/apps/api/src/modules/subscriptions/subscription.routes.ts +++ b/apps/api/src/modules/subscriptions/subscription.routes.ts @@ -27,7 +27,7 @@ router.post('/webhooks/amanpay', async (req, res, next) => { try { const rawBody = JSON.stringify(req.body) const signature = (req.headers['x-amanpay-signature'] as string) ?? '' - if (amanpay.isConfigured() && !amanpay.verifyWebhookSignature(rawBody, signature)) { + if (!amanpay.isConfigured() || !amanpay.verifyWebhookSignature(rawBody, signature)) { return res.status(401).json({ error: 'invalid_signature' }) } await service.handleAmanpayWebhook(req.body) @@ -39,7 +39,7 @@ router.post('/webhooks/paypal', async (req, res, next) => { try { const rawBody = JSON.stringify(req.body) const isValid = await paypal.verifyWebhookEvent(req.headers as Record, rawBody) - if (paypal.isConfigured() && !isValid) return res.status(401).json({ error: 'invalid_signature' }) + if (!paypal.isConfigured() || !isValid) return res.status(401).json({ error: 'invalid_signature' }) await service.handlePaypalWebhook(req.body) res.json({ received: true }) } catch (err) { next(err) } diff --git a/apps/api/src/modules/vehicles/vehicle.routes.ts b/apps/api/src/modules/vehicles/vehicle.routes.ts index 86b4ecf..b0e9b28 100644 --- a/apps/api/src/modules/vehicles/vehicle.routes.ts +++ b/apps/api/src/modules/vehicles/vehicle.routes.ts @@ -123,7 +123,7 @@ router.delete('/:id/calendar/blocks/:blockId', requireRole('MANAGER'), async (re router.get('/:id/maintenance', async (req, res, next) => { try { const { id } = parseParams(idParamSchema, req) - const logs = await service.getMaintenanceLogs(id) + const logs = await service.getMaintenanceLogs(id, req.companyId) ok(res, logs) } catch (err) { next(err) } }) diff --git a/apps/api/src/modules/vehicles/vehicle.service.ts b/apps/api/src/modules/vehicles/vehicle.service.ts index 4832ce1..c669ab8 100644 --- a/apps/api/src/modules/vehicles/vehicle.service.ts +++ b/apps/api/src/modules/vehicles/vehicle.service.ts @@ -110,7 +110,9 @@ export async function deleteCalendarBlock(vehicleId: string, companyId: string, await repo.deleteCalendarBlock(blockId, vehicleId) } -export async function getMaintenanceLogs(id: string) { +export async function getMaintenanceLogs(id: string, companyId: string) { + const vehicle = await repo.findById(id, companyId) + if (!vehicle) throw new NotFoundError('Vehicle not found') return repo.findMaintenanceLogs(id) } diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/contracts/[id]/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/contracts/[id]/page.tsx index 5112e35..972fbaf 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/contracts/[id]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/contracts/[id]/page.tsx @@ -641,7 +641,7 @@ export default function ContractDetailPage() { damageTypeLabels: { SCRATCH: 'Rayure', DENT: 'Bosse', CRACK: 'Fissure', CHIP: 'Éclat', MISSING: 'Manquant', STAIN: 'Tache', OTHER: 'Autre' }, severityLabels: { MINOR: 'Léger', MODERATE: 'Modéré', MAJOR: 'Majeur' }, fuelLevelLabels: { FULL: 'Plein', SEVEN_EIGHTHS: '7/8', THREE_QUARTERS: '3/4', FIVE_EIGHTHS: '5/8', HALF: '1/2', THREE_EIGHTHS: '3/8', QUARTER: '1/4', ONE_EIGHTH: '1/8', EMPTY: 'Vide' }, - paymentProviderLabels: { CASH: 'Espèces', STRIPE: 'Stripe', BANK_TRANSFER: 'Virement bancaire', CHECK: 'Chèque', PAYPAL: 'PayPal', CREDIT_CARD: 'Carte crédit', DEBIT_CARD: 'Carte débit' }, + paymentProviderLabels: { CASH: 'Espèces', STRIPE: 'Stripe', BANK_TRANSFER: 'Virement bancaire', CHECK: 'Chèque', PAYPAL: 'PayPal', CREDIT_CARD: 'Carte de crédit', DEBIT_CARD: 'Carte de débit' }, }, ar: { back: 'العودة إلى العقود', @@ -690,7 +690,7 @@ export default function ContractDetailPage() { noAdditionalDrivers: 'لا يوجد سائقون إضافيون.', noInsurance: 'لم يتم اختيار تأمين.', noPayments: 'لا توجد مدفوعات مسجلة.', - loading: 'جار تحميل العقد…', + loading: 'جارٍ تحميل العقد…', yes: 'نعم', no: 'لا', agreementTitle: 'عقد تأجير', @@ -769,8 +769,8 @@ export default function ContractDetailPage() { contractStatusLabels: { DRAFT: 'مسودة', PENDING: 'قيد الانتظار', ACTIVE: 'نشط', COMPLETED: 'مكتمل', CANCELLED: 'ملغى' }, paymentStatusLabels: { UNPAID: 'غير مدفوع', PAID: 'مدفوع', PARTIAL: 'جزئي', REFUNDED: 'مسترد', OVERDUE: 'متأخر', PENDING: 'قيد الانتظار', COMPLETED: 'مكتمل', FAILED: 'فشل' }, chargeTypeLabels: { PER_DAY: 'يومي', FLAT: 'مقطوع', FLAT_RATE: 'مقطوع', PER_RENTAL: 'لكل إيجار' }, - damageTypeLabels: { SCRATCH: 'خدش', DENT: 'دهس', CRACK: 'تشقق', CHIP: 'تقشر', MISSING: 'مفقود', STAIN: 'بقعة', OTHER: 'أخرى' }, - severityLabels: { MINOR: 'طفيف', MODERATE: 'متوسط', MAJOR: 'خطير' }, + damageTypeLabels: { SCRATCH: 'خدش', DENT: 'انبعاج', CRACK: 'تشقق', CHIP: 'تقشر', MISSING: 'مفقود', STAIN: 'بقعة', OTHER: 'أخرى' }, + severityLabels: { MINOR: 'طفيف', MODERATE: 'متوسط', MAJOR: 'شديد' }, fuelLevelLabels: { FULL: 'ممتلئ', SEVEN_EIGHTHS: '7/8', THREE_QUARTERS: '3/4', FIVE_EIGHTHS: '5/8', HALF: '1/2', THREE_EIGHTHS: '3/8', QUARTER: '1/4', ONE_EIGHTH: '1/8', EMPTY: 'فارغ' }, paymentProviderLabels: { CASH: 'نقداً', STRIPE: 'Stripe', BANK_TRANSFER: 'تحويل بنكي', CHECK: 'شيك', PAYPAL: 'PayPal', CREDIT_CARD: 'بطاقة ائتمان', DEBIT_CARD: 'بطاقة خصم' }, }, diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/settings/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/settings/page.tsx index 1a004aa..8b7f95e 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/settings/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/settings/page.tsx @@ -171,7 +171,7 @@ export default function SettingsPage() { saveAccounting: 'Enregistrer la comptabilité', displayName: 'Nom affiché', tagline: 'Slogan', - publicEmail: 'Email public', + publicEmail: 'E-mail public', publicPhone: 'Téléphone public', city: 'Ville', country: 'Pays', @@ -179,14 +179,14 @@ export default function SettingsPage() { contractLanguage: 'Langue du contrat', whatsapp: 'Numéro WhatsApp', brandColor: 'Couleur de marque', - listedMarketplace: 'Listé sur la marketplace', + listedMarketplace: 'Publié sur la marketplace', logoUpload: 'Logo', - heroUpload: 'Image héro', + heroUpload: 'Image principale', uploading: 'Téléversement…', logoUploaded: 'Logo téléversé', noLogo: 'Aucun logo téléversé', - heroUploaded: 'Image héro téléversée', - noHero: 'Aucune image héro téléversée', + heroUploaded: 'Image principale téléversée', + noHero: 'Aucune image principale téléversée', subdomain: 'Sous-domaine', customDomainLabel: 'Domaine personnalisé', domainPlaceholder: 'voitures.exemple.com', @@ -198,7 +198,7 @@ export default function SettingsPage() { additionalDriverCharge: 'Supplément conducteur additionnel', dailyDriverRate: 'Tarif conducteur/jour', flatDriverRate: 'Tarif conducteur fixe', - fuelPolicyNote: 'Note politique carburant', + fuelPolicyNote: 'Note sur la politique carburant', damagePolicy: 'Politique dommages', active: 'Actif', inactive: 'Inactif', @@ -213,7 +213,7 @@ export default function SettingsPage() { reportingPeriod: 'Période de reporting', reportFormat: 'Format de rapport', accountantName: 'Nom du comptable', - accountantEmail: 'Email du comptable', + accountantEmail: 'E-mail du comptable', autoSend: 'Envoyer automatiquement les rapports au comptable', amanpayMerchantId: 'ID marchand AmanPay', amanpaySecretKey: 'Clé secrète AmanPay', @@ -246,7 +246,7 @@ export default function SettingsPage() { saveAccounting: 'حفظ المحاسبة', displayName: 'اسم العرض', tagline: 'الشعار', - publicEmail: 'البريد العام', + publicEmail: 'البريد الإلكتروني العام', publicPhone: 'الهاتف العام', city: 'المدينة', country: 'الدولة', diff --git a/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx b/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx index 671b910..5b2c148 100644 --- a/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx +++ b/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx @@ -39,7 +39,7 @@ export default function ForgotPasswordPageClient({ embedded = false }: { embedde title: 'نسيت كلمة المرور؟', subtitle: 'أدخل بريدك الإلكتروني وسنرسل لك رابط إعادة التعيين.', email: 'البريد الإلكتروني', - submit: 'إرسال رابط الإعادة', + submit: 'إرسال رابط إعادة التعيين', submitting: 'جارٍ الإرسال…', backToLogin: 'العودة إلى تسجيل الدخول', successTitle: 'تحقق من بريدك الإلكتروني', diff --git a/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx b/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx index 1696ce7..19efd49 100644 --- a/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx +++ b/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx @@ -51,7 +51,7 @@ function ResetPasswordContent({ embedded = false }: { embedded?: boolean }) { errorShort: 'Le mot de passe doit contenir au moins 8 caractères.', errorInvalidToken: 'Ce lien est invalide ou a expiré. Veuillez en demander un nouveau.', errorGeneric: 'Une erreur est survenue. Veuillez réessayer.', - noToken: 'Aucun jeton trouvé. Veuillez demander une réinitialisation.', + noToken: 'Aucun jeton de réinitialisation trouvé. Veuillez demander un nouveau lien.', requestNew: 'Demander un nouveau lien', }, ar: { @@ -69,7 +69,7 @@ function ResetPasswordContent({ embedded = false }: { embedded?: boolean }) { errorShort: 'يجب أن تتكون كلمة المرور من 8 أحرف على الأقل.', errorInvalidToken: 'رابط إعادة التعيين غير صالح أو منتهي الصلاحية.', errorGeneric: 'حدث خطأ ما. يرجى المحاولة مرة أخرى.', - noToken: 'لم يتم العثور على رمز. يرجى طلب إعادة تعيين.', + noToken: 'لم يتم العثور على رمز إعادة التعيين. يرجى طلب رابط جديد.', requestNew: 'طلب رابط جديد', }, }[language] diff --git a/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx b/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx index 81631cc..1587a2c 100644 --- a/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx +++ b/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx @@ -44,7 +44,7 @@ export default function SignInPageClient({ embedded = false }: { embedded?: bool unexpectedError: 'Something went wrong. Please try again.', }, fr: { - brandName: 'Agence Espace', + brandName: 'Espace agence', title: 'Connexion', subtitle: 'Saisissez vos identifiants pour accéder à votre compte.', email: 'Email', @@ -57,12 +57,12 @@ export default function SignInPageClient({ embedded = false }: { embedded?: bool enterCode: "Entrez le code à 6 chiffres de votre application d'authentification.", back: 'Retour aux identifiants', forgotPassword: 'Mot de passe oublié ?', - invalidCredentials: 'Email ou mot de passe invalide.', + invalidCredentials: 'Adresse e-mail ou mot de passe invalide.', passwordNotSet: `Aucun mot de passe défini. Vérifiez votre e-mail d'invitation ou utilisez « Mot de passe oublié ? »`, unexpectedError: 'Une erreur est survenue. Veuillez réessayer.', }, ar: { - brandName: ' فضاء الوكالة ', + brandName: 'مساحة الوكالة', title: 'تسجيل الدخول', subtitle: 'أدخل بياناتك للوصول إلى حسابك.', email: 'البريد الإلكتروني', diff --git a/apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx b/apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx index d270284..cf351a0 100644 --- a/apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx +++ b/apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx @@ -174,14 +174,14 @@ export default function SignUpPage() { brand: 'RentalDriveGo', pageTitle: 'Lancez votre espace de location', pageSubtitle: 'Créez le compte utilisateur, complétez les informations de l’entreprise et lancez votre espace.', - steps: ['Compte utilisateur', 'Infos entreprise', 'Plan', 'Vérifier'], + steps: ['Compte utilisateur', 'Infos entreprise', 'Plan', 'Vérification'], ownerTitle: 'Compte utilisateur', ownerBody: 'Créez le compte utilisateur principal qui gérera cet espace entreprise.', partnerTitle: 'Infos entreprise', partnerBody: 'Complétez les informations de l’entreprise requises pour créer l’espace.', planTitle: 'Choisissez votre formule', planBody: 'Votre espace démarre immédiatement avec un essai gratuit de 14 jours dans la devise choisie.', - reviewTitle: 'Vérifier et lancer', + reviewTitle: 'Vérification et lancement', reviewBody: 'Nous allons créer immédiatement l’espace entreprise et démarrer l’essai de 14 jours avec la formule choisie.', firstName: 'Prénom du gérant/propriétaire', lastName: 'Nom du gérant/propriétaire', @@ -205,7 +205,7 @@ export default function SignUpPage() { createWorkspace: 'Créer l’espace', workspaceReady: 'Espace prêt', workspaceReadyBody: 'est maintenant actif et l’e-mail propriétaire', - enterDashboard: 'Entrer dans le dashboard', + enterDashboard: 'Accéder au tableau de bord', signInAnotherDevice: 'Se connecter sur un autre appareil', reviewWorkspace: 'Espace', reviewPlan: 'Formule', @@ -233,7 +233,7 @@ export default function SignUpPage() { '': 'Sélectionner les années d’activité', LESS_THAN_1: 'Moins de 1 an', ONE_TO_FIVE: '1 an à 5 ans', - MORE_THAN_5: 'Plus > 5 ans', + MORE_THAN_5: 'Plus de 5 ans', } as Record, planDescriptions: { STARTER: 'Flotte, offres et réservations essentielles.', diff --git a/apps/dashboard/src/components/I18nProvider.tsx b/apps/dashboard/src/components/I18nProvider.tsx index 6e9af2c..53dd18a 100644 --- a/apps/dashboard/src/components/I18nProvider.tsx +++ b/apps/dashboard/src/components/I18nProvider.tsx @@ -1335,8 +1335,8 @@ const dictionaries: Record = { damageMarkersHeading: 'علامات الضرر', removeMarker: 'حذف', noMarkers: 'لا توجد علامات ضرر محفوظة.', - damageTypeLabels: { SCRATCH: 'خدش', DENT: 'دهس', CRACK: 'تشقق', CHIP: 'تقشر', MISSING: 'مفقود', STAIN: 'بقعة', OTHER: 'أخرى' }, - severityLabels: { MINOR: 'طفيف', MODERATE: 'متوسط', MAJOR: 'خطير' }, + damageTypeLabels: { SCRATCH: 'خدش', DENT: 'انبعاج', CRACK: 'تشقق', CHIP: 'تقشر', MISSING: 'مفقود', STAIN: 'بقعة', OTHER: 'أخرى' }, + severityLabels: { MINOR: 'طفيف', MODERATE: 'متوسط', MAJOR: 'شديد' }, }, }, } diff --git a/apps/dashboard/src/components/layout/PublicFooter.tsx b/apps/dashboard/src/components/layout/PublicFooter.tsx index 6915628..3b358bd 100644 --- a/apps/dashboard/src/components/layout/PublicFooter.tsx +++ b/apps/dashboard/src/components/layout/PublicFooter.tsx @@ -16,8 +16,8 @@ type FooterItem = { const localeOptions: Array<{ value: DashboardLanguage; label: string; flag: string }> = [ { value: 'en', label: 'Global (English)', flag: '🇺🇸' }, - { value: 'ar', label: 'NorthAfrica (Arabic)', flag: '🇲🇦' }, - { value: 'fr', label: 'Europ (French)', flag: '🇫🇷' }, + { value: 'ar', label: 'North Africa (Arabic)', flag: '🇲🇦' }, + { value: 'fr', label: 'Europe (French)', flag: '🇫🇷' }, ] function getFooterContent(language: DashboardLanguage): { @@ -42,7 +42,7 @@ function getFooterContent(language: DashboardLanguage): { { label: 'Contacter les ventes', href: `${marketplaceUrl}/footer/contact-sales` }, { label: 'Nos bureaux', href: `${marketplaceUrl}/footer/our-offices` }, ], - localeLabel: 'Europ (French)', + localeLabel: 'Europe (French)', rightsLabel: 'Tous droits réservés.', } case 'ar': @@ -60,7 +60,7 @@ function getFooterContent(language: DashboardLanguage): { { label: 'تواصل مع المبيعات', href: `${marketplaceUrl}/footer/contact-sales` }, { label: 'مكاتبنا', href: `${marketplaceUrl}/footer/our-offices` }, ], - localeLabel: 'NorthAfrica (Arabic)', + localeLabel: 'North Africa (Arabic)', rightsLabel: 'جميع الحقوق محفوظة.', } case 'en': diff --git a/apps/dashboard/src/components/layout/PublicHeader.tsx b/apps/dashboard/src/components/layout/PublicHeader.tsx index e8078bf..604f546 100644 --- a/apps/dashboard/src/components/layout/PublicHeader.tsx +++ b/apps/dashboard/src/components/layout/PublicHeader.tsx @@ -42,7 +42,7 @@ export default function PublicHeader() { home: 'Accueil', marketplace: 'Marketplace', signIn: 'Connexion', - createAccount: "Créer Espace d'Agence", + createAccount: 'Créer un espace d’agence', theme: 'Mode', light: 'Clair', medium: 'Moyen', diff --git a/apps/marketplace/src/app/explore/page.tsx b/apps/marketplace/src/app/explore/page.tsx index 2ebcbb2..5126348 100644 --- a/apps/marketplace/src/app/explore/page.tsx +++ b/apps/marketplace/src/app/explore/page.tsx @@ -113,7 +113,7 @@ export default async function ExplorePage({ searchParams }: { searchParams?: Rec kicker: 'Découverte uniquement', title: "Trouvez votre prochaine location auprès d'entreprises locales fiables.", body: "Parcourez, filtrez et réservez — l'entreprise confirme et vous contacte directement.", - searchTitle: 'Réserver un véhicule', + searchTitle: 'Trouver un véhicule', pickupLocation: 'Lieu de départ', dropoffLocation: "Lieu de retour", pickupDate: 'Date de départ', diff --git a/apps/marketplace/src/app/features/page.tsx b/apps/marketplace/src/app/features/page.tsx index cf2e634..3131fc2 100644 --- a/apps/marketplace/src/app/features/page.tsx +++ b/apps/marketplace/src/app/features/page.tsx @@ -19,13 +19,13 @@ const copy = { fr: { kicker: 'Fonctionnalités', title: 'Opérations, découverte marketplace et réservation de marque.', - body: 'RentalDriveGo combine des opérations privées pour l’entreprise avec une marketplace publique qui envoie les clients vers le flux de paiement propre à chaque société.', + body: 'RentalDriveGo combine des opérations privées pour l’entreprise avec une marketplace publique qui redirige les clients vers le parcours de paiement propre à chaque société.', cards: [ ['Tableau de bord privé', 'Flotte, réservations, CRM, permissions d’équipe, facturation et rapports restent isolés par entreprise.'], ['Découverte marketplace', 'Les véhicules publiés et les offres publiques apparaissent sur `/explore` pour une navigation multi-entreprises.'], - ['Site de réservation de marque', 'Chaque entreprise dispose de son propre sous-domaine où les clients terminent réservation et paiement.'], + ['Site de réservation de marque', 'Chaque entreprise dispose de son propre sous-domaine où les clients finalisent la réservation et le paiement.'], ['Flexibilité de paiement', 'Utilisez AmanPay ou PayPal pour les abonnements et pour les paiements côté entreprise.'], - ['Opérations avancées', 'Assurance, conducteurs supplémentaires, règles tarifaires et politiques carburant structurées vivent dans le dashboard.'], + ['Opérations avancées', 'Assurance, conducteurs supplémentaires, règles tarifaires et politiques carburant structurées sont gérés dans les paramètres du tableau de bord.'], ['Contrôle des notifications', 'Le personnel comme les clients peuvent gérer leurs préférences de notification par événement et canal.'], ], }, diff --git a/apps/marketplace/src/components/MarketplaceShell.tsx b/apps/marketplace/src/components/MarketplaceShell.tsx index 9c69dc9..6cce54b 100644 --- a/apps/marketplace/src/components/MarketplaceShell.tsx +++ b/apps/marketplace/src/components/MarketplaceShell.tsx @@ -41,7 +41,7 @@ const dictionaries: Record = { marketplace: 'Accueil', explore: 'Marketplace', signIn: 'Connexion', - ownerSignIn: "Créer Espace d'Agence", + ownerSignIn: 'Créer un espace d’agence', language: 'Langue', theme: 'Mode', light: 'Clair', @@ -75,8 +75,8 @@ const PreferencesContext = createContext(null) const localeOptions: Array<{ value: MarketplaceLanguage; label: string; flag: string }> = [ { value: 'en', label: 'Global (English)', flag: '🇺🇸' }, - { value: 'ar', label: 'NorthAfrica (Arabic)', flag: '🇲🇦' }, - { value: 'fr', label: 'Europ (French)', flag: '🇫🇷' }, + { value: 'ar', label: 'North Africa (Arabic)', flag: '🇲🇦' }, + { value: 'fr', label: 'Europe (French)', flag: '🇫🇷' }, ] function getFooterContent(language: MarketplaceLanguage): { @@ -101,7 +101,7 @@ function getFooterContent(language: MarketplaceLanguage): { { label: 'Contacter les ventes', href: footerPageHref['contact-sales'] }, { label: 'Nos bureaux', href: footerPageHref['our-offices'] }, ], - localeLabel: 'Europ (French)', + localeLabel: 'Europe (French)', rightsLabel: 'Tous droits réservés.', } case 'ar': @@ -119,7 +119,7 @@ function getFooterContent(language: MarketplaceLanguage): { { label: 'تواصل مع المبيعات', href: footerPageHref['contact-sales'] }, { label: 'مكاتبنا', href: footerPageHref['our-offices'] }, ], - localeLabel: 'NorthAfrica (Arabic)', + localeLabel: 'North Africa (Arabic)', rightsLabel: 'جميع الحقوق محفوظة.', } case 'en': diff --git a/apps/marketplace/src/components/WorkspaceFrame.tsx b/apps/marketplace/src/components/WorkspaceFrame.tsx index bcb978b..5585ea4 100644 --- a/apps/marketplace/src/components/WorkspaceFrame.tsx +++ b/apps/marketplace/src/components/WorkspaceFrame.tsx @@ -54,7 +54,7 @@ export default function WorkspaceFrame({ target }: { target: FrameId }) { const frameHeight = isStandaloneAuthRoute ? '100vh' : 'calc(100vh - 56px)' const loadingLabel = { en: 'Loading', - fr: 'Chargement de', + fr: 'Chargement', ar: 'جارٍ تحميل', }[language] diff --git a/apps/marketplace/src/components/WorkspaceTabs.tsx b/apps/marketplace/src/components/WorkspaceTabs.tsx index 42b81e5..db8b459 100644 --- a/apps/marketplace/src/components/WorkspaceTabs.tsx +++ b/apps/marketplace/src/components/WorkspaceTabs.tsx @@ -34,35 +34,35 @@ export default function WorkspaceTabs() { workspaceC: 'Branded booking handoff begins only after a renter chooses a specific vehicle.', }, fr: { - badge: 'Espace unifie', - title: 'Un point d\'entree sur le port 3000 pour la decouverte marketplace, les operations entreprise et l\'administration plateforme.', + badge: 'Espace unifié', + title: 'Un point d’entrée sur le port 3000 pour la découverte marketplace, les opérations de l’entreprise et l’administration de la plateforme.', intro: - 'Les applications restent des surfaces Next.js separees en interne, mais la barre de navigation mene directement vers chaque espace sans changer de port manuellement.', + 'Les applications restent des interfaces Next.js séparées en interne, mais la barre de navigation mène directement à chaque espace sans avoir à changer de port manuellement.', sectionBadge: 'Marketplace RentalDriveGo', - sectionTitle: 'Parcourez les flottes locales sans perdre l\'identite de l\'entreprise qui gere la location.', - sectionBodyA: 'La decouverte commence ici. La reservation et le paiement se font sur le site de reservation propre a chaque entreprise.', - sectionBodyB: 'Les clients accedent a ce site de reservation seulement apres avoir choisi un vehicule dans la flotte d\'une entreprise.', - exploreVehicles: 'Explorer les vehicules', + sectionTitle: 'Parcourez les flottes locales sans perdre l’identité de l’entreprise qui gère la location.', + sectionBodyA: 'La découverte commence ici. La réservation et le paiement se font sur le site de réservation propre à chaque entreprise.', + sectionBodyB: 'Les clients accèdent à ce site de réservation seulement après avoir choisi un véhicule dans la flotte d’une entreprise.', + exploreVehicles: 'Explorer les véhicules', viewPricing: 'Voir les tarifs', nowServing: 'Disponible', - promoTitle: 'La decouverte, les tarifs, la connexion proprietaire et l\'entree vers la reservation restent ancrees dans la marketplace.', + promoTitle: 'La découverte, les tarifs, la connexion propriétaire et l’accès à la réservation restent ancrés dans la marketplace.', promoBody: - 'Espace entreprise et Operations plateforme restent accessibles depuis la navigation, tandis que la reservation de marque commence apres le choix d\'un vehicule.', + 'L’espace entreprise et les opérations de la plateforme restent accessibles depuis la navigation, tandis que la réservation de marque commence après le choix d’un véhicule.', customerPaths: 'Parcours client', - ownerSignIn: 'Connexion proprietaire', + ownerSignIn: 'Connexion propriétaire', pricing: 'Tarifs', workspaceAreas: 'Espaces de travail', - workspaceA: 'Espace entreprise pour la flotte, l\'equipe, la facturation et l\'analyse.', - workspaceB: 'Operations plateforme pour le support global et la supervision des tenants.', - workspaceC: 'Le transfert vers la reservation de marque commence seulement apres le choix d\'un vehicule precis.', + workspaceA: 'Espace entreprise pour la flotte, l’équipe, la facturation et l’analyse.', + workspaceB: 'Opérations plateforme pour le support global et la supervision multi-entreprises.', + workspaceC: 'Le transfert vers la réservation de marque commence seulement après le choix d’un véhicule précis.', }, ar: { badge: 'مساحة موحدة', title: 'نقطة دخول واحدة على المنفذ 3000 لاكتشاف السوق وعمليات الشركات وإدارة المنصة.', intro: - 'ما زالت التطبيقات تعمل كأسطح Next.js منفصلة داخلياً، لكن شريط التنقل ينقلك مباشرة إلى كل مساحة عمل بدون البحث عن المنافذ.', + 'ما زالت التطبيقات تعمل كواجهات Next.js منفصلة داخلياً، لكن شريط التنقل ينقلك مباشرة إلى كل مساحة عمل من دون الحاجة إلى البحث عن المنافذ.', sectionBadge: 'سوق RentalDriveGo', - sectionTitle: 'تصفح أساطيل التأجير المحلية من دون فقدان هوية الشركة التي تدير الحجز.', + sectionTitle: 'تصفح أساطيل التأجير المحلية مع الحفاظ على هوية الشركة التي تدير الحجز.', sectionBodyA: 'الاكتشاف يبدأ هنا. الحجز والدفع يتمان على موقع الحجز الخاص بكل شركة.', sectionBodyB: 'لا يصل العميل إلى موقع الحجز الخاص بالشركة إلا بعد اختيار سيارة من أسطول تلك الشركة.', exploreVehicles: 'استكشف السيارات', @@ -76,7 +76,7 @@ export default function WorkspaceTabs() { pricing: 'الأسعار', workspaceAreas: 'مساحات العمل', workspaceA: 'مساحة الشركة لإدارة الأسطول والفريق والفوترة والتحليلات.', - workspaceB: 'عمليات المنصة للدعم والإشراف على المستأجرين على مستوى المنصة.', + workspaceB: 'عمليات المنصة للدعم والإشراف على الشركات على مستوى المنصة.', workspaceC: 'الانتقال إلى موقع الحجز الخاص بالشركة يبدأ فقط بعد اختيار سيارة محددة.', }, }[language] diff --git a/packages/types/src/fuel.ts b/packages/types/src/fuel.ts index 244cb3b..7b0545d 100644 --- a/packages/types/src/fuel.ts +++ b/packages/types/src/fuel.ts @@ -3,27 +3,27 @@ export type FuelPolicyType = 'FULL_TO_FULL' | 'FULL_TO_EMPTY' | 'SAME_TO_SAME' | export const FUEL_POLICY_LABELS: Record> = { FULL_TO_FULL: { en: 'Full-to-Full: Return vehicle with a full tank. A refueling fee applies if returned with less fuel.', - fr: "Plein-à-Plein: Retournez le véhicule avec le plein. Des frais de carburant s'appliquent sinon.", + fr: "Plein à plein : retournez le véhicule avec le plein. Des frais de ravitaillement s'appliquent sinon.", ar: 'ممتلئ إلى ممتلئ: يجب إعادة السيارة بخزان ممتلئ. رسوم إضافية في حال الإعادة بمستوى أقل.', }, FULL_TO_EMPTY: { en: 'Full-to-Empty: Vehicle is provided with a full tank. No refund for unused fuel upon return.', - fr: 'Plein-à-Vide: Le véhicule est fourni avec un plein. Aucun remboursement pour carburant non utilisé.', + fr: "Plein à vide : le véhicule est livré avec le plein. Aucun remboursement n'est accordé pour le carburant non utilisé.", ar: 'ممتلئ إلى فارغ: تُسلَّم السيارة بخزان ممتلئ. لا استرداد للوقود غير المستخدم.', }, SAME_TO_SAME: { en: 'Same-to-Same: Return vehicle with the same fuel level as at pickup.', - fr: "Même niveau: Retournez avec le même niveau de carburant qu'au départ.", + fr: "Même niveau : retournez le véhicule avec le même niveau de carburant qu'au départ.", ar: 'نفس المستوى: أعد السيارة بنفس مستوى الوقود عند الاستلام.', }, PREPAID: { en: 'Prepaid Fuel: You pre-purchase a full tank at a fixed rate. Return at any fuel level.', - fr: "Carburant prépayé: Vous prépayez un plein à tarif fixe. Retour à n'importe quel niveau.", - ar: 'الوقود المدفوع مسبقاً: تدفع مقدماً لخزان كامل بسعر ثابت. العودة بأي مستوى.', + fr: "Carburant prépayé : vous achetez un plein à tarif fixe à l'avance. Retour possible avec n'importe quel niveau.", + ar: 'الوقود المدفوع مسبقاً: تدفع مقدماً ثمن خزان كامل بسعر ثابت. يمكن إعادة السيارة بأي مستوى.', }, FREE: { en: 'Fuel Included: Fuel cost is included in the rental price.', - fr: 'Carburant inclus: Le coût du carburant est inclus dans le tarif de location.', + fr: 'Carburant inclus : le coût du carburant est inclus dans le tarif de location.', ar: 'الوقود مشمول: تكلفة الوقود مشمولة في سعر الإيجار.', }, } diff --git a/packages/types/src/marketplace-homepage.ts b/packages/types/src/marketplace-homepage.ts index 48bb018..ce16e42 100644 --- a/packages/types/src/marketplace-homepage.ts +++ b/packages/types/src/marketplace-homepage.ts @@ -130,12 +130,12 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { fr: { sections: defaultMarketplaceHomepageSections, heroKicker: 'RentalDriveGo', - heroTitle: 'Une vitrine marketplace plus nette et plus forte.', + heroTitle: 'Une vitrine marketplace plus claire et plus percutante.', heroBody: - 'Les entreprises de location gèrent leurs opérations en privé, les clients parcourent une marketplace commune, et chaque réservation se termine sur le paiement de marque de la société.', + "Les entreprises de location gèrent leurs opérations en privé, les clients parcourent une marketplace commune et chaque réservation se finalise sur le parcours de paiement aux couleurs de l'entreprise.", startTrial: 'Commencer l’essai gratuit', exploreVehicles: 'Explorer les véhicules', - surfaceLabel: 'Surface marketplace', + surfaceLabel: 'Vitrine marketplace', surfaceTitle: 'Pensée pour deux audiences à la fois.', surfaceBody: 'Les opérateurs veulent du contrôle, les clients veulent de la confiance. La marketplace doit montrer les deux sans ressembler à un modèle générique.', @@ -144,26 +144,26 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { brandedFlows: 'Parcours de marque', multiTenant: 'Opérations multi-tenant', companyKicker: 'Flux opérateur', - companyTitle: 'Pilotez inventaire, offres, facturation et équipe depuis une seule couche de commande.', + companyTitle: 'Pilotez l’inventaire, les offres, la facturation et l’équipe depuis un seul centre de contrôle.', companyBody: 'Publiez une seule fois, choisissez ce qui apparaît publiquement, et gardez contrats, paiements et rapports isolés par entreprise.', renterKicker: 'Expérience client', - renterTitle: 'Laissez les clients comparer rapidement puis dirigez-les vers le bon site entreprise.', + renterTitle: 'Laissez les clients comparer rapidement, puis redirigez-les vers le bon site d’entreprise.', renterBody: - 'La marketplace agit comme moteur de découverte, pas comme agrégateur sans suite. Recherche ici, réservation là-bas, paiement direct.', + 'La marketplace sert de moteur de découverte, pas d’agrégateur sans suite. Recherche ici, réservation là-bas, paiement direct.', pillars: [ - ['Publication unifiée', 'Les photos, tarifs et offres circulent depuis un seul flux admin vers la découverte marketplace et les pages de réservation de marque.'], + ['Publication unifiée', 'Les photos, tarifs et offres circulent depuis un seul flux d’administration vers la découverte marketplace et les pages de réservation de marque.'], ['Découverte qualifiée', 'Offres mises en avant, navigation par localisation et signaux de réputation aident les clients à filtrer rapidement.'], - ['Revenus en direct', 'Les réservations basculent vers le paiement propre à l’entreprise, pour garder la relation client et l’encaissement.'], + ['Revenus en direct', 'Les réservations basculent vers le paiement propre à l’entreprise afin de préserver la relation client et l’encaissement.'], ].map(([title, body]) => ({ title: title!, body: body! })), metrics: [ ['01', 'Visibilité marketplace partagée'], ['02', 'Paiement direct entreprise'], - ['03', 'Paiements détenus par la société'], + ['03', 'Paiements gérés par l’entreprise'], ].map(([value, label]) => ({ value: value!, label: label! })), featureLabel: 'Ce que couvre le produit', features: [ - 'Gestion de flotte avec téléversement multi-photos', + 'Gestion de flotte avec téléversement de plusieurs photos', 'Offres marketplace et redirection vers la réservation', 'Site public de réservation par entreprise', 'CRM client, analytics et contrôle de facturation', @@ -176,18 +176,18 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { ].map(([step, title, body]) => ({ step: step!, title: title!, body: body! })), stepLabel: 'Étape', readyKicker: 'Prêt à démarrer', - readyTitle: 'Une homepage marketplace doit vendre le système en quelques secondes.', + readyTitle: 'La page d’accueil marketplace doit présenter le produit en quelques secondes.', readyBody: - 'Utilisez la marketplace pour capter la demande, puis faites passer les clients vers une expérience de marque qui garde prix, paiements et confiance liés à votre entreprise.', + 'Utilisez la marketplace pour capter la demande, puis orientez les clients vers une expérience de marque où les prix, les paiements et la confiance restent attachés à votre entreprise.', viewPricing: 'Voir les tarifs', createWorkspace: 'Créer l’espace', }, ar: { sections: defaultMarketplaceHomepageSections, heroKicker: 'RentalDriveGo', - heroTitle: 'واجهة سوق أوضح وأقوى.', + heroTitle: 'واجهة سوق أكثر وضوحاً وتأثيراً.', heroBody: - 'شركات التأجير تدير عملياتها بشكل خاص، والمستأجرون يتصفحون سوقاً مشتركاً، وكل حجز ينتهي في صفحة دفع تحمل هوية الشركة نفسها.', + 'شركات التأجير تدير عملياتها بشكل خاص، والمستأجرون يتصفحون سوقاً مشتركاً، وكل حجز ينتهي في صفحة دفع تحمل هوية الشركة ذاتها.', startTrial: 'ابدأ التجربة المجانية', exploreVehicles: 'استكشف السيارات', surfaceLabel: 'واجهة السوق', @@ -198,25 +198,25 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { trustedFleets: 'أساطيل موثوقة', brandedFlows: 'مسارات حجز مخصصة', multiTenant: 'عمليات متعددة الشركات', - companyKicker: 'تدفق المشغل', + companyKicker: 'سير عمل المشغل', companyTitle: 'تحكم في المخزون والعروض والفوترة والفريق من طبقة تشغيل واحدة.', companyBody: 'انشر المخزون مرة واحدة، وحدد ما يظهر للعامة، واحتفظ بالعقود والمدفوعات والتقارير معزولة لكل شركة.', renterKicker: 'تجربة المستأجر', - renterTitle: 'دع المستأجر يقارن بسرعة ثم انقله إلى موقع الشركة المناسب.', + renterTitle: 'دع المستأجرين يقارنون بسرعة ثم وجّههم إلى موقع الشركة المناسب.', renterBody: - 'السوق هنا محرك اكتشاف وليس مجمعاً بلا نهاية. البحث هنا، الحجز هناك، والدفع مباشرة للشركة.', + 'هذه المنصة محرك لاكتشاف الخيارات، وليست مُجمِّعاً بلا مسار واضح. ابحث هنا، احجز هناك، وادفع مباشرة للشركة.', pillars: [ - ['نشر موحد', 'صور السيارات والأسعار والعروض تنتقل من تدفق إدارة واحد إلى السوق وصفحات الحجز ذات الهوية الخاصة.'], + ['نشر موحد', 'صور السيارات والأسعار والعروض تنتقل من سير إدارة واحد إلى السوق وصفحات الحجز ذات الهوية الخاصة.'], ['اكتشاف مؤهل', 'العروض المميزة والتصفح حسب الموقع وإشارات السمعة تساعد المستأجرين على تضييق الخيارات بسرعة.'], - ['مسار إيراد مباشر', 'تنتقل الحجوزات إلى صفحة الدفع الخاصة بالشركة حتى تبقى الملكية المالية وعلاقة العميل مع النشاط نفسه.'], + ['مسار إيراد مباشر', 'تنتقل الحجوزات إلى صفحة الدفع الخاصة بالشركة حتى تبقى علاقة العميل والتحصيل المالي بيد الشركة نفسها.'], ].map(([title, body]) => ({ title: title!, body: body! })), metrics: [ ['01', 'ظهور مشترك في السوق'], ['02', 'دفع مباشر للشركة'], ['03', 'مدفوعات مملوكة للشركة'], ].map(([value, label]) => ({ value: value!, label: label! })), - featureLabel: 'ما الذي يغطيه المنتج', + featureLabel: 'ما الذي يقدمه المنتج', features: [ 'إدارة الأسطول مع رفع عدة صور', 'عروض السوق والتحويل إلى مسار الحجز', @@ -225,7 +225,7 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { ], stepsTitle: 'كيف تبدأ الشركات', steps: [ - ['1', 'أنشئ مساحة شركتك', 'اختر الخطة وابدأ تجربتك لمدة 14 يوماً ثم تحقق من حساب المالك.'], + ['1', 'أنشئ مساحة عمل شركتك', 'اختر الخطة وابدأ تجربتك لمدة 14 يوماً ثم فعّل حساب المالك.'], ['2', 'انشر السيارات والعروض', 'ارفع الصور مرة واحدة وتحكم فيما يظهر في السوق وفي الموقع المخصص.'], ['3', 'استقبل الحجوزات على موقعك', 'يكتشف المستأجرون أسطولك على RentalDriveGo ثم يدفعون مباشرة عبر مسار الحجز الخاص بك.'], ].map(([step, title, body]) => ({ step: step!, title: title!, body: body! })), @@ -233,7 +233,7 @@ export const defaultMarketplaceHomepageContent: MarketplaceHomepageConfig = { readyKicker: 'جاهز للانطلاق', readyTitle: 'يجب أن تشرح الصفحة الرئيسية قيمة المنصة خلال ثوانٍ.', readyBody: - 'استخدم السوق لجذب الطلب، ثم انقل المستأجرين إلى تجربة تحمل هوية شركتك وتحافظ على التسعير والمدفوعات والثقة داخل نشاطك.', + 'استخدم السوق لجذب الطلب، ثم انقل المستأجرين إلى تجربة تحمل هوية شركتك، بحيث تبقى الأسعار والمدفوعات والثقة مرتبطة بنشاطك.', viewPricing: 'عرض الأسعار', createWorkspace: 'إنشاء المساحة', },