GitHub Copilot for i18n: Setting Up Translation Workflows in VS Code
Learn how to configure GitHub Copilot for internationalization tasks. Create custom instructions, optimize prompts, and integrate with translation management systems.
My Copilot went from annoying to indispensable
Six months ago, I was ready to turn off Copilot for anything i18n-related. It kept suggesting hardcoded strings when I wanted translation keys. It autocompleted "Loading..." when I wanted t('common.loading'). It didn't understand our key naming conventions. Frustrating.
Then I discovered Copilot's custom instructions feature. Spent an evening configuring it properly, and now it's the opposite—Copilot actually helps with localization instead of fighting against it.
Here's the setup that transformed my workflow.
The problem with default Copilot behavior
Out of the box, Copilot doesn't know that you want i18n keys instead of hardcoded strings, your key naming conventions, which translation library you're using, or that certain text shouldn't be translated (logs, dev messages).
So it suggests what most code on GitHub does—hardcoded English strings everywhere.
The solution is teaching Copilot about YOUR project's i18n approach.
Setting up Copilot custom instructions
VS Code Copilot reads instructions from .github/copilot-instructions.md. Create this file in your project root.
Start with a section on internationalization. Explain that this project uses react-i18next for all user-facing text and should NEVER use hardcoded strings.
When writing user-facing text, use the t() function from useTranslation(). Use the Trans component for complex interpolation. Follow the key format namespace.section.element.
Give examples: for buttons use t('common.buttons.save') not "Save". For titles use t('settings.profile.title') not "Profile Settings". For messages use t('errors.network.timeout') not "Connection timed out".
Specify what NOT to translate: console.log messages, error messages in catch blocks (for debugging), CSS class names, technical identifiers, and dev environment labels.
List your namespaces: common for shared UI elements (buttons, labels), errors for error messages, validation for form validation messages, auth for authentication related, settings for the settings page, and feature-specific namespaces.
After adding this file, Copilot's suggestions shift dramatically. It starts proposing translation keys instead of strings.
Making Copilot understand your translation library
Different projects use different i18n libraries. Customize your instructions accordingly.
For react-i18next, document the import pattern: import useTranslation from 'react-i18next', then destructure t from useTranslation('namespace'). For components with HTML, use Trans from react-i18next with an i18nKey and components prop. For plurals, use t('items.count', { count: items.length }).
For next-intl, in Server Components import getTranslations from 'next-intl/server' and await it. In Client Components, import useTranslations from 'next-intl'. Note that messages are in /messages/{locale}.json.
For Vue I18n, in templates use {{ $t('key.path') }}. In setup, destructure t from useI18n() and call t('key.path').
Teaching Copilot your key naming conventions
This is where the magic happens. Be specific about how keys should be named.
Document the structure: keys follow namespace.section.element.
Document patterns: page titles use {page}.title (like "settings.title"), section headers use {page}.{section}.heading (like "settings.profile.heading"), buttons use {namespace}.buttons.{action} (like "common.buttons.save"), form labels use {page}.form.{field}.label (like "auth.form.email.label"), form placeholders use {page}.form.{field}.placeholder, error messages use errors.{category}.{type} (like "errors.validation.required"), and success messages use messages.success.{action} (like "messages.success.saved").
Document naming rules: use camelCase for multi-word keys, be descriptive (deleteConfirmation not msg1), avoid abbreviations (numberOfItems not numItems), and group related keys (all form fields under .form.).
With these patterns, when you type t(' after a button, Copilot suggests common.buttons. as a prefix.
Workspace-specific configurations
For larger projects, you might want different settings per workspace.
In .vscode/settings.json, you can set github.copilot.chat.localeOverride to "en-US" and configure which file types have Copilot enabled.
You can also create workspace-specific instruction files that extend the main one.
Prompts that work well for i18n tasks
Beyond passive suggestions, Copilot Chat is excellent for active i18n work. Here are prompts I use regularly:
Extracting strings: "Extract all hardcoded strings from this file to translation keys. Use the settings namespace. Update the JSX to use t() and add the keys to my en.json file."
Creating translation files: "Create a translation file for Spanish (es) based on my English file. Translate UI strings naturally, keep technical terms, maintain placeholder syntax like {{name}}."
Finding hardcoded strings: "Find all hardcoded user-facing strings in this component that should be internationalized. Don't include console.log or error messages for debugging."
Generating missing keys: "Look at my component and compare to my translation file. What keys am I using that don't exist in the translation file?"
Reviewing translations: "Review these Spanish translations for: natural language flow, proper plural handling, consistent terminology, and any UI-breaking length issues."
Integrating with translation management systems
Copilot can't directly call APIs (without MCP-enabled tools), but you can still streamline the workflow.
Option 1: Generate TMS-ready JSON. Add to your instructions that when generating new translation keys, Claude should also create a TMS import file in JSON format with key, value, context (for translators), and maxLength fields.
Option 2: CLI integration. Document in your instructions that after adding new keys, the developer should run intlpull upload to push new keys to TMS and intlpull download to pull latest translations.
Option 3: Use Copilot extensions. GitHub is rolling out Copilot extensions that can call external APIs. Watch for TMS integrations becoming available—this will enable true end-to-end automation.
Common Copilot i18n mistakes and fixes
Mistake 1: Suggesting wrong library syntax. If Copilot keeps suggesting i18n.t() when you use t(), add to instructions: "NEVER use: i18n.t(), i18next.t(), this.$t(). ALWAYS use: t() from useTranslation() hook."
Mistake 2: Forgetting namespace import. If components miss the useTranslation import, add: "Every component with translations must have: import { useTranslation } from 'react-i18next'; and const { t } = useTranslation('namespace'); inside the component."
Mistake 3: Inconsistent key casing. Add: "Key naming MUST be consistent. CORRECT: settings.profileTitle (camelCase). WRONG: settings.profile_title (snake_case). WRONG: settings.profile-title (kebab-case)."
Mistake 4: Translating technical content. Add: "Do NOT translate: variable names, API endpoints, log messages (unless user-facing), class names, ID attributes."
Advanced: Workspace agents with Copilot
VS Code's Copilot Chat supports the @workspace agent that can search your codebase. Use it for i18n audits:
"@workspace find all files with hardcoded strings that should use translation keys"
"@workspace show me all t() calls that reference keys not in our translation files"
"@workspace what namespaces are used in the settings feature?"
The @workspace agent has access to your project context, making these queries accurate.
Comparison: Copilot vs Claude Code vs Cursor for i18n
Having used all three extensively:
| Feature | GitHub Copilot | Claude Code | Cursor |
|---|---|---|---|
| Inline suggestions | Excellent | Good | Excellent |
| Custom instructions | Good | Excellent | Excellent |
| MCP support | Coming soon | Native | Native |
| TMS integration | Manual | Via MCP | Via MCP |
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| Inline suggestions | Excellent | Good | Excellent |
|---|---|---|---|
| Custom instructions | Good | Excellent | Excellent |
| MCP support | Coming soon | Native | Native |
| TMS integration | Manual | Via MCP | Via MCP |
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| Inline suggestions | Excellent | Good | Excellent |
|---|---|---|---|
| Custom instructions | Good | Excellent | Excellent |
| MCP support | Coming soon | Native | Native |
| TMS integration | Manual | Via MCP | Via MCP |
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| Custom instructions | Good | Excellent | Excellent |
|---|---|---|---|
| MCP support | Coming soon | Native | Native |
| TMS integration | Manual | Via MCP | Via MCP |
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| MCP support | Coming soon | Native | Native |
|---|---|---|---|
| TMS integration | Manual | Via MCP | Via MCP |
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| TMS integration | Manual | Via MCP | Via MCP |
|---|---|---|---|
| Multi-file edits | Limited | Native | Native |
| VS Code integration | Native | CLI | Separate app |
| Multi-file edits | Limited | Native | Native |
|---|---|---|---|
| VS Code integration | Native | CLI | Separate app |
| VS Code integration | Native | CLI | Separate app |
|---|
My take: Use Copilot if you're already in VS Code and want lightweight assistance. Use Claude Code if you need autonomous multi-step workflows. Use Cursor if you want Copilot-style UX with MCP capabilities.
They're not mutually exclusive—I use Copilot for quick suggestions and Claude Code for complex operations.
The instruction file that works
After much iteration, here's my complete copilot-instructions.md structure:
Start with the framework: library is react-i18next v14+, file format is JSON with nested structure, location is /public/locales/{lang}/{namespace}.json.
Document translation function usage: import useTranslation from react-i18next, destructure t from useTranslation('namespace'), and use t() in JSX.
Document key naming: namespace.section.element format. Pages use page name as namespace (settings, dashboard, auth). Shared elements use "common" namespace. Errors use "errors" namespace. Validation uses "validation" namespace.
List rules: NEVER use hardcoded user-facing strings, ALWAYS use t() for any text users see, ALWAYS include namespace in useTranslation(), ALWAYS use camelCase for keys, ALWAYS add context descriptions for ambiguous text.
Document key patterns for buttons, labels, placeholders, titles, messages, and errors.
Document plurals: use ICU format with t('items.count', { count: n }). In the JSON, use count_one for singular and count_other for plural.
Add CLI reminder: after adding keys, run intlpull upload.
Getting started
The investment is maybe 30 minutes to set up. The payoff is Copilot that actually helps with i18n instead of working against you.
---
*Using IntlPull? Add our CLI commands to your Copilot instructions. After Copilot helps you write translation keys, one command syncs them to your TMS.*