/** * 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.save' | 'common.delete' | 'common.edit' | 'common.search' | 'common.loading' | 'common.success' | 'common.error' | 'common.empty' | 'common.app_name' | '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.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'; 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.save': '保存', 'common.delete': '删除', 'common.edit': '编辑', 'common.search': '搜索', 'common.loading': '加载中...', 'common.success': '操作成功', 'common.error': '操作失败', 'common.empty': '暂无数据', 'common.app_name': 'OmniCard', 'card.save_contact': '保存到通讯录', 'card.exchange': '交换名片', 'card.ai_chat': 'AI 咨询', '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.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': '已复制微信号', }, }, 'en-US': { code: 'en-US', name: 'English', dir: 'ltr', messages: { 'common.confirm': 'Confirm', 'common.cancel': 'Cancel', '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', 'card.save_contact': 'Save Contact', 'card.exchange': 'Exchange Card', 'card.ai_chat': 'AI Assistant', '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': 'Customers', 'crm.follow_up': 'Follow-up', 'portal.unavailable': 'This page is 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.email': 'Email', 'portal.phone': 'Call', 'portal.case': 'Case study', 'portal.article': 'Insight', 'portal.social': 'Social', 'portal.video': 'Video', 'portal.block': 'Content', 'portal.contact_unset': 'This contact method is not set', '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', }, }, 'ar-SA': { code: 'ar-SA', name: 'العربية', dir: 'rtl', messages: { 'common.confirm': 'تأكيد', 'common.cancel': 'إلغاء', 'common.save': 'حفظ', 'common.delete': 'حذف', 'common.edit': 'تعديل', 'common.search': 'بحث', 'common.loading': 'جاري التحميل...', 'common.success': 'تمت العملية بنجاح', 'common.error': 'حدث خطأ', 'common.empty': 'لا توجد بيانات', 'common.app_name': 'OmniCard', '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.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': 'تم نسخ معرف وي تشات', }, }, 'es-ES': { code: 'es-ES', name: 'Español', dir: 'ltr', messages: { 'common.confirm': 'Confirmar', 'common.cancel': 'Cancelar', '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', 'card.save_contact': 'Guardar contacto', 'card.exchange': 'Intercambiar tarjeta', 'card.ai_chat': 'Asistente IA', '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.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', }, }, 'de-DE': { code: 'de-DE', name: 'Deutsch', dir: 'ltr', messages: { 'common.confirm': 'Bestätigen', 'common.cancel': 'Abbrechen', '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', 'card.save_contact': 'Kontakt speichern', 'card.exchange': 'Visitenkarte tauschen', 'card.ai_chat': 'KI-Assistent', '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.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', }, }, 'fr-FR': { code: 'fr-FR', name: 'Français', dir: 'ltr', messages: { 'common.confirm': 'Confirmer', 'common.cancel': 'Annuler', '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', 'card.save_contact': 'Enregistrer le contact', 'card.exchange': 'Échanger la carte', 'card.ai_chat': 'Assistant IA', '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.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é', }, }, 'ja-JP': { code: 'ja-JP', name: '日本語', dir: 'ltr', messages: { 'common.confirm': '確認', 'common.cancel': 'キャンセル', 'common.save': '保存', 'common.delete': '削除', 'common.edit': '編集', 'common.search': '検索', 'common.loading': '読み込み中...', 'common.success': '完了しました', 'common.error': 'エラーが発生しました', 'common.empty': 'データがありません', 'common.app_name': 'OmniCard', 'card.save_contact': '連絡先に保存', 'card.exchange': '名刺を交換', 'card.ai_chat': 'AI アシスタント', '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.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 をコピーしました', }, }, }; 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; } export function setAppLocale(lang: string): void { const normalized = normalizeLocale(lang); overrideLocale = normalized; syncState(normalized); try { uni.setStorageSync(PORTAL_LOCALE_STORAGE_KEY, normalized); } catch (_e) {} } export function initAppLocale(): OmniLocale { const current = detectLocale(); syncState(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) {}