mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 06:34:54 +08:00
feat(translation): add Rust Translation Memory and config migration
This commit is contained in:
@@ -4,6 +4,7 @@ pub mod game_client;
|
||||
pub mod game_version;
|
||||
pub mod resource;
|
||||
pub mod translation;
|
||||
pub mod translation_memory;
|
||||
|
||||
pub use game_client::{ClientStatus, GameClient, GameRegion};
|
||||
pub use game_version::{GameVersion, UnityVersion};
|
||||
@@ -14,3 +15,8 @@ pub use translation::{
|
||||
ExtractedText, SourceText, TextContext, TextMetadata, TextSource, TranslatedText,
|
||||
TranslationStatus,
|
||||
};
|
||||
pub use translation_memory::{
|
||||
TranslationMemoryContext, TranslationMemoryDraft, TranslationMemoryEntry,
|
||||
TranslationMemoryMatch, TranslationMemoryMatchKind, TranslationMemorySourceKind,
|
||||
TranslationMemorySourceTrace, TranslationMemorySummary, TranslationMemoryTrustStatus,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
//! Translation Memory 领域对象。
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// TM 记录的来源类型。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TranslationMemorySourceKind {
|
||||
/// 来自 provider 输出。
|
||||
Provider,
|
||||
/// 来自人工确认。
|
||||
Manual,
|
||||
/// 来自外部导入。
|
||||
Imported,
|
||||
}
|
||||
|
||||
impl TranslationMemorySourceKind {
|
||||
/// 返回稳定的持久化标签。
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Provider => "provider",
|
||||
Self::Manual => "manual",
|
||||
Self::Imported => "imported",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TM 记录的可信状态。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TranslationMemoryTrustStatus {
|
||||
/// 候选记录,不能自动复用。
|
||||
Candidate,
|
||||
/// 已确认可信,可在强匹配时自动复用。
|
||||
Trusted,
|
||||
/// 已被后续记录取代。
|
||||
Superseded,
|
||||
/// 已明确拒绝。
|
||||
Rejected,
|
||||
}
|
||||
|
||||
impl TranslationMemoryTrustStatus {
|
||||
/// 返回稳定的持久化标签。
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Candidate => "candidate",
|
||||
Self::Trusted => "trusted",
|
||||
Self::Superseded => "superseded",
|
||||
Self::Rejected => "rejected",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TM 查询结果的匹配类型。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TranslationMemoryMatchKind {
|
||||
/// 原始 source 和上下文都完全匹配,且记录可信,可自动复用。
|
||||
StrongExact,
|
||||
/// 原始 source 完全匹配,但上下文不同或不足,不能自动复用。
|
||||
CandidateExact,
|
||||
/// 原始 source 匹配,但上下文不兼容,不能自动复用。
|
||||
SourceOnly,
|
||||
}
|
||||
|
||||
impl TranslationMemoryMatchKind {
|
||||
/// 返回稳定的查询结果标签。
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::StrongExact => "strong_exact",
|
||||
Self::CandidateExact => "candidate_exact",
|
||||
Self::SourceOnly => "source_only",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 稳定的上下文键值。
|
||||
pub type TranslationMemoryContext = BTreeMap<String, String>;
|
||||
|
||||
/// TM 记录的 TextUnit / provider 溯源信息。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TranslationMemorySourceTrace {
|
||||
/// 源官方 release ID。
|
||||
pub official_release_id: String,
|
||||
/// 来源 TextUnit ID。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub unit_id: Option<String>,
|
||||
/// 来源任务 ID。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub task_id: Option<String>,
|
||||
/// 源资源 destination。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub destination: Option<String>,
|
||||
/// 源 ZIP/archive entry。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub archive_entry: Option<String>,
|
||||
/// Unity serialized file。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub serialized_file: Option<String>,
|
||||
/// Unity object path ID。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub path_id: Option<i64>,
|
||||
/// Unity class ID。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub class_id: Option<i32>,
|
||||
/// TypeTree 字段路径。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub field_path: Option<String>,
|
||||
/// TextUnit format。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub format: Option<String>,
|
||||
/// TextAsset 名称。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub asset_name: Option<String>,
|
||||
/// TextUnit 来源类型。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub text_source_kind: Option<String>,
|
||||
/// 源 URL。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub source_url: Option<String>,
|
||||
}
|
||||
|
||||
/// TM 记录的候选输入。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TranslationMemoryDraft {
|
||||
/// 原始 source text。
|
||||
pub source_text: String,
|
||||
/// 稳定上下文。
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub source_context: TranslationMemoryContext,
|
||||
/// 译文。
|
||||
pub translated_text: String,
|
||||
/// 译文来源类型。
|
||||
pub translation_source_kind: TranslationMemorySourceKind,
|
||||
/// 源官方 release。
|
||||
pub official_release_id: String,
|
||||
/// 原始 TextUnit / provider 溯源。
|
||||
pub source_trace: TranslationMemorySourceTrace,
|
||||
/// provider。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<String>,
|
||||
/// provider run。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider_run_id: Option<String>,
|
||||
/// 创建时间。
|
||||
pub observed_unix_seconds: u64,
|
||||
}
|
||||
|
||||
/// 持久化 TM 记录。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TranslationMemoryEntry {
|
||||
/// 稳定记录 ID。
|
||||
pub record_id: String,
|
||||
/// 原始 source text。
|
||||
pub source_text: String,
|
||||
/// source text hash。
|
||||
pub source_hash: String,
|
||||
/// 保守归一化后的 source text,仅用于辅助查询。
|
||||
pub normalized_source_text: String,
|
||||
/// 稳定上下文。
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub source_context: TranslationMemoryContext,
|
||||
/// 上下文 hash。
|
||||
pub source_context_hash: String,
|
||||
/// 译文。
|
||||
pub translated_text: String,
|
||||
/// 译文来源类型。
|
||||
pub translation_source_kind: TranslationMemorySourceKind,
|
||||
/// 当前可信状态。
|
||||
pub trust_status: TranslationMemoryTrustStatus,
|
||||
/// 源官方 release。
|
||||
pub official_release_id: String,
|
||||
/// 原始 TextUnit / provider 溯源。
|
||||
pub source_trace: TranslationMemorySourceTrace,
|
||||
/// provider。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider: Option<String>,
|
||||
/// provider run。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub provider_run_id: Option<String>,
|
||||
/// 创建时间。
|
||||
pub created_unix_seconds: u64,
|
||||
/// 更新时间。
|
||||
pub updated_unix_seconds: u64,
|
||||
/// 可信确认时间。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub trusted_unix_seconds: Option<u64>,
|
||||
/// 可信确认人。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub trusted_by: Option<String>,
|
||||
/// 可信确认说明。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub trusted_reason: Option<String>,
|
||||
/// 该记录替代了哪条记录。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub supersedes_record_id: Option<String>,
|
||||
/// 该记录被哪条记录替代。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub superseded_by_record_id: Option<String>,
|
||||
}
|
||||
|
||||
/// TM 查询结果。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TranslationMemoryMatch {
|
||||
/// 记录本体。
|
||||
pub entry: TranslationMemoryEntry,
|
||||
/// 匹配类型。
|
||||
pub match_kind: TranslationMemoryMatchKind,
|
||||
/// 是否允许自动复用。
|
||||
pub can_auto_reuse: bool,
|
||||
}
|
||||
|
||||
/// TM 仓储摘要。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TranslationMemorySummary {
|
||||
/// schema 版本。
|
||||
pub schema_version: u32,
|
||||
/// 记录总数。
|
||||
pub record_count: u64,
|
||||
/// 可信记录数。
|
||||
pub trusted_count: u64,
|
||||
/// 候选记录数。
|
||||
pub candidate_count: u64,
|
||||
/// 已替代记录数。
|
||||
pub superseded_count: u64,
|
||||
/// 已拒绝记录数。
|
||||
pub rejected_count: u64,
|
||||
}
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
pub mod cas_repository;
|
||||
pub mod resource_repository;
|
||||
pub mod translation_memory_repository;
|
||||
pub mod translation_repository;
|
||||
|
||||
pub use cas_repository::CasRepository;
|
||||
pub use resource_repository::ResourceRepository;
|
||||
pub use translation_memory_repository::TranslationMemoryRepository;
|
||||
pub use translation_repository::TranslationRepository;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//! Translation Memory 仓储契约。
|
||||
|
||||
use crate::domain::{
|
||||
TranslationMemoryContext, TranslationMemoryDraft, TranslationMemoryEntry,
|
||||
TranslationMemoryMatch, TranslationMemorySummary,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
/// 跨 official release 持久化的 Translation Memory 仓储。
|
||||
///
|
||||
/// 该契约只描述 V1 的精确查询和明确人工确认。仓储实现不得把
|
||||
/// `TranslationTaskStatus::Completed` 或 provider 成功隐式解释为 trusted。
|
||||
#[async_trait]
|
||||
pub trait TranslationMemoryRepository: Send + Sync {
|
||||
/// 保存一条 provider/manual/imported 译文候选。
|
||||
///
|
||||
/// 相同 source、上下文、译文、来源 release 和来源类型的重复写入必须幂等;
|
||||
/// 已 trusted 的记录不得被普通候选静默覆盖。
|
||||
async fn upsert_candidate(
|
||||
&self,
|
||||
draft: TranslationMemoryDraft,
|
||||
) -> crate::Result<TranslationMemoryEntry>;
|
||||
|
||||
/// 按原始 source text 和上下文查询精确匹配。
|
||||
///
|
||||
/// 实现可以返回 source 归一化后但原文不同的辅助候选,但这类结果不能自动复用。
|
||||
async fn find_matches(
|
||||
&self,
|
||||
source_text: &str,
|
||||
source_context: &TranslationMemoryContext,
|
||||
limit: usize,
|
||||
) -> crate::Result<Vec<TranslationMemoryMatch>>;
|
||||
|
||||
/// 显式确认一条记录为 trusted。
|
||||
async fn confirm(
|
||||
&self,
|
||||
record_id: &str,
|
||||
reviewer: &str,
|
||||
reason: Option<String>,
|
||||
) -> crate::Result<TranslationMemoryEntry>;
|
||||
|
||||
/// 按稳定记录 ID 读取一条 TM 记录。
|
||||
async fn find(&self, record_id: &str) -> crate::Result<TranslationMemoryEntry>;
|
||||
|
||||
/// 读取数据库和记录统计。
|
||||
async fn summary(&self) -> crate::Result<TranslationMemorySummary>;
|
||||
}
|
||||
Reference in New Issue
Block a user