Skip to content

Commit 2a14747

Browse files
committed
fix(core): preserve non-translatable xcstrings entries on edit set
1 parent bfd36d6 commit 2a14747

2 files changed

Lines changed: 143 additions & 0 deletions

File tree

langcodec-cli/tests/edit_cli_tests.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,3 +505,73 @@ fn test_edit_set_xcstrings_preserves_is_comment_auto_generated() {
505505
serde_json::Value::Bool(true)
506506
);
507507
}
508+
509+
#[test]
510+
fn test_edit_set_xcstrings_preserves_non_translatable_entry_without_localizations() {
511+
let temp_dir = TempDir::new().unwrap();
512+
let input_file = temp_dir.path().join("Localizable.xcstrings");
513+
let initial = r#"{
514+
"sourceLanguage": "en",
515+
"version": "1.0",
516+
"strings": {
517+
"game_title": {
518+
"localizations": {
519+
"en": {
520+
"stringUnit": {
521+
"state": "translated",
522+
"value": "Game"
523+
}
524+
}
525+
}
526+
},
527+
"Carrom": {
528+
"comment": "The text label for the Carrom game button.",
529+
"isCommentAutoGenerated": true,
530+
"shouldTranslate": false
531+
}
532+
}
533+
}
534+
"#;
535+
fs::write(&input_file, initial).unwrap();
536+
537+
let out = langcodec_cmd()
538+
.args([
539+
"edit",
540+
"set",
541+
"-i",
542+
input_file.to_str().unwrap(),
543+
"--lang",
544+
"en",
545+
"-k",
546+
"game_title",
547+
"-v",
548+
"Game Updated",
549+
])
550+
.output()
551+
.unwrap();
552+
assert!(
553+
out.status.success(),
554+
"stderr: {}",
555+
String::from_utf8_lossy(&out.stderr)
556+
);
557+
558+
let content = fs::read_to_string(&input_file).unwrap();
559+
let payload: serde_json::Value = serde_json::from_str(&content).unwrap();
560+
561+
assert_eq!(
562+
payload["strings"]["Carrom"]["comment"],
563+
"The text label for the Carrom game button."
564+
);
565+
assert_eq!(
566+
payload["strings"]["Carrom"]["isCommentAutoGenerated"],
567+
serde_json::Value::Bool(true)
568+
);
569+
assert_eq!(
570+
payload["strings"]["Carrom"]["shouldTranslate"],
571+
serde_json::Value::Bool(false)
572+
);
573+
assert_eq!(
574+
payload["strings"]["game_title"]["localizations"]["en"]["stringUnit"]["value"],
575+
"Game Updated"
576+
);
577+
}

langcodec/src/formats/xcstrings.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,30 @@ impl TryFrom<Format> for Vec<Resource> {
161161
custom: custom.clone(),
162162
});
163163
}
164+
} else {
165+
// Preserve non-translatable metadata-only entries by attaching an empty
166+
// do-not-translate entry to source language.
167+
let source_language = custom_meta
168+
.get("source_language")
169+
.cloned()
170+
.unwrap_or_else(|| "en".to_string());
171+
resource_map
172+
.entry(source_language.clone())
173+
.or_insert(Resource {
174+
metadata: Metadata {
175+
language: source_language,
176+
domain: String::default(),
177+
custom: custom_meta.clone(),
178+
},
179+
entries: Vec::new(),
180+
})
181+
.add_entry(Entry {
182+
id: id.clone(),
183+
value: Translation::Empty,
184+
comment: item.comment.clone(),
185+
status: EntryStatus::DoNotTranslate,
186+
custom: custom.clone(),
187+
});
164188
}
165189
continue;
166190
}
@@ -552,4 +576,53 @@ mod tests {
552576
assert_eq!(empty_entry.comment.as_deref(), Some("Missing translation"));
553577
}
554578
}
579+
580+
#[test]
581+
fn test_non_translatable_item_without_localizations_is_preserved() {
582+
let mut strings = HashMap::new();
583+
strings.insert(
584+
"game_title".to_string(),
585+
Item {
586+
localizations: {
587+
let mut localizations = HashMap::new();
588+
localizations.insert(
589+
"en".to_string(),
590+
Localization::from(StringUnit::new(EntryStatus::Translated, "Game")),
591+
);
592+
localizations
593+
},
594+
comment: None,
595+
extraction_state: None,
596+
should_translate: Some(true),
597+
is_comment_auto_generated: None,
598+
},
599+
);
600+
strings.insert(
601+
"Carrom".to_string(),
602+
Item {
603+
localizations: HashMap::new(),
604+
comment: Some("The text label for the Carrom game button.".to_string()),
605+
extraction_state: None,
606+
should_translate: Some(false),
607+
is_comment_auto_generated: Some(true),
608+
},
609+
);
610+
let format = Format {
611+
source_language: "en".to_string(),
612+
version: "1.0".to_string(),
613+
strings,
614+
};
615+
616+
let resources = Vec::<Resource>::try_from(format).expect("resources from xcstrings");
617+
let roundtrip = Format::try_from(resources).expect("xcstrings from resources");
618+
let carrom = roundtrip.strings.get("Carrom").expect("Carrom item exists");
619+
620+
assert!(carrom.localizations.is_empty());
621+
assert_eq!(
622+
carrom.comment.as_deref(),
623+
Some("The text label for the Carrom game button.")
624+
);
625+
assert_eq!(carrom.should_translate, Some(false));
626+
assert_eq!(carrom.is_comment_auto_generated, Some(true));
627+
}
555628
}

0 commit comments

Comments
 (0)