Files
my-vault/.scripts/rename-chinese-to-english.py

409 lines
14 KiB
Python
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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
中文文件/目录名重命名为英文
功能:
1. 扫描所有中文文件和目录
2. 生成英文重命名映射表
3. 执行重命名
4. 更新所有引用链接
"""
import os
import re
import csv
import argparse
from pathlib import Path
from typing import List, Dict, Tuple
# 中文到英文的翻译字典
TRANSLATION_DICT = {
# 项目相关
"广汽": "GAC",
"物联网AIOT": "AIoT",
"物联网": "IoT",
"AIOT": "AIoT",
"自动驾驶": "Autonomous-Driving",
"从化应急": "Conghua-Emergency",
"公文交换": "Document-Exchange",
"商务局": "Commerce-Bureau",
"市发改委": "Municipal-Development-Reform",
"虚拟数据中心": "Virtual-Data-Center",
"增城区": "Zengcheng-District",
"番禺区": "Panyu-District",
"工信": "Industry-Info",
"道路秩序": "Road-Order",
"南电": "CSG",
"南方电网": "China-Southern-Grid",
# 文件夹名称
"城中村": "Urban-Village",
"番禺城中村": "Panyu-Urban-Village",
"番禺公文": "Panyu-Document",
"番禺信用": "Panyu-Credit",
"番禺住建": "Panyu-Housing",
"公文": "Document",
"信用": "Credit",
"住建": "Housing",
"增城区信息化项目管理系统": "Zengcheng-IT-Project-Mgmt",
# 文件名关键词
"不明确的问题": "Unclear-Issues",
"培训记录整理": "Training-Records",
"设备添加": "Device-Addition",
"测试环境": "Test-Environment",
"维护": "Maintenance",
"数据流向": "Data-Flow",
"系统架构": "System-Architecture",
"登陆": "Login",
"配置": "Configuration",
"问题处理": "Issue-Handling",
"处理": "Handling",
"升级": "Upgrade",
"漏洞处理": "Vulnerability-Fix",
"达梦": "DM-Database",
"等保问题描述": "Security-Issues",
"等保修复": "Security-Fixes",
"基线": "Baseline",
"重建主节点": "Rebuild-Master-Node",
"设备电源": "Device-Power",
"智谱清言": "ChatGLM",
"热点升级": "Hotspot-Upgrade",
"自行车": "Bicycle",
"酸黄瓜制作": "Pickled-Cucumber-Recipe",
"酸黄瓜": "Pickled-Cucumber",
"文明": "Civilization",
"摩托罗拉": "Motorola",
"产出": "Deliverables",
"架构目标": "Architecture-Goals",
"决策方法": "Decision-Methods",
"系统架构分析员知识体系": "SA-Knowledge-System",
"芹菜炒牛肉": "Celery-Beef-Stir-Fry",
"图片生成提示词模板": "Image-Prompt-Template",
"在非原生ESIM设备上申请Giffgaff ESIM": "Apply-Giffgaff-ESIM-on-Non-Native-Device",
# 网页剪藏常用词
"联想移动互联及数字家庭产品服务支持": "Lenovo-Support",
"节点访问局域网服务器的配置方法": "LAN-Server-Access-Config",
"边界路由器配置": "Border-Router-Config",
"树莓派": "Raspberry-Pi",
"机场推荐": "VPN-Recommendations",
"机场评测": "VPN-Reviews",
"软件供应商手册": "Software-Vendor-Handbook",
"解读": "Interpretation",
"关于": "About",
"软件物料清单": "SBOM",
"如何": "How-to",
"教程": "Tutorial",
"博客": "Blog",
"技术文档": "Technical-Docs",
"学习笔记": "Learning-Notes",
"使用攻略": "Guide",
"安装": "Installation",
"配置": "Config",
"实现": "Implementation",
"什麼是": "What-is",
"軟體物料清單": "SBOM",
"網路安全解決方案": "Network-Security",
"艾索科技": "AISEC",
"作为旁路网关": "Bypass-Gateway",
"不是旁路由": "",
"单臂路由": "",
"的终极设置方法": "Ultimate-Setup",
"破解迷思": "Debunking-Myths",
"少数派": "sspai",
"麒麟": "Kylin",
"离线安装": "Offline-Installation",
"报文简介": "Message-Intro",
"公开镜像仓库未授权访问": "Public-Registry-Unauth",
"数据库从": "DB-Migration-from",
"迁移到": "to",
"综合讨论区": "Discussion",
"瀚思彼岸": "HASSBIAN",
"智能家居技术论坛": "Smart-Home-Forum",
"雾凇拼音": "Rime-Ice",
"长期维护的简体词库": "Simplified-Dict",
"中国申请": "China-Application",
"中国区免费": "China-Free",
"年使用攻略": "Year-Guide",
"云": "Cloud",
"详细": "Detailed",
"注册申请图文教程": "Registration-Tutorial",
"整合": "Integration",
"软件开发实施": "Software-Dev",
"广州尚鹏": "Guangzhou-SP",
"服装生鲜家具外贸供应链开源": "Supply-Chain",
"专业实施": "Pro-Impl",
"学习经验": "Learning-Experience",
"安装和打包": "Installation-Packaging",
"权限校验": "Permission-Validation",
"官网": "Official",
"学习笔记": "Learning-Notes",
"应用": "Application",
"部署": "Deployment",
"单实例数据库": "Single-Instance-DB",
"的工作学习笔记": "Work-Notes",
"集成": "Integration",
"国内镜像源列表": "China-Mirror-List",
"亲测可用": "Tested",
"技术文档": "Tech-Docs",
"安装前准备": "Pre-Installation",
"配置实例": "Config-Instance",
"数据库安装": "DB-Installation",
"技术我的一些": "My",
}
def has_chinese(text: str) -> bool:
"""检查文本是否包含中文"""
return bool(re.search(r'[\u4e00-\u9fa5]', text))
def translate_chinese(text: str, max_length: int = 50) -> str:
"""将中文翻译为英文"""
result = text
# 按长度排序,优先匹配长词组
sorted_dict = sorted(TRANSLATION_DICT.items(), key=lambda x: len(x[0]), reverse=True)
for chinese, english in sorted_dict:
result = result.replace(chinese, english)
# 移除剩余的中文(如果翻译不完整,保留拼音或删除)
result = re.sub(r'[\u4e00-\u9fa5]+', '-', result)
# 清理特殊字符
result = re.sub(r'[\u3000-\u303f\uff00-\uffef\s]+', '-', result)
result = re.sub(r'[<>:"/\\|?*]+', '-', result)
result = re.sub(r'-+', '-', result)
result = result.strip('-')
# 控制长度
if len(result) > max_length:
result = result[:max_length].rstrip('-')
return result
def scan_chinese_files_and_dirs(root_dir: Path) -> Tuple[List[Path], List[Path]]:
"""扫描所有包含中文的文件和目录"""
chinese_files = []
chinese_dirs = []
# 排除的目录
exclude_dirs = {'.git', '.obsidian', '.smart-env', 'node_modules'}
for item in root_dir.rglob('*'):
# 跳过排除的目录
if any(excl in item.parts for excl in exclude_dirs):
continue
if has_chinese(item.name):
if item.is_file():
chinese_files.append(item)
elif item.is_dir():
chinese_dirs.append(item)
# 目录按深度排序(深的在前,避免父目录先改名)
chinese_dirs.sort(key=lambda p: len(p.parts), reverse=True)
return chinese_files, chinese_dirs
def generate_mappings(files: List[Path], dirs: List[Path], root_dir: Path, max_length: int = 50) -> List[Dict]:
"""生成重命名映射"""
mappings = []
# 处理目录
for dir_path in dirs:
old_name = dir_path.name
new_name = translate_chinese(old_name, max_length)
rel_path = dir_path.relative_to(root_dir)
new_path = dir_path.parent / new_name
new_rel_path = new_path.relative_to(root_dir)
mappings.append({
'type': 'Directory',
'old_path': str(rel_path),
'new_path': str(new_rel_path),
'old_name': old_name,
'new_name': new_name,
'abs_old_path': str(dir_path),
'abs_new_path': str(new_path)
})
# 处理文件
for file_path in files:
old_name = file_path.name
stem = file_path.stem
suffix = file_path.suffix
new_stem = translate_chinese(stem, max_length - len(suffix))
new_name = new_stem + suffix
rel_path = file_path.relative_to(root_dir)
new_path = file_path.parent / new_name
new_rel_path = new_path.relative_to(root_dir)
mappings.append({
'type': 'File',
'old_path': str(rel_path),
'new_path': str(new_rel_path),
'old_name': old_name,
'new_name': new_name,
'abs_old_path': str(file_path),
'abs_new_path': str(new_path)
})
return mappings
def save_mappings(mappings: List[Dict], output_file: Path):
"""保存映射表到CSV"""
with open(output_file, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['type', 'old_path', 'new_path', 'old_name', 'new_name'])
writer.writeheader()
for mapping in mappings:
writer.writerow({
'type': mapping['type'],
'old_path': mapping['old_path'],
'new_path': mapping['new_path'],
'old_name': mapping['old_name'],
'new_name': mapping['new_name']
})
def execute_rename(mappings: List[Dict]) -> Tuple[int, int]:
"""执行重命名"""
renamed = 0
failed = 0
for mapping in mappings:
try:
old_path = Path(mapping['abs_old_path'])
new_path = Path(mapping['abs_new_path'])
if old_path.exists():
old_path.rename(new_path)
print(f"[+] {mapping['type']}: {mapping['old_name']} -> {mapping['new_name']}")
renamed += 1
else:
print(f"[!] Skip (not exist): {mapping['old_path']}")
except Exception as e:
print(f"[-] Failed: {mapping['old_path']} - {e}")
failed += 1
return renamed, failed
def update_markdown_links(root_dir: Path, mappings: List[Dict]) -> int:
"""更新 Markdown 文件中的链接"""
updated_count = 0
md_files = list(root_dir.rglob('*.md'))
# 排除某些目录
exclude_dirs = {'.git', '.obsidian', '.smart-env'}
md_files = [f for f in md_files if not any(excl in f.parts for excl in exclude_dirs)]
for md_file in md_files:
try:
content = md_file.read_text(encoding='utf-8')
original_content = content
# 更新 Wiki 链接 [[中文名]]
for mapping in mappings:
if mapping['type'] == 'File':
old_stem = Path(mapping['old_name']).stem
new_stem = Path(mapping['new_name']).stem
# Wiki 链接
content = re.sub(
rf'\[\[{re.escape(old_stem)}\]\]',
f'[[{new_stem}]]',
content
)
# 相对路径链接
old_path_escaped = re.escape(mapping['old_path'].replace('\\', '/'))
new_path_fixed = mapping['new_path'].replace('\\', '/')
content = re.sub(old_path_escaped, new_path_fixed, content)
if content != original_content:
md_file.write_text(content, encoding='utf-8')
rel_path = md_file.relative_to(root_dir)
print(f"[+] Updated: {rel_path}")
updated_count += 1
except Exception as e:
print(f"[-] Update failed: {md_file} - {e}")
return updated_count
def main():
parser = argparse.ArgumentParser(description='中文文件名重命名为英文')
parser.add_argument('--execute', action='store_true', help='执行重命名(默认为预览模式)')
parser.add_argument('--max-length', type=int, default=50, help='文件名最大长度(默认50')
args = parser.parse_args()
root_dir = Path.cwd()
mapping_file = root_dir / 'chinese-to-english-mapping.csv'
print("=== Chinese to English Rename Tool ===\n")
if args.execute:
print("[!] EXECUTE MODE: Will rename files and update links")
else:
print("[i] PREVIEW MODE: Generate mapping only, no actual rename")
print(" Use --execute to perform actual rename")
print()
# Scan
print("[*] Scanning Chinese files and directories...")
chinese_files, chinese_dirs = scan_chinese_files_and_dirs(root_dir)
print(f" Found {len(chinese_files)} Chinese files")
print(f" Found {len(chinese_dirs)} Chinese directories\n")
# Generate mappings
print("[*] Generating rename mappings...")
mappings = generate_mappings(chinese_files, chinese_dirs, root_dir, args.max_length)
save_mappings(mappings, mapping_file)
print(f" [+] Mapping saved to: {mapping_file}")
print(f" [+] Total {len(mappings)} rename tasks\n")
# Preview
print("[*] Rename Preview (first 20):")
print(f"{'Type':<10} {'Old Name':<40} {'New Name':<40}")
print("-" * 90)
for mapping in mappings[:20]:
print(f"{mapping['type']:<10} {mapping['old_name']:<40} {mapping['new_name']:<40}")
if len(mappings) > 20:
print(f"\n ... and {len(mappings) - 20} more (see full list: {mapping_file})")
print()
# Execute rename
if args.execute:
print("[*] Starting rename...")
renamed, failed = execute_rename(mappings)
print(f"\n[*] Rename complete!")
print(f" Success: {renamed}")
print(f" Failed: {failed}\n")
# Update links
print("[*] Updating Markdown links...")
updated_count = update_markdown_links(root_dir, mappings)
print(f"\n[*] Link update complete! Updated {updated_count} files\n")
else:
print("[i] Next steps:")
print(f" 1. Review mapping: {mapping_file}")
print(f" 2. If OK, run: python .scripts/rename-chinese-to-english.py --execute\n")
print("[+] Done!")
if __name__ == '__main__':
main()