impact-ui 3.7.40-alpha.2 → 3.7.40-alpha.3
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/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Table/agGridLocale.js +95 -0
- package/src/components/Table/index.js +35 -5
- package/src/components/TableOld/index.js +27 -4
- package/src/stories/I18n.mdx +319 -0
- package/src/stories/LocaleDictionaryCollapse.jsx +44 -0
- package/src/stories/storybookLinks.mdx +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AGLocaleMap, BASE_LOCALE } from "../../locales/index";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AG Grid bakes these pagination labels into the panel HTML at create time.
|
|
5
|
+
* Updating localeText/getLocaleText alone will not refresh them — we patch
|
|
6
|
+
* these keys into the DOM after setAgGridLocaleText().
|
|
7
|
+
*/
|
|
8
|
+
export const AG_GRID_PAGINATION_LOCALE_KEYS = [
|
|
9
|
+
"page",
|
|
10
|
+
"to",
|
|
11
|
+
"of",
|
|
12
|
+
"firstPage",
|
|
13
|
+
"previousPage",
|
|
14
|
+
"nextPage",
|
|
15
|
+
"lastPage",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function getAgGridLocaleTextForLocale(locale) {
|
|
19
|
+
return AGLocaleMap?.[locale] || AGLocaleMap?.[BASE_LOCALE] || {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Update the mutable key→value object that getLocaleText reads from.
|
|
24
|
+
* Call this when the app locale changes (instead of remounting the grid).
|
|
25
|
+
*/
|
|
26
|
+
export function setAgGridLocaleText(localeTextRef, locale) {
|
|
27
|
+
localeTextRef.current = getAgGridLocaleTextForLocale(locale);
|
|
28
|
+
return localeTextRef.current;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* AG Grid getLocaleText callback — looks up one key at a time from the
|
|
33
|
+
* object managed by setAgGridLocaleText.
|
|
34
|
+
*/
|
|
35
|
+
export function createAgGridGetLocaleText(localeTextRef) {
|
|
36
|
+
return (params) => {
|
|
37
|
+
let localisedText = localeTextRef.current?.[params.key];
|
|
38
|
+
|
|
39
|
+
if (localisedText == null) {
|
|
40
|
+
return params.defaultValue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const variableValues = params.variableValues;
|
|
44
|
+
if (variableValues?.length) {
|
|
45
|
+
let found = 0;
|
|
46
|
+
while (found < variableValues.length) {
|
|
47
|
+
const idx = localisedText.indexOf("${variable}");
|
|
48
|
+
if (idx === -1) break;
|
|
49
|
+
localisedText =
|
|
50
|
+
localisedText.slice(0, idx) +
|
|
51
|
+
variableValues[found] +
|
|
52
|
+
localisedText.slice(idx + "${variable}".length);
|
|
53
|
+
found += 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return localisedText;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Apply the pagination keys from the current localeText object onto the
|
|
63
|
+
* already-rendered AG Grid paging panel (no grid remount).
|
|
64
|
+
*/
|
|
65
|
+
export function syncAgGridPaginationLocale(container, localeText) {
|
|
66
|
+
if (!container || !localeText) return;
|
|
67
|
+
|
|
68
|
+
container.querySelectorAll(".ag-paging-panel").forEach((panel) => {
|
|
69
|
+
const setByIdSuffix = (suffix, text) => {
|
|
70
|
+
if (text == null) return;
|
|
71
|
+
const el = panel.querySelector(`[id$="-${suffix}"]`);
|
|
72
|
+
if (el) el.textContent = text;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
setByIdSuffix("to", localeText.to);
|
|
76
|
+
setByIdSuffix("of", localeText.of);
|
|
77
|
+
setByIdSuffix("start-page", localeText.page);
|
|
78
|
+
setByIdSuffix("of-page", localeText.of);
|
|
79
|
+
|
|
80
|
+
const buttons = panel.querySelectorAll(
|
|
81
|
+
".ag-paging-page-summary-panel .ag-paging-button"
|
|
82
|
+
);
|
|
83
|
+
const buttonLabels = [
|
|
84
|
+
localeText.firstPage,
|
|
85
|
+
localeText.previousPage,
|
|
86
|
+
localeText.nextPage,
|
|
87
|
+
localeText.lastPage,
|
|
88
|
+
];
|
|
89
|
+
buttons.forEach((button, index) => {
|
|
90
|
+
if (buttonLabels[index]) {
|
|
91
|
+
button.setAttribute("aria-label", buttonLabels[index]);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -39,7 +39,12 @@ import { buildAdvanceSearchFilterModel } from "./filterUtils";
|
|
|
39
39
|
import BulkActionBar from "./actionBar";
|
|
40
40
|
import AdvanceSortModal from "./AdvanceSortModal";
|
|
41
41
|
import { useTranslation } from "../../i18n";
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
createAgGridGetLocaleText,
|
|
44
|
+
getAgGridLocaleTextForLocale,
|
|
45
|
+
setAgGridLocaleText,
|
|
46
|
+
syncAgGridPaginationLocale,
|
|
47
|
+
} from "./agGridLocale";
|
|
43
48
|
|
|
44
49
|
const TableContent = forwardRef(
|
|
45
50
|
(
|
|
@@ -159,6 +164,24 @@ const TableContent = forwardRef(
|
|
|
159
164
|
);
|
|
160
165
|
|
|
161
166
|
const { t, locale } = useTranslation();
|
|
167
|
+
const localeTextRef = useRef(getAgGridLocaleTextForLocale(locale));
|
|
168
|
+
const getLocaleText = useMemo(
|
|
169
|
+
() => createAgGridGetLocaleText(localeTextRef),
|
|
170
|
+
[]
|
|
171
|
+
);
|
|
172
|
+
const syncPaginationLocale = useCallback(() => {
|
|
173
|
+
syncAgGridPaginationLocale(
|
|
174
|
+
document.getElementById(gridId || "myGrid"),
|
|
175
|
+
localeTextRef.current
|
|
176
|
+
);
|
|
177
|
+
}, [gridId]);
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
setAgGridLocaleText(localeTextRef, locale);
|
|
181
|
+
const frameId = requestAnimationFrame(syncPaginationLocale);
|
|
182
|
+
return () => cancelAnimationFrame(frameId);
|
|
183
|
+
}, [locale, syncPaginationLocale]);
|
|
184
|
+
|
|
162
185
|
const saveViewCustomFilter = saveViewCustomFilterProp || {
|
|
163
186
|
options: [
|
|
164
187
|
{ label: t("table.viewGlobal"), value: "global" },
|
|
@@ -698,6 +721,7 @@ const TableContent = forwardRef(
|
|
|
698
721
|
|
|
699
722
|
const onFirstDataRendered = (params) => {
|
|
700
723
|
if (params?.api) params.api.getModel().appliedFontSize = tableFontSize;
|
|
724
|
+
syncPaginationLocale();
|
|
701
725
|
args?.onFirstDataRendered?.(params);
|
|
702
726
|
args?.gridOptions?.onFirstDataRendered?.(params);
|
|
703
727
|
};
|
|
@@ -731,9 +755,14 @@ const TableContent = forwardRef(
|
|
|
731
755
|
const paginationChanged = useCallback(
|
|
732
756
|
(params) => {
|
|
733
757
|
setDynmaicStyleToButtonComponent();
|
|
758
|
+
syncPaginationLocale();
|
|
734
759
|
onPaginationChanged(params);
|
|
735
760
|
},
|
|
736
|
-
[
|
|
761
|
+
[
|
|
762
|
+
setDynmaicStyleToButtonComponent,
|
|
763
|
+
syncPaginationLocale,
|
|
764
|
+
onPaginationChanged,
|
|
765
|
+
]
|
|
737
766
|
);
|
|
738
767
|
|
|
739
768
|
useEffect(() => {
|
|
@@ -1538,10 +1567,11 @@ const TableContent = forwardRef(
|
|
|
1538
1567
|
}`}
|
|
1539
1568
|
>
|
|
1540
1569
|
<AgGridReact
|
|
1541
|
-
{...(({ components, ...rest }) => rest)(
|
|
1570
|
+
{...(({ components, localeText, getLocaleText, ...rest }) => rest)(
|
|
1571
|
+
args
|
|
1572
|
+
)}
|
|
1542
1573
|
components={wrappedComponents}
|
|
1543
1574
|
ref={actualRef}
|
|
1544
|
-
key={`aggrid-${locale}`}
|
|
1545
1575
|
suppressRowTransform={
|
|
1546
1576
|
viewPoint === "list" ? false : args?.suppressRowTransform
|
|
1547
1577
|
}
|
|
@@ -1576,7 +1606,7 @@ const TableContent = forwardRef(
|
|
|
1576
1606
|
getRowClass={getRowClassWrapper}
|
|
1577
1607
|
noRowsOverlayComponent={EmptyStateOverlay}
|
|
1578
1608
|
onSortChanged={onGridSortChanged}
|
|
1579
|
-
|
|
1609
|
+
getLocaleText={getLocaleText}
|
|
1580
1610
|
/>
|
|
1581
1611
|
{isCommentFeatureEnabled && enableCommentPanel && (
|
|
1582
1612
|
<div
|
|
@@ -25,7 +25,12 @@ import TableAdvanceSearchModal from "../Table/advanceSearchModal";
|
|
|
25
25
|
import BulkActionBar from "./actionBar";
|
|
26
26
|
import { ReactComponent as EmptyStateImage } from "../../assets/emptyStateImage.svg";
|
|
27
27
|
import { useTranslation } from "../../i18n";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
createAgGridGetLocaleText,
|
|
30
|
+
getAgGridLocaleTextForLocale,
|
|
31
|
+
setAgGridLocaleText,
|
|
32
|
+
syncAgGridPaginationLocale,
|
|
33
|
+
} from "../Table/agGridLocale";
|
|
29
34
|
|
|
30
35
|
export const OldTable = forwardRef(
|
|
31
36
|
(
|
|
@@ -119,6 +124,23 @@ export const OldTable = forwardRef(
|
|
|
119
124
|
);
|
|
120
125
|
|
|
121
126
|
const { t, locale } = useTranslation();
|
|
127
|
+
const localeTextRef = useRef(getAgGridLocaleTextForLocale(locale));
|
|
128
|
+
const getLocaleText = useMemo(
|
|
129
|
+
() => createAgGridGetLocaleText(localeTextRef),
|
|
130
|
+
[]
|
|
131
|
+
);
|
|
132
|
+
const syncPaginationLocale = useCallback(() => {
|
|
133
|
+
syncAgGridPaginationLocale(
|
|
134
|
+
document.getElementById(gridId || "myGrid"),
|
|
135
|
+
localeTextRef.current
|
|
136
|
+
);
|
|
137
|
+
}, [gridId]);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
setAgGridLocaleText(localeTextRef, locale);
|
|
141
|
+
const frameId = requestAnimationFrame(syncPaginationLocale);
|
|
142
|
+
return () => cancelAnimationFrame(frameId);
|
|
143
|
+
}, [locale, syncPaginationLocale]);
|
|
122
144
|
|
|
123
145
|
const getSaveViewCustomFilter = saveViewCustomFilter ?? {
|
|
124
146
|
options: [
|
|
@@ -319,12 +341,14 @@ export const OldTable = forwardRef(
|
|
|
319
341
|
}, [columnDefs]);
|
|
320
342
|
|
|
321
343
|
const paginationChanged = (params) => {
|
|
344
|
+
syncPaginationLocale();
|
|
322
345
|
setDynmaicStyleToButtonComponent();
|
|
323
346
|
onPaginationChanged(params);
|
|
324
347
|
};
|
|
325
348
|
|
|
326
349
|
const onFirstDataRendered = (params) => {
|
|
327
350
|
if (params?.api) params.api.getModel().appliedFontSize = tableFontSize;
|
|
351
|
+
syncPaginationLocale();
|
|
328
352
|
args?.onFirstDataRendered?.(params);
|
|
329
353
|
args?.gridOptions?.onFirstDataRendered?.(params);
|
|
330
354
|
};
|
|
@@ -620,9 +644,8 @@ export const OldTable = forwardRef(
|
|
|
620
644
|
}`}
|
|
621
645
|
>
|
|
622
646
|
<AgGridReact
|
|
623
|
-
{...args}
|
|
647
|
+
{...(({ localeText, getLocaleText, ...rest }) => rest)(args)}
|
|
624
648
|
ref={actualRef}
|
|
625
|
-
key={`aggrid-${locale}`}
|
|
626
649
|
suppressRowTransform={
|
|
627
650
|
viewPoint === "list" ? false : args?.suppressRowTransform
|
|
628
651
|
}
|
|
@@ -651,7 +674,7 @@ export const OldTable = forwardRef(
|
|
|
651
674
|
onFirstDataRendered={onFirstDataRendered}
|
|
652
675
|
getRowHeight={getRowHeightWrapper}
|
|
653
676
|
getRowClass={getRowClassWrapper}
|
|
654
|
-
|
|
677
|
+
getLocaleText={getLocaleText}
|
|
655
678
|
/>
|
|
656
679
|
</div>
|
|
657
680
|
{(bottomLeftOptions || bottomCenterOptions || args.pagination) && (
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { Meta } from "@storybook/blocks";
|
|
2
|
+
import { LocaleDictionaryCollapse } from "./LocaleDictionaryCollapse";
|
|
3
|
+
|
|
4
|
+
<Meta title="Internationalization" />
|
|
5
|
+
|
|
6
|
+
<div className="ia-styles ia-guide-container i18n-guide">
|
|
7
|
+
|
|
8
|
+
# Internationalization (i18n)
|
|
9
|
+
|
|
10
|
+
`impact-ui` ships a built-in multi-language system based on React Context. Library components read their internal strings (labels, placeholders, messages, aria text, etc.) through it. Consuming apps can supply their own dictionaries, override library strings, and switch languages at runtime.
|
|
11
|
+
|
|
12
|
+
**Shipped locales:** `en-US` (complete base) and `de-DE` (German).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## How it works
|
|
17
|
+
|
|
18
|
+
Messages are resolved with a 3-layer merge (later layers win):
|
|
19
|
+
|
|
20
|
+
1. **`en-US`** — guaranteed-complete base (nothing renders blank)
|
|
21
|
+
2. **`ourDictionaries[locale]`** — impact-ui translation for the active locale (e.g. `de-DE`)
|
|
22
|
+
3. **Host messages** — from your app’s `loadMessages(locale)` (always win)
|
|
23
|
+
|
|
24
|
+
Interpolation uses `{placeholder}` tokens:
|
|
25
|
+
|
|
26
|
+
```jsx
|
|
27
|
+
t("monthPicker.placeholder", { format: "MM/YYYY" })
|
|
28
|
+
// → "Select MM/YYYY" (en-US) / "Auswählen MM/YYYY" (de-DE)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Missing keys fall back to the key string itself so nothing renders blank.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Wrap your app with `I18nProvider`
|
|
36
|
+
|
|
37
|
+
```jsx
|
|
38
|
+
import { I18nProvider } from "impact-ui";
|
|
39
|
+
|
|
40
|
+
const appMessages = {
|
|
41
|
+
"en-US": { "app.title": "My App" },
|
|
42
|
+
"de-DE": { "app.title": "Meine App" },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const App = () => (
|
|
46
|
+
<I18nProvider
|
|
47
|
+
defaultLocale="en-US"
|
|
48
|
+
loadMessages={(locale) => appMessages[locale] || {}}
|
|
49
|
+
>
|
|
50
|
+
<YourApp />
|
|
51
|
+
</I18nProvider>
|
|
52
|
+
);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`loadMessages` can also return a Promise for lazy-loaded dictionaries:
|
|
56
|
+
|
|
57
|
+
```jsx
|
|
58
|
+
loadMessages={(locale) =>
|
|
59
|
+
import(`./locales/${locale}.json`).then((m) => m.default)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
> `I18nProvider` is optional — without it, components fall back to static English and `setLocale` becomes a no-op.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 2. Translate your own strings with `useTranslation`
|
|
68
|
+
|
|
69
|
+
Use this for any text your app passes into `impact-ui` components as props (titles, menu labels, etc.). Prefix your own keys (e.g. `app.*`) to avoid clashing with library keys (`common.*`, `table.*`, …):
|
|
70
|
+
|
|
71
|
+
```jsx
|
|
72
|
+
import { useTranslation, Header } from "impact-ui";
|
|
73
|
+
|
|
74
|
+
const Example = () => {
|
|
75
|
+
const { t } = useTranslation();
|
|
76
|
+
return <Header title={t("app.header.title")} />;
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Library components (`Loader`, `Table`, `Sidebar`, `ChatBot`, `Select`, date pickers, etc.) translate their own internal strings automatically once `I18nProvider` is mounted — no extra props needed.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. Switch the active language
|
|
85
|
+
|
|
86
|
+
```jsx
|
|
87
|
+
import { useI18n } from "impact-ui";
|
|
88
|
+
|
|
89
|
+
const LanguageToggle = () => {
|
|
90
|
+
const { locale, setLocale } = useI18n();
|
|
91
|
+
return (
|
|
92
|
+
<button onClick={() => setLocale(locale === "en-US" ? "de-DE" : "en-US")}>
|
|
93
|
+
{locale}
|
|
94
|
+
</button>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Or use the `Header` built-in language switcher:
|
|
100
|
+
|
|
101
|
+
```jsx
|
|
102
|
+
import { Header, useI18n } from "impact-ui";
|
|
103
|
+
|
|
104
|
+
const AppHeader = () => {
|
|
105
|
+
const { setLocale } = useI18n();
|
|
106
|
+
return (
|
|
107
|
+
<Header
|
|
108
|
+
showLanguageIcon={true}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Date pickers also sync `moment.locale`, and AG Grid uses the matching locale pack via `AGLocaleMap`.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Override library strings
|
|
119
|
+
|
|
120
|
+
Return overrides from `loadMessages` using the same keys shipped by the library — host messages always win:
|
|
121
|
+
|
|
122
|
+
```jsx
|
|
123
|
+
loadMessages={(locale) => ({
|
|
124
|
+
"header.askIris":
|
|
125
|
+
locale === "de-DE" ? "Frag Iris" : "Ask Iris (custom)",
|
|
126
|
+
})}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Inspect shipped keys in code:
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
import { enUS, ourDictionaries } from "impact-ui";
|
|
133
|
+
|
|
134
|
+
console.log(Object.keys(enUS));
|
|
135
|
+
console.log(ourDictionaries["de-DE"]);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 5. Add a locale not shipped by the library
|
|
141
|
+
|
|
142
|
+
`impact-ui` currently maintains `en-US` and `de-DE`. For any other locale, supply your own JSON via `loadMessages`. Library keys you don’t override fall back to English.
|
|
143
|
+
|
|
144
|
+
```jsx
|
|
145
|
+
const frFR = {
|
|
146
|
+
"common.cancel": "Annuler",
|
|
147
|
+
"common.apply": "Appliquer",
|
|
148
|
+
// …override only the keys you need
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
<I18nProvider
|
|
152
|
+
defaultLocale="fr-FR"
|
|
153
|
+
loadMessages={(locale) => (locale === "fr-FR" ? frFR : {})}
|
|
154
|
+
>
|
|
155
|
+
<YourApp />
|
|
156
|
+
</I18nProvider>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Storybook tooling
|
|
162
|
+
|
|
163
|
+
Use the Storybook toolbar while browsing component stories:
|
|
164
|
+
|
|
165
|
+
- **Language** — switch between English and Deutsch
|
|
166
|
+
- **i18n Keys** — show raw keys (`common.cancel`) instead of translated text, so you can trace which key backs any visible string
|
|
167
|
+
|
|
168
|
+
For consumer-style demos, stories can declare their own dictionary via `parameters.i18nMessages` and the `I18nDemo` helper (see any `Internationalized` story).
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Available exports
|
|
173
|
+
|
|
174
|
+
| Export | Purpose |
|
|
175
|
+
| --- | --- |
|
|
176
|
+
| `I18nProvider` | Context provider — wrap your app |
|
|
177
|
+
| `useTranslation` | `{ t, locale, … }` for translating strings |
|
|
178
|
+
| `useI18n` | Full context (`locale`, `setLocale`, `messages`, `loading`, `t`) |
|
|
179
|
+
| `I18nContext` | Raw React context (advanced) |
|
|
180
|
+
| `buildMessages` / `translate` | Pure helpers (no React) |
|
|
181
|
+
| `ourDictionaries` | Map of shipped locale → dictionary |
|
|
182
|
+
| `enUS` | English base dictionary |
|
|
183
|
+
| `BASE_LOCALE` | `"en-US"` |
|
|
184
|
+
| `LOCALE_REGISTRY` | Labels + flag icons for the Header switcher |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Shipped dictionaries
|
|
189
|
+
|
|
190
|
+
Expand a panel below to inspect the live JSON used by the library. Content is imported from `src/locales/` so it stays in sync with the source files.
|
|
191
|
+
|
|
192
|
+
<LocaleDictionaryCollapse />
|
|
193
|
+
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<style>
|
|
197
|
+
{`
|
|
198
|
+
.i18n-guide h1,
|
|
199
|
+
.i18n-guide h2,
|
|
200
|
+
.i18n-guide h3 {
|
|
201
|
+
margin-bottom: 12px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.i18n-guide section,
|
|
205
|
+
.i18n-guide p {
|
|
206
|
+
margin-bottom: 12px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.i18n-guide pre {
|
|
210
|
+
background: #f6f8fa;
|
|
211
|
+
border: 1px solid #e1e4e8;
|
|
212
|
+
border-radius: 6px;
|
|
213
|
+
padding: 12px 16px;
|
|
214
|
+
overflow-x: auto;
|
|
215
|
+
font-size: 13px;
|
|
216
|
+
line-height: 1.45;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.i18n-guide table {
|
|
220
|
+
width: 100%;
|
|
221
|
+
border-collapse: collapse;
|
|
222
|
+
margin: 12px 0 24px;
|
|
223
|
+
font-size: 14px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.i18n-guide th,
|
|
227
|
+
.i18n-guide td {
|
|
228
|
+
border: 1px solid #e1e4e8;
|
|
229
|
+
padding: 8px 12px;
|
|
230
|
+
text-align: left;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.i18n-guide th {
|
|
234
|
+
background: #f6f8fa;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.i18n-dict-collapse {
|
|
238
|
+
display: flex;
|
|
239
|
+
flex-direction: column;
|
|
240
|
+
gap: 12px;
|
|
241
|
+
margin: 16px 0 32px;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.i18n-dict-panel {
|
|
245
|
+
border: 1px solid #d0d7de;
|
|
246
|
+
border-radius: 8px;
|
|
247
|
+
background: #fff;
|
|
248
|
+
overflow: hidden;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.i18n-dict-summary {
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
justify-content: space-between;
|
|
255
|
+
gap: 12px;
|
|
256
|
+
padding: 12px 16px;
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
list-style: none;
|
|
259
|
+
font-weight: 600;
|
|
260
|
+
font-size: 14px;
|
|
261
|
+
background: #f6f8fa;
|
|
262
|
+
user-select: none;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.i18n-dict-summary::-webkit-details-marker {
|
|
266
|
+
display: none;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.i18n-dict-summary::before {
|
|
270
|
+
content: "▸";
|
|
271
|
+
display: inline-block;
|
|
272
|
+
margin-right: 8px;
|
|
273
|
+
transition: transform 0.15s ease;
|
|
274
|
+
color: #57606a;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.i18n-dict-panel[open] > .i18n-dict-summary::before {
|
|
278
|
+
transform: rotate(90deg);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.i18n-dict-summary-title {
|
|
282
|
+
flex: 1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.i18n-dict-summary-meta {
|
|
286
|
+
font-weight: 500;
|
|
287
|
+
font-size: 12px;
|
|
288
|
+
color: #57606a;
|
|
289
|
+
background: #eaeef2;
|
|
290
|
+
border-radius: 999px;
|
|
291
|
+
padding: 2px 10px;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.i18n-dict-description {
|
|
295
|
+
margin: 12px 16px 0;
|
|
296
|
+
font-size: 13px;
|
|
297
|
+
color: #57606a;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.i18n-dict-pre {
|
|
301
|
+
margin: 12px 16px 16px;
|
|
302
|
+
max-height: 420px;
|
|
303
|
+
overflow: auto;
|
|
304
|
+
background: #0d1117;
|
|
305
|
+
color: #e6edf3;
|
|
306
|
+
border: none;
|
|
307
|
+
border-radius: 6px;
|
|
308
|
+
padding: 12px 16px;
|
|
309
|
+
font-size: 12px;
|
|
310
|
+
line-height: 1.5;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.i18n-dict-pre code {
|
|
314
|
+
background: transparent;
|
|
315
|
+
color: inherit;
|
|
316
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
317
|
+
}
|
|
318
|
+
`}
|
|
319
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react"; // dont remove
|
|
2
|
+
import enUS from "../locales/en-US.json";
|
|
3
|
+
import deDE from "../locales/de-DE.json";
|
|
4
|
+
|
|
5
|
+
const dictionaries = [
|
|
6
|
+
{
|
|
7
|
+
code: "en-US",
|
|
8
|
+
title: "en-US.json — English (US)",
|
|
9
|
+
description: "Complete base dictionary. Missing keys in other locales fall back here.",
|
|
10
|
+
data: enUS,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
code: "de-DE",
|
|
14
|
+
title: "de-DE.json — Deutsch",
|
|
15
|
+
description: "German translations shipped by impact-ui. Partial keys fall back to en-US.",
|
|
16
|
+
data: deDE,
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Collapsible viewer for the shipped locale JSON files.
|
|
22
|
+
* Used by I18n.mdx so the full dictionaries don't dominate the docs page.
|
|
23
|
+
*/
|
|
24
|
+
export function LocaleDictionaryCollapse() {
|
|
25
|
+
return (
|
|
26
|
+
<div className="i18n-dict-collapse">
|
|
27
|
+
{dictionaries.map(({ code, title, description, data }) => {
|
|
28
|
+
const keyCount = Object.keys(data).length;
|
|
29
|
+
return (
|
|
30
|
+
<details key={code} className="i18n-dict-panel">
|
|
31
|
+
<summary className="i18n-dict-summary">
|
|
32
|
+
<span className="i18n-dict-summary-title">{title}</span>
|
|
33
|
+
<span className="i18n-dict-summary-meta">{keyCount} keys</span>
|
|
34
|
+
</summary>
|
|
35
|
+
<p className="i18n-dict-description">{description}</p>
|
|
36
|
+
<pre className="i18n-dict-pre">
|
|
37
|
+
<code style={{color:'black'}}>{JSON.stringify(data, null, 2)}</code>
|
|
38
|
+
</pre>
|
|
39
|
+
</details>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -6,6 +6,7 @@ import { Meta } from "@storybook/blocks";
|
|
|
6
6
|
|
|
7
7
|
## Core Documentation
|
|
8
8
|
- [Getting Started](https://impact-ui-v3.iaproducts.ai/?path=/docs/getting-started--docs)
|
|
9
|
+
- [Internationalization](https://impact-ui-v3.iaproducts.ai/?path=/docs/internationalization--docs)
|
|
9
10
|
- [Change Log](https://impact-ui-v3.iaproducts.ai/?path=/docs/change-log--docs)
|
|
10
11
|
|
|
11
12
|
## Patterns
|