app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 5081dd397512af7213a4a05f40ebab509d0b04c7
parent fa39edacd059ec42ddc3be91ecd28cc61a8b3c37
Author: triesap <tyson@radroots.org>
Date:   Mon,  2 Feb 2026 14:32:16 +0000

build: add embedded i18n assets

- add mf2-i18n config and english locale seed
- embed generated id map and pack artifacts
- load embedded runtime from build outputs
- update cargo lockfile for mf2-i18n deps

Diffstat:
MCargo.lock | 14++++++++++++++
Aapp/i18n/build/i18n.catalog.json | 8++++++++
Aapp/i18n/build/id_map.json | 2++
Aapp/i18n/build/id_map_hash | 1+
Aapp/i18n/build/manifest.json | 2++
Aapp/i18n/build/packs/en.mf2pack | 0
Aapp/i18n/id_salt.txt | 1+
Aapp/i18n/locales/en/messages.mf2 | 1+
Aapp/i18n/mf2-i18n.toml | 3+++
Mapp/src/i18n.rs | 48++++++++++++++++++++++++++++++++++++++++++++++--
10 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1328,6 +1328,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] +name = "mf2-i18n-core" +version = "0.1.0" + +[[package]] +name = "mf2-i18n-embedded" +version = "0.1.0" +dependencies = [ + "mf2-i18n-core", +] + +[[package]] name = "next_tuple" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1676,7 +1687,10 @@ dependencies = [ "js-sys", "leptos", "leptos_router", + "mf2-i18n-core", + "mf2-i18n-embedded", "radroots-app-core", + "radroots-app-lib", "radroots-app-ui-components", "radroots-log", "radroots-nostr", diff --git a/app/i18n/build/i18n.catalog.json b/app/i18n/build/i18n.catalog.json @@ -0,0 +1,7 @@ +{ + "schema": 1, + "project": "radroots-app", + "generated_at": "2026-02-02T00:00:00Z", + "default_locale": "en", + "messages": [] +} +\ No newline at end of file diff --git a/app/i18n/build/id_map.json b/app/i18n/build/id_map.json @@ -0,0 +1 @@ +{} +\ No newline at end of file diff --git a/app/i18n/build/id_map_hash b/app/i18n/build/id_map_hash @@ -0,0 +1 @@ +sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/app/i18n/build/manifest.json b/app/i18n/build/manifest.json @@ -0,0 +1 @@ +{"schema":1,"release_id":"dev","generated_at":"2026-02-02T00:00:00Z","default_locale":"en","supported_locales":["en"],"id_map_hash":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","mf2_packs":{"en":{"kind":"base","url":"packs/en.mf2pack","hash":"sha256:f8f3c3a662af2cb807c03b5f908735a19c5ee8f414e2cf0002cfcad7c25de6f0","size":136,"content_encoding":"identity","pack_schema":0}}} +\ No newline at end of file diff --git a/app/i18n/build/packs/en.mf2pack b/app/i18n/build/packs/en.mf2pack Binary files differ. diff --git a/app/i18n/id_salt.txt b/app/i18n/id_salt.txt @@ -0,0 +1 @@ +radroots-app-v1 diff --git a/app/i18n/locales/en/messages.mf2 b/app/i18n/locales/en/messages.mf2 @@ -0,0 +1 @@ +# radroots app messages diff --git a/app/i18n/mf2-i18n.toml b/app/i18n/mf2-i18n.toml @@ -0,0 +1,3 @@ +default_locale = "en" +source_dirs = ["locales"] +project_salt_path = "id_salt.txt" diff --git a/app/src/i18n.rs b/app/src/i18n.rs @@ -1,9 +1,12 @@ #![forbid(unsafe_code)] +use std::collections::BTreeMap; + use leptos::prelude::{use_context, LocalStorage, RwSignal, StoredValue, WithUntracked, WithValue}; use mf2_i18n_core::Args; -use mf2_i18n_embedded::EmbeddedRuntime; +use mf2_i18n_core::MessageId; +use mf2_i18n_embedded::{EmbeddedPack, EmbeddedRuntime}; use radroots_app_lib::get_locale; #[derive(Clone, Copy)] @@ -15,7 +18,7 @@ pub struct RadrootsAppI18nContext { pub fn app_i18n_init() -> RadrootsAppI18nContext { let locale = get_locale(&["en"]); let locale = RwSignal::new_local(locale); - let runtime = StoredValue::new(None::<EmbeddedRuntime>); + let runtime = StoredValue::new(load_embedded_runtime()); RadrootsAppI18nContext { locale, runtime } } @@ -40,6 +43,47 @@ pub fn translate(key: &str) -> String { }) } +fn load_embedded_runtime() -> Option<EmbeddedRuntime> { + let id_map = load_id_map()?; + let id_map_hash = load_id_map_hash()?; + let packs = [EmbeddedPack { + locale: "en", + bytes: load_pack_en(), + }]; + EmbeddedRuntime::new(id_map, id_map_hash, &packs, "en").ok() +} + +fn load_id_map() -> Option<BTreeMap<String, MessageId>> { + let raw = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/i18n/build/id_map.json")); + let parsed: BTreeMap<String, u32> = serde_json::from_slice(raw).ok()?; + let mut map = BTreeMap::new(); + for (key, id) in parsed { + map.insert(key, MessageId::new(id)); + } + Some(map) +} + +fn load_id_map_hash() -> Option<[u8; 32]> { + let raw = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/i18n/build/id_map_hash")); + let text = std::str::from_utf8(raw).ok()?; + let value = text.trim(); + let hex_value = value.strip_prefix("sha256:").unwrap_or(value); + let bytes = hex::decode(hex_value).ok()?; + if bytes.len() != 32 { + return None; + } + let mut out = [0u8; 32]; + out.copy_from_slice(&bytes); + Some(out) +} + +fn load_pack_en() -> &'static [u8] { + include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/i18n/build/packs/en.mf2pack" + )) +} + #[macro_export] macro_rules! t { ($key:literal) => {