Files
carmanagement/apps/public-site/src/lib/i18n.ts
T
2026-05-09 21:10:38 -04:00

1008 lines
45 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export type PublicSiteLanguage = 'en' | 'fr' | 'ar'
export const PUBLIC_SITE_LANGUAGE_COOKIE = 'public-site-language'
export function isPublicSiteLanguage(value: string | null | undefined): value is PublicSiteLanguage {
return value === 'en' || value === 'fr' || value === 'ar'
}
export type PublicSiteDictionary = {
lang: string
dir: 'ltr' | 'rtl'
siteNameFallback: string
rentalCompanyFallback: string
nav: {
about: string
vehicles: string
offers: string
pricing: string
blog: string
contact: string
bookCar: string
siteNavigation: string
}
home: {
heroTitleFallback: string
heroDescription: string
viewOffers: string
viewPricing: string
contactCompany: string
heroImageMissing: string
activeOffers: string
seeAllOffers: string
noActiveOffers: string
publishedVehicles: string
available: (count: number) => string
viewVehicle: string
}
about: {
eyebrow: string
defaultTagline: string
location: string
phone: string
email: string
whatsapp: string
locationMissing: string
phoneMissing: string
emailMissing: string
whatsappMissing: string
heroImageMissing: string
directBookingTitle: string
directBookingBody: string
publishedFleetTitle: string
publishedFleetBody: string
marketplaceReadyTitle: string
marketplaceReadyBody: string
companyDetails: string
companyAddressMissing: string
}
contact: {
title: string
description: string
}
offers: {
title: string
empty: string
noDescription: string
bookOffer: string
}
pricing: {
eyebrow: string
title: string
description: string
popular: string
acceptedPaymentMethods: string
paymentsDescription: string
faq: {
paymentMethodsQuestion: string
paymentMethodsAnswer: string
connectPaymentsQuestion: string
connectPaymentsAnswer: string
supportedCurrenciesQuestion: string
supportedCurrenciesAnswer: string
}
plans: {
starterDescription: string
growthDescription: string
proDescription: string
starterFeatures: string[]
growthFeatures: string[]
proFeatures: string[]
}
}
blog: {
eyebrow: string
title: string
description: string
draftPlaceholder: string
posts: Array<{
title: string
excerpt: string
category: string
}>
}
vehicles: {
title: string
available: (count: number) => string
noVehiclesTitle: string
noVehiclesBody: string
clearFilters: string
noPhoto: string
seats: (count: number) => string
perDay: string
viewDetails: string
filters: {
title: string
clearAll: string
category: string
transmission: string
maxPricePerDay: string
upTo: string
}
unavailable: {
eyebrow: string
title: string
body: string
backHome: string
}
detail: {
perDay: string
bookNow: string
back: string
availableFrom: string
unavailableNoDate: string
}
}
booking: {
loadingForm: string
eyebrow: string
title: string
steps: {
selectDates: string
driverDetails: string
reviewBooking: string
payment: string
}
loadingVehicle: string
vehicleNotFound: string
vehicleLoadError: string
vehicleAvailableFrom: (date: string) => string
vehicleUnavailableNoDate: string
bookingOptionsLoadError: string
startDate: string
endDate: string
continueToDriverDetails: string
firstName: string
lastName: string
email: string
phone: string
driverLicense: string
nationality: string
dateOfBirth: string
licenseIssued: string
licenseExpiry: string
insuranceAndPickupPolicies: string
insuranceHint: string
requiredCoverage: string
none: string
noOptionalInsurance: string
additionalDriver: string
charge: string
promoCode: string
enterCode: string
apply: string
appliedPromo: (title: string, discount: number) => string
back: string
reviewBookingCta: string
confirmReservation: string
processing: string
reservationCreated: string
manualApprovalRequired: string
totalDue: string
currency: string
paymentMethod: string
paymentMethods: {
amanpay: { title: string; description: string }
paypal: { title: string; description: string }
cash: { title: string; description: string }
}
redirectingToPayment: string
confirmPayAtPickup: string
payWith: (provider: string) => string
summary: {
vehicle: string
dates: string
primaryDriver: string
email: string
driverLicense: string
licenseExpiry: string
additionalDriver: string
promoCode: string
discount: (discount: number) => string
total: string
}
validation: {
pickStartDate: string
pickEndDate: string
endDateAfterStart: string
firstNameRequired: string
lastNameRequired: string
validEmailRequired: string
driverLicenseRequired: string
dateOfBirthRequired: string
licenseExpiryRequired: string
licenseIssueDateRequired: string
additionalDriverFirstNameRequired: string
additionalDriverLastNameRequired: string
additionalDriverLicenseRequired: string
}
promo: {
invalid: string
unavailable: string
}
submit: {
failed: string
network: string
}
payment: {
initiateFailed: string
network: string
}
}
confirmation: {
eyebrow: string
title: string
description: string
bookingSummary: string
vehicle: string
customer: string
email: string
dates: string
total: string
reference: string
status: string
browseMoreVehicles: string
backHome: string
}
}
const dictionaries: Record<PublicSiteLanguage, PublicSiteDictionary> = {
en: {
lang: 'en',
dir: 'ltr',
siteNameFallback: 'Company site',
rentalCompanyFallback: 'Rental company',
nav: {
about: 'About',
vehicles: 'Vehicles',
offers: 'Offers',
pricing: 'Pricing',
blog: 'Blog',
contact: 'Contact',
bookCar: 'Book a car',
siteNavigation: 'Site navigation',
},
home: {
heroTitleFallback: 'Book directly with the rental company.',
heroDescription: 'This branded site handles booking and payment directly. Marketplace discovery routes renters here for checkout.',
viewOffers: 'View offers',
viewPricing: 'View pricing',
contactCompany: 'Contact company',
heroImageMissing: 'Hero image not configured yet.',
activeOffers: 'Active offers',
seeAllOffers: 'See all offers',
noActiveOffers: 'No active offers right now.',
publishedVehicles: 'Published vehicles',
available: (count) => `${count} available`,
viewVehicle: 'View vehicle',
},
about: {
eyebrow: 'About',
defaultTagline: 'This company uses RentalDriveGo to publish vehicles, run bookings, and accept direct rental payments.',
location: 'Location',
phone: 'Phone',
email: 'Email',
whatsapp: 'WhatsApp',
locationMissing: 'Location not published yet',
phoneMissing: 'Phone not published yet',
emailMissing: 'Email not published yet',
whatsappMissing: 'WhatsApp not published yet',
heroImageMissing: 'Add a hero image in branding settings to personalize this page.',
directBookingTitle: 'Direct booking',
directBookingBody: 'Renters book and pay directly on the company site instead of checking out on the marketplace.',
publishedFleetTitle: 'Published fleet',
publishedFleetBody: 'Vehicle photos, pricing, and availability are managed from the private dashboard and published here automatically.',
marketplaceReadyTitle: 'Marketplace ready',
marketplaceReadyBody: 'Marketplace discovery redirects qualified renters into this branded site for final booking and payment.',
companyDetails: 'Company details',
companyAddressMissing: 'A public address has not been configured yet.',
},
contact: {
title: 'Contact',
description: "Use the company's public contact details or extend this page to call the `/site/:slug/contact` endpoint.",
},
offers: {
title: 'Offers',
empty: 'No offers are available right now.',
noDescription: 'No description provided.',
bookOffer: 'Book with this offer',
},
pricing: {
eyebrow: 'Pricing',
title: 'Plans for growing rental operations',
description: 'Choose a plan that matches your fleet size and booking volume. The design spec supports AmanPay and PayPal, plus MAD, USD, and EUR billing.',
popular: 'Popular',
acceptedPaymentMethods: 'Accepted payment methods',
paymentsDescription: 'Pay securely with AmanPay or PayPal. Supported currencies: MAD, USD, EUR.',
faq: {
paymentMethodsQuestion: 'Which payment methods are supported?',
paymentMethodsAnswer: 'Companies can use AmanPay or PayPal for subscriptions, and renters pay companies directly through the company-configured payment methods.',
connectPaymentsQuestion: 'Can I start before connecting payments?',
connectPaymentsAnswer: 'Yes. Trialing companies can configure branding, fleet, and offers before activating their live payment setup.',
supportedCurrenciesQuestion: 'Which currencies are supported?',
supportedCurrenciesAnswer: 'The current design supports MAD, USD, and EUR across subscription billing and rental pricing.',
},
plans: {
starterDescription: 'For smaller fleets that need direct bookings and a branded web presence.',
growthDescription: 'For operators scaling marketing, offers, and multi-role workflows.',
proDescription: 'For larger operators that need branding control and deeper operational flexibility.',
starterFeatures: ['Branded booking site', 'Fleet publishing', 'Marketplace visibility', 'Basic team access'],
growthFeatures: ['Everything in Starter', 'Featured marketplace offers', 'Advanced pricing rules', 'Expanded reporting'],
proFeatures: ['Custom branding', 'Priority onboarding', 'Advanced operations', 'White-label polish'],
},
},
blog: {
eyebrow: 'Blog',
title: 'Insights for rental operators',
description: 'The design spec includes a standard blog route. This page seeds that route with editorial-style placeholders that can later be replaced by CMS-backed content.',
draftPlaceholder: 'Draft article placeholder',
posts: [
{
title: 'How direct booking changes rental margins',
excerpt: 'Why moving renters from marketplace discovery into a branded booking experience protects pricing and customer relationships.',
category: 'Operations',
},
{
title: 'What to publish on a modern rental fleet page',
excerpt: 'A practical checklist for vehicle photos, specs, pricing clarity, and trust signals that convert browsers into bookings.',
category: 'Marketing',
},
{
title: 'Using promotions without breaking your pricing model',
excerpt: 'Featured offers work best when they are time-bound, measurable, and attached to actual fleet strategy.',
category: 'Revenue',
},
],
},
vehicles: {
title: 'Our Fleet',
available: (count) => `${count} vehicle${count !== 1 ? 's' : ''} available`,
noVehiclesTitle: 'No vehicles match your filters.',
noVehiclesBody: 'Try adjusting the category, price, or transmission filter.',
clearFilters: 'Clear filters',
noPhoto: 'No photo',
seats: (count) => `${count} seats`,
perDay: 'per day',
viewDetails: 'View details',
filters: {
title: 'Filters',
clearAll: 'Clear all',
category: 'Category',
transmission: 'Transmission',
maxPricePerDay: 'Max price / day',
upTo: 'Up to',
},
unavailable: {
eyebrow: 'Site unavailable',
title: 'Vehicle details are temporarily unavailable.',
body: 'The public site is running, but the local database is not reachable right now.',
backHome: 'Back to home',
},
detail: {
perDay: '/ day',
bookNow: 'Book now',
back: 'Back',
availableFrom: 'Available from',
unavailableNoDate: 'This vehicle is temporarily unavailable for new reservations.',
},
},
booking: {
loadingForm: 'Loading booking form...',
eyebrow: 'Booking flow',
title: 'Complete your reservation',
steps: {
selectDates: 'Select dates',
driverDetails: 'Driver details',
reviewBooking: 'Review booking',
payment: 'Payment',
},
loadingVehicle: 'Loading vehicle...',
vehicleNotFound: 'Vehicle not found.',
vehicleLoadError: 'Could not load vehicle details.',
vehicleAvailableFrom: (date) => `This vehicle can only be reserved starting ${date}.`,
vehicleUnavailableNoDate: 'This vehicle is temporarily unavailable for new reservations.',
bookingOptionsLoadError: 'Could not load booking options.',
startDate: 'Start date',
endDate: 'End date',
continueToDriverDetails: 'Continue to driver details ->',
firstName: 'First name',
lastName: 'Last name',
email: 'Email',
phone: 'Phone',
driverLicense: 'Driver license',
nationality: 'Nationality',
dateOfBirth: 'Date of birth',
licenseIssued: 'License issued',
licenseExpiry: 'License expiry',
insuranceAndPickupPolicies: 'Insurance and pickup policies',
insuranceHint: 'Select optional protection before confirming your reservation.',
requiredCoverage: 'Required coverage',
none: 'None',
noOptionalInsurance: 'No optional insurance add-ons are available for this company.',
additionalDriver: 'Additional driver',
charge: 'Charge',
promoCode: 'Promo code',
enterCode: 'Enter code',
apply: 'Apply',
appliedPromo: (title, discount) => `✓ "${title}" applied - ${discount}% off`,
back: '<- Back',
reviewBookingCta: 'Review booking ->',
confirmReservation: 'Confirm reservation',
processing: 'Processing...',
reservationCreated: '✓ Reservation created - choose how you would like to pay',
manualApprovalRequired: 'Driver license review is required before online payment. You can confirm with pay-at-pickup or wait for staff approval.',
totalDue: 'Total due',
currency: 'Currency',
paymentMethod: 'Payment method',
paymentMethods: {
amanpay: { title: 'AmanPay', description: 'Secure online payment via AmanPay' },
paypal: { title: 'PayPal', description: 'Pay with your PayPal account or card' },
cash: { title: 'Pay at pickup', description: 'Pay cash or by card when you pick up the vehicle' },
},
redirectingToPayment: 'Redirecting to payment...',
confirmPayAtPickup: 'Confirm - pay at pickup',
payWith: (provider) => `Pay with ${provider} ->`,
summary: {
vehicle: 'Vehicle',
dates: 'Dates',
primaryDriver: 'Primary driver',
email: 'Email',
driverLicense: 'Driver license',
licenseExpiry: 'License expiry',
additionalDriver: 'Additional driver',
promoCode: 'Promo code',
discount: (discount) => `Discount (${discount}%)`,
total: 'Total',
},
validation: {
pickStartDate: 'Pick a start date.',
pickEndDate: 'Pick an end date.',
endDateAfterStart: 'End date must be after start date.',
firstNameRequired: 'First name required.',
lastNameRequired: 'Last name required.',
validEmailRequired: 'Valid email required.',
driverLicenseRequired: 'Driver license required.',
dateOfBirthRequired: 'Date of birth required.',
licenseExpiryRequired: 'License expiry required.',
licenseIssueDateRequired: 'License issue date required.',
additionalDriverFirstNameRequired: 'Additional driver first name required.',
additionalDriverLastNameRequired: 'Additional driver last name required.',
additionalDriverLicenseRequired: 'Additional driver license required.',
},
promo: {
invalid: 'Invalid or expired promo code.',
unavailable: 'Could not validate promo code. Please try again.',
},
submit: {
failed: 'Booking failed. Please try again.',
network: 'Network error. Please check your connection and try again.',
},
payment: {
initiateFailed: 'Payment could not be initiated.',
network: 'Network error. Please try again.',
},
},
confirmation: {
eyebrow: 'Reservation saved',
title: 'Booking request received.',
description: 'The reservation has been created in draft status and will be confirmed by staff or after payment.',
bookingSummary: 'Booking summary',
vehicle: 'Vehicle',
customer: 'Customer',
email: 'Email',
dates: 'Dates',
total: 'Total',
reference: 'Reference',
status: 'Status',
browseMoreVehicles: 'Browse more vehicles',
backHome: 'Back to home',
},
},
fr: {
lang: 'fr',
dir: 'ltr',
siteNameFallback: "Site de l'entreprise",
rentalCompanyFallback: 'Société de location',
nav: {
about: 'À propos',
vehicles: 'Véhicules',
offers: 'Offres',
pricing: 'Tarifs',
blog: 'Blog',
contact: 'Contact',
bookCar: 'Réserver une voiture',
siteNavigation: 'Navigation du site',
},
home: {
heroTitleFallback: "Réservez directement auprès de l'entreprise de location.",
heroDescription: 'Ce site de marque gère directement la réservation et le paiement. La découverte sur la marketplace redirige les locataires ici pour finaliser la commande.',
viewOffers: 'Voir les offres',
viewPricing: 'Voir les tarifs',
contactCompany: "Contacter l'entreprise",
heroImageMissing: "L'image principale n'est pas encore configurée.",
activeOffers: 'Offres actives',
seeAllOffers: 'Voir toutes les offres',
noActiveOffers: "Aucune offre active pour le moment.",
publishedVehicles: 'Véhicules publiés',
available: (count) => `${count} disponible${count > 1 ? 's' : ''}`,
viewVehicle: 'Voir le véhicule',
},
about: {
eyebrow: 'À propos',
defaultTagline: 'Cette entreprise utilise RentalDriveGo pour publier ses véhicules, gérer les réservations et accepter des paiements directs.',
location: 'Localisation',
phone: 'Téléphone',
email: 'E-mail',
whatsapp: 'WhatsApp',
locationMissing: 'Localisation non publiée',
phoneMissing: 'Téléphone non publié',
emailMissing: 'E-mail non publié',
whatsappMissing: 'WhatsApp non publié',
heroImageMissing: 'Ajoutez une image principale dans les paramètres de marque pour personnaliser cette page.',
directBookingTitle: 'Réservation directe',
directBookingBody: "Les locataires réservent et paient directement sur le site de l'entreprise au lieu de finaliser sur la marketplace.",
publishedFleetTitle: 'Flotte publiée',
publishedFleetBody: 'Les photos, tarifs et disponibilités des véhicules sont gérés depuis le tableau de bord privé et publiés ici automatiquement.',
marketplaceReadyTitle: 'Prêt pour la marketplace',
marketplaceReadyBody: 'La découverte sur la marketplace redirige les locataires qualifiés vers ce site de marque pour la réservation finale et le paiement.',
companyDetails: "Détails de l'entreprise",
companyAddressMissing: "Aucune adresse publique n'a encore été configurée.",
},
contact: {
title: 'Contact',
description: "Utilisez les coordonnées publiques de l'entreprise ou étendez cette page pour appeler l'endpoint `/site/:slug/contact`.",
},
offers: {
title: 'Offres',
empty: "Aucune offre n'est disponible pour le moment.",
noDescription: 'Aucune description fournie.',
bookOffer: 'Réserver avec cette offre',
},
pricing: {
eyebrow: 'Tarifs',
title: 'Des formules pour faire grandir votre activité',
description: 'Choisissez une formule adaptée à la taille de votre flotte et à votre volume de réservations. La spécification prend en charge AmanPay et PayPal, ainsi que la facturation en MAD, USD et EUR.',
popular: 'Populaire',
acceptedPaymentMethods: 'Moyens de paiement acceptés',
paymentsDescription: 'Payez en toute sécurité avec AmanPay ou PayPal. Devises prises en charge : MAD, USD, EUR.',
faq: {
paymentMethodsQuestion: 'Quels moyens de paiement sont pris en charge ?',
paymentMethodsAnswer: "Les entreprises peuvent utiliser AmanPay ou PayPal pour les abonnements, et les locataires paient directement l'entreprise via les méthodes configurées par celle-ci.",
connectPaymentsQuestion: 'Puis-je commencer avant de connecter les paiements ?',
connectPaymentsAnswer: "Oui. Les entreprises en essai peuvent configurer leur marque, leur flotte et leurs offres avant d'activer leur configuration de paiement en direct.",
supportedCurrenciesQuestion: 'Quelles devises sont prises en charge ?',
supportedCurrenciesAnswer: 'Le design actuel prend en charge MAD, USD et EUR pour la facturation des abonnements et les tarifs de location.',
},
plans: {
starterDescription: 'Pour les petites flottes qui ont besoin de réservations directes et dune présence web de marque.',
growthDescription: 'Pour les opérateurs qui développent leur marketing, leurs offres et leurs workflows multi-rôles.',
proDescription: 'Pour les grands opérateurs qui ont besoin de contrôle de marque et dune flexibilité opérationnelle plus profonde.',
starterFeatures: ['Site de réservation de marque', 'Publication de flotte', 'Visibilité marketplace', "Accès d'équipe basique"],
growthFeatures: ['Tout le contenu de Starter', 'Offres marketplace mises en avant', 'Règles tarifaires avancées', 'Reporting élargi'],
proFeatures: ['Marque personnalisée', 'Onboarding prioritaire', 'Opérations avancées', 'Finition white-label'],
},
},
blog: {
eyebrow: 'Blog',
title: 'Analyses pour les opérateurs de location',
description: 'La spécification inclut une route blog standard. Cette page alimente cette route avec des espaces réservés éditoriaux qui pourront ensuite être remplacés par un contenu relié à un CMS.',
draftPlaceholder: "Brouillon d'article",
posts: [
{
title: 'Comment la réservation directe change les marges',
excerpt: 'Pourquoi déplacer les locataires de la découverte marketplace vers une expérience de réservation de marque protège les prix et la relation client.',
category: 'Opérations',
},
{
title: "Que publier sur une page de flotte moderne",
excerpt: 'Une checklist pratique pour les photos, spécifications, clarté des prix et signaux de confiance qui convertissent les visiteurs en réservations.',
category: 'Marketing',
},
{
title: 'Utiliser des promotions sans casser votre modèle tarifaire',
excerpt: 'Les offres mises en avant fonctionnent mieux lorsquelles sont limitées dans le temps, mesurables et alignées sur une vraie stratégie de flotte.',
category: 'Revenus',
},
],
},
vehicles: {
title: 'Notre flotte',
available: (count) => `${count} véhicule${count > 1 ? 's' : ''} disponible${count > 1 ? 's' : ''}`,
noVehiclesTitle: 'Aucun véhicule ne correspond à vos filtres.',
noVehiclesBody: 'Essayez de modifier le filtre de catégorie, de prix ou de transmission.',
clearFilters: 'Effacer les filtres',
noPhoto: 'Pas de photo',
seats: (count) => `${count} places`,
perDay: 'par jour',
viewDetails: 'Voir les détails',
filters: {
title: 'Filtres',
clearAll: 'Tout effacer',
category: 'Catégorie',
transmission: 'Transmission',
maxPricePerDay: 'Prix max / jour',
upTo: "Jusqu'à",
},
unavailable: {
eyebrow: 'Site indisponible',
title: 'Les détails du véhicule sont temporairement indisponibles.',
body: 'Le site public fonctionne, mais la base de données locale est actuellement inaccessible.',
backHome: "Retour à l'accueil",
},
detail: {
perDay: '/ jour',
bookNow: 'Réserver maintenant',
back: 'Retour',
availableFrom: 'Disponible à partir du',
unavailableNoDate: 'Ce véhicule est temporairement indisponible pour les nouvelles réservations.',
},
},
booking: {
loadingForm: 'Chargement du formulaire...',
eyebrow: 'Parcours de réservation',
title: 'Finalisez votre réservation',
steps: {
selectDates: 'Choisir les dates',
driverDetails: 'Informations conducteur',
reviewBooking: 'Vérifier la réservation',
payment: 'Paiement',
},
loadingVehicle: 'Chargement du véhicule...',
vehicleNotFound: 'Véhicule introuvable.',
vehicleLoadError: 'Impossible de charger les détails du véhicule.',
vehicleAvailableFrom: (date) => `Ce véhicule ne peut être réservé qu’à partir du ${date}.`,
vehicleUnavailableNoDate: 'Ce véhicule est temporairement indisponible pour les nouvelles réservations.',
bookingOptionsLoadError: 'Impossible de charger les options de réservation.',
startDate: 'Date de début',
endDate: 'Date de fin',
continueToDriverDetails: 'Continuer vers les informations conducteur ->',
firstName: 'Prénom',
lastName: 'Nom',
email: 'E-mail',
phone: 'Téléphone',
driverLicense: 'Permis de conduire',
nationality: 'Nationalité',
dateOfBirth: 'Date de naissance',
licenseIssued: 'Permis délivré le',
licenseExpiry: 'Expiration du permis',
insuranceAndPickupPolicies: 'Assurance et politiques de remise',
insuranceHint: 'Sélectionnez une protection optionnelle avant de confirmer votre réservation.',
requiredCoverage: 'Couverture obligatoire',
none: 'Aucune',
noOptionalInsurance: "Aucune assurance optionnelle n'est disponible pour cette entreprise.",
additionalDriver: 'Conducteur additionnel',
charge: 'Frais',
promoCode: 'Code promo',
enterCode: 'Entrer le code',
apply: 'Appliquer',
appliedPromo: (title, discount) => `✓ "${title}" appliqué - ${discount}% de réduction`,
back: '<- Retour',
reviewBookingCta: 'Vérifier la réservation ->',
confirmReservation: 'Confirmer la réservation',
processing: 'Traitement...',
reservationCreated: '✓ Réservation créée - choisissez votre mode de paiement',
manualApprovalRequired: 'La vérification du permis est requise avant le paiement en ligne. Vous pouvez confirmer avec paiement au retrait ou attendre la validation du personnel.',
totalDue: 'Total à payer',
currency: 'Devise',
paymentMethod: 'Mode de paiement',
paymentMethods: {
amanpay: { title: 'AmanPay', description: 'Paiement en ligne sécurisé via AmanPay' },
paypal: { title: 'PayPal', description: 'Payez avec votre compte PayPal ou votre carte' },
cash: { title: 'Paiement au retrait', description: 'Payez en espèces ou par carte lors de la récupération du véhicule' },
},
redirectingToPayment: 'Redirection vers le paiement...',
confirmPayAtPickup: 'Confirmer - payer au retrait',
payWith: (provider) => `Payer avec ${provider} ->`,
summary: {
vehicle: 'Véhicule',
dates: 'Dates',
primaryDriver: 'Conducteur principal',
email: 'E-mail',
driverLicense: 'Permis',
licenseExpiry: 'Expiration du permis',
additionalDriver: 'Conducteur additionnel',
promoCode: 'Code promo',
discount: (discount) => `Réduction (${discount}%)`,
total: 'Total',
},
validation: {
pickStartDate: 'Choisissez une date de début.',
pickEndDate: 'Choisissez une date de fin.',
endDateAfterStart: 'La date de fin doit être après la date de début.',
firstNameRequired: 'Le prénom est requis.',
lastNameRequired: 'Le nom est requis.',
validEmailRequired: 'Un e-mail valide est requis.',
driverLicenseRequired: 'Le permis de conduire est requis.',
dateOfBirthRequired: 'La date de naissance est requise.',
licenseExpiryRequired: "La date d'expiration du permis est requise.",
licenseIssueDateRequired: 'La date de délivrance du permis est requise.',
additionalDriverFirstNameRequired: 'Le prénom du conducteur additionnel est requis.',
additionalDriverLastNameRequired: 'Le nom du conducteur additionnel est requis.',
additionalDriverLicenseRequired: 'Le permis du conducteur additionnel est requis.',
},
promo: {
invalid: 'Code promo invalide ou expiré.',
unavailable: 'Impossible de valider le code promo. Réessayez.',
},
submit: {
failed: 'La réservation a échoué. Réessayez.',
network: 'Erreur réseau. Vérifiez votre connexion puis réessayez.',
},
payment: {
initiateFailed: 'Le paiement na pas pu être démarré.',
network: 'Erreur réseau. Réessayez.',
},
},
confirmation: {
eyebrow: 'Réservation enregistrée',
title: 'Demande de réservation reçue.',
description: 'La réservation a été créée en brouillon et sera confirmée par le personnel ou après paiement.',
bookingSummary: 'Résumé de réservation',
vehicle: 'Véhicule',
customer: 'Client',
email: 'E-mail',
dates: 'Dates',
total: 'Total',
reference: 'Référence',
status: 'Statut',
browseMoreVehicles: 'Voir plus de véhicules',
backHome: "Retour à l'accueil",
},
},
ar: {
lang: 'ar',
dir: 'rtl',
siteNameFallback: 'موقع الشركة',
rentalCompanyFallback: 'شركة تأجير',
nav: {
about: 'من نحن',
vehicles: 'المركبات',
offers: 'العروض',
pricing: 'الأسعار',
blog: 'المدونة',
contact: 'اتصل بنا',
bookCar: 'احجز سيارة',
siteNavigation: 'روابط الموقع',
},
home: {
heroTitleFallback: 'احجز مباشرة مع شركة التأجير.',
heroDescription: 'هذا الموقع المخصص للعلامة التجارية يدير الحجز والدفع مباشرة. الاكتشاف عبر المنصة يحول المستأجرين إلى هنا لإتمام الحجز.',
viewOffers: 'عرض العروض',
viewPricing: 'عرض الأسعار',
contactCompany: 'التواصل مع الشركة',
heroImageMissing: 'لم يتم إعداد صورة رئيسية بعد.',
activeOffers: 'العروض النشطة',
seeAllOffers: 'عرض كل العروض',
noActiveOffers: 'لا توجد عروض نشطة حالياً.',
publishedVehicles: 'المركبات المنشورة',
available: (count) => `${count} متاح`,
viewVehicle: 'عرض المركبة',
},
about: {
eyebrow: 'من نحن',
defaultTagline: 'تستخدم هذه الشركة RentalDriveGo لنشر المركبات وإدارة الحجوزات وقبول مدفوعات الإيجار المباشرة.',
location: 'الموقع',
phone: 'الهاتف',
email: 'البريد الإلكتروني',
whatsapp: 'واتساب',
locationMissing: 'لم يتم نشر الموقع بعد',
phoneMissing: 'لم يتم نشر الهاتف بعد',
emailMissing: 'لم يتم نشر البريد الإلكتروني بعد',
whatsappMissing: 'لم يتم نشر واتساب بعد',
heroImageMissing: 'أضف صورة رئيسية من إعدادات العلامة التجارية لتخصيص هذه الصفحة.',
directBookingTitle: 'حجز مباشر',
directBookingBody: 'يقوم المستأجرون بالحجز والدفع مباشرة على موقع الشركة بدلاً من إتمام العملية في المنصة.',
publishedFleetTitle: 'أسطول منشور',
publishedFleetBody: 'تتم إدارة صور المركبات والأسعار والتوفر من لوحة التحكم الخاصة ونشرها هنا تلقائياً.',
marketplaceReadyTitle: 'جاهز للمنصة',
marketplaceReadyBody: 'الاكتشاف عبر المنصة يوجه المستأجرين المؤهلين إلى هذا الموقع المخصص لإتمام الحجز والدفع.',
companyDetails: 'تفاصيل الشركة',
companyAddressMissing: 'لم يتم إعداد عنوان عام بعد.',
},
contact: {
title: 'اتصل بنا',
description: 'استخدم بيانات الاتصال العامة للشركة أو وسّع هذه الصفحة لاستدعاء نقطة النهاية `/site/:slug/contact`.',
},
offers: {
title: 'العروض',
empty: 'لا توجد عروض متاحة حالياً.',
noDescription: 'لا يوجد وصف متاح.',
bookOffer: 'احجز بهذا العرض',
},
pricing: {
eyebrow: 'الأسعار',
title: 'خطط تناسب نمو عمليات التأجير',
description: 'اختر الخطة التي تناسب حجم أسطولك وحجم الحجوزات. يدعم التصميم AmanPay وPayPal إلى جانب الفوترة بعملات MAD وUSD وEUR.',
popular: 'الأكثر شيوعاً',
acceptedPaymentMethods: 'وسائل الدفع المقبولة',
paymentsDescription: 'ادفع بأمان عبر AmanPay أو PayPal. العملات المدعومة: MAD وUSD وEUR.',
faq: {
paymentMethodsQuestion: 'ما وسائل الدفع المدعومة؟',
paymentMethodsAnswer: 'يمكن للشركات استخدام AmanPay أو PayPal للاشتراكات، بينما يدفع المستأجرون مباشرة للشركات عبر الوسائل التي تضبطها الشركة.',
connectPaymentsQuestion: 'هل يمكنني البدء قبل ربط وسائل الدفع؟',
connectPaymentsAnswer: 'نعم. يمكن للشركات في الفترة التجريبية إعداد العلامة التجارية والأسطول والعروض قبل تفعيل الدفع المباشر.',
supportedCurrenciesQuestion: 'ما العملات المدعومة؟',
supportedCurrenciesAnswer: 'يدعم التصميم الحالي MAD وUSD وEUR عبر فوترة الاشتراكات وتسعير الإيجارات.',
},
plans: {
starterDescription: 'للأساطيل الصغيرة التي تحتاج إلى حجوزات مباشرة وحضور ويب مخصص للعلامة التجارية.',
growthDescription: 'للمشغلين الذين يوسعون التسويق والعروض وتدفقات العمل متعددة الأدوار.',
proDescription: 'للمشغلين الأكبر الذين يحتاجون إلى تحكم أعمق بالعلامة التجارية ومرونة تشغيلية أكبر.',
starterFeatures: ['موقع حجز مخصص', 'نشر الأسطول', 'الظهور في المنصة', 'وصول أساسي للفريق'],
growthFeatures: ['كل ما في Starter', 'عروض مميزة في المنصة', 'قواعد تسعير متقدمة', 'تقارير موسعة'],
proFeatures: ['علامة تجارية مخصصة', 'تهيئة أولوية', 'عمليات متقدمة', 'لمسة وايت ليبل'],
},
},
blog: {
eyebrow: 'المدونة',
title: 'رؤى لمشغلي التأجير',
description: 'يتضمن التصميم مساراً قياسياً للمدونة. تملأ هذه الصفحة هذا المسار بمحتوى تمهيدي يمكن استبداله لاحقاً بمحتوى من نظام إدارة محتوى.',
draftPlaceholder: 'مسودة مقال',
posts: [
{
title: 'كيف يغير الحجز المباشر هوامش الربح',
excerpt: 'نقل المستأجرين من الاكتشاف عبر المنصة إلى تجربة حجز تحمل علامتك يحمي التسعير وعلاقة العميل.',
category: 'العمليات',
},
{
title: 'ماذا تنشر في صفحة أسطول حديثة',
excerpt: 'قائمة عملية لصور المركبات والمواصفات ووضوح الأسعار وعناصر الثقة التي تحول الزائر إلى حجز.',
category: 'التسويق',
},
{
title: 'استخدام العروض دون إفساد نموذج التسعير',
excerpt: 'تعمل العروض المميزة بشكل أفضل عندما تكون محددة زمنياً وقابلة للقياس ومرتبطة باستراتيجية أسطول حقيقية.',
category: 'الإيرادات',
},
],
},
vehicles: {
title: 'أسطولنا',
available: (count) => `${count} مركبة متاحة`,
noVehiclesTitle: 'لا توجد مركبات تطابق عوامل التصفية.',
noVehiclesBody: 'حاول تعديل فلاتر الفئة أو السعر أو ناقل الحركة.',
clearFilters: 'مسح الفلاتر',
noPhoto: 'لا توجد صورة',
seats: (count) => `${count} مقاعد`,
perDay: 'في اليوم',
viewDetails: 'عرض التفاصيل',
filters: {
title: 'الفلاتر',
clearAll: 'مسح الكل',
category: 'الفئة',
transmission: 'ناقل الحركة',
maxPricePerDay: 'الحد الأقصى للسعر / يوم',
upTo: 'حتى',
},
unavailable: {
eyebrow: 'الموقع غير متاح',
title: 'تفاصيل المركبة غير متاحة مؤقتاً.',
body: 'الموقع العام يعمل، لكن قاعدة البيانات المحلية غير متاحة حالياً.',
backHome: 'العودة للرئيسية',
},
detail: {
perDay: '/ يوم',
bookNow: 'احجز الآن',
back: 'رجوع',
availableFrom: 'متاح ابتداءً من',
unavailableNoDate: 'هذه المركبة غير متاحة مؤقتاً للحجوزات الجديدة.',
},
},
booking: {
loadingForm: 'جاري تحميل نموذج الحجز...',
eyebrow: 'خطوات الحجز',
title: 'أكمل حجزك',
steps: {
selectDates: 'اختيار التواريخ',
driverDetails: 'بيانات السائق',
reviewBooking: 'مراجعة الحجز',
payment: 'الدفع',
},
loadingVehicle: 'جاري تحميل المركبة...',
vehicleNotFound: 'المركبة غير موجودة.',
vehicleLoadError: 'تعذر تحميل تفاصيل المركبة.',
vehicleAvailableFrom: (date) => `يمكن حجز هذه المركبة ابتداءً من ${date}.`,
vehicleUnavailableNoDate: 'هذه المركبة غير متاحة مؤقتاً للحجوزات الجديدة.',
bookingOptionsLoadError: 'تعذر تحميل خيارات الحجز.',
startDate: 'تاريخ البداية',
endDate: 'تاريخ النهاية',
continueToDriverDetails: 'المتابعة إلى بيانات السائق ->',
firstName: 'الاسم الأول',
lastName: 'اسم العائلة',
email: 'البريد الإلكتروني',
phone: 'الهاتف',
driverLicense: 'رخصة القيادة',
nationality: 'الجنسية',
dateOfBirth: 'تاريخ الميلاد',
licenseIssued: 'تاريخ إصدار الرخصة',
licenseExpiry: 'تاريخ انتهاء الرخصة',
insuranceAndPickupPolicies: 'التأمين وسياسات الاستلام',
insuranceHint: 'اختر حماية اختيارية قبل تأكيد الحجز.',
requiredCoverage: 'التغطية المطلوبة',
none: 'لا يوجد',
noOptionalInsurance: 'لا توجد إضافات تأمين اختيارية متاحة لهذه الشركة.',
additionalDriver: 'سائق إضافي',
charge: 'الرسوم',
promoCode: 'رمز ترويجي',
enterCode: 'أدخل الرمز',
apply: 'تطبيق',
appliedPromo: (title, discount) => `✓ تم تطبيق "${title}" بخصم ${discount}%`,
back: '<- رجوع',
reviewBookingCta: 'مراجعة الحجز ->',
confirmReservation: 'تأكيد الحجز',
processing: 'جارٍ المعالجة...',
reservationCreated: '✓ تم إنشاء الحجز - اختر طريقة الدفع',
manualApprovalRequired: 'مراجعة رخصة القيادة مطلوبة قبل الدفع الإلكتروني. يمكنك التأكيد مع الدفع عند الاستلام أو انتظار موافقة الموظفين.',
totalDue: 'المبلغ المستحق',
currency: 'العملة',
paymentMethod: 'طريقة الدفع',
paymentMethods: {
amanpay: { title: 'AmanPay', description: 'دفع إلكتروني آمن عبر AmanPay' },
paypal: { title: 'PayPal', description: 'ادفع عبر حساب PayPal أو البطاقة' },
cash: { title: 'الدفع عند الاستلام', description: 'ادفع نقداً أو بالبطاقة عند استلام المركبة' },
},
redirectingToPayment: 'جارٍ التحويل إلى الدفع...',
confirmPayAtPickup: 'تأكيد - الدفع عند الاستلام',
payWith: (provider) => `ادفع عبر ${provider} ->`,
summary: {
vehicle: 'المركبة',
dates: 'التواريخ',
primaryDriver: 'السائق الرئيسي',
email: 'البريد الإلكتروني',
driverLicense: 'رخصة القيادة',
licenseExpiry: 'انتهاء الرخصة',
additionalDriver: 'سائق إضافي',
promoCode: 'الرمز الترويجي',
discount: (discount) => `الخصم (${discount}%)`,
total: 'الإجمالي',
},
validation: {
pickStartDate: 'اختر تاريخ البداية.',
pickEndDate: 'اختر تاريخ النهاية.',
endDateAfterStart: 'يجب أن يكون تاريخ النهاية بعد تاريخ البداية.',
firstNameRequired: 'الاسم الأول مطلوب.',
lastNameRequired: 'اسم العائلة مطلوب.',
validEmailRequired: 'البريد الإلكتروني الصحيح مطلوب.',
driverLicenseRequired: 'رخصة القيادة مطلوبة.',
dateOfBirthRequired: 'تاريخ الميلاد مطلوب.',
licenseExpiryRequired: 'تاريخ انتهاء الرخصة مطلوب.',
licenseIssueDateRequired: 'تاريخ إصدار الرخصة مطلوب.',
additionalDriverFirstNameRequired: 'الاسم الأول للسائق الإضافي مطلوب.',
additionalDriverLastNameRequired: 'اسم العائلة للسائق الإضافي مطلوب.',
additionalDriverLicenseRequired: 'رخصة السائق الإضافي مطلوبة.',
},
promo: {
invalid: 'الرمز الترويجي غير صالح أو منتهي.',
unavailable: 'تعذر التحقق من الرمز الترويجي. حاول مرة أخرى.',
},
submit: {
failed: 'فشل الحجز. حاول مرة أخرى.',
network: 'خطأ في الشبكة. تحقق من الاتصال ثم حاول مرة أخرى.',
},
payment: {
initiateFailed: 'تعذر بدء عملية الدفع.',
network: 'خطأ في الشبكة. حاول مرة أخرى.',
},
},
confirmation: {
eyebrow: 'تم حفظ الحجز',
title: 'تم استلام طلب الحجز.',
description: 'تم إنشاء الحجز بحالة مسودة وسيتم تأكيده من قبل الموظفين أو بعد الدفع.',
bookingSummary: 'ملخص الحجز',
vehicle: 'المركبة',
customer: 'العميل',
email: 'البريد الإلكتروني',
dates: 'التواريخ',
total: 'الإجمالي',
reference: 'المرجع',
status: 'الحالة',
browseMoreVehicles: 'تصفح المزيد من المركبات',
backHome: 'العودة للرئيسية',
},
},
}
export function getPublicSiteDictionary(language: PublicSiteLanguage): PublicSiteDictionary {
return dictionaries[language]
}