From c2525fd0f04bc88a02b6e5d6ddf3bb955e2aa3cb Mon Sep 17 00:00:00 2001 From: randogoth Date: Thu, 8 Oct 2026 22:32:52 +0300 Subject: [PATCH] refactor: flatten Fluent selectors into one message per variant --- locales/en-US/main.ftl | 66 +++++++++++++++++++----------------------- src/ops.rs | 12 ++++---- src/view.rs | 18 ++++++++---- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index 764ee53..da95583 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -33,34 +33,32 @@ mail-message-not-found = Message not found. ## Fetch — the status note after a fetch completes ## -## `$variant` picks the one combination of "N rejected" and "(cancelled)" -## that applies, so each case reads as one complete sentence rather than -## stitched-together fragments. +## Split from one selector into one message per combination of "N +## rejected" and "(cancelled)" that can apply; the caller in ops.rs +## picks the id to match, since a flat message can't branch on a +## variable itself. -fetch-result-note = { $variant -> - [rejected] { $total } fetched, { $stored } stored, { $rejected } rejected - [cancelled] { $total } fetched, { $stored } stored (cancelled) - [rejected-cancelled] { $total } fetched, { $stored } stored, { $rejected } rejected (cancelled) - *[plain] { $total } fetched, { $stored } stored -} +fetch-result-note-rejected = { $total } fetched, { $stored } stored, { $rejected } rejected +fetch-result-note-cancelled = { $total } fetched, { $stored } stored (cancelled) +fetch-result-note-rejected-cancelled = { $total } fetched, { $stored } stored, { $rejected } rejected (cancelled) +fetch-result-note-plain = { $total } fetched, { $stored } stored ## Send — the status note after a send completes ## -## `$variant` picks the one combination of trust-change clause and -## accept-token clause that applies, each spelled out as a full sentence. -## Any warning fumi itself reports is appended separately in code, since -## that text is fumi's own and not part of this catalogue. +## Split from one selector into one message per combination of +## trust-change clause and accept-token clause that can apply; the +## caller in ops.rs picks the id to match. Any warning fumi itself +## reports is appended separately in code, since that text is fumi's +## own and not part of this catalogue. -send-result-note = { $variant -> - [token] Sent to { $address } ({ $bytes } bytes), accept token attached - [unverified] Sent to { $address } ({ $bytes } bytes); new contact learned from an unpinned server (unverified) - [unverified-token] Sent to { $address } ({ $bytes } bytes), accept token attached; new contact learned from an unpinned server (unverified) - [tofu] Sent to { $address } ({ $bytes } bytes); new contact learned (trust on first use) - [tofu-token] Sent to { $address } ({ $bytes } bytes), accept token attached; new contact learned (trust on first use) - [rotated] Sent to { $address } ({ $bytes } bytes); contact rotated to a new key (chain verified) - [rotated-token] Sent to { $address } ({ $bytes } bytes), accept token attached; contact rotated to a new key (chain verified) - *[plain] Sent to { $address } ({ $bytes } bytes) -} +send-result-note-token = Sent to { $address } ({ $bytes } bytes), accept token attached +send-result-note-unverified = Sent to { $address } ({ $bytes } bytes); new contact learned from an unpinned server (unverified) +send-result-note-unverified-token = Sent to { $address } ({ $bytes } bytes), accept token attached; new contact learned from an unpinned server (unverified) +send-result-note-tofu = Sent to { $address } ({ $bytes } bytes); new contact learned (trust on first use) +send-result-note-tofu-token = Sent to { $address } ({ $bytes } bytes), accept token attached; new contact learned (trust on first use) +send-result-note-rotated = Sent to { $address } ({ $bytes } bytes); contact rotated to a new key (chain verified) +send-result-note-rotated-token = Sent to { $address } ({ $bytes } bytes), accept token attached; contact rotated to a new key (chain verified) +send-result-note-plain = Sent to { $address } ({ $bytes } bytes) ## Background-operation status notes ## @@ -143,15 +141,13 @@ contacts-import-button = Import ## Mail — the open-message meta line ## -## `$kept` picks the one variant that applies, each a full sentence, -## rather than an optional clause spliced onto a shared prefix. +## Split from one selector into one message per variant, each a full +## sentence; the caller in view.rs picks `-yes`/`-no` to match `$kept`. mail-meta-to = to mail-meta-from = from -mail-meta-line = { $kept -> - *[no] { $label } { $from } · { $time } UTC · id { $id }… - [yes] { $label } { $from } · { $time } UTC · id { $id }… · a server copy still exists -} +mail-meta-line-no = { $label } { $from } · { $time } UTC · id { $id }… +mail-meta-line-yes = { $label } { $from } · { $time } UTC · id { $id }… · a server copy still exists mail-no-subject = (no subject) mail-no-reply-address = no reply address: can't accept or block contacts-empty-hint = no contacts @@ -173,8 +169,7 @@ contacts-pin-button-unfetched = Pin server mail-empty-title = No message selected mail-empty-description = Pick a message from the list above to read it. # The two sentences shown under Fetch when the inbox is empty. -mail-empty-hint = Fetching removes mail from the server once it's stored here. - Turn on "Leave mail on server" in Settings to keep it there instead. +mail-empty-hint = Fetching removes mail from the server once it's stored here. Turn on "Leave mail on server" in Settings to keep it there instead. detail-qr-tooltip = Show the address as a QR code detail-copy-tooltip = Copy the address @@ -194,11 +189,10 @@ contact-state-blocked = blocked contact-state-verified = verified contact-state-tofu = trust on first use # `$state` is one of the four words above, already resolved by the caller -# through `contact_state`; this message only adds the server's pin state. -contact-meta-line = { $state } · server { $pinned -> - [yes] pinned - *[no] not pinned -} +# through `contact_state`. Split from one selector into one message per +# pin state; the caller in view.rs picks `-yes`/`-no` to match `$pinned`. +contact-meta-line-yes = { $state } · server pinned +contact-meta-line-no = { $state } · server not pinned ## Detail pane — the monospace key/value block shared by contacts and the ## own identity view. Column widths are padded to these labels in code diff --git a/src/ops.rs b/src/ops.rs index 5226335..6be3797 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -509,11 +509,13 @@ pub fn fetch( (false, false) => "plain", }; let mut args = fluent_bundle::FluentArgs::new(); - args.set("variant", variant); args.set("total", fetched.total as i64); args.set("stored", fetched.stored as i64); args.set("rejected", fetched.rejected.len() as i64); - Ok(i18n::t_args("fetch-result-note", Some(&args))) + Ok(i18n::t_args( + &format!("fetch-result-note-{variant}"), + Some(&args), + )) } /// Fields fumi itself attaches to every message (sec 5.7, 5.8); a @@ -634,10 +636,9 @@ pub fn send( (None, false) => "plain".to_string(), }; let mut args = fluent_bundle::FluentArgs::new(); - args.set("variant", variant); args.set("address", addr.short()); args.set("bytes", sent.bytes as i64); - let mut note = i18n::t_args("send-result-note", Some(&args)); + let mut note = i18n::t_args(&format!("send-result-note-{variant}"), Some(&args)); if let Some(warning) = sent.warning { // Fumi's own warning text, appended as a second, independent // clause rather than folded into the catalogue — it isn't ours @@ -1112,12 +1113,11 @@ mod tests { #[test] fn interpolated_messages_carry_no_bidi_isolation_marks() { let mut args = fluent_bundle::FluentArgs::new(); - args.set("variant", "plain"); args.set("total", 5i64); args.set("stored", 5i64); args.set("rejected", 0i64); assert_eq!( - i18n::t_args("fetch-result-note", Some(&args)), + i18n::t_args("fetch-result-note-plain", Some(&args)), "5 fetched, 5 stored" ); } diff --git a/src/view.rs b/src/view.rs index 3bddc89..b14ba34 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1810,11 +1810,13 @@ fn render(model: &Carrier, w: &mut CarrierWidgets, sender: &ComponentSender = msg .headers