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.
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β$9approach.π₯ 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:
Problems with positional substitutions at scale:
$1and$2give translators no hint of what the values representβ Proposed API
β Expected behaviour
{key}tokens in the message string$1β$9array substitutions are unchangedβ Reference implementation
The core logic is a single regex replace β minimal surface area, no new dependencies:
β Notes
Happy to open a PR with a full implementation including type definitions and tests if this direction is approved.