feat: extract all user-facing strings into a Fluent catalogue
This commit is contained in:
parent
4c5665a6ff
commit
57e75af3ac
8 changed files with 1024 additions and 326 deletions
343
locales/en-US/main.ftl
Normal file
343
locales/en-US/main.ftl
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
## Loaded once at startup by `src/i18n.rs`; every user-facing string in
|
||||
## the app lives here, grouped by the feature it belongs to.
|
||||
|
||||
## Seed parsing — restoring an identity from a pasted seed
|
||||
|
||||
seed-bad-hex = bad hex
|
||||
seed-wrong-length = a seed is 32 bytes
|
||||
|
||||
## Local files — errors reading/writing the key and its sidecars
|
||||
|
||||
file-error = { $path }: { $reason }
|
||||
key-file-wrong-length = { $path }: not a 32-byte master
|
||||
|
||||
## Compose — validating a typed `Key: value` header row
|
||||
|
||||
compose-header-colon-missing = a header is `Key: value` — the colon is missing
|
||||
compose-header-reserved-key = { $key } is attached by the client and cannot be set
|
||||
compose-header-invalid-key = keys are 1-64 characters of letters, digits and dashes
|
||||
compose-header-value-multiline = a value must stay on one line
|
||||
compose-header-duplicate-key = { $key } is set twice
|
||||
compose-headers-too-large = the headers exceed the frontmatter budget
|
||||
compose-headers-too-many = too many headers
|
||||
|
||||
## Contacts — importing a smol:// address
|
||||
|
||||
contacts-import-needs-key = import needs a self-certifying smol:// address carrying a key
|
||||
|
||||
## Mail — opening a message by id
|
||||
|
||||
mail-message-id-wrong-length = message ids are 64 hex characters
|
||||
mail-message-id-wrong-bytes = message ids are 32 bytes
|
||||
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.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
## 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.
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
## Background-operation status notes
|
||||
##
|
||||
## Built in `spawn_op` (src/view.rs) rather than ops.rs, since that is
|
||||
## where each `PendingOp` result is assembled for the worker thread.
|
||||
|
||||
status-registered-note = registered
|
||||
status-restored-note = restored at rotation { $index }
|
||||
status-deleted-note = deleted
|
||||
status-accepted-note = accepted { $address }
|
||||
status-blocked-note = blocked { $address }
|
||||
status-not-blocked-note = { $address } was not blocked
|
||||
status-imported-note = imported { $address } (verified)
|
||||
status-rotated-note = rotated to index { $index }; new key { $key_prefix }…
|
||||
|
||||
## Backup
|
||||
|
||||
backup-written-note = backup written to { $path }
|
||||
|
||||
## Drafts
|
||||
|
||||
draft-saved-note = draft saved
|
||||
|
||||
## Log out
|
||||
|
||||
logged-out-note = logged out. Create a new identity or restore a seed.
|
||||
|
||||
## Onboarding
|
||||
|
||||
identity-created-note = Identity created. Write the seed down; it is the only secret.
|
||||
seed-restored-note = Seed restored. Bind a username or recall an address.
|
||||
|
||||
## Signup
|
||||
|
||||
signup-enter-host-error = enter the server host first
|
||||
signup-enter-user-host-error = enter user and host
|
||||
|
||||
## Compose
|
||||
|
||||
compose-enter-recipient-error = enter a recipient
|
||||
compose-header-row-error = header { $index }: { $reason }
|
||||
reply-no-address-error = this sender left no address to reply to
|
||||
reply-subject-prefix = Re: { $subject }
|
||||
address-invalid-error = that address does not parse
|
||||
|
||||
## Contacts
|
||||
|
||||
contacts-reply-address-invalid-error = the sender's reply address does not parse
|
||||
contacts-sender-key-invalid-error = the sender's key does not parse
|
||||
contacts-accept-no-address-error = this sender left no address to accept
|
||||
contacts-block-no-address-error = this sender left no address to block
|
||||
|
||||
## Common — short words reused verbatim across several buttons/dialogs
|
||||
|
||||
action-cancel = Cancel
|
||||
action-back = Back
|
||||
action-block = Block
|
||||
action-unblock = Unblock
|
||||
action-accept = Accept
|
||||
action-reply = Reply
|
||||
action-delete = Delete
|
||||
# Shown wherever a fetched server key is on screen before it's pinned:
|
||||
# the compose's pin card, the signup server step, and the contact detail
|
||||
# body when a key was fetched for that contact.
|
||||
trust-fingerprint-hint = If the server operator published a key or fingerprint, make sure it matches before trusting it.
|
||||
|
||||
## Sidebar
|
||||
|
||||
mail-fetch-button = Fetch
|
||||
mail-write-button = Write
|
||||
folder-inbox = Inbox
|
||||
folder-requests = Requests
|
||||
folder-sent = Sent
|
||||
folder-drafts = Drafts
|
||||
settings-nav-label = Settings
|
||||
identity-you-caption = you
|
||||
contacts-caption = contacts
|
||||
contacts-import-placeholder = Import a smol:// address
|
||||
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.
|
||||
|
||||
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-no-subject = (no subject)
|
||||
mail-no-reply-address = no reply address: cannot accept or block
|
||||
contacts-empty-hint = no contacts
|
||||
|
||||
## Mail list — empty-folder messages, and the own-identity detail
|
||||
|
||||
mail-inbox-empty-title = Inbox is empty
|
||||
mail-inbox-empty-description = Fetch to check the mailbox.
|
||||
mail-requests-empty-title = No requests
|
||||
mail-requests-empty-description = Mail from senders you have not accepted yet arrives here.
|
||||
mail-sent-empty-title = Nothing sent yet
|
||||
mail-sent-empty-description = Messages you send keep a sealed copy here.
|
||||
mail-drafts-empty-title = No drafts
|
||||
mail-drafts-empty-description = A compose closed from the sidebar is kept here.
|
||||
own-identity-meta = you · rotation { $rotations }
|
||||
contacts-pin-button-fetched = Pin this key
|
||||
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.
|
||||
detail-qr-tooltip = Show the address as a QR code
|
||||
detail-copy-tooltip = Copy the address
|
||||
|
||||
## Compose
|
||||
|
||||
compose-to-label = To
|
||||
compose-subject-label = Subject
|
||||
compose-add-header-button = + header
|
||||
compose-send-button = Send
|
||||
compose-header-placeholder = Key: value
|
||||
compose-header-remove-tooltip = Remove this header
|
||||
|
||||
## Contacts — trust state shown in the sidebar and the detail pane
|
||||
|
||||
contact-state-accepted = accepted
|
||||
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
|
||||
}
|
||||
|
||||
## Detail pane — the monospace key/value block shared by contacts and the
|
||||
## own identity view. Column widths are padded to these labels in code
|
||||
## (14 characters), so a much longer translation would throw the
|
||||
## alignment off; flagged in the extraction report.
|
||||
|
||||
detail-key-label = key
|
||||
detail-fingerprint-label = fingerprint
|
||||
detail-server-label = server
|
||||
contact-server-key-offered = this server presents
|
||||
|
||||
## Settings — tab switcher labels
|
||||
|
||||
settings-tab-identity = Identity
|
||||
settings-tab-server = Server
|
||||
settings-tab-backup = Backup
|
||||
settings-tab-advanced = Advanced
|
||||
|
||||
## Settings — Identity tab
|
||||
|
||||
settings-field-address = Address
|
||||
settings-field-key = Key
|
||||
settings-field-fingerprint = Fingerprint
|
||||
settings-field-uri = URI
|
||||
property-row-copy-tooltip = Copy the { $field }
|
||||
settings-recovery-secret-title = Recovery Secret
|
||||
settings-secret-reveal-tooltip = Reveal the recovery secret
|
||||
settings-secret-hide-tooltip = Hide the recovery secret
|
||||
settings-secret-copy-tooltip = Copy the recovery secret
|
||||
|
||||
## Settings — Server tab
|
||||
|
||||
settings-field-host = Host
|
||||
settings-field-public-key = Public Key
|
||||
settings-public-key-not-pinned = not pinned yet
|
||||
server-connection-title = Connection
|
||||
server-timeout-title = Timeout
|
||||
server-timeout-subtitle = Seconds before a network call gives up
|
||||
server-fetching-title = Fetching
|
||||
server-leave-mail-title = Leave mail on server
|
||||
server-leave-mail-subtitle = Keep a copy on the server after fetching, so another client can fetch it too. When this is enabled, deleting a message removes it from this client and the server.
|
||||
|
||||
## Settings — Backup tab
|
||||
|
||||
backup-page-description = One file holding your mail, contacts and server info for moving to another client, or for keeping what the server no longer has. It is sealed to this identity and deliberately leaves the secret itself out.
|
||||
backup-export-title = Export
|
||||
backup-export-subtitle = Write a backup file
|
||||
backup-import-title = Import
|
||||
backup-import-subtitle = Restore from a backup file
|
||||
backup-export-dialog-title = Export Backup
|
||||
backup-import-dialog-title = Import Backup
|
||||
|
||||
## Settings — Advanced tab
|
||||
|
||||
advanced-rotations-title = Rotations
|
||||
advanced-rotations-description = Issues a fresh key from your secret with a signed certificate proving it replaces this one. Contacts accept the change from that chain, and the old key stays readable. Rotation is not revocation.
|
||||
advanced-rotations-completed = Completed
|
||||
advanced-rotate-button = Rotate identity
|
||||
advanced-first-contact-title = First Contact
|
||||
advanced-first-contact-description = A recipient who is not a contact yet, and whose address carries no key, has to be looked up on their own server. That answer is only as good as the server: one with no pinned key can return a key of its own and read what you send.
|
||||
advanced-strict-pinning-title = Require a pinned server
|
||||
advanced-strict-pinning-subtitle = Stop such a send and offer to pin the server's key first, so it can be checked against what the operator published. With this off, the key is taken on trust and the send is only reported as unverified afterwards.
|
||||
advanced-logout-title = Log Out
|
||||
advanced-logout-description = Removes this identity from this client entirely: the secret, pins, contacts and every cached message. Without a backup of the secret there is no way back in. Mail already fetched here may have no other copy anywhere, unless "leave mail on server" was on. Whatever is still on the server comes back on the first fetch after logging back in.
|
||||
advanced-logout-button = Log out and delete all data
|
||||
|
||||
## Onboarding
|
||||
##
|
||||
## The app name itself ("carrier") is a brand, not translated prose, and
|
||||
## is left as a literal in code — see the extraction report.
|
||||
|
||||
onboard-tagline = smolmail: one seed, sealed mail, five operations
|
||||
onboard-create-button = Create a new identity
|
||||
onboard-restore-button = Restore existing identity from seed
|
||||
onboard-created-title = Write this seed down
|
||||
onboard-created-description = It is the only secret, and it is shown once.
|
||||
onboard-saved-button = I saved it
|
||||
onboard-restore-title = Restore from a seed
|
||||
onboard-seed-entry-title = Seed — 32 bytes, base32 or hex
|
||||
onboard-restore-go-button = Restore
|
||||
|
||||
## Signup
|
||||
|
||||
signup-server-title = Set up the server
|
||||
signup-fetch-key-button = Fetch the server's key
|
||||
signup-continue-button = Continue
|
||||
signup-start-over-button = Start over
|
||||
signup-account-title = Choose your address
|
||||
signup-mode-new-label = Create a new address
|
||||
signup-mode-existing-label = I already have this address
|
||||
signup-user-label = User
|
||||
signup-invite-label = Invite token (if required by the server)
|
||||
signup-register-button = Register
|
||||
signup-recall-button = Restore access
|
||||
|
||||
## Compose — the inline pin card shown after a blocked send
|
||||
|
||||
compose-pin-title = { $host } is not pinned
|
||||
compose-pin-body = { $address } is not a contact yet, so carrier would have to ask this server for their key — and a server with no pinned key can answer with one of its own and read the message.
|
||||
compose-pin-button-fetch = Fetch their server's key
|
||||
compose-pin-button-send = Pin and send
|
||||
|
||||
## Dialogs
|
||||
|
||||
delete-dialog-title = Delete this message?
|
||||
delete-dialog-body-kept = Deleting it here leaves the server's copy in place. Deleting it everywhere asks the server to drop that copy too.
|
||||
delete-dialog-body-gone = The sealed copy in the local store is removed.
|
||||
delete-dialog-everywhere-button = Delete Everywhere
|
||||
rotate-dialog-title = Rotate the identity?
|
||||
reset-dialog-title = Log out and delete all data?
|
||||
# `$list` is the sender's own frontmatter rows, already typed by a human
|
||||
# and reproduced verbatim — not translatable prose itself, just data
|
||||
# interpolated into this one sentence.
|
||||
reply-dialog-title = Carry the sender's fields into the reply?
|
||||
reply-dialog-body = This message carries its own frontmatter:{"\u000A"}{"\u000A"}{ $list }{"\u000A"}{"\u000A"}Reply with copies of these fields, or start with none. The fields stay editable either way.
|
||||
reply-dialog-cancel-button = Cancel Reply
|
||||
reply-dialog-without-button = Reply Without
|
||||
reply-dialog-copy-button = Copy Fields
|
||||
key-change-dialog-title = Replace this contact's key?
|
||||
key-change-dialog-body = { $address } is already on file with a different key.{"\u000A"}{"\u000A"}known { $known_fingerprint }{"\u000A"}new { $offered_fingerprint }{"\u000A"}{"\u000A"}The address you were given carries the new key and would replace the stored one. If this contact did not hand it to you themselves, stop: a key swapped in on the way reads everything you send them.
|
||||
key-change-dialog-replace-button = Replace Key
|
||||
|
||||
## Status bar
|
||||
##
|
||||
## `model.busy` also doubles as an internal state-machine key compared
|
||||
## against literal English in app.rs (e.g. `self.busy.as_deref() ==
|
||||
## Some("fetching")`), so that value itself is not translated — only its
|
||||
## display here is, via this lookup from the internal label to a phrase.
|
||||
## See the extraction report for the coupling this implies.
|
||||
|
||||
status-busy-fetching = fetching…
|
||||
status-busy-refreshing = refreshing…
|
||||
status-busy-registering = registering…
|
||||
status-busy-restoring = restoring…
|
||||
status-fetching-progress = fetching… { $count } in inbox
|
||||
|
||||
## First contact / server trust
|
||||
|
||||
first-contact-blocked-error = { $address } is not a contact yet and { $host } is not pinned — pin their server's key to send.
|
||||
server-key-offered-note = The server presented this key. Pin it only if the fingerprint matches what the operator published.
|
||||
server-key-already-pinned-note = This server's key is already pinned.
|
||||
server-key-pinned-note = server key pinned
|
||||
Loading…
Add table
Add a link
Reference in a new issue