Adds comprehensive instructions for AI assistants (Claude/Copilot) to
work with the tinymist localization (l10n) system. The new
`.github/copilot-instructions-l10n.md` file provides detailed guidance
on both phases of the localization process:
## Marking and Extracting Messages
**Rust Backend (`crates/`):**
```rust
// Simple localized message
let message = tinymist_l10n::t!("error.file-not-found", "File not found");
// With parameters
let message = tinymist_l10n::t!(
"error.invalid-config",
"Invalid configuration: {key}",
key = config_key
);
```
**TypeScript VSCode Extension (`editors/vscode/`):**
```typescript
import { l10nMsg } from "../l10n";
// Simple message
const message = l10nMsg("Export as PDF");
// With parameters
const message = l10nMsg("Processing {count} files", { count: fileCount });
```
After adding localized messages, run `yarn build:l10n` to extract them
to TOML files.
## Translating Messages
The system uses TOML format designed for easy LLM modification:
```toml
[export.pdf.success]
en = "PDF exported successfully"
zh = "PDF 导出成功"
fr = "PDF exporté avec succès"
```
## Key Features
- **Complete workflow examples** for both Rust and TypeScript
- **Hierarchical key naming conventions** (e.g.,
`component.category.action`)
- **Translation guidelines** with parameter preservation
- **Best practices** for when to localize vs. when not to
- **Troubleshooting section** for common issues
- **File structure reference** showing the relationship between source
files and locale files
The instructions enable AI assistants to properly add new localizable
strings, update existing translations, and maintain the multilingual
support across the entire tinymist ecosystem.
Fixes #2075.
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com>
8.5 KiB
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:
- Marking and Extracting Messages: Adding localization calls in code and extracting them
- 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:
// 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.exportPdftinymist.config.badServerConfigtinymist-project.validate-error.root-path-not-absolute
TypeScript/VSCode Extension (editors/vscode/)
Adding Localized Messages
Use the l10nMsg function imported from ../l10n:
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:
import { l10nMsg } from "../l10n"; // Adjust path as needed
Extracting Messages
After adding localized messages to code, extract them using:
yarn build:l10n
This command:
- Scans Rust and TypeScript files for localization calls
- Extracts messages to TOML files in
locales/:locales/tinymist-rt.toml(Rust messages)locales/tinymist-vscode-rt.toml(TypeScript messages)
- 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:
# 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
[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
[extension.tinymist.command.tinymist.restartServer]
en = "Restart server"
zh = "重启服务器"
es = "Reiniciar servidor" # New Spanish translation
de = "Server neu starten" # New German translation
Translation Guidelines
- Preserve Parameters: Keep parameter placeholders like
{key},{count},{value} - Maintain Formatting: Preserve newlines and special characters
- Context Awareness: Consider the UI context where the message appears
- Consistency: Use consistent terminology across related messages
Language Codes
Use standard language codes:
en- English (required, default)zh- Simplified Chinesezh-TW- Traditional Chinesefr- Frenchde- Germanes- Spanishja- Japaneseru- Russian
Examples
Example 1: Adding a New Error Message (Rust)
- Add to Rust code:
return Err(tinymist_l10n::t!(
"compilation.invalid-syntax",
"Invalid syntax at line {line}",
line = line_number
).into());
- Extract messages:
yarn build:l10n
- Add translations in
locales/tinymist-rt.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)
- Add to TypeScript code:
const exportButton = vscode.window.createQuickPick();
exportButton.title = l10nMsg("Select Export Format");
- Extract messages:
yarn build:l10n
- Add translations in
locales/tinymist-vscode-rt.toml:
["Select Export Format"]
en = "Select Export Format"
zh = "选择导出格式"
fr = "Sélectionner le format d'exportation"
Example 3: Complex Message with Multiple Parameters
let message = tinymist_l10n::t!(
"preview.compilation-stats",
"Compiled {pages} pages in {duration}ms",
pages = page_count,
duration = compile_time
);
Corresponding translation:
[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
- Keep messages concise but informative
- Use parameters for dynamic content instead of string concatenation
- Design for translation - avoid culture-specific references
- Group related messages using consistent key prefixes
Key Naming
// 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
-
Build errors after adding localization:
- Ensure proper import of
tinymist_l10norl10nMsg - Check for typos in macro/function calls
- Run
yarn build:l10nto extract new messages
- Ensure proper import of
-
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
-
Parameter substitution not working:
- Verify parameter names match between code and translation
- Check for missing or extra parameters in translations
Testing Localization
- Test message extraction:
yarn build:l10n
-
Verify locale files contain your new messages
-
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
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:
let message = tinymist_l10n::t!(
"files.count",
"{count} file(s) processed",
count = file_count
);
Translation:
[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.