feat: add internationalization support with Chinese translations

- Introduce Translator class for simple i18n with English and Chinese locales
- Add concurrent.futures, os, and locale imports for parallel execution and language detection
- Implement automatic language detection based on system locale
- Provide translation keys for build messages, test results, and command outputs
- Support dynamic language switching via set_language method
This commit is contained in:
zzy
2025-12-11 12:27:11 +08:00
parent 098e41d3e5
commit 35c13ee30a

View File

@@ -15,6 +15,9 @@ import time
import sys
import json
import re
import concurrent.futures
import os
import locale
class ColorFormatter(logging.Formatter):
@@ -50,6 +53,111 @@ def setup_logger() -> logging.Logger:
logger = setup_logger()
# 简单的翻译系统
class Translator:
"""简单的翻译器"""
def __init__(self, lang=None):
def detect_language() -> str:
(name, _) = locale.getlocale()
if name is None:
return "en_US"
lower_lang = name.lower()
# 检查是否包含中文相关关键词
chinese_keywords = [
"chinese",
"china",
"zh",
"cn",
]
for keyword in chinese_keywords:
if keyword in lower_lang:
return "zh_CN"
return "en_US"
self.lang: str = lang if lang is not None else detect_language()
self.translations = {
"zh_CN": {
"build_complete": "完成 {mode} 目标,耗时 {time} (使用 {jobs} 线程)",
"clean_directory": "已清理构建目录: {path}",
"cleaned_files": "清理了 {count} 个文件,总大小 {size}",
"no_build_dir": "没有找到构建目录,无需清理",
"no_targets": "没有可运行的目标",
"running_test": "运行测试: {name}",
"no_tests": "没有找到匹配的测试",
"test_passed": " ✓ 测试 {name} 通过",
"test_failed": " ✗ 测试 {name} 失败",
"test_timeout": " ✗ 测试 {name} 超时",
"test_error": " ✗ 测试 {name} 运行异常: {error}",
"test_results": "测试结果: {passed} 通过, {failed} 失败",
"project_initialized": "项目 '{name}' 初始化完成!",
"project_structure": "项目结构:",
"usage": "使用以下命令构建项目:",
"build_command": " cbuild build",
"run_command": " cbuild run",
"test_command": " cbuild test",
"dependency_tree": "dependency tree:",
"skipping": "跳过 {file}",
"executing_command": "执行命令: {cmd}",
"command_failed": "命令执行失败 [{code}]: {cmd}",
"compiling_file": "编译文件 {file} 时出错: {error}",
"linking_target": "链接目标 {target} 时出错: {error}",
"regex_filter": "使用正则表达式过滤测试: '{pattern}',匹配到 {count} 个测试",
"invalid_regex": "无效的正则表达式 '{pattern}': {error}",
},
"en_US": {
"build_complete": "Finished {mode} target(s) in {time} (using {jobs} threads)",
"clean_directory": "Cleaned build directory: {path}",
"cleaned_files": "Cleaned {count} files, total size {size}",
"no_build_dir": "No build directory found, nothing to clean",
"no_targets": "No target to run",
"running_test": "Running test: {name}",
"no_tests": "No matching tests found",
"test_passed": " ✓ Test {name} passed",
"test_failed": " ✗ Test {name} failed",
"test_timeout": " ✗ Test {name} timed out",
"test_error": " ✗ Test {name} error: {error}",
"test_results": "Test results: {passed} passed, {failed} failed",
"project_initialized": "Project '{name}' initialized!",
"project_structure": "Project structure:",
"usage": "Use the following commands to build the project:",
"build_command": " cbuild build",
"run_command": " cbuild run",
"test_command": " cbuild test",
"dependency_tree": "dependency tree:",
"skipping": "Skipping {file}",
"executing_command": "Executing command: {cmd}",
"command_failed": "Command failed [{code}]: {cmd}",
"compiling_file": "Error compiling file {file}: {error}",
"linking_target": "Error linking target {target}: {error}",
"regex_filter": "Filtering tests with regex: '{pattern}', matched {count} tests",
"invalid_regex": "Invalid regex '{pattern}': {error}",
},
}
def translate(self, key, **kwargs):
"""翻译键值"""
lang_dict = self.translations.get(self.lang, self.translations["zh_CN"])
template = lang_dict.get(key)
if template is None:
return key
if kwargs:
try:
return template.format(**kwargs)
except KeyError:
return template
return template
def set_language(self, lang):
"""设置语言"""
if lang in self.translations:
self.lang = lang
# 全局翻译器实例
translator = Translator(None)
@dataclass
class Dependency:
"""依赖配置"""
@@ -590,7 +698,11 @@ class PackageBuilder:
"""包构建器"""
def __init__(
self, package_path: Path, compiler: Compiler, mode: BuildMode = BuildMode.DEV
self,
package_path: Path,
compiler: Compiler,
mode: BuildMode = BuildMode.DEV,
jobs: int = 0, # 0 表示自动检测 CPU 核心数
):
self.package = PackageConfig(package_path)
self.context = BuildContext(self.package, mode)
@@ -598,6 +710,11 @@ class PackageBuilder:
self.cache = BuildCache(self.context.paths.output)
self.mode = mode
self.flags = self.compiler.get_flags(mode)
self.jobs = jobs if jobs > 0 else self._get_cpu_count()
def _get_cpu_count(self) -> int:
"""获取 CPU 核心数"""
return os.cpu_count() or 1
def _compile(self, source: Path, output: Path):
"""编译单个源文件"""
@@ -607,7 +724,73 @@ class PackageBuilder:
)
self.cache.update(source, output)
else:
logger.debug("跳过 %s", source)
logger.debug(translator.translate("skipping", file=str(source)))
def _parallel_compile(self, compile_tasks: list[tuple[Path, Path]]) -> list[Path]:
"""并行编译多个源文件"""
if not compile_tasks:
return []
# 如果只有一个文件或线程数为1使用串行编译
if len(compile_tasks) == 1 or self.jobs == 1:
objects = []
for src, obj in compile_tasks:
self._compile(src, obj)
objects.append(obj)
return objects
# 使用线程池并行编译
objects = []
with concurrent.futures.ThreadPoolExecutor(max_workers=self.jobs) as executor:
# 提交所有编译任务
future_to_task = {}
for src, obj in compile_tasks:
future = executor.submit(self._compile, src, obj)
future_to_task[future] = (src, obj)
# 等待所有任务完成
for future in concurrent.futures.as_completed(future_to_task):
src, obj = future_to_task[future]
try:
future.result() # 检查是否有异常
objects.append(obj)
except Exception as e:
logger.error("编译文件 %s 时出错: %s", src, e)
raise
return objects
def _parallel_link(self, link_tasks: list[tuple[Target, list[Path]]]):
"""并行链接多个目标"""
if not link_tasks:
return
# 如果只有一个链接任务或线程数为1使用串行链接
if len(link_tasks) == 1 or self.jobs == 1:
for target, objects in link_tasks:
objects.append(target.object)
self.compiler.link(objects, target.output, self.flags)
return
# 使用线程池并行链接
with concurrent.futures.ThreadPoolExecutor(max_workers=self.jobs) as executor:
# 提交所有链接任务
future_to_task = {}
for target, objects in link_tasks:
objects.append(target.object)
future = executor.submit(
self.compiler.link, objects, target.output, self.flags
)
future_to_task[future] = target
# 等待所有任务完成
for future in concurrent.futures.as_completed(future_to_task):
target = future_to_task[future]
try:
future.result() # 检查是否有异常
except Exception as e:
logger.error("链接目标 %s 时出错: %s", target.name, e)
raise
def build(self, target_types: list[TargetType]):
"""构建包"""
@@ -618,14 +801,16 @@ class PackageBuilder:
self.context.paths.output.mkdir(parents=True, exist_ok=True)
self.context.paths.objects.mkdir(parents=True, exist_ok=True)
# 编译所有依赖的源文件
objects = []
for src, obj in self.context.get_compile_objects():
self._compile(src, obj)
objects.append(obj)
# 获取需要编译的文件列表
compile_tasks = self.context.get_compile_objects()
# 使用线程池并行编译
objects = self._parallel_compile(compile_tasks)
# 构建目标
exec_types = {TargetType.MAIN_EXEC, TargetType.EXEC, TargetType.TEST_EXEC}
link_tasks = []
for target in self.context.get_targets():
if target.type not in target_types:
continue
@@ -633,14 +818,17 @@ class PackageBuilder:
continue
self._compile(target.source, target.object)
objects.append(target.object)
self.compiler.link(objects, target.output, self.flags)
objects.pop()
link_tasks.append((target, objects.copy()))
# 并行链接
self._parallel_link(link_tasks)
self.cache.save()
elapsed = time.time() - start
time_str = f"{elapsed:.2f}s" if elapsed < 60 else f"{elapsed / 60:.1f}m"
logger.info("完成 %s 目标,耗时 %s", self.mode.value, time_str)
logger.info(
"完成 %s 目标,耗时 %s (使用 %d 线程)", self.mode.value, time_str, self.jobs
)
def run(self):
"""运行主程序"""
@@ -830,6 +1018,9 @@ def create_parser():
"--compiler", "-c", choices=["gcc", "clang"], default="gcc", help="编译器"
)
subparser.add_argument("--record", "-r", action="store_true", help="记录命令")
subparser.add_argument(
"--jobs", "-j", type=int, default=0, help="并行编译任务数 (0=自动检测)"
)
mode_group = subparser.add_mutually_exclusive_group()
mode_group.add_argument(
"--debug",
@@ -919,8 +1110,11 @@ def main():
# 获取构建模式
mode = getattr(args, "mode", BuildMode.DEV)
# 获取线程数
jobs = getattr(args, "jobs", 0)
# 创建构建器
builder = PackageBuilder(Path(args.path), compiler, mode)
builder = PackageBuilder(Path(args.path), compiler, mode, jobs)
# 执行命令
if args.command == "build":