/** * OmniCard AI 访客端多语言词典(镜像 admin/src/locale/omni.ts) * 支持 zh-CN / en-US / ar-SA / es-ES / de-DE / fr-FR / ja-JP 与 RTL */ export type OmniLocale = | 'zh-CN' | 'en-US' | 'ar-SA' | 'es-ES' | 'de-DE' | 'fr-FR' | 'ja-JP'; export type Direction = 'ltr' | 'rtl'; export interface LocaleMeta { code: OmniLocale; name: string; dir: Direction; } export type OmniMessageKey = | 'common.confirm' | 'common.cancel' | 'common.back' | 'common.save' | 'common.delete' | 'common.edit' | 'common.search' | 'common.loading' | 'common.success' | 'common.error' | 'common.empty' | 'common.app_name' | 'portal.preview_mode' | 'card.save_contact' | 'card.exchange' | 'card.ai_chat' | 'card.share' | 'card.scan_nfc' | 'card.view_profile' | 'radar.online_visitor' | 'radar.active_now' | 'radar.visitor_trace' | 'radar.lead_score' | 'crm.contacts' | 'crm.leads' | 'crm.follow_up' | 'portal.unavailable' | 'portal.retry' | 'portal.pronounce' | 'portal.pronouncing' | 'portal.whatsapp' | 'portal.linkedin' | 'portal.telegram' | 'portal.twitter' | 'portal.wechat' | 'portal.line' | 'portal.kakao' | 'portal.email' | 'portal.phone' | 'portal.case' | 'portal.article' | 'portal.social' | 'portal.video' | 'portal.block' | 'portal.contact_unset' | 'portal.copied' | 'portal.copied_link' | 'portal.copied_whatsapp' | 'portal.copied_linkedin' | 'portal.copied_email' | 'portal.copied_telegram' | 'portal.copied_twitter' | 'portal.copied_wechat' | 'portal.copied_line' | 'portal.copied_kakao' | 'portal.collect' | 'portal.collected' | 'portal.appointment' | 'portal.appointment_desc' | 'portal.book_now' | 'portal.google_cal' | 'portal.outlook_cal' | 'portal.ics_download' | 'portal.copied_meeting_link' | 'portal.appointment_form_title' | 'portal.appointment_form_desc' | 'portal.appointment_name' | 'portal.appointment_contact' | 'portal.appointment_company' | 'portal.appointment_time' | 'portal.appointment_time_video' | 'portal.appointment_time_offline' | 'portal.appointment_time_call' | 'portal.appointment_notes' | 'portal.appointment_notes_placeholder' | 'portal.appointment_photos' | 'portal.appointment_photos_tip' | 'portal.appointment_submit' | 'portal.appointment_submitting' | 'portal.appointment_success_tip' | 'portal.appointment_fill_required' | 'portal.cookie_notice' | 'portal.cookie_accept' | 'portal.cookie_reject' | 'tab.workplace' | 'tab.card' | 'tab.radar' | 'tab.leads' | 'tab.profile'; export interface OmniLocaleConfig { code: OmniLocale; name: string; dir: Direction; messages: Record; } export const omniLocales: Record = { 'zh-CN': { code: 'zh-CN', name: '简体中文', dir: 'ltr', messages: { 'common.confirm': '确认', 'common.cancel': '取消', 'common.back': '返回', 'common.save': '保存', 'common.delete': '删除', 'common.edit': '编辑', 'common.search': '搜索', 'common.loading': '加载中...', 'common.success': '操作成功', 'common.error': '操作失败', 'common.empty': '暂无数据', 'common.app_name': 'OmniCard', 'portal.preview_mode': '预览中', 'card.save_contact': '保存到通讯录', 'card.exchange': '交换名片', 'card.ai_chat': '在线咨询', 'card.share': '分享名片', 'card.scan_nfc': 'NFC 感应', 'card.view_profile': '查看微官网', 'radar.online_visitor': '在线访客', 'radar.active_now': '当前活跃', 'radar.visitor_trace': '访问轨迹', 'radar.lead_score': '意向度评分', 'crm.contacts': '联系人列表', 'crm.leads': '客户管理', 'crm.follow_up': '跟进记录', 'portal.unavailable': '微官网暂不可用', 'portal.retry': '重新加载', 'portal.pronounce': '发音', 'portal.pronouncing': '播放中', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': '微信', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': '邮件', 'portal.phone': '电话', 'portal.case': '客户案例', 'portal.article': '专栏文章', 'portal.social': '社交媒体', 'portal.video': '视频演示', 'portal.block': '板块内容', 'portal.contact_unset': '该联系方式未设置', 'portal.copied': '已复制', 'portal.copied_link': '直链已复制', 'portal.copied_whatsapp': 'WhatsApp 号码已复制', 'portal.copied_linkedin': 'LinkedIn 链接已复制', 'portal.copied_email': '邮箱地址已复制', 'portal.copied_telegram': 'Telegram 账号已复制', 'portal.copied_twitter': 'X (Twitter) 链接已复制', 'portal.copied_wechat': '微信号已复制', 'portal.copied_line': 'LINE 链接已复制', 'portal.copied_kakao': 'KakaoTalk 链接已复制', 'portal.collect': '收入名片夹', 'portal.collected': '已收入', 'portal.appointment': '预约商谈', 'portal.appointment_desc': '预约 30 分钟视频会议或线下商谈', 'portal.book_now': '立即预约', 'portal.google_cal': 'Google 日历', 'portal.outlook_cal': 'Outlook 日历', 'portal.ics_download': '下载 ICS 文件', 'portal.copied_meeting_link': '日程邀请链接已复制', 'portal.appointment_form_title': '预约商务会谈', 'portal.appointment_form_desc': '填写沟通需求与期望时间', 'portal.appointment_name': '您的姓名', 'portal.appointment_contact': '联系电话 / WhatsApp / 微信', 'portal.appointment_company': '所属公司 (选填)', 'portal.appointment_time': '沟通形式与时间', 'portal.appointment_time_video': '视频会议 (30分钟)', 'portal.appointment_time_offline': '线下商谈 / 拜访', 'portal.appointment_time_call': '电话沟通', 'portal.appointment_notes': '合作需求与议题', 'portal.appointment_notes_placeholder': '请简述您的业务需求或合作意向', 'portal.appointment_photos': '相关材料或照片 (选填)', 'portal.appointment_photos_tip': '可上传名片、材料或样品照片,最多3张', 'portal.appointment_submit': '提交预约', 'portal.appointment_submitting': '提交中...', 'portal.appointment_success_tip': '预约已提交,将尽快与您联系', 'portal.appointment_fill_required': '请填写姓名和有效联系方式', 'portal.cookie_notice': '我们使用 Cookie 改善体验并分析访问。继续浏览前请确认是否同意。', 'portal.cookie_accept': '同意', 'portal.cookie_reject': '拒绝', 'tab.workplace': '工作台', 'tab.card': '名片', 'tab.radar': '雷达', 'tab.leads': '潜客', 'tab.profile': '我的', }, }, 'en-US': { code: 'en-US', name: 'English', dir: 'ltr', messages: { 'common.confirm': 'Confirm', 'common.cancel': 'Cancel', 'common.back': 'Back', 'common.save': 'Save', 'common.delete': 'Delete', 'common.edit': 'Edit', 'common.search': 'Search', 'common.loading': 'Loading...', 'common.success': 'Success', 'common.error': 'Error occurred', 'common.empty': 'No data available', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'Preview', 'card.save_contact': 'Save Contact', 'card.exchange': 'Exchange Card', 'card.ai_chat': 'Inquire', 'card.share': 'Share Card', 'card.scan_nfc': 'NFC Tap', 'card.view_profile': 'View Profile', 'radar.online_visitor': 'Online Visitors', 'radar.active_now': 'Active Now', 'radar.visitor_trace': 'Visitor Trail', 'radar.lead_score': 'Lead Score', 'crm.contacts': 'Contacts', 'crm.leads': 'Leads', 'crm.follow_up': 'Follow-up', 'portal.unavailable': 'This page is currently unavailable', 'portal.retry': 'Reload', 'portal.pronounce': 'Pronounce', 'portal.pronouncing': 'Playing', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': 'WeChat', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'Email', 'portal.phone': 'Call', 'portal.case': 'Case Study', 'portal.article': 'Article', 'portal.social': 'Social Media', 'portal.video': 'Video', 'portal.block': 'Section', 'portal.contact_unset': 'This contact method is not configured', 'portal.copied': 'Copied', 'portal.copied_link': 'Link copied', 'portal.copied_whatsapp': 'WhatsApp link copied', 'portal.copied_linkedin': 'LinkedIn link copied', 'portal.copied_email': 'Email copied', 'portal.copied_telegram': 'Telegram link copied', 'portal.copied_twitter': 'X link copied', 'portal.copied_wechat': 'WeChat ID copied', 'portal.copied_line': 'LINE link copied', 'portal.copied_kakao': 'KakaoTalk link copied', 'portal.collect': 'Save to Hall', 'portal.collected': 'Saved to Hall', 'portal.appointment': 'Book Appointment', 'portal.appointment_desc': 'Book a 30-min video meeting or in-person talk', 'portal.book_now': 'Book Now', 'portal.google_cal': 'Google Calendar', 'portal.outlook_cal': 'Outlook Calendar', 'portal.ics_download': 'Download ICS File', 'portal.copied_meeting_link': 'Calendar link copied', 'portal.appointment_form_title': 'Book a Meeting', 'portal.appointment_form_desc': 'Share your meeting preferences and requirements', 'portal.appointment_name': 'Your Name', 'portal.appointment_contact': 'Phone / WhatsApp / Email', 'portal.appointment_company': 'Company (Optional)', 'portal.appointment_time': 'Meeting Format & Time', 'portal.appointment_time_video': 'Video Meeting (30 min)', 'portal.appointment_time_offline': 'In-Person Meeting', 'portal.appointment_time_call': 'Phone Call', 'portal.appointment_notes': 'Topics & Agenda', 'portal.appointment_notes_placeholder': 'Briefly describe your project or partnership goals...', 'portal.appointment_photos': 'Card / Photo Attachments (Optional)', 'portal.appointment_photos_tip': 'Upload business card, project brief or product sample (up to 3)', 'portal.appointment_submit': 'Submit Appointment', 'portal.appointment_submitting': 'Submitting...', 'portal.appointment_success_tip': 'Appointment submitted! We will contact you shortly.', 'portal.appointment_fill_required': 'Please provide your name and contact info', 'portal.cookie_notice': 'We use cookies to improve your experience and understand visits. Please choose before continuing.', 'portal.cookie_accept': 'Accept', 'portal.cookie_reject': 'Reject', 'tab.workplace': 'Workplace', 'tab.card': 'Card', 'tab.radar': 'Radar', 'tab.leads': 'Leads', 'tab.profile': 'Profile', }, }, 'ar-SA': { code: 'ar-SA', name: 'العربية', dir: 'rtl', messages: { 'common.confirm': 'تأكيد', 'common.cancel': 'إلغاء', 'common.back': 'رجوع', 'common.save': 'حفظ', 'common.delete': 'حذف', 'common.edit': 'تعديل', 'common.search': 'بحث', 'common.loading': 'جاري التحميل...', 'common.success': 'تمت العملية بنجاح', 'common.error': 'حدث خطأ', 'common.empty': 'لا توجد بيانات', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'معاينة', 'card.save_contact': 'حفظ جهة الاتصال', 'card.exchange': 'تبادل البطاقات', 'card.ai_chat': 'استشارة', 'card.share': 'مشاركة البطاقة', 'card.scan_nfc': 'مسح NFC', 'card.view_profile': 'عرض الملف الشخصي', 'radar.online_visitor': 'الزوار الحاليون', 'radar.active_now': 'نشط الآن', 'radar.visitor_trace': 'مسار الزائر', 'radar.lead_score': 'درجة العميل المحتمل', 'crm.contacts': 'جهات الاتصال', 'crm.leads': 'العملاء', 'crm.follow_up': 'المتابعة', 'portal.unavailable': 'الصفحة غير متاحة حاليا', 'portal.retry': 'إعادة التحميل', 'portal.pronounce': 'الاستماع للنطق', 'portal.pronouncing': 'جار التشغيل', 'portal.whatsapp': 'واتساب', 'portal.linkedin': 'لينكد إن', 'portal.telegram': 'تيليجرام', 'portal.twitter': 'إكس', 'portal.wechat': 'وي تشات', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'البريد', 'portal.phone': 'اتصال', 'portal.case': 'دراسات الحالة', 'portal.article': 'مقالات', 'portal.social': 'التواصل الاجتماعي', 'portal.video': 'فيديو', 'portal.block': 'محتوى', 'portal.contact_unset': 'لم يتم تعيين وسيلة التواصل', 'portal.copied': 'تم النسخ', 'portal.copied_link': 'تم نسخ الرابط', 'portal.copied_whatsapp': 'تم نسخ رابط واتساب', 'portal.copied_linkedin': 'تم نسخ رابط لينكد إن', 'portal.copied_email': 'تم نسخ البريد الإلكتروني', 'portal.copied_telegram': 'تم نسخ رابط تيليجرام', 'portal.copied_twitter': 'تم نسخ رابط إكس', 'portal.copied_wechat': 'تم نسخ معرف وي تشات', 'portal.copied_line': 'تم نسخ رابط LINE', 'portal.copied_kakao': 'تم نسخ رابط KakaoTalk', 'portal.collect': 'حفظ في القاعة', 'portal.collected': 'تم الحفظ في القاعة', 'portal.appointment': 'حجز موعد', 'portal.appointment_desc': 'احجز اجتماع فيديو عالمي لمدة 30 دقيقة أو محادثة شخصية', 'portal.book_now': 'احجز موعداً', 'portal.google_cal': 'تقويم Google', 'portal.outlook_cal': 'تقويم Outlook', 'portal.ics_download': 'تنزيل ملف التقويم (ICS)', 'portal.copied_meeting_link': 'تم نسخ رابط التقويم', 'portal.appointment_form_title': 'حجز موعد عمل', 'portal.appointment_form_desc': 'أرسل رغبتك ومتطلباتك إلى صاحب البطاقة مباشرة', 'portal.appointment_name': 'اسمك الكريم', 'portal.appointment_contact': 'رقم الهاتف / واتساب / البريد', 'portal.appointment_company': 'اسم الشركة (اختياري)', 'portal.appointment_time': 'طريقة / وقت الاجتماع المفضل', 'portal.appointment_time_video': 'فيديو عبر الإنترنت (30 دقيقة)', 'portal.appointment_time_offline': 'لقاء شخصي في المعرض', 'portal.appointment_time_call': 'مكالمة هاتفية سريعة', 'portal.appointment_notes': 'المتطلبات وموضوع النقاش', 'portal.appointment_notes_placeholder': 'صف بإيجاز احتياجاتك أو أهداف التعاون...', 'portal.appointment_photos': 'صور البطاقة أو متطلبات العمل (اختياري)', 'portal.appointment_photos_tip': 'يمكنك إرفاق بطاقة عملك أو متطلباتك (بحد أقصى 3 صور)', 'portal.appointment_submit': 'إرسال طلب الموعد', 'portal.appointment_submitting': 'جاري الإرسال...', 'portal.appointment_success_tip': 'تم إرسال طلبك بنجاح! سيتواصل معك صاحب البطاقة قريباً.', 'portal.appointment_fill_required': 'يرجى إدخال الاسم ومعلومات التواصل', 'portal.cookie_notice': 'نستخدم ملفات تعريف الارتباط لتحسين تجربتك وفهم الزيارات. يرجى الاختيار قبل المتابعة.', 'portal.cookie_accept': 'موافق', 'portal.cookie_reject': 'رفض', 'tab.workplace': 'المكتب', 'tab.card': 'بطاقتي', 'tab.radar': 'الرادار', 'tab.leads': 'العملاء', 'tab.profile': 'حسابي', }, }, 'es-ES': { code: 'es-ES', name: 'Español', dir: 'ltr', messages: { 'common.confirm': 'Confirmar', 'common.cancel': 'Cancelar', 'common.back': 'Volver', 'common.save': 'Guardar', 'common.delete': 'Eliminar', 'common.edit': 'Editar', 'common.search': 'Buscar', 'common.loading': 'Cargando...', 'common.success': 'Listo', 'common.error': 'Ha ocurrido un error', 'common.empty': 'No hay datos', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'Vista previa', 'card.save_contact': 'Guardar contacto', 'card.exchange': 'Intercambiar tarjeta', 'card.ai_chat': 'Consulta', 'card.share': 'Compartir tarjeta', 'card.scan_nfc': 'Toque NFC', 'card.view_profile': 'Ver perfil', 'radar.online_visitor': 'Visitantes en línea', 'radar.active_now': 'Activos ahora', 'radar.visitor_trace': 'Recorrido del visitante', 'radar.lead_score': 'Puntuación de interés', 'crm.contacts': 'Contactos', 'crm.leads': 'Clientes', 'crm.follow_up': 'Seguimiento', 'portal.unavailable': 'Esta página no está disponible', 'portal.retry': 'Recargar', 'portal.pronounce': 'Pronunciar', 'portal.pronouncing': 'Reproduciendo', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': 'WeChat', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'Correo', 'portal.phone': 'Llamar', 'portal.case': 'Caso de éxito', 'portal.article': 'Artículo', 'portal.social': 'Redes sociales', 'portal.video': 'Vídeo', 'portal.block': 'Contenido', 'portal.contact_unset': 'Este medio de contacto no está configurado', 'portal.copied': 'Copiado', 'portal.copied_link': 'Enlace copiado', 'portal.copied_whatsapp': 'Enlace de WhatsApp copiado', 'portal.copied_linkedin': 'Enlace de LinkedIn copiado', 'portal.copied_email': 'Correo copiado', 'portal.copied_telegram': 'Enlace de Telegram copiado', 'portal.copied_twitter': 'Enlace de X copiado', 'portal.copied_wechat': 'ID de WeChat copiado', 'portal.copied_line': 'Enlace de LINE copiado', 'portal.copied_kakao': 'Enlace de KakaoTalk copiado', 'portal.collect': 'Guardar en el hall', 'portal.collected': 'Guardado en el hall', 'portal.appointment': 'Agendar Reunión', 'portal.appointment_desc': 'Reserva una reunión virtual de 30 min o charla presencial', 'portal.book_now': 'Agendar Cita', 'portal.google_cal': 'Google Calendar', 'portal.outlook_cal': 'Outlook Calendar', 'portal.ics_download': 'Descargar Archivo ICS', 'portal.copied_meeting_link': 'Enlace de calendario copiado', 'portal.appointment_form_title': 'Agendar Reunión de Negocios', 'portal.appointment_form_desc': 'Envía tus requisitos y propuesta directamente al titular de la tarjeta', 'portal.appointment_name': 'Tu Nombre', 'portal.appointment_contact': 'Teléfono / WhatsApp / Correo', 'portal.appointment_company': 'Empresa (Opcional)', 'portal.appointment_time': 'Formato / Horario Preferido', 'portal.appointment_time_video': 'Videollamada (30 min)', 'portal.appointment_time_offline': 'Reunión Presencial', 'portal.appointment_time_call': 'Llamada Rápida', 'portal.appointment_notes': 'Requisitos y Temas', 'portal.appointment_notes_placeholder': 'Describe brevemente tus necesidades o propuesta...', 'portal.appointment_photos': 'Fotos de tarjeta o requisitos (Opcional)', 'portal.appointment_photos_tip': 'Sube tu tarjeta de presentación o fotos del proyecto (hasta 3)', 'portal.appointment_submit': 'Enviar Solicitud', 'portal.appointment_submitting': 'Enviando...', 'portal.appointment_success_tip': '¡Solicitud enviada! El titular se pondrá en contacto pronto.', 'portal.appointment_fill_required': 'Por favor indica tu nombre y contacto', 'portal.cookie_notice': 'Usamos cookies para mejorar tu experiencia y entender las visitas. Elige antes de continuar.', 'portal.cookie_accept': 'Aceptar', 'portal.cookie_reject': 'Rechazar', 'tab.workplace': 'Panel', 'tab.card': 'Tarjeta', 'tab.radar': 'Radar', 'tab.leads': 'Clientes', 'tab.profile': 'Perfil', }, }, 'de-DE': { code: 'de-DE', name: 'Deutsch', dir: 'ltr', messages: { 'common.confirm': 'Bestätigen', 'common.cancel': 'Abbrechen', 'common.back': 'Zurück', 'common.save': 'Speichern', 'common.delete': 'Löschen', 'common.edit': 'Bearbeiten', 'common.search': 'Suchen', 'common.loading': 'Wird geladen...', 'common.success': 'Erfolgreich', 'common.error': 'Ein Fehler ist aufgetreten', 'common.empty': 'Keine Daten vorhanden', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'Vorschau', 'card.save_contact': 'Kontakt speichern', 'card.exchange': 'Visitenkarte tauschen', 'card.ai_chat': 'Anfrage', 'card.share': 'Karte teilen', 'card.scan_nfc': 'NFC-Tipp', 'card.view_profile': 'Profil ansehen', 'radar.online_visitor': 'Online-Besucher', 'radar.active_now': 'Jetzt aktiv', 'radar.visitor_trace': 'Besucherpfad', 'radar.lead_score': 'Lead-Score', 'crm.contacts': 'Kontakte', 'crm.leads': 'Kunden', 'crm.follow_up': 'Nachverfolgung', 'portal.unavailable': 'Diese Seite ist nicht verfügbar', 'portal.retry': 'Neu laden', 'portal.pronounce': 'Aussprache', 'portal.pronouncing': 'Wird wiedergegeben', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': 'WeChat', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'E-Mail', 'portal.phone': 'Anrufen', 'portal.case': 'Fallstudie', 'portal.article': 'Einblick', 'portal.social': 'Social Media', 'portal.video': 'Video', 'portal.block': 'Inhalt', 'portal.contact_unset': 'Dieser Kontaktweg ist nicht hinterlegt', 'portal.copied': 'Kopiert', 'portal.copied_link': 'Link kopiert', 'portal.copied_whatsapp': 'WhatsApp-Link kopiert', 'portal.copied_linkedin': 'LinkedIn-Link kopiert', 'portal.copied_email': 'E-Mail kopiert', 'portal.copied_telegram': 'Telegram-Link kopiert', 'portal.copied_twitter': 'X-Link kopiert', 'portal.copied_wechat': 'WeChat-ID kopiert', 'portal.copied_line': 'LINE-Link kopiert', 'portal.copied_kakao': 'KakaoTalk-Link kopiert', 'portal.collect': 'In die Halle legen', 'portal.collected': 'In der Halle gespeichert', 'portal.appointment': 'Termin vereinbaren', 'portal.appointment_desc': 'Buchen Sie ein 30-minütiges globales Meeting', 'portal.book_now': 'Termin anfragen', 'portal.google_cal': 'Google Kalender', 'portal.outlook_cal': 'Outlook Kalender', 'portal.ics_download': 'ICS-Datei herunterladen', 'portal.copied_meeting_link': 'Kalenderlink kopiert', 'portal.appointment_form_title': 'Geschäftstermin anfragen', 'portal.appointment_form_desc': 'Senden Sie Ihre Anforderungen direkt an den Karteninhaber', 'portal.appointment_name': 'Ihr Name', 'portal.appointment_contact': 'Telefon / WhatsApp / E-Mail', 'portal.appointment_company': 'Unternehmen (optional)', 'portal.appointment_time': 'Bevorzugtes Format / Zeit', 'portal.appointment_time_video': 'Online-Meeting (30 Min.)', 'portal.appointment_time_offline': 'Persönliches Treffen', 'portal.appointment_time_call': 'Kurzer Telefonanruf', 'portal.appointment_notes': 'Themen & Anforderungen', 'portal.appointment_notes_placeholder': 'Beschreiben Sie kurz Ihr Vorhaben oder Ihre Fragen...', 'portal.appointment_photos': 'Visitenkarte / Projektfotos (Optional)', 'portal.appointment_photos_tip': 'Visitenkarte, Projektskizze oder Produktfotos hochladen (max. 3)', 'portal.appointment_submit': 'Termin anfragen', 'portal.appointment_submitting': 'Wird gesendet...', 'portal.appointment_success_tip': 'Anfrage gesendet! Der Karteninhaber meldet sich in Kürze.', 'portal.appointment_fill_required': 'Bitte Namen und Kontaktdaten angeben', 'portal.cookie_notice': 'Wir verwenden Cookies, um Ihr Erlebnis zu verbessern und Besuche zu verstehen. Bitte wählen Sie vor dem Fortfahren.', 'portal.cookie_accept': 'Akzeptieren', 'portal.cookie_reject': 'Ablehnen', 'tab.workplace': 'Cockpit', 'tab.card': 'Karte', 'tab.radar': 'Radar', 'tab.leads': 'Leads', 'tab.profile': 'Profil', }, }, 'fr-FR': { code: 'fr-FR', name: 'Français', dir: 'ltr', messages: { 'common.confirm': 'Confirmer', 'common.cancel': 'Annuler', 'common.back': 'Retour', 'common.save': 'Enregistrer', 'common.delete': 'Supprimer', 'common.edit': 'Modifier', 'common.search': 'Rechercher', 'common.loading': 'Chargement...', 'common.success': 'Terminé', 'common.error': 'Une erreur est survenue', 'common.empty': 'Aucune donnée', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'Aperçu', 'card.save_contact': 'Enregistrer le contact', 'card.exchange': 'Échanger la carte', 'card.ai_chat': 'Demande', 'card.share': 'Partager la carte', 'card.scan_nfc': 'Toucher NFC', 'card.view_profile': 'Voir le profil', 'radar.online_visitor': 'Visiteurs en ligne', 'radar.active_now': 'Actifs maintenant', 'radar.visitor_trace': 'Parcours visiteur', 'radar.lead_score': 'Score d’intérêt', 'crm.contacts': 'Contacts', 'crm.leads': 'Clients', 'crm.follow_up': 'Suivi', 'portal.unavailable': 'Cette page est indisponible', 'portal.retry': 'Recharger', 'portal.pronounce': 'Prononcer', 'portal.pronouncing': 'Lecture en cours', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': 'WeChat', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'E-mail', 'portal.phone': 'Appeler', 'portal.case': 'Étude de cas', 'portal.article': 'Analyse', 'portal.social': 'Réseaux sociaux', 'portal.video': 'Vidéo', 'portal.block': 'Contenu', 'portal.contact_unset': 'Ce moyen de contact n’est pas renseigné', 'portal.copied': 'Copié', 'portal.copied_link': 'Lien copié', 'portal.copied_whatsapp': 'Lien WhatsApp copié', 'portal.copied_linkedin': 'Lien LinkedIn copié', 'portal.copied_email': 'E-mail copié', 'portal.copied_telegram': 'Lien Telegram copié', 'portal.copied_twitter': 'Lien X copié', 'portal.copied_wechat': 'Identifiant WeChat copié', 'portal.copied_line': 'Lien LINE copié', 'portal.copied_kakao': 'Lien KakaoTalk copié', 'portal.collect': 'Ajouter au hall', 'portal.collected': 'Ajouté au hall', 'portal.appointment': 'Prendre Rendez-vous', 'portal.appointment_desc': 'Réservez une réunion vidéo globale de 30 minutes', 'portal.book_now': 'Prendre RDV', 'portal.google_cal': 'Google Agenda', 'portal.outlook_cal': 'Outlook Calendrier', 'portal.ics_download': 'Télécharger le fichier ICS', 'portal.copied_meeting_link': 'Lien de calendrier copié', 'portal.appointment_form_title': 'Demande de Rendez-vous', 'portal.appointment_form_desc': 'Transmettez vos besoins directement au titulaire de la carte', 'portal.appointment_name': 'Votre Nom', 'portal.appointment_contact': 'Téléphone / WhatsApp / E-mail', 'portal.appointment_company': 'Société (Facultatif)', 'portal.appointment_time': 'Format / Créneau Souhaité', 'portal.appointment_time_video': 'Visio en ligne (30 min)', 'portal.appointment_time_offline': 'Rencontre en personne', 'portal.appointment_time_call': 'Appel rapide', 'portal.appointment_notes': 'Besoins & Sujets', 'portal.appointment_notes_placeholder': 'Décrivez brièvement votre projet ou vos attentes...', 'portal.appointment_photos': 'Carte de visite / Photos (Optionnel)', 'portal.appointment_photos_tip': 'Ajoutez votre carte ou photos du projet (jusqu\'à 3)', 'portal.appointment_submit': 'Envoyer la demande', 'portal.appointment_submitting': 'Envoi en cours...', 'portal.appointment_success_tip': 'Demande envoyée ! Le titulaire vous contactera sous peu.', 'portal.appointment_fill_required': 'Veuillez renseigner votre nom et vos coordonnées', 'portal.cookie_notice': 'Nous utilisons des cookies pour améliorer votre expérience et comprendre les visites. Merci de choisir avant de continuer.', 'portal.cookie_accept': 'Accepter', 'portal.cookie_reject': 'Refuser', 'tab.workplace': 'Tableau', 'tab.card': 'Carte', 'tab.radar': 'Radar', 'tab.leads': 'Contacts', 'tab.profile': 'Profil', }, }, 'ja-JP': { code: 'ja-JP', name: '日本語', dir: 'ltr', messages: { 'common.confirm': '確認', 'common.cancel': 'キャンセル', 'common.back': '戻る', 'common.save': '保存', 'common.delete': '削除', 'common.edit': '編集', 'common.search': '検索', 'common.loading': '読み込み中...', 'common.success': '完了しました', 'common.error': 'エラーが発生しました', 'common.empty': 'データがありません', 'common.app_name': 'OmniCard', 'portal.preview_mode': 'プレビュー', 'card.save_contact': '連絡先に保存', 'card.exchange': '名刺を交換', 'card.ai_chat': 'オンライン相談', 'card.share': '名刺をシェア', 'card.scan_nfc': 'NFC タップ', 'card.view_profile': 'プロフィールを見る', 'radar.online_visitor': 'オンライン訪問者', 'radar.active_now': '現在アクティブ', 'radar.visitor_trace': '訪問履歴', 'radar.lead_score': '関心度スコア', 'crm.contacts': '連絡先', 'crm.leads': '顧客', 'crm.follow_up': 'フォロー記録', 'portal.unavailable': 'このページは現在ご利用いただけません', 'portal.retry': '再読み込み', 'portal.pronounce': '発音', 'portal.pronouncing': '再生中', 'portal.whatsapp': 'WhatsApp', 'portal.linkedin': 'LinkedIn', 'portal.telegram': 'Telegram', 'portal.twitter': 'X', 'portal.wechat': 'WeChat', 'portal.line': 'LINE', 'portal.kakao': 'KakaoTalk', 'portal.email': 'メール', 'portal.phone': '電話する', 'portal.case': '導入事例', 'portal.article': 'インサイト', 'portal.social': 'ソーシャル', 'portal.video': '動画', 'portal.block': 'コンテンツ', 'portal.contact_unset': 'この連絡先は未設定です', 'portal.copied': 'コピーしました', 'portal.copied_link': 'リンクをコピーしました', 'portal.copied_whatsapp': 'WhatsApp のリンクをコピーしました', 'portal.copied_linkedin': 'LinkedIn のリンクをコピーしました', 'portal.copied_email': 'メールアドレスをコピーしました', 'portal.copied_telegram': 'Telegram のリンクをコピーしました', 'portal.copied_twitter': 'X のリンクをコピーしました', 'portal.copied_wechat': 'WeChat ID をコピーしました', 'portal.copied_line': 'LINE のリンクをコピーしました', 'portal.copied_kakao': 'KakaoTalk のリンクをコピーしました', 'portal.collect': 'ホールに保存', 'portal.collected': 'ホールに保存しました', 'portal.appointment': '日程を予約する', 'portal.appointment_desc': '30分間のオンライン面談または商谈を予約', 'portal.book_now': '予約・相談する', 'portal.google_cal': 'Google カレンダー', 'portal.outlook_cal': 'Outlook カレンダー', 'portal.ics_download': 'ICSファイルをダウンロード', 'portal.copied_meeting_link': 'カレンダーのリンクをコピーしました', 'portal.appointment_form_title': '商談・面談のご予約', 'portal.appointment_form_desc': 'ご相談内容やご要望を名刺所有者へ直接送信します', 'portal.appointment_name': 'お名前', 'portal.appointment_contact': '電話番号 / WhatsApp / メール', 'portal.appointment_company': '会社名・組織名(任意)', 'portal.appointment_time': 'ご希望の面談形式・時間', 'portal.appointment_time_video': 'オンライン面談(30分)', 'portal.appointment_time_offline': '対面・展示会での商談', 'portal.appointment_time_call': 'お電話でのクイック相談', 'portal.appointment_notes': 'ご相談内容・ご要望', 'portal.appointment_notes_placeholder': '事業内容やご興味のある点をご記入ください...', 'portal.appointment_photos': '名刺・資料写真(任意)', 'portal.appointment_photos_tip': '名刺や商品、仕様書の写真を最大3枚まで添付できます', 'portal.appointment_submit': '予約・相談を送信', 'portal.appointment_submitting': '送信中...', 'portal.appointment_success_tip': 'ご予約を送信しました。担当者より追ってご連絡いたします。', 'portal.appointment_fill_required': 'お名前とご連絡先を入力してください', 'portal.cookie_notice': 'Cookie を使用して体験を向上し、訪問状況を把握します。続行前にご選択ください。', 'portal.cookie_accept': '同意する', 'portal.cookie_reject': '拒否する', 'tab.workplace': 'ワーク', 'tab.card': '名刺', 'tab.radar': 'レーダー', 'tab.leads': '見込み客', 'tab.profile': 'マイ', }, }, }; export const PORTAL_LOCALE_STORAGE_KEY = 'omni_locale'; export interface AppLocaleState { current: OmniLocale; isRtl: boolean; dir: Direction; } export const appLocaleState: AppLocaleState = { current: 'zh-CN', isRtl: false, dir: 'ltr', }; let overrideLocale: OmniLocale | null = null; function syncState(target: OmniLocale): void { appLocaleState.current = target; appLocaleState.isRtl = isRtlLocale(target); appLocaleState.dir = appLocaleState.isRtl ? 'rtl' : 'ltr'; } /** * 将任意语言标识规范化为 7 种主流语言之一(回退 en-US 国际默认) */ export function normalizeLocale(raw: string): OmniLocale { if (!raw || typeof raw !== 'string') { return 'en-US'; } const lower = raw.trim().toLowerCase(); if (lower.startsWith('zh')) { return 'zh-CN'; } if (lower.startsWith('ar')) { return 'ar-SA'; } if (lower.startsWith('es')) { return 'es-ES'; } if (lower.startsWith('de')) { return 'de-DE'; } if (lower.startsWith('fr')) { return 'fr-FR'; } if (lower.startsWith('ja')) { return 'ja-JP'; } if (lower.startsWith('en')) { return 'en-US'; } return 'en-US'; } export function resolveAppLocale(raw: string, fallback?: string): OmniLocale { if (!raw || !raw.trim()) { return fallback ? normalizeLocale(fallback) : 'en-US'; } return normalizeLocale(raw); } /** * 将系统语言码映射为门户语言:zh → zh-CN,ar → ar-SA, * es → es-ES,de → de-DE,fr → fr-FR,ja → ja-JP,其余 en-US */ export function resolveDeviceLocale(raw: string): OmniLocale { return normalizeLocale(raw); } /** * 根据访客的国家码映射主流语言或回退 fallback(默认 en-US) */ export function resolveGeoLocale(countryCode?: string, fallback: string = 'en-US'): OmniLocale { if (!countryCode || !countryCode.trim()) { return normalizeLocale(fallback); } const code = countryCode.trim().toUpperCase(); if (code === 'CN' || code === 'TW' || code === 'HK') { return 'zh-CN'; } if (code === 'SA' || code === 'AE' || code === 'EG') { return 'ar-SA'; } if (code === 'ES' || code === 'MX') { return 'es-ES'; } if (code === 'DE' || code === 'AT') { return 'de-DE'; } if (code === 'FR') { return 'fr-FR'; } if (code === 'JP') { return 'ja-JP'; } return normalizeLocale(fallback); } /** * 判定语言是否为 RTL(如阿拉伯语 ar-SA、希伯来语 he-IL、波斯语 fa、乌尔都语 ur) */ export function isRtlLocale(lang: string): boolean { if (!lang || typeof lang !== 'string') { return false; } const code = lang.trim().toLowerCase(); if ( code.startsWith('ar') || code.startsWith('he') || code.startsWith('fa') || code.startsWith('ur') ) { return true; } const item = (omniLocales as Record)[lang]; return item != null && item.dir === 'rtl'; } function readDeviceLanguage(): string { try { const fromLocale = uni.getLocale(); if (fromLocale && fromLocale.trim() !== '') { return fromLocale; } } catch (_e) {} try { const info = uni.getSystemInfoSync(); if (info && info.language && info.language.trim() !== '') { return info.language; } } catch (_e) {} return ''; } /** * 读取本地持久化语言 */ function readStorageLanguage(): string { try { const stored = uni.getStorageSync(PORTAL_LOCALE_STORAGE_KEY) as string; if (stored && typeof stored === 'string' && stored.trim() !== '') { return stored.trim(); } } catch (_e) {} return ''; } /** * 读取当前检测语言。无信号时回退 zh-CN */ export function detectLocale(): OmniLocale { if (overrideLocale != null) { return overrideLocale; } const stored = readStorageLanguage(); if (stored) { return normalizeLocale(stored); } const raw = readDeviceLanguage(); if (!raw) { return 'zh-CN'; } return resolveDeviceLocale(raw); } export function getCurrentLocale(): OmniLocale { const current = detectLocale(); syncState(current); return current; } /** * 根据语言刷新底部原生 TabBar 5 大菜单标题 (工作台, 名片, 雷达, 潜客, 我的) */ export function updateTabBarTitles(locale?: OmniLocale): void { const targetLocale = locale != null ? locale : getCurrentLocale(); const items: Array<{ index: number; key: OmniMessageKey }> = [ { index: 0, key: 'tab.workplace' }, { index: 1, key: 'tab.card' }, { index: 2, key: 'tab.radar' }, { index: 3, key: 'tab.leads' }, { index: 4, key: 'tab.profile' }, ]; for (let i = 0; i < items.length; i++) { try { if (typeof uni !== 'undefined' && typeof uni.setTabBarItem === 'function') { uni.setTabBarItem({ index: items[i].index, text: getOmniText(targetLocale, items[i].key), }); } } catch (_e) {} } } export function setAppLocale(lang: string): void { const normalized = normalizeLocale(lang); overrideLocale = normalized; syncState(normalized); try { uni.setStorageSync(PORTAL_LOCALE_STORAGE_KEY, normalized); } catch (_e) {} updateTabBarTitles(normalized); try { if (typeof uni !== 'undefined' && typeof uni.$emit === 'function') { uni.$emit('omni_locale_changed', normalized); } } catch (_e) {} } export function initAppLocale(): OmniLocale { const current = detectLocale(); syncState(current); updateTabBarTitles(current); return current; } export function setCurrentLocale(locale: OmniLocale): void { setAppLocale(locale); } export function isRtl(locale: OmniLocale): boolean { return isRtlLocale(locale); } export function getOmniText(locale: OmniLocale, key: OmniMessageKey): string { const dict = omniLocales[locale] || omniLocales['en-US']; return dict.messages[key] || omniLocales['en-US'].messages[key] || key; } // 模块加载时同步一次初始全局状态 try { initAppLocale(); } catch (_e) {}