feat(translation): add Rust Translation Memory and config migration

This commit is contained in:
2026-09-06 22:48:51 +08:00
parent 93f4bc69b3
commit 7d6389806b
18 changed files with 4414 additions and 384 deletions
+31 -7
View File
@@ -9,7 +9,7 @@ use crate::path_security::{
use crate::{
LocalizedPatchInput, LocalizedPatchOperationMetadata, LocalizedStringFieldPatch,
LocalizedTextAssetPatch, PersistedTranslationTask, SqliteTranslationTaskRepository,
TranslationTaskStatus, TranslationTaskUnitResult,
TranslationTaskResultSourceKind, TranslationTaskStatus, TranslationTaskUnitResult,
};
use bat_assetbundle::{
patch_unityfs_field, patch_unityfs_string_field, patch_unityfs_text_asset, FieldPatch,
@@ -72,6 +72,12 @@ pub struct TranslationWorkbenchEntry {
/// Provider run that produced this translation, when imported from worker output.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider_run_id: Option<String>,
/// Source of the worker result (`provider`, `manual`, or `translation_memory`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub translation_source_kind: Option<String>,
/// Trusted Translation Memory record used for this translation, when applicable.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub translation_memory_record_id: Option<String>,
/// Worker completion time for provider-produced text.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub translated_unix_seconds: Option<u64>,
@@ -616,6 +622,8 @@ fn localized_patch_metadata(entry: &TranslationWorkbenchEntry) -> LocalizedPatch
.to_string(),
translation_provider: entry.translation_provider.clone(),
provider_run_id: entry.provider_run_id.clone(),
translation_source_kind: entry.translation_source_kind.clone(),
translation_memory_record_id: entry.translation_memory_record_id.clone(),
review_status: entry
.review_status
.clone()
@@ -887,18 +895,30 @@ fn workbench_entry_from_worker_result(
) -> TranslationWorkbenchEntry {
let mut entry = TranslationWorkbenchEntry::from_index(unit);
entry.translated_text = Some(result.translated_text.clone());
entry.translation_provider = task
.provider
.clone()
.or_else(|| Some(result.provider.clone()))
.filter(|provider| !provider.trim().is_empty());
entry.translation_provider = match result.source_kind {
TranslationTaskResultSourceKind::TranslationMemory => None,
TranslationTaskResultSourceKind::Provider | TranslationTaskResultSourceKind::Manual => task
.provider
.clone()
.or_else(|| Some(result.provider.clone()))
.filter(|provider| !provider.trim().is_empty()),
};
entry.provider_run_id = task
.provider_run_id
.clone()
.or_else(|| Some(result.provider_run_id.clone()))
.filter(|provider_run_id| !provider_run_id.trim().is_empty());
entry.translation_source_kind = Some(result.source_kind.as_str().to_string());
entry.translation_memory_record_id = result.translation_memory_record_id.clone();
entry.translated_unix_seconds = Some(result.translated_unix_seconds);
entry.review_status = Some("provider_completed".to_string());
entry.review_status = Some(
match result.source_kind {
TranslationTaskResultSourceKind::Provider => "provider_completed",
TranslationTaskResultSourceKind::Manual => "manual_submitted",
TranslationTaskResultSourceKind::TranslationMemory => "translation_memory_reused",
}
.to_string(),
);
entry
}
@@ -938,6 +958,8 @@ impl TranslationWorkbenchEntry {
translated_text: None,
translation_provider: None,
provider_run_id: None,
translation_source_kind: None,
translation_memory_record_id: None,
translated_unix_seconds: None,
review_status: None,
format: unit.format.clone(),
@@ -975,6 +997,8 @@ mod tests {
translated_text: None,
translation_provider: None,
provider_run_id: None,
translation_source_kind: None,
translation_memory_record_id: None,
translated_unix_seconds: None,
review_status: None,
format: Some("plain".to_string()),