finance-dashboard/lib/companyintel-i18n.js

154 lines
5.0 KiB
JavaScript
Raw Permalink Normal View History

2026-06-04 09:32:28 +00:00
// 公司研究資料:欄位中文化(職稱/產業常用對照,非機器翻譯全文)
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', '領導團隊'),
})),
};
}