// 公司研究資料:欄位中文化(職稱/產業常用對照,非機器翻譯全文) const TITLE_ZH = [ [/chief executive officer|ceo/i, '執行長'], [/chief financial officer|cfo/i, '財務長'], [/chief operating officer|coo/i, '營運長'], [/chief technology officer|cto/i, '技術長'], [/executive vice president|evp/i, '執行副總'], [/senior vice president|svp/i, '資深副總'], [/vice president|vp/i, '副總'], [/president.*chief executive|president and chief executive/i, '執行長暨總裁'], [/president/i, '總裁'], [/general counsel/i, '法務長'], [/chief accounting officer/i, '會計長'], [/principal financial officer/i, '主要財務負責人'], [/principal executive officer/i, '主要執行負責人'], [/principal accounting officer/i, '主要會計負責人'], [/director/i, '董事'], [/chairman/i, '董事長'], [/operations/i, '營運'], [/worldwide field/i, '全球業務'], ]; const SECTOR_ZH = { Technology: '科技', 'Financial Services': '金融服務', Healthcare: '醫療保健', 'Consumer Cyclical': '循環性消費', 'Consumer Defensive': '防禦性消費', Energy: '能源', Industrials: '工業', 'Basic Materials': '原物料', 'Real Estate': '房地產', Utilities: '公用事業', 'Communication Services': '通訊服務', }; const INDUSTRY_HINTS = [ [/semiconductor/i, '半導體'], [/software/i, '軟體'], [/internet/i, '網際網路'], [/bank/i, '銀行'], [/biotech|pharma/i, '生技/製藥'], [/retail/i, '零售'], [/auto/i, '汽車'], ]; export function looksLikePersonName(name) { if (!name || name.length > 55) return false; if (/^Item \d/i.test(name) || name === 'Action' || name.startsWith('/s/')) return false; const n = name.toLowerCase(); if (/financial|exhibit|schedule|statement|supplementary|governance|table of|designated|hedge|accounting|income|operations|revenue|consolidated|index|former|current|named|other|each|page|directors and|from our|served as/.test(n)) return false; const parts = name.trim().split(/\s+/); if (parts.length < 2 || parts.length > 5) return false; if (!/^[A-Z]/.test(parts[0])) return false; return parts.every(p => /^[A-Za-z'.-]+$/.test(p)); } export function looksLikeExecutiveTitle(title) { if (!title || title.length > 100) return false; if (/financial statement|exhibit|supplementary|schedule|table of contents|designated|hedge|accounting/i.test(title)) return false; if (/income from|cost of revenue|gross profit|net income|operating income/i.test(title)) return false; const t = title.toLowerCase(); if (t === 'director' || t === 'directors') return false; return /chief|president|executive vice|general counsel|operations|officer|accounting|counsel|field/i.test(t); } export function isOfficerRow(name, title) { return looksLikePersonName(name) && looksLikeExecutiveTitle(title); } export function sanitizeOfficers(list) { return (list || []).filter(o => isOfficerRow(o.name, o.title)); } export function translateOfficerTitle(title) { const t = String(title || '').trim(); if (!t) return ''; for (const [re, zh] of TITLE_ZH) { if (re.test(t)) return zh; } return t; } export function translateSector(sector) { const s = String(sector || '').trim(); if (!s) return '—'; return SECTOR_ZH[s] || s; } export function translateIndustry(industry) { const s = String(industry || '').trim(); if (!s) return '—'; for (const [re, zh] of INDUSTRY_HINTS) { if (re.test(s)) return `${zh}(${s})`; } return s; } export function localizeOfficer(o) { const title = o.titleZh || o.title || ''; const titleZh = o.titleZh || translateOfficerTitle(title); return { ...o, title, titleZh, titleDisplay: titleZh && titleZh !== title ? `${titleZh} · ${title}` : (titleZh || title), }; } export function mergeCustomIntel(intel, custom) { if (!custom) return intel; const out = { ...intel, customUpdatedAt: custom.updatedAt || null }; if (custom.profileZh) { out.profileZh = custom.profileZh; } if (custom.officers?.length) { out.management = { ...out.management, officers: custom.officers.map(localizeOfficer), source: '本機自訂', }; } if (custom.news?.length) { out.news = custom.news.map(n => ({ ...n, titleZh: n.titleZh || n.title, descriptionZh: n.descriptionZh || n.description, })); } if (custom.managementNotes) { out.management = { ...out.management, notesZh: custom.managementNotes }; } return out; } export function localizeIntel(intel) { if (!intel) return intel; const officers = sanitizeOfficers(intel.management?.officers || []).map(localizeOfficer); const news = (intel.news || []).map(n => ({ ...n, titleZh: n.titleZh || n.title, descriptionZh: n.descriptionZh || n.description, })); return { ...intel, management: { ...intel.management, officers }, news, searchesZh: (intel.management?.searches || []).map(s => ({ ...s, labelZh: s.labelZh || s.label.replace('Management', '管理層').replace('Leadership', '領導團隊'), })), }; }