Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,39 @@ yarn dev:all # Include all optional services
- Single source: `ghost/i18n/locales/{locale}/{namespace}.json`
- Namespaces: `ghost`, `portal`, `signup-form`, `comments`, `search`
- 60+ supported locales
- Context descriptions: `ghost/i18n/locales/context.json` — every key must have a non-empty description

**Translation Workflow:**
```bash
yarn workspace @tryghost/i18n translate # Extract keys from source, update all locale files + context.json
yarn workspace @tryghost/i18n lint:translations # Validate interpolation variables across locales
```

`yarn translate` is run as part of `yarn workspace @tryghost/i18n test`. In CI, it fails if translation keys or `context.json` are out of date (`failOnUpdate: process.env.CI`). Always run `yarn translate` after adding or changing `t()` calls.

**Rules for Translation Keys:**
1. **Never split sentences across multiple `t()` calls.** Translators cannot reorder words across separate keys. Instead, use `@doist/react-interpolate` to embed React elements (links, bold, etc.) within a single translatable string.
2. **Always provide context descriptions.** When adding a new key, add a description in `context.json` explaining where the string appears and what it does. CI will reject empty descriptions.
3. **Use interpolation for dynamic values.** Ghost uses `{variable}` syntax: `t('Welcome back, {name}!', {name: firstname})`
Comment on lines +164 to +169
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify CI enforcement wording for empty context descriptions.

The docs state CI rejects empty descriptions, but the referenced enforcement shown in ghost/i18n/generate-context.js only fails when context.json is out of date, not when values are empty. Consider either (a) tightening wording to “required by convention” or (b) adding explicit validation logic for non-empty descriptions and then documenting that behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 164 - 169, Update the documentation and enforcement
so they match: either relax the AGENTS.md wording to say non-empty context
descriptions are "required by convention" or (recommended) add explicit
validation in generate-context.js to fail CI when any context.json entry has an
empty description; implement a check (e.g., in the function that reads/generates
context.json) that iterates entries, detects empty or whitespace-only
descriptions, logs a descriptive error and exits non-zero (process.exit(1)), and
then update AGENTS.md to state that CI will fail on empty descriptions. Include
references to context.json and the generate-context.js validation function so
reviewers can find the change.

4. **Use `<tag>` syntax for inline elements.** Combined with `@doist/react-interpolate`: `t('Click <a>here</a> to retry')` with `mapping={{ a: <a href="..." /> }}`

**Correct pattern (using Interpolate):**
```jsx
import Interpolate from '@doist/react-interpolate';

<Interpolate
mapping={{ a: <a href={link} /> }}
string={t('Could not sign in. <a>Click here to retry</a>')}
/>
```

**Incorrect pattern (split sentences):**
```jsx
// BAD: translators cannot reorder "Click here to retry" relative to the first sentence
{t('Could not sign in.')} <a href={link}>{t('Click here to retry')}</a>
```

See `apps/portal/src/components/pages/email-receiving-faq.js` for a canonical example of correct `Interpolate` usage.

### Build Dependencies (Nx)

Expand Down
Loading