Commit Graph

4447 Commits

Author SHA1 Message Date
Alex Gleason 39f288d819 wallet: turn 'Back up wallet' into an in-page dialog
The Back up wallet menu item on /wallet now opens the existing
WalletBackupMnemonicDialog inline instead of navigating to a dedicated
page. The dialog already existed in src/components/WalletBackupMnemonic.tsx
but wasn't wired up anywhere — this finally uses it.

- WalletPage: import WalletBackupMnemonicDialog, hold an open/closed state,
  flip the menu item from <Link to="/wallet/backup"> to a regular
  DropdownMenuItem that calls setBackupOpen(true) onSelect. Render the
  dialog alongside the existing Send / Receive / SP-scan dialogs. The
  dialog's internal gating still hides for extension/bunker signers, but
  the menu only renders in the 'available' branch of WalletPage so that
  fallback isn't user-visible in practice.
- AppRouter: drop the lazy import and route for WalletBackupPage, replace
  /wallet/backup with a redirect to /wallet so old bookmarks land somewhere
  sensible. Point /wallet/settings/backup at /wallet for the same reason.
- Delete WalletBackupPage.tsx \u2014 no longer reachable.
- Locales: remove the now-unused walletBackupPage block (six keys:
  seoTitle, seoDescription, title, subtitle, loggedOut, unsupported) from
  en + 14 other locales. The dialog reuses walletBackup.* and
  walletSettings.backup.label, which are unchanged.
2026-05-24 17:47:39 -05:00
Alex Gleason 96cf2bcbe0 wallet: collapse settings hub into an overflow menu on /wallet
The cog in the top-right of /wallet now opens a 3-dots (MoreVertical)
dropdown with two items — Back up wallet and Legacy wallet recovery —
each linking straight to the existing /wallet/backup and /wallet/legacy
pages. /wallet/settings (the intermediate Apple-style settings hub) is
gone; the route now redirects to /wallet for any old bookmarks.

- WalletPage: swap the Settings <Link> for a DropdownMenu with two
  Link-backed items. Reuse the existing walletSettings.{backup,legacy}.label
  strings as menu labels.
- AppRouter: drop the lazy import + route for BitcoinWalletSettingsPage,
  replace /wallet/settings with a redirect to /wallet, keep the existing
  /wallet/settings/{backup,legacy} redirects.
- WalletBackupPage / LegacyWalletRecoveryPage: backTo now points at
  /wallet directly instead of the removed hub.
- Delete BitcoinWalletSettingsPage.tsx — no longer reachable.
- Locales: rename wallet.openSettings -> wallet.openMenu and prune the
  walletSettings hub strings (seoTitle, seoDescription, title, subtitle,
  per-row descriptions) across en + 14 other locales, keeping only the
  two label values that the menu items now read.
2026-05-24 17:41:42 -05:00
Alex Gleason 2742e3d0da router: swap the lazy-route Suspense fallback for a centered spinner
The Suspense fallback for code-split routes was a max-w-6xl skeleton
shaped like a generic landing-style page (header bar + two text rows +
a 288px hero block). It ended up wrong-shaped for most routes — narrow
settings pages, the max-w-sm wallet screen, etc. — and on a fast chunk
load it would flash a wide block of placeholder geometry that bore no
resemblance to the page that actually rendered a moment later.

Replace it with a neutral centered Loader2 spinner. A spinner reads as
"loading" without committing to any particular page shape, so it works
equally well as the fallback for every lazy route in the app.

Also revert the balance skeleton on /wallet — that one was tuned to the
final balance shape (h-10 w-40 + h-4 w-24) and reads fine; it was the
route-level fallback that needed to change.

Regression-of: 9c16b300
2026-05-24 17:30:01 -05:00
Alex Gleason 9c16b300ea wallet: tighten layout widths, flatten settings sub-routes, swap balance skeleton for a spinner
The Wallet Settings hub stretched its header across the full layout width
while the menu list was capped at max-w-md, so the title floated centered
above a much narrower card. Constrain the page (header included) to the
same max-w-md container so the back arrow, title, and list line up. Apply
the same fix to /wallet/backup and /wallet/legacy.

Also tighten /wallet itself: the settings cog used to sit in a full-width
row while the balance + send/receive controls were max-w-sm, leaving the
cog floating off to the right. Pull the cog into the same max-w-sm
container so it sits flush with the rest of the UI.

The balance-loading state used two stacked skeletons (h-10 w-40, h-4 w-24)
that didn't match the final shape of the rendered balance — replace them
with a centered RefreshCw spinner. Drop the now-unused Skeleton import.

Flatten the sub-routes from /wallet/settings/backup -> /wallet/backup and
/wallet/settings/legacy -> /wallet/legacy. The deeper paths were redundant
since these are leaf pages reached only via the settings hub. Add Navigate
redirects from the old paths so any existing links / muscle memory still
resolve.
2026-05-24 17:26:32 -05:00
Alex Gleason 72a297935c wallet: move backup and legacy recovery into a /wallet/settings hub
The wallet home (/wallet) now ships an Apple-style cog in the top-right
that leads to a new /wallet/settings hub with two rows:

  - Back up wallet -> /wallet/settings/backup
  - Legacy wallet recovery -> /wallet/settings/legacy

The inline 'Back up wallet' text link is removed from /wallet — the
seed-phrase reveal lives behind the cog now. The 'Move funds to your new
wallet' migration banner is removed too, along with the
useHdWalletV1Migration call that powered it. The same detection still
runs inside the legacy hub, but only when the user actually opens that
page; visiting /wallet no longer issues any Blockbook xpub scan or
NIP-78 query for legacy funds.

The Legacy Wallet Recovery hub surfaces the two previous Agora wallet
generations as separate options:

  - V2 Prelaunch Beta Wallet — sweep via the existing /wallet/migrate-v1
    flow (BIP-86 nsec-as-seed, plus its BIP-352 silent-payment UTXOs).
  - V1 Breeze Wallet — sweep via /wallet/recovery, the previously
    orphaned route for the Pathos-era Lightning custody. Reachable from
    the UI for the first time.

Both options are documented as one-way sweeps into the user's current
wallet; the legacy wallet itself is not restored.

i18n: drop the now-unused wallet.backupAction and wallet.migration.*
keys. New namespaces walletSettings, walletBackupPage, and walletLegacy
added to en.json and the 14 other locales (ar, es, fa, fr, hi, id, km,
ps, pt, ru, sn, tr, zh, zh-Hant).
2026-05-24 17:19:02 -05:00
Alex Gleason 1e3e985622 Merge branch 'main' of gitlab.com:soapbox-pub/agora 2026-05-24 16:40:07 -05:00
Chad Curtis e00a135eb0 Merge branch 'main' of gitlab.com:soapbox-pub/agora into feat/my-square
# Conflicts:
#	src/components/TopNav.tsx
#	src/components/auth/AccountSwitcher.tsx
2026-05-24 16:27:43 -05:00
mkfain 17ac152d08 Add Traditional Chinese (zh-Hant) locale
Derived from the existing Simplified Chinese (zh) locale via OpenCC-style
s2twp conversion (Simplified -> Traditional with Taiwan phrase
preferences), then hand-corrected for the cases where automated
conversion picks the wrong vocabulary swap:

- 支援 only for technical 'X supports Y feature' contexts; 支持 retained
  for 'support a cause/person/activist' which is the more common sense
  for an activism platform.
- 連結 (link/connect) rather than 連線 (online) in the hero tagline
  'connecting activists to unstoppable funds'.
- 代碼 rather than 程式碼 for silent-payment / donation 'code'
  identifiers (the source is a payment-address token, not source code).
- 實例 rather than 例項 for 'instance' (self-hosted weserv).
- 影片 rather than 視頻 for 'video' (Taiwan vocab).
- Fixed an automated-conversion artifact 聚整合員 -> 聚集成員.

The Traditional resource is also registered under zh-TW and zh-HK so
that browsers reporting those device locales route directly to the
Traditional file instead of falling back to Simplified zh.

Mainland zh-CN continues to resolve to zh (Simplified) via i18next's
nonExplicitSupportedLngs. The language switcher dropdown shows both
'简体中文' and '繁體中文' as distinct choices.

Key parity verified: 1677 leaves, same as en.json and zh.json, zero
missing or extra keys, all {{placeholders}} and <N>markup</N> tokens
preserved verbatim from the source.
2026-05-24 16:11:03 -05:00
mkfain 97da8ae822 Add Han display font for Chinese hero text
Bebas Neue (the .font-display family) ships only Latin glyphs, so
Chinese hero headlines fall back to system fonts and lose their
industrial display character. Add @fontsource/noto-sans-tc weight 900
and a :lang() rule that swaps it in for any .font-display element
while the page language is a Chinese variant (zh, zh-Hant, zh-TW,
zh-HK).

The fontsource CSS uses unicode-range descriptors, so non-Chinese
users do not download the Han glyph slices (effectively zero cost
for Latin-only locales).

The rule reverses Tailwind's italic, uppercase, tracking, and the
hero's 0.022em -webkit-text-stroke fatten trick — none of those are
meaningful for CJK text and the stroke trick muddies strokes at
weight 900.
2026-05-24 15:57:52 -05:00
Alex Gleason c7473f824b wallet: derive BIP-39 mnemonic from nsec (v2) so funds import into any BIP-39 wallet
The HD wallet seed is now BIP-39-compatible. Pipeline:

  entropy  = HKDF-SHA256(nsec, info="agora/v1", length=32)
  mnemonic = BIP-39 encoding of (entropy || checksum)  // 24 words
  seed     = PBKDF2-HMAC-SHA512(mnemonic, salt="mnemonic", iters=2048)

The 24 words import cleanly into Sparrow, Electrum, Trezor, Ledger,
BlueWallet, Phoenix, etc., at the BIP-86 / BIP-352 paths. HKDF domain
separation means a leaked mnemonic compromises only the wallet, not
the Nostr identity (unlike the raw nsec).

v1 derivation (nsec used directly as BIP-32 master seed) is retained
as migration-only code. A new /wallet/migrate-v1 page detects funds
at the legacy addresses and builds a single sweep PSBT to consolidate
them into the v2 wallet. A persistent banner on /wallet surfaces the
flow when v1 funds exist.

The mnemonic shows up in two places: a "Back up wallet" dialog on
/wallet, and a section in Profile -> Advanced next to the nsec
backup. nsec backup copy updated to explain the relationship.

Locked test vectors pin the entire derivation pipeline (nsec -> 24
words -> first BIP-86 address -> sp1q...) so any future drift fails
loudly. Regenerate via scripts/derive_vectors.mjs.

Other changes:
- Re-key SP storage NIP-78 d-tag to /v2 so v1 and v2 UTXOs do not mix
- Re-key the persisted receive-address cursor to :v2: namespace
- Relax SP spend-key helper to 16-64 byte seeds (BIP-32 range) so the
  migration sweep can sign with the legacy 32-byte v1 seed too
- Remove stale NIP-SP references from derivation comments (the draft
  was not relevant to our use case)
- Document the wallet derivation scheme in NIP.md
- Translate every new string to all 10 non-English locales
2026-05-24 15:39:22 -05:00
mkfain 88a5f45eac i18n: add Turkish (tr) locale
Adds a complete Türkçe translation. Turkish is spoken by ~90M people
and has an unusually strong mission fit for Agora: Turkey has
documented Bitcoin demand from currency-crisis users, journalist
crackdowns, and an active diaspora that often funds back home.

Register is modern İstanbul Turkish (BBC Türkçe / WhatsApp Türkçe
style) — polite-but-direct, formal siz throughout. English loanwords
kept where they're standard in everyday Turkish (Bitcoin, Lightning,
Nostr, röle, zap); Turkish equivalents used where they read naturally
(cüzdan, gönderi, anlık bildirim).

{{appName}} suffixing uses the standard Turkish apostrophe convention
for proper nouns ({{appName}}'da, {{appName}}'nın, etc.). Suffixes
are pinned to back-vowel harmony because "Agora" ends in 'a' — this
will break if AppConfig.appName is ever changed to a front-vowel word
like "Eylem" or "Yardım", but that's a deliberate trade-off for
not having to fork the JSON across deployments.

heroTagline mirrors the English design rhythm — line break before the
highlight, with the inline-block orange box on its own line as the
visual centerpiece:
"Aktivistleri<1></1><0>durdurulamaz</0> finansmana bağlıyoruz."
The trailing period is glued to a multi-word phrase rather than
sitting alone after the inline-block, avoiding the wrap bug we fixed
for Portuguese.

Turkish casing note: the hero h1 has text-transform: uppercase. Turkish
has dotted/dotless i (İ/i, I/ı). CSS uppercase honors the lang="tr"
attribute that applyDocumentDirection sets on <html>, so dotted
lowercase "i" uppercases to "İ" correctly without manual handling.

i18n auto-detection picks up the new locale on devices set to Turkish
(navigator.language returns tr/tr-TR).

Validation: en/tr leaf counts both 1677, no extra/missing keys,
locales.test.ts passes (120 tests, +2 for the new locale).
2026-05-24 15:30:42 -05:00
mkfain e25f575d18 i18n: add Indonesian (id) locale
Adds a complete Bahasa Indonesia translation. Indonesian is the
eleventh-most-spoken language worldwide (~200M speakers) and brings
Agora to Indonesia's growing Bitcoin-aware activist community plus the
broader maritime Southeast Asia.

Register is standard Bahasa Indonesia (formal-but-conversational,
the style used by Kompas, Tempo, Tirto). Avoids Malaysian-Malay-only
vocabulary so Malaysian users (handled via nonExplicitSupportedLngs
folding ms-* → id is NOT happening here; ms users will fall back to
English until we add ms separately, which is acceptable given the
~85% mutual intelligibility). Uses Anda for the second person, the
established UI convention. Standard Indonesian tech vocabulary where
present (dompet, kampanye, donasi, donatur, ikrar, penggalangan dana,
pengaturan); English loanwords kept where they're normal in modern
Indonesian software (Bitcoin, Lightning, Nostr, feed, post, relay,
zap, NIP, BIP).

heroTagline uses the period-inside-highlight pattern:
"Menghubungkan aktivis dengan<1></1>pendanaan <0>tak terbendung.</0>"
The trailing period lives inside the orange highlight span, so it
can't orphan to its own line at the hero's text-8xl size — the same
fix we applied for Portuguese.

i18n auto-detection picks up the new locale on devices set to
Indonesian (navigator.language returns id/id-ID).

Validation: en/id leaf counts both 1677, no extra/missing keys,
locales.test.ts passes (118 tests, +2 for the new locale).
2026-05-24 15:29:36 -05:00
mkfain 348b86d22b i18n: add Hindi (hi) locale
Adds a complete Hindi translation alongside the existing 11 locales.
Hindi (हिन्दी) is the fourth-most-spoken language in the world (~610M
speakers) and brings Agora to a large activist population currently
served only by the English fallback.

Translation register is Hindustani / news-style (BBC Hindi-flavored),
not Sanskrit-leaning शुद्ध हिन्दी — chosen for accessibility to the
broadest Hindi-speaking audience, including users who'd otherwise
struggle with formal government Hindi. Uses आप throughout for the
second person. Standard tech loanwords kept transliterated where they
read naturally (वॉलेट, पोस्ट, कैंपेन, फ़ीड); brand names and
protocol tokens (Bitcoin, Nostr, BIP-352, nsec, sp1…) kept in Latin
script.

heroTagline uses the safe structure — short highlight on the adjective
"अजेय" with a trailing phrase ending in danda (।), avoiding the
inline-block-followed-by-lone-punctuation wrap bug that previously hit
the Portuguese locale.

i18n auto-detection picks up the new locale on devices set to Hindi
(navigator.language returns hi/hi-IN), with nonExplicitSupportedLngs
folding regional variants into the hi bucket. The runtime fallback to
English still works for any keys we might miss in future edits.

Validation: en/hi leaf counts both 1677, no extra/missing keys,
locales.test.ts passes (116 tests, +2 for the new locale).
2026-05-24 15:28:31 -05:00
mkfain 2b544d1f7a fix(i18n): keep trailing period inside hero highlight in pt locale
The Portuguese heroTagline read

  "Conectando ativistas a<1></1><0>financiamento incontrolável</0>."

where the period sits outside the <0>...</0> orange highlighter span.
That span is rendered as inline-block w-fit (CampaignsPage.tsx:203-207),
and the boundary between an inline-block and the following bare text
node is a soft-wrap opportunity. When the highlighted phrase
"financiamento incontrolável" filled the line — which it does at the
hero's text-7xl/8xl sizes inside max-w-2xl — the trailing period
wrapped to its own line.

Restructure the markup so only the adjective is highlighted and the
period lives inside the orange box, mirroring the design intent of the
English original (where the highlight is on the adjective and the noun
trails it):

  "Conectando ativistas a<1></1>financiamento <0>incontrolável.</0>"

"financiamento " is now plain text before the box, and the period is
part of the box's content, so there is no wrap opportunity between the
last visible character and the period.

Other locales (fr, ru) have the same structural shape but short enough
highlighted words that the issue doesn't manifest in practice; leaving
them alone.
2026-05-24 12:55:48 -05:00
mkfain bf87c21587 fix(faq): correct address derivation source, lead silent-payments answer with wallet send support
The donation address derivation FAQ said "derived from your Nostr public
key," but the HD wallet (src/lib/hdwallet/derivation.ts) actually uses
the user's nsec (secret key) as the BIP-32 master seed for both the
BIP-86 Taproot wallet and the BIP-352 silent-payments wallet. Updated
the wording across why-not-rotating-addresses, what-is-nostr, the donor
guide's arrivesDirectly step, and the activist guide's howReceiving
intro.

The 'Does Agora support silent payments?' answer only mentioned the
activist receive side. Agora's own wallet supports sending silent
payments too (HDSendBitcoinDialog accepts sp1… addresses via the
BIP-352 sender in src/lib/hdwallet/sp/sender.ts), so the answer now
leads with that on the send side and follows with the existing
activist-receive paragraphs.

Propagated to all 10 non-English locales (ar, es, fa, fr, km, ps, pt,
ru, sn, zh).
2026-05-24 12:48:30 -05:00
mkfain 3dac23d2af i18n: translate new sections in ru/fr/pt locales
Catches the ru/fr/pt locales up with the keys added upstream while the
initial translations were in flight: translate, forms additions,
organizationContext, groups.detail, calendarEvents, follow,
campaignsDetail.openInWallet, the assorted countryPlaceholder /
showLess / readMore entries, and a handful of scattered new keys.
2026-05-24 11:50:25 -05:00
mkfain e7e0236fa7 i18n: translate Privacy + CSAE policy markdown into Russian, French, Brazilian Portuguese
Adds src/content/privacy/{ru,fr,pt}.md and src/content/csae/{ru,fr,pt}.md,
and registers them in usePolicyMarkdown.ts so the policy pages serve the
matching translation when the user's language is set to one of the three
new locales (falling back to en otherwise).
2026-05-24 11:43:49 -05:00
lemon d11cc212d7 fix: update new locale keys 2026-05-24 00:46:09 -07:00
lemon 157e04fed1 fix: localize shared creation form copy 2026-05-24 00:40:27 -07:00
lemon 8f0de73d86 fix: localize event creation page 2026-05-24 00:40:27 -07:00
lemon 98b4df47d1 fix: restore event creation page 2026-05-24 00:40:27 -07:00
lemon 64ac109e6c fix: translate event detail page 2026-05-24 00:40:27 -07:00
lemon 81a765aef1 fix: translate detail page labels 2026-05-24 00:40:27 -07:00
lemon 78d680fa37 fix: localize feed mode switcher 2026-05-24 00:37:04 -07:00
lemon 2df55a731a fix: translate compose destination strings 2026-05-24 00:34:04 -07:00
lemon 00eb2128c3 fix: place card translate controls in footers 2026-05-24 00:30:29 -07:00
lemon 4d40099426 feat: translate preview cards 2026-05-24 00:30:29 -07:00
lemon 9392cc5061 feat: translate event detail cards 2026-05-24 00:30:29 -07:00
lemon 24ba779da6 fix: map Persian and Pashto translation targets 2026-05-24 00:30:29 -07:00
lemon 0efd72268e feat: translate campaign cards 2026-05-24 00:30:29 -07:00
lemon 3e7b7e824f fix: move note translation action 2026-05-24 00:30:29 -07:00
lemon 8d7d81dd3f feat: add note translation button 2026-05-24 00:30:29 -07:00
mkfain ab48ab3aa3 i18n(ru): translate guides (donor + activist) — Russian locale complete 2026-05-24 00:46:15 -05:00
mkfain 60f745af0b i18n(ru): translate profileSettings, faq 2026-05-24 00:44:09 -05:00
mkfain 431f3e43de i18n(ru): translate notifications, notifSettings, search, profile 2026-05-24 00:41:14 -05:00
mkfain 47bccf4d7d i18n(ru): translate wallet, walletRecovery, walletConnect, walletSend, bitcoinPublic, spScan 2026-05-24 00:39:21 -05:00
mkfain fb84d29898 i18n(ru): translate campaignsDetail, campaigns, settings, language, organizers 2026-05-24 00:37:32 -05:00
mkfain 4833f26847 i18n(ru): translate policyPages, about, forms, notFound, pledges, groups, campaignsCreate 2026-05-24 00:35:54 -05:00
mkfain fefe2cae1e i18n(ru): translate common, nav, auth, feed, compose, noteCard, noteMoreMenu 2026-05-24 00:31:33 -05:00
mkfain 6100ad657c i18n: translate Brazilian Portuguese (pt) locale 2026-05-24 00:29:19 -05:00
mkfain 3f8fe5d1f6 i18n: translate French (fr) locale 2026-05-24 00:13:16 -05:00
mkfain 614966f764 i18n: scaffold Russian, French, and Brazilian Portuguese locales
Bootstraps src/locales/{ru,fr,pt}.json from en.json as a starting
point and wires all three into i18n.ts. Strings are still English
pending the per-section translation passes that follow.
2026-05-23 23:57:12 -05:00
mkfain 90a1b17c07 i18n: translate CSAE policy into km and sn
Fills the last two gaps in CSAE policy coverage. Khmer and Shona
markdown files land beside the existing six (en, es, zh, ar, fa, ps),
and both entries are registered in the usePolicyMarkdown loader.

Every long-form policy / guide surface is now translated in all eight
shipped locales.
2026-05-23 23:19:04 -05:00
mkfain 75d5b7a09d i18n: translate Donor / Activist guides into es, zh, ar, fa, ps, km, sn
Ships the full guides.* namespace in every non-English locale:

  * guides.shared (tldr eyebrow, payment badge labels, payment
    comparison table headers and all 4 donor rows / 7 activist rows)
  * guides.donor (tldr, flow steps, comparison footnote, two callouts,
    privacy optionGrid, silentToday prose)
  * guides.activist (tldr, two prose sections, comparison footnote,
    silentToday prose, movePromptly steps, cashout optionGrid, tumblers
    callout)

Product / wallet / project names (Bitcoin, Nostr, BIP-352, Ditto Wallet,
Dana, Sparrow, BlueWallet, Phoenix, Bisq, Wasabi, Boltz, Bitrefill,
HodlHodl, RoboSats, Cash App, Coinbase, Strike, Venmo, Kraken, Binance,
PayPal, Amazon, Uber) and tag chips (non-custodial, BIP-352, low fees,
etc.) stay in English by design — the chips live in code, not in
locales.
2026-05-23 23:07:49 -05:00
mkfain f66e6e80c1 i18n: route Donor / Activist guide strings through locales
Splits the guide content same way the FAQ was split:

  * Structure (block order, kinds, audience, callout variant, option
    grid chips/hrefs) stays in helpContent.ts as a typed array of
    GuideBlockStructure descriptors with stable IDs.
  * Every user-visible string moves to en.json under guides.donor.*,
    guides.activist.*, and guides.shared.* (badge labels, tldr eyebrow,
    payment comparison table headers + rows).

PaymentComparisonTable, InlinePaymentBadge, and GuideTLDR call
useTranslation() so a language switch triggers re-render. DonorGuidePage
and ActivistGuidePage already do, so getDonorGuideBlocks /
getActivistGuideBlocks re-run on every render and pick up fresh i18n
values automatically.

This commit lands the en strings only; non-en locales follow in the
next commit.
2026-05-23 23:07:49 -05:00
Alex Gleason eed83796f2 Replace footer copyright with source code link 2026-05-23 22:00:56 -05:00
mkfain b84ddd5c39 i18n: translate FAQ into es, zh, ar, fa, ps, km, sn
Ships the `faq.*` namespace in every non-English locale. Each locale
gets all 4 category labels and all 23 items (15 visible across About
Agora / Bitcoin Donations / About Nostr + 8 hidden legacy items used by
`HelpTip` call sites in settings pages).

Missing-key fallback to English still works via i18next's default
fallback chain — if a future FAQ item lands in en.json before the other
locales catch up, those locales will fall back to English at that key
without breaking the renderer.
2026-05-23 21:32:00 -05:00
Alex Gleason ca74e87ea3 Merge branch 'main' of gitlab.com:soapbox-pub/agora
# Conflicts:
#	src/lib/helpContent.ts
2026-05-23 21:11:19 -05:00
Alex Gleason 6f187d580f Merge branch 'main' of gitlab.com:soapbox-pub/agora
# Conflicts:
#	src/components/music/MusicTrackRow.tsx
#	src/pages/ActionDetailPage.tsx
2026-05-23 21:09:16 -05:00
mkfain e63a08c2e2 i18n: route FAQ strings through the locale namespace
Splits the FAQ definition in `helpContent.ts` into two layers:

  * a structural template (category order, item IDs, hidden flag) that
    stays in TS, and
  * a flat `faq.*` namespace in `locales/*.json` that holds every
    user-visible string (category labels, questions, answer paragraph
    arrays).

`getFAQCategories` / `getFAQItems` / `getFAQItem` keep the same
`(appName)` signature and `FAQCategory` / `FAQItem` shape — internally
they resolve strings through `i18n.t()`, with `{appName}` literals
rewritten to `{{appName}}` for i18next interpolation. Answer arrays use
`returnObjects: true` so a missing locale entry falls back to English
without breaking the renderer.

`HelpFAQSection` reads `i18n.language` to re-resolve on language switch;
`HelpTip` calls `useTranslation()` for the same reason. The Donor /
Activist guide blocks below are still keyed off the original
single-brace `{appName}` literal — that's a separate i18n pass.
2026-05-23 21:05:46 -05:00