Skip to content

Migrate localization from vscode-nls to vscode.l10n#14550

Draft
sean-mcmanus wants to merge 5 commits into
mainfrom
seanmcm/updateVscodeLoc
Draft

Migrate localization from vscode-nls to vscode.l10n#14550
sean-mcmanus wants to merge 5 commits into
mainfrom
seanmcm/updateVscodeLoc

Conversation

@sean-mcmanus

@sean-mcmanus sean-mcmanus commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Fix by Copilot with Claude Opus 4.8 in VS Code (with review fixes from GPT-5.5).

Fixes #13145 and #14549 .

Why

Profiling an unresponsive extension-host session showed a single synchronous call — loadNlsBundle (vscode-nls reading the standalone NLS bundle from disk during module require) — blocking the extension host for ~897 ms at activation, about 90% of the captured CPU profile. VS Code's built-in vscode.l10n API loads the localization bundle itself (declared via the l10n field in package.json), so strings are no longer read synchronously on the activation path.

What changed

  • Replaced all 384 localize(...) call sites across 38 source files with l10n.t(...) (via const l10n = vscode.l10n;, or a named l10n import in files that already import named symbols from vscode), dropping the explicit string keys (the English message is now the key). The { key, comment } form becomes { message, args, comment }.
  • Removed the per-file vscode-nls header (nls.config(...) + loadMessageBundle()); ensured import * as vscode from 'vscode'.
  • Updated the native-strings generator (generateNativeStrings.ts) to emit l10n.t, and regenerated nativeStrings.ts / localized_string_ids.h.
  • package.json: added "l10n": "./l10n", swapped vscode-nls-dev@vscode/l10n-dev, removed the runtime vscode-nls dependency.
  • webpack.config.js: removed the vscode-nls-dev webpack loader (VS Code loads bundles at runtime now).
  • gulpfile.js: rewrote translations-export / -import / -generate onto @vscode/l10n-dev (English bundle → l10n/bundle.l10n.json, per-language → l10n/bundle.l10n.<lang>.json), preserving the custom HTML / JSON-schema / walkthrough / package.nls flows.
  • Migrated existing translations: the 13 language bundles were rebuilt from the legacy nls-keyed i18n/*/src/**.i18n.json into i18n/<lang>/bundle.l10n.json (743 strings each), keyed by the English message — or by message/comment for the 81 strings that carry a translator comment, matching how @vscode/l10n-dev keys the bundle so those strings resolve in every locale.
  • Updated CONTRIBUTING.md and the prep check; .gitignore now ignores the generated l10n/bundle.l10n*.json.

Verification

  • tsc --noEmit clean; eslint src .scripts clean; node --check on gulpfile + webpack.
  • Three call sites needed String(...) coercion because l10n.t types args as string | number | boolean (stricter than nls's any); this preserves nls's formatting behavior.

Localization call sites must be a literal l10n.t / vscode.l10n.t

@vscode/l10n-dev extracts strings at build time by statically scanning source for l10n.t(...) / vscode.l10n.t(...) calls — it does not execute code or follow aliases. An alias like const localize = vscode.l10n.t compiles and works at runtime, but the extractor never sees localize(...), so those strings are silently dropped from bundle.l10n.json and render in English in every locale. That is why each file calls l10n.t directly via const l10n = vscode.l10n; rather than a custom-named alias.

Notes

  • The legacy i18n/*/src/**.i18n.json files are superseded for source strings and are safe to delete once a loc cycle validates the new bundles.

Migration approach

  • Source call sites were converted with an AST-based codemod (TypeScript compiler API) that
    located every localize(...) call and spliced in the equivalent l10n call, preserving
    surrounding formatting, argument expressions, and message/escape sequences:
    • localize('key', 'Message {0}', a)l10n.t('Message {0}', a)
    • localize({ key, comment: [...] }, 'Message {0}', a)
      l10n.t({ message: 'Message {0}', args: [a], comment: [...] })
  • Placeholders remain {0}-style, so message text is unchanged across the migration.

Call-site form (l10n.t)

Every call is a literal l10n.t(...):

  • Files that already import named symbols from vscode merge l10n into that named import
    (e.g. targetNodes.ts, cppBuildTaskProvider.ts).
  • All other files keep import * as vscode from 'vscode' and add const l10n = vscode.l10n;.
    @vscode/l10n-dev extracts strings by a static AST scan of l10n.t(...) / vscode.l10n.t(...)
    calls; it does not follow aliases, so a custom-named alias (const localize = vscode.l10n.t)
    would compile and run but silently drop those strings from the bundle — hence the literal l10n.t.

Translation recovery

The existing translations were keyed by the old vscode-nls string IDs and would otherwise be
orphaned (the new model keys by the English string). A one-time remap joined the pre-migration
key → English map (from git HEAD, plus nativeStrings.json for the generated file) with the
legacy key → translation files, producing English-keyed i18n/<lang>/bundle.l10n.json:

  • 13 languages: chs, cht, csy, deu, esn, fra, ita, jpn, kor, plk, ptb, rus, trk
  • 743 unique entries per language (763 mapped, ~20 identical-English strings deduped)
  • 4 stale keys (debugger.noDebug.*.not.supported) were dropped — verified absent from source,
    so never looked up at runtime.
  • The per-language bundles were then re-keyed to match @vscode/l10n-dev's scheme: plain strings
    stay keyed by the English message, while the 81 strings that carry a translator comment are
    keyed message/comment. The initial remap had keyed everything by message only, which would
    have made commented strings fall back to English in every locale; all 13 bundles were rebuilt so
    every key matches the generated English bundle.

At build time generateSrcLocBundle copies i18n/<folder>/bundle.l10n.json to
l10n/bundle.l10n.<lang>.json (e.g. chszh-cn).

Type-coercion fixes

vscode.l10n.t types args as string | number | boolean, stricter than vscode-nls's any.
Two args were undefined-typed under the new signature and one file had a pre-existing duplicate
vscode import surfaced by the change:

  • common.ts: String(exitCode)
  • LanguageServer/configurations.ts: String(dotConfigPath)
  • Debugger/debugAdapterDescriptorFactory.ts: removed a duplicate import * as vscode
    String(...) reproduces vscode-nls's internal formatting exactly.

Other fixes surfaced in review

  • Corrected a user-facing typo ProduceVersionProductVersion (the literal
    SystemVersion.plist field name) in platform.ts and in both the key and value of all 13
    recovered bundles.
  • Closed unmatched single quotes around interpolated paths in the .scripts/common.ts prep
    warnings.

Follow-ups / validation

  • Run yarn prep (invokes translations-generate) and confirm l10n/bundle.l10n.*.json are
    produced; verify a non-English VS Code display language shows the recovered strings.
  • The XLF round-trip (translations-export / -import) now targets @vscode/l10n-dev; validate
    it against the MLCP loc pipeline on the next cycle.
  • After the new bundles are validated, the legacy i18n/*/src/**.i18n.json files can be deleted.

@github-project-automation github-project-automation Bot moved this to Pull Request in cpptools Jun 26, 2026
@sean-mcmanus sean-mcmanus requested a review from Copilot June 26, 2026 20:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request migrates the extension’s localization pipeline from vscode-nls / vscode-nls-dev to VS Code’s built-in vscode.l10n runtime API plus @vscode/l10n-dev for extraction/build, to remove synchronous bundle loading from the activation path.

Changes:

  • Replaced localize(...) usage and per-file vscode-nls setup with vscode.l10n.t(...) across the extension.
  • Switched localization build/export/import tooling from vscode-nls-dev to @vscode/l10n-dev, and updated webpack/package configuration accordingly.
  • Updated contributor documentation and prep checks to reflect the new l10n/ bundle flow.

Reviewed changes

Copilot reviewed 21 out of 60 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Extension/yarn.lock Adds @vscode/l10n-dev and its dependency graph; removes vscode-nls(-dev) entries.
Extension/webpack.config.js Removes vscode-nls-dev webpack loader and env-gated rewriting.
Extension/src/SSH/TargetsView/targetNodes.ts Migrates SSH targets view strings to vscode.l10n.t.
Extension/src/SSH/TargetsView/sshTargetsProvider.ts Migrates SSH targets provider UI/errors to vscode.l10n.t.
Extension/src/SSH/sshHosts.ts Migrates SSH host/config logging strings to vscode.l10n.t.
Extension/src/SSH/sshCommandRunner.ts Migrates SSH command runner prompts/logging strings to vscode.l10n.t.
Extension/src/SSH/commands.ts Migrates SSH validation errors to vscode.l10n.t.
Extension/src/SSH/commandInteractors.ts Migrates SSH interactor error messages to vscode.l10n.t.
Extension/src/platform.ts Migrates platform detection errors to vscode.l10n.t.
Extension/src/main.ts Migrates activation-time UI/log strings to vscode.l10n.t.
Extension/src/logger.ts Migrates output channel titles and crash guidance text to vscode.l10n.t.
Extension/src/LanguageServer/ui.ts Migrates status bar/language status strings to vscode.l10n.t.
Extension/src/LanguageServer/settingsPanel.ts Migrates settings panel title to vscode.l10n.t.
Extension/src/LanguageServer/settings.ts Migrates user-facing settings notices to vscode.l10n.t.
Extension/src/LanguageServer/referencesTreeDataProvider.ts Migrates “Go to reference” command title to vscode.l10n.t.
Extension/src/LanguageServer/references.ts Migrates references UI/progress/channel strings to vscode.l10n.t.
Extension/src/LanguageServer/Providers/renameProvider.ts Migrates rename validation/error messages to vscode.l10n.t.
Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts Migrates Copilot hover link/disclaimer strings to vscode.l10n.t.
Extension/src/LanguageServer/Providers/codeActionProvider.ts Migrates inline macro strings/disabled reasons to vscode.l10n.t.
Extension/src/LanguageServer/localization.ts Migrates symbol-scope formatting localization to vscode.l10n.t.
Extension/src/LanguageServer/lmTool.ts Migrates LM tool error logging strings to vscode.l10n.t.
Extension/src/LanguageServer/languageConfig.ts Migrates duplicate-pattern warning to vscode.l10n.t.
Extension/src/LanguageServer/extension.ts Migrates command/UI strings and warnings to vscode.l10n.t.
Extension/src/LanguageServer/devcmd.ts Migrates dev environment command prompts/errors to vscode.l10n.t.
Extension/src/LanguageServer/cppBuildTaskProvider.ts Migrates build task strings to vscode.l10n.t.
Extension/src/LanguageServer/copilotProviders.ts Migrates related-files provider error logging to vscode.l10n.t.
Extension/src/LanguageServer/configurations.ts Migrates configuration diagnostics/messages to vscode.l10n.t.
Extension/src/LanguageServer/codeAnalysis.ts Migrates code analysis code action titles to vscode.l10n.t.
Extension/src/LanguageServer/client.ts Migrates client UI prompts/errors/progress strings to vscode.l10n.t.
Extension/src/expand.ts Migrates expansion warnings/errors to vscode.l10n.t.
Extension/src/Debugger/runWithoutDebuggingAdapter.ts Migrates “no terminal emulator” error to vscode.l10n.t.
Extension/src/Debugger/ParsedEnvironmentFile.ts Migrates envfile parse warning to vscode.l10n.t.
Extension/src/Debugger/nativeAttach.ts Migrates native attach errors/timeouts to vscode.l10n.t.
Extension/src/Debugger/extension.ts Migrates debugger command strings/prompts to vscode.l10n.t.
Extension/src/Debugger/debugAdapterDescriptorFactory.ts Migrates debugger support warnings to vscode.l10n.t.
Extension/src/Debugger/configurations.ts Migrates snippet/template strings to vscode.l10n.t.
Extension/src/Debugger/configurationProvider.ts Migrates debug config picker/errors/deploy-step messages to vscode.l10n.t.
Extension/src/Debugger/attachToProcess.ts Migrates remote attach picker errors to vscode.l10n.t.
Extension/src/Debugger/attachQuickPick.ts Migrates attach quick pick UI strings to vscode.l10n.t.
Extension/src/cppTools.ts Migrates provider registration logging to vscode.l10n.t.
Extension/src/common.ts Migrates common prompts/log messages to vscode.l10n.t.
Extension/package.json Adds l10n bundle location; removes vscode-nls(-dev); updates webpack script.
Extension/gulpfile.js Rewrites translation export/import and bundle generation to @vscode/l10n-dev.
Extension/.scripts/generateNativeStrings.ts Updates generator output to use vscode.l10n.t.
Extension/.scripts/common.ts Updates prep check to validate l10n/bundle.l10n.json exists.
Extension/.gitignore Ignores generated l10n/bundle.l10n*.json artifacts.
CONTRIBUTING.md Updates contributor guidance for vscode.l10n and @vscode/l10n-dev.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Extension/src/SSH/TargetsView/targetNodes.ts Outdated
Comment thread Extension/src/LanguageServer/cppBuildTaskProvider.ts Outdated
Comment thread Extension/.scripts/common.ts Outdated
Comment thread Extension/src/platform.ts Outdated
Comment thread Extension/src/LanguageServer/localization.ts Outdated
Comment thread Extension/src/Debugger/attachQuickPick.ts Outdated
@sean-mcmanus

This comment was marked as resolved.

@sean-mcmanus

sean-mcmanus commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

I didn't want to check this in until after we get our pending 1.33.x updated loc strings, in case there are any issues...maybe 1.33.4.

@sean-mcmanus sean-mcmanus modified the milestones: 1.33.3, 1.33.4 Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pull Request

Development

Successfully merging this pull request may close these issues.

Migrate from vscode-nls/vscode-nls-dev to vscode-l10n

3 participants