feat(glossary): 实现 Rust Glossary V1

This commit is contained in:
2026-09-07 22:38:53 +08:00
parent 8fc93b8f39
commit 94483ff14d
42 changed files with 4543 additions and 92 deletions
@@ -104,7 +104,12 @@ pub(super) fn run_translation_validate(options: &CliOptions) -> anyhow::Result<(
.ok_or_else(|| anyhow::anyhow!("i18n validate 必须指定 --translation-file"))?;
let (resource_root, release_id) = current_official_release(options)?;
let workbench = read_translation_workbench(path)?;
let validation = validate_translation_workbench(&resource_root, &release_id, &workbench)?;
let validation = validate_translation_workbench_with_glossary_path(
&resource_root,
&release_id,
&workbench,
options.glossary_path.as_deref(),
)?;
let data = serde_json::json!({
"official_release_id": release_id,
"resource_root": resource_root,
@@ -141,7 +146,50 @@ pub(super) fn run_translation_set(options: &CliOptions) -> anyhow::Result<()> {
.translation_id
.as_deref()
.ok_or_else(|| anyhow::anyhow!("translation-set 必须指定 --translation-id"))?;
let entry = set_translation(path, entry_id, text)?;
let glossary_override = match (
options.glossary_reviewer.as_deref(),
options.glossary_reason.as_deref(),
options.glossary_override_provenance.as_deref(),
) {
(None, None, None) => None,
(Some(reviewer), Some(reason), Some(provenance)) => Some(
bat_core::domain::GlossaryOverride {
reviewer: reviewer.to_string(),
reason: reason.to_string(),
provenance: provenance.to_string(),
confirmed_unix_seconds: unix_seconds_now(),
},
),
_ => {
return Err(anyhow::anyhow!(
"Glossary override 必须同时指定 --glossary-reviewer、--glossary-reason 和 --glossary-provenance"
))
}
};
let workbench = read_translation_workbench(path)?;
let glossary_path = options.glossary_path.clone().unwrap_or_else(|| {
bat_infrastructure::SqliteGlossaryRepository::repository_path(
&workbench.official_resource_root,
)
});
let entry = if std::fs::symlink_metadata(&glossary_path).is_ok() {
let (resource_root, _) = current_official_release(options)?;
set_translation_checked_with_glossary_path(
&resource_root,
path,
entry_id,
text,
glossary_override,
options.glossary_path.as_deref(),
)?
} else {
if glossary_override.is_some() {
return Err(anyhow::anyhow!(
"当前项目没有 Glossary 数据库,不能提交 Glossary override"
));
}
set_translation(path, entry_id, text)?
};
let data = serde_json::json!({
"translation_file": path,
"entry": entry,
@@ -232,6 +280,7 @@ pub(super) fn run_translation_task_update(options: &CliOptions) -> anyhow::Resul
let report = update_translation_task_status_report(
&options.state_dir,
Some(&serde_json::Value::Object(params)),
options.glossary_path.as_deref(),
)?;
print_json_value(options.output_format, &report)
}
@@ -316,6 +365,12 @@ pub(super) fn publish_localized_report(
"翻译工作台资源根目录与当前 release 不一致;请重新导出"
));
}
validate_translation_workbench_with_glossary_path(
&resource_root,
&official_release_id,
&workbench,
options.glossary_path.as_deref(),
)?;
let operations = localized_patch_operations(&resource_root, &workbench)?;
let localized_release_id = options.localized_release_id.clone().or_else(|| {
options