Details
-
Bug
-
Resolution: Fixed
-
Major
-
18.3.0
-
Unknown
-
N/A
-
N/A
-
Description
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.
Attachments
Issue Links
- relates to
-
XWIKI-24700 A failed translations request never resolves the xwiki-l10n RequireJS module, blocking all dependent modules
-
- Closed
-
-
XWIKI-24046 Implement a module based client-side API and implementation for translations
-
- Closed
-
-
XWIKI-24741 Provide a localization adapter for ES modules
-
- Closed
-