Add a runtime i18n library, then convert the strings Copilot has already written, then keep the new ones out. The step most teams get wrong is the last one, because a repository instructions file does not reach every surface Copilot writes code from. Chat and the coding agent read it. Inline autocomplete does not. globalize.now is AI-powered localization infrastructure that converts the codebase once and produces the keys and locale files your i18n library reads.
Why does Copilot still hardcode strings after I add an instructions file?
Because the instructions file is not wired into the fastest path. VS Code's documentation states plainly that custom instructions are not taken into account for the inline suggestions you get as you type. That is the mode most people spend most of their day in.
So the rule you wrote is real, and it applies when you ask Copilot a question in chat or hand a task to the coding agent. It does not apply when you type a JSX tag and press Tab on a greyed-out completion. Both of those produce committed code. Only one of them has read your rule.
Which Copilot surfaces read your instructions?
Three surfaces write code into your repository, and they do not behave the same way.
| Surface | Reads instructions? | Where the output lands |
|---|---|---|
| Inline completions | No | Straight into the file you are editing |
| Chat and agent mode | Yes | Edits you review in the editor |
| Coding agent | Yes | A draft pull request you review |
The coding agent is the friendliest of the three for this problem. You assign it an issue, it works in the background and opens a draft pull request, so its output arrives somewhere a reviewer already looks. Inline completions arrive with no gate at all.
How do you set up i18n in a Copilot project?
Pick the runtime library your framework wants first. next-intl for the Next.js App Router, react-i18next for any other React app, Lingui if you want compile-time extraction and PO catalogs. We compared the three in next-intl vs react-i18next vs Lingui.
Copilot is not framework-specific the way Lovable or Bolt are, which means the stack question is genuinely open and the library choice is yours. What is not open is the order. Install the library, convert the strings that already exist, and only then worry about prevention. A prevention rule written over a codebase that is still 90 percent hardcoded English gives you nothing to measure.
What should go in .github/copilot-instructions.md?
A repository instructions file at that path is detected automatically and applied to chat requests in the workspace. GitHub's own guidance is to keep each instruction short and self-contained, to explain the reasoning, and to skip anything a linter already enforces. An i18n section that follows that advice is small:
## User-facing text
- User-facing text belongs in the message catalog, never in a component literal.
- Use the translation function from our i18n library for every visible string.
- Reason: a literal cannot be translated and does not show up as a missing key,
so it ships silently in one language.
- Add new English values to the source catalog only. Never hand-edit other locales.
- Not user-facing: test names, log lines, error codes, internal constants.The last line matters more than it looks. Rules that over-apply get ignored, and a rule that tells an agent to wrap log messages will teach it that the whole section is noise. If you have no instructions file at all yet, VS Code can draft one from your codebase with the init command in chat, which is a better starting point than a blank file.
Can a path-specific instructions file do better?
Yes, and this is the one lever Copilot has that editor rules files elsewhere do not. Files ending in .instructions.md, stored under .github/instructions, carry an applyTo glob in their frontmatter and load only when the agent touches a matching file.
---
name: 'UI strings'
description: 'String handling rules for components'
applyTo: '**/*.{jsx,tsx}'
---
Every visible string in this file must come from the catalog.
Key format is feature.element.state, lowerCamelCase.
If you add a key here, add its English value to the source catalog in the same change.Scoping the rule to component files is what keeps it credible. It fires where user-facing text lives and stays quiet in your API routes and migration scripts. If you already keep an AGENTS.md or a CLAUDE.md at the repository root, Copilot reads those as always-on instructions too, so a team running several agents does not need a separate file per tool.
What catches the strings instructions miss?
A lint rule in CI, because it is the only layer in this stack that fails a build. Instructions change the odds; a failing check changes the outcome. We worked through that enforcement layer in detail in why AI agents keep adding hardcoded strings, and the setup transfers unchanged to a Copilot repository.
The Copilot-specific addition is the pull request. Repository instructions apply to Copilot code review as well as chat, so the same file that shapes generation also shapes the review comments on the coding agent's draft PR. That gives you one gate that inline autocomplete cannot walk past, which is the closest thing to a fix for the gap at the top of this post.
None of this retires the debt already in the repository, and none of it fills a locale file. Catching a literal in review tells you a string is missing. Somebody still has to key it, translate it, and keep the other locales from drifting out of sync.
Where does globalize.now fit?
At the layer that produces the catalog, which is the part neither your i18n library nor your instructions file does. You connect the repository in the app and it converts the codebase once, replacing hardcoded strings with keys and generating the locale files. After that one-time conversion, push jobs translate the new catalog units your commits introduce.
Output is JSON or PO, so the catalogs land in the shape your runtime library already reads. The same approach covers the other agentic editors: Claude Code and Cursor hit the same wall from a different direction, and there is a step-by-step version for a Next.js codebase in adding i18n to an AI-built Next.js app. The developer docs cover the conversion in more detail, and plans are on the pricing page.
The short version
Write the instructions file, scope a path-specific rule to your component files, and put a lint rule in CI so the gap between what Copilot was told and what Copilot typed becomes a red build instead of a shipped English string. Then convert the backlog once, so the rule has something consistent to protect.
globalize.now turns hardcoded app copy into translation-ready locale files and keeps them updated as you ship.
Try globalize.now free