docs: rewrite the author-facing copy in plain language
The copy was written for someone who had read the spec. It named UTF-8, RFC 3339, canonical form, code points and frontmatter fields, and the one place it gave instructions it gave them as fumi command lines, which say nothing to anyone using carrier or gsmol. Now it uses the words the clients put on screen: "header", with the "+ header" button both of them have, and "Requests folder", which is what both call it. One word per idea throughout — post, feed, header, Requests folder — and reasons are whole sentences that say what happened and what to do, so the reply no longer opens "Reason:" followed by a lowercase fragment. The first-contact mail is the one a stranger reads before anything else, so it now covers accepting this address: a recommendation, not the mechanism behind it. The mechanism would also have aged badly, since a client that delivers its token once makes accepting cost a single refused post rather than all of them. A refused post gets a detail paragraph where one line cannot hold the fix. It tells the author their token arrived and to send again, and what to do if it is refused a second time, which is what happens on a client that attaches the token to everything. A first post on an empty feed says so differently: the author is watching their first attempt fail and needs to hear the feed is fine. Seven admin strings were built with format! and never reached the catalogue, so an ack opened its body in lowercase and could not be translated. They are entries now, and say what blocking does rather than naming the mechanism. The cap warning said "90% of its 1048576 byte limit". It says 1 MB. Four rig assertions matched the old wording and now match the new. They have to read rendered mail, so they stay coupled to it in a way the unit tests, which assert on Reason variants, do not.
This commit is contained in:
parent
9c9dce57e7
commit
72c353cb5a
5 changed files with 271 additions and 128 deletions
|
|
@ -1,157 +1,201 @@
|
|||
## Loaded once by `src/i18n.rs`; every string the daemon mails to an author
|
||||
## lives here, grouped by the mail it belongs to. Operator-facing CLI output is
|
||||
## deliberately not here — see the module header.
|
||||
## Every string the daemon mails to an author. Operator CLI output is not
|
||||
## here, deliberately: see the i18n module header.
|
||||
##
|
||||
## Keep the `-32` style placeables out: these bodies are read in plain-text
|
||||
## mail clients and on phones, so they stay short lines and no markup.
|
||||
## House style, in short. Write to "you" and use contractions. Sentence case.
|
||||
## Start with a verb where you can. Say what happened and what to do next,
|
||||
## without blaming anyone and without the words invalid, illegal or fatal.
|
||||
## Use the words the clients use: "header", not frontmatter field; "Requests
|
||||
## folder", not requests tier; "post", not record, twt or entry. No em dashes.
|
||||
##
|
||||
## These are read in plain-text mail clients and on phones, so keep lines
|
||||
## short and leave markup out.
|
||||
|
||||
## Error replies — a mail the daemon could not apply
|
||||
## Replies about a message we couldn't publish
|
||||
|
||||
reply-subject = twtxt: { $reason }
|
||||
reply-body =
|
||||
Your mail could not be applied.
|
||||
{ $reason }
|
||||
|
||||
Reason: { $reason }
|
||||
|
||||
Quoted mail:
|
||||
Here's the message:
|
||||
{ $quoted }
|
||||
|
||||
## Retention — the one-time warning as a feed approaches its size cap
|
||||
## Reasons. Each one is a whole sentence: it becomes the subject line, and
|
||||
## the body opens with it.
|
||||
##
|
||||
## Each MUST be a single line, however long. A reason goes into a mail header,
|
||||
## and a line break there breaks the whole header block, so the reply arrives
|
||||
## with no subject at all. Fluent keeps the newlines in a wrapped value, so
|
||||
## there's no safe way to wrap these. `every_reason_is_a_single_line` checks
|
||||
## it. When the fix needs more room, put it in a detail paragraph below.
|
||||
|
||||
cap-warning-subject = twtxt: feed at 90% of its size cap
|
||||
reason-body-not-utf8 = We couldn't read the text in this message. Try sending it again.
|
||||
reason-post-not-canonical = We couldn't rebuild this message exactly as you sent it, so we can't prove it's yours. Send the text on its own.
|
||||
reason-repost-not-canonical = We couldn't rebuild this repost exactly as you sent it. Send it again with only the Twt-Repost header.
|
||||
reason-fields-beyond-reply-to = This post carries extra headers. Send it again with just the text.
|
||||
reason-nick-invalid = A nick can use a-z, 0-9 and hyphens, up to 30 characters.
|
||||
reason-delete-needs-timestamp = Twt-Delete needs the time of the post to remove, like 2026-10-14T19:39:39Z.
|
||||
reason-prune-mode-unknown = Twt-Prune takes fifo or off, not "{ $got }".
|
||||
|
||||
## Limits on what a post can contain
|
||||
|
||||
reason-too-many-codepoints = A post can be up to 160 characters. Shorten it and send it again.
|
||||
reason-too-many-bytes = A post can be up to 640 bytes, and accents and emoji use several each. Shorten it and send it again.
|
||||
reason-forbidden-char = A post has to fit on one line. Take out the line breaks and tabs, then send it again.
|
||||
reason-bad-timestamp = That isn't a time we can read. Use the form 2026-10-14T19:39:39Z.
|
||||
reason-duplicate-timestamp = Your feed already has a post at that time.
|
||||
|
||||
## Quota and retention
|
||||
|
||||
reason-over-quota-post = You've reached today's limit for posts. Try again tomorrow.
|
||||
reason-over-quota-repost = You've reached today's limit for reposts. Try again tomorrow.
|
||||
reason-collision = Your feed already has a post at { $timestamp }. Wait a second, then send it again.
|
||||
reason-feed-full = Your feed is full. Remove a post with Twt-Delete, or send Twt-Prune: fifo to drop the oldest ones from now on.
|
||||
reason-reply-to-changed = This feed belongs to { $author }. Send your posts from that address.
|
||||
reason-no-record-at = Your feed has no post at { $timestamp }.
|
||||
|
||||
## Commands
|
||||
|
||||
reason-one-command-per-mail = Send one command per message. Twt-Register, Twt-Help, Twt-Admit, Twt-Block and Twt-Registration each need their own, and feed commands need another.
|
||||
reason-bad-address = "{ $got }" isn't a smolmail address.
|
||||
reason-registration-mode-unknown = Twt-Registration takes open, closed or silent, not "{ $got }".
|
||||
|
||||
## An accept token in a post. The author didn't do anything wrong: their
|
||||
## client added the token because they accepted this address. The fix depends
|
||||
## on which client they use, so it goes in a detail paragraph.
|
||||
|
||||
reason-accept-field-on-post = A post can't carry the accept token your client attached.
|
||||
reason-accept-field-on-first-post = Your first post carried an accept token, which we can't publish.
|
||||
|
||||
## Detail paragraphs, added to the end of the reply when one line isn't
|
||||
## enough room for the fix. Most reasons don't need one.
|
||||
|
||||
reason-accept-field-on-post-detail =
|
||||
|
||||
We publish your post by rebuilding exactly what you signed, so anyone can
|
||||
check it was you. A token can't be part of that.
|
||||
|
||||
We've got your token now, so send the same post again and it should go up.
|
||||
|
||||
If it's refused again, your client is adding the token to every message
|
||||
instead of sending it once. Block this address in your client and send
|
||||
the post again. Blocking only moves our replies back to your Requests
|
||||
folder.
|
||||
|
||||
reason-accept-field-on-first-post-detail =
|
||||
|
||||
Your feed is fine. It's there and it's waiting for its first post.
|
||||
|
||||
We publish your post by rebuilding exactly what you signed, so anyone can
|
||||
check it was you. A token can't be part of that.
|
||||
|
||||
We've got your token now, so send that post again and it should go up.
|
||||
|
||||
If it's refused again, your client is adding the token to every message
|
||||
instead of sending it once. Block this address in your client and send
|
||||
the post again. Blocking only moves our replies back to your Requests
|
||||
folder.
|
||||
|
||||
## Warning as a feed fills up
|
||||
|
||||
cap-warning-subject = twtxt: your feed is nearly full
|
||||
cap-warning-body =
|
||||
Your feed has reached 90% of the { $cap }-byte cap.
|
||||
Your feed has reached 90% of its { $cap } limit.
|
||||
|
||||
In hard-limit mode (the default) a full feed rejects new records; delete
|
||||
some with Twt-Delete, or choose FIFO pruning with Twt-Prune: fifo, which
|
||||
drops the oldest records to make room.
|
||||
When it's full, new posts are refused. Remove some with Twt-Delete, or
|
||||
send Twt-Prune: fifo and we'll drop the oldest ones to make room.
|
||||
|
||||
## Admission — the mail that hands over the accept token
|
||||
## The mail that hands over the accept token
|
||||
|
||||
welcome-subject = twtxt: your feed is ready
|
||||
welcome-body =
|
||||
Your smolboid feed is live: { $url }
|
||||
Your feed is live: { $url }
|
||||
|
||||
Post by sending mail to this address with the twt as the body. Manage it
|
||||
with the Twt-Nick, Twt-Delete, Twt-Repost and Twt-Prune frontmatter
|
||||
fields, and send Twt-Help to get this list again.
|
||||
Send a message to this address and it becomes a post.
|
||||
|
||||
## Rejection reasons — one per `reason::Reason` variant, rendered into the
|
||||
## reply subject and body above. Each is a sentence fragment completing
|
||||
## "your mail could not be applied because …", so they start lowercase and
|
||||
## carry the fix where there is one.
|
||||
##
|
||||
## Each MUST be a single line, however long. These are interpolated into the
|
||||
## reply's Subject, and a frontmatter value carrying a line break makes the
|
||||
## whole block malformed — smolmail then fails closed toward display (SPEC.md
|
||||
## sec 5.5) and the reply arrives with no subject at all. Fluent multiline
|
||||
## values keep their newlines, so there is no safe way to wrap them here.
|
||||
## `reason::tests::every_reason_is_a_single_line` enforces this.
|
||||
To manage your feed, add one of these headers: Twt-Nick, Twt-Delete,
|
||||
Twt-Repost or Twt-Prune. Send Twt-Help for what each one does.
|
||||
|
||||
reason-body-not-utf8 = body is not UTF-8
|
||||
reason-post-not-canonical = post mail is not in canonical form
|
||||
reason-repost-not-canonical = repost mail is not in canonical form
|
||||
reason-fields-beyond-reply-to = post mail carries fields beyond Reply-To (Subject or headers); resend the bare text
|
||||
reason-nick-invalid = nick must be 1-30 bytes of a-z, 0-9 or hyphen
|
||||
reason-delete-needs-timestamp = Twt-Delete needs an RFC 3339 UTC timestamp
|
||||
reason-prune-mode-unknown = unknown prune mode `{ $got }`, want fifo or off
|
||||
|
||||
## Post rules, from core::rules::RuleError
|
||||
|
||||
reason-too-many-codepoints = more than 160 Unicode code points
|
||||
reason-too-many-bytes = more than 640 bytes
|
||||
reason-forbidden-char = contains TAB, LF, CR or U+2028
|
||||
reason-bad-timestamp = not an RFC 3339 UTC timestamp at second precision
|
||||
reason-duplicate-timestamp = timestamp already exists in this feed
|
||||
|
||||
## Retention and quota, from the apply step
|
||||
|
||||
reason-over-quota-post = daily post quota exceeded; try tomorrow
|
||||
reason-over-quota-repost = daily repost quota exceeded; try tomorrow
|
||||
reason-collision = timestamp { $timestamp } already exists; wait a second and resend
|
||||
reason-feed-full = feed is at its size cap; delete records with Twt-Delete or choose Twt-Prune: fifo
|
||||
reason-reply-to-changed = your Reply-To does not match the feed's bound author { $author }; send from that address or mail the operator
|
||||
reason-no-record-at = no record at { $timestamp }
|
||||
|
||||
## Mailbox requests — register, help and the admin commands
|
||||
|
||||
reason-one-command-per-mail = one command per mail: send Twt-Register, Twt-Help, Twt-Admit, Twt-Block or Twt-Registration on its own, and feed commands in a separate mail
|
||||
reason-bad-address = `{ $got }` is not a smolmail address
|
||||
reason-registration-mode-unknown = unknown registration mode `{ $got }`, want open, closed or silent
|
||||
|
||||
## First contact — an unknown sender who may register
|
||||
## First contact from someone who doesn't have a feed
|
||||
|
||||
info-subject = twtxt: how to get a feed
|
||||
info-body =
|
||||
You can publish a twtxt feed here by sending mail.
|
||||
|
||||
Ask for one, optionally naming the nick you want:
|
||||
To get one, send a message to this address with this header:
|
||||
|
||||
fumi send { $mailbox } --header "Twt-Register: yournick"
|
||||
Twt-Register: yournick
|
||||
|
||||
We will reply with your feed's address and the commands for managing it.
|
||||
Nothing is published until you ask, and this mail is the only one you get
|
||||
Headers sit next to the subject in most clients, often behind a button
|
||||
marked "+ header". Leave the value empty and we'll use your username.
|
||||
|
||||
Our reply arrives in your Requests folder, not your inbox, because you
|
||||
haven't accepted this address. We recommend leaving it that way.
|
||||
|
||||
We publish nothing until you ask, and this is the only mail you'll get
|
||||
until you do.
|
||||
|
||||
Our replies arrive in your requests tier, so look for them there:
|
||||
|
||||
fumi fetch && fumi list --requests
|
||||
|
||||
Do not accept this address in your client. If you do, it will attach an
|
||||
accept token to everything you send, and a published record cannot carry
|
||||
one and still be verifiable — so your posts would be refused.
|
||||
|
||||
## First contact while registration is closed
|
||||
|
||||
info-closed-subject = twtxt: registration is closed
|
||||
info-closed-subject = twtxt: we're not taking new feeds
|
||||
info-closed-body =
|
||||
Thanks for writing. This instance is not taking new feeds at the moment,
|
||||
so there is nothing for you to do here yet.
|
||||
Thanks for writing. We're not taking new feeds right now, so there's
|
||||
nothing to do here yet.
|
||||
|
||||
Nothing was published and nothing was kept.
|
||||
|
||||
## Twt-Help — the command reference
|
||||
## Twt-Help
|
||||
|
||||
help-subject = twtxt: your commands
|
||||
help-body =
|
||||
Your feed: { $url }
|
||||
|
||||
Post by sending mail to this address with the twt as the body. The body is
|
||||
published byte for byte, at most 160 characters.
|
||||
Send a message to this address and it becomes a post. We publish exactly
|
||||
what you write, up to 160 characters, on one line.
|
||||
|
||||
Management fields, one per mail:
|
||||
To manage your feed, add one of these headers. One per message.
|
||||
|
||||
Twt-Nick: yournick set the displayed nick
|
||||
Twt-Delete: <timestamp> remove one post or repost
|
||||
Twt-Repost: <mention> <#id> publish a repost
|
||||
Twt-Prune: fifo | off what a full feed does at its size cap
|
||||
Twt-Nick: yournick change the name shown on your feed
|
||||
Twt-Delete: <time> remove one post or repost
|
||||
Twt-Repost: <mention> <#id> share someone else's post
|
||||
Twt-Prune: fifo | off what happens when your feed fills up
|
||||
Twt-Help: send this list again
|
||||
|
||||
help-admin-section =
|
||||
|
||||
Admin, from your address:
|
||||
You're an admin here, so you can also send:
|
||||
|
||||
Twt-Admit: user@host give someone a feed
|
||||
Twt-Block: user@host withdraw someone's token
|
||||
Twt-Block: user@host stop someone posting
|
||||
Twt-Registration: open | closed | silent
|
||||
|
||||
## Admin notifications
|
||||
|
||||
admin-notify-subject = twtxt: new feed { $slug }
|
||||
admin-notify-body =
|
||||
A feed was created.
|
||||
Someone registered a feed.
|
||||
|
||||
Address: { $address }
|
||||
Feed: { $url }
|
||||
Fingerprint: { $fingerprint }
|
||||
|
||||
Verify that fingerprint out of band if you do not already know this
|
||||
author. Twt-Block withdraws their token.
|
||||
If you don't know this person, check that fingerprint with them some
|
||||
other way. Send Twt-Block to stop them posting.
|
||||
|
||||
## What an admin command did. These are sentences on their own: they become
|
||||
## the subject and open the body.
|
||||
|
||||
admin-admitted = Gave { $address } a feed at { $url }.
|
||||
admin-admit-failed = Couldn't give { $address } a feed. { $detail }
|
||||
admin-blocked = Blocked { $address }. Their posts won't go up from now on.
|
||||
admin-block-unknown = { $address } wasn't accepted here, so there was nothing to block.
|
||||
admin-block-failed = Couldn't block { $address }. { $detail }
|
||||
admin-block-half-done = Blocked { $address } here, but the server still has their token, so their posts may still go up. { $detail }
|
||||
admin-registration-open = Anyone can register a feed now.
|
||||
admin-registration-closed = Registration is closed. People who write in are told so.
|
||||
admin-registration-silent = Registration is closed and people who write in get no reply.
|
||||
|
||||
admin-ack-subject = twtxt: { $what }
|
||||
admin-ack-body =
|
||||
{ $what }
|
||||
|
||||
Sent because your address is listed as an admin of this instance.
|
||||
|
||||
reason-accept-field-on-post = your client attached an accept token because this address is in your contacts; a published record is verified by rebuilding the exact bytes you signed and cannot include a token, so withdraw the acceptance and resend
|
||||
You're getting this because you're an admin here.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue