# Localization Instructions for Claude/Copilot

This document provides comprehensive guidance for adding, updating, and maintaining localization (l10n) in the Tinymist project.

## Overview

Tinymist's localization system supports multiple languages across two main components:
- **Rust backend** (`crates/`): Language server functionality 
- **VSCode extension** (`editors/vscode/`): Editor integration

The localization process has two main phases:
1. **Marking and Extracting Messages**: Adding localization calls in code and extracting them
2. **Translating Messages**: Adding translations to locale files

## Phase 1: Marking and Extracting Messages

### Rust Backend (`crates/`)

#### Adding Localized Messages

Use the `tinymist_l10n::t!` macro with a key and default English message:

```rust
// Simple message
let message = tinymist_l10n::t!("error.file-not-found", "File not found");

// Message with parameters
let message = tinymist_l10n::t!(
    "error.invalid-config", 
    "Invalid configuration: {key}", 
    key = config_key
);
```

**Note**: Use the full `tinymist_l10n::t!` path - no need to import the macro separately.

#### Key Naming Convention

Use hierarchical dot-separated keys:
- `component.category.specific-action`
- Examples:
  - `tinymist-query.code-action.exportPdf`
  - `tinymist.config.badServerConfig`
  - `tinymist-project.validate-error.root-path-not-absolute`

### TypeScript/VSCode Extension (`editors/vscode/`)

#### Adding Localized Messages

Use the `l10nMsg` function imported from `../l10n`:

```typescript
import { l10nMsg } from "../l10n";

// Simple message
const message = l10nMsg("Export as PDF");

// Message with parameters
const message = l10nMsg("Processing {count} files", { count: fileCount });
```

#### Import Pattern

Always import `l10nMsg` from the relative path to `l10n.ts`:

```typescript
import { l10nMsg } from "../l10n";  // Adjust path as needed
```

### Extracting Messages

After adding localized messages to code, extract them using:

```bash
yarn build:l10n
```

This command:
1. Scans Rust and TypeScript files for localization calls
2. Extracts messages to TOML files in `locales/`:
   - `locales/tinymist-rt.toml` (Rust messages)
   - `locales/tinymist-vscode-rt.toml` (TypeScript messages)
3. Updates existing translations while preserving manual edits

#### When to Run Extraction

- **Required**: After adding new localized messages to code
- **Not needed**: When only editing translations in `locales/` files

## Phase 2: Translating Messages

### Locale File Format

Locale files use TOML format designed for easy modification by LLMs:

```toml
# The translations are partially generated by copilot

[key.name]
en = "English message"
zh = "Chinese translation"
zh-TW = "Traditional Chinese translation"
fr = "French translation"
```

### Available Locale Files

- `locales/tinymist-vscode.toml` - VSCode extension UI (manual translations)
- `locales/tinymist-vscode-rt.toml` - VSCode extension runtime (auto-extracted)
- `locales/tinymist-rt.toml` - Rust backend runtime (auto-extracted)

### Adding Translations

#### Example: Adding a French Translation

```toml
[extension.tinymist.command.tinymist.exportCurrentPdf]
en = "Export the Opened File as PDF"
zh = "将当前打开的文件导出为 PDF"
fr = "Exporter le fichier ouvert en PDF"  # Add this line
```

#### Example: Adding Support for a New Language

```toml
[extension.tinymist.command.tinymist.restartServer]
en = "Restart server"
zh = "重启服务器"
es = "Reiniciar servidor"  # New Spanish translation
de = "Server neu starten"  # New German translation
```

### Translation Guidelines

1. **Preserve Parameters**: Keep parameter placeholders like `{key}`, `{count}`, `{value}`
2. **Maintain Formatting**: Preserve newlines and special characters
3. **Context Awareness**: Consider the UI context where the message appears
4. **Consistency**: Use consistent terminology across related messages

### Language Codes

Use standard language codes:
- `en` - English (required, default)
- `zh` - Simplified Chinese
- `zh-TW` - Traditional Chinese
- `fr` - French
- `de` - German
- `es` - Spanish
- `ja` - Japanese
- `ru` - Russian

## Examples

### Example 1: Adding a New Error Message (Rust)

1. **Add to Rust code**:
```rust
return Err(tinymist_l10n::t!(
    "compilation.invalid-syntax", 
    "Invalid syntax at line {line}", 
    line = line_number
).into());
```

2. **Extract messages**:
```bash
yarn build:l10n
```

3. **Add translations** in `locales/tinymist-rt.toml`:
```toml
[compilation.invalid-syntax]
en = "Invalid syntax at line {line}"
zh = "第 {line} 行语法错误"
fr = "Syntaxe invalide à la ligne {line}"
```

### Example 2: Adding a New UI Label (TypeScript)

1. **Add to TypeScript code**:
```typescript
const exportButton = vscode.window.createQuickPick();
exportButton.title = l10nMsg("Select Export Format");
```

2. **Extract messages**:
```bash
yarn build:l10n
```

3. **Add translations** in `locales/tinymist-vscode-rt.toml`:
```toml
["Select Export Format"]
en = "Select Export Format"
zh = "选择导出格式"
fr = "Sélectionner le format d'exportation"
```

### Example 3: Complex Message with Multiple Parameters

```rust
let message = tinymist_l10n::t!(
    "preview.compilation-stats",
    "Compiled {pages} pages in {duration}ms",
    pages = page_count,
    duration = compile_time
);
```

Corresponding translation:
```toml
[preview.compilation-stats]
en = "Compiled {pages} pages in {duration}ms"
zh = "在 {duration} 毫秒内编译了 {pages} 页"
fr = "Compilé {pages} pages en {duration}ms"
```

## Best Practices

### When to Localize

**DO localize:**
- User-facing error messages
- UI labels and buttons
- Status messages
- Command descriptions
- Configuration descriptions

**DON'T localize:**
- Log messages for developers
- Debug output
- Technical error codes
- File paths or URLs
- Code identifiers

### Message Design

1. **Keep messages concise** but informative
2. **Use parameters** for dynamic content instead of string concatenation
3. **Design for translation** - avoid culture-specific references
4. **Group related messages** using consistent key prefixes

### Key Naming

```rust
// Good: Hierarchical, descriptive
tinymist_l10n::t!("export.pdf.success", "PDF exported successfully")
tinymist_l10n::t!("export.pdf.error.permission", "Permission denied for PDF export")

// Bad: Flat, unclear
tinymist_l10n::t!("msg1", "PDF exported successfully")
tinymist_l10n::t!("err", "Permission denied")
```

## Troubleshooting

### Common Issues

1. **Build errors after adding localization**:
   - Ensure proper import of `tinymist_l10n` or `l10nMsg`
   - Check for typos in macro/function calls
   - Run `yarn build:l10n` to extract new messages

2. **Missing translations**:
   - Verify the key exists in the appropriate locale file
   - Check if extraction was run after adding the message
   - Ensure locale file syntax is valid TOML

3. **Parameter substitution not working**:
   - Verify parameter names match between code and translation
   - Check for missing or extra parameters in translations

### Testing Localization

1. **Test message extraction**:
```bash
yarn build:l10n
```

2. **Verify locale files** contain your new messages

3. **Test in different languages** by changing VSCode language settings

## File Structure Reference

```
locales/
├── README.md                    # Documentation
├── tinymist-vscode.toml        # Manual VSCode translations
├── tinymist-vscode-rt.toml     # Auto-extracted VSCode messages
└── tinymist-rt.toml            # Auto-extracted Rust messages

crates/
├── tinymist-l10n/              # Localization library
└── */src/**/*.rs               # Rust source files (use tinymist_l10n::t!)

editors/vscode/
├── src/l10n.ts                 # TypeScript l10n helper
└── src/**/*.ts                 # TypeScript source files (use l10nMsg)

scripts/
└── build-l10n.mjs              # Message extraction script
```

## Advanced Usage

### Conditional Messages

```rust
let message = if is_error {
    tinymist_l10n::t!("status.error", "Error occurred")
} else {
    tinymist_l10n::t!("status.success", "Operation completed")
};
```

### Pluralization

Handle plurals with parameters:

```rust
let message = tinymist_l10n::t!(
    "files.count", 
    "{count} file(s) processed",
    count = file_count
);
```

Translation:
```toml
[files.count]
en = "{count} file(s) processed"
zh = "已处理 {count} 个文件"
fr = "{count} fichier(s) traité(s)"
```

Remember to run `yarn build:l10n` after adding any new localized messages to code, and only edit translation files manually for languages other than English.