Waseel: driver capture, chat/calls, dispatch, and session fixes
Driver onboarding now photographs the licence, ID card and vehicle
registration and reads the credential fields off them, plus a camera-only
profile selfie riders check the arriving driver against. Adds in-app chat
and WebRTC calls, push-backed ride offers, ratings, cancellation and
payment sheets, settlement, and the owner dashboard endpoints behind them.
Camera permission on Android:
- Declare CAMERA and READ_MEDIA_IMAGES in the manifest. expo-image-picker's
own plugin never declares CAMERA, and Android denies a request for an
undeclared permission instantly and silently — no dialog is ever shown,
which is indistinguishable from the app not asking at all.
- Handle canAskAgain: once Android stops showing the dialog, repeating why
we need it is a dead end, so offer Open Settings instead (lib/capture-
permission.ts), matching what the location flow already did.
Session: a 401 on a request that carried a token now ends the session
instead of being reinterpreted per-screen — driver-home had been reading it
as "this user has no driver profile" and showing an onboarding form to an
already-onboarded driver. Requests without a token are exempt so a failed
sign-in doesn't sign you out, and the notification is latched per token so
concurrent polls tear the session down once. (root) gains the auth guard
that turns that into the sign-in screen; app/index.tsx only guarded the way
in, leaving a session that ended mid-screen with nowhere to go.
Also ignore .uploads/ — it holds driver licence, ID and vehicle scans plus
profile photos, which are personal data and must not be committed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1d84003e0a
commit
8807ff41c5
+314
-3
@@ -135,6 +135,9 @@ export const ar = {
|
||||
recentRides: "الرحلات الأخيرة",
|
||||
noRecent: "لا توجد رحلات حديثة.",
|
||||
noRecentAlt: "لا توجد رحلات حديثة",
|
||||
activeRideWithDriver: "{name} في الطريق",
|
||||
rateLastRide: "كيف كانت رحلتك الأخيرة؟",
|
||||
rate: "قيّم",
|
||||
},
|
||||
|
||||
rides: {
|
||||
@@ -155,7 +158,38 @@ export const ar = {
|
||||
title: "الدردشة",
|
||||
messageAlt: "رسالة",
|
||||
noMessages: "لا توجد رسائل بعد",
|
||||
startConversation: "ابدأ محادثة مع أصدقائك وعائلتك",
|
||||
startConversation: "تفتح المحادثة مع سائقك بمجرد مطابقة الرحلة.",
|
||||
inputPlaceholder: "رسالة…",
|
||||
send: "إرسال",
|
||||
loadError: "تعذّر تحميل الرسائل. اسحب للأسفل لإعادة المحاولة.",
|
||||
sendError: "تعذّر إرسال الرسالة. حاول مرة أخرى.",
|
||||
cannotMessage: "لم تعد هذه الرحلة نشطة.",
|
||||
call: "اتصال",
|
||||
},
|
||||
|
||||
call: {
|
||||
incoming: "مكالمة واردة",
|
||||
outgoing: "جارٍ الاتصال…",
|
||||
connecting: "جارٍ التوصيل…",
|
||||
inCall: "أثناء المكالمة",
|
||||
ended: "انتهت المكالمة",
|
||||
missed: "مكالمة فائتة",
|
||||
declined: "تم رفض المكالمة",
|
||||
failed: "فشل الاتصال",
|
||||
unavailable: "لا توجد رحلة نشطة للاتصال.",
|
||||
accept: "قبول",
|
||||
decline: "رفض",
|
||||
end: "إنهاء المكالمة",
|
||||
mute: "كتم",
|
||||
unmute: "إلغاء الكتم",
|
||||
speaker: "مكبر الصوت",
|
||||
speakerOff: "إيقاف مكبر الصوت",
|
||||
cancel: "إلغاء",
|
||||
connectingWith: "جارٍ الاتصال بـ {name}…",
|
||||
micDeniedTitle: "الميكروفون محظور",
|
||||
micDeniedBody:
|
||||
"يحتاج وسيط إلى ميكروفون لإجراء المكالمات. فعّله من الإعدادات.",
|
||||
audioFailed: "تعذّر بدء الصوت. حاول مرة أخرى.",
|
||||
},
|
||||
|
||||
profile: {
|
||||
@@ -169,14 +203,34 @@ export const ar = {
|
||||
emailPlaceholder: "بريدك الإلكتروني",
|
||||
},
|
||||
|
||||
adjustPin: {
|
||||
pickupLabel: "نقطة الصعود",
|
||||
destinationLabel: "نقطة النزول",
|
||||
locating: "جارٍ تحديد المكان…",
|
||||
hint: "حرّك الخريطة لوضع الدبوس في المكان الذي تريده تمامًا.",
|
||||
confirmPickup: "تأكيد نقطة الصعود",
|
||||
confirmDestination: "تأكيد نقطة النزول",
|
||||
recenter: "الذهاب إلى موقعي",
|
||||
},
|
||||
|
||||
findRide: {
|
||||
adjustOnMap: "حدّدها على الخريطة",
|
||||
title: "الرحلة",
|
||||
from: "من",
|
||||
to: "إلى",
|
||||
findNow: "ابحث الآن",
|
||||
service: "نوع الرحلة",
|
||||
nAvailable: "{n} قريب",
|
||||
estimatedFare: "الأجرة التقديرية",
|
||||
setBothPoints: "حدّد نقطة الانطلاق والوجهة",
|
||||
payLaterHint:
|
||||
"سيرى السائقون القريبون طلبك. أنت تختار من يأخذه، وتدفع بعد ذلك.",
|
||||
sending: "جارٍ إرسال طلبك…",
|
||||
},
|
||||
|
||||
confirmRide: {
|
||||
noDriversInRadius: "لا يوجد سائقو {service} ضمن {km} كم",
|
||||
tryInstead: "المتاح قربك الآن:",
|
||||
title: "طلب رحلة",
|
||||
yourTrip: "رحلتك",
|
||||
pickup: "نقطة الصعود",
|
||||
@@ -189,6 +243,13 @@ export const ar = {
|
||||
lbpEstimate: "≈ {lbp}",
|
||||
noDrivers: "لا يوجد سائقو {service} متصلون الآن",
|
||||
findingDrivers: "جارٍ البحث عن سائقين قريبين…",
|
||||
driversNearby: {
|
||||
one: "سائق واحد قريب",
|
||||
other: "{n} سائقين قريبين",
|
||||
zero: "لا سائقين قريبين",
|
||||
},
|
||||
withinRadius: "ضمن {km} كم منك",
|
||||
searchingRadius: "البحث ضمن {km} كم…",
|
||||
nearestDriver: "أقرب سائق ≈ {eta} دقيقة",
|
||||
requesting: "جارٍ الطلب…",
|
||||
noDriversOnline: "لا يوجد سائقون متصلون",
|
||||
@@ -202,20 +263,60 @@ export const ar = {
|
||||
alertErrorFallback: "حدث خطأ أثناء حجز رحلتك. حاول مرة أخرى.",
|
||||
alertPayCardTitle: "الدفع بالبطاقة",
|
||||
alertPayCardBody: "سيتم خصم ${fare} من بطاقتك.",
|
||||
alertInProgressTitle: "لديك رحلة جارية",
|
||||
alertInProgressBody: "لديك رحلة جارية. أنهِها أو ألغِها قبل حجز رحلة أخرى.",
|
||||
viewRide: "عرض الرحلة",
|
||||
},
|
||||
|
||||
bookRide: {
|
||||
status: {
|
||||
requested: "جارٍ البحث عن سائقك…",
|
||||
choosing: "اختر سائقك",
|
||||
accepted: "تم تعيين سائق — في طريقه إليك",
|
||||
enRoute: "أنت في الرحلة",
|
||||
completed: "وصلت!",
|
||||
cancelled: "تم إلغاء الرحلة",
|
||||
arrived: "سائقك وصل",
|
||||
expired: "لا يوجد سائق متاح",
|
||||
},
|
||||
rideNotFound: "الرحلة غير موجودة.",
|
||||
couldNotLoad: "تعذّر تحميل هذه الرحلة.",
|
||||
backHome: "العودة للرئيسية",
|
||||
matchingDriver: "نطابقك مع أقرب سائق {service}.",
|
||||
searchingFor: "جارٍ البحث منذ {seconds} ثانية",
|
||||
match: {
|
||||
driverFallback: "سائقك",
|
||||
alertBody: "حدث خطأ ما. حاول مرة أخرى.",
|
||||
},
|
||||
offers: {
|
||||
title: "سائقون متاحون",
|
||||
count: {
|
||||
one: "عرض واحد",
|
||||
other: "{n} عروض",
|
||||
zero: "لا عروض بعد",
|
||||
},
|
||||
away: "{eta} دقيقة · {distance}",
|
||||
seats: {
|
||||
one: "مقعد واحد",
|
||||
other: "{n} مقاعد",
|
||||
zero: "",
|
||||
},
|
||||
pick: "اختر",
|
||||
goneTitle: "لم يعد هذا السائق متاحًا",
|
||||
goneBody: "ارتبط برحلة أخرى. اختر سائقًا آخر من القائمة.",
|
||||
goneBodyPaid:
|
||||
"ارتبط برحلة أخرى. لم يُستخدم دفعك — اختر سائقًا آخر وسيذهب المبلغ إليه.",
|
||||
},
|
||||
payment: {
|
||||
title: "كيف تريد الدفع؟",
|
||||
titleNamed: "رحلة مع {name}",
|
||||
subtitle: "الأجرة ${fare}",
|
||||
cash: "الدفع نقدًا",
|
||||
cashHint: "سلّم الأجرة للسائق عند الوصول.",
|
||||
card: "الدفع بالبطاقة",
|
||||
cardHint: "يُخصم الآن، قبل انطلاق السائق.",
|
||||
working: "جارٍ التنفيذ…",
|
||||
},
|
||||
ratingFallback: "—",
|
||||
paymentCash: "💵 نقدًا للسائق",
|
||||
paymentCard: "💳 مدفوع بالبطاقة",
|
||||
@@ -226,10 +327,25 @@ export const ar = {
|
||||
cancelling: "جارٍ الإلغاء…",
|
||||
alertErrorTitle: "خطأ",
|
||||
alertErrorBody: "تعذّر إلغاء هذه الرحلة. حاول مرة أخرى.",
|
||||
pickupCodeLabel: "رمز الصعود",
|
||||
pickupCodeHint: "أعطِ هذا الرمز للسائق لبدء الرحلة.",
|
||||
driverHere: "سائقك في الخارج",
|
||||
cashDue: "ادفع ${amount} نقدًا للسائق.",
|
||||
youRated: "قيّمت هذه الرحلة {n}★",
|
||||
rateDriver: "قيّم سائقك",
|
||||
noDriversFound:
|
||||
"لم يقبل أي سائق طلبك. لم يتم خصم أي مبلغ — حاول مرة أخرى بعد قليل.",
|
||||
cancelledByDriver: "ألغى السائق هذه الرحلة.",
|
||||
enRouteNotice: "رحلة سعيدة — سينهي السائق الرحلة عند الوصول.",
|
||||
},
|
||||
|
||||
driver: {
|
||||
home: {
|
||||
owesCompany: "العمولة المستحقة عليك",
|
||||
owesCompanyHint: "من رحلاتك النقدية — سلّمها في المكتب.",
|
||||
owedToDriver: "الشركة مدينة لك",
|
||||
owedToDriverHint: "رحلات البطاقة، تُدفع لك.",
|
||||
afterFee: "بعد عمولة ${fee}",
|
||||
signOutAlt: "تسجيل الخروج",
|
||||
driverMode: "وضع السائق",
|
||||
online: "● متصل — يتلقّى طلبات الرحلات",
|
||||
@@ -238,6 +354,7 @@ export const ar = {
|
||||
completedToday: "المكتملة اليوم",
|
||||
incomingRequests: "الطلبات الواردة",
|
||||
incomingRequestsOffline: "الطلبات الواردة (غير متصل)",
|
||||
finishCurrentRide: "أنهِ رحلتك الحالية لرؤية الطلبات الجديدة.",
|
||||
waitingRequests: "بانتظار طلبات الرحلات…",
|
||||
goOnlineStart: "اتصل لبدء القيادة.",
|
||||
welcome: "أهلاً {name}",
|
||||
@@ -256,35 +373,216 @@ export const ar = {
|
||||
alertCreateBody: "تعذّر إنشاء ملف السائق. حاول مرة أخرى.",
|
||||
alertToggleBody: "تعذّر تغيير حالتك. حاول مرة أخرى.",
|
||||
noRequestsAlt: "لا توجد رحلات حديثة",
|
||||
cashInHand: "النقد المحصّل اليوم",
|
||||
uncollected: "أجرة غير محصّلة اليوم",
|
||||
ratingCount: "{n} تقييم",
|
||||
ratingNew: "سائق جديد",
|
||||
alertOfflineBlocked: "أنهِ رحلتك الحالية أو ألغِها قبل قطع الاتصال.",
|
||||
},
|
||||
credentials: {
|
||||
title: "أوراقك الثبوتية",
|
||||
intro:
|
||||
"ندقّق في أوراق كل سائق قبل أن يبدأ العمل. سيراجع فريقنا هذه المعلومات.",
|
||||
licenseNumber: "رقم رخصة السوق",
|
||||
licenseNumberPlaceholder: "كما هو مدوّن على الرخصة",
|
||||
licenseExpiry: "تاريخ انتهاء الرخصة",
|
||||
nationalId: "رقم الهوية",
|
||||
nationalIdPlaceholder: "رقم بطاقة الهوية",
|
||||
plateNumber: "رقم اللوحة",
|
||||
plateNumberPlaceholder: "مثال: 123456/B",
|
||||
reviewNote:
|
||||
"يبقى حسابك غير متصل إلى أن تتم الموافقة عليه، وعادةً ما يستغرق ذلك أقل من يوم.",
|
||||
submit: "إرسال للمراجعة",
|
||||
errorTitle: "راجع معلوماتك",
|
||||
errorMissing: "رقم الرخصة ورقم الهوية ورقم اللوحة كلها مطلوبة.",
|
||||
errorExpiryFormat: "أدخل تاريخ انتهاء الرخصة بصيغة YYYY-MM-DD.",
|
||||
errorExpired: "هذه الرخصة منتهية الصلاحية.",
|
||||
errorScanRequired: "صوّر رخصة السوق قبل الإرسال.",
|
||||
alertResubmitBody: "تعذّر إعادة إرسال معلوماتك. حاول مرة أخرى.",
|
||||
},
|
||||
captureUnavailable:
|
||||
"التقاط الصور غير متوفّر في هذه النسخة من التطبيق. حدّث التطبيق إلى آخر إصدار وحاول مجددًا.",
|
||||
photo: {
|
||||
title: "صورتك",
|
||||
hint: "تُلتقط الآن بالكاميرا، لا من معرض الصور. يراها الركاب بجانب اسمك عند اختيار السائق، ويتأكدون بها أنك أنت عند نقطة الانطلاق. انظر إلى الكاميرا في إضاءة جيدة.",
|
||||
take: "التقط صورة",
|
||||
retake: "أعد الالتقاط",
|
||||
required: "التقط صورة شخصية قبل الإرسال.",
|
||||
permissionTitle: "الإذن مطلوب",
|
||||
permissionCamera:
|
||||
"يحتاج وصيل إلى الكاميرا لالتقاط صورتك. اسمح بالوصول إلى الكاميرا للمتابعة.",
|
||||
permissionCameraBlocked:
|
||||
"الوصول إلى الكاميرا معطّل لتطبيق وصيل، ولن يسألك أندرويد مرة أخرى من هنا. افتح الإعدادات ثم فعّل «الكاميرا» ضمن الأذونات.",
|
||||
errorTitle: "تعذّر حفظ الصورة",
|
||||
errorBody: "حدث خطأ ما. حاول مرة أخرى.",
|
||||
errorTooLarge: "الصورة كبيرة جدًا. جرّب التقاط صورة جديدة.",
|
||||
errorRateLimit: "عدد المحاولات كبير. انتظر بضع دقائق وحاول مجددًا.",
|
||||
errorUnsupported: "استخدم صورة بصيغة JPEG أو PNG أو WebP.",
|
||||
},
|
||||
scan: {
|
||||
licenseLabel: "رخصة السوق",
|
||||
licenseHint:
|
||||
"ضعها على سطح مستوٍ واملأ بها الإطار. نقرأ منها الرقم وتاريخ الانتهاء.",
|
||||
idLabel: "بطاقة الهوية",
|
||||
idHint: "الوجه الذي يظهر فيه رقم الهوية.",
|
||||
vehicle_regLabel: "رخصة سير السيارة",
|
||||
vehicle_regHint: "الصفحة التي يظهر فيها رقم اللوحة ونوع السيارة.",
|
||||
optional: "اختياري",
|
||||
take: "التقط صورة",
|
||||
retake: "أعد التصوير",
|
||||
choose: "اختر صورة",
|
||||
reading: "نقرأ المستند…",
|
||||
filled: {
|
||||
one: "عبّأنا معلومة واحدة — راجعها أدناه.",
|
||||
other: "عبّأنا {n} معلومات — راجعها أدناه.",
|
||||
},
|
||||
savedNoFields:
|
||||
"حفظنا الصورة، لكن تعذّرت قراءة المعلومات. أدخلها يدويًا أدناه.",
|
||||
savedUnreadable:
|
||||
"حفظنا الصورة. خدمة القراءة غير متوفّرة حاليًا — أدخل المعلومات يدويًا أدناه.",
|
||||
alreadyOnFile: "لدينا صورة محفوظة مسبقًا. أعد التصوير عند الحاجة فقط.",
|
||||
allRead: "قرأناها من أوراقك",
|
||||
missingPrompt:
|
||||
"تعذّرت قراءة هذه المعلومات من أوراقك. أضفها وينتهي الأمر.",
|
||||
edit: "راجع المعلومات أو عدّلها",
|
||||
done: "تم",
|
||||
checkPrompt: "صحّح أي معلومة قُرئت خطأً، ثم اضغط تم.",
|
||||
permissionTitle: "الإذن مطلوب",
|
||||
permissionCamera:
|
||||
"اسمح بالوصول إلى الكاميرا لتصوير أوراقك، أو اختر صورة موجودة بدلًا من ذلك.",
|
||||
permissionCameraBlocked:
|
||||
"الوصول إلى الكاميرا معطّل لتطبيق وصيل، ولن يسألك أندرويد مرة أخرى من هنا. افتح الإعدادات وفعّل «الكاميرا» ضمن الأذونات، أو اختر صورة موجودة بدلًا من ذلك.",
|
||||
permissionLibrary: "اسمح بالوصول إلى الصور لاختيار صورة لأوراقك.",
|
||||
permissionLibraryBlocked:
|
||||
"الوصول إلى الصور معطّل لتطبيق وصيل، ولن يسألك أندرويد مرة أخرى من هنا. افتح الإعدادات وفعّل «الصور» ضمن الأذونات، أو التقط صورة بالكاميرا بدلًا من ذلك.",
|
||||
errorTitle: "تعذّرت القراءة",
|
||||
errorBody: "حدث خطأ ما. حاول مجددًا أو أدخل المعلومات يدويًا أدناه.",
|
||||
errorTooLarge: "الصورة كبيرة جدًا. جرّب التقاط صورة جديدة.",
|
||||
errorRateLimit: "عدد المحاولات كبير. انتظر بضع دقائق وحاول مجددًا.",
|
||||
errorUnsupported: "استخدم صورة بصيغة JPEG أو PNG أو WebP.",
|
||||
errorRetry: "لم يتم الرفع. حاول مجددًا أو أدخل المعلومات يدويًا أدناه.",
|
||||
},
|
||||
review: {
|
||||
pendingTitle: "قيد المراجعة",
|
||||
pendingBody:
|
||||
"نراجع معلوماتك الآن. ستتمكّن من الاتصال فور الموافقة على حسابك.",
|
||||
rejectedTitle: "لم تتم الموافقة",
|
||||
rejectedBody: "لم تجتز معلوماتك المراجعة. صحّحها أدناه وأعد إرسالها.",
|
||||
suspendedTitle: "الحساب موقوف",
|
||||
suspendedBody:
|
||||
"تم إيقاف حساب السائق الخاص بك. تواصل مع الدعم لمعالجة الأمر.",
|
||||
approvedTitle: "تمت الموافقة",
|
||||
approvedBody: "أنت جاهز للعمل.",
|
||||
reasonLabel: "السبب",
|
||||
checkAgain: "تحقّق مجددًا",
|
||||
resubmitTitle: "صحّح معلوماتك",
|
||||
resubmitIntro: "صحّح ما هو خاطئ وسنراجعه من جديد.",
|
||||
resubmit: "إعادة الإرسال",
|
||||
},
|
||||
offerCard: {
|
||||
youEarn: "أرباحك",
|
||||
newRequest: "طلب جديد · {service}",
|
||||
openFor: "متاح لمدة",
|
||||
seconds: "{n} ث",
|
||||
awayFromPickup: "{km} كم عن نقطة الصعود",
|
||||
firstIn: "ستكون الأول",
|
||||
rivals: {
|
||||
one: "سائق آخر تقدّم",
|
||||
other: "{n} سائقين آخرين تقدّموا",
|
||||
zero: "ستكون الأول",
|
||||
},
|
||||
cash: "💵 نقدًا",
|
||||
card: "💳 بطاقة",
|
||||
fromAlt: "من",
|
||||
toAlt: "إلى",
|
||||
tripTime: "زمن الرحلة",
|
||||
fare: "الأجرة",
|
||||
decline: "رفض",
|
||||
accept: "قبول",
|
||||
offer: "تقدّم لهذه الرحلة",
|
||||
withdraw: "سحب عرضي",
|
||||
waitingOnRider: "تم التقديم — بانتظار اختيار الراكب",
|
||||
lostTitle: "أُغلق الطلب",
|
||||
lostBody:
|
||||
"اختار الراكب سائقًا آخر، أو انتهت مهلة الطلب. أنت متاح للطلب التالي.",
|
||||
alertOfferBody: "تعذّر إرسال عرضك. حاول مرة أخرى.",
|
||||
alertWithdrawBody: "تعذّر سحب عرضك. حاول مرة أخرى.",
|
||||
},
|
||||
activeRide: {
|
||||
youEarn: "أرباحك",
|
||||
headToPickup: "اتجه إلى نقطة الصعود",
|
||||
tripInProgress: "الرحلة جارية",
|
||||
rider: "{name}",
|
||||
pickupPin: "موقع صعود الراكب",
|
||||
dropoffPin: "نقطة النزول",
|
||||
navigateToPickup: "التوجّه إلى نقطة الصعود",
|
||||
navigateToDropoff: "التوجّه إلى نقطة النزول",
|
||||
alertNavigateBody: "تعذّر فتح تطبيق ملاحة على هذا الهاتف.",
|
||||
fromAlt: "من",
|
||||
toAlt: "إلى",
|
||||
fare: "الأجرة",
|
||||
message: "رسالة",
|
||||
call: "اتصال",
|
||||
startTrip: "ابدأ الرحلة",
|
||||
completeTrip: "أنهِ الرحلة",
|
||||
cancelRide: "إلغاء الرحلة",
|
||||
cancelConfirmTitle: "إلغاء هذه الرحلة؟",
|
||||
cancelConfirmBody: "سيتم إخطار الراكب وستُعلَّم الرحلة كملغاة.",
|
||||
cancelConfirmDismiss: "الاحتفاظ بالرحلة",
|
||||
cancelConfirmConfirm: "إلغاء الرحلة",
|
||||
alertErrorTitle: "خطأ",
|
||||
alertAcceptBody: "تعذّر قبول هذه الرحلة. ربما حُجزت أو انتهت صلاحيتها.",
|
||||
alertDeclineBody: "تعذّر رفض هذه الرحلة. حاول مرة أخرى.",
|
||||
alertUpdateBody: "تعذّر تحديث الرحلة. حاول مرة أخرى.",
|
||||
alertCancelBody: "تعذّر إلغاء الرحلة. حاول مرة أخرى.",
|
||||
imHere: "لقد وصلت",
|
||||
atPickup: "عند نقطة الصعود — بانتظار الراكب",
|
||||
askForCode: "اطلب من الراكب رمز الصعود المكوّن من 4 أرقام.",
|
||||
cashConfirmTitle: "تحصيل الأجرة",
|
||||
cashConfirmBody: "هل حصّلت ${amount} نقدًا من الراكب؟",
|
||||
cashCollected: "نعم، حصّلتها",
|
||||
cashNotCollected: "لم أحصّلها",
|
||||
},
|
||||
},
|
||||
|
||||
rating: {
|
||||
rateDriverTitle: "كيف كانت رحلتك مع {name}؟",
|
||||
rateRiderTitle: "كيف كان {name} كراكب؟",
|
||||
subtitle: "تقييمك يبقى خاصًا ولا يُعرض للطرف الآخر.",
|
||||
starLabel: "{n} نجوم",
|
||||
commentPlaceholder: "أضف تعليقًا (اختياري)",
|
||||
submit: "إرسال التقييم",
|
||||
notNow: "ليس الآن",
|
||||
error: "تعذّر إرسال تقييمك. حاول مرة أخرى.",
|
||||
},
|
||||
|
||||
cancelSheet: {
|
||||
title: "إلغاء هذه الرحلة؟",
|
||||
subtitleRider: "أخبرنا بالسبب لتحسين المطابقة.",
|
||||
subtitleDriver: "سيتم إخطار الراكب وستُعلَّم الرحلة كملغاة.",
|
||||
confirm: "إلغاء الرحلة",
|
||||
keepRide: "الاحتفاظ بالرحلة",
|
||||
cancelling: "جارٍ الإلغاء…",
|
||||
reasons: {
|
||||
changed_mind: "غيّرت رأيي",
|
||||
wait_too_long: "الانتظار طويل جدًا",
|
||||
wrong_address: "عنوان صعود خاطئ",
|
||||
driver_no_show: "السائق لم يصل",
|
||||
rider_no_show: "الراكب لم يحضر",
|
||||
unreachable: "تعذّر التواصل معه",
|
||||
vehicle_issue: "مشكلة في المركبة",
|
||||
other: "سبب آخر",
|
||||
},
|
||||
},
|
||||
|
||||
pickupCode: {
|
||||
title: "ابدأ الرحلة",
|
||||
subtitle: "أدخل الرمز المكوّن من 4 أرقام من شاشة الراكب.",
|
||||
startTrip: "ابدأ الرحلة",
|
||||
wrongCode: "الرمز غير مطابق. تحقّق مع الراكب.",
|
||||
},
|
||||
|
||||
services: {
|
||||
nearbyCount: "{n} قريب",
|
||||
noneNearby: "لا يوجد قريب",
|
||||
car: { label: "سيارة", tagline: "رحلة يومية، حتى 4 مقاعد." },
|
||||
moto: { label: "موتور", tagline: "تجنّب الزحام — راكب واحد، بدون أمتعة." },
|
||||
courier: {
|
||||
@@ -374,6 +672,16 @@ export const ar = {
|
||||
"الخريطة غير متاحة على الويب.\nافتح التطبيق على أندرويد أو iOS للتجربة الكاملة.",
|
||||
},
|
||||
rideCard: {
|
||||
outcomeCompleted: "مكتملة",
|
||||
outcomeCancelled: "ملغاة",
|
||||
outcomeExpired: "لم يتم العثور على سائق",
|
||||
cancelledByYou: "ألغيتها",
|
||||
cancelledByDriver: "ألغاها السائق",
|
||||
cancelledBySystem: "لا يوجد سائق متاح",
|
||||
noDriver: "لم يُعيَّن سائق",
|
||||
paymentNotCharged: "لم يتم الخصم",
|
||||
paymentRefundDue: "مستحق الاسترداد",
|
||||
paymentCashCollected: "دُفعت نقدًا",
|
||||
mapAlt: "خريطة",
|
||||
originAlt: "المصدر",
|
||||
destinationAlt: "الوجهة",
|
||||
@@ -470,6 +778,9 @@ export const ar = {
|
||||
rtlRestartBody:
|
||||
"سيُطبَّق التخطيط العربي بالكامل في المرة القادمة التي تفتح فيها التطبيق.",
|
||||
},
|
||||
general: {
|
||||
title: "عام",
|
||||
},
|
||||
keepAwake: {
|
||||
title: "عدم قفل الشاشة",
|
||||
description: "إبقاء الشاشة مضاءة أثناء فتح التطبيق.",
|
||||
|
||||
+322
-3
@@ -136,6 +136,9 @@ export const en = {
|
||||
recentRides: "Recent Rides",
|
||||
noRecent: "No recent rides found.",
|
||||
noRecentAlt: "No recent rides found",
|
||||
activeRideWithDriver: "{name} is on the way",
|
||||
rateLastRide: "How was your last ride?",
|
||||
rate: "Rate",
|
||||
},
|
||||
|
||||
rides: {
|
||||
@@ -156,7 +159,38 @@ export const en = {
|
||||
title: "Chat",
|
||||
messageAlt: "message",
|
||||
noMessages: "No Messages Yet",
|
||||
startConversation: "Start a conversation with your friends and family",
|
||||
startConversation: "Messages open with your driver once a ride is matched.",
|
||||
inputPlaceholder: "Message…",
|
||||
send: "Send",
|
||||
loadError: "Couldn't load messages. Pull to retry.",
|
||||
sendError: "Couldn't send message. Try again.",
|
||||
cannotMessage: "This ride is no longer active.",
|
||||
call: "Call",
|
||||
},
|
||||
|
||||
call: {
|
||||
incoming: "Incoming call",
|
||||
outgoing: "Calling…",
|
||||
connecting: "Connecting…",
|
||||
inCall: "In call",
|
||||
ended: "Call ended",
|
||||
missed: "Missed call",
|
||||
declined: "Call declined",
|
||||
failed: "Call failed",
|
||||
unavailable: "No active ride to call.",
|
||||
accept: "Accept",
|
||||
decline: "Decline",
|
||||
end: "End call",
|
||||
mute: "Mute",
|
||||
unmute: "Unmute",
|
||||
speaker: "Speaker",
|
||||
speakerOff: "Speaker off",
|
||||
cancel: "Cancel",
|
||||
connectingWith: "Connecting with {name}…",
|
||||
micDeniedTitle: "Microphone blocked",
|
||||
micDeniedBody:
|
||||
"Waseel needs microphone access to make calls. Enable it in Settings.",
|
||||
audioFailed: "Couldn't start audio. Please try again.",
|
||||
},
|
||||
|
||||
profile: {
|
||||
@@ -170,14 +204,34 @@ export const en = {
|
||||
emailPlaceholder: "Your Email address",
|
||||
},
|
||||
|
||||
adjustPin: {
|
||||
pickupLabel: "Pickup point",
|
||||
destinationLabel: "Drop-off point",
|
||||
locating: "Finding this place…",
|
||||
hint: "Drag the map to move the pin exactly where you want it.",
|
||||
confirmPickup: "Confirm pickup point",
|
||||
confirmDestination: "Confirm drop-off point",
|
||||
recenter: "Go to my location",
|
||||
},
|
||||
|
||||
findRide: {
|
||||
adjustOnMap: "Set it on the map",
|
||||
title: "Ride",
|
||||
from: "From",
|
||||
to: "To",
|
||||
findNow: "Find now",
|
||||
service: "Ride type",
|
||||
nAvailable: "{n} nearby",
|
||||
estimatedFare: "Estimated fare",
|
||||
setBothPoints: "Set a pickup and destination",
|
||||
payLaterHint:
|
||||
"Drivers nearby will see your request. You choose who takes it, and pay after.",
|
||||
sending: "Sending your request…",
|
||||
},
|
||||
|
||||
confirmRide: {
|
||||
noDriversInRadius: "No {service} drivers within {km} km",
|
||||
tryInstead: "Available near you right now:",
|
||||
title: "Request Ride",
|
||||
yourTrip: "Your trip",
|
||||
pickup: "Pickup",
|
||||
@@ -190,6 +244,13 @@ export const en = {
|
||||
lbpEstimate: "≈ {lbp}",
|
||||
noDrivers: "No {service} drivers online right now",
|
||||
findingDrivers: "Finding drivers nearby…",
|
||||
driversNearby: {
|
||||
one: "1 driver nearby",
|
||||
other: "{n} drivers nearby",
|
||||
zero: "No drivers nearby",
|
||||
},
|
||||
withinRadius: "Within {km} km of you",
|
||||
searchingRadius: "Searching within {km} km…",
|
||||
nearestDriver: "Nearest driver ≈ {eta} min away",
|
||||
requesting: "Requesting…",
|
||||
noDriversOnline: "No drivers online",
|
||||
@@ -204,20 +265,61 @@ export const en = {
|
||||
"Something went wrong while booking your ride. Please try again.",
|
||||
alertPayCardTitle: "Pay by card",
|
||||
alertPayCardBody: "Your card will be charged ${fare}.",
|
||||
alertInProgressTitle: "Ride already in progress",
|
||||
alertInProgressBody:
|
||||
"You have a ride in progress. Finish or cancel it before booking another.",
|
||||
viewRide: "View ride",
|
||||
},
|
||||
|
||||
bookRide: {
|
||||
status: {
|
||||
requested: "Finding your driver…",
|
||||
choosing: "Choose your driver",
|
||||
accepted: "Driver assigned — heading to you",
|
||||
enRoute: "On your trip",
|
||||
completed: "You've arrived!",
|
||||
cancelled: "Ride cancelled",
|
||||
arrived: "Your driver is here",
|
||||
expired: "No driver available",
|
||||
},
|
||||
rideNotFound: "Ride not found.",
|
||||
couldNotLoad: "Could not load this ride.",
|
||||
backHome: "Back Home",
|
||||
matchingDriver: "We're matching you with the nearest {service} driver.",
|
||||
searchingFor: "Searching for {seconds}s",
|
||||
match: {
|
||||
driverFallback: "Your driver",
|
||||
alertBody: "Something went wrong. Please try again.",
|
||||
},
|
||||
offers: {
|
||||
title: "Drivers available",
|
||||
count: {
|
||||
one: "1 offer",
|
||||
other: "{n} offers",
|
||||
zero: "No offers yet",
|
||||
},
|
||||
away: "{eta} min away · {distance}",
|
||||
seats: {
|
||||
one: "1 seat",
|
||||
other: "{n} seats",
|
||||
zero: "",
|
||||
},
|
||||
pick: "Choose",
|
||||
goneTitle: "That driver is gone",
|
||||
goneBody: "They took another ride. Pick someone else from the list.",
|
||||
goneBodyPaid:
|
||||
"They took another ride. Your payment hasn't been used — pick someone else and it will go to them.",
|
||||
},
|
||||
payment: {
|
||||
title: "How would you like to pay?",
|
||||
titleNamed: "Ride with {name}",
|
||||
subtitle: "Fare ${fare}",
|
||||
cash: "Pay cash",
|
||||
cashHint: "Hand the fare to your driver at drop-off.",
|
||||
card: "Pay by card",
|
||||
cardHint: "Charged now, before your driver sets off.",
|
||||
working: "Working…",
|
||||
},
|
||||
ratingFallback: "—",
|
||||
paymentCash: "💵 Cash to driver",
|
||||
paymentCard: "💳 Paid by card",
|
||||
@@ -228,10 +330,26 @@ export const en = {
|
||||
cancelling: "Cancelling…",
|
||||
alertErrorTitle: "Error",
|
||||
alertErrorBody: "Could not cancel this ride. Please try again.",
|
||||
pickupCodeLabel: "Your pickup code",
|
||||
pickupCodeHint: "Give this to your driver to start the trip.",
|
||||
driverHere: "Your driver is outside",
|
||||
cashDue: "Pay ${amount} in cash to your driver.",
|
||||
youRated: "You rated this ride {n}★",
|
||||
rateDriver: "Rate your driver",
|
||||
noDriversFound:
|
||||
"No driver picked up your request. Nothing was charged — try again in a moment.",
|
||||
cancelledByDriver: "Your driver cancelled this ride.",
|
||||
enRouteNotice:
|
||||
"Enjoy your ride — your driver will end the trip on arrival.",
|
||||
},
|
||||
|
||||
driver: {
|
||||
home: {
|
||||
owesCompany: "Commission you owe",
|
||||
owesCompanyHint: "Your cash rides — hand this in at the office.",
|
||||
owedToDriver: "The company owes you",
|
||||
owedToDriverHint: "Card rides, paid out to you.",
|
||||
afterFee: "after ${fee} platform fee",
|
||||
signOutAlt: "Sign out",
|
||||
driverMode: "Driver mode",
|
||||
online: "● Online — receiving ride requests",
|
||||
@@ -240,6 +358,7 @@ export const en = {
|
||||
completedToday: "Completed today",
|
||||
incomingRequests: "Incoming requests",
|
||||
incomingRequestsOffline: "Incoming requests (offline)",
|
||||
finishCurrentRide: "Finish your current ride to see new requests.",
|
||||
waitingRequests: "Waiting for ride requests…",
|
||||
goOnlineStart: "Go online to start driving.",
|
||||
welcome: "Welcome, {name}",
|
||||
@@ -260,36 +379,223 @@ export const en = {
|
||||
"Could not create your driver profile. Please try again.",
|
||||
alertToggleBody: "Could not change your status. Please try again.",
|
||||
noRequestsAlt: "No recent rides found",
|
||||
cashInHand: "Cash collected today",
|
||||
uncollected: "Uncollected fares today",
|
||||
ratingCount: "{n} ratings",
|
||||
ratingNew: "New driver",
|
||||
alertOfflineBlocked:
|
||||
"Finish or cancel your current ride before going offline.",
|
||||
},
|
||||
credentials: {
|
||||
title: "Your credentials",
|
||||
intro:
|
||||
"We check every driver before they can take a ride. These details are reviewed by our team.",
|
||||
licenseNumber: "Driving licence number",
|
||||
licenseNumberPlaceholder: "As printed on your licence",
|
||||
licenseExpiry: "Licence expiry",
|
||||
nationalId: "National ID number",
|
||||
nationalIdPlaceholder: "Your ID card number",
|
||||
plateNumber: "Plate number",
|
||||
plateNumberPlaceholder: "e.g. 123456/B",
|
||||
reviewNote:
|
||||
"Your account stays offline until a reviewer approves it. This usually takes less than a day.",
|
||||
submit: "Submit for review",
|
||||
errorTitle: "Check your details",
|
||||
errorMissing:
|
||||
"Licence number, national ID and plate number are all required.",
|
||||
errorExpiryFormat: "Enter the licence expiry as YYYY-MM-DD.",
|
||||
errorExpired: "That licence has already expired.",
|
||||
errorScanRequired: "Scan your driving licence before submitting.",
|
||||
alertResubmitBody: "Could not resubmit your details. Please try again.",
|
||||
},
|
||||
captureUnavailable:
|
||||
"Photo capture isn't available in this version of the app. Please update to the latest version and try again.",
|
||||
photo: {
|
||||
title: "Your photo",
|
||||
hint: "Taken now with the camera, not from your gallery. Riders see it next to your name when they pick a driver, and use it to check it's you at pickup. Face the camera in good light.",
|
||||
take: "Take photo",
|
||||
retake: "Retake",
|
||||
required: "Take a profile photo before submitting.",
|
||||
permissionTitle: "Permission needed",
|
||||
permissionCamera:
|
||||
"Waseel needs the camera to take your photo. Allow camera access to continue.",
|
||||
permissionCameraBlocked:
|
||||
"Camera access is turned off for Waseel, and Android won't ask again from here. Open Settings, then turn on Camera under Permissions.",
|
||||
errorTitle: "Couldn't save that photo",
|
||||
errorBody: "Something went wrong. Please try again.",
|
||||
errorTooLarge: "That photo is too large. Try taking a new one.",
|
||||
errorRateLimit: "Too many uploads. Wait a few minutes and try again.",
|
||||
errorUnsupported: "Use a JPEG, PNG or WebP photo.",
|
||||
},
|
||||
scan: {
|
||||
licenseLabel: "Driving licence",
|
||||
licenseHint:
|
||||
"Lay it flat and fill the frame. We read the number and expiry off it.",
|
||||
idLabel: "ID card",
|
||||
idHint: "The side showing your ID number.",
|
||||
vehicle_regLabel: "Vehicle registration",
|
||||
vehicle_regHint: "The page showing the plate number and the car model.",
|
||||
optional: "Optional",
|
||||
take: "Take photo",
|
||||
retake: "Rescan",
|
||||
choose: "Choose photo",
|
||||
reading: "Reading your document…",
|
||||
filled: {
|
||||
one: "Filled in 1 detail — check it below.",
|
||||
other: "Filled in {n} details — check them below.",
|
||||
},
|
||||
savedNoFields:
|
||||
"Photo saved, but we couldn't read the details. Type them in below.",
|
||||
savedUnreadable:
|
||||
"Photo saved. Scanning is unavailable right now — type the details in below.",
|
||||
alreadyOnFile: "A scan is already on file. Rescan only if you need to.",
|
||||
allRead: "Read from your documents",
|
||||
missingPrompt:
|
||||
"We couldn't read these off your documents. Please add them and you're done.",
|
||||
edit: "Check or edit details",
|
||||
done: "Done",
|
||||
checkPrompt: "Correct anything that was read wrongly, then tap Done.",
|
||||
permissionTitle: "Permission needed",
|
||||
permissionCamera:
|
||||
"Allow camera access to photograph your documents, or choose a photo instead.",
|
||||
permissionCameraBlocked:
|
||||
"Camera access is turned off for Waseel, and Android won't ask again from here. Open Settings and turn on Camera under Permissions — or choose an existing photo instead.",
|
||||
permissionLibrary:
|
||||
"Allow photo access to pick a picture of your documents.",
|
||||
permissionLibraryBlocked:
|
||||
"Photo access is turned off for Waseel, and Android won't ask again from here. Open Settings and turn on Photos under Permissions — or take a photo with the camera instead.",
|
||||
errorTitle: "Couldn't scan that",
|
||||
errorBody:
|
||||
"Something went wrong. Try again, or type the details in below.",
|
||||
errorTooLarge: "That photo is too large. Try taking a new one.",
|
||||
errorRateLimit: "Too many scans. Wait a few minutes and try again.",
|
||||
errorUnsupported: "Use a JPEG, PNG or WebP photo.",
|
||||
errorRetry: "Not uploaded. Try again, or type the details in below.",
|
||||
},
|
||||
review: {
|
||||
pendingTitle: "Under review",
|
||||
pendingBody:
|
||||
"We're checking your details. You'll be able to go online as soon as you're approved.",
|
||||
rejectedTitle: "Not approved",
|
||||
rejectedBody:
|
||||
"Your details didn't pass review. Correct them below and submit again.",
|
||||
suspendedTitle: "Account suspended",
|
||||
suspendedBody:
|
||||
"Your driver account has been suspended. Contact support to sort this out.",
|
||||
approvedTitle: "Approved",
|
||||
approvedBody: "You're cleared to drive.",
|
||||
reasonLabel: "Reason",
|
||||
checkAgain: "Check again",
|
||||
resubmitTitle: "Correct your details",
|
||||
resubmitIntro: "Fix what's wrong and we'll review it again.",
|
||||
resubmit: "Submit again",
|
||||
},
|
||||
offerCard: {
|
||||
youEarn: "You earn",
|
||||
newRequest: "New request · {service}",
|
||||
openFor: "On the board for",
|
||||
seconds: "{n}s",
|
||||
awayFromPickup: "{km} km from pickup",
|
||||
firstIn: "You'd be first",
|
||||
rivals: {
|
||||
one: "1 other driver offered",
|
||||
other: "{n} other drivers offered",
|
||||
zero: "You'd be first",
|
||||
},
|
||||
cash: "💵 Cash",
|
||||
card: "💳 Card",
|
||||
fromAlt: "From",
|
||||
toAlt: "To",
|
||||
tripTime: "Trip time",
|
||||
fare: "Fare",
|
||||
decline: "Decline",
|
||||
accept: "Accept",
|
||||
offer: "Offer this ride",
|
||||
withdraw: "Withdraw my offer",
|
||||
waitingOnRider: "Offered — waiting for the rider to choose",
|
||||
lostTitle: "Request closed",
|
||||
lostBody:
|
||||
"The rider went with another driver, or the request timed out. You're free for the next one.",
|
||||
alertOfferBody: "Could not send your offer. Please try again.",
|
||||
alertWithdrawBody: "Could not withdraw your offer. Please try again.",
|
||||
},
|
||||
activeRide: {
|
||||
youEarn: "You earn",
|
||||
headToPickup: "Head to pickup",
|
||||
tripInProgress: "Trip in progress",
|
||||
rider: "{name}",
|
||||
pickupPin: "Rider pickup",
|
||||
dropoffPin: "Drop-off",
|
||||
navigateToPickup: "Navigate to pickup",
|
||||
navigateToDropoff: "Navigate to drop-off",
|
||||
alertNavigateBody: "Could not open a navigation app on this phone.",
|
||||
fromAlt: "From",
|
||||
toAlt: "To",
|
||||
fare: "Fare",
|
||||
message: "Message",
|
||||
call: "Call",
|
||||
startTrip: "Start trip",
|
||||
completeTrip: "Complete trip",
|
||||
cancelRide: "Cancel ride",
|
||||
cancelConfirmTitle: "Cancel this ride?",
|
||||
cancelConfirmBody:
|
||||
"The rider will be notified and the ride will be marked as cancelled.",
|
||||
cancelConfirmDismiss: "Keep ride",
|
||||
cancelConfirmConfirm: "Cancel ride",
|
||||
alertErrorTitle: "Error",
|
||||
alertAcceptBody:
|
||||
"Could not accept this ride. It may have been taken or expired.",
|
||||
alertDeclineBody: "Could not decline this ride. Please try again.",
|
||||
alertUpdateBody: "Could not update the ride. Please try again.",
|
||||
alertCancelBody: "Could not cancel the ride. Please try again.",
|
||||
imHere: "I've arrived",
|
||||
atPickup: "At pickup — waiting for rider",
|
||||
askForCode: "Ask the rider for their 4-digit pickup code.",
|
||||
cashConfirmTitle: "Collect the fare",
|
||||
cashConfirmBody: "Did you collect ${amount} in cash from the rider?",
|
||||
cashCollected: "Yes, collected",
|
||||
cashNotCollected: "Not collected",
|
||||
},
|
||||
},
|
||||
|
||||
rating: {
|
||||
rateDriverTitle: "How was your ride with {name}?",
|
||||
rateRiderTitle: "How was {name} as a passenger?",
|
||||
subtitle: "Your rating stays private to the other person.",
|
||||
starLabel: "{n} stars",
|
||||
commentPlaceholder: "Add a comment (optional)",
|
||||
submit: "Submit rating",
|
||||
notNow: "Not now",
|
||||
error: "Could not submit your rating. Please try again.",
|
||||
},
|
||||
|
||||
cancelSheet: {
|
||||
title: "Cancel this ride?",
|
||||
subtitleRider: "Tell us why so we can improve matching.",
|
||||
subtitleDriver: "The rider will be notified and the ride marked cancelled.",
|
||||
confirm: "Cancel ride",
|
||||
keepRide: "Keep ride",
|
||||
cancelling: "Cancelling…",
|
||||
reasons: {
|
||||
changed_mind: "I changed my mind",
|
||||
wait_too_long: "The wait is too long",
|
||||
wrong_address: "Wrong pickup address",
|
||||
driver_no_show: "The driver never arrived",
|
||||
rider_no_show: "The rider never showed up",
|
||||
unreachable: "I couldn't reach them",
|
||||
vehicle_issue: "Vehicle problem",
|
||||
other: "Another reason",
|
||||
},
|
||||
},
|
||||
|
||||
pickupCode: {
|
||||
title: "Start the trip",
|
||||
subtitle: "Enter the 4-digit code from the rider's screen.",
|
||||
startTrip: "Start trip",
|
||||
wrongCode: "That code doesn't match. Check with the rider.",
|
||||
},
|
||||
|
||||
services: {
|
||||
nearbyCount: "{n} nearby",
|
||||
noneNearby: "None nearby",
|
||||
car: { label: "Car", tagline: "An everyday ride, up to 4 seats." },
|
||||
moto: {
|
||||
label: "Moto",
|
||||
@@ -385,6 +691,16 @@ export const en = {
|
||||
"Map is not available on web.\nRun on Android/iOS for the full experience.",
|
||||
},
|
||||
rideCard: {
|
||||
outcomeCompleted: "Completed",
|
||||
outcomeCancelled: "Cancelled",
|
||||
outcomeExpired: "No driver found",
|
||||
cancelledByYou: "You cancelled",
|
||||
cancelledByDriver: "Driver cancelled",
|
||||
cancelledBySystem: "No driver available",
|
||||
noDriver: "No driver assigned",
|
||||
paymentNotCharged: "Not charged",
|
||||
paymentRefundDue: "Refund due",
|
||||
paymentCashCollected: "Cash paid",
|
||||
mapAlt: "Map",
|
||||
originAlt: "Origin",
|
||||
destinationAlt: "Destination",
|
||||
@@ -485,6 +801,9 @@ export const en = {
|
||||
rtlRestartBody:
|
||||
"Arabic layout will apply fully the next time you open the app.",
|
||||
},
|
||||
general: {
|
||||
title: "General",
|
||||
},
|
||||
keepAwake: {
|
||||
title: "Do not lock screen",
|
||||
description: "Keep the display on while the app is open.",
|
||||
|
||||
+330
-3
@@ -138,6 +138,9 @@ export const fr = {
|
||||
recentRides: "Courses récentes",
|
||||
noRecent: "Aucune course récente.",
|
||||
noRecentAlt: "Aucune course récente",
|
||||
activeRideWithDriver: "{name} arrive",
|
||||
rateLastRide: "Comment s'est passée votre dernière course ?",
|
||||
rate: "Noter",
|
||||
},
|
||||
|
||||
rides: {
|
||||
@@ -159,7 +162,38 @@ export const fr = {
|
||||
messageAlt: "message",
|
||||
noMessages: "Pas encore de messages",
|
||||
startConversation:
|
||||
"Démarrez une conversation avec vos amis et votre famille",
|
||||
"La conversation s'ouvre avec votre chauffeur une fois la course attribuée.",
|
||||
inputPlaceholder: "Message…",
|
||||
send: "Envoyer",
|
||||
loadError: "Impossible de charger les messages. Tirez pour réessayer.",
|
||||
sendError: "Impossible d'envoyer le message. Réessayez.",
|
||||
cannotMessage: "Cette course n'est plus active.",
|
||||
call: "Appeler",
|
||||
},
|
||||
|
||||
call: {
|
||||
incoming: "Appel entrant",
|
||||
outgoing: "Appel en cours…",
|
||||
connecting: "Connexion…",
|
||||
inCall: "En appel",
|
||||
ended: "Appel terminé",
|
||||
missed: "Appel manqué",
|
||||
declined: "Appel refusé",
|
||||
failed: "Échec de l'appel",
|
||||
unavailable: "Aucune course active à appeler.",
|
||||
accept: "Accepter",
|
||||
decline: "Refuser",
|
||||
end: "Terminer l'appel",
|
||||
mute: "Muet",
|
||||
unmute: "Activer le micro",
|
||||
speaker: "Haut-parleur",
|
||||
speakerOff: "Haut-parleur off",
|
||||
cancel: "Annuler",
|
||||
connectingWith: "Connexion à {name}…",
|
||||
micDeniedTitle: "Microphone bloqué",
|
||||
micDeniedBody:
|
||||
"Waseel a besoin du microphone pour passer des appels. Activez-le dans les réglages.",
|
||||
audioFailed: "Impossible de démarrer l'audio. Réessayez.",
|
||||
},
|
||||
|
||||
profile: {
|
||||
@@ -173,14 +207,35 @@ export const fr = {
|
||||
emailPlaceholder: "Votre adresse e-mail",
|
||||
},
|
||||
|
||||
adjustPin: {
|
||||
pickupLabel: "Point de départ",
|
||||
destinationLabel: "Point de dépose",
|
||||
locating: "Localisation en cours…",
|
||||
hint: "Faites glisser la carte pour placer le repère exactement où vous voulez.",
|
||||
confirmPickup: "Confirmer le départ",
|
||||
confirmDestination: "Confirmer la dépose",
|
||||
recenter: "Aller à ma position",
|
||||
},
|
||||
|
||||
findRide: {
|
||||
adjustOnMap: "Placer sur la carte",
|
||||
title: "Course",
|
||||
from: "De",
|
||||
to: "À",
|
||||
findNow: "Rechercher",
|
||||
service: "Type de course",
|
||||
nAvailable: "{n} à proximité",
|
||||
estimatedFare: "Tarif estimé",
|
||||
setBothPoints: "Indiquez un départ et une destination",
|
||||
payLaterHint:
|
||||
"Les chauffeurs proches verront votre demande. Vous choisissez qui vous prend, et payez ensuite.",
|
||||
sending: "Envoi de votre demande…",
|
||||
},
|
||||
|
||||
confirmRide: {
|
||||
noDriversInRadius: "Aucun chauffeur {service} dans un rayon de {km} km",
|
||||
tryInstead: "Disponible près de vous maintenant :",
|
||||
searchingRadius: "Recherche dans un rayon de {km} km…",
|
||||
title: "Demander une course",
|
||||
yourTrip: "Votre trajet",
|
||||
pickup: "Départ",
|
||||
@@ -193,6 +248,12 @@ export const fr = {
|
||||
lbpEstimate: "≈ {lbp}",
|
||||
noDrivers: "Aucun chauffeur {service} en ligne pour l'instant",
|
||||
findingDrivers: "Recherche de chauffeurs à proximité…",
|
||||
driversNearby: {
|
||||
one: "1 chauffeur à proximité",
|
||||
other: "{n} chauffeurs à proximité",
|
||||
zero: "Aucun chauffeur à proximité",
|
||||
},
|
||||
withinRadius: "Dans un rayon de {km} km",
|
||||
nearestDriver: "Chauffeur le plus proche ≈ {eta} min",
|
||||
requesting: "Demande en cours…",
|
||||
noDriversOnline: "Aucun chauffeur en ligne",
|
||||
@@ -208,21 +269,63 @@ export const fr = {
|
||||
"Une erreur est survenue lors de la réservation. Réessayez.",
|
||||
alertPayCardTitle: "Payer par carte",
|
||||
alertPayCardBody: "Votre carte sera débitée de ${fare}.",
|
||||
alertInProgressTitle: "Course déjà en cours",
|
||||
alertInProgressBody:
|
||||
"Vous avez une course en cours. Terminez-la ou annulez-la avant d'en réserver une autre.",
|
||||
viewRide: "Voir la course",
|
||||
},
|
||||
|
||||
bookRide: {
|
||||
status: {
|
||||
requested: "Recherche de votre chauffeur…",
|
||||
choosing: "Choisissez votre chauffeur",
|
||||
accepted: "Chauffeur assigné — en route vers vous",
|
||||
enRoute: "Course en cours",
|
||||
completed: "Vous êtes arrivé !",
|
||||
cancelled: "Course annulée",
|
||||
arrived: "Votre chauffeur est arrivé",
|
||||
expired: "Aucun chauffeur disponible",
|
||||
},
|
||||
rideNotFound: "Course introuvable.",
|
||||
couldNotLoad: "Chargement de cette course impossible.",
|
||||
backHome: "Retour à l'accueil",
|
||||
matchingDriver:
|
||||
"Nous vous mettons en relation avec le chauffeur {service} le plus proche.",
|
||||
searchingFor: "Recherche depuis {seconds} s",
|
||||
match: {
|
||||
driverFallback: "Votre chauffeur",
|
||||
alertBody: "Une erreur est survenue. Réessayez.",
|
||||
},
|
||||
offers: {
|
||||
title: "Chauffeurs disponibles",
|
||||
count: {
|
||||
one: "1 proposition",
|
||||
other: "{n} propositions",
|
||||
zero: "Aucune proposition",
|
||||
},
|
||||
away: "{eta} min · {distance}",
|
||||
seats: {
|
||||
one: "1 place",
|
||||
other: "{n} places",
|
||||
zero: "",
|
||||
},
|
||||
pick: "Choisir",
|
||||
goneTitle: "Ce chauffeur n'est plus libre",
|
||||
goneBody:
|
||||
"Il a pris une autre course. Choisissez-en un autre dans la liste.",
|
||||
goneBodyPaid:
|
||||
"Il a pris une autre course. Votre paiement n'a pas été utilisé — choisissez-en un autre et il lui sera versé.",
|
||||
},
|
||||
payment: {
|
||||
title: "Comment souhaitez-vous payer ?",
|
||||
titleNamed: "Course avec {name}",
|
||||
subtitle: "Tarif ${fare}",
|
||||
cash: "Payer en espèces",
|
||||
cashHint: "Réglez la course au chauffeur à l'arrivée.",
|
||||
card: "Payer par carte",
|
||||
cardHint: "Débité maintenant, avant le départ du chauffeur.",
|
||||
working: "En cours…",
|
||||
},
|
||||
ratingFallback: "—",
|
||||
paymentCash: "💵 Espèces au chauffeur",
|
||||
paymentCard: "💳 Payé par carte",
|
||||
@@ -233,10 +336,26 @@ export const fr = {
|
||||
cancelling: "Annulation…",
|
||||
alertErrorTitle: "Erreur",
|
||||
alertErrorBody: "Annulation de cette course impossible. Réessayez.",
|
||||
pickupCodeLabel: "Votre code de prise en charge",
|
||||
pickupCodeHint: "Donnez-le à votre chauffeur pour démarrer la course.",
|
||||
driverHere: "Votre chauffeur est dehors",
|
||||
cashDue: "Payez ${amount} en espèces à votre chauffeur.",
|
||||
youRated: "Vous avez noté cette course {n}★",
|
||||
rateDriver: "Noter votre chauffeur",
|
||||
noDriversFound:
|
||||
"Aucun chauffeur n'a accepté votre demande. Rien n'a été débité — réessayez dans un instant.",
|
||||
cancelledByDriver: "Votre chauffeur a annulé cette course.",
|
||||
enRouteNotice:
|
||||
"Bonne route — votre chauffeur terminera la course à l'arrivée.",
|
||||
},
|
||||
|
||||
driver: {
|
||||
home: {
|
||||
owesCompany: "Commission que vous devez",
|
||||
owesCompanyHint: "Vos courses en espèces — à remettre au bureau.",
|
||||
owedToDriver: "L'entreprise vous doit",
|
||||
owedToDriverHint: "Courses par carte, à vous verser.",
|
||||
afterFee: "après ${fee} de commission",
|
||||
signOutAlt: "Déconnexion",
|
||||
driverMode: "Mode chauffeur",
|
||||
online: "● En ligne — réception des demandes",
|
||||
@@ -245,6 +364,8 @@ export const fr = {
|
||||
completedToday: "Terminées aujourd'hui",
|
||||
incomingRequests: "Demandes entrantes",
|
||||
incomingRequestsOffline: "Demandes entrantes (hors ligne)",
|
||||
finishCurrentRide:
|
||||
"Terminez votre course en cours pour voir les nouvelles demandes.",
|
||||
waitingRequests: "En attente de demandes…",
|
||||
goOnlineStart: "Passez en ligne pour commencer à rouler.",
|
||||
welcome: "Bienvenue, {name}",
|
||||
@@ -264,36 +385,229 @@ export const fr = {
|
||||
alertCreateBody: "Création du profil chauffeur impossible. Réessayez.",
|
||||
alertToggleBody: "Changement de statut impossible. Réessayez.",
|
||||
noRequestsAlt: "Aucune course récente",
|
||||
cashInHand: "Espèces encaissées aujourd'hui",
|
||||
uncollected: "Courses non encaissées aujourd'hui",
|
||||
ratingCount: "{n} évaluations",
|
||||
ratingNew: "Nouveau chauffeur",
|
||||
alertOfflineBlocked:
|
||||
"Terminez ou annulez votre course en cours avant de passer hors ligne.",
|
||||
},
|
||||
credentials: {
|
||||
title: "Vos documents",
|
||||
intro:
|
||||
"Chaque chauffeur est vérifié avant sa première course. Notre équipe examine ces informations.",
|
||||
licenseNumber: "Numéro de permis de conduire",
|
||||
licenseNumberPlaceholder: "Tel qu'inscrit sur le permis",
|
||||
licenseExpiry: "Expiration du permis",
|
||||
nationalId: "Numéro de carte d'identité",
|
||||
nationalIdPlaceholder: "Votre numéro d'identité",
|
||||
plateNumber: "Numéro de plaque",
|
||||
plateNumberPlaceholder: "ex. 123456/B",
|
||||
reviewNote:
|
||||
"Votre compte reste hors ligne jusqu'à validation, généralement en moins d'une journée.",
|
||||
submit: "Envoyer pour vérification",
|
||||
errorTitle: "Vérifiez vos informations",
|
||||
errorMissing:
|
||||
"Le numéro de permis, la carte d'identité et la plaque sont tous obligatoires.",
|
||||
errorExpiryFormat:
|
||||
"Saisissez l'expiration du permis au format AAAA-MM-JJ.",
|
||||
errorExpired: "Ce permis est déjà expiré.",
|
||||
errorScanRequired: "Scannez votre permis de conduire avant d'envoyer.",
|
||||
alertResubmitBody: "Renvoi de vos informations impossible. Réessayez.",
|
||||
},
|
||||
captureUnavailable:
|
||||
"La prise de photo n'est pas disponible dans cette version de l'application. Mettez-la à jour et réessayez.",
|
||||
photo: {
|
||||
title: "Votre photo",
|
||||
hint: "Prise maintenant avec l'appareil photo, pas depuis votre galerie. Les passagers la voient à côté de votre nom au moment de choisir un chauffeur, et s'en servent pour vérifier que c'est bien vous au départ. Regardez l'objectif, dans une bonne lumière.",
|
||||
take: "Prendre une photo",
|
||||
retake: "Reprendre",
|
||||
required: "Prenez une photo de profil avant d'envoyer.",
|
||||
permissionTitle: "Autorisation requise",
|
||||
permissionCamera:
|
||||
"Waseel a besoin de l'appareil photo pour prendre votre photo. Autorisez-y l'accès pour continuer.",
|
||||
permissionCameraBlocked:
|
||||
"L'accès à l'appareil photo est désactivé pour Waseel, et Android ne le redemandera plus depuis ici. Ouvrez les réglages, puis activez Appareil photo dans les autorisations.",
|
||||
errorTitle: "Photo non enregistrée",
|
||||
errorBody: "Une erreur s'est produite. Réessayez.",
|
||||
errorTooLarge: "Cette photo est trop lourde. Prenez-en une nouvelle.",
|
||||
errorRateLimit: "Trop d'envois. Attendez quelques minutes et réessayez.",
|
||||
errorUnsupported: "Utilisez une photo JPEG, PNG ou WebP.",
|
||||
},
|
||||
scan: {
|
||||
licenseLabel: "Permis de conduire",
|
||||
licenseHint:
|
||||
"Posez-le à plat et remplissez le cadre. Nous y lisons le numéro et l'expiration.",
|
||||
idLabel: "Carte d'identité",
|
||||
idHint: "La face où figure votre numéro d'identité.",
|
||||
vehicle_regLabel: "Carte grise",
|
||||
vehicle_regHint:
|
||||
"La page où figurent le numéro de plaque et le modèle du véhicule.",
|
||||
optional: "Facultatif",
|
||||
take: "Photographier",
|
||||
retake: "Rescanner",
|
||||
choose: "Choisir une photo",
|
||||
reading: "Lecture de votre document…",
|
||||
filled: {
|
||||
one: "1 information remplie — vérifiez-la ci-dessous.",
|
||||
other: "{n} informations remplies — vérifiez-les ci-dessous.",
|
||||
},
|
||||
savedNoFields:
|
||||
"Photo enregistrée, mais les informations n'ont pas pu être lues. Saisissez-les ci-dessous.",
|
||||
savedUnreadable:
|
||||
"Photo enregistrée. La lecture est indisponible pour le moment — saisissez les informations ci-dessous.",
|
||||
alreadyOnFile:
|
||||
"Un scan est déjà enregistré. Rescannez seulement si nécessaire.",
|
||||
allRead: "Lu sur vos documents",
|
||||
missingPrompt:
|
||||
"Nous n'avons pas pu lire ces informations sur vos documents. Ajoutez-les et c'est terminé.",
|
||||
edit: "Vérifier ou modifier",
|
||||
done: "Terminé",
|
||||
checkPrompt: "Corrigez ce qui a été mal lu, puis appuyez sur Terminé.",
|
||||
permissionTitle: "Autorisation requise",
|
||||
permissionCamera:
|
||||
"Autorisez l'appareil photo pour photographier vos documents, ou choisissez une photo existante.",
|
||||
permissionCameraBlocked:
|
||||
"L'accès à l'appareil photo est désactivé pour Waseel, et Android ne le redemandera plus depuis ici. Ouvrez les réglages et activez Appareil photo dans les autorisations — ou choisissez une photo existante.",
|
||||
permissionLibrary:
|
||||
"Autorisez l'accès aux photos pour choisir une image de vos documents.",
|
||||
permissionLibraryBlocked:
|
||||
"L'accès aux photos est désactivé pour Waseel, et Android ne le redemandera plus depuis ici. Ouvrez les réglages et activez Photos dans les autorisations — ou prenez une photo avec l'appareil photo.",
|
||||
errorTitle: "Scan impossible",
|
||||
errorBody:
|
||||
"Une erreur s'est produite. Réessayez ou saisissez les informations ci-dessous.",
|
||||
errorTooLarge: "Cette photo est trop lourde. Prenez-en une nouvelle.",
|
||||
errorRateLimit: "Trop de scans. Attendez quelques minutes et réessayez.",
|
||||
errorUnsupported: "Utilisez une photo JPEG, PNG ou WebP.",
|
||||
errorRetry:
|
||||
"Envoi échoué. Réessayez ou saisissez les informations ci-dessous.",
|
||||
},
|
||||
review: {
|
||||
pendingTitle: "En cours de vérification",
|
||||
pendingBody:
|
||||
"Nous vérifions vos informations. Vous pourrez passer en ligne dès validation.",
|
||||
rejectedTitle: "Non validé",
|
||||
rejectedBody:
|
||||
"Vos informations n'ont pas été validées. Corrigez-les ci-dessous et renvoyez-les.",
|
||||
suspendedTitle: "Compte suspendu",
|
||||
suspendedBody:
|
||||
"Votre compte chauffeur a été suspendu. Contactez le support pour régulariser.",
|
||||
approvedTitle: "Validé",
|
||||
approvedBody: "Vous êtes autorisé à rouler.",
|
||||
reasonLabel: "Motif",
|
||||
checkAgain: "Vérifier à nouveau",
|
||||
resubmitTitle: "Corrigez vos informations",
|
||||
resubmitIntro: "Corrigez ce qui ne va pas et nous vérifierons à nouveau.",
|
||||
resubmit: "Renvoyer",
|
||||
},
|
||||
offerCard: {
|
||||
youEarn: "Vous gagnez",
|
||||
newRequest: "Nouvelle demande · {service}",
|
||||
openFor: "Disponible encore",
|
||||
seconds: "{n} s",
|
||||
awayFromPickup: "{km} km du point de départ",
|
||||
firstIn: "Vous seriez le premier",
|
||||
rivals: {
|
||||
one: "1 autre chauffeur s'est proposé",
|
||||
other: "{n} autres chauffeurs se sont proposés",
|
||||
zero: "Vous seriez le premier",
|
||||
},
|
||||
cash: "💵 Espèces",
|
||||
card: "💳 Carte",
|
||||
fromAlt: "De",
|
||||
toAlt: "À",
|
||||
tripTime: "Durée",
|
||||
fare: "Tarif",
|
||||
decline: "Refuser",
|
||||
accept: "Accepter",
|
||||
offer: "Proposer cette course",
|
||||
withdraw: "Retirer ma proposition",
|
||||
waitingOnRider: "Proposé — en attente du choix du passager",
|
||||
lostTitle: "Demande close",
|
||||
lostBody:
|
||||
"Le passager a choisi un autre chauffeur, ou la demande a expiré. Vous êtes libre pour la suivante.",
|
||||
alertOfferBody: "Envoi de votre proposition impossible. Réessayez.",
|
||||
alertWithdrawBody: "Retrait de votre proposition impossible. Réessayez.",
|
||||
},
|
||||
activeRide: {
|
||||
youEarn: "Vous gagnez",
|
||||
dropoffPin: "Dépose",
|
||||
navigateToPickup: "Naviguer vers le départ",
|
||||
navigateToDropoff: "Naviguer vers la dépose",
|
||||
alertNavigateBody:
|
||||
"Impossible d'ouvrir une application de navigation sur ce téléphone.",
|
||||
headToPickup: "Direction le départ",
|
||||
tripInProgress: "Course en cours",
|
||||
rider: "{name}",
|
||||
pickupPin: "Départ du passager",
|
||||
fromAlt: "De",
|
||||
toAlt: "À",
|
||||
fare: "Tarif",
|
||||
message: "Message",
|
||||
call: "Appeler",
|
||||
startTrip: "Démarrer la course",
|
||||
completeTrip: "Terminer la course",
|
||||
cancelRide: "Annuler la course",
|
||||
cancelConfirmTitle: "Annuler cette course ?",
|
||||
cancelConfirmBody:
|
||||
"Le passager sera averti et la course sera marquée comme annulée.",
|
||||
cancelConfirmDismiss: "Garder la course",
|
||||
cancelConfirmConfirm: "Annuler la course",
|
||||
alertErrorTitle: "Erreur",
|
||||
alertAcceptBody:
|
||||
"Acceptation impossible. La course a peut-être été prise ou a expiré.",
|
||||
alertDeclineBody: "Refus de la course impossible. Réessayez.",
|
||||
alertUpdateBody: "Mise à jour de la course impossible. Réessayez.",
|
||||
alertCancelBody: "Annulation de la course impossible. Réessayez.",
|
||||
imHere: "Je suis arrivé",
|
||||
atPickup: "Sur place — en attente du passager",
|
||||
askForCode: "Demandez au passager son code à 4 chiffres.",
|
||||
cashConfirmTitle: "Encaisser la course",
|
||||
cashConfirmBody: "Avez-vous encaissé ${amount} en espèces ?",
|
||||
cashCollected: "Oui, encaissé",
|
||||
cashNotCollected: "Non encaissé",
|
||||
},
|
||||
},
|
||||
|
||||
rating: {
|
||||
rateDriverTitle: "Comment s'est passée votre course avec {name} ?",
|
||||
rateRiderTitle: "Comment était {name} comme passager ?",
|
||||
subtitle: "Votre note reste privée vis-à-vis de l'autre personne.",
|
||||
starLabel: "{n} étoiles",
|
||||
commentPlaceholder: "Ajouter un commentaire (facultatif)",
|
||||
submit: "Envoyer la note",
|
||||
notNow: "Plus tard",
|
||||
error: "Envoi de votre note impossible. Réessayez.",
|
||||
},
|
||||
|
||||
cancelSheet: {
|
||||
title: "Annuler cette course ?",
|
||||
subtitleRider: "Dites-nous pourquoi pour améliorer nos attributions.",
|
||||
subtitleDriver:
|
||||
"Le passager sera averti et la course sera marquée comme annulée.",
|
||||
confirm: "Annuler la course",
|
||||
keepRide: "Garder la course",
|
||||
cancelling: "Annulation…",
|
||||
reasons: {
|
||||
changed_mind: "J'ai changé d'avis",
|
||||
wait_too_long: "L'attente est trop longue",
|
||||
wrong_address: "Mauvaise adresse de départ",
|
||||
driver_no_show: "Le chauffeur n'est jamais arrivé",
|
||||
rider_no_show: "Le passager ne s'est pas présenté",
|
||||
unreachable: "Impossible de le joindre",
|
||||
vehicle_issue: "Problème de véhicule",
|
||||
other: "Autre raison",
|
||||
},
|
||||
},
|
||||
|
||||
pickupCode: {
|
||||
title: "Démarrer la course",
|
||||
subtitle: "Saisissez le code à 4 chiffres affiché chez le passager.",
|
||||
startTrip: "Démarrer",
|
||||
wrongCode: "Ce code ne correspond pas. Vérifiez avec le passager.",
|
||||
},
|
||||
|
||||
services: {
|
||||
nearbyCount: "{n} à proximité",
|
||||
noneNearby: "Aucun à proximité",
|
||||
car: {
|
||||
label: "Voiture",
|
||||
tagline: "Une course quotidienne, jusqu'à 4 places.",
|
||||
@@ -392,6 +706,16 @@ export const fr = {
|
||||
"La carte n'est pas disponible sur le web.\nUtilisez Android ou iOS pour l'expérience complète.",
|
||||
},
|
||||
rideCard: {
|
||||
outcomeCompleted: "Terminée",
|
||||
outcomeCancelled: "Annulée",
|
||||
outcomeExpired: "Aucun chauffeur trouvé",
|
||||
cancelledByYou: "Vous avez annulé",
|
||||
cancelledByDriver: "Le chauffeur a annulé",
|
||||
cancelledBySystem: "Aucun chauffeur disponible",
|
||||
noDriver: "Aucun chauffeur attribué",
|
||||
paymentNotCharged: "Non débité",
|
||||
paymentRefundDue: "Remboursement dû",
|
||||
paymentCashCollected: "Payé en espèces",
|
||||
mapAlt: "Carte",
|
||||
originAlt: "Origine",
|
||||
destinationAlt: "Destination",
|
||||
@@ -492,6 +816,9 @@ export const fr = {
|
||||
rtlRestartBody:
|
||||
"La mise en page arabe s'appliquera pleinement à la prochaine ouverture de l'application.",
|
||||
},
|
||||
general: {
|
||||
title: "Général",
|
||||
},
|
||||
keepAwake: {
|
||||
title: "Ne pas verrouiller l'écran",
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user