i18n Messages
Scope:
packages/*/src/messages/i18n.ts+ code consuming those messages vianls.localize- NOT
package.json/package.nls.jsonnls strings (command titles etc.) - NOT unrelated code
Placeholder semantics
nls.localize(key, ...args) substitutes via node:util.format — packages/salesforcedx-vscode-i18n/src/i18n/message.ts:50 (return format(possibleLabel, ...labelArgs)).
| Token | Use | Behavior (node:util.format) |
|---|---|---|
%s |
string | string |
%d |
count/number | number as-is (3.7→3.7, no truncation) |
%i |
integer | truncates float (3.7→3) |
%f |
float | float |
%j |
value | JSON |
%o %O |
value | inspected object |
%c |
— | CSS directive, no output |
%% |
literal % |
% only when args present; bare string skips format() (message.ts:37 args.length > 0 guard) and keeps %% |
%n |
NONE | unsupported — renders literal %n, NOT substituted |
Default: counts → %d, strings → %s. (Origin: Removed %n orgs shipped literal %n.)
Arg-count gotcha
message.ts:39 counts specifiers with /%[sdifj%]/g (%% excluded, L40). On mismatch it logs and trims extra args (L41-48); missing args are NOT filled — trailing unmatched tokens render literally.
%o%O%csubstitute at runtime but are NOT in that regex → they escape arg-count validation. Avoid relying on them; prefer%s/%d/%j.%nis not counted and not a real token → expected-count is off by what you intended and the%nprints literally.
Reuse first
grep package.nls.json for an existing equivalent before adding — reuse, don't duplicate.
Per-surface style
From salesforcedx-vscode-org (src/messages/i18n.ts, package.nls.json):
| Surface | Example | Cap | Punctuation |
|---|---|---|---|
Command title (package.nls.json *_text) |
SFDX: Create a Default Scratch Org... |
Title Case, SFDX: prefix |
none (... ok for further input) |
| QuickPick/InputBox placeholder | i18n.ts:67 Select scratch orgs and sandboxes to delete |
sentence | none |
| Confirm prompt | i18n.ts:68 Permanently delete %d org(s)? This cannot be undone. |
sentence | terminal ./? |
Notification (show*Message) |
i18n.ts:92 ... orgs expire in the next %d days. ... |
sentence | terminal . |
| Button/action label | i18n.ts:69,70 Delete, Logout |
Title Case | none |
| Tree/status-bar label+tooltip | i18n.ts:96 Open Default Org in Browser |
Title Case | none |
| Log/channel line | — | sentence | . |
| Validation error | — | sentence | terminal . |
No severity prefix
Don't prefix new strings with Error:/Warning: — the show*Message API supplies severity. i18n.ts:92 (Warning: One or more...) is a legacy anti-pattern; don't copy it, and don't edit it (out of scope). See vscode-window-messages.
Cross-ref
vscode-window-messages covers the notification/button API (which show*Message, return values, Effect, modal). This skill is the message text. Don't duplicate API guidance here.
package.json / package.nls.json command titles, menus, and notification slot keys: command-ui.