fix(glossary): 绑定 QA identity 与人工 override
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

This commit is contained in:
2026-09-09 00:25:49 +08:00
parent 0275a890bc
commit 68c6c91b1e
22 changed files with 844 additions and 92 deletions
+10 -5
View File
@@ -10,10 +10,10 @@ use bat_infrastructure::{
apply_unityfs_text_asset_patch_file, changed_endpoint_urls,
completed_worker_translation_workbench, diff_extended_snapshot, export_translation_workbench,
gc_orphan_staging_with_cas_root, get_translation_entry, lexical_absolute,
localized_patch_operations, open_append_file, read_download_manifest_at, read_file_no_symlink,
read_localized_patch_manifest_at, read_localized_version_state, read_parse_cache_at,
read_snapshot, read_textunit_index_at, read_translation_workbench, read_version_state,
redact_proxy_url, repack_bundle, resolve_curl_proxy, set_translation,
localized_patch_operations_with_glossary_path, open_append_file, read_download_manifest_at,
read_file_no_symlink, read_localized_patch_manifest_at, read_localized_version_state,
read_parse_cache_at, read_snapshot, read_textunit_index_at, read_translation_workbench,
read_version_state, redact_proxy_url, repack_bundle, resolve_curl_proxy, set_translation,
set_translation_checked_with_glossary_path, unset_translation, validate_output_root,
validate_runtime_state_dir, validate_translation_workbench_with_glossary_path,
write_file_atomic, write_official_textunit_queues, CurlProxyConfig, CurlProxyMode,
@@ -430,6 +430,7 @@ struct CliOptions {
glossary_reviewer: Option<String>,
glossary_reason: Option<String>,
glossary_override_provenance: Option<String>,
glossary_qa_identity: Option<String>,
glossary_source_text: Option<String>,
glossary_context_json: Option<String>,
glossary_review_status: Option<String>,
@@ -555,6 +556,7 @@ impl Default for CliOptions {
glossary_reviewer: None,
glossary_reason: None,
glossary_override_provenance: None,
glossary_qa_identity: None,
glossary_source_text: None,
glossary_context_json: None,
glossary_review_status: None,
@@ -6949,6 +6951,9 @@ fn parse_args_with_env(
"--glossary-provenance" | "--glossary-override-provenance" => {
options.glossary_override_provenance = Some(next_option_value(&mut args, &flag)?);
}
"--glossary-qa-identity" => {
options.glossary_qa_identity = Some(next_option_value(&mut args, &flag)?);
}
"--glossary-source-text" => {
options.glossary_source_text = Some(next_option_value(&mut args, &flag)?);
}
@@ -7871,7 +7876,7 @@ fn parse_args_with_env(
|| tools_are_non_default(&options.config, &options.env_baseline_config)
{
return Err(anyhow::anyhow!(
"translation workbench 编辑命令只接受 --translation-file、--translation-id、译文字段、--state-dir --json/--human"
"translation workbench 编辑命令只接受 --translation-file、--translation-id、译文和 Glossary override 字段、--state-dir 以及 --json/--human"
));
}
options.progress = false;
@@ -448,6 +448,7 @@ Sync:
--glossary-reviewer <ID> Reviewer for Glossary updates/reviews/delete
--glossary-reason <TEXT> Reason for Glossary review/delete or override
--glossary-provenance <TEXT> Provenance for an explicit Glossary override
--glossary-qa-identity <ID> Current blocking Glossary QA identity for an override
--worker-concurrency <N> Translation worker concurrency (default: 8, range 1..=256)
--worker-max-attempts <N> Maximum claims per translation task
--worker-lease-seconds <N> Lease seconds for one claimed task
+10 -15
View File
@@ -1,6 +1,6 @@
use super::report_output::print_json_value;
use super::*;
use bat_core::domain::{GlossaryOverride, TranslationMemoryContext};
use bat_core::domain::{validate_glossary_override, GlossaryOverride, TranslationMemoryContext};
use bat_core::repositories::TranslationMemoryRepository;
use std::collections::BTreeMap;
@@ -377,24 +377,19 @@ async fn build_manual_translation_results(
} else {
None
};
if let Some(qa) = glossary_qa.as_ref().filter(|qa| qa.status.is_blocked()) {
let Some(override_record) = param.glossary_override.as_ref() else {
if let Some(qa) = glossary_qa.as_ref() {
if qa.status.is_blocked() {
validate_glossary_override(qa, param.glossary_override.as_ref()).map_err(
|error| {
anyhow::anyhow!("TextUnit {} 的 glossary_override 无效:{error}", unit_id)
},
)?;
} else if param.glossary_override.is_some() {
return Err(anyhow::anyhow!(
"TextUnit {} 的 Glossary QA blocked;必须提供 glossary_override",
unit_id
));
};
if override_record.reviewer.trim().is_empty()
|| override_record.reason.trim().is_empty()
|| override_record.provenance.trim().is_empty()
|| override_record.confirmed_unix_seconds == 0
{
return Err(anyhow::anyhow!(
"TextUnit {} 的 glossary_override 不完整或 confirmed_unix_seconds 无效",
"TextUnit {} 不能为非 blocking Glossary QA 指定 override",
unit_id
));
}
let _ = qa;
} else if param.glossary_override.is_some() {
return Err(anyhow::anyhow!(
"TextUnit {} 不能为非 blocking Glossary QA 指定 override",
@@ -150,10 +150,12 @@ pub(super) fn run_translation_set(options: &CliOptions) -> anyhow::Result<()> {
options.glossary_reviewer.as_deref(),
options.glossary_reason.as_deref(),
options.glossary_override_provenance.as_deref(),
options.glossary_qa_identity.as_deref(),
) {
(None, None, None) => None,
(Some(reviewer), Some(reason), Some(provenance)) => Some(
(None, None, None, None) => None,
(Some(reviewer), Some(reason), Some(provenance), Some(qa_identity)) => Some(
bat_core::domain::GlossaryOverride {
qa_identity: qa_identity.to_string(),
reviewer: reviewer.to_string(),
reason: reason.to_string(),
provenance: provenance.to_string(),
@@ -162,7 +164,7 @@ pub(super) fn run_translation_set(options: &CliOptions) -> anyhow::Result<()> {
),
_ => {
return Err(anyhow::anyhow!(
"Glossary override 必须同时指定 --glossary-reviewer、--glossary-reason 和 --glossary-provenance"
"Glossary override 必须同时指定 --glossary-qa-identity、--glossary-reviewer、--glossary-reason 和 --glossary-provenance"
))
}
};
@@ -371,7 +373,11 @@ pub(super) fn publish_localized_report(
&workbench,
options.glossary_path.as_deref(),
)?;
let operations = localized_patch_operations(&resource_root, &workbench)?;
let operations = localized_patch_operations_with_glossary_path(
&resource_root,
&workbench,
options.glossary_path.as_deref(),
)?;
let localized_release_id = options.localized_release_id.clone().or_else(|| {
options
.config