finance-dashboard/lib/companyintel-i18n.js

154 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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