Agent-First Mail v0.4: The Workspace Speaks Your Language

by Agent-First Kit Contributors

v0.4 localizes the part of afmail you actually read: every generated Markdown view and workspace scaffold now ships in nine fully-translated language packs, chosen by one config line — while the CLI, the wire format, and the one push boundary stay exactly where they were.

afmail is a workspace made of files you read. v0.1 drew the line — your agent reads and works in local files, and the real mailbox only changes on --confirm. v0.2 collapsed every queued effect into one all-or-nothing push. v0.3 gave the workspace a sense of who. All three share an assumption v0.4 finally drops: that the files you read are in English.

Reading in afmail has always meant open the Markdown — the triage view, the message view, the case scaffold, the draft. Those views are generated for a human to read over the agent’s shoulder. If that human thinks in Chinese or German or Japanese, an English-only view is friction at exactly the moment that matters: the review before the push. v0.4 translates that surface.

Nine packs, one config line

A workspace renders its views in whatever language you set in .afmail/config.json:

{
  "workspace": {
    "language_bcp47": "zh-TW"
  }
}

That’s the whole switch. Set it and the next afmail pull, triage list, or message show renders its views in that language. v0.4 ships nine fully-translated packs — English, German, Spanish, French, Japanese, Korean, Portuguese, Simplified Chinese, and Traditional Chinese — with no English prose left bleeding through: section headers, the SPF/DKIM/DMARC security notes, the related-messages guidance, the workspace AGENTS.md scaffold, all of it.

The value is set with a BCP-47 tag, so it resolves the way you’d expect — de, de-DE, and de-AT all land on the German pack. Chinese resolves by script, not just region: zh-Hant, zh-TW, zh-HK, and zh-MO get Traditional; bare zh and the Simplified markers get Simplified. Leave the line out and you get en-US, same as before.

Translated prose, stable structure

A localized view changes only what a person reads. Look at a triage view’s related-messages block — the guidance sentence and the column labels are Chinese, but the message IDs, the RFC header names, and the table shape are untouched:

## 相关消息

这些消息通过 RFC 头(`In-Reply-To` / `References`)关联。处置前请把它们明确加入同一个事项。

| 消息 | 方向 | 发件人 | 主题 | 时间 | 工作区状态 |
| --- | --- | --- | --- | --- | --- |

That line matters more than it looks. The locators your agent acts on — message IDs, case UIDs, frontmatter keys — are identical in every language, so switching packs never changes what the agent has to type. Localization is a property of the display, not the data.

And it stops at the display. The CLI’s own output — status counts, command results, error codes — stays English, because that’s the machine-stable seam the agent reasons against. v0.4 localizes what humans read, and deliberately leaves what the agent parses alone.

Add a language by dropping a directory

The packs are just trees of MiniJinja templates under templates/<language>/, keyed by view (triage/view.md.j2, message/section.md.j2, case/case.md.j2, …). A workspace can override any of them, and adding a tenth language is the same move as overriding one: drop a directory of templates next to the built-ins. Any key you don’t provide falls back to en-US, so a partial pack renders cleanly while you fill it in.

afmail render templates    # export the built-in templates to customize from

There’s no translation logic compiled into afmail — no display strings buried in Rust to chase down. A language lives entirely in its directory of Markdown, which is why a pack can be complete, corrected, or created without touching the binary.

Same boundary, now legible

None of this moves the line v0.1 drew. Localization changes how a view reads, never what gets sent or when. The push is still one all-or-nothing operation, the draft footer is still written into the body where you can see it before it leaves, and the agent still queues effects locally and asks you to push once. v0.4 just makes the thing you read before you say yes legible in the language you think in.

Install

brew install agentfirstkit/tap/afmail        # macOS / Linux
scoop bucket add agentfirstkit https://github.com/agentfirstkit/scoop-bucket
scoop install afmail                          # Windows
cargo install agent-first-mail                # any platform

After installing, run afmail skill install so your agent reloads afmail’s behavior rules.