wxt-dev/wxt

Feature request: named object substitutions in i18n.t()

Open

#2,332 建立於 2026年5月3日

在 GitHub 查看
 (6 留言) (0 反應) (0 負責人)TypeScript (9,861 star) (511 fork)user submission
contribution welcomegood first issue

描述

Feature Request

Add support for passing a plain object as the substitution argument to i18n.t(), allowing named {placeholder} syntax in message strings as an alternative to the positional $1–$9 approach.

🔥 Motivation

The current positional substitution system works well for simple cases, but becomes fragile and hard to read when a message has 2 or more dynamic values:

# en.yml
msg: My name is $1, and I create extensions using $2.
i18n.t('msg', ['Muzamil', 'WXT'])

Problems with positional substitutions at scale:

  • Order-dependent — reordering args silently breaks translations
  • No translator context$1 and $2 give translators no hint of what the values represent
  • Hard to maintain — readability degrades quickly with 3+ substitutions
  • Word order inflexibility — some languages require a different word order than English, which positional args can't support

✅ Proposed API

# en.yml
msg: My name is {name}, and I create extensions using {tool}.
i18n.t('msg', {
  name: 'Muzamil',
  tool: 'WXT',
})
// => "My name is Muzamil, and I create extensions using WXT."

✅ Expected behaviour

  • Object keys map to {key} tokens in the message string
  • Unmatched placeholders are left as-is — no silent empty strings
  • Fully backward-compatible — existing $1–$9 array substitutions are unchanged
  • Works alongside plural forms — count as second arg, object as third
  • Type-safe: TypeScript can infer required keys from the message template

✅ Reference implementation

The core logic is a single regex replace — minimal surface area, no new dependencies:

// In packages/i18n/src/index.ts
 
// Detect object arg alongside existing number/array detection
} else if (typeof arg === 'object' && !Array.isArray(arg)) {
  objectSub = arg;
}
 
// Apply after browser.i18n.getMessage resolves the message
if (objectSub) {
  message = message.replace(/\{(\w+)\}/g, (match, key) =>
    Object.prototype.hasOwnProperty.call(objectSub, key)
      ? String(objectSub[key])
      : match
  );
}

✅ Notes

Happy to open a PR with a full implementation including type definitions and tests if this direction is approved.

貢獻者指南