Files
zerp/frontend/src/contexts/LanguageContext.tsx
2026-03-11 16:40:25 +04:00

626 lines
23 KiB
TypeScript

'use client'
import { createContext, useContext, useState, useEffect, ReactNode } from 'react'
type Language = 'en' | 'ar'
interface LanguageContextType {
language: Language
setLanguage: (lang: Language) => void
t: (key: string) => string
dir: 'ltr' | 'rtl'
}
const LanguageContext = createContext<LanguageContextType | undefined>(undefined)
export function LanguageProvider({ children }: { children: ReactNode }) {
const [language, setLanguageState] = useState<Language>('en')
useEffect(() => {
// Load language from localStorage
const savedLang = localStorage.getItem('language') as Language
if (savedLang && (savedLang === 'en' || savedLang === 'ar')) {
setLanguageState(savedLang)
}
}, [])
useEffect(() => {
// Update document direction and lang attribute
document.documentElement.lang = language
document.documentElement.dir = language === 'ar' ? 'rtl' : 'ltr'
}, [language])
const setLanguage = (lang: Language) => {
setLanguageState(lang)
localStorage.setItem('language', lang)
}
const t = (key: string): string => {
const keys = key.split('.')
let value: any = translations[language]
for (const k of keys) {
value = value?.[k]
}
return value || key
}
return (
<LanguageContext.Provider
value={{
language,
setLanguage,
t,
dir: language === 'ar' ? 'rtl' : 'ltr'
}}
>
{children}
</LanguageContext.Provider>
)
}
export function useLanguage() {
const context = useContext(LanguageContext)
if (!context) {
throw new Error('useLanguage must be used within a LanguageProvider')
}
return context
}
// Translation dictionary
const translations = {
en: {
common: {
save: 'Save',
cancel: 'Cancel',
delete: 'Delete',
edit: 'Edit',
add: 'Add',
search: 'Search',
filter: 'Filter',
export: 'Export',
import: 'Import',
loading: 'Loading...',
noData: 'No data available',
error: 'An error occurred',
success: 'Success',
confirm: 'Confirm',
back: 'Back',
next: 'Next',
finish: 'Finish',
close: 'Close',
yes: 'Yes',
no: 'No',
required: 'Required',
optional: 'Optional',
actions: 'Actions',
status: 'Status',
active: 'Active',
inactive: 'Inactive',
archived: 'Archived',
deleted: 'Deleted'
},
nav: {
dashboard: 'Dashboard',
contacts: 'Contacts',
crm: 'CRM',
projects: 'Projects',
inventory: 'Inventory',
hr: 'HR',
marketing: 'Marketing',
settings: 'Settings',
logout: 'Logout'
},
contacts: {
title: 'Contacts',
addContact: 'Add Contact',
editContact: 'Edit Contact',
deleteContact: 'Delete Contact',
viewContact: 'View Contact',
mergeContacts: 'Merge Contacts',
importContacts: 'Import Contacts',
exportContacts: 'Export Contacts',
totalContacts: 'Total Contacts',
searchPlaceholder: 'Search by name, email, or phone...',
noContactsFound: 'No contacts found',
contactDetails: 'Contact Details',
contactInfo: 'Contact Information',
companyInfo: 'Company Information',
address: 'Address',
categories: 'Categories & Tags',
relationships: 'Relationships',
hierarchy: 'Hierarchy',
activities: 'Activities',
history: 'History',
type: 'Type',
name: 'Name',
nameAr: 'Name (Arabic)',
email: 'Email',
phone: 'Phone',
mobile: 'Mobile',
website: 'Website',
companyName: 'Company Name',
companyNameAr: 'Company Name (Arabic)',
taxNumber: 'Tax Number',
commercialRegister: 'Commercial Register',
city: 'City',
country: 'Country',
postalCode: 'Postal Code',
source: 'Source',
rating: 'Rating',
tags: 'Tags',
individual: 'Individual',
company: 'Company',
holding: 'Holding',
government: 'Government',
addRelationship: 'Add Relationship',
relationshipType: 'Relationship Type',
startDate: 'Start Date',
endDate: 'End Date',
notes: 'Notes',
representative: 'Representative',
partner: 'Partner',
supplier: 'Supplier',
employee: 'Employee',
subsidiary: 'Subsidiary',
branch: 'Branch',
parentCompany: 'Parent Company',
customer: 'Customer',
vendor: 'Vendor',
companyEmployee: 'Company Employee',
other: 'Other',
duplicateFound: 'Potential Duplicates Found',
duplicateWarning: 'Similar contacts found. Please review before continuing.',
mergeInstead: 'Merge Instead',
continueAnyway: 'Continue Anyway',
sourceContact: 'Source Contact',
targetContact: 'Target Contact',
compareFields: 'Compare Fields',
preview: 'Preview',
mergeWarning: 'This action cannot be undone!',
mergeReason: 'Reason for Merge',
mergeSuccess: 'Contacts merged successfully!',
importSuccess: 'Contacts imported successfully',
exportSuccess: 'Contacts exported successfully',
deleteConfirm: 'Are you sure you want to delete this contact?',
deleteSuccess: 'Contact deleted successfully',
createSuccess: 'Contact created successfully',
updateSuccess: 'Contact updated successfully'
},
crm: {
title: 'CRM',
subtitle: 'CRM & Sales Pipeline',
addDeal: 'Add Deal',
editDeal: 'Edit Deal',
dealName: 'Deal Name',
contact: 'Contact',
structure: 'Deal Structure',
pipeline: 'Pipeline',
stage: 'Stage',
probability: 'Probability',
estimatedValue: 'Estimated Value (SAR)',
expectedCloseDate: 'Expected Close Date',
searchPlaceholder: 'Search deals...',
filterStructure: 'Structure',
filterStage: 'Stage',
filterStatus: 'Status',
all: 'All',
view: 'View',
win: 'Win',
lose: 'Lose',
archive: 'Archive',
deleteDeal: 'Delete Deal',
markWon: 'Mark as Won',
markLost: 'Mark as Lost',
actualValue: 'Actual Value (SAR)',
wonReason: 'Reason Won',
lostReason: 'Reason Lost',
noDealsFound: 'No deals found',
createSuccess: 'Deal created successfully',
updateSuccess: 'Deal updated successfully',
winSuccess: 'Deal won successfully',
loseSuccess: 'Deal marked as lost',
deleteSuccess: 'Deal archived successfully',
fixFormErrors: 'Please fix form errors',
pipelineRequired: 'Pipeline is required',
dealNameMin: 'Deal name must be at least 3 characters',
contactRequired: 'Contact is required',
structureRequired: 'Deal structure is required',
stageRequired: 'Stage is required',
valueRequired: 'Estimated value must be greater than 0',
selectPipeline: 'Select Pipeline',
selectContact: 'Select Contact',
enterDealName: 'Enter deal name',
structureB2B: 'B2B - شركة لشركة',
structureB2C: 'B2C - شركة لفرد',
structureB2G: 'B2G - شركة لحكومة',
structurePartnership: 'Partnership - شراكة',
dealDetail: 'Deal Details',
quotes: 'Quotes',
history: 'History',
dealInfo: 'Deal Info',
quickActions: 'Quick Actions',
totalValue: 'Total Value',
expectedValue: 'Expected Value',
activeDeals: 'Active Deals',
wonDeals: 'Won Deals',
inPipeline: 'In pipeline',
winRate: 'win rate',
conversion: 'conversion',
retry: 'Retry',
createFirstDeal: 'Create First Deal',
loadingDeals: 'Loading deals...',
creating: 'Creating...',
updating: 'Updating...',
updateDeal: 'Update Deal',
createDeal: 'Create Deal',
newDeal: 'New Deal',
allStructures: 'All Structures',
allStages: 'All Stages',
allStatus: 'All Status',
deal: 'Deal',
value: 'Value',
owner: 'Owner',
markDealWon: 'Mark Deal as Won',
markDealLost: 'Mark Deal as Lost',
reasonForWinning: 'Reason for Winning',
reasonForLosing: 'Reason for Losing',
winPlaceholder: 'Why did we win this deal?',
losePlaceholder: 'Why did we lose this deal?',
createNewDeal: 'Create New Deal',
paginationPrevious: 'Previous',
paginationNext: 'Next',
processing: 'Processing...',
deleting: 'Deleting...',
deleteDealConfirm: 'Are you sure you want to delete',
deleteDealDesc: 'This will mark the deal as lost',
costSheets: 'Cost Sheets',
contracts: 'Contracts',
invoices: 'Invoices',
addCostSheet: 'Add Cost Sheet',
addContract: 'Add Contract',
addInvoice: 'Add Invoice',
approve: 'Approve',
reject: 'Reject',
markSigned: 'Mark Signed',
recordPayment: 'Record Payment',
costSheetApproved: 'Cost sheet approved',
costSheetRejected: 'Cost sheet rejected',
contractSigned: 'Contract signed',
paymentRecorded: 'Payment recorded',
costSheetCreated: 'Cost sheet created',
contractCreated: 'Contract created',
invoiceCreated: 'Invoice created',
costSheetItems: 'Cost items (description, source, cost, quantity)',
invoiceItems: 'Line items (description, quantity, unit price)',
description: 'Description',
source: 'Source',
addRow: 'Add row',
totalCost: 'Total Cost',
suggestedPrice: 'Suggested Price',
profitMargin: 'Profit Margin',
contractTitle: 'Contract Title',
contractType: 'Contract Type',
contractTypeSales: 'Sales',
contractTypeService: 'Service',
contractTypeMaintenance: 'Maintenance',
contractValue: 'Contract Value',
startDate: 'Start Date',
endDate: 'End Date',
paymentTerms: 'Payment Terms',
deliveryTerms: 'Delivery Terms',
terms: 'Terms & Conditions',
subtotal: 'Subtotal',
taxAmount: 'Tax Amount',
total: 'Total',
dueDate: 'Due Date',
paidAmount: 'Paid Amount',
paidDate: 'Paid Date'
},
import: {
title: 'Import Contacts',
downloadTemplate: 'Download Excel Template',
dragDrop: 'Drag & drop an Excel or CSV file here',
orClick: 'or click to select a file',
fileRequirements: 'File Requirements:',
step: 'Step',
uploading: 'Uploading...',
importing: 'Importing...',
rowsPreview: 'rows to preview',
warning: 'Warning',
duplicateHandling: 'Duplicate contacts will be skipped and logged in the error report.',
results: 'Results',
successful: 'Successful',
duplicates: 'Duplicates',
failed: 'Failed',
errors: 'Errors',
downloadErrorReport: 'Download Error Report',
importComplete: 'Import completed'
},
messages: {
loginSuccess: 'Login successful',
loginError: 'Invalid credentials',
networkError: 'Network error. Please check your connection.',
permissionDenied: 'Permission denied',
sessionExpired: 'Session expired. Please login again.'
}
},
ar: {
common: {
save: 'حفظ',
cancel: 'إلغاء',
delete: 'حذف',
edit: 'تعديل',
add: 'إضافة',
search: 'بحث',
filter: 'تصفية',
export: 'تصدير',
import: 'استيراد',
loading: 'جاري التحميل...',
noData: 'لا توجد بيانات',
error: 'حدث خطأ',
success: 'نجح',
confirm: 'تأكيد',
back: 'رجوع',
next: 'التالي',
finish: 'إنهاء',
close: 'إغلاق',
yes: 'نعم',
no: 'لا',
required: 'مطلوب',
optional: 'اختياري',
actions: 'إجراءات',
status: 'الحالة',
active: 'نشط',
inactive: 'غير نشط',
archived: 'مؤرشف',
deleted: 'محذوف'
},
nav: {
dashboard: 'لوحة التحكم',
contacts: 'جهات الاتصال',
crm: 'إدارة العملاء',
projects: 'المشاريع',
inventory: 'المخزون',
hr: 'الموارد البشرية',
marketing: 'التسويق',
settings: 'الإعدادات',
logout: 'تسجيل الخروج'
},
contacts: {
title: 'جهات الاتصال',
addContact: 'إضافة جهة اتصال',
editContact: 'تعديل جهة الاتصال',
deleteContact: 'حذف جهة الاتصال',
viewContact: 'عرض جهة الاتصال',
mergeContacts: 'دمج جهات الاتصال',
importContacts: 'استيراد جهات الاتصال',
exportContacts: 'تصدير جهات الاتصال',
totalContacts: 'إجمالي جهات الاتصال',
searchPlaceholder: 'البحث بالاسم أو البريد الإلكتروني أو الهاتف...',
noContactsFound: 'لم يتم العثور على جهات اتصال',
contactDetails: 'تفاصيل جهة الاتصال',
contactInfo: 'معلومات الاتصال',
companyInfo: 'معلومات الشركة',
address: 'العنوان',
categories: 'الفئات والعلامات',
relationships: 'العلاقات',
hierarchy: 'الهيكل التنظيمي',
activities: 'الأنشطة',
history: 'السجل',
type: 'النوع',
name: 'الاسم',
nameAr: 'الاسم (بالعربية)',
email: 'البريد الإلكتروني',
phone: 'الهاتف',
mobile: 'الجوال',
website: 'الموقع الإلكتروني',
companyName: 'اسم الشركة',
companyNameAr: 'اسم الشركة (بالعربية)',
taxNumber: 'الرقم الضريبي',
commercialRegister: 'السجل التجاري',
city: 'المدينة',
country: 'الدولة',
postalCode: 'الرمز البريدي',
source: 'المصدر',
rating: 'التقييم',
tags: 'العلامات',
individual: 'فرد',
company: 'شركة',
holding: 'مجموعة',
government: 'حكومي',
addRelationship: 'إضافة علاقة',
relationshipType: 'نوع العلاقة',
startDate: 'تاريخ البداية',
endDate: 'تاريخ النهاية',
notes: 'ملاحظات',
representative: 'ممثل',
partner: 'شريك',
supplier: 'مورد',
employee: 'موظف',
subsidiary: 'فرع تابع',
branch: 'فرع',
parentCompany: 'الشركة الأم',
customer: 'عميل',
vendor: 'بائع',
companyEmployee: 'موظف الشركة',
other: 'أخرى',
duplicateFound: 'تم العثور على جهات اتصال مشابهة',
duplicateWarning: 'تم العثور على جهات اتصال مشابهة. يرجى المراجعة قبل المتابعة.',
mergeInstead: 'دمج بدلاً من ذلك',
continueAnyway: 'متابعة على أي حال',
sourceContact: 'جهة الاتصال المصدر',
targetContact: 'جهة الاتصال الهدف',
compareFields: 'مقارنة الحقول',
preview: 'معاينة',
mergeWarning: 'لا يمكن التراجع عن هذا الإجراء!',
mergeReason: 'سبب الدمج',
mergeSuccess: 'تم دمج جهات الاتصال بنجاح!',
importSuccess: 'تم استيراد جهات الاتصال بنجاح',
exportSuccess: 'تم تصدير جهات الاتصال بنجاح',
deleteConfirm: 'هل أنت متأكد من حذف جهة الاتصال هذه؟',
deleteSuccess: 'تم حذف جهة الاتصال بنجاح',
createSuccess: 'تم إنشاء جهة الاتصال بنجاح',
updateSuccess: 'تم تحديث جهة الاتصال بنجاح'
},
crm: {
title: 'إدارة العملاء',
subtitle: 'إدارة العلاقات والمبيعات',
addDeal: 'إضافة صفقة',
editDeal: 'تعديل الصفقة',
dealName: 'اسم الصفقة',
contact: 'جهة الاتصال',
structure: 'هيكل الصفقة',
pipeline: 'مسار المبيعات',
stage: 'المرحلة',
probability: 'احتمالية الفوز',
estimatedValue: 'القيمة المقدرة (ر.س)',
expectedCloseDate: 'تاريخ الإغلاق المتوقع',
searchPlaceholder: 'البحث في الصفقات...',
filterStructure: 'الهيكل',
filterStage: 'المرحلة',
filterStatus: 'الحالة',
all: 'الكل',
view: 'عرض',
win: 'فوز',
lose: 'خسارة',
archive: 'أرشفة',
deleteDeal: 'حذف الصفقة',
markWon: 'تحديد كفائز',
markLost: 'تحديد كخاسر',
actualValue: 'القيمة الفعلية (ر.س)',
wonReason: 'سبب الفوز',
lostReason: 'سبب الخسارة',
noDealsFound: 'لم يتم العثور على صفقات',
createSuccess: 'تم إنشاء الصفقة بنجاح',
updateSuccess: 'تم تحديث الصفقة بنجاح',
winSuccess: 'تم الفوز بالصفقة بنجاح',
loseSuccess: 'تم تحديد الصفقة كخاسرة',
deleteSuccess: 'تم أرشفة الصفقة بنجاح',
fixFormErrors: 'يرجى إصلاح أخطاء النموذج',
pipelineRequired: 'مسار المبيعات مطلوب',
dealNameMin: 'اسم الصفقة يجب أن يكون 3 أحرف على الأقل',
contactRequired: 'جهة الاتصال مطلوبة',
structureRequired: 'هيكل الصفقة مطلوب',
stageRequired: 'المرحلة مطلوبة',
valueRequired: 'القيمة المقدرة يجب أن تكون أكبر من 0',
selectPipeline: 'اختر المسار',
selectContact: 'اختر جهة الاتصال',
enterDealName: 'أدخل اسم الصفقة',
structureB2B: 'B2B - شركة لشركة',
structureB2C: 'B2C - شركة لفرد',
structureB2G: 'B2G - شركة لحكومة',
structurePartnership: 'شراكة - Partnership',
dealDetail: 'تفاصيل الصفقة',
quotes: 'عروض الأسعار',
history: 'السجل',
dealInfo: 'معلومات الصفقة',
quickActions: 'إجراءات سريعة',
totalValue: 'إجمالي القيمة',
expectedValue: 'القيمة المتوقعة',
activeDeals: 'الصفقات النشطة',
wonDeals: 'الصفقات الرابحة',
inPipeline: 'في المسار',
winRate: 'معدل الفوز',
conversion: 'التحويل',
retry: 'إعادة المحاولة',
createFirstDeal: 'إنشاء أول صفقة',
loadingDeals: 'جاري تحميل الصفقات...',
creating: 'جاري الإنشاء...',
updating: 'جاري التحديث...',
updateDeal: 'تحديث الصفقة',
createDeal: 'إنشاء الصفقة',
newDeal: 'صفقة جديدة',
allStructures: 'جميع الهياكل',
allStages: 'جميع المراحل',
allStatus: 'جميع الحالات',
deal: 'الصفقة',
value: 'القيمة',
owner: 'المالك',
markDealWon: 'تحديد الصفقة كرابحة',
markDealLost: 'تحديد الصفقة كخاسرة',
reasonForWinning: 'سبب الفوز',
reasonForLosing: 'سبب الخسارة',
winPlaceholder: 'لماذا ربحنا هذه الصفقة؟',
losePlaceholder: 'لماذا خسرنا هذه الصفقة؟',
createNewDeal: 'إنشاء صفقة جديدة',
paginationPrevious: 'السابق',
paginationNext: 'التالي',
processing: 'جاري المعالجة...',
deleting: 'جاري الحذف...',
deleteDealConfirm: 'هل أنت متأكد من حذف',
deleteDealDesc: 'سيتم تحديد الصفقة كخاسرة',
costSheets: 'كشوفات التكلفة',
contracts: 'العقود',
invoices: 'الفواتير',
addCostSheet: 'إضافة كشف تكلفة',
addContract: 'إضافة عقد',
addInvoice: 'إضافة فاتورة',
approve: 'موافقة',
reject: 'رفض',
markSigned: 'توقيع',
recordPayment: 'تسجيل الدفع',
costSheetApproved: 'تمت الموافقة على كشف التكلفة',
costSheetRejected: 'تم رفض كشف التكلفة',
contractSigned: 'تم توقيع العقد',
paymentRecorded: 'تم تسجيل الدفع',
costSheetCreated: 'تم إنشاء كشف التكلفة',
contractCreated: 'تم إنشاء العقد',
invoiceCreated: 'تم إنشاء الفاتورة',
costSheetItems: 'بنود التكلفة (الوصف، المصدر، التكلفة، الكمية)',
invoiceItems: 'بنود الفاتورة (الوصف، الكمية، سعر الوحدة)',
description: 'الوصف',
source: 'المصدر',
addRow: 'إضافة صف',
totalCost: 'إجمالي التكلفة',
suggestedPrice: 'السعر المقترح',
profitMargin: 'هامش الربح',
contractTitle: 'عنوان العقد',
contractType: 'نوع العقد',
contractTypeSales: 'مبيعات',
contractTypeService: 'خدمة',
contractTypeMaintenance: 'صيانة',
contractValue: 'قيمة العقد',
startDate: 'تاريخ البداية',
endDate: 'تاريخ النهاية',
paymentTerms: 'شروط الدفع',
deliveryTerms: 'شروط التسليم',
terms: 'الشروط والأحكام',
subtotal: 'المجموع الفرعي',
taxAmount: 'ضريبة',
total: 'الإجمالي',
dueDate: 'تاريخ الاستحقاق',
paidAmount: 'المبلغ المدفوع',
paidDate: 'تاريخ الدفع'
},
import: {
title: 'استيراد جهات الاتصال',
downloadTemplate: 'تحميل قالب Excel',
dragDrop: 'اسحب وأفلت ملف Excel أو CSV هنا',
orClick: 'أو انقر لتحديد ملف',
fileRequirements: 'متطلبات الملف:',
step: 'خطوة',
uploading: 'جاري الرفع...',
importing: 'جاري الاستيراد...',
rowsPreview: 'صفوف للمعاينة',
warning: 'تنبيه',
duplicateHandling: 'سيتم تخطي جهات الاتصال المكررة وتسجيلها في تقرير الأخطاء.',
results: 'النتائج',
successful: 'ناجح',
duplicates: 'مكرر',
failed: 'فشل',
errors: 'أخطاء',
downloadErrorReport: 'تحميل تقرير الأخطاء',
importComplete: 'اكتمل الاستيراد'
},
messages: {
loginSuccess: 'تم تسجيل الدخول بنجاح',
loginError: 'بيانات الدخول غير صحيحة',
networkError: 'خطأ في الشبكة. يرجى التحقق من الاتصال.',
permissionDenied: 'غير مصرح',
sessionExpired: 'انتهت الجلسة. يرجى تسجيل الدخول مرة أخرى.'
}
}
}