同步家纺情报 v2 数据、59 个严格权威源、趋势图库、移动端交互、Google/Yandex 翻译门控及静态 SEO/GEO 构建。 补充面向 C# 研发的 README,明确当前无 LLM、多模态与生图模型,记录翻译请求参数、缓存与失败策略、数据处理边界、前端职责和无 GitHub Pages 部署流程。 新增按日期倒序维护的 CHANGELOG,记录影响、验证与代码/数据/dist 回滚方法。 验证:11 个 Python 契约测试、13 个 Node 前端测试通过;静态构建生成 1096 个 Sitemap URL。
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
||
"""家纺情报的中文标题翻译与术语校准。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
import requests
|
||
|
||
if __package__:
|
||
from .update_textile_trends import load_zh_cache, save_zh_cache, translate_to_zh_cn
|
||
else:
|
||
from update_textile_trends import load_zh_cache, save_zh_cache, translate_to_zh_cn
|
||
|
||
|
||
BROWSER_UA = (
|
||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0 Safari/537.36"
|
||
)
|
||
CALENDAR_TITLE_OVERRIDES = {
|
||
"New Year's Day": "元旦",
|
||
"New Year’s Day": "元旦",
|
||
"New Year's Eve": "跨年夜",
|
||
"St. Stephen's Day": "圣斯蒂芬节",
|
||
"Christmas Eve": "平安夜",
|
||
"Pentecost": "圣灵降临节",
|
||
"Immaculate Conception": "圣母无染原罪节",
|
||
"Maundy Thursday": "濯足节",
|
||
"Chuseok": "韩国秋夕",
|
||
"All Saints Day": "诸圣节",
|
||
"All Saints' Day Eve": "诸圣节前夜",
|
||
"All Souls' Day": "追思亡者节",
|
||
"Armistice Day": "停战纪念日",
|
||
"Summer bank holiday": "夏季银行假日",
|
||
"Early May bank holiday": "五月初银行假日",
|
||
"Spring bank holiday": "春季银行假日",
|
||
"May Day": "五一劳动节",
|
||
"Good Friday": "耶稣受难日",
|
||
"Holy Saturday": "圣周六",
|
||
"Easter Monday": "复活节星期一",
|
||
"Christmas Day": "圣诞节",
|
||
"Boxing Day": "节礼日",
|
||
"Saint Patrick's Day": "圣帕特里克节",
|
||
"St Patrick’s Day": "圣帕特里克节",
|
||
"National Heroes Day": "国家英雄日",
|
||
"National Day for Truth and Reconciliation": "真相与和解国家纪念日",
|
||
"National Day of Spain": "西班牙国庆日",
|
||
"German Unity Day": "德国统一日",
|
||
"National Foundation Day": "建国纪念日",
|
||
"Hangul Day": "韩文日",
|
||
"Labour Thanksgiving Day": "勤劳感谢之日",
|
||
"Thanksgiving": "感恩节",
|
||
"Remembrance Day": "阵亡将士纪念日",
|
||
"Veterans Day": "退伍军人节",
|
||
"Day of Reconciliation": "和解日",
|
||
"Day of Goodwill": "友善日",
|
||
"Deepavali": "屠妖节",
|
||
"Hari Raya Puasa": "开斋节",
|
||
"Hari Raya Haji": "哈芝节",
|
||
"Vesak Day": "卫塞节",
|
||
"Eid al-Fitr First Day (Tentative Date)": "开斋节首日(暂定)",
|
||
"Eid al-Fitr Second Day (Tentative Date)": "开斋节次日(暂定)",
|
||
"Eid al-Fitr Third Day (Tentative Date)": "开斋节第三日(暂定)",
|
||
"Eid al-Adha First Day (Tentative Date)": "宰牲节首日(暂定)",
|
||
"Eid al-Adha Second Day (Tentative Date)": "宰牲节次日(暂定)",
|
||
"Eid al-Adha Third Day (Tentative Date)": "宰牲节第三日(暂定)",
|
||
"Eid al-Adha Fourth Day (Tentative Date)": "宰牲节第四日(暂定)",
|
||
"Anzac Day": "澳新军团日",
|
||
"Waitangi Day": "怀唐伊日",
|
||
"Matariki": "毛利新年 Matariki",
|
||
"Juneteenth National Independence Day": "六月节国家独立日",
|
||
}
|
||
|
||
|
||
def normalize_text(value: str) -> str:
|
||
return " ".join((value or "").replace("\u00a0", " ").split())
|
||
|
||
|
||
def refine_calendar_translation(value: str) -> str:
|
||
replacements = {
|
||
"假定日期": "暂定日期",
|
||
"第一天(暂定日期)": "首日(暂定)",
|
||
"第二天(暂定日期)": "次日(暂定)",
|
||
"第三天(暂定日期)": "第三日(暂定)",
|
||
"第四天(暂定日期)": "第四日(暂定)",
|
||
"银行关闭日": "银行休业日",
|
||
"家庭日": "家庭日",
|
||
}
|
||
output = normalize_text(value)
|
||
for source, target in replacements.items():
|
||
output = output.replace(source, target)
|
||
return output
|
||
|
||
|
||
def translate_calendar_titles(items: list[dict[str, Any]], output_dir: Path, max_new: int = 260) -> None:
|
||
cache = load_zh_cache(output_dir)
|
||
session = requests.Session()
|
||
session.headers.update({"User-Agent": BROWSER_UA, "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"})
|
||
translated_count = 0
|
||
cache_changed = False
|
||
for item in items:
|
||
current = normalize_text(str(item.get("title") or ""))
|
||
if not current:
|
||
continue
|
||
english = normalize_text(str(item.get("title_english") or current))
|
||
translated = CALENDAR_TITLE_OVERRIDES.get(english)
|
||
if translated:
|
||
if item.get("source_id") != "calendar_nager" or not item.get("title_original"):
|
||
item["title_original"] = current
|
||
item["title"] = translated
|
||
item["translation_status"] = "已按权威节日名称校准并保留当地名称"
|
||
continue
|
||
if re.search(r"[\u4e00-\u9fff]", current):
|
||
continue
|
||
translated = cache.get(english)
|
||
if not translated and translated_count < max_new:
|
||
translated = translate_to_zh_cn(session, english)
|
||
if translated:
|
||
cache[english] = translated
|
||
translated_count += 1
|
||
cache_changed = True
|
||
if not translated or not re.search(r"[\u4e00-\u9fff]", translated):
|
||
translated = f"{english}({item.get('region') or '当地'}公共假日)"
|
||
item["translation_status"] = "保留官方名称并补充中文语境"
|
||
else:
|
||
translated = refine_calendar_translation(translated)
|
||
item["translation_status"] = "已翻译并保留官方名称"
|
||
if item.get("source_id") != "calendar_nager" or not item.get("title_original"):
|
||
item["title_original"] = current
|
||
item["title"] = translated
|
||
if cache_changed:
|
||
save_zh_cache(output_dir, cache)
|