Skip to main content

xcstrings_mcp/model/
translation.rs

1use std::collections::BTreeMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
7pub struct TranslationUnit {
8    pub key: String,
9    pub source_text: String,
10    pub target_locale: String,
11    #[serde(default, skip_serializing_if = "Option::is_none")]
12    pub comment: Option<String>,
13    pub format_specifiers: Vec<String>,
14    pub has_plurals: bool,
15    pub has_substitutions: bool,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
19pub struct CompletedTranslation {
20    pub key: String,
21    pub locale: String,
22    pub value: String,
23    #[serde(default, skip_serializing_if = "Option::is_none")]
24    pub plural_forms: Option<BTreeMap<String, String>>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub substitution_name: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
30pub struct FileSummary {
31    pub source_language: String,
32    pub total_keys: usize,
33    pub translatable_keys: usize,
34    pub locales: Vec<String>,
35    pub keys_by_state: BTreeMap<String, usize>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
39pub struct SubmitResult {
40    pub accepted: usize,
41    pub rejected: Vec<RejectedTranslation>,
42    pub dry_run: bool,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
46pub struct RejectedTranslation {
47    pub key: String,
48    pub reason: String,
49}
50
51/// Per-locale translation coverage statistics.
52#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
53pub struct LocaleCoverage {
54    pub locale: String,
55    pub total_keys: usize,
56    pub translatable_keys: usize,
57    pub translated: usize,
58    pub percentage: f64,
59}
60
61/// Full coverage report across all locales.
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
63pub struct CoverageReport {
64    pub source_language: String,
65    pub total_keys: usize,
66    pub translatable_keys: usize,
67    pub locales: Vec<LocaleCoverage>,
68}
69
70/// Validation result with errors and warnings for a single locale.
71#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
72pub struct ValidationReport {
73    pub locale: String,
74    pub errors: Vec<ValidationIssue>,
75    pub warnings: Vec<ValidationIssue>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
79pub struct ValidationIssue {
80    pub key: String,
81    pub issue_type: String,
82    pub message: String,
83}
84
85/// Locale info for list_locales output.
86#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
87pub struct LocaleInfo {
88    pub locale: String,
89    pub translated: usize,
90    pub total: usize,
91    pub percentage: f64,
92}
93
94/// A key requiring plural translation (returned by get_plurals).
95#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
96pub struct PluralUnit {
97    pub key: String,
98    pub source_text: String,
99    pub target_locale: String,
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub comment: Option<String>,
102    pub format_specifiers: Vec<String>,
103    /// Required plural forms for target locale (from CLDR).
104    pub required_forms: Vec<String>,
105    /// Source language plural forms (if available).
106    pub source_forms: BTreeMap<String, String>,
107    /// Existing translations per plural form (if partially translated).
108    pub existing_translations: BTreeMap<String, String>,
109    /// True if this key uses substitutions (%#@VAR@).
110    pub has_substitutions: bool,
111    /// Device variant forms needed (if any).
112    #[serde(default, skip_serializing_if = "Vec::is_empty")]
113    pub device_forms: Vec<String>,
114}
115
116/// A nearby key sharing a common prefix, used for translator context.
117#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
118pub struct ContextKey {
119    pub key: String,
120    pub source_text: String,
121    #[serde(default, skip_serializing_if = "Option::is_none")]
122    pub translated_text: Option<String>,
123}