@object-ui/i18n 17.6.0 → 17.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4320 -0
- package/README.md +192 -13
- package/dist/builtinAggregateLabels.d.ts +40 -0
- package/dist/builtinAggregateLabels.js +23 -0
- package/dist/currency.d.ts +27 -3
- package/dist/currency.js +28 -4
- package/dist/fallbackInterpolation.d.ts +79 -0
- package/dist/fallbackInterpolation.js +124 -0
- package/dist/i18n.d.ts +27 -1
- package/dist/i18n.js +99 -16
- package/dist/index.d.ts +7 -13
- package/dist/index.js +41 -17
- package/dist/locales/ar.d.ts +946 -113
- package/dist/locales/ar.js +1095 -161
- package/dist/locales/de.d.ts +618 -113
- package/dist/locales/de.js +715 -134
- package/dist/locales/en.d.ts +632 -127
- package/dist/locales/en.js +1044 -162
- package/dist/locales/es.d.ts +700 -113
- package/dist/locales/es.js +794 -130
- package/dist/locales/fr.d.ts +700 -113
- package/dist/locales/fr.js +795 -132
- package/dist/locales/index.d.ts +6990 -1169
- package/dist/locales/index.js +48 -9
- package/dist/locales/ja.d.ts +618 -113
- package/dist/locales/ja.js +708 -128
- package/dist/locales/ko.d.ts +618 -113
- package/dist/locales/ko.js +712 -132
- package/dist/locales/pt.d.ts +700 -113
- package/dist/locales/pt.js +794 -130
- package/dist/locales/registry.d.ts +102 -0
- package/dist/locales/registry.js +220 -0
- package/dist/locales/rtl.d.ts +24 -0
- package/dist/locales/rtl.js +26 -0
- package/dist/locales/ru.d.ts +782 -113
- package/dist/locales/ru.js +910 -152
- package/dist/locales/zh.d.ts +635 -130
- package/dist/locales/zh.js +725 -131
- package/dist/pickLocalized.d.ts +30 -0
- package/dist/pickLocalized.js +39 -0
- package/dist/provider.d.ts +101 -12
- package/dist/provider.js +335 -50
- package/dist/translateFn.d.ts +29 -0
- package/dist/translateFn.js +8 -0
- package/dist/useDisplayLocale.d.ts +11 -0
- package/dist/useDisplayLocale.js +11 -0
- package/dist/useObjectLabel.d.ts +40 -18
- package/dist/useObjectLabel.js +46 -14
- package/dist/useSafeTranslation.js +40 -42
- package/dist/utils/formatting.d.ts +16 -0
- package/dist/utils/formatting.js +23 -3
- package/dist/utils/spec-formatters.d.ts +13 -23
- package/dist/utils/spec-formatters.js +41 -2
- package/dist/utils/spec-translations.d.ts +16 -3
- package/dist/utils/spec-translations.js +52 -13
- package/package.json +12 -6
package/dist/pickLocalized.d.ts
CHANGED
|
@@ -71,3 +71,33 @@ export declare function pickLocalized(value: unknown, language: string | undefin
|
|
|
71
71
|
* this area survived unread.
|
|
72
72
|
*/
|
|
73
73
|
export declare function setLocalized(value: unknown, language: string | undefined | null, next: string): string | Record<string, unknown>;
|
|
74
|
+
/**
|
|
75
|
+
* Remove the `language` entry from a possibly-localized value — the CLEAR arm
|
|
76
|
+
* of the same editor {@link setLocalized} serves, and the reason it cannot be
|
|
77
|
+
* spelled as `setLocalized(value, language, '')`.
|
|
78
|
+
*
|
|
79
|
+
* A single-line authoring input has three actions, not two: type, retype, and
|
|
80
|
+
* clear. `setLocalized` answers the first two. Answering the third with "write
|
|
81
|
+
* the empty string" stores `{ en: '' }` — an entry that claims "English has a
|
|
82
|
+
* title and it is blank", which then SHADOWS every display fallback: a viewer
|
|
83
|
+
* in `en` sees nothing where the map's other locales would have been offered.
|
|
84
|
+
* Answering it with "drop the whole value" is the objectui#9274 data loss
|
|
85
|
+
* itself, arriving through the clearing door instead of the typing door.
|
|
86
|
+
*
|
|
87
|
+
* So clearing removes exactly one entry — the same entry {@link setLocalized}
|
|
88
|
+
* would have written, chosen by the same {@link localeWriteKey} limbs, which is
|
|
89
|
+
* what keeps the cleared entry the one the author was actually looking at.
|
|
90
|
+
*
|
|
91
|
+
* - Plain string / `null` / `undefined` / array: there is no map, so there is
|
|
92
|
+
* nothing localized left — `undefined`. The caller decides how its own field
|
|
93
|
+
* spells empty (`undefined` for an optional key, `''` for a required one).
|
|
94
|
+
* - Map with the active entry present: that entry is removed and every other
|
|
95
|
+
* locale is carried across untouched. When it was the LAST entry the result
|
|
96
|
+
* is `undefined` rather than `{}`, because an empty map is not a title.
|
|
97
|
+
* - Map WITHOUT an entry for the active locale: returned unchanged. The box was
|
|
98
|
+
* showing a display fallback borrowed from another locale, and clearing a
|
|
99
|
+
* borrowed string must not delete the locale it was borrowed from — the same
|
|
100
|
+
* refusal {@link localeWriteKey} enforces on the write side, which is why
|
|
101
|
+
* both read the same limbs from the same file.
|
|
102
|
+
*/
|
|
103
|
+
export declare function clearLocalized(value: unknown, language: string | undefined | null): Record<string, unknown> | undefined;
|
package/dist/pickLocalized.js
CHANGED
|
@@ -144,3 +144,42 @@ export function setLocalized(value, language, next) {
|
|
|
144
144
|
const o = value;
|
|
145
145
|
return { ...o, [localeWriteKey(o, language)]: next };
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Remove the `language` entry from a possibly-localized value — the CLEAR arm
|
|
149
|
+
* of the same editor {@link setLocalized} serves, and the reason it cannot be
|
|
150
|
+
* spelled as `setLocalized(value, language, '')`.
|
|
151
|
+
*
|
|
152
|
+
* A single-line authoring input has three actions, not two: type, retype, and
|
|
153
|
+
* clear. `setLocalized` answers the first two. Answering the third with "write
|
|
154
|
+
* the empty string" stores `{ en: '' }` — an entry that claims "English has a
|
|
155
|
+
* title and it is blank", which then SHADOWS every display fallback: a viewer
|
|
156
|
+
* in `en` sees nothing where the map's other locales would have been offered.
|
|
157
|
+
* Answering it with "drop the whole value" is the objectui#9274 data loss
|
|
158
|
+
* itself, arriving through the clearing door instead of the typing door.
|
|
159
|
+
*
|
|
160
|
+
* So clearing removes exactly one entry — the same entry {@link setLocalized}
|
|
161
|
+
* would have written, chosen by the same {@link localeWriteKey} limbs, which is
|
|
162
|
+
* what keeps the cleared entry the one the author was actually looking at.
|
|
163
|
+
*
|
|
164
|
+
* - Plain string / `null` / `undefined` / array: there is no map, so there is
|
|
165
|
+
* nothing localized left — `undefined`. The caller decides how its own field
|
|
166
|
+
* spells empty (`undefined` for an optional key, `''` for a required one).
|
|
167
|
+
* - Map with the active entry present: that entry is removed and every other
|
|
168
|
+
* locale is carried across untouched. When it was the LAST entry the result
|
|
169
|
+
* is `undefined` rather than `{}`, because an empty map is not a title.
|
|
170
|
+
* - Map WITHOUT an entry for the active locale: returned unchanged. The box was
|
|
171
|
+
* showing a display fallback borrowed from another locale, and clearing a
|
|
172
|
+
* borrowed string must not delete the locale it was borrowed from — the same
|
|
173
|
+
* refusal {@link localeWriteKey} enforces on the write side, which is why
|
|
174
|
+
* both read the same limbs from the same file.
|
|
175
|
+
*/
|
|
176
|
+
export function clearLocalized(value, language) {
|
|
177
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value))
|
|
178
|
+
return undefined;
|
|
179
|
+
const o = value;
|
|
180
|
+
const key = localeWriteKey(o, language);
|
|
181
|
+
if (!Object.prototype.hasOwnProperty.call(o, key))
|
|
182
|
+
return o;
|
|
183
|
+
const rest = Object.fromEntries(Object.entries(o).filter(([k]) => k !== key));
|
|
184
|
+
return Object.keys(rest).length > 0 ? rest : undefined;
|
|
185
|
+
}
|
package/dist/provider.d.ts
CHANGED
|
@@ -17,6 +17,22 @@ import { type I18nConfig } from './i18n.js';
|
|
|
17
17
|
* Exported so an app that builds its own i18next instance (and therefore owns
|
|
18
18
|
* its bootstrap language — see {@link I18nProviderProps.instance}) can honour
|
|
19
19
|
* the same preference, and so a sign-out flow can clear it.
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ **What this slot means changed for a SIGNED-IN user (objectui#10059).**
|
|
22
|
+
* A signed-in user's language has one source of truth and it is not on the
|
|
23
|
+
* device: it is the server column `sys_user.locale`, read back through
|
|
24
|
+
* `GET /auth/me/localization` and written by both the profile page's language
|
|
25
|
+
* card and the console's globe menu. For that user this slot is a CACHE of the
|
|
26
|
+
* column — it still decides the first paint, because the column cannot be read
|
|
27
|
+
* synchronously, and it is then overwritten by whatever the column says. It
|
|
28
|
+
* decides on its own only BEFORE sign-in, which is the case this provider can
|
|
29
|
+
* see and the only one it adjudicates.
|
|
30
|
+
*
|
|
31
|
+
* This provider deliberately does not implement that rule: reading a `sys_user`
|
|
32
|
+
* row needs an authenticated data adapter, which is a host concern and not a
|
|
33
|
+
* dependency a renderer package takes. The host applies the column through
|
|
34
|
+
* {@link I18nContextValue.changeLanguage} like any other switch — in this
|
|
35
|
+
* repository, `@object-ui/app-shell`'s `useUserLocale`.
|
|
20
36
|
*/
|
|
21
37
|
export declare const LOCALE_STORAGE_KEY = "objectui-locale";
|
|
22
38
|
/**
|
|
@@ -29,28 +45,53 @@ export declare const LOCALE_STORAGE_KEY = "objectui-locale";
|
|
|
29
45
|
*/
|
|
30
46
|
export declare function readStoredLanguage(): string | null;
|
|
31
47
|
/**
|
|
32
|
-
* `localStorage` key caching the
|
|
33
|
-
* choice (objectui#4035).
|
|
48
|
+
* `localStorage` key caching the server's resolved locale for the signed-in
|
|
49
|
+
* caller — a *seed*, not a choice (objectui#4035).
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ **The value is per-USER; the slot is per-DEVICE (objectui#10193).**
|
|
52
|
+
* `GET /auth/me/localization` answers `locale` as the caller's own
|
|
53
|
+
* `sys_user.locale` when set, else the request's `Accept-Language`, else the
|
|
54
|
+
* deployment default — and answers no locale at all before sign-in. It is not
|
|
55
|
+
* a tenant setting, whatever earlier prose here said. The slot is deliberately
|
|
56
|
+
* NOT keyed by user: the boot reads it before anyone is signed in, when no
|
|
57
|
+
* user id exists to key by. What bounds it to one owner is
|
|
58
|
+
* `@object-ui/auth`'s change-of-owner purge (objectui#5664), which deletes it
|
|
59
|
+
* when a different user adopts the browser, and which the host answers by
|
|
60
|
+
* re-resolving the live language (`@object-ui/app-shell`'s
|
|
61
|
+
* `useSignedInUserLocale`).
|
|
62
|
+
*
|
|
63
|
+
* **What a visitor who is not signed in reads**, declared as device behaviour:
|
|
64
|
+
* the language the LAST signed-in owner of this browser was served, until
|
|
65
|
+
* someone else signs in. That is the only server answer this device has ever
|
|
66
|
+
* received — the endpoint gives none before sign-in — and the sign-in page is
|
|
67
|
+
* the one surface it paints.
|
|
34
68
|
*
|
|
35
69
|
* Deliberately a separate slot from {@link LOCALE_STORAGE_KEY}, and that
|
|
36
70
|
* separation is the whole point rather than a tidiness preference. The two
|
|
37
71
|
* values have different provenance and therefore different rights:
|
|
38
72
|
*
|
|
39
|
-
* - {@link LOCALE_STORAGE_KEY} is what the *user* picked. It
|
|
40
|
-
* everything and must survive a
|
|
41
|
-
*
|
|
42
|
-
*
|
|
73
|
+
* - {@link LOCALE_STORAGE_KEY} is what the *user* picked on this device. It
|
|
74
|
+
* outranks everything this provider can see and must survive a change in
|
|
75
|
+
* what the server answers for the seed below. ⚠️ It does NOT outrank the signed-in user's own
|
|
76
|
+
* `sys_user.locale`, which no tier here represents — see that key's own
|
|
77
|
+
* docblock (objectui#10059).
|
|
78
|
+
* - This slot is what the server resolved for the signed-in caller, cached so
|
|
79
|
+
* the next boot can apply it synchronously. It outranks only the environment
|
|
80
|
+
* (browser language).
|
|
43
81
|
*
|
|
44
82
|
* Writing the server seed into the explicit-choice slot would erase that
|
|
45
83
|
* difference permanently: the seed would then be indistinguishable from a
|
|
46
|
-
* deliberate user choice, so it would (a) outrank a later
|
|
47
|
-
* the device to a stale locale forever, and (b) suppress browser
|
|
84
|
+
* deliberate user choice, so it would (a) outrank a later server-side change
|
|
85
|
+
* and pin the device to a stale locale forever, and (b) suppress browser
|
|
86
|
+
* detection for
|
|
48
87
|
* a user who never expressed a preference. Only a real switch promotes a
|
|
49
88
|
* language to the explicit slot — see the `languageChanged` choke point.
|
|
50
89
|
*/
|
|
51
90
|
export declare const LOCALE_SEED_STORAGE_KEY = "objectui-locale-seed";
|
|
52
91
|
/**
|
|
53
|
-
* Read the cached
|
|
92
|
+
* Read the cached locale seed — the server's last resolved locale for this
|
|
93
|
+
* browser's last signed-in caller (see {@link LOCALE_SEED_STORAGE_KEY}) — or
|
|
94
|
+
* `null` when nothing is cached.
|
|
54
95
|
*
|
|
55
96
|
* Same defensive posture as {@link readStoredLanguage}: `localStorage` is
|
|
56
97
|
* absent under SSR and *throws* in Safari private mode / partitioned iframes,
|
|
@@ -58,12 +99,13 @@ export declare const LOCALE_SEED_STORAGE_KEY = "objectui-locale-seed";
|
|
|
58
99
|
*/
|
|
59
100
|
export declare function readCachedLanguageSeed(): string | null;
|
|
60
101
|
/**
|
|
61
|
-
* Cache the
|
|
62
|
-
* of the stale-while-revalidate contract (objectstack#5419,
|
|
102
|
+
* Cache the locale the server just resolved for the signed-in caller — the
|
|
103
|
+
* "revalidate" half of the stale-while-revalidate contract (objectstack#5419,
|
|
104
|
+
* ruling point 2).
|
|
63
105
|
*
|
|
64
106
|
* Call this with whatever `/auth/me/localization` actually returned for an
|
|
65
107
|
* authenticated caller. A `null`/`undefined` locale CLEARS the cache rather
|
|
66
|
-
* than leaving the old value: a
|
|
108
|
+
* than leaving the old value: a server answer that drops the locale must reach
|
|
67
109
|
* choice-less devices on their next boot, and "keep the last good seed forever"
|
|
68
110
|
* is precisely the being-pinned-by-a-stale-seed failure the ruling forbids.
|
|
69
111
|
*
|
|
@@ -72,6 +114,53 @@ export declare function readCachedLanguageSeed(): string | null;
|
|
|
72
114
|
* cannot complete.
|
|
73
115
|
*/
|
|
74
116
|
export declare function cacheLanguageSeed(locale: string | null | undefined): void;
|
|
117
|
+
/**
|
|
118
|
+
* Options shared by {@link resolveBootstrapLanguage} and
|
|
119
|
+
* {@link preloadBootstrapLocale}. Each field mirrors the {@link I18nProvider}
|
|
120
|
+
* prop of the same name, because the two must answer identically — a host that
|
|
121
|
+
* preloads one catalogue while the provider boots into another has bought a
|
|
122
|
+
* flash of `en` instead of avoiding one.
|
|
123
|
+
*/
|
|
124
|
+
export interface BootstrapLocaleOptions {
|
|
125
|
+
/** The `config` you will pass to {@link I18nProvider}. */
|
|
126
|
+
config?: I18nConfig;
|
|
127
|
+
/** The `persistLanguage` you will pass to {@link I18nProvider}. Default `true`. */
|
|
128
|
+
persistLanguage?: boolean;
|
|
129
|
+
/** Whether you will pass a `loadLanguage` loader. Default `false`. */
|
|
130
|
+
hasLoader?: boolean;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The language {@link I18nProvider} will boot in, computed WITHOUT creating an
|
|
134
|
+
* i18next instance (objectui#7479).
|
|
135
|
+
*
|
|
136
|
+
* The full precedence chain, unchanged: explicit choice → seed →
|
|
137
|
+
* browser language → `defaultLanguage` → `en`.
|
|
138
|
+
*/
|
|
139
|
+
export declare function resolveBootstrapLanguage(options?: BootstrapLocaleOptions): string;
|
|
140
|
+
/**
|
|
141
|
+
* Fetch the catalogue {@link I18nProvider} will boot in, BEFORE you render it.
|
|
142
|
+
*
|
|
143
|
+
* ## Why a host would await this
|
|
144
|
+
*
|
|
145
|
+
* Built-in catalogues are lazy since objectui#7479, so the provider can be in
|
|
146
|
+
* one of two states on its first render, and the difference is visible:
|
|
147
|
+
*
|
|
148
|
+
* - **catalogue already resident** — the first paint is in the user's
|
|
149
|
+
* language, exactly as it was when all ten packs shipped eagerly;
|
|
150
|
+
* - **catalogue still in flight** — the first paint renders through
|
|
151
|
+
* `fallbackLng: 'en'` and re-renders in the user's language when the fetch
|
|
152
|
+
* lands. Correct, never blank, and a flash of English for a `zh-CN` viewer.
|
|
153
|
+
*
|
|
154
|
+
* ⇒ any host that owns an await before `createRoot().render()` should spend it
|
|
155
|
+
* here; `apps/console/src/main.tsx` does, alongside the runtime-config and auth
|
|
156
|
+
* preflight round-trips it already waits on. A host that has no such seam gets
|
|
157
|
+
* the documented `en`-while-loading behaviour instead, which is why this is a
|
|
158
|
+
* function a host CALLS rather than a requirement the provider imposes.
|
|
159
|
+
*
|
|
160
|
+
* Never rejects: a catalogue that will not download must not take the boot
|
|
161
|
+
* down, and the provider retries on mount.
|
|
162
|
+
*/
|
|
163
|
+
export declare function preloadBootstrapLocale(options?: BootstrapLocaleOptions): Promise<void>;
|
|
75
164
|
interface I18nContextValue {
|
|
76
165
|
/** Current language code */
|
|
77
166
|
language: string;
|