Problem The client-side Query type published by @xwiki/platform-localization-api has an optional locale field:
type Query =
| string[]
| {
prefix?: string | undefined;
keys: string[];
locale?: string;
};
That field never has any effect. Translations are always resolved in the locale of the current page, whatever the caller asks for, and a key already resolved in one locale is served from the cache in every other locale. Steps to reproduce
- On a page whose <html> element carries lang="en", resolve the key
core.viewers.save through the standard resolver, asking for locale: "fr".
- Observe the request sent to /rest/wikis/<wiki>/localization/translations.
- Resolve the same key again, this time with locale: "en".
Actual behaviour
- The request carries locale=en — the page locale — not locale=fr, so the English value comes
back.
- The second call sends no request at all and returns the value cached by the first one.
Concretely, a Vue component whose locale differs from the page locale never gets its own translations: useI18nAdapter does watch(locale, (lang) => load(lang)), but the reload resolves in the page locale again, so switching locale re-fetches the same strings. Cause Two independent defects on the same path. 1. The default resolver discards the query object. In core/localization/localization-default/src/index.ts line 40, initialize flattens the query to a plain array of fully-qualified keys before delegating:
let remainingTranslations = Array.isArray(query)
? query
: query.keys.map((key) => (query.prefix ?? "") + key);
...
resolved = Object.assign(
resolved,
await translator.resolve(remainingTranslations),
);
The prefix survives, because it is concatenated into each key, but the locale is dropped: the string locale does not appear anywhere in localization-default. The translator then takes its Array.isArray(query) branch and falls back to document.documentElement.getAttribute("lang") ?? "en", so the if (query.locale) branch at localization-resolver-xwiki-rest/src/index.ts line 73 is unreachable through the standard resolver. 2. The translator's cache has no locale dimension. In core/localization/localization-resolvers/localization-resolver-xwiki-rest/src/index.ts, cache (line 33) is a flat map from key to value, and the key filters at lines 55 and 63 skip any key already present in it regardless of the locale being requested. So even a caller that reaches the translator directly, with a locale, gets the value cached for a previous locale — and when every requested key is cached, no request is sent at all. Expected behaviour
- A query's locale reaches the translator: initialize should forward the query rather than
flatten it, or carry the locale alongside the remaining keys.
- The cache should be keyed by locale, so the same key resolved in two locales results in two entries
and two requests.
Notes
- The existing unit tests cover the two halves separately — handles object query with a locale
sends one fr query, caches resolved keys and skips fetch on second call repeats one locale — but never the same key across two locales, which is why neither defect is caught. A regression test should combine them.
- A smaller problem in the same function: _resolve(cache) (line 115) hands the caller the shared
mutable cache object itself rather than the translations for the query. initialize copies it through Object.assign, so the standard path is unaffected, but a direct consumer of the translator receives an object that keeps changing under it, and that holds keys it never asked for.
|