tinymist/.github/copilot-instructions-l10n.md
Copilot 3699450369
feat: Add comprehensive Claude/Copilot localization instructions (#2076)
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>
2025-08-25 06:36:54 +08:00

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:

  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:

// 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:

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:

  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:

# 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

  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:
return Err(tinymist_l10n::t!(
    "compilation.invalid-syntax", 
    "Invalid syntax at line {line}", 
    line = line_number
).into());
  1. Extract messages:
yarn build:l10n
  1. 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)

  1. Add to TypeScript code:
const exportButton = vscode.window.createQuickPick();
exportButton.title = l10nMsg("Select Export Format");
  1. Extract messages:
yarn build:l10n
  1. 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

  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

// 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:
yarn build:l10n
  1. Verify locale files contain your new messages

  2. 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.