commit d46b0c64370ec4e101c6a90c3a73e79e22e2baa7
parent 5081dd397512af7213a4a05f40ebab509d0b04c7
Author: triesap <tyson@radroots.org>
Date: Mon, 2 Feb 2026 15:20:41 +0000
app: localize shell and home status
- add home and nav translation keys with aria labels
- map init, health, reset, and notification statuses
- translate home dashboard headings and buttons
- regenerate mf2-i18n build artifacts
Diffstat:
7 files changed, 1023 insertions(+), 39 deletions(-)
diff --git a/app/i18n/build/i18n.catalog.json b/app/i18n/build/i18n.catalog.json
@@ -3,5 +3,710 @@
"project": "radroots-app",
"generated_at": "2026-02-02T00:00:00Z",
"default_locale": "en",
- "messages": []
+ "messages": [
+ {
+ "key": "app.common.missing",
+ "id": 3520194192,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.common.unknown",
+ "id": 1588621956,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.aria",
+ "id": 3172671582,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.button.checking",
+ "id": 3465392175,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.button.run",
+ "id": 1230595839,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.active_key",
+ "id": 3589833151,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.bootstrap_state",
+ "id": 737539659,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.datastore_roundtrip",
+ "id": 1765955433,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.key_maps",
+ "id": 1556244806,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.keystore",
+ "id": 4253927470,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.notifications",
+ "id": 3066505377,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.state_active_key",
+ "id": 1422267473,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.item.tangle",
+ "id": 4017335715,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.message.mismatch",
+ "id": 2731529562,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.message.missing",
+ "id": 2655522886,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.message.unavailable",
+ "id": 645848060,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.message.uninitialized",
+ "id": 3760340840,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.status.error",
+ "id": 1466113983,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.status.ok",
+ "id": 828208412,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.status.skipped",
+ "id": 3849381953,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.health.title",
+ "id": 1957618139,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.aria",
+ "id": 1715867969,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.button.request",
+ "id": 3959491343,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.button.requesting",
+ "id": 3188736353,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.status.default",
+ "id": 3760721877,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.status.denied",
+ "id": 4076317564,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.status.granted",
+ "id": 1582331756,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.status.unavailable",
+ "id": 3118891586,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.notifications.title",
+ "id": 907277970,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.aria",
+ "id": 3577090589,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.button",
+ "id": 3119733506,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.status.done",
+ "id": 3550097324,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.status.idle",
+ "id": 4013677279,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.status.missing_backends",
+ "id": 3398209588,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.reset.status.resetting",
+ "id": 3968037487,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.status.aria",
+ "id": 3526893198,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.home.title",
+ "id": 3182100107,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.database",
+ "id": 3687544718,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.download_geo",
+ "id": 3254824087,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.download_sql",
+ "id": 333951479,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.error",
+ "id": 388972410,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.geocoder",
+ "id": 192352243,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.idle",
+ "id": 1446931460,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.ready",
+ "id": 4200712024,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.init.stage.storage",
+ "id": 1242977634,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.home",
+ "id": 2360822558,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.logs",
+ "id": 952998791,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.primary_aria",
+ "id": 3495744422,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.settings",
+ "id": 326721352,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.setup",
+ "id": 2066290074,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.nav.ui",
+ "id": 2416341108,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "app.not_found",
+ "id": 3182331848,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.init.assets",
+ "id": 172100683,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.init.config",
+ "id": 3737196850,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.init.datastore",
+ "id": 1124445179,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.init.idb",
+ "id": 3385056441,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.init.keystore",
+ "id": 2043630525,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.state.already_exists",
+ "id": 4003177973,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.state.checksum_invalid",
+ "id": 56309183,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.state.corrupt",
+ "id": 2251393452,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.state.missing",
+ "id": 2404167425,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.app.state.schema_unsupported",
+ "id": 3991204251,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.client.notifications.read_failure",
+ "id": 350195904,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ },
+ {
+ "key": "error.client.notifications.unavailable",
+ "id": 4206009627,
+ "args": [],
+ "features": {
+ "select": false,
+ "plural_cardinal": false,
+ "plural_ordinal": false,
+ "formatters": []
+ }
+ }
+ ]
}
\ No newline at end of file
diff --git a/app/i18n/build/id_map.json b/app/i18n/build/id_map.json
@@ -1 +1,66 @@
-{}
-\ No newline at end of file
+{
+ "app.common.missing": 3520194192,
+ "app.common.unknown": 1588621956,
+ "app.home.health.aria": 3172671582,
+ "app.home.health.button.checking": 3465392175,
+ "app.home.health.button.run": 1230595839,
+ "app.home.health.item.active_key": 3589833151,
+ "app.home.health.item.bootstrap_state": 737539659,
+ "app.home.health.item.datastore_roundtrip": 1765955433,
+ "app.home.health.item.key_maps": 1556244806,
+ "app.home.health.item.keystore": 4253927470,
+ "app.home.health.item.notifications": 3066505377,
+ "app.home.health.item.state_active_key": 1422267473,
+ "app.home.health.item.tangle": 4017335715,
+ "app.home.health.message.mismatch": 2731529562,
+ "app.home.health.message.missing": 2655522886,
+ "app.home.health.message.unavailable": 645848060,
+ "app.home.health.message.uninitialized": 3760340840,
+ "app.home.health.status.error": 1466113983,
+ "app.home.health.status.ok": 828208412,
+ "app.home.health.status.skipped": 3849381953,
+ "app.home.health.title": 1957618139,
+ "app.home.notifications.aria": 1715867969,
+ "app.home.notifications.button.request": 3959491343,
+ "app.home.notifications.button.requesting": 3188736353,
+ "app.home.notifications.status.default": 3760721877,
+ "app.home.notifications.status.denied": 4076317564,
+ "app.home.notifications.status.granted": 1582331756,
+ "app.home.notifications.status.unavailable": 3118891586,
+ "app.home.notifications.title": 907277970,
+ "app.home.reset.aria": 3577090589,
+ "app.home.reset.button": 3119733506,
+ "app.home.reset.status.done": 3550097324,
+ "app.home.reset.status.idle": 4013677279,
+ "app.home.reset.status.missing_backends": 3398209588,
+ "app.home.reset.status.resetting": 3968037487,
+ "app.home.status.aria": 3526893198,
+ "app.home.title": 3182100107,
+ "app.init.stage.database": 3687544718,
+ "app.init.stage.download_geo": 3254824087,
+ "app.init.stage.download_sql": 333951479,
+ "app.init.stage.error": 388972410,
+ "app.init.stage.geocoder": 192352243,
+ "app.init.stage.idle": 1446931460,
+ "app.init.stage.ready": 4200712024,
+ "app.init.stage.storage": 1242977634,
+ "app.nav.home": 2360822558,
+ "app.nav.logs": 952998791,
+ "app.nav.primary_aria": 3495744422,
+ "app.nav.settings": 326721352,
+ "app.nav.setup": 2066290074,
+ "app.nav.ui": 2416341108,
+ "app.not_found": 3182331848,
+ "error.app.init.assets": 172100683,
+ "error.app.init.config": 3737196850,
+ "error.app.init.datastore": 1124445179,
+ "error.app.init.idb": 3385056441,
+ "error.app.init.keystore": 2043630525,
+ "error.app.state.already_exists": 4003177973,
+ "error.app.state.checksum_invalid": 56309183,
+ "error.app.state.corrupt": 2251393452,
+ "error.app.state.missing": 2404167425,
+ "error.app.state.schema_unsupported": 3991204251,
+ "error.client.notifications.read_failure": 350195904,
+ "error.client.notifications.unavailable": 4206009627
+}
+\ No newline at end of file
diff --git a/app/i18n/build/id_map_hash b/app/i18n/build/id_map_hash
@@ -1 +1 @@
-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+sha256:6f697fe6b69bf95389c0703cd551d61fecee1f5607ad76520d10390781027b82
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: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
+{"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
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
@@ -1 +1,133 @@
-# radroots app messages
+# common
+app.common.missing = missing
+
+app.common.unknown = unknown
+
+# nav
+app.nav.primary_aria = primary
+
+app.nav.home = home
+
+app.nav.logs = logs
+
+app.nav.ui = ui
+
+app.nav.settings = settings
+
+app.nav.setup = setup
+
+# not found
+app.not_found = not found
+
+# init stages
+app.init.stage.idle = idle
+
+app.init.stage.storage = storage
+
+app.init.stage.download_sql = download sql
+
+app.init.stage.download_geo = download geo
+
+app.init.stage.database = database
+
+app.init.stage.geocoder = geocoder
+
+app.init.stage.ready = ready
+
+app.init.stage.error = error
+
+# home
+app.home.title = app
+
+app.home.status.aria = status
+
+app.home.reset.aria = reset
+
+app.home.reset.button = reset
+
+app.home.reset.status.idle = idle
+
+app.home.reset.status.resetting = resetting
+
+app.home.reset.status.missing_backends = backends unavailable
+
+app.home.reset.status.done = reset complete
+
+app.home.notifications.aria = notifications
+
+app.home.notifications.title = notifications
+
+app.home.notifications.button.request = request
+
+app.home.notifications.button.requesting = requesting
+
+app.home.notifications.status.granted = granted
+
+app.home.notifications.status.denied = denied
+
+app.home.notifications.status.default = default
+
+app.home.notifications.status.unavailable = unavailable
+
+app.home.health.aria = health checks
+
+app.home.health.title = health checks
+
+app.home.health.button.checking = checking
+
+app.home.health.button.run = run checks
+
+app.home.health.item.key_maps = key maps
+
+app.home.health.item.bootstrap_state = bootstrap state
+
+app.home.health.item.state_active_key = active key state
+
+app.home.health.item.notifications = notifications
+
+app.home.health.item.tangle = tangle
+
+app.home.health.item.datastore_roundtrip = datastore roundtrip
+
+app.home.health.item.keystore = keystore
+
+app.home.health.item.active_key = active key
+
+app.home.health.status.ok = ok
+
+app.home.health.status.error = error
+
+app.home.health.status.skipped = skipped
+
+app.home.health.message.missing = missing
+
+app.home.health.message.mismatch = mismatch
+
+app.home.health.message.uninitialized = uninitialized
+
+app.home.health.message.unavailable = unavailable
+
+# errors
+error.app.init.idb = storage unavailable
+
+error.app.init.datastore = datastore unavailable
+
+error.app.init.keystore = keystore unavailable
+
+error.app.init.config = configuration error
+
+error.app.init.assets = assets unavailable
+
+error.app.state.missing = setup required
+
+error.app.state.corrupt = stored data is corrupt
+
+error.app.state.checksum_invalid = stored data checksum invalid
+
+error.app.state.schema_unsupported = stored data schema unsupported
+
+error.app.state.already_exists = setup already completed
+
+error.client.notifications.unavailable = notifications unavailable
+
+error.client.notifications.read_failure = notifications permission read failed
diff --git a/app/src/app.rs b/app/src/app.rs
@@ -20,6 +20,7 @@ use radroots_app_ui_components::{
RadrootsAppUiButtonLayoutPair,
};
+use crate::t;
use crate::{
app_init_assets,
app_init_backends,
@@ -78,10 +79,81 @@ fn health_status_color(status: RadrootsAppHealthCheckStatus) -> &'static str {
}
}
+fn init_stage_label(stage: RadrootsAppInitStage) -> String {
+ match stage {
+ RadrootsAppInitStage::Idle => t!("app.init.stage.idle"),
+ RadrootsAppInitStage::Storage => t!("app.init.stage.storage"),
+ RadrootsAppInitStage::DownloadSql => t!("app.init.stage.download_sql"),
+ RadrootsAppInitStage::DownloadGeo => t!("app.init.stage.download_geo"),
+ RadrootsAppInitStage::Database => t!("app.init.stage.database"),
+ RadrootsAppInitStage::Geocoder => t!("app.init.stage.geocoder"),
+ RadrootsAppInitStage::Ready => t!("app.init.stage.ready"),
+ RadrootsAppInitStage::Error => t!("app.init.stage.error"),
+ }
+}
+
+fn health_status_label(status: RadrootsAppHealthCheckStatus) -> String {
+ match status {
+ RadrootsAppHealthCheckStatus::Ok => t!("app.home.health.status.ok"),
+ RadrootsAppHealthCheckStatus::Error => t!("app.home.health.status.error"),
+ RadrootsAppHealthCheckStatus::Skipped => t!("app.home.health.status.skipped"),
+ }
+}
+
+fn health_message_label(message: &str) -> String {
+ match message {
+ "missing" => t!("app.home.health.message.missing"),
+ "mismatch" => t!("app.home.health.message.mismatch"),
+ "uninitialized" => t!("app.home.health.message.uninitialized"),
+ "unavailable" => t!("app.home.health.message.unavailable"),
+ _ => message.to_string(),
+ }
+}
+
fn health_result_label(result: &RadrootsAppHealthCheckResult) -> String {
+ let status = health_status_label(result.status);
match result.message.as_deref() {
- Some(message) => format!("{}: {}", result.status.as_str(), message),
- None => result.status.as_str().to_string(),
+ Some(message) => format!("{}: {}", status, health_message_label(message)),
+ None => status,
+ }
+}
+
+fn error_label(key: &str) -> Option<String> {
+ let label = match key {
+ "error.app.init.idb" => t!("error.app.init.idb"),
+ "error.app.init.datastore" => t!("error.app.init.datastore"),
+ "error.app.init.keystore" => t!("error.app.init.keystore"),
+ "error.app.init.config" => t!("error.app.init.config"),
+ "error.app.init.assets" => t!("error.app.init.assets"),
+ "error.app.state.missing" => t!("error.app.state.missing"),
+ "error.app.state.corrupt" => t!("error.app.state.corrupt"),
+ "error.app.state.checksum_invalid" => t!("error.app.state.checksum_invalid"),
+ "error.app.state.schema_unsupported" => t!("error.app.state.schema_unsupported"),
+ "error.app.state.already_exists" => t!("error.app.state.already_exists"),
+ "error.client.notifications.unavailable" => t!("error.client.notifications.unavailable"),
+ "error.client.notifications.read_failure" => t!("error.client.notifications.read_failure"),
+ _ => return None,
+ };
+ Some(label)
+}
+
+fn reset_status_label(value: &str) -> String {
+ match value {
+ "reset_idle" => t!("app.home.reset.status.idle"),
+ "resetting" => t!("app.home.reset.status.resetting"),
+ "reset_missing_backends" => t!("app.home.reset.status.missing_backends"),
+ "reset_done" => t!("app.home.reset.status.done"),
+ _ => error_label(value).unwrap_or_else(|| value.to_string()),
+ }
+}
+
+fn notifications_status_label(value: &str) -> String {
+ match value {
+ "granted" => t!("app.home.notifications.status.granted"),
+ "denied" => t!("app.home.notifications.status.denied"),
+ "default" => t!("app.home.notifications.status.default"),
+ "unavailable" => t!("app.home.notifications.status.unavailable"),
+ _ => error_label(value).unwrap_or_else(|| value.to_string()),
}
}
@@ -105,7 +177,7 @@ enum RadrootsAppSetupFarmerChoice {
fn active_key_label(value: Option<String>) -> String {
let Some(value) = value else {
- return "missing".to_string();
+ return t!("app.common.missing");
};
if value.len() <= 12 {
return value;
@@ -1145,7 +1217,9 @@ fn HomePage() -> impl IntoView {
let reset_label = move || {
reset_status
.get()
- .unwrap_or_else(|| "reset_idle".to_string())
+ .as_deref()
+ .map(reset_status_label)
+ .unwrap_or_else(|| t!("app.home.reset.status.idle"))
};
let health_disabled = move || {
backends.with(|value| value.is_none())
@@ -1158,21 +1232,23 @@ fn HomePage() -> impl IntoView {
let notifications_label = move || {
notifications_status
.get()
- .unwrap_or_else(|| "unknown".to_string())
+ .as_deref()
+ .map(notifications_status_label)
+ .unwrap_or_else(|| t!("app.common.unknown"))
};
let notifications_button_label = move || {
if notifications_requesting.get() {
- "requesting"
+ t!("app.home.notifications.button.requesting")
} else {
- "request"
+ t!("app.home.notifications.button.request")
}
};
view! {
<main id="app-home" class="app-page app-page-scroll">
<header id="app-home-header">
- <h1 id="app-home-title">"app"</h1>
+ <h1 id="app-home-title">{t!("app.home.title")}</h1>
</header>
- <section id="app-home-status" aria-label="Status">
+ <section id="app-home-status" aria-label=t!("app.home.status.aria")>
<div id="app-home-status-row" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
<span
style=move || format!(
@@ -1180,10 +1256,10 @@ fn HomePage() -> impl IntoView {
status_color()
)
></span>
- <span>{move || init_state.get().stage.as_str()}</span>
+ <span>{move || init_stage_label(init_state.get().stage)}</span>
</div>
</section>
- <section id="app-home-reset" aria-label="Reset">
+ <section id="app-home-reset" aria-label=t!("app.home.reset.aria")>
<div id="app-home-reset-row" style="margin-top: 12px; display: flex; align-items: center; gap: 8px;">
<button
on:click=move |_| {
@@ -1244,14 +1320,14 @@ fn HomePage() -> impl IntoView {
}
disabled=reset_disabled
>
- "reset"
+ {t!("app.home.reset.button")}
</button>
<span>{reset_label}</span>
</div>
</section>
- <section id="app-home-notifications" aria-label="Notifications" style="margin-top: 16px;">
+ <section id="app-home-notifications" aria-label=t!("app.home.notifications.aria") style="margin-top: 16px;">
<header id="app-home-notifications-header">
- <h2 id="app-home-notifications-title" style="font-weight: 600;">"notifications"</h2>
+ <h2 id="app-home-notifications-title" style="font-weight: 600;">{t!("app.home.notifications.title")}</h2>
</header>
<div id="app-home-notifications-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
<button
@@ -1307,9 +1383,9 @@ fn HomePage() -> impl IntoView {
<span>{notifications_label}</span>
</div>
</section>
- <section id="app-home-health" aria-label="Health checks" style="margin-top: 16px;">
+ <section id="app-home-health" aria-label=t!("app.home.health.aria") style="margin-top: 16px;">
<header id="app-home-health-header">
- <h2 id="app-home-health-title" style="font-weight: 600;">"health checks"</h2>
+ <h2 id="app-home-health-title" style="font-weight: 600;">{t!("app.home.health.title")}</h2>
</header>
<div id="app-home-health-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;">
<button
@@ -1332,7 +1408,13 @@ fn HomePage() -> impl IntoView {
}
disabled=health_disabled
>
- {move || if health_running.get() { "checking" } else { "run checks" }}
+ {move || {
+ if health_running.get() {
+ t!("app.home.health.button.checking")
+ } else {
+ t!("app.home.health.button.run")
+ }
+ }}
</button>
</div>
<ul id="app-home-health-list" style="margin-top: 8px; display: grid; gap: 6px;">
@@ -1343,7 +1425,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().key_maps.status)
)
></span>
- <span>"key_maps"</span>
+ <span>{t!("app.home.health.item.key_maps")}</span>
<span>{move || health_result_label(&health_report.get().key_maps)}</span>
</li>
<li id="app-home-health-bootstrap-state" style="display: flex; align-items: center; gap: 8px;">
@@ -1353,7 +1435,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().bootstrap_state.status)
)
></span>
- <span>"bootstrap_state"</span>
+ <span>{t!("app.home.health.item.bootstrap_state")}</span>
<span>{move || health_result_label(&health_report.get().bootstrap_state)}</span>
</li>
<li id="app-home-health-active-key-state" style="display: flex; align-items: center; gap: 8px;">
@@ -1363,7 +1445,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().state_active_key.status)
)
></span>
- <span>"state_active_key"</span>
+ <span>{t!("app.home.health.item.state_active_key")}</span>
<span>{move || health_result_label(&health_report.get().state_active_key)}</span>
</li>
<li id="app-home-health-notifications" style="display: flex; align-items: center; gap: 8px;">
@@ -1373,7 +1455,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().notifications.status)
)
></span>
- <span>"notifications"</span>
+ <span>{t!("app.home.health.item.notifications")}</span>
<span>{move || health_result_label(&health_report.get().notifications)}</span>
</li>
<li id="app-home-health-tangle" style="display: flex; align-items: center; gap: 8px;">
@@ -1383,7 +1465,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().tangle.status)
)
></span>
- <span>"tangle"</span>
+ <span>{t!("app.home.health.item.tangle")}</span>
<span>{move || health_result_label(&health_report.get().tangle)}</span>
</li>
<li id="app-home-health-datastore" style="display: flex; align-items: center; gap: 8px;">
@@ -1393,7 +1475,7 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().datastore_roundtrip.status)
)
></span>
- <span>"datastore_roundtrip"</span>
+ <span>{t!("app.home.health.item.datastore_roundtrip")}</span>
<span>{move || health_result_label(&health_report.get().datastore_roundtrip)}</span>
</li>
<li id="app-home-health-keystore" style="display: flex; align-items: center; gap: 8px;">
@@ -1403,11 +1485,11 @@ fn HomePage() -> impl IntoView {
health_status_color(health_report.get().keystore.status)
)
></span>
- <span>"keystore"</span>
+ <span>{t!("app.home.health.item.keystore")}</span>
<span>{move || health_result_label(&health_report.get().keystore)}</span>
</li>
<li id="app-home-health-active-key" style="display: flex; align-items: center; gap: 8px;">
- <span>"active_key"</span>
+ <span>{t!("app.home.health.item.active_key")}</span>
<span>{move || active_key_label(active_key.get())}</span>
</li>
</ul>
@@ -1546,17 +1628,17 @@ fn AppShell() -> impl IntoView {
fallback=|| view! { <SetupPage /> }
>
<div id="app-shell">
- <nav id="app-nav" aria-label="Primary" style="display:flex;gap:12px;margin-bottom:12px;">
- <A href="/" exact=true>"home"</A>
- <A href="/logs">"logs"</A>
- <A href="/ui">"ui"</A>
- <A href="/settings">"settings"</A>
- <A href="/setup">"setup"</A>
+ <nav id="app-nav" aria-label=t!("app.nav.primary_aria") style="display:flex;gap:12px;margin-bottom:12px;">
+ <A href="/" exact=true>{t!("app.nav.home")}</A>
+ <A href="/logs">{t!("app.nav.logs")}</A>
+ <A href="/ui">{t!("app.nav.ui")}</A>
+ <A href="/settings">{t!("app.nav.settings")}</A>
+ <A href="/setup">{t!("app.nav.setup")}</A>
</nav>
<Routes
fallback=|| view! {
<main id="app-not-found" class="app-page app-page-fixed">
- <p id="app-not-found-label">"not_found"</p>
+ <p id="app-not-found-label">{t!("app.not_found")}</p>
</main>
}
>