PageSourceSearch

https://snipershopping.com/shared/js/i18n.js

js snipershopping.com collected 2026-10-06 10:32:17 UTC 43,886 bytes, 805 lines download raw bytes

1/* Sniper - English / Arabic.
2   Swaps every element carrying data-i18n, flips the document to RTL, and loads the
3   Arabic face. The layout mirrors on its own because the stylesheet uses logical
4   properties throughout - nothing here touches CSS beyond setting dir.
5
6   Scope note, honestly stated: this covers the interface and the copy written for
7   these pages. PRODUCT names and descriptions come from the catalogue and are
8   English-only until the Arabic fields are migrated from the store - they fall back
9   to English rather than showing an empty line, which is the rule in section 14.1
10   of the plan. */
11(function () {
12  "use strict";
13
14  var KEY = "sniper.lang.v1";
15
16  var AR = {
17    /* ---- top bar ---- */
18    "top.safety": "معدات سلامة معتمدة تُورَّد في البحرين ودول الخليج",
19    "top.shopping": "توصيل مجاني داخل البحرين للطلبات فوق {amount} · شحن لدول الخليج",
20    "top.lang": "English",
21
22    /* ---- navigation ---- */
23    "nav.home": "الرئيسية",
24    "nav.sections": "أقسام السلامة",
25    "nav.industries": "القطاعات",
26    "nav.brands": "تسوق حسب العلامة",
27    "nav.about": "من نحن",
28    "nav.contact": "اتصل بنا",
29    "nav.collection": "التشكيلة",
30    "nav.summer": "تشكيلة الصيف",
31    "nav.search": "بحث",
32    "nav.cart": "السلة",
33    "nav.account": "حسابي",
34    "nav.quote": "طلب عرض سعر",
35
36    /* ---- cross-division block ---- */
37    "cross.shopping": "سنايبر و 5.11",
38    "cross.shopping.sub": "تكتيكال وخارجي",
39    "cross.safety": "سنايبر للسلامة",
40    "cross.safety.sub": "كن مستعداً · ابقَ آمناً",
41
42    /* ---- mega menu, safety ---- */
43    "mega.ppe": "معدات الوقاية الشخصية",
44    "mega.fall": "الحماية من السقوط والعمل على ارتفاع",
45    "mega.breathing": "أجهزة التنفس والإنقاذ",
46    "mega.gas": "كشف الغازات والمراقبة",
47    "mega.measuring": "أجهزة القياس",
48    "mega.lighting": "الإضاءة الصناعية وإضاءة السلامة",
49    "mega.road": "الطرق والمرور والإنشاءات",
50    "mega.envspec": "البيئة والمعدات المتخصصة",
51    "mega.person": "حماية الشخص",
52    "mega.detect": "التنفس والكشف",
53    "mega.site": "حماية الموقع",
54    "mega.access": "التحكم بالدخول",
55    "mega.shopby": "تسوق حسب",
56    "mega.cert": "معيار الاعتماد",
57    "mega.brand": "العلامة التجارية",
58    "mega.promo.title": "تجهيز موقع كامل؟",
59    "mega.promo.body": "أرسل لنا قائمة الكميات أو كشف المناقصة وسيقوم فريق السلامة بتسعيرها بنداً بنداً، مع أسعار الجملة وشروط ائتمان للحسابات المعتمدة.",
60    "mega.promo.cta": "اطلب عرض سعر",
61
62    /* ---- mega menu, shopping ---- */
63    "mega.apparel": "ملابس",
64    "mega.men": "رجال",
65    "mega.women": "نساء",
66    "mega.footwear": "أحذية",
67    "mega.bags": "حقائب",
68    "mega.accessories": "إكسسوارات",
69    "mega.bundle.title": "كوّن باقتك",
70    "mega.bundle.body": "اختر ما تريد من التشكيلة ويُطبَّق الخصم تلقائياً في السلة - بدون رمز خصم وبدون مفاجآت عند الدفع.",
71    "mega.bundle.cta": "ابدأ باقتك",
72
73    /* ---- sections, safety home ---- */
74    "sec.kicker.sections": "أقسام السلامة",
75    "sec.categories": "تسوق حسب الفئة",
76    "sec.viewall.sections": "كل الأقسام",
77    "sec.kicker.instock": "متوفر الآن",
78    "sec.featured": "منتجات السلامة المختارة",
79    "sec.allproducts": "كل المنتجات",
80    "sec.kicker.industry": "مباشرةً إلى ما يحتاجه عملك",
81    "sec.industries": "تسوق حسب القطاع",
82    "sec.allindustries": "كل القطاعات",
83    "sec.kicker.brands": "شركات مصنعة معتمدة",
84    "sec.brands": "تسوق حسب العلامة",
85    "sec.allbrands": "كل العلامات",
86    "sec.kicker.why": "لماذا سنايبر للسلامة",
87    "sec.why": "يشتريها من لا يحتمل أن تخذله",
88
89    /* ---- sections, shopping home ---- */
90    "sec.kicker.collection": "التشكيلة",
91    "sec.viewall": "تصفح الكل",
92    "sec.kicker.new": "وصل حديثاً",
93    "sec.new": "أحدث المنتجات",
94    "sec.kicker.popular": "متوفر الآن",
95    "sec.popular": "الأكثر طلباً هذا الشهر",
96    "sec.kicker.ourbrands": "العلامات التي نوفرها",
97    "sec.ourbrands": "علاماتنا التجارية",
98    "sec.kicker.whysniper": "لماذا سنايبر",
99    "sec.whysniper": "لماذا سنايبر؟",
100    "sec.shopbags": "تسوق الحقائب",
101
102    /* ---- hero, safety ---- */
103    "hero.kicker": "كن مستعداً · ابقَ آمناً",
104    "hero.title": "جهّز كل عامل. احمِ كل وردية.",
105    "hero.body": "معدات وقاية شخصية معتمدة، وحماية من السقوط، وكشف الغازات، ومعدات سلامة المواقع للمحترفين والمقاولين وإدارات المشتريات في البحرين ودول الخليج.",
106    "hero.cta1": "تسوق معدات السلامة",
107    "hero.cta2": "اطلب عرض سعر للشركات",
108    "strip.cr": "مورّد مسجل · س.ت 55377-11",
109    "strip.delivery": "توصيل داخل البحرين وشحن خليجي",
110    "strip.brands": "علامات معتمدة متوفرة في المخزون",
111    "strip.corporate": "حسابات شركات وأسعار جملة",
112
113    /* ---- hero, shopping ---- */
114    "vhero.kicker": "ملابس الأداء التكتيكي",
115    "vhero.line1": "مصنوع للأداء.",
116    "vhero.line2": "نحن نتحدث بالجودة.",
117    "vhero.sub": "قابل للتنفس. ثابت اللون. مقاوم للانكماش.",
118    "vhero.cta1": "تسوق حسب الفئة",
119    "vhero.cta2": "وصل حديثاً",
120    "vstrip.delivery": "توصيل مجاني للطلبات فوق {amount}",
121    "vstrip.gcc": "شحن إلى دول الخليج",
122    "vstrip.since": "نعمل منذ 2010",
123    "vstrip.branches": "فرعان في البحرين",
124
125    /* ---- product card ---- */
126    "card.add": "أضف إلى السلة",
127    "card.options": "اختر الخيارات",
128    "card.unavailable": "غير متوفر",
129    "card.quote": "أضف إلى عرض السعر",
130    "card.instock": "متوفر",
131    "card.oos": "غير متوفر",
132    "card.bulk": "متاح بالجملة",
133    "card.sale": "تخفيض",
134    "card.onrequest": "السعر عند الطلب",
135
136    /* ---- footer ---- */
137    "foot.about.safety": "قسم السلامة في سنايبر. معدات حماية شخصية ومواقع معتمدة، تُورَّد في البحرين ودول الخليج منذ 2010.",
138    "foot.about.shopping": "ملابس وأحذية ومعدات تكتيكية وخارجية ومهنية. نخدم مملكة البحرين منذ 2010، ونشحن إلى دول الخليج.",
139    "foot.sections": "أقسام السلامة",
140    "foot.more": "أقسام أخرى",
141    "foot.company": "الشركة",
142    "foot.contact": "تواصل معنا",
143    "foot.shop": "تسوق",
144    "foot.rights": "جميع الحقوق محفوظة",
145    "foot.quote": "اطلب عرض سعر",
146    "foot.shopcta": "تسوق التشكيلة",
147    "foot.cr": "سجل تجاري",
148    "foot.vat": "الرقم الضريبي",
149    "gcc.badge": "توصيل لجميع دول الخليج",
150    "gcc.t1": "نشحن لجميع",
151    "gcc.t2": "دول الخليج",
152    "gcc.sub": "من البحرين إلى كل الخليج - نوصل معداتك وملابسك مباشرة إلى باب منزلك في أي مكان في دول مجلس التعاون الخليجي.",
153    "gcc.shop": "تسوق منتجاتنا",
154    "gcc.note": "توصيل سريع ومتتبَّع لجميع دول مجلس التعاون",
155    "gcc.bh": "البحرين",
156    "gcc.sa": "السعودية",
157    "gcc.ae": "الإمارات",
158    "gcc.kw": "الكويت",
159    "gcc.qa": "قطر",
160    "gcc.om": "عُمان",
161    "foot.delivery": "التوصيل والشحن",
162    "foot.returns": "الإرجاع والاسترداد",
163    "foot.terms": "الشروط والأحكام",
164    "foot.privacy": "سياسة الخصوصية",
165    "foot.contactUs": "اتصل بنا",
166    "legal.returns.lead": "غيرت رأيك أو هناك خطأ في طلبك؟ إليك طريقة الإرجاع والاسترداد.",
167    "legal.delivery.lead": "أين نوصل، وكم يكلف، وكم يستغرق.",
168    "legal.terms.lead": "الشروط التي تنطبق عند استخدامك لهذا الموقع والطلب منا.",
169    "legal.privacy.lead": "ما البيانات التي نجمعها، ولماذا، وما هي حقوقك.",
170    "order.invoice": "الفاتورة (للطباعة أو الحفظ PDF)",
171    "order.proforma": "فاتورة مبدئية",
172    "checkout.vat": "الأسعار تشمل ضريبة القيمة المضافة حيثما تنطبق.",
173    "checkout.agree": "بتقديم الطلب فإنك توافق على {terms} و{returns} و{privacy}.",
174
175    /* ---- listing ---- */
176    "list.filters": "التصفية",
177    "list.clear": "مسح الكل",
178    "list.sort.featured": "الترتيب: المميزة",
179    "list.sort.priceasc": "السعر: من الأقل للأعلى",
180    "list.sort.pricedesc": "السعر: من الأعلى للأقل",
181    "list.sort.name": "الاسم: أ إلى ي",
182    "list.loadmore": "عرض المزيد",
183    "list.grid": "عرض شبكي",
184    "list.list": "عرض قائمة",
185    "filter.gender": "النوع",
186    "filter.industry": "القطاع",
187    "filter.men": "رجال",
188    "filter.women": "نساء",
189    "cat.products": "منتج",
190    "cat.shown": "معروض",
191    "cat.borrow": "لا توجد منتجات هنا بعد - نعرض كل ما في {name}.",
192    "cat.industryBlurb": "معدات سلامة مختارة لقطاع {name} - كل الفئات في مكان واحد. استخدم الفلاتر لتضييق البحث.",
193
194    /* ---- sorting ---- */
195    "sort.best": "الترتيب: الأكثر مبيعاً",
196    "sort.newest": "الأحدث أولاً",
197    "sort.priceasc": "السعر: من الأقل إلى الأعلى",
198    "sort.pricedesc": "السعر: من الأعلى إلى الأقل",
199    "sort.name": "الاسم: أ إلى ي",
200
201    /* ---- product page ---- */
202    /* ---- sign in, account, loyalty ---- */
203    "auth.signin": "تسجيل الدخول",
204    "auth.createAccount": "إنشاء حساب",
205    "auth.name": "الاسم الكامل",
206    "auth.email": "البريد الإلكتروني",
207    "auth.password": "كلمة المرور",
208    "auth.passHint": "٨ أحرف على الأقل",
209    "auth.forgot": "نسيت كلمة المرور؟",
210    "auth.forgotTitle": "إعادة تعيين كلمة المرور",
211    "auth.forgotLead": "أدخل بريدك الإلكتروني وسنرسل لك رابطاً لتعيين كلمة مرور جديدة.",
212    "auth.sendLink": "إرسال الرابط",
213    "auth.backToSignin": "العودة لتسجيل الدخول",
214    "auth.resetSent": "إذا كان لهذا البريد حساب، فالرابط في طريقه إليك.",
215    "auth.haveAccount": "لديك حساب بالفعل؟",
216    "auth.noAccount": "جديد هنا؟",
217    "auth.guest": "لا تحتاج إلى حساب للطلب - يمكنك إتمام الشراء كزائر.",
218    "auth.signinLead": "أهلاً بعودتك. طلباتك وبياناتك المحفوظة بانتظارك.",
219    "auth.registerLead": "لا يستغرق سوى لحظة، ويحفظ طلباتك وعناوينك ونقاطك في مكان واحد.",
220    "auth.whyTitle": "لماذا تنشئ حساباً",
221    "auth.perkOrders": "طلباتك",
222    "auth.perkOrdersBody": "كل طلب وحالته وتتبعه في قائمة واحدة، مع إعادة الطلب بنقرة.",
223    "auth.perkAddress": "العناوين المحفوظة",
224    "auth.perkAddressBody": "أتمم الشراء دون كتابة عنوانك من جديد.",
225    "auth.perkInvoices": "الفواتير",
226    "auth.perkInvoicesBody": "حمّل أي فاتورة أو إشعار تسليم بصيغة PDF.",
227    "auth.perkPointsBody": "اجمع النقاط على مشترياتك واستخدمها عند الدفع.",
228    "auth.badEmail": "هذا لا يبدو بريداً إلكترونياً صحيحاً.",
229    "auth.badName": "الرجاء إدخال اسمك.",
230    "auth.badPass": "استخدم ٨ أحرف على الأقل.",
231    "auth.needPass": "أدخل كلمة المرور.",
232    "account.hello": "حسابك",
233    "account.helloName": "مرحباً",
234    "account.signout": "تسجيل الخروج",
235    "account.profile": "بياناتك",
236    "account.addresses": "العناوين المحفوظة",
237    "account.address": "العنوان",
238    "account.addAddress": "إضافة عنوان",
239    "account.noAddress": "لا توجد عناوين محفوظة بعد. العنوان الذي تستخدمه عند الدفع يُحفظ هنا.",
240    "account.default": "الافتراضي",
241    "account.orders": "الطلبات الأخيرة",
242    "account.order": "الطلب",
243    "account.date": "التاريخ",
244    "account.status": "الحالة",
245    "account.total": "الإجمالي",
246    "account.view": "عرض",
247    "account.noOrders": "لا شيء بعد. ستظهر طلباتك وتتبعها هنا.",
248    "account.startShopping": "ابدأ التسوق",
249    "account.phone": "رقم الجوال",
250    "account.notSet": "غير محدد",
251    "account.edit": "تعديل البيانات",
252    "account.needSignin": "سجّل الدخول لعرض حسابك",
253    "account.needSigninBody": "طلباتك وعناوينك المحفوظة ونقاطك موجودة هنا.",
254    "loyalty.title": "بطاقة الولاء",
255    "loyalty.points": "نقطة",
256    "loyalty.minimum": "الحد الأدنى لاستخدام نقاطك",
257    "loyalty.needMore": "النقاط المتبقية",
258    "loyalty.ready": "لديك نقاط كافية لاستخدامها عند الدفع.",
259    "loyalty.collect": "اجمع النقاط على كل ما تشتريه.",
260    "crumb.home": "الرئيسية",
261
262    "pdp.styleno": "رقم الموديل",
263    "pdp.qty": "الكمية",
264    "pdp.size": "المقاس",
265    "pdp.length": "الطول",
266    "pdp.sizeguide": "دليل المقاسات",
267    "pdp.addcart": "أضف إلى السلة",
268    "pdp.addquote": "أضف إلى عرض السعر",
269    "pdp.desc": "الوصف",
270    "pdp.spec": "المواصفات",
271    "pdp.docs": "المستندات",
272    "pdp.delivery": "التوصيل والإرجاع",
273    "pdp.related": "منتجات ذات صلة",
274    "pdp.samesection": "من نفس القسم",
275
276    /* ---- contact ---- */
277    "contact.title": "تواصل مع فريقنا",
278    "contact.call": "اتصل بنا",
279    "contact.email": "راسلنا",
280    "contact.hq": "المكتب الرئيسي",
281    "contact.send": "أرسل رسالة",
282    "contact.name": "الاسم الكامل",
283    "contact.company": "الشركة",
284    "contact.phone": "الهاتف",
285    "contact.message": "الرسالة",
286    "contact.submit": "إرسال الطلب",
287    "contact.branches": "زر أحد الفروع",
288    "contact.faq": "أسئلة شائعة",
289
290    /* ---- misc ---- */
291    /* ---- product page trust list (editable content block) ---- */
292    "trust.delivery": "توصيل داخل البحرين وشحن إلى دول الخليج",
293    "trust.genuine": "بضاعة أصلية، تُصدر الفاتورة بالدينار البحريني من سنايبر",
294    "trust.cr": "مورّد مسجل تجارياً، وتُصدر الفاتورة بالدينار البحريني",
295    "trust.sizing": "غير متأكد من المقاس؟",
296    "trust.ask": "اسأل الفريق",
297    "trust.spec": "غير متأكد أن هذه هي المواصفة المناسبة؟",
298    "trust.askdesk": "اسأل فريق السلامة",
299    /* ---- size charts ---- */
300    "size.dontknow": "لا تعرف مقاسك؟",
301    "size.brandnote": "المقاسات تختلف بين العلامات التجارية - راجع جدول",
302    "size.check": "تحقق من المقاس",
303    "size.viewfull": "عرض جدول المقاسات كاملاً",
304    "size.close": "إغلاق",
305    "size.chart": "جدول المقاسات",
306    "size.measure": "طريقة القياس",
307    "size.converted": "السنتيمترات محوّلة من قياسات الشركة المصنّعة بالبوصة.",
308    "size.convertedIn": "البوصات محوّلة من قياسات الشركة المصنّعة بالسنتيمتر.",
309    "size.nolength": "تحويل مقاسات",
310    "fx.note": "الأسعار محوّلة من الدينار البحريني للعرض فقط. تُثبَّت عملة الطلب عند إتمام الشراء.",
311    "cart.label": "سلتك",
312    "cart.title": "سلة المشتريات",
313    "cart.size": "مقاس",
314    "cart.length": "الطول",
315    "cart.empty": "سلتك فارغة. أي منتج تضيفه من أي من القسمين سيظهر هنا.",
316    "cart.subtotal": "المجموع الفرعي",
317    "cart.note": "يُحتسب التوصيل وأسعار الجملة عند إتمام الشراء، وتُثبَّت عملة الطلب عند تأكيده.",
318    "cart.checkout": "إتمام الشراء",
319    "cart.continue": "متابعة التسوق",
320    "cart.added": "أُضيف إلى السلة",
321    "cart.quoted": "أُضيف إلى طلب عرض السعر",
322    "cart.soon": "إتمام الشراء هو المرحلة القادمة من التطوير",
323    "cart.note2": "تُطبَّق أسعار الجملة عند إتمام الشراء، وتُثبَّت عملة الطلب عند تأكيده.",
324    "cart.delivery": "التوصيل",
325    "cart.total": "الإجمالي",
326
327    /* ---- documentation requests ---- */
328    "req.title": "طلب المستندات",
329    "req.sub": "مجاناً - نرسلها إلى بريدك الإلكتروني، عادةً في نفس يوم العمل.",
330    "req.which": "ما المستندات التي تحتاجها؟",
331    "req.name": "الاسم",
332    "req.email": "البريد الإلكتروني",
333    "req.privacy": "نستخدم هذه البيانات فقط لإرسال المستندات والاحتفاظ بسجل لطلبك.",
334    "req.send": "إرسال الطلب",
335    "req.sending": "جارٍ الإرسال...",
336    "req.thanks": "تم استلام طلبك",
337    "req.okbody": "سيرسل فريق السلامة المستندات إلى",
338    "req.mailopen": "خطوة أخيرة - اضغط إرسال في بريدك",
339    "req.mailbody": "فُتح تطبيق البريد وفيه تفاصيل الطلب. أرسله وسيرد فريق السلامة على",
340    "req.noapp": "لم يُفتح تطبيق البريد؟ راسلنا على",
341    "req.close": "إغلاق",
342    "req.errdoc": "اختر مستنداً واحداً على الأقل.",
343    "req.errname": "يرجى إدخال اسمك.",
344    "req.erremail": "يرجى إدخال بريد إلكتروني صحيح.",
345    "req.errsend": "لم يتم الإرسال. حاول مرة أخرى أو راسلنا عبر البريد.",
346
347    /* ---- shipping ---- */
348    "ship.to": "التوصيل إلى",
349    "ship.free": "مجاناً",
350    "ship.quoted": "يُسعَّر بعد الطلب",
351    "ship.freight": "الشحن",
352    "ship.freeover": "مجاناً للطلبات فوق",
353    "ship.addmore": "أضف",
354    "ship.forfree": "للحصول مجاناً على",
355    "ship.arrives": "يصل",
356    "ship.sameday": "في نفس اليوم",
357    "ship.items": "قطع",
358    "ship.oneitem": "قطعة واحدة",
359    "ship.none": "لا نوصّل إلى هذه الوجهة عبر الموقع بعد - تواصل معنا وسنرتّب ذلك.",
360    "search.placeholder": "ابحث بالاسم أو الوصف أو رقم الموديل",
361    "search.hint": "اكتب حرفين على الأقل. يشمل البحث اسم المنتج ووصفه ورقم الموديل.",
362    "search.none": "لا توجد نتائج لـ",
363    "search.ask": "نوفّر منتجات من خارج المخزون أسبوعياً - تواصل معنا.",
364    "search.shown": "من أقرب النتائج",
365    /* ---- live store: checkout, orders, accounts ---- */
366    "nav.myAccount": "حسابي",
367    "checkout.title": "إتمام الطلب",
368    "checkout.loading": "جارٍ تحميل سلتك...",
369    "order.title": "طلبك",
370    "order.loading": "جارٍ تحميل طلبك...",
371    "checkout.empty": "سلتك فارغة",
372    "checkout.emptyIssues": "المنتجات التي كانت فيها لم تعد متوفرة.",
373    "checkout.retry": "حاول مرة أخرى",
374    "checkout.signedInAs": "مسجّل الدخول باسم",
375    "checkout.haveAccount": "لديك حساب؟",
376    "checkout.orGuest": "لعناوينك المحفوظة ونقاطك - أو تابع كضيف.",
377    "checkout.contact": "بياناتك",
378    "account.company": "الشركة",
379    "checkout.newAddress": "عنوان جديد",
380    "checkout.line1": "المبنى والطريق والمجمع",
381    "checkout.line2": "الشقة أو المكتب أو معلم قريب",
382    "checkout.city": "المدينة أو المنطقة",
383    "checkout.postcode": "الرمز البريدي",
384    "checkout.saveAddress": "احفظ هذا العنوان في حسابي",
385    "checkout.delivery": "عنوان التوصيل",
386    "checkout.country": "الدولة",
387    "checkout.method": "طريقة التوصيل",
388    "checkout.usePoints": "استخدم نقاطي",
389    "checkout.nothingToPay": "نقاطك تغطي هذا الطلب - لا يوجد مبلغ مستحق.",
390    "checkout.noPayment": "الدفع الإلكتروني غير متاح لهذا الطلب. يرجى التواصل معنا لإتمامه.",
391    "checkout.payment": "الدفع",
392    "checkout.note": "ملاحظات الطلب",
393    "checkout.notePh": "تعليمات التوصيل، رقم أمر الشراء، مسؤول الموقع...",
394    "checkout.placeQuote": "أرسل الطلب - الدفع بعد تأكيد الشحن",
395    "checkout.payNow": "ادفع بأمان",
396    "checkout.place": "أرسل الطلب",
397    "checkout.summary": "طلبك",
398    "checkout.pointsDiscount": "النقاط",
399    "order.payTabby": "ادفع مع تابي",
400    "order.choosePay": "اختر طريقة الدفع:",
401    "order.payCard": "بطاقة أو بنفت أو Apple Pay",
402    "order.payTabby4": "قسّمها على 4 دفعات مع تابي",
403    "checkout.couponCovers": "القسيمة تغطي هذا الطلب - لا يوجد مبلغ مستحق.",
404    "coupon.title": "قسيمة خصم",
405    "coupon.row": "قسيمة",
406    "coupon.placeholder": "رمز القسيمة",
407    "coupon.apply": "تطبيق",
408    "coupon.remove": "إزالة القسيمة",
409    "coupon.yours": "قسائمك",
410    "coupon.use": "تطبيق",
411    "coupon.switch": "استخدمها بدلاً منها",
412    "coupon.off": "خصم",
413    "coupon.on": "على",
414    "coupon.upTo": "حتى",
415    "coupon.min": "عند الشراء بـ",
416    "coupon.notSale": "لا تشمل المنتجات المخفّضة",
417    "coupon.until": "حتى",
418    "coupon.mine": "قسائمي",
419    "coupon.applied": "مطبّقة على سلتك",
420    "coupon.openCart": "افتح السلة",
421    "coupon.useNow": "استخدمها الآن",
422    "coupon.saved": "تم تطبيق القسيمة - ستُخصم عند الدفع.",
423    "checkout.vatIncl": "شامل ضريبة القيمة المضافة",
424    "checkout.vatAbroad": "لا تُطبَّق ضريبة القيمة المضافة البحرينية على الطلبات خارج البحرين - الأسعار معروضة بدونها.",
425    "checkout.fx": "يتم الخصم بالدينار البحريني:",
426    "checkout.chargedIn": "سيتم الخصم بعملة",
427    "checkout.freightNote": "يحدد فريقنا تكلفة شحن هذا الطلب. نرسل لك الإجمالي النهائي ورابط الدفع عبر البريد - لا يتم خصم أي مبلغ الآن.",
428    "checkout.terms": "بإرسال الطلب فإنك توافق على شروط البيع. الأسعار تشمل ضريبة القيمة المضافة حيثما تنطبق.",
429    "checkout.secure": "تُدخل بيانات البطاقة في صفحة Tap الآمنة - لا نراها ولا نحتفظ بها.",
430    "order.thanks": "شكراً لك - تم تأكيد طلبك",
431    "order.emailed": "أرسلنا تأكيداً إلى",
432    "order.payFailed": "لم تتم عملية الدفع",
433    "order.payFailedBody": "لم يتم خصم أي مبلغ. طلبك محفوظ - يمكنك المحاولة مرة أخرى أدناه بالبطاقة نفسها أو بأخرى.",
434    "order.received": "شكراً لك - استلمنا طلبك",
435    "order.order": "الطلب",
436    "order.amountDue": "المبلغ المستحق",
437    "order.heldUntil": "منتجاتك محجوزة لك حتى",
438    "order.tryAgain": "أعد محاولة الدفع",
439    "order.payNow": "ادفع الآن",
440    "order.items": "المنتجات",
441    "order.refunded": "المبلغ المسترد",
442    "order.details": "تفاصيل الطلب",
443    "order.tracking": "رقم التتبع",
444    "order.trackSmsa": "تتبّع الشحنة لدى SMSA",
445    "account.track": "تتبّع",
446    "account.trackingNo": "رقم التتبع",
447    "account.trackTitle": "تتبّع طلبك",
448    "account.trackHelp": "أدخل رقم التتبع من رسالة الشحن. يصلك بعد حجز شحنة طلبك لدى SMSA.",
449    "account.trackNumber": "رقم التتبع",
450    "account.trackGo": "تتبّع",
451    "account.trackNone": "لا توجد حركة بعد - ستمسح SMSA الطرد عند استلامه.",
452    "account.trackSmsa": "عرضها على موقع SMSA",
453    "account.trackEmpty": "أدخل رقم التتبع.",
454    "order.help": "لديك سؤال عن هذا الطلب؟ اتصل بنا أو رد على رسالة التأكيد مع ذكر رقم الطلب.",
455    "order.notFound": "لم نتمكن من العثور على هذا الطلب.",
456    "account.loading": "جارٍ تحميل حسابك...",
457    "account.verifiedOk": "شكراً لك - تم تأكيد بريدك الإلكتروني.",
458    "account.verifyBad": "انتهت صلاحية رابط التأكيد أو تم استخدامه من قبل.",
459    "account.resend": "أرسل رابطاً جديداً",
460    "account.resent": "أرسلنا رابط تأكيد جديداً. تحقق من بريدك.",
461    "account.unverified": "يرجى تأكيد بريدك الإلكتروني - أرسلنا لك رابطاً عند التسجيل.",
462    "account.save": "حفظ",
463    "account.cancel": "إلغاء",
464    "account.changePassword": "تغيير كلمة المرور",
465    "account.currentPassword": "كلمة المرور الحالية",
466    "auth.newPassword": "كلمة المرور الجديدة",
467    "account.makeDefault": "اجعله الافتراضي",
468    "account.remove": "حذف",
469    "account.removeAsk": "هل تريد حذف هذا العنوان؟",
470    "account.saved": "تم حفظ بياناتك.",
471    "account.passwordSaved": "تم تغيير كلمة المرور وتسجيل الخروج من الأجهزة الأخرى.",
472    "auth.newPassTitle": "اختر كلمة مرور جديدة",
473    "auth.newPassLead": "اختر كلمة مرور لم تستخدمها هنا من قبل. ستُسجَّل دخولك مباشرة بعد ذلك.",
474    "auth.savePassword": "احفظ وسجّل الدخول",
475    "brands.none": "تظهر العلامات التجارية هنا عند إضافة المنتجات.",
476    "brands.product": "منتج",
477    "brands.products": "منتجات",
478    "checkout.secureTabby": "تُكمل الدفع على صفحة تابي الآمنة.",
479    "size.nocharts": "لم تُنشر جداول مقاسات بعد.",
480    "size.none": "لا يوجد جدول مقاسات منشور لهذه العلامة بعد. اسألنا وسنؤكد لك المقاس قبل الطلب.",
481    "size.sub": "القياسات من الشركة المصنّعة نفسها. يمكنك التبديل بين البوصة والسنتيمتر.",
482    "api.offline": "هذه نسخة معاينة - المتجر غير متصل.",
483    "api.failed": "لم تتم العملية. يرجى المحاولة مرة أخرى.",
484    "api.network": "تعذر الوصول إلى المتجر. تحقق من اتصالك وحاول مرة أخرى."
485  };
486
487  /* ---------------------------------------------------------------- phrases
488     Everything else on the page - text written straight into the HTML or put
489     there by a script - is translated by its English wording:
490       window.SNIPER_PHRASES   the built-in Arabic (shared/js/i18n-phrases.js)
491       window.SNIPER_I18N      what was changed in Admin > Translations (from
492                               business-data.js): { ar: {text: arabic}, en:
493                               {text: english wording}, defaultLang }
494     A key above ("nav.home") can be changed from the admin the same way.
495     Numbers in a phrase are placeholders - "Showing 12 of 40" is looked up as
496     "Showing {0} of {1}" - and go back in wrapped as left-to-right runs, so
497     prices, quantities and phone numbers never reverse in Arabic.
498     Text still in English on the Arabic site is reported (a handful at a time)
499     to Admin > Translations > Missing, so it can be translated there. */
500  var O = window.SNIPER_I18N || {};
501  var OAR = O.ar || {}, OEN = O.en || {};
502  var PH = window.SNIPER_PHRASES || {};
503  Object.keys(OAR).forEach(function (k) { if (/^[a-z0-9]+(\.[a-zA-Z0-9]+)+$/.test(k)) AR[k] = OAR[k]; });
504  var HAS = Object.prototype.hasOwnProperty;
505  var LRI = "\u2066", PDI = "\u2069";
506
507  var lang = "en";
508  try {
509    var q = new URLSearchParams(location.search).get("lang");
510    var saved = localStorage.getItem(KEY);
511    if (q === "ar" || q === "en") lang = q;
512    else if (saved === "ar" || saved === "en") lang = saved;
513    else if (O.defaultLang === "ar") lang = "ar";
514  } catch (err) { /* ignore */ }
515
516  /* Values that change from the admin rather than from the language - the
517     free-delivery threshold is the one that matters - are written into the
518     strings as {amount} and filled in here. Both languages take the same
519     value, so a number set once in the config is correct in both. The value
520     may carry markup (the threshold arrives as a <b data-bhd> the currency
521     switcher rewrites), so it is filled in after escaping, never before. */
522  function fill(s) {
523    var V = window.SNIPER_I18N_VARS;
524    if (!V || s == null || String(s).indexOf("{") === -1) return s;
525    return String(s).replace(/\{(\w+)\}/g, function (m, k) {
526      return V[k] != null ? V[k] : m;
527    });
528  }
529
530  function t(key, fallback) {
531    if (lang === "ar" && AR[key]) return AR[key];
532    if (lang === "en" && HAS.call(OEN, key)) return OEN[key];
533    return fallback;
534  }
535
536  /* Phone numbers must stay left-to-right even in Arabic: the bidi algorithm
537     otherwise reorders the groups and throws the + to the far end, so
538     "+973 1723 2066" renders as "2066 1723 973+". Wrapping just the number in
539     a <bdi dir="ltr"> isolates it. Only text nodes carrying digits are touched -
540     icons and everything else in the link are left exactly where they were. */
541  function isolatePhones() {
542    document.querySelectorAll('a[href^="tel:"]').forEach(function (a) {
543      if (a.dataset.telWrapped) return;
544      a.dataset.telWrapped = "1";
545      Array.prototype.slice.call(a.childNodes).forEach(function (n) {
546        if (n.nodeType === 3 && /\d/.test(n.nodeValue)) {
547          var b = document.createElement("bdi");
548          b.setAttribute("dir", "ltr");
549          b.textContent = n.nodeValue;
550          a.replaceChild(b, n);
551        }
552      });
553    });
554  }
555
556  /* ------------------------------------------------ the phrase pass */
557  var hasEn = Object.keys(OEN).length > 0;
558  var ORIG = new WeakMap(), WROTE = new WeakMap(), ATTR_ORIG = new WeakMap();
559  var ATTRS = ["placeholder", "title", "aria-label", "alt"];
560  var SKIP = "script,style,noscript,textarea,code,pre,[data-i18n],[data-noi18n],[contenteditable]";
561  /* product data - names, brands, descriptions - is the catalogue's, not the interface's */
562  var DATA_ZONE = ".prod__name,.prod__brand,#bbName,.buybox__brand,#pDesc,#pSpec,.pdp__desc,.cline__name,.co__lname,.brand-strip,[data-product-name],.gallery,.srch__name,.card-title,.legal__doc,[data-contact],.footer__contact,.topbar__contact";
563  var NUM = /\+?\d(?:[\d.,:\/-]*\d)?(?:\s\d(?:[\d.,:\/-]*\d)?)*/g;
564
565  function norm(s) { return s.replace(/[\s ]+/g, " ").trim(); }
566  function lookup(s) {
567    if (lang === "ar") {
568      if (HAS.call(OAR, s)) return OAR[s];
569      if (HAS.call(PH, s)) return PH[s];
570    } else if (HAS.call(OEN, s)) return OEN[s];
571    return null;
572  }
573  function iso(v) { return LRI + v + PDI; }
574  function translate(raw, report) {
575    var s = norm(raw);
576    if (!s) return null;
577    if (!/[A-Za-z]/.test(s)) {
578      /* a bare number with gaps in it (a phone number) must not reverse */
579      return lang === "ar" && /\d\s+\d/.test(s) && s.indexOf(LRI) === -1 ? iso(s) : null;
580    }
581    var hit = lookup(s);
582    if (hit != null) return hit;
583    if (/\d/.test(s)) {
584      var nums = [], i = 0;
585      var tpl = s.replace(NUM, function (m) { nums.push(m); return "{" + (i++) + "}"; });
586      hit = lookup(tpl);
587      if (hit != null) {
588        return hit.replace(/\{(\d+)\}/g, function (m, k) { return nums[k] != null ? (lang === "ar" ? iso(nums[k]) : nums[k]) : m; });
589      }
590      if (report) miss(tpl);
591    } else if (report) miss(s);
592    return null;
593  }
594  function textNode(n) {
595    var el = n.parentElement;
596    if (!el || el.closest(SKIP)) return;
597    var cur = n.nodeValue;
598    if (WROTE.get(n) !== cur) ORIG.set(n, cur);          // new wording from a script
599    var en = ORIG.get(n);
600    if (en == null || !/\S/.test(en)) return;
601    var out = null;
602    if (lang === "ar" || hasEn) out = translate(en, lang === "ar" && !el.closest(DATA_ZONE));
603    var lead = en.match(/^\s*/)[0], trail = en.match(/\s*$/)[0];
604    /* English that stays English on the Arabic page (a brand, a standard, the
605       company name, an address) keeps its own left-to-right order: without this
606       "EN ISO 20345" shows as "20345 EN ISO" and a closing full stop jumps to
607       the front */
608    if (lang === "ar") {
609      var body = out == null ? norm(en) : out;
610      if (/[A-Za-z]/.test(body) && /[\s\d.]/.test(body) && !/[\u0600-\u06FF]/.test(body) && body.indexOf(LRI) !== 0) out = iso(body.replace(/[\u2066\u2069]/g, ""));
611    }
612    var next = out == null ? en : lead + out + trail;
613    if (next !== cur) { WROTE.set(n, next); n.nodeValue = next; }
614    else WROTE.set(n, cur);
615  }
616  function attrs(el) {
617    if (el.closest(SKIP)) return;
618    var store = ATTR_ORIG.get(el);
619    ATTRS.forEach(function (a) {
620      if (!el.hasAttribute(a)) return;
621      var v = el.getAttribute(a);
622      if (!store) { store = {}; ATTR_ORIG.set(el, store); }
623      var rec = store[a];
624      if (!rec || rec.wrote !== v) rec = store[a] = { en: v, wrote: null };
625      var out = (lang === "ar" || hasEn) ? translate(rec.en, false) : null;
626      var next = out == null ? rec.en : out;
627      rec.wrote = next;
628      if (next !== v) el.setAttribute(a, next);
629    });
630  }
631  /* a data-i18n element a script adds after the page loaded (the cart drawer) */
632  function keyEl(n) {
633    if (n.getAttribute("data-i18n-lang") === lang) return;
634    var key = n.getAttribute("data-i18n");
635    if (!n.dataset.en) n.dataset.en = n.innerHTML;
636    var next = fill(lang === "ar" ? (AR[key] || n.dataset.en) : (HAS.c
636all(OEN, key) ? OEN[key] : n.dataset.en));
637    n.setAttribute("data-i18n-lang", lang);
638    if (n.innerHTML !== next) n.innerHTML = next;
639  }
640  var busy = false;
641  function phrasePass(root) {
642    if (!root || (lang !== "ar" && !hasEn && !touched)) return;
643    touched = touched || lang === "ar";
644    busy = true;
645    if (root.nodeType === 1 && root !== document.body) {
646      if (root.hasAttribute("data-i18n")) keyEl(root);
647      root.querySelectorAll("[data-i18n]").forEach(keyEl);
648    }
649    if (root.nodeType === 3) textNode(root);
650    else if (root.nodeType === 1) {
651      attrs(root);
652      var w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT), n;
653      while ((n = w.nextNode())) { if (n.nodeType === 3) textNode(n); else attrs(n); }
654    }
655    busy = false;
656  }
657  var touched = false, observer = null;
658  function onMutate(list) {
659    if (busy) return;
660    list.forEach(function (m) {
661      if (m.type === "characterData") phrasePass(m.target);
662      else if (m.type === "attributes") { busy = true; attrs(m.target); busy = false; }
663      else m.addedNodes.forEach(phrasePass);
664    });
665  }
666  var TITLE0 = null;
667  function translateTitle() {
668    if (TITLE0 == null || (document.title !== TITLE0.wrote)) TITLE0 = { en: document.title, wrote: null };
669    var parts = TITLE0.en.split(" | ").map(function (p) { var o = (lang === "ar" || hasEn) ? translate(p, false) : null; return o == null ? p : o.replace(/[⁦⁩]/g, ""); });
670    TITLE0.wrote = parts.join(" | ");
671    document.title = TITLE0.wrote;
672  }
673
674  /* English text left on the Arabic site goes to Admin > Translations */
675  var MISS = {}, missT = null, missN = 0;
676  function miss(s) {
677    if (s.length > 280 || /@|https?:|www\./.test(s) || !/[A-Za-z]{3,}/.test(s)) return;
678    if (HAS.call(MISS, s) || missN >= 60) return;
679    MISS[s] = 1; missN++;
680    clearTimeout(missT);
681    missT = setTimeout(sendMissing, 4000);
682  }
683  function sendMissing() {
684    var seen = {};
685    try { seen = JSON.parse(sessionStorage.getItem("sniper.i18n.sent") || "{}"); } catch (e) { /* ignore */ }
686    var list = Object.keys(MISS).filter(function (s) { return !seen[s]; });
687    MISS = {};
688    if (!list.length || !window.fetch || /^file:/.test(location.protocol)) return;
689    list.forEach(function (s) { seen[s] = 1; });
690    try { sessionStorage.setItem("sniper.i18n.sent", JSON.stringify(seen)); } catch (e) { /* ignore */ }
691    var body = JSON.stringify({ page: location.pathname, strings: list.slice(0, 60) });
692    try {
693      if (navigator.sendBeacon) navigator.sendBeacon("/api/i18n/missing", new Blob([body], { type: "text/plain" }));
694      else fetch("/api/i18n/missing", { method: "POST", body: body, headers: { "Content-Type": "text/plain" }, keepalive: true });
695    } catch (e) { /* ignore */ }
696  }
697
698  /* Product names and descriptions: the catalogue carries the Arabic as
699     nameAr / descAr / shortAr (Products > a product, or Translations > Products). In
700     Arabic each product's name and description are swapped for them as the
701     catalogue loads, so every card, product page, search result and cart line
702     shows them without knowing about languages. No Arabic yet - English stays. */
703  function localiseCatalog(list) {
704    if (!Array.isArray(list)) return;
705    list.forEach(function (p) {
706      if (!p || typeof p !== "object") return;
707      if (!p._en) {
708        try { Object.defineProperty(p, "_en", { value: { name: p.name, desc: p.desc, short: p.short }, enumerable: false }); } catch (e) { return; }
709      }
710      var ar = lang === "ar";
711      p.name = ar && p.nameAr ? p.nameAr : p._en.name;
712      p.desc = ar && p.descAr ? p.descAr : p._en.desc;
713      p.short = ar ? (p.shortAr || (p.descAr ? "" : p._en.short)) : p._en.short;
714    });
715  }
716  (function () {
717    var cat = window.SNIPER_CATALOG;
718    localiseCatalog(cat);
719    try {
720      Object.defineProperty(window, "SNIPER_CATALOG", {
721        configurable: true, enumerable: true,
722        get: function () { return cat; },
723        set: function (v) { cat = v; localiseCatalog(cat); }
724      });
725    } catch (e) { /* leave it as a plain value */ }
726  })();
727
728  function apply() {
729    localiseCatalog(window.SNIPER_CATALOG);
730    var html = document.documentElement;
731    html.setAttribute("lang", lang);
732    html.setAttribute("dir", lang === "ar" ? "rtl" : "ltr");
733    document.body.classList.toggle("is-ar", lang === "ar");
734
735    if (lang === "ar" && !document.getElementById("ar-font")) {
736      var l = document.createElement("link");
737      l.id = "ar-font";
738      l.rel = "stylesheet";
739      l.href = "https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700&display=swap";
740      document.head.appendChild(l);
741    }
742
743    document.querySelectorAll("[data-i18n]").forEach(function (n) {
744      var key = n.getAttribute("data-i18n");
745      if (!n.dataset.en) n.dataset.en = n.innerHTML;
746      n.setAttribute("data-i18n-lang", lang);
747      n.innerHTML = fill(lang === "ar" ? (AR[key] || n.dataset.en) : (HAS.c
747all(OEN, key) ? OEN[key] : n.dataset.en));
748    });
749
750    document.querySelectorAll("[data-i18n-attr]").forEach(function (n) {
751      n.getAttribute("data-i18n-attr").split(",").forEach(function (pair) {
752        var bits = pair.split(":");
753        var attr = bits[0].trim(), key = bits[1].trim();
754        /* dataset keys cannot hold "-x": aria-label is kept as data-en-aria-label */
755        var store = "en" + attr.replace(/(^|-)([a-z])/g, function (m, d, c) { return c.toUpperCase(); });
756        if (!n.dataset[store]) n.dataset[store] = n.getAttribute(attr) || "";
757        n.setAttribute(attr, lang === "ar" ? (AR[key] || n.dataset[store]) : n.dataset[store]);
758      });
759    });
760
761    isolatePhones();
762    phrasePass(document.body);
763    if (lang === "ar" || hasEn) {
764      if (!observer && window.MutationObserver) {
765        observer = new MutationObserver(onMutate);
766        observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ATTRS });
767      }
768    }
769    translateTitle();
770
771    document.querySelectorAll(".js-lang").forEach(function (n) {
772      n.textContent = lang === "ar" ? "EN" : "AR";
773    });
774    document.dispatchEvent(new CustomEvent("sniper:lang", { detail: { lang: lang } }));
775  }
776
777  function set(next) {
778    lang = next;
779    try { localStorage.setItem(KEY, next); } catch (err) { /* ignore */ }
780    apply();
781  }
782
783  document.addEventListener("click", function (e) {
784    var b = e.target.closest(".js-lang");
785    if (!b) return;
786    e.preventDefault();
787    set(lang === "ar" ? "en" : "ar");
788  });
789
790  window.sniperI18n = { t: t, set: set, fill: fill, get lang() { return lang; }, dict: AR,
791    /* a phrase in the current language, for scripts that build text themselves */
792    p: function (en) { var o = (lang === "ar" || hasEn) ? translate(en, false) : null; return o == null ? en : o; } };
793
794  /* This script sits at the top of <body>, so at this point the page below it
795     has not been parsed - there is nothing to translate yet. Set the document
796     direction immediately (it must be right before the first paint), then do
797     the text pass once the DOM exists. */
798  document.documentElement.setAttribute("lang", lang);
799  document.documentElement.setAttribute("dir", lang === "ar" ? "rtl" : "ltr");
800  if (document.readyState === "loading") {
801    document.addEventListener("DOMContentLoaded", apply);
802  } else {
803    apply();
804  }
805})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.