@object-ui/i18n 17.5.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 +4954 -0
- package/README.md +234 -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 +28 -2
- package/dist/i18n.js +99 -17
- package/dist/index.d.ts +17 -23
- package/dist/index.js +51 -27
- package/dist/locales/ar.d.ts +1113 -370
- package/dist/locales/ar.js +1276 -428
- package/dist/locales/de.d.ts +785 -370
- package/dist/locales/de.js +896 -401
- package/dist/locales/en.d.ts +800 -385
- package/dist/locales/en.js +1261 -419
- package/dist/locales/es.d.ts +867 -370
- package/dist/locales/es.js +976 -398
- package/dist/locales/fr.d.ts +867 -370
- package/dist/locales/fr.js +976 -399
- package/dist/locales/index.d.ts +8682 -3761
- package/dist/locales/index.js +68 -29
- package/dist/locales/ja.d.ts +785 -370
- package/dist/locales/ja.js +891 -395
- package/dist/locales/ko.d.ts +785 -370
- package/dist/locales/ko.js +893 -399
- package/dist/locales/pt.d.ts +867 -370
- package/dist/locales/pt.js +975 -397
- 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 +949 -370
- package/dist/locales/ru.js +1093 -419
- package/dist/locales/zh.d.ts +803 -388
- package/dist/locales/zh.js +899 -388
- package/dist/pickLocalized.d.ts +67 -0
- package/dist/pickLocalized.js +114 -0
- package/dist/provider.d.ts +102 -13
- 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 +13 -2
- package/dist/useObjectLabel.d.ts +118 -22
- package/dist/useObjectLabel.js +148 -23
- package/dist/useSafeTranslation.js +41 -43
- package/dist/utils/formatting.d.ts +16 -0
- package/dist/utils/formatting.js +23 -3
- package/dist/utils/index.d.ts +4 -4
- package/dist/utils/index.js +4 -4
- 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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE interpolator every provider-less translation path in this package
|
|
3
|
+
* runs — extracted so there is exactly one of it (objectui#6219).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this module exists at all
|
|
6
|
+
*
|
|
7
|
+
* Three things can render a translation in this repo, and each has its own
|
|
8
|
+
* answer to "does a `{{hole}}` get filled?":
|
|
9
|
+
*
|
|
10
|
+
* 1. **i18next, with an `I18nProvider` mounted.** It interpolates. This is
|
|
11
|
+
* the reference behaviour every other path has to agree with.
|
|
12
|
+
* 2. **`createSafeTranslation`'s `fallbackT`** (`useSafeTranslation.ts`).
|
|
13
|
+
* It interpolates — with the exact literal needle below.
|
|
14
|
+
* 3. **react-i18next's not-ready `t`**, which is what a bare
|
|
15
|
+
* `useObjectTranslation()` yields when no i18next instance is initialised.
|
|
16
|
+
* Measured in `react-i18next@17.0.11/dist/es/useTranslation.js`:
|
|
17
|
+
* `notReadyT` returns `options.defaultValue` VERBATIM and does not
|
|
18
|
+
* interpolate at all.
|
|
19
|
+
*
|
|
20
|
+
* (3) was objectui#6219: 68 inline defaults across 24 files carried a
|
|
21
|
+
* `{{hole}}` that reached the user as literal braces on any host that embeds an
|
|
22
|
+
* ObjectUI component without `I18nProvider` — the configuration
|
|
23
|
+
* `createSafeTranslation` exists for (objectui#3865), so a supported one.
|
|
24
|
+
* `useObjectTranslation` now runs its not-ready result through this function,
|
|
25
|
+
* which makes (3) agree with (2), and (2) already agrees with (1) for the one
|
|
26
|
+
* spelling this repo is allowed to author.
|
|
27
|
+
*
|
|
28
|
+
* ## Why one shared function rather than a second copy
|
|
29
|
+
*
|
|
30
|
+
* The semantics below are pinned in three places at once —
|
|
31
|
+
* `useSafeTranslation.test.tsx` (behaviour), `fallback-placeholder-spelling-3512.test.ts`
|
|
32
|
+
* (the copy is held to what this can resolve) and
|
|
33
|
+
* `scripts/check-i18n-call-site-keys.mjs` class 7 (the same rule over inline
|
|
34
|
+
* defaults). A second interpolator with its own drift would have made all three
|
|
35
|
+
* of those true of one path and quietly false of the other. There is one
|
|
36
|
+
* function, so "the fallback resolves only `{{name}}`" is one fact.
|
|
37
|
+
*
|
|
38
|
+
* ## The exact spelling, and why only that one
|
|
39
|
+
*
|
|
40
|
+
* objectui#3512 measured that i18next additionally accepts `{{ name }}`,
|
|
41
|
+
* `{{count, number}}`, `{{- name}}` and `$t(key)`, and the ruling was
|
|
42
|
+
* deliberately NOT to teach the fallback three more dialects (a second
|
|
43
|
+
* interpolator to keep in step with i18next forever) but to hold the copy to
|
|
44
|
+
* the one spelling both paths agree on — enforced by the two gates named above.
|
|
45
|
+
* This module keeps that ruling: it widens WHICH BINDINGS interpolate, never
|
|
46
|
+
* WHICH SPELLINGS resolve.
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* The i18next option that names the string to use when the lookup misses.
|
|
50
|
+
*
|
|
51
|
+
* It is a LOOKUP CONTROL, not interpolation data (objectui#3865): it chooses
|
|
52
|
+
* which string is rendered, so it must never also be spliced into a
|
|
53
|
+
* `{{defaultValue}}` hole in the string it chose — or in any other.
|
|
54
|
+
*/
|
|
55
|
+
export const DEFAULT_VALUE_OPTION = 'defaultValue';
|
|
56
|
+
/**
|
|
57
|
+
* Fill `{{name}}` holes in `value` from `options`, i18next-compatibly.
|
|
58
|
+
*
|
|
59
|
+
* Returns `value` untouched when there is nothing to fill from, so a caller can
|
|
60
|
+
* hand it every result unconditionally.
|
|
61
|
+
*
|
|
62
|
+
* @param value - The string that was chosen for rendering.
|
|
63
|
+
* @param options - The `t()` call's options object, or `undefined`.
|
|
64
|
+
*/
|
|
65
|
+
export function interpolateFallback(value, options) {
|
|
66
|
+
if (!options)
|
|
67
|
+
return value;
|
|
68
|
+
let out = value;
|
|
69
|
+
for (const [k, v] of Object.entries(options)) {
|
|
70
|
+
// Reserved: see DEFAULT_VALUE_OPTION. Skipped whatever its type, so an
|
|
71
|
+
// ignored non-string default cannot re-enter through this loop.
|
|
72
|
+
//
|
|
73
|
+
// This is the one deliberate divergence from i18next on this path, and it
|
|
74
|
+
// is unreachable with today's strings: i18next passes the whole options
|
|
75
|
+
// object to its interpolator, so it WOULD render
|
|
76
|
+
// `'Fallback: {{defaultValue}}'` as `'Fallback: INLINE'` (measured on
|
|
77
|
+
// 26.3.6) — but no value in this repo spells that hole (zero hits for
|
|
78
|
+
// `{{defaultValue` across packages/apps/examples), and splicing a fallback
|
|
79
|
+
// string into a hole named after itself has no sensible reading. The
|
|
80
|
+
// alternative — letting a call site's fallback text leak into an unrelated
|
|
81
|
+
// table value — is the worse of the two.
|
|
82
|
+
if (k === DEFAULT_VALUE_OPTION)
|
|
83
|
+
continue;
|
|
84
|
+
// `split(needle).join(value)` — deliberately not `replace`, and not
|
|
85
|
+
// `replaceAll` either (objectui#3418). This path must agree with i18next,
|
|
86
|
+
// which serves the *provider* path; any divergence is a silent fork that
|
|
87
|
+
// only shows up on provider-less hosts, where we are least likely to see
|
|
88
|
+
// it:
|
|
89
|
+
// 1. `replace` with a string needle substitutes only the FIRST
|
|
90
|
+
// occurrence. i18next substitutes every one, so a sentence that
|
|
91
|
+
// repeats a placeholder ("Selected {{count}} of {{count}} items" —
|
|
92
|
+
// natural in many locales, and often required by RTL / agglutinative
|
|
93
|
+
// word order) leaked literal braces to users.
|
|
94
|
+
// 2. `replace` AND `replaceAll` both interpret `$&`, `` $` ``, `$'` and
|
|
95
|
+
// `$$` in the *replacement* string. i18next does not. Values here are
|
|
96
|
+
// runtime data — record labels, search terms — so this one is
|
|
97
|
+
// reachable today, unlike (1).
|
|
98
|
+
// split/join is literal on both sides, which is exactly i18next's
|
|
99
|
+
// behaviour, and needs no regex escaping of the placeholder name.
|
|
100
|
+
out = out.split(`{{${k}}}`).join(String(v));
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The options object of a `t()` call, or `undefined` when the call passed none.
|
|
106
|
+
*
|
|
107
|
+
* i18next's `t` accepts both `t(key, options)` and `t(key, defaultValue,
|
|
108
|
+
* options)`, so the options object is the LAST argument when it is an object at
|
|
109
|
+
* all. A string second argument is a default value, not data — there is nothing
|
|
110
|
+
* to interpolate from, which is why `t(key, 'Hi {{name}}')` keeps its braces on
|
|
111
|
+
* both paths.
|
|
112
|
+
*
|
|
113
|
+
* Arrays are excluded deliberately: `t(key, [...])` is not an i18next options
|
|
114
|
+
* shape, and `Object.entries` over one would mint `{{0}}`-style needles that no
|
|
115
|
+
* copy in this repo spells.
|
|
116
|
+
*/
|
|
117
|
+
export function optionsOf(args) {
|
|
118
|
+
if (args.length < 2)
|
|
119
|
+
return undefined;
|
|
120
|
+
const last = args[args.length - 1];
|
|
121
|
+
if (typeof last !== 'object' || last === null || Array.isArray(last))
|
|
122
|
+
return undefined;
|
|
123
|
+
return last;
|
|
124
|
+
}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -4,13 +4,21 @@
|
|
|
4
4
|
* Wraps i18next with Object UI defaults and built-in locale support.
|
|
5
5
|
*/
|
|
6
6
|
import { type i18n as I18nInstance } from 'i18next';
|
|
7
|
-
import type { TranslationKeys } from './locales/en';
|
|
7
|
+
import type { TranslationKeys } from './locales/en.js';
|
|
8
8
|
export interface I18nConfig {
|
|
9
9
|
/** Default language (default: 'en') */
|
|
10
10
|
defaultLanguage?: string;
|
|
11
11
|
/** Fallback language (default: 'en') */
|
|
12
12
|
fallbackLanguage?: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Additional translation resources, **deep-merged** over the built-in packs.
|
|
15
|
+
*
|
|
16
|
+
* The packs' top-level keys are the namespace groups (`common`, `calendar`,
|
|
17
|
+
* `list`, ...), so supplying a partial group merges into it and leaves the
|
|
18
|
+
* group's other keys in place:
|
|
19
|
+
* `{ en: { calendar: { today: 'Heute' } } }` overrides `calendar.today` only.
|
|
20
|
+
* Arrays are replaced wholesale, never concatenated (objectui#7572).
|
|
21
|
+
*/
|
|
14
22
|
resources?: Record<string, Record<string, unknown>>;
|
|
15
23
|
/** Whether to detect browser language automatically (default: true) */
|
|
16
24
|
detectBrowserLanguage?: boolean;
|
|
@@ -39,6 +47,24 @@ export interface I18nConfig {
|
|
|
39
47
|
* API — shared between `i18n.ts` and `useObjectLabel.ts` to avoid drift.
|
|
40
48
|
*/
|
|
41
49
|
export declare const I18N_PROBE_FLAG = "__ouiLabelProbe";
|
|
50
|
+
/**
|
|
51
|
+
* The language {@link createI18n} will boot in for this config — `defaultLanguage`,
|
|
52
|
+
* overridden by a browser language this renderer can produce.
|
|
53
|
+
*
|
|
54
|
+
* Exported because the answer is needed BEFORE an instance exists
|
|
55
|
+
* (objectui#7479): with the catalogues lazy, a host that wants its first render
|
|
56
|
+
* to already be in the right language has to know which catalogue to await, and
|
|
57
|
+
* the only alternative — create an instance, read `.language`, throw it away —
|
|
58
|
+
* would boot i18next in the wrong locale to find out what the right one is.
|
|
59
|
+
* `I18nProvider` and {@link preloadBootstrapLocale} answer with THIS function,
|
|
60
|
+
* so the host and the provider cannot disagree about which catalogue is active.
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ Browser detection is asked of {@link isBuiltInLanguage} rather than of the
|
|
63
|
+
* merged resource map: a catalogue that has not been fetched yet is still a
|
|
64
|
+
* language this renderer can produce, and asking the map would have made
|
|
65
|
+
* detection depend on load order.
|
|
66
|
+
*/
|
|
67
|
+
export declare function pickInitialLanguage(config?: I18nConfig): string;
|
|
42
68
|
/**
|
|
43
69
|
* Create and initialize an i18next instance with Object UI defaults
|
|
44
70
|
*/
|
package/dist/i18n.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import i18next from 'i18next';
|
|
7
7
|
import { initReactI18next } from 'react-i18next';
|
|
8
|
-
import {
|
|
8
|
+
import { BUILT_IN_LANGUAGE_CODES, getLoadedBuiltInLocales, isBuiltInLanguage, } from './locales/registry.js';
|
|
9
|
+
import { isRTL } from './locales/rtl.js';
|
|
9
10
|
/**
|
|
10
11
|
* Internal `t()` option flag set by `useObjectLabel` on its convention-key
|
|
11
12
|
* probes. The missing-key handler skips any lookup carrying this flag, so the
|
|
@@ -34,23 +35,111 @@ function createMissingKeyHandler() {
|
|
|
34
35
|
return;
|
|
35
36
|
seen.add(dedupeKey);
|
|
36
37
|
const fb = fallbackValue ? `"${fallbackValue}"` : 'the key itself';
|
|
37
|
-
// eslint-disable-next-line no-console
|
|
38
38
|
console.warn(`[object-ui i18n] Missing translation for "${key}" (language "${lng}") — falling back to ${fb}.`);
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A plain object — the only shape the resource merge recurses into. Arrays and
|
|
43
|
+
* `null` are excluded on purpose; see {@link deepMergeTranslations}.
|
|
44
|
+
*/
|
|
45
|
+
function isPlainObject(value) {
|
|
46
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Deep-merge caller-supplied translations over a built-in language pack.
|
|
50
|
+
*
|
|
51
|
+
* A language pack's top-level keys ARE the namespace groups (`common`,
|
|
52
|
+
* `calendar`, `list`, ...), each a nested object, so the one-level merge this
|
|
53
|
+
* replaced made `resources: { en: { calendar: { today: 'Heute' } } }` **replace**
|
|
54
|
+
* the whole `calendar` group: `month` / `week` / `day` / `allDay` / `newEvent` /
|
|
55
|
+
* `moreEvents` / `unscheduled` left the instance, `t('calendar.allDay')` returned
|
|
56
|
+
* the bare key, and `calendar.allDay` reached the DOM as literal text
|
|
57
|
+
* (objectui#7572). Packs nest up to four levels below the group
|
|
58
|
+
* (`console.ai.empty.build.title`), so the merge recurses rather than adding one
|
|
59
|
+
* fixed extra level.
|
|
60
|
+
*
|
|
61
|
+
* ARRAYS ARE REPLACED, NOT CONCATENATED — a deliberate choice, not a library
|
|
62
|
+
* default. Measured: no built-in pack carries an array value today (every leaf
|
|
63
|
+
* in all ten packs is a string), so nothing observable rides on this; the rule
|
|
64
|
+
* decides what a future array means. An author who writes an array is naming the
|
|
65
|
+
* whole list, so replacement is the only rule that lets them shorten or reorder
|
|
66
|
+
* one, and the only one that is idempotent when the merge runs again. This is
|
|
67
|
+
* narrower than i18next's own `deepExtend` (which the provider's async
|
|
68
|
+
* `addResourceBundle` path uses): that recurses into arrays index-wise and would
|
|
69
|
+
* leave a longer base array's tail behind — the same silent-hybrid shape this
|
|
70
|
+
* issue is about.
|
|
71
|
+
*
|
|
72
|
+
* `__proto__` is skipped. Recursive assignment, unlike the object spread it
|
|
73
|
+
* replaces, would otherwise reach the prototype setter; i18next's `deepExtend`
|
|
74
|
+
* guards the same key.
|
|
75
|
+
*/
|
|
76
|
+
function deepMergeTranslations(base, override) {
|
|
77
|
+
const merged = { ...base };
|
|
78
|
+
for (const key of Object.keys(override)) {
|
|
79
|
+
if (key === '__proto__')
|
|
80
|
+
continue;
|
|
81
|
+
const incoming = override[key];
|
|
82
|
+
const existing = merged[key];
|
|
83
|
+
merged[key] =
|
|
84
|
+
isPlainObject(existing) && isPlainObject(incoming)
|
|
85
|
+
? deepMergeTranslations(existing, incoming)
|
|
86
|
+
: incoming;
|
|
87
|
+
}
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The language {@link createI18n} will boot in for this config — `defaultLanguage`,
|
|
92
|
+
* overridden by a browser language this renderer can produce.
|
|
93
|
+
*
|
|
94
|
+
* Exported because the answer is needed BEFORE an instance exists
|
|
95
|
+
* (objectui#7479): with the catalogues lazy, a host that wants its first render
|
|
96
|
+
* to already be in the right language has to know which catalogue to await, and
|
|
97
|
+
* the only alternative — create an instance, read `.language`, throw it away —
|
|
98
|
+
* would boot i18next in the wrong locale to find out what the right one is.
|
|
99
|
+
* `I18nProvider` and {@link preloadBootstrapLocale} answer with THIS function,
|
|
100
|
+
* so the host and the provider cannot disagree about which catalogue is active.
|
|
101
|
+
*
|
|
102
|
+
* ⚠️ Browser detection is asked of {@link isBuiltInLanguage} rather than of the
|
|
103
|
+
* merged resource map: a catalogue that has not been fetched yet is still a
|
|
104
|
+
* language this renderer can produce, and asking the map would have made
|
|
105
|
+
* detection depend on load order.
|
|
106
|
+
*/
|
|
107
|
+
export function pickInitialLanguage(config = {}) {
|
|
108
|
+
const { defaultLanguage = 'en', resources = {}, detectBrowserLanguage = true } = config;
|
|
109
|
+
if (!detectBrowserLanguage || typeof navigator === 'undefined')
|
|
110
|
+
return defaultLanguage;
|
|
111
|
+
const browserLang = navigator.language?.split('-')[0];
|
|
112
|
+
if (!browserLang)
|
|
113
|
+
return defaultLanguage;
|
|
114
|
+
const known = isBuiltInLanguage(browserLang) ||
|
|
115
|
+
Object.prototype.hasOwnProperty.call(resources, browserLang);
|
|
116
|
+
return known ? browserLang : defaultLanguage;
|
|
117
|
+
}
|
|
41
118
|
/**
|
|
42
119
|
* Create and initialize an i18next instance with Object UI defaults
|
|
43
120
|
*/
|
|
44
121
|
export function createI18n(config = {}) {
|
|
45
|
-
const {
|
|
46
|
-
//
|
|
122
|
+
const { fallbackLanguage = 'en', resources = {}, interpolation, warnMissingKeys = isDevEnv(), } = config;
|
|
123
|
+
// `defaultLanguage` and `detectBrowserLanguage` are read by
|
|
124
|
+
// {@link pickInitialLanguage}, which owns that precedence for this function
|
|
125
|
+
// AND for the hosts that must know the answer before an instance exists.
|
|
126
|
+
// Merge the catalogues that are RESIDENT with user-provided resources.
|
|
127
|
+
//
|
|
128
|
+
// ⛔ Every built-in code gets a bundle here even when its catalogue has not
|
|
129
|
+
// been fetched yet (objectui#7479). The empty bundle is load-bearing, not a
|
|
130
|
+
// placeholder: it keeps `instance.options.resources` — which
|
|
131
|
+
// {@link getAvailableLanguages}, the browser detection below and i18next's
|
|
132
|
+
// own `hasResourceBundle` all read — enumerating every language this package
|
|
133
|
+
// can produce, instead of shrinking to whatever happened to be loaded at
|
|
134
|
+
// instance-creation time. Until the catalogue lands, `fallbackLng` renders
|
|
135
|
+
// those keys through `en`; `I18nProvider` then deep-merges the real
|
|
136
|
+
// catalogue in WITHOUT overwriting anything `resources` put there, so the
|
|
137
|
+
// precedence below survives the async arrival.
|
|
47
138
|
const mergedResources = {};
|
|
48
|
-
|
|
139
|
+
const residentCatalogues = getLoadedBuiltInLocales();
|
|
140
|
+
for (const lang of BUILT_IN_LANGUAGE_CODES) {
|
|
49
141
|
mergedResources[lang] = {
|
|
50
|
-
translation: {
|
|
51
|
-
...translations,
|
|
52
|
-
...(resources[lang] || {}),
|
|
53
|
-
},
|
|
142
|
+
translation: deepMergeTranslations((residentCatalogues[lang] ?? {}), resources[lang] || {}),
|
|
54
143
|
};
|
|
55
144
|
}
|
|
56
145
|
// Add any additional languages from resources not in built-in locales
|
|
@@ -59,14 +148,7 @@ export function createI18n(config = {}) {
|
|
|
59
148
|
mergedResources[lang] = { translation: translations };
|
|
60
149
|
}
|
|
61
150
|
}
|
|
62
|
-
|
|
63
|
-
let lng = defaultLanguage;
|
|
64
|
-
if (detectBrowserLanguage && typeof navigator !== 'undefined') {
|
|
65
|
-
const browserLang = navigator.language?.split('-')[0];
|
|
66
|
-
if (browserLang && mergedResources[browserLang]) {
|
|
67
|
-
lng = browserLang;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
151
|
+
const lng = pickInitialLanguage(config);
|
|
70
152
|
const instance = i18next.createInstance();
|
|
71
153
|
// IMPORTANT: disable react-i18next Suspense. Otherwise `useTranslation()`
|
|
72
154
|
// calls during bootstrap throw a Suspense promise (the i18n instance is
|
package/dist/index.d.ts
CHANGED
|
@@ -30,26 +30,20 @@
|
|
|
30
30
|
*
|
|
31
31
|
* @packageDocumentation
|
|
32
32
|
*/
|
|
33
|
-
export { createI18n, getDirection, getAvailableLanguages, type I18nConfig, type TranslationKeys } from './i18n';
|
|
34
|
-
export { I18nProvider, useObjectTranslation, useI18nContext, LOCALE_STORAGE_KEY, readStoredLanguage, LOCALE_SEED_STORAGE_KEY, readCachedLanguageSeed, cacheLanguageSeed, type I18nProviderProps, } from './provider';
|
|
35
|
-
export { createSafeTranslation, useSafeTranslate } from './useSafeTranslation';
|
|
36
|
-
export {
|
|
37
|
-
export {
|
|
38
|
-
export {
|
|
39
|
-
export {
|
|
40
|
-
export {
|
|
41
|
-
export { default as
|
|
42
|
-
export {
|
|
43
|
-
export {
|
|
44
|
-
export {
|
|
45
|
-
export {
|
|
46
|
-
export {
|
|
47
|
-
export {
|
|
48
|
-
export {
|
|
49
|
-
export {
|
|
50
|
-
export { isSpecTranslationData, transformSpecTranslations, type SpecTranslationData, } from './utils/index';
|
|
51
|
-
export { pickLocalized } from './pickLocalized';
|
|
52
|
-
export { LocalizationProvider, useLocalization, type LocalizationValue } from './LocalizationContext';
|
|
53
|
-
export { resolveFieldCurrency } from './currency';
|
|
54
|
-
export { useDisplayLocale } from './useDisplayLocale';
|
|
55
|
-
export { formatDisplayNumber, shouldGroupDisplayNumber, type DisplayNumberFormatOptions, } from './utils/index';
|
|
33
|
+
export { createI18n, getDirection, getAvailableLanguages, pickInitialLanguage, type I18nConfig, type TranslationKeys, } from './i18n.js';
|
|
34
|
+
export { I18nProvider, useObjectTranslation, useI18nContext, LOCALE_STORAGE_KEY, readStoredLanguage, LOCALE_SEED_STORAGE_KEY, readCachedLanguageSeed, cacheLanguageSeed, resolveBootstrapLanguage, preloadBootstrapLocale, type BootstrapLocaleOptions, type I18nProviderProps, } from './provider.js';
|
|
35
|
+
export { createSafeTranslation, useSafeTranslate } from './useSafeTranslation.js';
|
|
36
|
+
export { builtinAggregateLabels, type SafeTranslate } from './builtinAggregateLabels.js';
|
|
37
|
+
export type { TranslateFn } from './translateFn.js';
|
|
38
|
+
export { useObjectLabel, useSafeFieldLabel } from './useObjectLabel.js';
|
|
39
|
+
export { isRTL, RTL_LANGUAGES } from './locales/rtl.js';
|
|
40
|
+
export { BUILT_IN_LANGUAGE_CODES, DEFAULT_BUILT_IN_LANGUAGE, getLoadedBuiltInLocales, isBuiltInLanguage, isBuiltInLocaleLoaded, loadBuiltInLocale, type LocaleCatalogue, } from './locales/registry.js';
|
|
41
|
+
export { default as en } from './locales/en.js';
|
|
42
|
+
export { formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, type DateFormatOptions, type CurrencyFormatOptions, type NumberFormatOptions, } from './utils/index.js';
|
|
43
|
+
export { resolvePlural, formatDateSpec, formatNumberSpec, applyLocaleConfig, type SpecPluralRule, type SpecDateFormat, type SpecNumberFormat, type SpecLocaleConfig, } from './utils/index.js';
|
|
44
|
+
export { isSpecTranslationData, transformSpecTranslations, type SpecTranslationData, } from './utils/index.js';
|
|
45
|
+
export { pickLocalized, setLocalized, clearLocalized } from './pickLocalized.js';
|
|
46
|
+
export { LocalizationProvider, useLocalization, type LocalizationValue } from './LocalizationContext.js';
|
|
47
|
+
export { resolveFieldCurrency } from './currency.js';
|
|
48
|
+
export { useDisplayLocale } from './useDisplayLocale.js';
|
|
49
|
+
export { formatDisplayNumber, shouldGroupDisplayNumber, type DisplayNumberFormatOptions, } from './utils/index.js';
|
package/dist/index.js
CHANGED
|
@@ -31,46 +31,70 @@
|
|
|
31
31
|
* @packageDocumentation
|
|
32
32
|
*/
|
|
33
33
|
// Core i18n setup
|
|
34
|
-
export { createI18n, getDirection, getAvailableLanguages } from './i18n';
|
|
34
|
+
export { createI18n, getDirection, getAvailableLanguages, pickInitialLanguage, } from './i18n.js';
|
|
35
35
|
// React integration
|
|
36
36
|
export { I18nProvider, useObjectTranslation, useI18nContext,
|
|
37
37
|
// Language-preference persistence: the key the provider writes, and the
|
|
38
38
|
// reader apps that bring their own i18next instance need to honour it.
|
|
39
39
|
LOCALE_STORAGE_KEY, readStoredLanguage,
|
|
40
|
-
//
|
|
41
|
-
// choice above, and never a substitute for it. An app that fetches
|
|
42
|
-
//
|
|
40
|
+
// Locale seed (objectui#4035) — a SEPARATE slot from the explicit
|
|
41
|
+
// choice above, and never a substitute for it. An app that fetches the
|
|
42
|
+
// server's resolved locale for the last signed-in caller caches it here; the provider applies it at
|
|
43
43
|
// bootstrap only when the user has chosen nothing themselves.
|
|
44
|
-
LOCALE_SEED_STORAGE_KEY, readCachedLanguageSeed, cacheLanguageSeed,
|
|
44
|
+
LOCALE_SEED_STORAGE_KEY, readCachedLanguageSeed, cacheLanguageSeed,
|
|
45
|
+
// Resolve the boot language, and fetch its catalogue, BEFORE first render
|
|
46
|
+
// (objectui#7479). A host with an await before `createRoot().render()` spends
|
|
47
|
+
// it here and paints in the user's language; a host without one gets the
|
|
48
|
+
// documented `en`-while-loading render instead.
|
|
49
|
+
resolveBootstrapLanguage, preloadBootstrapLocale, } from './provider.js';
|
|
45
50
|
// Safe translation hook factory
|
|
46
|
-
export { createSafeTranslation, useSafeTranslate } from './useSafeTranslation';
|
|
51
|
+
export { createSafeTranslation, useSafeTranslate } from './useSafeTranslation.js';
|
|
52
|
+
// objectui#7258 — locale labels for the server's built-in default measures,
|
|
53
|
+
// handed to core's `buildChartSeries` as `builtinAggregateLabels`.
|
|
54
|
+
export { builtinAggregateLabels } from './builtinAggregateLabels.js';
|
|
47
55
|
// Convention-based object/field label i18n
|
|
48
|
-
export { useObjectLabel, useSafeFieldLabel } from './useObjectLabel';
|
|
49
|
-
// Locale packs
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
export
|
|
59
|
-
export
|
|
60
|
-
|
|
56
|
+
export { useObjectLabel, useSafeFieldLabel } from './useObjectLabel.js';
|
|
57
|
+
// Locale packs — ⛔ ONE catalogue is statically re-exported here, not ten
|
|
58
|
+
// (objectui#7479).
|
|
59
|
+
//
|
|
60
|
+
// This entry used to re-export all ten packs and `builtInLocales`, which made
|
|
61
|
+
// every one of them statically reachable from any import of this package. The
|
|
62
|
+
// console measured the result: ~450 KB gzipped of translation data in the EAGER
|
|
63
|
+
// closure, ten catalogues fetched before first paint for a viewer who reads
|
|
64
|
+
// one, and every new key paid for by every visitor in every language.
|
|
65
|
+
//
|
|
66
|
+
// ⛔ Do not restore a static re-export of a second catalogue here, and do not
|
|
67
|
+
// re-export `builtInLocales` from this entry. Both put all ten back:
|
|
68
|
+
// `builtInLocales` names every pack in one object literal, so nothing can shake
|
|
69
|
+
// it. `scripts/check-eager-locale-catalogues.mjs` fails the build if a
|
|
70
|
+
// non-active catalogue reappears in the console's eager closure, and
|
|
71
|
+
// `src/__tests__/entry-locale-shape-7479.test.ts` fails on the SOURCE shape so
|
|
72
|
+
// the reason is legible without a console build.
|
|
73
|
+
//
|
|
74
|
+
// What replaced them:
|
|
75
|
+
// - `en` stays — it is `fallbackLng`, the source of `TranslationKeys`, and
|
|
76
|
+
// the synchronous dictionary the app-shell splash renders from before i18n
|
|
77
|
+
// is usable. See `./locales/registry.js` for the full argument.
|
|
78
|
+
// - `loadBuiltInLocale(code)` fetches any of the ten on demand.
|
|
79
|
+
// - `BUILT_IN_LANGUAGE_CODES` enumerates them at zero payload.
|
|
80
|
+
// - `@object-ui/i18n/locales` is the explicit, published door to all ten for
|
|
81
|
+
// a caller that genuinely wants them all resident (the parity suites do).
|
|
82
|
+
export { isRTL, RTL_LANGUAGES } from './locales/rtl.js';
|
|
83
|
+
export { BUILT_IN_LANGUAGE_CODES, DEFAULT_BUILT_IN_LANGUAGE, getLoadedBuiltInLocales, isBuiltInLanguage, isBuiltInLocaleLoaded, loadBuiltInLocale, } from './locales/registry.js';
|
|
84
|
+
export { default as en } from './locales/en.js';
|
|
61
85
|
// Formatting utilities
|
|
62
|
-
export { formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, } from './utils/index';
|
|
86
|
+
export { formatDate, formatDateTime, formatRelativeTime, formatCurrency, formatNumber, } from './utils/index.js';
|
|
63
87
|
// Spec-aligned formatters (v2.0.7)
|
|
64
|
-
export { resolvePlural, formatDateSpec, formatNumberSpec, applyLocaleConfig, } from './utils/index';
|
|
88
|
+
export { resolvePlural, formatDateSpec, formatNumberSpec, applyLocaleConfig, } from './utils/index.js';
|
|
65
89
|
// Spec TranslationData → flat namespace transform (consumed by
|
|
66
90
|
// `useObjectLabel`). Exposed so apps don't have to copy/paste — see
|
|
67
91
|
// `apps/console/src/loadLanguage.ts` for the canonical consumer.
|
|
68
|
-
export { isSpecTranslationData, transformSpecTranslations, } from './utils/index';
|
|
69
|
-
export { pickLocalized } from './pickLocalized';
|
|
70
|
-
export { LocalizationProvider, useLocalization } from './LocalizationContext';
|
|
71
|
-
export { resolveFieldCurrency } from './currency';
|
|
92
|
+
export { isSpecTranslationData, transformSpecTranslations, } from './utils/index.js';
|
|
93
|
+
export { pickLocalized, setLocalized, clearLocalized } from './pickLocalized.js';
|
|
94
|
+
export { LocalizationProvider, useLocalization } from './LocalizationContext.js';
|
|
95
|
+
export { resolveFieldCurrency } from './currency.js';
|
|
72
96
|
// The one number-display formatter (objectui#4033) and the locale it formats
|
|
73
97
|
// in. Every field cell / widget / metric renderer goes through this pair —
|
|
74
98
|
// see `utils/number-display.ts` for the grouping policy and its interim status.
|
|
75
|
-
export { useDisplayLocale } from './useDisplayLocale';
|
|
76
|
-
export { formatDisplayNumber, shouldGroupDisplayNumber, } from './utils/index';
|
|
99
|
+
export { useDisplayLocale } from './useDisplayLocale.js';
|
|
100
|
+
export { formatDisplayNumber, shouldGroupDisplayNumber, } from './utils/index.js';
|