@asafarim/shared-i18n 0.8.1 β 0.9.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/README.md +266 -350
- package/demo/README.md +119 -148
- package/demo/index.html +12 -1
- package/demo/node_modules/.bin/kill-port +17 -0
- package/demo/node_modules/.bin/tsc +5 -9
- package/demo/node_modules/.bin/tsserver +5 -9
- package/demo/node_modules/.bin/vite +5 -9
- package/demo/package.json +7 -4
- package/demo/public/404.html +24 -0
- package/demo/public/favicon.svg +4 -4
- package/demo/public/logo.svg +24 -24
- package/demo/src/App.tsx +178 -129
- package/demo/src/components/CountryLanguageSelectorsPage.tsx +240 -0
- package/demo/src/components/GetStartedSection.tsx +56 -56
- package/demo/src/components/KeyTable.tsx +29 -29
- package/demo/src/components/LanguageBar.tsx +145 -103
- package/demo/src/components/LanguageSwitcherDemo.module.css +114 -114
- package/demo/src/components/LanguageSwitchersPage.tsx +245 -0
- package/demo/src/components/Logo.tsx +6 -6
- package/demo/src/components/OverviewSection.tsx +58 -43
- package/demo/src/components/Panel.tsx +15 -15
- package/demo/src/components/RoutingLabPage.tsx +147 -0
- package/demo/src/components/StatusCard.tsx +109 -109
- package/demo/src/data/countries.ts +48 -0
- package/demo/src/i18n/localeAdapter.ts +91 -0
- package/demo/src/i18n/localeRouting.ts +77 -0
- package/demo/src/index.css +1075 -644
- package/demo/src/locales/de/demo.json +202 -84
- package/demo/src/locales/en/demo.json +201 -85
- package/demo/src/locales/fr/demo.json +203 -85
- package/demo/src/locales/it/demo.json +202 -84
- package/demo/src/locales/lb/demo.json +201 -0
- package/demo/src/locales/nl/demo.json +203 -85
- package/demo/src/main.tsx +32 -29
- package/demo/tsconfig.json +18 -18
- package/demo/tsconfig.node.json +10 -10
- package/demo/tsconfig.tsbuildinfo +1 -1
- package/demo/vite-env.d.ts +7 -7
- package/demo/vite.config.d.ts +2 -2
- package/demo/vite.config.js +10 -10
- package/dist/components/LanguageSwitcher.module.css +303 -303
- package/dist/country-language-selector.css +431 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -5
- package/demo/dist/Icon Dropdown_Limited Languages.png +0 -0
- package/demo/dist/Select Dropdown_Text Only.png +0 -0
- package/demo/dist/assets/favicon-BZYZvBLo.svg +0 -4
- package/demo/dist/assets/index-BdjqKw_N.css +0 -1
- package/demo/dist/assets/index-C1Tq1uEr.js +0 -191
- package/demo/dist/favicon.svg +0 -4
- package/demo/dist/index.html +0 -27
- package/demo/dist/logo.svg +0 -24
- package/demo/node_modules/.bin/browserslist +0 -21
- package/demo/node_modules/.bin/browserslist.CMD +0 -12
- package/demo/node_modules/.bin/browserslist.ps1 +0 -41
- package/demo/node_modules/.bin/tsc.CMD +0 -12
- package/demo/node_modules/.bin/tsc.ps1 +0 -41
- package/demo/node_modules/.bin/tsserver.CMD +0 -12
- package/demo/node_modules/.bin/tsserver.ps1 +0 -41
- package/demo/node_modules/.bin/vite.CMD +0 -12
- package/demo/node_modules/.bin/vite.ps1 +0 -41
- package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js +0 -848
- package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js.map +0 -7
- package/demo/node_modules/.vite/deps/_metadata.json +0 -76
- package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
- package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
- package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js +0 -1004
- package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js.map +0 -7
- package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js +0 -292
- package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js.map +0 -7
- package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js +0 -280
- package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js.map +0 -7
- package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js +0 -400
- package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js.map +0 -7
- package/demo/node_modules/.vite/deps/i18next.js +0 -2392
- package/demo/node_modules/.vite/deps/i18next.js.map +0 -7
- package/demo/node_modules/.vite/deps/package.json +0 -3
- package/demo/node_modules/.vite/deps/react-dom.js +0 -6
- package/demo/node_modules/.vite/deps/react-dom.js.map +0 -7
- package/demo/node_modules/.vite/deps/react-dom_client.js +0 -20217
- package/demo/node_modules/.vite/deps/react-dom_client.js.map +0 -7
- package/demo/node_modules/.vite/deps/react-i18next.js +0 -869
- package/demo/node_modules/.vite/deps/react-i18next.js.map +0 -7
- package/demo/node_modules/.vite/deps/react.js +0 -5
- package/demo/node_modules/.vite/deps/react.js.map +0 -7
- package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -278
- package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
- package/demo/node_modules/.vite/deps/react_jsx-runtime.js +0 -6
- package/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
- package/demo/src/components/CountryLanguageDemo.tsx +0 -140
- package/demo/src/components/LanguageSwitcherDemo.tsx +0 -256
package/README.md
CHANGED
|
@@ -1,350 +1,266 @@
|
|
|
1
|
-
# @asafarim/shared-i18n
|
|
2
|
-
|
|
3
|
-
Lightweight
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- π Works in any React + TypeScript project (monorepo or standalone)
|
|
10
|
-
- ποΈ JSON-based translations per language and namespace
|
|
11
|
-
- π Cookie-based language persistence
|
|
12
|
-
- βοΈ Optional backend sync for user language preferences
|
|
13
|
-
- β‘ Lazy loading support for app-specific translations
|
|
14
|
-
- πͺ React hooks
|
|
15
|
-
- π¨
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
npm i @asafarim/shared-i18n
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
### 1
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import
|
|
36
|
-
|
|
37
|
-
import
|
|
38
|
-
import
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
### Integration Notes
|
|
269
|
-
|
|
270
|
-
- The selector automatically handles countryβlanguage relationships
|
|
271
|
-
- Language changes integrate seamlessly with shared-i18n's `useTranslation()` hook
|
|
272
|
-
- Locale persists to localStorage for user preference retention
|
|
273
|
-
- See [full documentation](https://alisafari-it.github.io/country-language-switch/#/get-started) for additional props and customization options
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
### initI18n(config?: I18nConfig)
|
|
277
|
-
|
|
278
|
-
Initialize i18next with the shared configuration.
|
|
279
|
-
|
|
280
|
-
Parameters:
|
|
281
|
-
|
|
282
|
-
- config.defaultNS β Default namespace (default: 'common')
|
|
283
|
-
- config.ns β Namespaces to load (default: ['common'])
|
|
284
|
-
- config.resources β App-specific translation resources
|
|
285
|
-
- config.supportedLngs β Optional list of supported language codes to enable
|
|
286
|
-
- config.defaultLanguage β Optional fallback language code
|
|
287
|
-
|
|
288
|
-
### useLanguage()
|
|
289
|
-
|
|
290
|
-
Hook for managing language preferences.
|
|
291
|
-
|
|
292
|
-
Returns:
|
|
293
|
-
|
|
294
|
-
- language β Current language code
|
|
295
|
-
- changeLanguage(lang) β Function to change language
|
|
296
|
-
- isChanging β Boolean indicating if language change is in progress
|
|
297
|
-
|
|
298
|
-
### useTranslation()
|
|
299
|
-
|
|
300
|
-
Re-exported from react-i18next. See official docs.
|
|
301
|
-
|
|
302
|
-
### getApiUrl(envVarName?, defaultUrl?)
|
|
303
|
-
|
|
304
|
-
Configurable API URL resolver for flexible backend integration.
|
|
305
|
-
|
|
306
|
-
Parameters:
|
|
307
|
-
|
|
308
|
-
- envVarName β Environment variable name to check (default: 'VITE_API_URL')
|
|
309
|
-
- defaultUrl β Default URL if no env var is set (default: 'http://localhost')
|
|
310
|
-
|
|
311
|
-
Returns: Resolved API URL string
|
|
312
|
-
|
|
313
|
-
### setApiUrlResolver(resolver)
|
|
314
|
-
|
|
315
|
-
Set a custom API URL resolver function for complete control over URL resolution.
|
|
316
|
-
|
|
317
|
-
Parameters:
|
|
318
|
-
|
|
319
|
-
- resolver β Function that returns the API URL string
|
|
320
|
-
|
|
321
|
-
Example:
|
|
322
|
-
|
|
323
|
-
```tsx
|
|
324
|
-
import { setApiUrlResolver, getApiUrl } from '@asafarim/shared-i18n/utils';
|
|
325
|
-
|
|
326
|
-
// Custom resolver
|
|
327
|
-
setApiUrlResolver(() => 'https://my-custom-api.example.com');
|
|
328
|
-
|
|
329
|
-
// Or use environment variables
|
|
330
|
-
const url = getApiUrl('VITE_CUSTOM_API_URL', 'https://fallback.example.com');
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## Cookie and backend integration
|
|
334
|
-
|
|
335
|
-
- A preferredLanguage cookie is used to persist the selected language in the browser.
|
|
336
|
-
- If your environment provides an Identity API, updateUserLanguagePreference can sync the preference server-side. If not, the library still works fully client-side.
|
|
337
|
-
|
|
338
|
-
To point to a backend, optionally set:
|
|
339
|
-
|
|
340
|
-
```env
|
|
341
|
-
VITE_IDENTITY_API_URL=https://your-identity.example.com
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
## Built-in translations
|
|
345
|
-
|
|
346
|
-
This package ships with default English and Dutch common translations. You can ignore them and supply your own resources if preferred.
|
|
347
|
-
|
|
348
|
-
## License
|
|
349
|
-
|
|
350
|
-
MIT Β© ASafariM
|
|
1
|
+
# @asafarim/shared-i18n
|
|
2
|
+
|
|
3
|
+
Lightweight React + TypeScript i18n package built on top of i18next and react-i18next. Ships with sensible defaults (English and Dutch) but supports any language. Includes a **LanguageSwitcher** and, new in v0.9, re-exports the full **CountryLanguageSelector** from `@asafarim/country-language-selector` β so consumers install only one package.
|
|
4
|
+
|
|
5
|
+
* Live demo: [https://alisafari-it.github.io/shared-i18n/](https://alisafari-it.github.io/shared-i18n/)
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- π Works in any React + TypeScript project (monorepo or standalone)
|
|
10
|
+
- ποΈ JSON-based translations per language and namespace
|
|
11
|
+
- π Cookie-based language persistence with automatic browser detection
|
|
12
|
+
- βοΈ Optional backend sync for user language preferences
|
|
13
|
+
- β‘ Lazy loading support for app-specific translations
|
|
14
|
+
- πͺ React hooks: `useLanguage`, `useTranslation`
|
|
15
|
+
- π¨ **LanguageSwitcher** β three variants: buttons, select, icon-dropdown
|
|
16
|
+
- πΊοΈ **CountryLanguageSelector** β re-exported, image or emoji flags, locale-aware URLs
|
|
17
|
+
- π Configurable API URL resolution for flexible backend integration
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @asafarim/shared-i18n
|
|
23
|
+
# or: npm i @asafarim/shared-i18n
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That is the only package you need. `@asafarim/country-language-selector` is a bundled dependency and re-exported for you.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Initialize i18n
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// main.tsx
|
|
34
|
+
import { initI18n } from '@asafarim/shared-i18n';
|
|
35
|
+
import '@asafarim/shared-i18n/country-language-selector.css'; // if using CountryLanguageSelector
|
|
36
|
+
|
|
37
|
+
import enApp from './locales/en/app.json';
|
|
38
|
+
import nlApp from './locales/nl/app.json';
|
|
39
|
+
|
|
40
|
+
initI18n({
|
|
41
|
+
defaultLanguage: 'en',
|
|
42
|
+
defaultNS: 'common',
|
|
43
|
+
ns: ['common', 'app'],
|
|
44
|
+
resources: {
|
|
45
|
+
en: { app: enApp },
|
|
46
|
+
nl: { app: nlApp }
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Translate in components
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useTranslation } from '@asafarim/shared-i18n';
|
|
55
|
+
|
|
56
|
+
function MyComponent() {
|
|
57
|
+
const { t } = useTranslation('app');
|
|
58
|
+
return <h1>{t('welcome')}</h1>;
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## LanguageSwitcher
|
|
65
|
+
|
|
66
|
+
Language-only switcher with cookie persistence.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { LanguageSwitcher } from '@asafarim/shared-i18n';
|
|
70
|
+
|
|
71
|
+
// Buttons
|
|
72
|
+
<LanguageSwitcher variant="buttons" />
|
|
73
|
+
|
|
74
|
+
// Select dropdown (no emoji)
|
|
75
|
+
<LanguageSwitcher variant="select" showEmoji={false} />
|
|
76
|
+
|
|
77
|
+
// Icon dropdown with flag emoji
|
|
78
|
+
<LanguageSwitcher variant="icon-dropdown" />
|
|
79
|
+
|
|
80
|
+
// Toggle between exactly 2 languages
|
|
81
|
+
<LanguageSwitcher variant="select" languages={['en', 'nl']} isToggler={true} />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Props
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
|------|------|---------|-------------|
|
|
88
|
+
| `variant` | `"buttons" \| "select" \| "icon-dropdown"` | `"buttons"` | UI variant |
|
|
89
|
+
| `languages` | `SupportedLanguage[]` | all supported | Subset of languages to show |
|
|
90
|
+
| `showEmoji` | `boolean` | `true` | Show flag emoji in select/icon variants |
|
|
91
|
+
| `showLabel` | `boolean` | `true` | Show language name label |
|
|
92
|
+
| `isToggler` | `boolean` | `true` | Renders as toggle when exactly 2 languages given |
|
|
93
|
+
| `onChanged` | `(lang: SupportedLanguage) => void` | β | Callback on language change |
|
|
94
|
+
| `unstyled` | `boolean` | `false` | Omit built-in styles |
|
|
95
|
+
|
|
96
|
+
### Limitation β country awareness
|
|
97
|
+
|
|
98
|
+
`LanguageSwitcher` emits only a language code (e.g. `"en"`). It cannot distinguish `be-en` from `gb-en`. For locale-aware URLs, use `resolveLocaleFromLanguage` as an adapter:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { LanguageSwitcher } from '@asafarim/shared-i18n';
|
|
102
|
+
import { resolveLocaleFromLanguage } from './i18n/localeAdapter';
|
|
103
|
+
|
|
104
|
+
<LanguageSwitcher
|
|
105
|
+
variant="buttons"
|
|
106
|
+
onChanged={(lang) => {
|
|
107
|
+
const { locale, reason, message } = resolveLocaleFromLanguage(currentLocale, lang, countries);
|
|
108
|
+
if (reason === 'fallback-country') showNotice(message);
|
|
109
|
+
if (reason !== 'unsupported') navigate(locale);
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## CountryLanguageSelector
|
|
117
|
+
|
|
118
|
+
Country + language selector. Re-exported from `@asafarim/country-language-selector`.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import {
|
|
122
|
+
CountryLanguageSelector,
|
|
123
|
+
type Country,
|
|
124
|
+
type Locale,
|
|
125
|
+
} from '@asafarim/shared-i18n';
|
|
126
|
+
|
|
127
|
+
const countries: Country[] = [
|
|
128
|
+
{
|
|
129
|
+
code: 'BE', name: 'Belgium', nativeName: 'BelgiΓ«', flag: 'π§πͺ',
|
|
130
|
+
languages: [
|
|
131
|
+
{ code: 'en', label: 'English', nativeLabel: 'English' },
|
|
132
|
+
{ code: 'nl', label: 'Dutch', nativeLabel: 'Nederlands' },
|
|
133
|
+
{ code: 'fr', label: 'French', nativeLabel: 'FranΓ§ais' },
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
code: 'NL', name: 'Netherlands', nativeName: 'Nederland', flag: 'π³π±',
|
|
138
|
+
languages: [
|
|
139
|
+
{ code: 'nl', label: 'Dutch', nativeLabel: 'Nederlands' },
|
|
140
|
+
{ code: 'en', label: 'English', nativeLabel: 'English' },
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
code: 'GB', name: 'United Kingdom', nativeName: 'United Kingdom', flag: 'π¬π§',
|
|
145
|
+
languages: [
|
|
146
|
+
{ code: 'en', label: 'English', nativeLabel: 'English' },
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
function LocaleBar({ locale, onLocaleChange }) {
|
|
152
|
+
const { i18n } = useTranslation();
|
|
153
|
+
return (
|
|
154
|
+
<CountryLanguageSelector
|
|
155
|
+
countries={countries}
|
|
156
|
+
value={locale}
|
|
157
|
+
onChange={(next) => {
|
|
158
|
+
i18n.changeLanguage(next.language);
|
|
159
|
+
onLocaleChange(next);
|
|
160
|
+
}}
|
|
161
|
+
triggerVariant="compact"
|
|
162
|
+
flagMode="image"
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Key Props
|
|
169
|
+
|
|
170
|
+
| Prop | Type | Default | Description |
|
|
171
|
+
|------|------|---------|-------------|
|
|
172
|
+
| `countries` | `Country[]` | built-in list | Countries to offer |
|
|
173
|
+
| `value` | `Locale` | β | Controlled value |
|
|
174
|
+
| `defaultValue` | `Locale` | β | Uncontrolled initial value |
|
|
175
|
+
| `onChange` | `(locale, meta) => void` | β | Fired on every change |
|
|
176
|
+
| `triggerVariant` | `"compact" \| "full" \| "flag"` | `"compact"` | Trigger display |
|
|
177
|
+
| `flagMode` | `"emoji" \| "image"` | `"emoji"` | Flag rendering strategy |
|
|
178
|
+
| `align` | `"start" \| "end"` | `"end"` | Popover alignment |
|
|
179
|
+
| `renderTrigger` | `(ctx) => ReactNode` | β | Custom trigger render prop |
|
|
180
|
+
| `persistKey` | `string` | β | localStorage key (uncontrolled only) |
|
|
181
|
+
|
|
182
|
+
### Comparison: LanguageSwitcher vs CountryLanguageSelector
|
|
183
|
+
|
|
184
|
+
| Capability | LanguageSwitcher | CountryLanguageSelector |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| Changes i18n language | β
| β
via `locale.language` |
|
|
187
|
+
| Knows country | β | β
|
|
|
188
|
+
| Represents `be-en` | β | β
|
|
|
189
|
+
| Distinguishes `be-en` from `gb-en` | β | β
|
|
|
190
|
+
| Best for localized URLs | β needs adapter | β
|
|
|
191
|
+
| Best for translation-only apps | β
| optional |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## CSS Import
|
|
196
|
+
|
|
197
|
+
When using `CountryLanguageSelector`, import its stylesheet once in your entry point:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import '@asafarim/shared-i18n/country-language-selector.css';
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## More Languages
|
|
206
|
+
|
|
207
|
+
Add any language by including its JSON files:
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
initI18n({
|
|
211
|
+
ns: ['common', 'app'],
|
|
212
|
+
resources: {
|
|
213
|
+
en: { app: enApp },
|
|
214
|
+
nl: { app: nlApp },
|
|
215
|
+
fr: { app: frApp }
|
|
216
|
+
},
|
|
217
|
+
supportedLngs: ['en', 'nl', 'fr'],
|
|
218
|
+
defaultLanguage: 'en'
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## API Reference
|
|
225
|
+
|
|
226
|
+
### `initI18n(config?)`
|
|
227
|
+
|
|
228
|
+
| Param | Type | Description |
|
|
229
|
+
|-------|------|-------------|
|
|
230
|
+
| `defaultNS` | `string` | Default namespace (default: `'common'`) |
|
|
231
|
+
| `ns` | `string[]` | Namespaces to load |
|
|
232
|
+
| `resources` | object | App-specific translation resources |
|
|
233
|
+
| `supportedLngs` | `string[]` | Override supported languages |
|
|
234
|
+
| `defaultLanguage` | `string` | Fallback language code |
|
|
235
|
+
|
|
236
|
+
### `useLanguage()`
|
|
237
|
+
|
|
238
|
+
Returns `{ language, changeLanguage, isChanging }`.
|
|
239
|
+
|
|
240
|
+
### `useTranslation(ns?)`
|
|
241
|
+
|
|
242
|
+
Re-exported from react-i18next.
|
|
243
|
+
|
|
244
|
+
### `getApiUrl(envVarName?, defaultUrl?)`
|
|
245
|
+
|
|
246
|
+
Configurable API URL resolver.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Cookie & Backend Integration
|
|
251
|
+
|
|
252
|
+
User language preference is persisted in a `preferredLanguage` cookie. To sync with a backend, set:
|
|
253
|
+
|
|
254
|
+
```env
|
|
255
|
+
VITE_IDENTITY_API_URL=https://your-identity.example.com
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Built-in Translations
|
|
261
|
+
|
|
262
|
+
Ships with English and Dutch for the `common` namespace. Supply your own resources via `initI18n` to override or extend.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT Β© ASafariM
|