Feature request: named object substitutions in i18n.t()
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:
```yaml
# en.yml
msg: My name is $1, and I create extensions using $2.
```
```ts
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
```yaml
# en.yml
msg: My name is {name}, and I create extensions using {tool}.
```
```ts
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:
```ts
// 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.
6 条评论