Skip to content

Standard: Multilingual strings ​

Applies to ​

Every sentence a person sees. This covers the page (HTML and JS for the yard and the overlay), error and status messages the backend returns to the page, window titles and the tray menu, and file dialog titles. Supported languages are Korean (ko), English (en), Japanese (ja), and Simplified Chinese (zh-hans).

Not covered:

  • Sentences the app's CLI mode writes for the terminal and coding agents (usage, errors, MCP tool descriptions). These are fixed in English.
  • The template sentences of prompts sent to the AI (chat/prompt.rs). These are written in English, and the prompt sets the reply language.
  • Words sent by an agent or a person (say, AI replies). These are shown as they are, without translation.
  • Test code, comments, and developer docs (docs/), which are written in English. Dated design records and decision records stay as they are.

Rules ​

  • Page strings live in src/core/strings/<language>.js and backend strings in src-tauri/src/i18n/<language>.rs. The Korean file is the reference; other languages have the same keys in the same order. No other module or markup contains Korean sentences. The steps for adding a language are in Add a language.
  • Every language has the same keys, and the sentences for one key have the same placeholders ({name}). Lists contain nonempty localized lines with the same placeholders. Do not copy English sentences into another table to make a missing translation pass structural checks; review meaning and terminology in every language.
  • Every key the code calls (t('…'), list('…'), data-i18n, data-title, i18n::t("…")) exists in the table. A missing key shows the key name itself on screen.
  • Key names use lowercase and dots, with the area first (invite.open_folder, error.pet_missing). When the same sentence repeats, reuse the key.
  • Korean uses the friendly, polite style, English uses short sentences (only the first letter capitalized), and Japanese page strings use the です・ます style. Simplified Chinese uses plain, friendly 你 (never 您) with full-width punctuation. A friend's own spoken lines (speech bubbles, what it says on its own, at a new closeness level, about an event) are casual in every language: informal speech in Korean, casual speech in Japanese and Chinese, contractions in English. Everything else follows its language's UI tone above.
  • The English tables (en.js, en.rs) use American spelling (color, favorite).
  • In Korean, the particle after a name is attached to the placeholder (using {name:<consonant-form>/<vowel-form>}). The particle that matches whether the name ends in a final consonant is filled in automatically.
  • Coding agent status is sent as codes (@thinking, @tool Bash, @done). The app receives them and turns them into agent.* keys. When you add a new status, add the code and the key together.
  • Keep the terms below consistent. Their localized spellings are the corresponding keys in each language table; use those keys as the glossary instead of adding new synonyms. A named Mini pet is distinct from the mini window mode. Appearance changes never select a relationship.
TermMeaning
PetA character file (pet.json and the sprite sheet)
FriendSomeone invited into the yard
ImportBringing in pet files
InviteAdding a friend to the yard from a pet
AI connectionThe AI a friend uses to chat (Claude Code, Codex, Ollama, OpenAI-compatible)
Agent integrationHook and MCP settings for coding agents
Yard, mini mode, desktopThe main window, the small window mode, the desktop overlay
ClosenessHow close a friend has grown to the person, in five levels (Just met, At ease, Warm, Very close, Dearest)
RelationshipWho a friend is to the person (friend, partner, mentor, younger sibling), which sets how it talks
TagThe chasing game
Send awayTaking a friend out of the yard
TypeThe pet filter on the Invite tab (invite.kind_label)

This is how the page uses it.

js
import { t } from '../core/i18n.js';
// The Korean table supplies the particle form along with its localized sentence.
await confirmAction({ title: t('friend.send_away_confirm', { name: pet.name }) }); // "Send Frieren away from the yard?"
html
<button id="update-button" data-i18n="settings.update_check"></button>
<input data-i18n-placeholder="chat.placeholder">

This is how the backend uses it.

rust
return Err(i18n::tr("error.pets_limit", &[("limit", &LIMIT.to_string())]));

Recommendations ​

Follow these when you can. When you cannot, say why in the PR.

  • Spell out development terms (config, provider, sprite) in sentences people see.
  • Error messages say what failed and what the person can do (“Check your internet connection”).

Exceptions ​

WhenHow
Code that handles Korean itself, such as particle handling (src/core/words.js)Allowed. It is already in the guard test's exception list
Proper nouns and language names (the language choices in Settings)Allowed. They are not translated
You cannot fill every language at onceDo not leave them empty in English; split the PR. Tests block empty strings

Rationale ​

When strings are scattered through the code, they cannot be translated and the terms drift. Gathering them into two tables lets tests catch missing and mismatched entries. Agent hooks run in a different process from the app and do not know the app's language, so they send codes. Based on: decision “Strings live in two per-language tables, one for the page and one for the backend, and agent status is sent as codes”

Automated checks ​

  • tests/i18n.test.mjs — matching keys, placeholders and nonempty list lines across every language, English-only current developer guidance, Korean in page code and markup, comments included (only strings/ko.js, words.js and i18n.js may show it), and that the keys the page calls exist.
  • Rust i18n::tests — matching keys and placeholders across every language, Korean in every src/ module, comments included (test modules may use Korean input data), that the keys the backend calls exist, and agent code conversion.

Change history ​

  • 2026-10-01 First version
  • 2026-10-06 Simplified Chinese added; the guard tests also look at comments
  • 2026-10-10 Friends' own lines are casual in every language; American spelling in English; closeness, relationship, tag, send away and Type added to the terms

Free and open source under the MIT license. Not affiliated with OpenAI; Codex is a trademark of OpenAI.