@asafarim/shared-i18n 0.5.2 → 0.6.1

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.
Files changed (61) hide show
  1. package/README.md +170 -168
  2. package/demo/index.html +13 -0
  3. package/demo/node_modules/.bin/tsc +21 -0
  4. package/demo/node_modules/.bin/tsserver +21 -0
  5. package/demo/node_modules/.bin/vite +21 -0
  6. package/demo/package.json +24 -0
  7. package/demo/public/favicon.svg +4 -0
  8. package/demo/public/logo.svg +24 -0
  9. package/demo/src/App.tsx +112 -0
  10. package/demo/src/components/GetStartedSection.tsx +56 -0
  11. package/demo/src/components/KeyTable.tsx +29 -0
  12. package/demo/src/components/LanguageBar.tsx +19 -0
  13. package/demo/src/components/LanguageSwitcherDemo.module.css +113 -0
  14. package/demo/src/components/LanguageSwitcherDemo.tsx +184 -0
  15. package/demo/src/components/OverviewSection.tsx +43 -0
  16. package/demo/src/components/Panel.tsx +15 -0
  17. package/demo/src/components/StatusCard.tsx +109 -0
  18. package/demo/src/index.css +569 -0
  19. package/demo/src/locales/en/demo.json +85 -0
  20. package/demo/src/locales/fr/demo.json +85 -0
  21. package/demo/src/locales/nl/demo.json +85 -0
  22. package/demo/src/main.tsx +24 -0
  23. package/demo/tsconfig.json +18 -0
  24. package/demo/tsconfig.node.json +10 -0
  25. package/demo/vite-env.d.ts +7 -0
  26. package/demo/vite.config.ts +11 -0
  27. package/dist/components/LanguageSwitcher.d.ts +20 -0
  28. package/dist/components/LanguageSwitcher.d.ts.map +1 -0
  29. package/dist/components/LanguageSwitcher.js +72 -0
  30. package/dist/components/LanguageSwitcher.module.css +205 -0
  31. package/dist/config/i18n.d.ts +46 -0
  32. package/dist/config/i18n.d.ts.map +1 -0
  33. package/dist/config/i18n.js +66 -0
  34. package/dist/hooks/useLanguage.d.ts +12 -0
  35. package/dist/hooks/useLanguage.d.ts.map +1 -0
  36. package/dist/hooks/useLanguage.js +61 -0
  37. package/dist/index.d.ts +9 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +10 -0
  40. package/dist/locales/en/common.json +68 -0
  41. package/{locales → dist/locales}/en/identity-portal.json +69 -69
  42. package/dist/locales/nl/common.json +68 -0
  43. package/{locales → dist/locales}/nl/identity-portal.json +69 -69
  44. package/dist/tsconfig.tsbuildinfo +1 -0
  45. package/dist/utils/languageIcons.d.ts +4 -0
  46. package/dist/utils/languageIcons.d.ts.map +1 -0
  47. package/dist/utils/languageIcons.js +12 -0
  48. package/dist/utils/languageUtils.d.ts +45 -0
  49. package/dist/utils/languageUtils.d.ts.map +1 -0
  50. package/dist/utils/languageUtils.js +144 -0
  51. package/package.json +34 -22
  52. package/LICENSE +0 -23
  53. package/config/i18n.ts +0 -89
  54. package/hooks/useLanguage.ts +0 -80
  55. package/index.ts +0 -21
  56. package/locales/en/common.json +0 -66
  57. package/locales/en/web.json +0 -343
  58. package/locales/nl/common.json +0 -66
  59. package/locales/nl/web.json +0 -343
  60. package/tsconfig.json +0 -32
  61. package/utils/languageUtils.ts +0 -141
package/README.md CHANGED
@@ -1,168 +1,170 @@
1
- # @asafarim/shared-i18n
2
-
3
- Lightweight, simple translation module for any React + TypeScript app, built on top of i18next and react-i18next. It ships with sensible defaults (English and Dutch) but can support any language by adding JSON files to your locales folder.
4
-
5
- ## Features
6
-
7
- - 🌍 Works in any React + TypeScript project (monorepo or standalone)
8
- - 🗂️ JSON-based translations per language and namespace
9
- - 🔄 Cookie-based language persistence (browser) with automatic detection
10
- - ⚙️ Optional backend sync for user language preferences
11
- - ⚡ Lazy loading support for app-specific translations
12
- - 🪝 React hooks for language management (useLanguage) and translations (useTranslation)
13
-
14
- ## Installation
15
-
16
- ```bash
17
- pnpm add @asafarim/shared-i18n
18
- # or
19
- npm i @asafarim/shared-i18n
20
- # or
21
- yarn add @asafarim/shared-i18n
22
- ```
23
-
24
- ## Usage
25
-
26
- ### 1) Initialize i18n in your app
27
-
28
- In your app's main entry point (e.g., `main.tsx`):
29
-
30
- ```tsx
31
- import { StrictMode } from 'react';
32
- import { createRoot } from 'react-dom/client';
33
- import { initI18n } from '@asafarim/shared-i18n';
34
- import App from './App';
35
-
36
- // Optional: import your app-specific translations
37
- import enApp from './locales/en/app.json';
38
- import nlApp from './locales/nl/app.json';
39
-
40
- initI18n({
41
- defaultNS: 'common',
42
- ns: ['common', 'app'],
43
- resources: {
44
- en: { app: enApp },
45
- nl: { app: nlApp }
46
- }
47
- });
48
-
49
- createRoot(document.getElementById('root')!).render(
50
- <StrictMode>
51
- <App />
52
- </StrictMode>
53
- );
54
- ```
55
-
56
- ### 2) Use translations in components
57
-
58
- ```tsx
59
- import { useTranslation } from '@asafarim/shared-i18n';
60
-
61
- function MyComponent() {
62
- const { t } = useTranslation();
63
- return (
64
- <div>
65
- <h1>{t('welcome')}</h1>
66
- <p>{t('app:customKey')}</p>
67
- </div>
68
- );
69
- }
70
- ```
71
-
72
- ### 3) Add a language switcher
73
-
74
- If you use @asafarim/shared-ui-react you can leverage its LanguageSwitcher component. Otherwise, call useLanguage directly.
75
-
76
- ```tsx
77
- import { useLanguage } from '@asafarim/shared-i18n';
78
-
79
- export function LanguageToggle() {
80
- const { language, changeLanguage, isChanging } = useLanguage();
81
- return (
82
- <button onClick={() => changeLanguage(language === 'en' ? 'nl' : 'en')} disabled={isChanging}>
83
- Switch language (current: {language})
84
- </button>
85
- );
86
- }
87
- ```
88
-
89
- ## Add more languages
90
-
91
- Yes — you can support any language by adding the required JSON files to your locales folder. For example:
92
-
93
- ```
94
- your-app/
95
- src/
96
- locales/
97
- en/
98
- app.json
99
- nl/
100
- app.json
101
- anotherLang/
102
- new.json
103
- ```
104
-
105
- Then include them when initializing:
106
-
107
- ```tsx
108
- import anotherLang from './locales/anotherLang/new.json';
109
-
110
- initI18n({
111
- ns: ['common', 'app', 'new'],
112
- resources: {
113
- anotherLang: { new: anotherLang }
114
- },
115
- supportedLngs: ['en', 'nl', 'anotherLang'],
116
- defaultLanguage: 'en'
117
- });
118
- ```
119
-
120
- Notes:
121
- - If you pass supportedLngs, it will override the default supported languages.
122
- - defaultLanguage overrides the default fallback (which is English).
123
-
124
- ## API Reference
125
-
126
- ### initI18n(config?: I18nConfig)
127
-
128
- Initialize i18next with the shared configuration.
129
-
130
- Parameters:
131
- - config.defaultNS — Default namespace (default: 'common')
132
- - config.ns — Namespaces to load (default: ['common'])
133
- - config.resourcesApp-specific translation resources
134
- - config.supportedLngsOptional list of supported language codes to enable
135
- - config.defaultLanguageOptional fallback language code
136
-
137
- ### useLanguage()
138
-
139
- Hook for managing language preferences.
140
-
141
- Returns:
142
- - language — Current language code
143
- - changeLanguage(lang) — Function to change language
144
- - isChanging — Boolean indicating if language change is in progress
145
-
146
- ### useTranslation()
147
-
148
- Re-exported from react-i18next. See official docs.
149
-
150
- ## Cookie and backend integration
151
-
152
- - A preferredLanguage cookie is used to persist the selected language in the browser.
153
- - If your environment provides an Identity API, updateUserLanguagePreference can sync the preference server-side. If not, the library still works fully client-side.
154
-
155
- To point to a backend, optionally set:
156
-
157
- ```env
158
- VITE_IDENTITY_API_URL=https://your-identity.example.com
159
- ```
160
-
161
- ## Built-in translations
162
-
163
- This package ships with default English and Dutch common translations. You can ignore them and supply your own resources if preferred.
164
-
165
- ## License
166
-
167
- MIT © ASafariM
168
-
1
+ # @asafarim/shared-i18n
2
+
3
+ Lightweight, simple translation module for any React + TypeScript app, built on top of i18next and react-i18next. It ships with sensible defaults (English and Dutch) but can support any language by adding JSON files to your locales folder.
4
+
5
+ ## Features
6
+
7
+ - 🌍 Works in any React + TypeScript project (monorepo or standalone)
8
+ - 🗂️ JSON-based translations per language and namespace
9
+ - 🔄 Cookie-based language persistence (browser) with automatic detection
10
+ - ⚙️ Optional backend sync for user language preferences
11
+ - ⚡ Lazy loading support for app-specific translations
12
+ - 🪝 React hooks for language management (useLanguage) and translations (useTranslation)
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @asafarim/shared-i18n
18
+ # or
19
+ npm i @asafarim/shared-i18n
20
+ # or
21
+ yarn add @asafarim/shared-i18n
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### 1) Initialize i18n in your app
27
+
28
+ In your app's main entry point (e.g., `main.tsx`):
29
+
30
+ ```tsx
31
+ import { StrictMode } from 'react';
32
+ import { createRoot } from 'react-dom/client';
33
+ import { initI18n } from '@asafarim/shared-i18n';
34
+ import App from './App';
35
+
36
+ // Optional: import your app-specific translations
37
+ import enApp from './locales/en/app.json';
38
+ import nlApp from './locales/nl/app.json';
39
+
40
+ initI18n({
41
+ defaultNS: 'common',
42
+ ns: ['common', 'app'],
43
+ resources: {
44
+ en: { app: enApp },
45
+ nl: { app: nlApp }
46
+ }
47
+ });
48
+
49
+ createRoot(document.getElementById('root')!).render(
50
+ <StrictMode>
51
+ <App />
52
+ </StrictMode>
53
+ );
54
+ ```
55
+
56
+ ### 2) Use translations in components
57
+
58
+ ```tsx
59
+ import { useTranslation } from '@asafarim/shared-i18n';
60
+
61
+ function MyComponent() {
62
+ const { t } = useTranslation();
63
+ return (
64
+ <div>
65
+ <h1>{t('welcome')}</h1>
66
+ <p>{t('app:customKey')}</p>
67
+ </div>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ### 3) Add a language switcher
73
+
74
+ If you use @asafarim/shared-ui-react you can leverage its LanguageSwitcher component. Otherwise, call useLanguage directly.
75
+
76
+ ```tsx
77
+ import { useLanguage } from '@asafarim/shared-i18n';
78
+
79
+ export function LanguageToggle() {
80
+ const { language, changeLanguage, isChanging } = useLanguage();
81
+ return (
82
+ <button onClick={() => changeLanguage(language === 'en' ? 'nl' : 'en')} disabled={isChanging}>
83
+ Switch language (current: {language})
84
+ </button>
85
+ );
86
+ }
87
+ ```
88
+
89
+ ## Add more languages
90
+
91
+ Yes — you can support any language by adding the required JSON files to your locales folder. For example:
92
+
93
+ ```
94
+ your-app/
95
+ src/
96
+ locales/
97
+ en/
98
+ app.json
99
+ nl/
100
+ app.json
101
+ anotherLang/
102
+ new.json
103
+ ```
104
+
105
+ Then include them when initializing:
106
+
107
+ ```tsx
108
+ import anotherLang from './locales/anotherLang/new.json';
109
+
110
+ initI18n({
111
+ ns: ['common', 'app', 'new'],
112
+ resources: {
113
+ anotherLang: { new: anotherLang }
114
+ },
115
+ supportedLngs: ['en', 'nl', 'anotherLang'],
116
+ defaultLanguage: 'en'
117
+ });
118
+ ```
119
+
120
+ Notes:
121
+
122
+ - If you pass supportedLngs, it will override the default supported languages.
123
+ - defaultLanguage overrides the default fallback (which is English).
124
+
125
+ ## API Reference
126
+
127
+ ### initI18n(config?: I18nConfig)
128
+
129
+ Initialize i18next with the shared configuration.
130
+
131
+ Parameters:
132
+
133
+ - config.defaultNSDefault namespace (default: 'common')
134
+ - config.nsNamespaces to load (default: ['common'])
135
+ - config.resourcesApp-specific translation resources
136
+ - config.supportedLngs — Optional list of supported language codes to enable
137
+ - config.defaultLanguage — Optional fallback language code
138
+
139
+ ### useLanguage()
140
+
141
+ Hook for managing language preferences.
142
+
143
+ Returns:
144
+
145
+ - language — Current language code
146
+ - changeLanguage(lang) — Function to change language
147
+ - isChanging — Boolean indicating if language change is in progress
148
+
149
+ ### useTranslation()
150
+
151
+ Re-exported from react-i18next. See official docs.
152
+
153
+ ## Cookie and backend integration
154
+
155
+ - A preferredLanguage cookie is used to persist the selected language in the browser.
156
+ - If your environment provides an Identity API, updateUserLanguagePreference can sync the preference server-side. If not, the library still works fully client-side.
157
+
158
+ To point to a backend, optionally set:
159
+
160
+ ```env
161
+ VITE_IDENTITY_API_URL=https://your-identity.example.com
162
+ ```
163
+
164
+ ## Built-in translations
165
+
166
+ This package ships with default English and Dutch common translations. You can ignore them and supply your own resources if preferred.
167
+
168
+ ## License
169
+
170
+ MIT © ASafariM
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="./public/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>@asafarim/shared-i18n Demo</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="./src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsc" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules/typescript/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/typescript@5.8.3/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules/vite/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules/vite/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules/vite/bin/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules/vite/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/vite@6.4.1_@types+node@24.10.4/node_modules:/home/runner/work/shared-i18n/shared-i18n/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
19
+ else
20
+ exec node "$basedir/../vite/bin/vite.js" "$@"
21
+ fi
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "shared-i18n-demo",
3
+ "version": "0.6.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^19.0.0",
13
+ "react-dom": "^19.0.0",
14
+ "@asafarim/shared-i18n": "workspace:*",
15
+ "@asafarim/design-tokens": "^0.5.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/react": "^19.0.0",
19
+ "@types/react-dom": "^19.0.0",
20
+ "@vitejs/plugin-react": "^4.3.0",
21
+ "typescript": "~5.8.3",
22
+ "vite": "^6.0.0"
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
2
+ <rect width="100" height="100" fill="#4F46E5"/>
3
+ <text x="50" y="65" font-size="60" font-weight="bold" text-anchor="middle" fill="white">i18n</text>
4
+ </svg>
@@ -0,0 +1,24 @@
1
+ <svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#3A5AFE" />
5
+ <stop offset="100%" stop-color="#14B8A6" />
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0%" y1="0%" x2="0%" y2="100%">
8
+ <stop offset="0%" stop-color="#FFFFFF" stop-opacity="0.9" />
9
+ <stop offset="100%" stop-color="#FFFFFF" stop-opacity="0.2" />
10
+ </linearGradient>
11
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
12
+ <feDropShadow dx="0" dy="8" stdDeviation="12" flood-color="#0B1220" flood-opacity="0.28" />
13
+ </filter>
14
+ </defs>
15
+ <rect width="180" height="180" rx="32" fill="url(#bg)" filter="url(#shadow)" />
16
+ <g transform="translate(45 45)">
17
+ <path d="M45 0C20.147 0 0 20.147 0 45C0 69.853 20.147 90 45 90C69.853 90 90 69.853 90 45C90 20.147 69.853 0 45 0ZM45 72C29.088 72 15.75 58.662 15.75 42.75C15.75 26.838 29.088 13.5 45 13.5C60.912 13.5 74.25 26.838 74.25 42.75C74.25 58.662 60.912 72 45 72Z" fill="white" opacity="0.2" />
18
+ <path d="M24 27H66C70.971 27 75 31.029 75 36V54C75 58.971 70.971 63 66 63H24C19.029 63 15 58.971 15 54V36C15 31.029 19.029 27 24 27Z" fill="url(#accent)" />
19
+ <path d="M36 54V45C36 40.029 40.029 36 45 36C49.971 36 54 40.029 54 45V54" stroke="#0B1220" stroke-width="4" stroke-linecap="round" />
20
+ <circle cx="45" cy="21" r="6" fill="white" />
21
+ <path d="M33 69H57" stroke="white" stroke-width="4" stroke-linecap="round" opacity="0.7" />
22
+ <path d="M27 15H63" stroke="white" stroke-width="4" stroke-linecap="round" opacity="0.4" />
23
+ </g>
24
+ </svg>
@@ -0,0 +1,112 @@
1
+ import { useState } from 'react'
2
+ import { useTranslation } from '@asafarim/shared-i18n'
3
+ import LanguageBar from './components/LanguageBar'
4
+ import Panel from './components/Panel'
5
+ import KeyTable from './components/KeyTable'
6
+ import StatusCard from './components/StatusCard'
7
+ import OverviewSection from './components/OverviewSection'
8
+ import GetStartedSection from './components/GetStartedSection'
9
+ import LanguageSwitcherDemo from './components/LanguageSwitcherDemo'
10
+
11
+ type TabType = 'overview' | 'getStarted' | 'demo'
12
+
13
+ export default function App() {
14
+ const { t } = useTranslation('demo')
15
+ const [activeTab, setActiveTab] = useState<TabType>('overview')
16
+
17
+ return (
18
+ <div className="app-container">
19
+ <header className="app-header">
20
+ <div className="header-content">
21
+ <h1>{t('title')}</h1>
22
+ <p className="subtitle">{t('subtitle')}</p>
23
+ </div>
24
+ <LanguageBar />
25
+ </header>
26
+
27
+ <main className="app-main">
28
+ <div className="nav-tabs">
29
+ <button
30
+ className={`nav-tab ${activeTab === 'overview' ? 'active' : ''}`}
31
+ onClick={() => setActiveTab('overview')}
32
+ >
33
+ Overview
34
+ </button>
35
+ <button
36
+ className={`nav-tab ${activeTab === 'getStarted' ? 'active' : ''}`}
37
+ onClick={() => setActiveTab('getStarted')}
38
+ >
39
+ Get Started
40
+ </button>
41
+ <button
42
+ className={`nav-tab ${activeTab === 'demo' ? 'active' : ''}`}
43
+ onClick={() => setActiveTab('demo')}
44
+ >
45
+ Demo
46
+ </button>
47
+ </div>
48
+
49
+ {activeTab === 'overview' && <OverviewSection />}
50
+
51
+ {activeTab === 'getStarted' && <GetStartedSection />}
52
+
53
+ {activeTab === 'demo' && (
54
+ <>
55
+ <Panel title="LanguageSwitcher Component">
56
+ <LanguageSwitcherDemo />
57
+ </Panel>
58
+
59
+ <Panel title="Translations">
60
+ <div className="panel-grid">
61
+ <div>
62
+ <h3>Common Namespace</h3>
63
+ <KeyTable
64
+ namespace="common"
65
+ keys={['welcome', 'language', 'apps.appName.identity', 'apps.description.identity']}
66
+ />
67
+ </div>
68
+ <div>
69
+ <h3>Identity Portal Namespace</h3>
70
+ <KeyTable
71
+ namespace="identityPortal"
72
+ keys={[
73
+ 'navbar.admin-area',
74
+ 'navbar.auth.signIn',
75
+ 'navbar.auth.signOut',
76
+ 'dashboard.title',
77
+ 'dashboard.user-management.title'
78
+ ]}
79
+ />
80
+ </div>
81
+ </div>
82
+ </Panel>
83
+
84
+ <Panel title="Interpolation & Trans Component">
85
+ <InterpolationDemo />
86
+ </Panel>
87
+
88
+ <StatusCard />
89
+ </>
90
+ )}
91
+ </main>
92
+ </div>
93
+ )
94
+ }
95
+
96
+ function InterpolationDemo() {
97
+ const { t, i18n } = useTranslation('identityPortal')
98
+
99
+ return (
100
+ <div className="interpolation-demo">
101
+ <div className="demo-item">
102
+ <h4>navbar.auth.welcome with interpolation</h4>
103
+ <p className="demo-output">
104
+ {t('navbar.auth.welcome', { userName: 'Ali' })}
105
+ </p>
106
+ </div>
107
+ <div className="demo-item">
108
+ <h4>Current language: {i18n.language}</h4>
109
+ </div>
110
+ </div>
111
+ )
112
+ }
@@ -0,0 +1,56 @@
1
+ import { useState } from 'react'
2
+ import { useTranslation } from '@asafarim/shared-i18n'
3
+
4
+ export default function GetStartedSection() {
5
+ const { t } = useTranslation('demo')
6
+ const [expandedStep, setExpandedStep] = useState<number>(0)
7
+ const getStarted = t('getStarted', { returnObjects: true }) as any
8
+
9
+ return (
10
+ <div className="get-started-section">
11
+ <div className="gs-header">
12
+ <h2 className="gs-title">{getStarted.heading}</h2>
13
+ <p className="gs-intro">{getStarted.intro}</p>
14
+ </div>
15
+
16
+ <div className="steps-container">
17
+ {getStarted.steps.map((step: any, idx: number) => (
18
+ <div key={idx} className="step-item">
19
+ <button
20
+ className={`step-header ${expandedStep === idx ? 'expanded' : ''}`}
21
+ onClick={() => setExpandedStep(expandedStep === idx ? -1 : idx)}
22
+ >
23
+ <div className="step-number">{idx + 1}</div>
24
+ <div className="step-info">
25
+ <h3>{step.title}</h3>
26
+ <p>{step.description}</p>
27
+ </div>
28
+ <div className="step-toggle">
29
+ {expandedStep === idx ? '−' : '+'}
30
+ </div>
31
+ </button>
32
+ {expandedStep === idx && (
33
+ <div className="step-content">
34
+ <pre className="code-block">
35
+ <code>{step.code}</code>
36
+ </pre>
37
+ </div>
38
+ )}
39
+ </div>
40
+ ))}
41
+ </div>
42
+
43
+ <div className="tips-section">
44
+ <div className="tips-icon">💡</div>
45
+ <div className="tips-content">
46
+ <h3>{getStarted.tips.title}</h3>
47
+ <ul className="tips-list">
48
+ {getStarted.tips.items.map((tip: string, idx: number) => (
49
+ <li key={idx}>{tip}</li>
50
+ ))}
51
+ </ul>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ )
56
+ }