app

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

commit 2724e4532abd1abb9b3be0fb641067dcd3b51a0f
parent d46b0c64370ec4e101c6a90c3a73e79e22e2baa7
Author: triesap <tyson@radroots.org>
Date:   Mon,  2 Feb 2026 15:36:21 +0000

app: localize setup flow

- add setup intro, key choice, and profile messages
- translate farmer choice labels and setup prompts
- localize setup navigation and confirm copy
- regenerate mf2-i18n build artifacts

Diffstat:
Mapp/i18n/build/i18n.catalog.json | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/i18n/build/id_map.json | 18++++++++++++++++++
Mapp/i18n/build/id_map_hash | 2+-
Mapp/i18n/build/manifest.json | 4++--
Mapp/i18n/build/packs/en.mf2pack | 0
Mapp/i18n/locales/en/messages.mf2 | 37+++++++++++++++++++++++++++++++++++++
Mapp/src/app.rs | 48+++++++++++++++++++++++-------------------------
7 files changed, 279 insertions(+), 28 deletions(-)

diff --git a/app/i18n/build/i18n.catalog.json b/app/i18n/build/i18n.catalog.json @@ -5,6 +5,28 @@ "default_locale": "en", "messages": [ { + "key": "app.common.back", + "id": 2990764910, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.common.continue", + "id": 385087683, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { "key": "app.common.missing", "id": 3520194192, "args": [], @@ -16,6 +38,17 @@ } }, { + "key": "app.common.no", + "id": 1027581146, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { "key": "app.common.unknown", "id": 1588621956, "args": [], @@ -27,6 +60,17 @@ } }, { + "key": "app.common.yes", + "id": 1057379348, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { "key": "app.home.health.aria", "id": 3172671582, "args": [], @@ -577,6 +621,160 @@ } }, { + "key": "app.setup.farmer.title", + "id": 3967778278, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.intro.body", + "id": 3075641340, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.intro.kicker", + "id": 397915792, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.intro.welcome", + "id": 909038201, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.key_add.placeholder", + "id": 728766925, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.key_add.title", + "id": 3229091810, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.key_choice.create", + "id": 963716841, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.key_choice.title", + "id": 2829853649, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.key_choice.use_existing", + "id": 3945623834, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.profile.confirm_no_name", + "id": 420411302, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.profile.nip05.prefix", + "id": 3344734641, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.profile.nip05.suffix", + "id": 853057844, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.profile.placeholder", + "id": 321152177, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { + "key": "app.setup.profile.title", + "id": 103377718, + "args": [], + "features": { + "select": false, + "plural_cardinal": false, + "plural_ordinal": false, + "formatters": [] + } + }, + { "key": "error.app.init.assets", "id": 172100683, "args": [], diff --git a/app/i18n/build/id_map.json b/app/i18n/build/id_map.json @@ -1,6 +1,10 @@ { + "app.common.back": 2990764910, + "app.common.continue": 385087683, "app.common.missing": 3520194192, + "app.common.no": 1027581146, "app.common.unknown": 1588621956, + "app.common.yes": 1057379348, "app.home.health.aria": 3172671582, "app.home.health.button.checking": 3465392175, "app.home.health.button.run": 1230595839, @@ -51,6 +55,20 @@ "app.nav.setup": 2066290074, "app.nav.ui": 2416341108, "app.not_found": 3182331848, + "app.setup.farmer.title": 3967778278, + "app.setup.intro.body": 3075641340, + "app.setup.intro.kicker": 397915792, + "app.setup.intro.welcome": 909038201, + "app.setup.key_add.placeholder": 728766925, + "app.setup.key_add.title": 3229091810, + "app.setup.key_choice.create": 963716841, + "app.setup.key_choice.title": 2829853649, + "app.setup.key_choice.use_existing": 3945623834, + "app.setup.profile.confirm_no_name": 420411302, + "app.setup.profile.nip05.prefix": 3344734641, + "app.setup.profile.nip05.suffix": 853057844, + "app.setup.profile.placeholder": 321152177, + "app.setup.profile.title": 103377718, "error.app.init.assets": 172100683, "error.app.init.config": 3737196850, "error.app.init.datastore": 1124445179, diff --git a/app/i18n/build/id_map_hash b/app/i18n/build/id_map_hash @@ -1 +1 @@ -sha256:6f697fe6b69bf95389c0703cd551d61fecee1f5607ad76520d10390781027b82 +sha256:bbf87cb3cd423c088f4002810c7cc87b8a2d251e6b76d4e71cdf02b5e4d84b08 diff --git a/app/i18n/build/manifest.json b/app/i18n/build/manifest.json @@ -1 +1 @@ -{"schema":1,"release_id":"dev","generated_at":"2026-02-02T00:00:00Z","default_locale":"en","supported_locales":["en"],"id_map_hash":"sha256:6f697fe6b69bf95389c0703cd551d61fecee1f5607ad76520d10390781027b82","mf2_packs":{"en":{"kind":"base","url":"packs/en.mf2pack","hash":"sha256:8c7a60157aafe3fc9a5acb3725cf5a748eea3a252839eb1e4aa87ddb8ad86f31","size":3182,"content_encoding":"identity","pack_schema":0}}} -\ No newline at end of file +{"schema":1,"release_id":"dev","generated_at":"2026-02-02T00:00:00Z","default_locale":"en","supported_locales":["en"],"id_map_hash":"sha256:bbf87cb3cd423c088f4002810c7cc87b8a2d251e6b76d4e71cdf02b5e4d84b08","mf2_packs":{"en":{"kind":"base","url":"packs/en.mf2pack","hash":"sha256:91a973fb93cd33c3122aefe92ebf98976e720a07eced81f074ad2b80e2516c22","size":4210,"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/locales/en/messages.mf2 b/app/i18n/locales/en/messages.mf2 @@ -3,6 +3,14 @@ app.common.missing = missing app.common.unknown = unknown +app.common.back = Back + +app.common.continue = Continue + +app.common.no = No + +app.common.yes = Yes + # nav app.nav.primary_aria = primary @@ -107,6 +115,35 @@ app.home.health.message.uninitialized = uninitialized app.home.health.message.unavailable = unavailable +# setup +app.setup.intro.kicker = Configure + +app.setup.intro.welcome = Welcome to Radroots! + +app.setup.intro.body = Your device will be configured by the setup wizard. + +app.setup.key_choice.title = Configure Device + +app.setup.key_choice.create = Create new keypair + +app.setup.key_choice.use_existing = Use existing keypair + +app.setup.key_add.title = Add existing key + +app.setup.key_add.placeholder = Enter nostr nsec/hex + +app.setup.profile.title = Add Profile + +app.setup.profile.placeholder = Enter profile name + +app.setup.profile.nip05.prefix = Create + +app.setup.profile.nip05.suffix = NIP-05 address + +app.setup.profile.confirm_no_name = Your profile will be created without a name. You can change this later in Settings > Profile + +app.setup.farmer.title = Setup for Farmer + # errors error.app.init.idb = storage unavailable diff --git a/app/src/app.rs b/app/src/app.rs @@ -253,9 +253,6 @@ fn spawn_health_checks( } const APP_HEALTH_CHECK_DELAY_MS: u32 = 300; -const APP_SETUP_NO_PROFILE_NAME_CONFIRM: &str = - "Your profile will be created without a name. You can change this later in Settings > Profile"; - fn app_health_check_delay_ms() -> u32 { APP_HEALTH_CHECK_DELAY_MS } @@ -518,11 +515,10 @@ fn SetupPage() -> impl IntoView { let profile_name = profile_name.get(); if profile_name.trim().is_empty() { let setup_step = setup_step.clone(); + let confirm_message = t!("app.setup.profile.confirm_no_name"); spawn_local(async move { let notifications = RadrootsAppNotifications::new(None); - let confirm = notifications - .confirm_message(APP_SETUP_NO_PROFILE_NAME_CONFIRM) - .await; + let confirm = notifications.confirm_message(&confirm_message).await; if confirm { setup_step.set(RadrootsAppSetupStep::FarmerSetup); } @@ -637,7 +633,7 @@ fn SetupPage() -> impl IntoView { id="app-setup-intro-kicker" class="w-full text-left font-sans font-[400] text-sm uppercase text-ly0-gl-label" > - "Configure" + {t!("app.setup.intro.kicker")} </p> <div id="app-setup-intro-copy" @@ -647,13 +643,13 @@ fn SetupPage() -> impl IntoView { id="app-setup-intro-line-welcome" class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl" > - "Welcome to Radroots!" + {t!("app.setup.intro.welcome")} </p> <p id="app-setup-intro-line-body" class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl" > - "Your device will be configured by the setup wizard." + {t!("app.setup.intro.body")} </p> </div> </footer> @@ -680,7 +676,7 @@ fn SetupPage() -> impl IntoView { class="flex flex-row w-full justify-center items-center" > <p class="font-sans font-[600] text-ly0-gl text-3xl"> - "Configure Device" + {t!("app.setup.key_choice.title")} </p> </div> <div @@ -706,7 +702,7 @@ fn SetupPage() -> impl IntoView { } > <span class="font-sans font-[600] text-ly0-gl text-xl"> - "Create new keypair" + {t!("app.setup.key_choice.create")} </span> </button> <button @@ -728,7 +724,7 @@ fn SetupPage() -> impl IntoView { } > <span class="font-sans font-[600] text-ly0-gl text-xl"> - "Use existing keypair" + {t!("app.setup.key_choice.use_existing")} </span> </button> </div> @@ -752,13 +748,13 @@ fn SetupPage() -> impl IntoView { id="app-setup-key-add-existing-title" class="font-sans font-[600] text-ly0-gl text-3xl capitalize" > - "Add existing key" + {t!("app.setup.key_add.title")} </p> <input id="app-setup-key-add-existing-input" class="input-base w-lo_ios0 ios1:w-lo_ios1 text-[1.25rem] text-center placeholder:opacity-60" type="text" - placeholder="Enter nostr nsec/hex" + placeholder=t!("app.setup.key_add.placeholder") prop:value=move || nostr_key_add.get() on:input=move |ev| { nostr_key_add.set(event_target_value(&ev)); @@ -785,7 +781,7 @@ fn SetupPage() -> impl IntoView { id="app-setup-profile-title" class="font-sans font-[600] text-ly0-gl text-3xl" > - "Add Profile" + {t!("app.setup.profile.title")} </p> <div id="app-setup-profile-fields" @@ -795,7 +791,7 @@ fn SetupPage() -> impl IntoView { id="app-setup-profile-name" class="input-base w-lo_ios0 ios1:w-lo_ios1 text-[1.25rem] text-center placeholder:opacity-60" type="text" - placeholder="Enter profile name" + placeholder=t!("app.setup.profile.placeholder") prop:value=move || profile_name.get() on:keydown=move |ev: KeyboardEvent| { if ev.key() == "Enter" { @@ -824,11 +820,13 @@ fn SetupPage() -> impl IntoView { class="flex flex-row justify-center items-center" > <span class="font-sans font-[400] text-ly0-gl text-[14px] tracking-wide"> - "Create " + {t!("app.setup.profile.nip05.prefix")} + {" "} <span class="font-mono font-[500] tracking-tight px-[3px]"> "@radroots" </span> - " NIP-05 address" + {" "} + {t!("app.setup.profile.nip05.suffix")} </span> </label> </div> @@ -858,7 +856,7 @@ fn SetupPage() -> impl IntoView { class="flex flex-row w-full justify-center items-center" > <p class="font-sans font-[600] text-ly0-gl text-3xl"> - "Setup for Farmer" + {t!("app.setup.farmer.title")} </p> </div> <div @@ -883,7 +881,7 @@ fn SetupPage() -> impl IntoView { } > <span class="font-sans font-[600] text-ly0-gl text-xl"> - "Yes" + {t!("app.common.yes")} </span> </button> <button @@ -904,7 +902,7 @@ fn SetupPage() -> impl IntoView { } > <span class="font-sans font-[600] text-ly0-gl text-xl"> - "No" + {t!("app.common.no")} </span> </button> </div> @@ -1118,17 +1116,17 @@ fn SetupPage() -> impl IntoView { && setup_key_choice.get().is_none()) || (matches!(step, RadrootsAppSetupStep::FarmerSetup) && setup_farmer_choice.get().is_none()); - let continue_label = "Continue"; - let back_label = "Back"; + let continue_label = t!("app.common.continue"); + let back_label = t!("app.common.back"); let continue_action = RadrootsAppUiButtonLayoutAction { - label: continue_label.to_string(), + label: continue_label, disabled: continue_disabled, loading: false, on_click: advance_step_click.clone(), }; let back_action = RadrootsAppUiButtonLayoutBackAction { visible: !matches!(step, RadrootsAppSetupStep::Intro), - label: Some(back_label.to_string()), + label: Some(back_label), disabled: false, on_click: rewind_step.clone(), };