String packs: translate mode. Strings carry an invisible key marker, a click on a word opens an editor that checks the edit with the pack rules, edits collect in the tab as a draft pack that applies after a reload, and "Copy pack file" gives the pack; the Languages section switches it on (the delivery then carries the whole inventory, packs or not); native code accepts such a delivery without packs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CRTkafa 2026-10-01 04:25:04 +07:00
parent 8a0ab6d3a1
commit b4157d7eac
8 changed files with 359 additions and 6 deletions

View file

@ -302,12 +302,17 @@ impl StringsStore {
}
let value: serde_json::Value = serde_json::from_str(text).map_err(|_| "VALIDATION: the delivery is not JSON")?;
let inventory = value.get("inventory").filter(|v| v.is_object()).cloned();
let packs = value.get("packs").filter(|v| v.as_array().is_some_and(|packs| !packs.is_empty() && packs.len() <= MAX_ENABLED)).cloned();
// Translate mode may run without any pack (the author starts from the site's own words).
let translate = value.get("translate").and_then(serde_json::Value::as_bool).unwrap_or(false);
let packs = value.get("packs").filter(|v| v.as_array().is_some_and(|packs| (translate || !packs.is_empty()) && packs.len() <= MAX_ENABLED)).cloned();
let (Some(inventory), Some(packs)) = (inventory, packs) else {
return Err("VALIDATION: a delivery is { inventory, packs } with 1 to 8 packs".into());
};
stored.insert("inventory".into(), inventory);
stored.insert("packs".into(), packs);
if translate {
stored.insert("translate".into(), true.into());
}
}
stored.insert("rev".into(), rev.into());
let text = serde_json::Value::Object(stored).to_string();
@ -402,6 +407,7 @@ mod tests {
assert_eq!(store.delivery().as_deref(), Some(stored.as_str()));
assert!(store.set_delivery(Some("[]"), 8).is_err());
assert!(store.set_delivery(Some(r#"{"inventory":{},"packs":[]}"#), 8).is_err(), "no packs is None, not an empty list");
assert_eq!(store.set_delivery(Some(r#"{"inventory":{},"packs":[],"translate":true}"#), 8).unwrap(), r#"{"inventory":{},"packs":[],"rev":8,"translate":true}"#, "translate mode may start without a pack");
assert_eq!(store.list().packs.len(), 2, "the delivery file is not a pack");
assert_eq!(store.set_delivery(None, 9).unwrap(), r#"{"rev":9}"#);
store.remove("second").unwrap();