@objectstack/service-i18n 4.0.2 → 4.0.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/service-i18n@4.0.2 build /home/runner/work/framework/framework/packages/services/service-i18n
2
+ > @objectstack/service-i18n@4.0.4 build /home/runner/work/framework/framework/packages/services/service-i18n
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -12,11 +12,11 @@
12
12
  CJS Build start
13
13
  CJS dist/index.cjs 8.50 KB
14
14
  CJS dist/index.cjs.map 18.42 KB
15
- CJS ⚡️ Build success in 81ms
15
+ CJS ⚡️ Build success in 123ms
16
16
  ESM dist/index.js 6.84 KB
17
17
  ESM dist/index.js.map 18.02 KB
18
- ESM ⚡️ Build success in 86ms
18
+ ESM ⚡️ Build success in 130ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 15228ms
20
+ DTS ⚡️ Build success in 16111ms
21
21
  DTS dist/index.d.ts 4.41 KB
22
22
  DTS dist/index.d.cts 4.41 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @objectstack/service-i18n
2
2
 
3
+ ## 4.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [326b66b]
8
+ - @objectstack/spec@4.0.4
9
+ - @objectstack/core@4.0.4
10
+
11
+ ## 4.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - @objectstack/spec@4.0.3
16
+ - @objectstack/core@4.0.3
17
+
3
18
  ## 4.0.2
4
19
 
5
20
  ### Patch Changes
package/README.md ADDED
@@ -0,0 +1,377 @@
1
+ # @objectstack/service-i18n
2
+
3
+ I18n Service for ObjectStack — implements `II18nService` with file-based locale loading and translation management.
4
+
5
+ ## Features
6
+
7
+ - **Multi-Language Support**: Manage translations for unlimited languages
8
+ - **File-Based Locales**: Load translations from JSON/YAML files
9
+ - **Namespace Support**: Organize translations by domain (e.g., `common`, `errors`, `ui`)
10
+ - **Interpolation**: Dynamic variable replacement in translations
11
+ - **Pluralization**: Language-specific plural rules
12
+ - **Fallback Chain**: Graceful fallback from dialect → base language → default
13
+ - **Type-Safe**: TypeScript support with type-safe translation keys
14
+ - **Hot Reload**: Reload translations without restarting (development)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pnpm add @objectstack/service-i18n
20
+ ```
21
+
22
+ ## Basic Usage
23
+
24
+ ```typescript
25
+ import { defineStack } from '@objectstack/spec';
26
+ import { ServiceI18n } from '@objectstack/service-i18n';
27
+
28
+ const stack = defineStack({
29
+ services: [
30
+ ServiceI18n.configure({
31
+ defaultLocale: 'en-US',
32
+ supportedLocales: ['en-US', 'es-ES', 'fr-FR', 'de-DE'],
33
+ loadPath: './locales/{{lng}}/{{ns}}.json',
34
+ }),
35
+ ],
36
+ });
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ ```typescript
42
+ interface I18nServiceConfig {
43
+ /** Default locale (e.g., 'en-US') */
44
+ defaultLocale: string;
45
+
46
+ /** List of supported locales */
47
+ supportedLocales: string[];
48
+
49
+ /** Path template for locale files */
50
+ loadPath: string;
51
+
52
+ /** Fallback locale when translation is missing */
53
+ fallbackLocale?: string;
54
+
55
+ /** Enable hot reload in development */
56
+ hotReload?: boolean;
57
+ }
58
+ ```
59
+
60
+ ## Directory Structure
61
+
62
+ ```
63
+ locales/
64
+ ├── en-US/
65
+ │ ├── common.json
66
+ │ ├── errors.json
67
+ │ └── ui.json
68
+ ├── es-ES/
69
+ │ ├── common.json
70
+ │ ├── errors.json
71
+ │ └── ui.json
72
+ └── fr-FR/
73
+ ├── common.json
74
+ ├── errors.json
75
+ └── ui.json
76
+ ```
77
+
78
+ Example `locales/en-US/common.json`:
79
+
80
+ ```json
81
+ {
82
+ "welcome": "Welcome to ObjectStack",
83
+ "greeting": "Hello, {{name}}!",
84
+ "item_count": "You have {{count}} item",
85
+ "item_count_plural": "You have {{count}} items",
86
+ "save_button": "Save",
87
+ "cancel_button": "Cancel"
88
+ }
89
+ ```
90
+
91
+ ## Service API
92
+
93
+ ```typescript
94
+ // Get i18n service
95
+ const i18n = kernel.getService<II18nService>('i18n');
96
+ ```
97
+
98
+ ### Basic Translation
99
+
100
+ ```typescript
101
+ // Simple translation
102
+ const text = await i18n.t('common:welcome');
103
+ // "Welcome to ObjectStack"
104
+
105
+ // With interpolation
106
+ const greeting = await i18n.t('common:greeting', { name: 'Alice' });
107
+ // "Hello, Alice!"
108
+
109
+ // With pluralization
110
+ const count1 = await i18n.t('common:item_count', { count: 1 });
111
+ // "You have 1 item"
112
+
113
+ const count5 = await i18n.t('common:item_count', { count: 5 });
114
+ // "You have 5 items"
115
+ ```
116
+
117
+ ### Change Locale
118
+
119
+ ```typescript
120
+ // Set locale for current context
121
+ await i18n.setLocale('es-ES');
122
+
123
+ // Get current locale
124
+ const locale = i18n.getLocale();
125
+ // "es-ES"
126
+
127
+ // Translate in specific locale (without changing context)
128
+ const text = await i18n.t('common:welcome', { locale: 'fr-FR' });
129
+ ```
130
+
131
+ ### Namespaces
132
+
133
+ ```typescript
134
+ // Load translation from 'errors' namespace
135
+ const errorMsg = await i18n.t('errors:not_found');
136
+
137
+ // Load multiple namespaces
138
+ await i18n.loadNamespaces(['common', 'ui', 'errors']);
139
+
140
+ // Check if namespace is loaded
141
+ const isLoaded = i18n.isNamespaceLoaded('common');
142
+ ```
143
+
144
+ ### Locale Management
145
+
146
+ ```typescript
147
+ // Get all supported locales
148
+ const locales = i18n.getSupportedLocales();
149
+ // ['en-US', 'es-ES', 'fr-FR', 'de-DE']
150
+
151
+ // Check if locale is supported
152
+ const isSupported = i18n.isLocaleSupported('ja-JP');
153
+ // false
154
+
155
+ // Get locale metadata
156
+ const metadata = i18n.getLocaleMetadata('en-US');
157
+ // {
158
+ // name: 'English (United States)',
159
+ // nativeName: 'English (United States)',
160
+ // direction: 'ltr',
161
+ // pluralRules: 'en'
162
+ // }
163
+ ```
164
+
165
+ ## Advanced Features
166
+
167
+ ### Nested Keys
168
+
169
+ ```json
170
+ {
171
+ "user": {
172
+ "profile": {
173
+ "title": "User Profile",
174
+ "edit": "Edit Profile"
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ```typescript
181
+ await i18n.t('common:user.profile.title');
182
+ // "User Profile"
183
+ ```
184
+
185
+ ### Arrays
186
+
187
+ ```json
188
+ {
189
+ "days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
190
+ }
191
+ ```
192
+
193
+ ```typescript
194
+ const days = await i18n.t('common:days', { returnObjects: true });
195
+ // ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
196
+ ```
197
+
198
+ ### Context-Based Translations
199
+
200
+ ```json
201
+ {
202
+ "friend": "A friend",
203
+ "friend_male": "A boyfriend",
204
+ "friend_female": "A girlfriend"
205
+ }
206
+ ```
207
+
208
+ ```typescript
209
+ await i18n.t('common:friend', { context: 'male' });
210
+ // "A boyfriend"
211
+
212
+ await i18n.t('common:friend', { context: 'female' });
213
+ // "A girlfriend"
214
+ ```
215
+
216
+ ### Formatting
217
+
218
+ ```typescript
219
+ // Date formatting
220
+ const formatted = await i18n.formatDate(new Date(), {
221
+ locale: 'es-ES',
222
+ format: 'long',
223
+ });
224
+ // "15 de enero de 2024"
225
+
226
+ // Number formatting
227
+ const price = await i18n.formatNumber(1234.56, {
228
+ style: 'currency',
229
+ currency: 'EUR',
230
+ locale: 'fr-FR',
231
+ });
232
+ // "1 234,56 €"
233
+
234
+ // Relative time
235
+ const relative = await i18n.formatRelative(new Date('2024-01-01'), {
236
+ locale: 'en-US',
237
+ });
238
+ // "3 months ago"
239
+ ```
240
+
241
+ ### Dynamic Loading
242
+
243
+ ```typescript
244
+ // Add a new locale dynamically
245
+ await i18n.addLocale('ja-JP', {
246
+ loadPath: './locales/ja-JP/{{ns}}.json',
247
+ });
248
+
249
+ // Remove a locale
250
+ await i18n.removeLocale('ja-JP');
251
+
252
+ // Reload translations (useful in development)
253
+ await i18n.reload();
254
+ ```
255
+
256
+ ## Integration with Metadata
257
+
258
+ Translate metadata labels automatically:
259
+
260
+ ```typescript
261
+ import { defineObject } from '@objectstack/spec';
262
+
263
+ const contact = defineObject({
264
+ name: 'contact',
265
+ label: 'i18n:objects.contact.label', // References translation key
266
+ fields: [
267
+ {
268
+ name: 'name',
269
+ label: 'i18n:fields.contact.name',
270
+ type: 'text',
271
+ },
272
+ ],
273
+ });
274
+
275
+ // Translation file: locales/en-US/metadata.json
276
+ {
277
+ "objects": {
278
+ "contact": {
279
+ "label": "Contact",
280
+ "label_plural": "Contacts"
281
+ }
282
+ },
283
+ "fields": {
284
+ "contact": {
285
+ "name": "Full Name"
286
+ }
287
+ }
288
+ }
289
+ ```
290
+
291
+ ## REST API Endpoints
292
+
293
+ ```
294
+ GET /api/v1/i18n/locales # Get supported locales
295
+ GET /api/v1/i18n/translations/:locale # Get all translations for locale
296
+ POST /api/v1/i18n/translate # Translate keys (batch)
297
+ ```
298
+
299
+ ## Client Integration
300
+
301
+ ### React Hook Example
302
+
303
+ ```typescript
304
+ import { useTranslation } from '@objectstack/client-react';
305
+
306
+ function MyComponent() {
307
+ const { t, locale, setLocale } = useTranslation();
308
+
309
+ return (
310
+ <div>
311
+ <h1>{t('common:welcome')}</h1>
312
+ <button onClick={() => setLocale('es-ES')}>
313
+ Español
314
+ </button>
315
+ </div>
316
+ );
317
+ }
318
+ ```
319
+
320
+ ## Best Practices
321
+
322
+ 1. **Use Namespaces**: Organize translations by domain (common, ui, errors, metadata)
323
+ 2. **Consistent Keys**: Use dot notation for nested keys (e.g., `user.profile.title`)
324
+ 3. **Provide Context**: Use context for gender, formality, or pluralization variants
325
+ 4. **Fallback Values**: Always provide fallback translations in default locale
326
+ 5. **Avoid Hardcoding**: Never hardcode user-facing text; use translation keys
327
+ 6. **Professional Translation**: Use professional translators for production
328
+ 7. **Version Control**: Store translation files in version control
329
+
330
+ ## Locale Coverage Detection
331
+
332
+ ```typescript
333
+ // Get coverage statistics
334
+ const coverage = await i18n.getCoverage();
335
+ // {
336
+ // 'en-US': { total: 245, missing: 0, percentage: 100 },
337
+ // 'es-ES': { total: 245, missing: 12, percentage: 95.1 },
338
+ // 'fr-FR': { total: 245, missing: 45, percentage: 81.6 }
339
+ // }
340
+
341
+ // Get missing keys for a locale
342
+ const missing = await i18n.getMissingKeys('es-ES');
343
+ // ['errors.validation.email', 'ui.dashboard.title', ...]
344
+ ```
345
+
346
+ ## Performance Considerations
347
+
348
+ - **Lazy Loading**: Namespaces are loaded on demand
349
+ - **Caching**: Translations are cached in memory
350
+ - **Hot Reload**: Only enable in development
351
+ - **Bundle Size**: Load only required locales on client
352
+
353
+ ## Contract Implementation
354
+
355
+ Implements `II18nService` from `@objectstack/spec/contracts`:
356
+
357
+ ```typescript
358
+ interface II18nService {
359
+ t(key: string, options?: TranslationOptions): Promise<string>;
360
+ setLocale(locale: string): Promise<void>;
361
+ getLocale(): string;
362
+ getSupportedLocales(): string[];
363
+ loadNamespaces(namespaces: string[]): Promise<void>;
364
+ formatDate(date: Date, options?: FormatOptions): Promise<string>;
365
+ formatNumber(value: number, options?: FormatOptions): Promise<string>;
366
+ }
367
+ ```
368
+
369
+ ## License
370
+
371
+ Apache-2.0
372
+
373
+ ## See Also
374
+
375
+ - [i18next Documentation](https://www.i18next.com/)
376
+ - [@objectstack/spec/system (Translation schema)](../../spec/src/system/)
377
+ - [I18n Best Practices Guide](/content/docs/guides/i18n/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/service-i18n",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "license": "Apache-2.0",
5
5
  "description": "I18n Service for ObjectStack — implements II18nService with file-based locale loading",
6
6
  "type": "module",
@@ -14,13 +14,13 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@objectstack/core": "4.0.2",
18
- "@objectstack/spec": "4.0.2"
17
+ "@objectstack/core": "4.0.4",
18
+ "@objectstack/spec": "4.0.4"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/node": "^25.5.2",
21
+ "@types/node": "^25.6.0",
22
22
  "typescript": "^6.0.2",
23
- "vitest": "^4.1.2"
23
+ "vitest": "^4.1.4"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsup --config ../../../tsup.config.ts",