# Internationalization

SQL Catalog supports `en` and `zh-CN`. English is the fallback locale. The frontend and backend
read the same canonical message catalogs from `shared/i18n/locales`.

## Terminology

| Stable concept | English | Simplified Chinese |
| --- | --- | --- |
| Product | SQL Catalog | SQL Catalog |
| Workbench | Workbench | 工作台 |
| Catalog | Catalog | SQL 目录 |
| Catalog Item | Catalog Item | SQL 档案 |
| Finding | Finding | 异常发现 |
| Symptom | Symptom | 症状 |
| Treatment | Treatment | 治理疗程 |
| Record | Record | 治理记录 |
| Timeline | Timeline | 历程 |
| Organization | Organization | 组织 |
| Cluster Group | Cluster Group | 集群组 |

The brand remains `SQL Catalog` in both languages. The approved Chinese module label is always
`SQL 目录`.

## Translation Boundary

Translate product-owned presentation content:

- navigation, page labels, forms, filters, charts, Tooltips, dialogs, empty states, and accessibility text;
- built-in domain labels and built-in Symptom Registry names and descriptions;
- backend-generated API errors, Catalog Timeline projections, and audit Timeline summaries.

Keep source and user data unchanged:

- SQL text, digests, identifiers, source codes, metric keys, and raw payload fields;
- names, notes, Markdown, titles, evidence, and URLs entered by users or external systems;
- custom Symptom Registry entries. Only the six built-in symptom definitions are localized.

The database stores stable codes and source data. Backend Timeline text is localized while projecting
the response, so changing languages does not rewrite historical records.

## Frontend

Locale resolution order is:

1. `localStorage` preference;
2. browser language;
3. `en` fallback.

The user menu changes language globally without changing the current route, query parameters,
scope, or selected tab. Date, number, chart, Calendar, and time-zone labels use the active locale.
Language-dependent React Query entries include the locale in their query key.

## API Contract

Clients select a response language with `Accept-Language`:

```http
Accept-Language: zh-CN, en;q=0.8
```

The server responds with:

```http
Content-Language: zh-CN
Vary: Accept-Language
```

Unsupported languages fall back to English. Locale negotiation is request-scoped and has no mutable
process-wide language state, so concurrent English and Chinese requests cannot affect each other.

Errors retain a language-neutral code and return a localized message descriptor:

```json
{
  "error": {
    "code": "finding_not_found",
    "message": {
      "key": "api:errors.finding_not_found",
      "text": "未找到异常发现。"
    }
  }
}
```

Callers should branch on `error.code`, not `message.text`. `message.key` is stable and `message.text`
is ready for display. Structured domain codes in successful responses remain language-neutral.

## Catalog Layout

Namespaces are listed in `shared/i18n/manifest.json`:

- `common`: shell, actions, preferences, table, and time labels;
- `domain`: entity names and stable domain-code labels;
- `ui`: page and component presentation text;
- `api`: localized API error text;
- `timeline`: Timeline chrome and backend event projections;
- `metrics`: chart and metric presentation text.

Both locales must contain identical keys and interpolation placeholders. Validate catalogs with:

```bash
python3 scripts/check_i18n.py
```

When adding product-owned text, add the key to both locales in the appropriate namespace. Do not use
translated display strings as API values, database codes, query filters, or workflow state.
