Skip to content

Commit 71d233d

Browse files
author
FreeSynergy
committed
feat(store): add fsn store i18n subcommand (status/set/check)
- I18nCommand enum: Status, Set { lang }, Check - status: fetch Node/catalog/i18n.toml, show completeness + schema_version - set/check: stubs, TODO implement download + activation - BUNDLED_SCHEMA_VERSION const for schema mismatch detection
1 parent bed6f79 commit 71d233d

2 files changed

Lines changed: 90 additions & 1 deletion

File tree

cli/crates/fsn-node-cli/src/cli.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,24 @@ pub enum StoreCommand {
219219
},
220220
/// Check for available updates
221221
Update,
222+
/// Manage UI language packs
223+
I18n {
224+
#[command(subcommand)]
225+
cmd: I18nCommand,
226+
},
227+
}
228+
229+
#[derive(Subcommand)]
230+
pub enum I18nCommand {
231+
/// Show available languages and their completeness
232+
Status,
233+
/// Download and activate a language pack
234+
Set {
235+
/// BCP 47 language code (e.g. "de", "fr", "ja")
236+
lang: String,
237+
},
238+
/// Check if installed language is up to date with the current schema
239+
Check,
222240
}
223241

224242
#[derive(Subcommand)]
@@ -279,6 +297,11 @@ pub async fn run() -> Result<()> {
279297
StoreCommand::Info { id } => commands::store::info(&id).await,
280298
StoreCommand::Install { id } => commands::store::install(&id).await,
281299
StoreCommand::Update => commands::store::update_check().await,
300+
StoreCommand::I18n { cmd } => match cmd {
301+
I18nCommand::Status => commands::store::i18n_status().await,
302+
I18nCommand::Set { lang } => commands::store::i18n_set(&lang).await,
303+
I18nCommand::Check => commands::store::i18n_check().await,
304+
},
282305
},
283306
Command::Server { cmd } => match cmd {
284307
ServerCommand::Setup => commands::server_setup::run(&root).await,

cli/crates/fsn-node-cli/src/commands/store.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// `fsn store` — browse and manage the FreeSynergy module store.
1+
// `fsn store` — browse and manage the FreeSynergy module store and language packs.
22
//
33
// Uses StoreClient (fsn-store) to fetch the Node.Store catalog and display
44
// available modules. Install/update are delegated to `fsn deploy` once
@@ -7,6 +7,10 @@
77
use anyhow::{Context, Result};
88
use fsn_node_core::store::StoreEntry;
99
use fsn_store::StoreClient;
10+
use toml;
11+
12+
// Schema version bundled with this binary — must match Lib's sync_snippets.py SCHEMA_VERSION.
13+
const BUNDLED_SCHEMA_VERSION: &str = "1.0.0";
1014

1115
// ── Catalog helper ─────────────────────────────────────────────────────────────
1216

@@ -116,3 +120,65 @@ pub async fn update_check() -> Result<()> {
116120
println!("To update a deployed module, run `fsn update --service <name>`.");
117121
Ok(())
118122
}
123+
124+
// ── i18n ───────────────────────────────────────────────────────────────────────
125+
126+
/// One entry from `[[languages]]` in the store's `catalog/i18n.toml`.
127+
#[derive(serde::Deserialize)]
128+
struct I18nCatalogEntry {
129+
code: String,
130+
name: String,
131+
#[serde(default)]
132+
completeness: u8,
133+
#[serde(default)]
134+
schema_version: String,
135+
// file / sha256 / size are used by the updater, not displayed here
136+
}
137+
138+
#[derive(serde::Deserialize)]
139+
struct I18nCatalog {
140+
#[serde(default, rename = "languages")]
141+
languages: Vec<I18nCatalogEntry>,
142+
}
143+
144+
/// Show all available language packs from the store catalog with completeness.
145+
pub async fn i18n_status() -> Result<()> {
146+
let client = StoreClient::node_store();
147+
let raw = client
148+
.fetch_raw("Node/catalog/i18n.toml")
149+
.await
150+
.context("fetching i18n catalog")?;
151+
let catalog: I18nCatalog = toml::from_str(&raw).context("parsing i18n catalog")?;
152+
153+
if catalog.languages.is_empty() {
154+
println!("No language packs available in the store yet.");
155+
return Ok(());
156+
}
157+
158+
println!("{:<6} {:<24} {:>5} {:<8} {}", "CODE", "LANGUAGE", "COMP%", "SCHEMA", "");
159+
println!("{}", "─".repeat(58));
160+
for e in &catalog.languages {
161+
let ok = e.schema_version.is_empty() || e.schema_version == BUNDLED_SCHEMA_VERSION;
162+
let marker = if ok { "✓" } else { "⚠ outdated" };
163+
let schema = if e.schema_version.is_empty() { BUNDLED_SCHEMA_VERSION } else { &e.schema_version };
164+
println!("{:<6} {:<24} {:>4}% {:<8} {}", e.code, e.name, e.completeness, schema, marker);
165+
}
166+
println!("\nRequired schema: {BUNDLED_SCHEMA_VERSION} (⚠ = needs update, run `fsn store i18n set <code>`)");
167+
Ok(())
168+
}
169+
170+
/// Download and activate a language pack.
171+
pub async fn i18n_set(lang: &str) -> Result<()> {
172+
// TODO: fetch merged lang file from store → write to ~/.config/fsn/i18n/{lang}.toml → set active
173+
println!("Language pack '{lang}' — download and activation not yet implemented.");
174+
println!("Run `fsn store i18n status` to see available packs.");
175+
Ok(())
176+
}
177+
178+
/// Check whether the currently active language pack matches the bundled schema version.
179+
pub async fn i18n_check() -> Result<()> {
180+
// TODO: read active lang from ~/.config/fsn/config.toml, compare schema_version
181+
println!("Schema version (bundled): {BUNDLED_SCHEMA_VERSION}");
182+
println!("Active language check — not yet implemented.");
183+
Ok(())
184+
}

0 commit comments

Comments
 (0)