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:
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) => {