@owlmeans/client-i18n 0.1.2 → 0.1.4

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 (3) hide show
  1. package/README.md +26 -453
  2. package/package.json +6 -5
  3. package/tsconfig.json +6 -11
package/README.md CHANGED
@@ -1,488 +1,61 @@
1
1
  # @owlmeans/client-i18n
2
2
 
3
- The **@owlmeans/client-i18n** package provides React-based internationalization functionality for OwlMeans Common Libraries, designed for fullstack microservices and microclients development with focus on security and proper authentication and authorization.
3
+ React i18next adapter that loads translations registered via `@owlmeans/i18n` into react-i18next.
4
4
 
5
- ## Purpose
5
+ ## Overview
6
6
 
7
- This package serves as the client-side React integration layer for the OwlMeans i18n system that:
7
+ - `I18nContext` — React provider component that initializes i18next with resources from `i18nStorage`
8
+ - `useI18nApp(ns?)` — hook that returns a translation function scoped to the app namespace
9
+ - `useCommonI18n(ns?)` — hook for shared/common i18n strings
10
+ - Bridges `@owlmeans/i18n`'s storage format to react-i18next's resource bundles
8
11
 
9
- - **Integrates with React applications** using React Context and hooks
10
- - **Provides React hooks for translations** with automatic resource loading
11
- - **Supports dynamic resource loading** with automatic caching
12
- - **Integrates with client context** for configuration-driven internationalization
13
- - **Offers multi-level translation support** (library, app, service levels)
14
- - **Enables namespace-based organization** for different translation contexts
12
+ ## Installation
15
13
 
16
- ## Core Concepts
17
-
18
- ### I18n Context Provider
19
- A React Context Provider that initializes and manages the i18next instance for the entire React application, integrated with OwlMeans client configuration.
20
-
21
- ### Automatic Resource Loading
22
- Translation resources are automatically loaded and registered with i18next when requested through hooks, with intelligent caching to prevent duplicate loading.
23
-
24
- ### Multi-Level Translation Hooks
25
- Different hooks for accessing translations at different levels:
26
- - Library-level translations (`useI18nLib`)
27
- - Application-level translations (`useI18nApp`)
28
- - Service-level translations (`useCommonI18n`)
29
-
30
- ### Client Context Integration
31
- Seamless integration with OwlMeans client context system for configuration-driven internationalization setup.
32
-
33
- ## API Reference
34
-
35
- ### Components
14
+ ```bash
15
+ bun add @owlmeans/client-i18n
16
+ ```
36
17
 
37
- #### `I18nContext`
18
+ ## Usage
38
19
 
39
- React Context Provider component that provides i18n functionality to the entire React application.
20
+ Wrap the app with the i18n provider:
40
21
 
41
22
  ```typescript
42
23
  import { I18nContext } from '@owlmeans/client-i18n'
43
- import { ClientConfig } from '@owlmeans/client-context'
44
24
 
45
- const App: React.FC = () => {
46
- const config = useClientConfig() // Your client configuration
47
-
25
+ function App() {
48
26
  return (
49
- <I18nContext config={config}>
27
+ <I18nContext lng="en">
50
28
  <AppContent />
51
29
  </I18nContext>
52
30
  )
53
31
  }
54
32
  ```
55
33
 
56
- **Props:**
57
- - `config`: ClientConfig - The client configuration object
58
- - `children`: ReactNode - Child components
59
-
60
- ### Hooks
61
-
62
- #### `useCommonI18n(resourceName, ns?, prefix?): TFunction`
63
-
64
- Main hook for accessing translations with automatic resource loading.
65
-
66
- ```typescript
67
- import { useCommonI18n } from '@owlmeans/client-i18n'
68
-
69
- const MyComponent: React.FC = () => {
70
- const t = useCommonI18n('user-forms', 'ui', 'profile')
71
-
72
- return (
73
- <div>
74
- <h1>{t('title')}</h1>
75
- <p>{t('description')}</p>
76
- </div>
77
- )
78
- }
79
- ```
80
-
81
- **Parameters:**
82
- - `resourceName`: string - Name of the translation resource to load
83
- - `ns`: string (optional) - Namespace for the translations (defaults to default namespace)
84
- - `prefix`: string (optional) - Key prefix for translation lookups
85
-
86
- **Returns:** `TFunction` - i18next translation function
87
-
88
- **Behavior:**
89
- - Automatically loads translation resources for the current language
90
- - Caches loaded resources to prevent duplicate loading
91
- - Falls back to default language if current language resources are not available
92
- - Supports multi-level translation priority (service > app > library)
93
-
94
- #### `useI18nLib(libName, prefix?): TFunction`
95
-
96
- Hook for accessing library-level translations in the 'lib' namespace.
97
-
98
- ```typescript
99
- import { useI18nLib } from '@owlmeans/client-i18n'
100
-
101
- const LibraryComponent: React.FC = () => {
102
- const t = useI18nLib('common-ui', 'buttons')
103
-
104
- return (
105
- <button>{t('save')}</button>
106
- )
107
- }
108
- ```
109
-
110
- **Parameters:**
111
- - `libName`: string - Name of the library translation resource
112
- - `prefix`: string (optional) - Key prefix for translation lookups
113
-
114
- **Returns:** `TFunction` - i18next translation function configured for library namespace
115
-
116
- #### `useI18nApp(appName?, prefix?): TFunction`
117
-
118
- Hook for accessing application-level translations using the service name from context.
34
+ Translate strings in a component:
119
35
 
120
36
  ```typescript
121
37
  import { useI18nApp } from '@owlmeans/client-i18n'
122
38
 
123
- const AppComponent: React.FC = () => {
124
- const t = useI18nApp(undefined, 'navigation')
125
-
126
- return (
127
- <nav>
128
- <a href="/">{t('home')}</a>
129
- <a href="/profile">{t('profile')}</a>
130
- </nav>
131
- )
132
- }
133
- ```
134
-
135
- **Parameters:**
136
- - `appName`: string (optional) - Name of the app (defaults to service name from context)
137
- - `prefix`: string (optional) - Key prefix for translation lookups
138
-
139
- **Returns:** `TFunction` - i18next translation function configured for app namespace
140
-
141
- ### Types
142
-
143
- #### `I18nContextProps`
144
-
145
- Props interface for the I18nContext component.
146
-
147
- ```typescript
148
- interface I18nContextProps extends PropsWithChildren {
149
- config: ClientConfig
150
- }
151
- ```
152
-
153
- #### `I18nProps`
154
-
155
- Generic interface for components that accept i18n properties.
156
-
157
- ```typescript
158
- interface I18nProps {
159
- i18n?: I18nBaseProps
160
- }
161
- ```
162
-
163
- #### `I18nBaseProps`
164
-
165
- Base properties for i18n configuration.
166
-
167
- ```typescript
168
- interface I18nBaseProps {
169
- resource?: string // Resource name override
170
- ns?: string // Namespace override
171
- prefix?: string // Key prefix override
172
- suppress?: boolean // Suppress translation loading
173
- }
174
- ```
175
-
176
- ### Utilities
177
-
178
- The package also exports utilities under the `/utils` subpackage:
179
-
180
- ```typescript
181
- import { useI18nInstance } from '@owlmeans/client-i18n/utils'
182
- ```
183
-
184
- #### `useI18nInstance(config): i18n`
185
-
186
- Hook that creates and configures an i18next instance based on client configuration.
187
-
188
- ```typescript
189
- import { useI18nInstance } from '@owlmeans/client-i18n/utils'
190
-
191
- const config = useClientConfig()
192
- const i18n = useI18nInstance(config)
193
- ```
194
-
195
- ## Usage Examples
196
-
197
- ### Basic Setup
198
-
199
- ```typescript
200
- import React from 'react'
201
- import { render } from 'react-dom'
202
- import { I18nContext } from '@owlmeans/client-i18n'
203
- import { makeClientConfig, ClientContext } from '@owlmeans/client-context'
204
- import { AppType } from '@owlmeans/context'
205
-
206
- const config = makeClientConfig(AppType.Frontend, 'my-app', {
207
- // Client configuration
208
- defaultLng: 'en',
209
- debug: { i18n: true }
210
- })
211
-
212
- const App: React.FC = () => (
213
- <ClientContext.Provider value={context}>
214
- <I18nContext config={config}>
215
- <MainApp />
216
- </I18nContext>
217
- </ClientContext.Provider>
218
- )
219
-
220
- render(<App />, document.getElementById('root'))
221
- ```
222
-
223
- ### Component with Translations
224
-
225
- ```typescript
226
- import React from 'react'
227
- import { useCommonI18n, useI18nLib, useI18nApp } from '@owlmeans/client-i18n'
228
-
229
- const UserProfileForm: React.FC = () => {
230
- // Different translation levels
231
- const tLib = useI18nLib('forms') // Library translations
232
- const tApp = useI18nApp() // App-specific translations
233
- const tUser = useCommonI18n('user-profile', 'ui') // Service translations
234
-
235
- return (
236
- <form>
237
- <h1>{tApp('profile.title')}</h1>
238
-
239
- <label>
240
- {tUser('name.label')}
241
- <input placeholder={tUser('name.placeholder')} />
242
- </label>
243
-
244
- <label>
245
- {tUser('email.label')}
246
- <input type="email" placeholder={tUser('email.placeholder')} />
247
- </label>
248
-
249
- <div>
250
- <button type="submit">{tLib('save')}</button>
251
- <button type="button">{tLib('cancel')}</button>
252
- </div>
253
- </form>
254
- )
39
+ function ProjectTitle({ titleKey }: { titleKey: string }) {
40
+ const t = useI18nApp('manager-web')
41
+ return <h1>{t(titleKey)}</h1>
255
42
  }
256
43
  ```
257
44
 
258
- ### Custom Hook for Component Translations
259
-
260
- ```typescript
261
- import { useCommonI18n } from '@owlmeans/client-i18n'
262
-
263
- const useProductTranslations = (prefix?: string) => {
264
- return useCommonI18n('products', 'ecommerce', prefix)
265
- }
266
-
267
- const ProductCard: React.FC<{ product: Product }> = ({ product }) => {
268
- const t = useProductTranslations('card')
269
-
270
- return (
271
- <div className="product-card">
272
- <h3>{product.name}</h3>
273
- <p>{t('price')}: ${product.price}</p>
274
- <button>{t('addToCart')}</button>
275
- </div>
276
- )
277
- }
278
- ```
279
-
280
- ### Dynamic Resource Loading
281
-
282
- ```typescript
283
- import React, { useState } from 'react'
284
- import { useCommonI18n } from '@owlmeans/client-i18n'
285
-
286
- const DynamicContent: React.FC = () => {
287
- const [selectedModule, setSelectedModule] = useState('dashboard')
288
-
289
- // Translations are loaded dynamically based on selected module
290
- const t = useCommonI18n(selectedModule, 'modules')
291
-
292
- return (
293
- <div>
294
- <select
295
- value={selectedModule}
296
- onChange={(e) => setSelectedModule(e.target.value)}
297
- >
298
- <option value="dashboard">Dashboard</option>
299
- <option value="analytics">Analytics</option>
300
- <option value="settings">Settings</option>
301
- </select>
302
-
303
- <div>
304
- <h2>{t('title')}</h2>
305
- <p>{t('description')}</p>
306
- </div>
307
- </div>
308
- )
309
- }
310
- ```
311
-
312
- ### Error Handling and Fallbacks
313
-
314
- ```typescript
315
- import React from 'react'
316
- import { useCommonI18n } from '@owlmeans/client-i18n'
317
-
318
- const SafeTranslatedComponent: React.FC = () => {
319
- const t = useCommonI18n('user-interface', 'ui')
320
-
321
- // Translation function handles missing keys gracefully
322
- return (
323
- <div>
324
- {/* Will show key if translation missing */}
325
- <h1>{t('welcome', 'Welcome')}</h1>
326
-
327
- {/* Will use fallback value */}
328
- <p>{t('description', { defaultValue: 'Default description' })}</p>
329
-
330
- {/* With interpolation */}
331
- <p>{t('greeting', { name: 'User', defaultValue: 'Hello, {{name}}!' })}</p>
332
- </div>
333
- )
334
- }
335
- ```
336
-
337
- ### Multi-Language Support
338
-
339
- ```typescript
340
- import React from 'react'
341
- import { useTranslation } from 'react-i18next'
342
- import { useCommonI18n } from '@owlmeans/client-i18n'
343
-
344
- const LanguageSwitcher: React.FC = () => {
345
- const { i18n } = useTranslation()
346
- const t = useCommonI18n('language-switcher', 'ui')
347
-
348
- const changeLanguage = (lng: string) => {
349
- i18n.changeLanguage(lng)
350
- }
351
-
352
- return (
353
- <div>
354
- <h3>{t('selectLanguage')}</h3>
355
- <button onClick={() => changeLanguage('en')}>
356
- {t('languages.english')}
357
- </button>
358
- <button onClick={() => changeLanguage('es')}>
359
- {t('languages.spanish')}
360
- </button>
361
- <button onClick={() => changeLanguage('fr')}>
362
- {t('languages.french')}
363
- </button>
364
- </div>
365
- )
366
- }
367
- ```
368
-
369
- ## Integration Patterns
370
-
371
- ### With Component Libraries
372
-
373
- ```typescript
374
- // Create a wrapper component for your design system
375
- import React from 'react'
376
- import { useI18nLib } from '@owlmeans/client-i18n'
377
-
378
- interface ButtonProps {
379
- variant: 'primary' | 'secondary'
380
- i18nKey: string
381
- children?: React.ReactNode
382
- }
383
-
384
- const TranslatedButton: React.FC<ButtonProps> = ({
385
- variant,
386
- i18nKey,
387
- children
388
- }) => {
389
- const t = useI18nLib('ui-components', 'buttons')
390
-
391
- return (
392
- <button className={`btn btn-${variant}`}>
393
- {children || t(i18nKey)}
394
- </button>
395
- )
396
- }
397
-
398
- // Usage
399
- <TranslatedButton variant="primary" i18nKey="save" />
400
- ```
401
-
402
- ### With Forms
403
-
404
- ```typescript
405
- import React from 'react'
406
- import { useForm } from 'react-hook-form'
407
- import { useCommonI18n } from '@owlmeans/client-i18n'
408
-
409
- const LoginForm: React.FC = () => {
410
- const t = useCommonI18n('auth-forms', 'auth')
411
- const { register, handleSubmit, formState: { errors } } = useForm()
412
-
413
- return (
414
- <form onSubmit={handleSubmit(onSubmit)}>
415
- <div>
416
- <label>{t('email.label')}</label>
417
- <input
418
- {...register('email', { required: t('email.required') })}
419
- placeholder={t('email.placeholder')}
420
- />
421
- {errors.email && <span>{errors.email.message}</span>}
422
- </div>
423
-
424
- <div>
425
- <label>{t('password.label')}</label>
426
- <input
427
- type="password"
428
- {...register('password', { required: t('password.required') })}
429
- placeholder={t('password.placeholder')}
430
- />
431
- {errors.password && <span>{errors.password.message}</span>}
432
- </div>
433
-
434
- <button type="submit">{t('submit')}</button>
435
- </form>
436
- )
437
- }
438
- ```
439
-
440
- ## Performance Considerations
441
-
442
- ### Resource Loading Caching
443
- The package implements intelligent caching:
444
-
445
- ```typescript
446
- // Resources are cached per language and resource name
447
- const i18nLoadingCache = new Set<string>()
448
-
449
- // Cache key format: "{language}:{resourceName}:{namespace}"
450
- const key = `${i18n.language}:${resourceName}:${ns}`
451
- ```
452
-
453
- ### Automatic Resource Management
454
- - **Lazy Loading** - Resources are only loaded when first requested
455
- - **Duplicate Prevention** - Multiple components using the same resource won't trigger multiple loads
456
- - **Fallback Loading** - Default language resources are loaded as fallbacks
457
-
458
- ### Best Practices
45
+ ## API
459
46
 
460
- 1. **Use specific resource names** - Avoid loading large translation bundles
461
- 2. **Leverage namespaces** - Organize translations by functional area
462
- 3. **Implement prefixes** - Use prefixes to scope translations to specific components
463
- 4. **Cache translation functions** - Don't recreate translation functions unnecessarily
47
+ ### `I18nContext`
464
48
 
465
- ## Error Handling
49
+ React provider component. Props: `lng: string`, `children: ReactNode`.
466
50
 
467
- The package handles various error scenarios gracefully:
51
+ ### `useI18nApp(ns?): TFunction`
468
52
 
469
- - **Missing resources** - Falls back to translation keys or default values
470
- - **Network failures** - Uses cached resources or fallback languages
471
- - **Invalid configurations** - Provides sensible defaults
472
- - **Component unmounting** - Prevents memory leaks from resource loading
53
+ Returns a react-i18next `t()` function scoped to the app-level namespace.
473
54
 
474
- ## Dependencies
55
+ ### `useCommonI18n(ns?): TFunction`
475
56
 
476
- This package depends on:
477
- - `@owlmeans/client` - Client-side context hooks
478
- - `@owlmeans/client-context` - Client context management
479
- - `@owlmeans/i18n` - Core i18n functionality and resource management
480
- - `react` - React framework (peer dependency)
481
- - `react-i18next` - React integration for i18next
482
- - `i18next` - Core i18n library
57
+ Returns a `t()` function for shared/common translations.
483
58
 
484
59
  ## Related Packages
485
60
 
486
- - [`@owlmeans/i18n`](../i18n) - Core i18n functionality and resource management
487
- - [`@owlmeans/client-context`](../client-context) - Client-side context management
488
- - [`@owlmeans/client`](../client) - Client-side utilities and hooks
61
+ - [`@owlmeans/i18n`](../i18n) — `addI18nApp`, `addI18n` to register translations before render
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/client-i18n",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -31,14 +31,15 @@
31
31
  "react": "*"
32
32
  },
33
33
  "devDependencies": {
34
+ "@owlmeans/dep-config": "workspace:*",
34
35
  "@types/react": "^19.2.7",
35
36
  "nodemon": "^3.1.11",
36
- "typescript": "^5.8.3"
37
+ "typescript": "^6.0.2"
37
38
  },
38
39
  "dependencies": {
39
- "@owlmeans/client": "^0.1.2",
40
- "@owlmeans/client-context": "^0.1.2",
41
- "@owlmeans/i18n": "^0.1.2",
40
+ "@owlmeans/client": "^0.1.4",
41
+ "@owlmeans/client-context": "^0.1.4",
42
+ "@owlmeans/i18n": "^0.1.4",
42
43
  "i18next": "^23.15.1",
43
44
  "react-i18next": "^15.0.2"
44
45
  },
package/tsconfig.json CHANGED
@@ -1,16 +1,11 @@
1
1
  {
2
2
  "extends": [
3
- "../tsconfig.default.json",
4
- "../tsconfig.react.json",
3
+ "@owlmeans/dep-config/tsconfig.base.json",
4
+ "@owlmeans/dep-config/tsconfig.react.json"
5
5
  ],
6
6
  "compilerOptions": {
7
- "rootDir": "./src/", /* Specify the root folder within your source files. */
8
- "outDir": "./build/", /* Specify an output folder for all emitted files. */
9
- "moduleResolution": "Bundler"
7
+ "rootDir": "./src/",
8
+ "outDir": "./build/"
10
9
  },
11
- "exclude": [
12
- "./dist/**/*",
13
- "./build/**/*",
14
- "./*.ts"
15
- ]
16
- }
10
+ "exclude": ["./dist/**/*", "./build/**/*", "./*.ts"]
11
+ }