Summary
When a plural() candidate form contains an interpolation (i.e. it's a template literal, not a plain string literal), wuchale does not treat the array as a plural. Instead, each form is extracted as its own standalone singular message. The resulting .po has no msgid_plural, so the strings are neither pluralized nor grouped.
Version
wuchale 0.25.x (reproduced on 0.25.5), @wuchale/svelte, SvelteKit loader.
Reproduction
<script>
let name = 'Alice';
let count = 3;
</script>
{plural(count, [`${name}'s # file`, `${name}'s # files`])}
The intent: # is the plural count, and ${name} is an ordinary (string) argument interpolated into every form — e.g. "Alice's 1 file" / "Alice's 3 files".
Expected .po
A single plural entry carrying the interpolation placeholder:
#. 0: name
msgid "{0}'s # file"
msgid_plural "{0}'s # files"
msgstr[0] "{0}'s # file"
msgstr[1] "{0}'s # files"
Actual .po
Two independent singular messages, no msgid_plural:
#. 0: name
msgid "{0}'s # file"
msgstr "{0}'s # file"
#. 0: name
msgid "{0}'s # files"
msgstr "{0}'s # files"
By contrast, the same call with plain-string forms (plural(count, ["# file", "# files"])) is extracted correctly as one plural entry, so the trigger is specifically the interpolation in the form.
Likely origin
In the vanilla adapter transformer, the array-candidate branch of the plural() handler requires every element to be a string Literal and bails out to the default call-expression handling the moment it encounters a TemplateLiteral:
// wuchale/dist/adapter-vanilla/transformer.js (~L338)
const candidates = [];
for (const elm of argVal.elements) {
if (!elm || elm.type !== 'Literal' || typeof elm.value !== 'string') {
return this.defaultVisitCallExpression(node); // <-- bails on TemplateLiteral
}
candidates.push(elm.value);
}
Once it falls back to the default handler, each template-literal form is visited individually and emitted as its own message, which is exactly the observed output.
Why this seems supportable (not a fundamental limitation)
- PO format: gettext plural messages fully support positional placeholders alongside the plural count, so
msgid "{0}'s # file" / msgid_plural "{0}'s # files" is valid.
- Compiled representation: wuchale already encodes both interpolation (composite arrays with numeric arg indices, e.g.
["Add to ", 0, "."]) and plural forms (arrays of strings, e.g. ["# file", "# files"]). A plural-with-interpolation is just the nesting of the two — an array of composite arrays — and the runtime's mixedToString already expands composite arrays against args.
The main work appears to be (1) accepting TemplateLiteral elements in the plural candidate loop and extracting their static+interpolation parts the same way single-message template literals are handled, and (2) threading the extra args through the plural runtime helper so the selected form gets its placeholders expanded (in addition to #).
Scope note
This example deliberately uses a string argument (name) so the only number involved is the plural count itself, keeping the case unambiguous. (A form that also interpolates a second number — e.g. "3 of 5 files" — raises a separate question about which value drives pluralization; that's out of scope here.)
Summary
When a
plural()candidate form contains an interpolation (i.e. it's a template literal, not a plain string literal), wuchale does not treat the array as a plural. Instead, each form is extracted as its own standalone singular message. The resulting.pohas nomsgid_plural, so the strings are neither pluralized nor grouped.Version
wuchale0.25.x (reproduced on 0.25.5),@wuchale/svelte, SvelteKit loader.Reproduction
The intent:
#is the plural count, and${name}is an ordinary (string) argument interpolated into every form — e.g. "Alice's 1 file" / "Alice's 3 files".Expected
.poA single plural entry carrying the interpolation placeholder:
Actual
.poTwo independent singular messages, no
msgid_plural:By contrast, the same call with plain-string forms (
plural(count, ["# file", "# files"])) is extracted correctly as one plural entry, so the trigger is specifically the interpolation in the form.Likely origin
In the vanilla adapter transformer, the array-candidate branch of the
plural()handler requires every element to be a stringLiteraland bails out to the default call-expression handling the moment it encounters aTemplateLiteral:Once it falls back to the default handler, each template-literal form is visited individually and emitted as its own message, which is exactly the observed output.
Why this seems supportable (not a fundamental limitation)
msgid "{0}'s # file"/msgid_plural "{0}'s # files"is valid.["Add to ", 0, "."]) and plural forms (arrays of strings, e.g.["# file", "# files"]). A plural-with-interpolation is just the nesting of the two — an array of composite arrays — and the runtime'smixedToStringalready expands composite arrays against args.The main work appears to be (1) accepting
TemplateLiteralelements in the plural candidate loop and extracting their static+interpolation parts the same way single-message template literals are handled, and (2) threading the extra args through the plural runtime helper so the selected form gets its placeholders expanded (in addition to#).Scope note
This example deliberately uses a string argument (
name) so the only number involved is the plural count itself, keeping the case unambiguous. (A form that also interpolates a second number — e.g. "3 of 5 files" — raises a separate question about which value drives pluralization; that's out of scope here.)