# Server-Side i18n

Read this when you need to add multi-language support to server-side code, translate API responses, or use translations in CLI commands.

## Code Templates

### Locale File Structure

Place translation files in `src/locale/`:

```
src/locale/
  en-US.json
  zh-CN.json
```

```json
// src/locale/en-US.json
{
  "Hello": "Hello",
  "Welcome {{name}}": "Welcome {{name}}",
  "Task created": "Task created"
}
```

```json
// src/locale/zh-CN.json
{
  "Hello": "你好",
  "Welcome {{name}}": "欢迎 {{name}}",
  "Task created": "任务已创建"
}
```

New locale files require an app restart to take effect. Verify at:
`http://localhost:13000/api/app:getLang?locale=zh-CN`

### this.t() — Plugin Instance Translation

```ts
export class MyPluginServer extends Plugin {
  async load() {
    this.app.resourceManager.registerActionHandlers({
      'myResource:greet': async (ctx, next) => {
        // this.t() auto-scopes to this plugin's namespace
        ctx.body = { message: this.t('Hello') };
        await next();
      },
    });
  }
}
```

### ctx.t() — Request Context Translation

Translates based on the client's locale (from `X-Locale` header or `?locale=` query param).

```ts
async handler(ctx, next) {
  // Auto-detects client language
  const msg = ctx.t('Hello');

  // With namespace (for another plugin's translations)
  const msg2 = ctx.t('Hello', { ns: '@my-project/plugin-hello' });

  // With template variables
  const msg3 = ctx.t('Welcome {{name}}', { name: 'John' });

  ctx.body = { message: msg };
  await next();
}
```

### app.i18n — Global i18n Instance

For CLI commands or contexts without a request:

```ts
async load() {
  this.app.command('my-command').action(async () => {
    await this.app.changeLanguage('zh-CN');
    console.log(this.app.i18n.t('Task created'));
  });
}
```

### Client Language Detection

The client locale is determined by (in priority order):
1. `X-Locale` request header (recommended)
2. `?locale=` query parameter

```
GET /api/test?locale=zh-CN
# or
GET /api/test
X-Locale: zh-CN
```

### Equivalence

`this.t('key')` is equivalent to `ctx.t('key', { ns: pluginNamespace })`.

## Deep Reference

- https://docs.nocobase.com/cn/plugin-development/server/i18n.md

## Related

- [./context.md](./context.md) — ctx.t() in request handlers
- [./plugin.md](./plugin.md) — this.t() in plugin class
- [../client/i18n.md](../client/i18n.md) — Client-side i18n (tExpr, useT)
