@v-miniapp/locale 1.0.2

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 ADDED
@@ -0,0 +1,497 @@
1
+ # @v-miniapp/locale
2
+
3
+ **@v-miniapp/locale** là bộ thư viện component React mạnh mẽ được thiết kế đặc biệt cho việc phát triển Router V-MiniApp.
4
+
5
+ ## Cài đặt
6
+
7
+ ```bash
8
+ npm install @v-miniapp/locale
9
+ # hoặc
10
+ pnpm add @v-miniapp/locale
11
+ # hoặc
12
+ yarn add @v-miniapp/locale
13
+ ```
14
+
15
+ ## Ví dụ bắt đầu sử dụng
16
+
17
+ ```tsx
18
+ import { LocalesProvider, type ILocalesConfig } from '@v-miniapp/ui-react'
19
+
20
+ const localesConfig: ILocalesConfig = {
21
+ defaultLanguage: 'vi',
22
+ resources: {
23
+ vi: {
24
+ 'app.title': 'Ứng dụng của tôi',
25
+ },
26
+ en: {
27
+ 'app.title': 'My Application',
28
+ },
29
+ },
30
+ }
31
+
32
+ function MyApp() {
33
+ return (
34
+ <>
35
+ <LocalesProvider config={localesConfig}>
36
+ {/* Your app content */}
37
+ </LocalesProvider>
38
+ </>
39
+ )
40
+ }
41
+ ```
42
+
43
+ ## Mở rộng với Custom Languages và Locales Keys
44
+
45
+ Bạn có thể thêm các ngôn ngữ tùy chỉnh ngoài `vi` và `en`, đồng thời định nghĩa custom locales keys với type safety thông qua **module augmentation** trong `global.d.ts`:
46
+
47
+ ### Bước 1: Tạo JSON files cho translations
48
+
49
+ `locales/vi.json`:
50
+
51
+ ```json
52
+ {
53
+ "app.title": "Ứng dụng của tôi",
54
+ "app.description": "Mô tả ứng dụng",
55
+ "user.greeting": "Xin chào {name}!",
56
+ "user.welcome": "Chào mừng bạn đến với {app}",
57
+ "button.submit": "Gửi",
58
+ "button.reset": "Đặt lại"
59
+ }
60
+ ```
61
+
62
+ `locales/en.json`:
63
+
64
+ ```json
65
+ {
66
+ "app.title": "My Application",
67
+ "app.description": "Application description",
68
+ "user.greeting": "Hello {name}!",
69
+ "user.welcome": "Welcome to {app}",
70
+ "button.submit": "Submit",
71
+ "button.reset": "Reset"
72
+ }
73
+ ```
74
+
75
+ `locales/jp.json`:
76
+
77
+ ```json
78
+ {
79
+ "app.title": "私のアプリケーション",
80
+ "app.description": "アプリケーションの説明",
81
+ "user.greeting": "こんにちは {name}!",
82
+ "user.welcome": "{app}へようこそ",
83
+ "button.submit": "送信",
84
+ "button.reset": "リセット"
85
+ }
86
+ ```
87
+
88
+ ### Bước 2: Declare module augmentation trong global.d.ts
89
+
90
+ Tạo hoặc update file `global.d.ts` (hoặc `vite-env.d.ts`) trong project của bạn:
91
+
92
+ ```tsx
93
+ import '@v-miniapp/ui-react'
94
+ import vi from './locales/vi.json'
95
+
96
+ declare module '@v-miniapp/ui-react' {
97
+ interface ICustomLocales {
98
+ resource: typeof vi
99
+ language: 'jp' | 'en' | 'vi'
100
+ }
101
+ }
102
+ ```
103
+
104
+ **Lưu ý:**
105
+
106
+ - Import từ JSON files và dùng `typeof vi` để infer types tự động, đảm bảo type safety
107
+ - `resource` là type của locales keys (từ JSON file)
108
+ - `language` là union type của các language codes mà bạn muốn hỗ trợ
109
+
110
+ ### Bước 3: Sử dụng trong localesConfig
111
+
112
+ ```tsx
113
+ import vi from './locales/vi.json'
114
+ import en from './locales/en.json'
115
+ import jp from './locales/jp.json'
116
+
117
+ const localesConfig: ILocalesConfig = {
118
+ defaultLanguage: 'vi',
119
+ resources: {
120
+ vi,
121
+ en,
122
+ jp,
123
+ },
124
+ }
125
+
126
+ export function MiniApp() {
127
+ return <App config={appConfig} localesConfig={localesConfig} />
128
+ }
129
+ ```
130
+
131
+ Sau khi declare `ICustomLocales`, TypeScript sẽ tự động infer:
132
+
133
+ - Locales keys từ `typeof vi`
134
+ - Language codes từ `'jp' | 'en' | 'vi'`
135
+
136
+ Tất cả các functions và hooks sẽ có type safety với các keys và languages đã declare.
137
+
138
+ ## Sử dụng Translation Functions
139
+
140
+ ### Standalone Functions
141
+
142
+ ```tsx
143
+ import { t, getLanguage, setLanguage } from '@v-miniapp/ui-react'
144
+
145
+ // Basic usage
146
+ const title = t('app.title')
147
+
148
+ // With template params
149
+ const greeting = t('user.greeting', { name: 'John' })
150
+ // Translation: "Xin chào {name}!" → "Xin chào John!"
151
+
152
+ // Get translation in specific language
153
+ const titleVi = t('app.title', undefined, 'vi')
154
+ const titleEn = t('app.title', undefined, 'en')
155
+
156
+ // Get current language
157
+ const currentLang = getLanguage() // 'vi' | 'en' | 'jp' (inferred từ ICustomLocales)
158
+
159
+ // Change language
160
+ setLanguage('en') // Type-safe, chỉ chấp nhận languages đã declare
161
+ ```
162
+
163
+ **⚠️ Lưu ý về Reactivity:**
164
+
165
+ Các standalone functions `t` **không có reactivity**. Khi ngôn ngữ thay đổi, component sử dụng các functions này sẽ **không tự động re-render**.
166
+
167
+ ```tsx
168
+ // ❌ Không reactive - Component không re-render khi language thay đổi
169
+ function MyComponent() {
170
+ const title = t('app.title')
171
+ const greeting = t('user.greeting', undefined, 'en')
172
+
173
+ return (
174
+ <div>
175
+ {title} - {greeting}
176
+ </div>
177
+ )
178
+ // Khi setLanguage() được gọi, component này sẽ KHÔNG re-render
179
+ }
180
+ ```
181
+
182
+ **Giải pháp:** Sử dụng **React Hook** `useTranslate` trong components để có reactivity.
183
+
184
+ ### React Hooks
185
+
186
+ Sử dụng hooks trong React components để có reactivity:
187
+
188
+ ```tsx
189
+ import { useTranslate, useLanguage } from '@v-miniapp/ui-react'
190
+
191
+ function MyComponent() {
192
+ // useTranslate hook - tự động infer keys từ ICustomLocales
193
+ const t = useTranslate()
194
+
195
+ // useLanguage hook - tự động infer languages từ ICustomLocales
196
+ const { language, setLanguage } = useLanguage()
197
+
198
+ return (
199
+ <div>
200
+ <h1>{t('app.title')}</h1>
201
+ <p>{t('user.greeting', { name: 'John' })}</p>
202
+
203
+ <p>Current language: {language}</p>
204
+ <button onClick={() => setLanguage('vi')}>Tiếng Việt</button>
205
+ <button onClick={() => setLanguage('en')}>English</button>
206
+ <button onClick={() => setLanguage('jp')}>日本語</button>
207
+ </div>
208
+ )
209
+ }
210
+ ```
211
+
212
+ **✅ Reactivity:**
213
+
214
+ React hook `useTranslate` **có reactivity**. Component sử dụng hook này sẽ **tự động re-render** khi ngôn ngữ thay đổi.
215
+
216
+ ```tsx
217
+ // ✅ Có reactive - Component tự động re-render khi language thay đổi
218
+ function MyComponent() {
219
+ const t = useTranslate()
220
+
221
+ return <div>{t('app.title')}</div>
222
+ // Khi setLanguage() được gọi, component này sẽ TỰ ĐỘNG re-render
223
+ }
224
+ ```
225
+
226
+ ## Template Parameters
227
+
228
+ Locales module hỗ trợ template parameters trong translation strings sử dụng cú pháp \`&#123;key&#125;\`:
229
+
230
+ ```tsx
231
+ // Locales definition (trong JSON files hoặc resources)
232
+ const resources = {
233
+ vi: {
234
+ 'user.greeting': 'Xin chào {name}!',
235
+ 'user.welcome': 'Chào mừng bạn đến với {app}',
236
+ 'calendar.headerLabel': '{month} - {year}',
237
+ },
238
+ en: {
239
+ 'user.greeting': 'Hello {name}!',
240
+ 'user.welcome': 'Welcome to {app}',
241
+ 'calendar.headerLabel': '{month} - {year}',
242
+ },
243
+ }
244
+
245
+ // Usage
246
+ t('user.greeting', { name: 'John' })
247
+ // → "Xin chào John!" (vi) hoặc "Hello John!" (en)
248
+
249
+ t('user.welcome', { app: 'MyApp' })
250
+ // → "Chào mừng bạn đến với MyApp" (vi) hoặc "Welcome to MyApp" (en)
251
+
252
+ t('calendar.headerLabel', { month: 'Tháng 1', year: '2024' })
253
+ // → "Tháng 1 - 2024"
254
+ ```
255
+
256
+ ## Type Inference và Type Safety
257
+
258
+ TypeScript tự động infer custom keys và custom languages từ module augmentation trong `global.d.ts`. Sau khi declare `ICustomLocales`:
259
+
260
+ ```tsx
261
+ // global.d.ts
262
+ declare module '@v-miniapp/ui-react' {
263
+ interface ICustomLocales {
264
+ resource: typeof vi // Infer keys từ vi.json
265
+ language: 'jp' | 'en' | 'vi' // Infer languages
266
+ }
267
+ }
268
+ ```
269
+
270
+ Tất cả các functions và hooks sẽ tự động có type safety:
271
+
272
+ ```tsx
273
+ // ✅ Type-safe: t() sẽ suggest keys từ ICustomLocales.resource
274
+ const title = t('app.title')
275
+
276
+ // ✅ Type-safe: setLanguage() chỉ chấp nhận 'jp' | 'en' | 'vi'
277
+ setLanguage('jp')
278
+
279
+ // ❌ Type error: 'ja' không có trong ICustomLocales.language
280
+ setLanguage('ja') // Error!
281
+
282
+ // ❌ Type error: Key không tồn tại trong ICustomLocales.resource
283
+ const invalid = t('app.invalidKey') // Error!
284
+ ```
285
+
286
+ ## Best Practices
287
+
288
+ ### 1. Sử dụng hooks trong React components
289
+
290
+ **Luôn sử dụng `useTranslate` trong React components** để có reactivity. Chỉ sử dụng standalone functions `t` khi:
291
+
292
+ - Bạn gọi từ bên ngoài React components (utility functions, services, etc.)
293
+ - Bạn không cần component re-render khi ngôn ngữ thay đổi
294
+
295
+ ```tsx
296
+ // ✅ Good - Trong React component, dùng hooks
297
+ function MyComponent() {
298
+ const t = useTranslate()
299
+ return <div>{t('app.title')}</div>
300
+ }
301
+
302
+ // ✅ Good - Ngoài React component, dùng standalone function
303
+ export function getPageTitle() {
304
+ return t('app.title')
305
+ }
306
+
307
+ // ❌ Bad - Trong React component, dùng standalone function (không reactive)
308
+ function MyComponent() {
309
+ const title = t('app.title')
310
+ return <div>{title}</div> // Sẽ không re-render khi language thay đổi
311
+ }
312
+ ```
313
+
314
+ ### 2. Đặt tên keys có cấu trúc
315
+
316
+ Sử dụng dot notation để nhóm keys theo module/feature:
317
+
318
+ ```tsx
319
+ // ✅ Good
320
+ 'app.title'
321
+ 'user.greeting'
322
+ 'button.submit'
323
+ 'form.validation.required'
324
+
325
+ // ❌ Bad
326
+ 'appTitle'
327
+ 'userGreeting'
328
+ 'submitButton'
329
+ ```
330
+
331
+ ### 3. Sử dụng module augmentation cho type safety
332
+
333
+ Declare `ICustomLocales` trong `global.d.ts` để có type safety cho cả keys và languages:
334
+
335
+ ```tsx
336
+ // ✅ Type-safe với module augmentation
337
+ declare module '@v-miniapp/ui-react' {
338
+ interface ICustomLocales {
339
+ resource: typeof vi
340
+ language: 'jp' | 'en' | 'vi'
341
+ }
342
+ }
343
+
344
+ // ❌ Không có type safety nếu không declare
345
+ const title = t('app.title') // key là string, không có autocomplete
346
+ ```
347
+
348
+ ### 4. Override default translations
349
+
350
+ Bạn có thể override các translation keys mặc định:
351
+
352
+ ```tsx
353
+ const localesConfig: ILocalesConfig = {
354
+ resources: {
355
+ vi: {
356
+ // Override default pull-to-refresh text
357
+ 'pullToRefresh.canReleaseText': 'Thả tay để làm mới',
358
+ 'pullToRefresh.completeText': 'Hoàn tất',
359
+
360
+ // Add custom keys
361
+ 'app.title': 'Ứng dụng của tôi',
362
+ },
363
+ },
364
+ }
365
+ ```
366
+
367
+ ### 5. Import từ JSON files
368
+
369
+ Import translations từ JSON files và sử dụng `typeof` để infer types:
370
+
371
+ ```tsx
372
+ // ✅ Import từ JSON
373
+ import vi from './locales/vi.json'
374
+
375
+ declare module '@v-miniapp/ui-react' {
376
+ interface ICustomLocales {
377
+ resource: typeof vi // Auto-infer types
378
+ language: 'jp' | 'en' | 'vi'
379
+ }
380
+ }
381
+ ```
382
+
383
+ ## Reference
384
+
385
+ ### Types
386
+
387
+ #### `ILocalesConfig`
388
+
389
+ Cấu hình locales cho App component hoặc LocalesProvider.
390
+
391
+ ```typescript
392
+ type ILocalesConfig = {
393
+ defaultLanguage?: IAppLanguage
394
+ resources?: IOptinalResources
395
+ }
396
+ ```
397
+
398
+ **Properties:**
399
+
400
+ | Name | Type | Required | Description |
401
+ | :---------------- | :------------------ | :------- | :------------------------------------------------------------------------------------------------------- |
402
+ | `defaultLanguage` | `IAppLanguage` | No | Ngôn ngữ mặc định. Có thể là `'vi'`, `'en'`, hoặc `'system'` (hoặc custom languages từ `ICustomLocales`) |
403
+ | `resources` | `IOptinalResources` | No | Object chứa các locales translations. Keys là language codes, values là resource maps |
404
+
405
+ #### `ICustomLocales`
406
+
407
+ Interface để declare custom locales thông qua module augmentation.
408
+
409
+ ```typescript
410
+ interface ICustomLocales {
411
+ resource: Record<string, string> // Type của locales keys (thường là typeof vi từ JSON)
412
+ language: string // Union type của language codes (e.g., 'vi' | 'en' | 'jp')
413
+ }
414
+ ```
415
+
416
+ **Ví dụ:**
417
+
418
+ ```tsx
419
+ // global.d.ts
420
+ declare module '@v-miniapp/ui-react' {
421
+ interface ICustomLocales {
422
+ resource: typeof vi // Infer từ vi.json
423
+ language: 'jp' | 'en' | 'vi' // Custom languages
424
+ }
425
+ }
426
+ ```
427
+
428
+ #### `IAppLanguage`
429
+
430
+ Union type của language codes bao gồm default và custom languages.
431
+
432
+ ```typescript
433
+ type IAppLanguage = ILang | 'system'
434
+ ```
435
+
436
+ `ILang` được infer từ `IDefaultLocales['language']` (default: `'vi' | 'en'`) và `ICustomLocales['language']` (nếu có).
437
+
438
+ ### Components
439
+
440
+ #### `LocalesProvider`
441
+
442
+ Component để khởi tạo locales module.
443
+
444
+ ```typescript
445
+ type LocalesProviderProps = {
446
+ config?: ILocalesConfig
447
+ }
448
+ ```
449
+
450
+ **Usage:**
451
+
452
+ ```tsx
453
+ <LocalesProvider config={localesConfig} />
454
+ ```
455
+
456
+ **Lưu ý:** Component này chỉ init một lần khi mount, nên cần đặt ở top-level.
457
+
458
+ ### Functions & Hooks
459
+
460
+ #### `t(key, params?, lang?)`
461
+
462
+ Standalone function để translate key.
463
+
464
+ - **Parameters:**
465
+ - `key`: `IResourceKey` - Locales key (inferred từ `ICustomLocales.resource` nếu có)
466
+ - `params?`: `ITemplateParams` - Template parameters object
467
+ - `lang?`: `ILang` - Ngôn ngữ chỉ định
468
+ - **Returns:** `string` - Translated text
469
+ - **Reactivity:** ❌ Không có reactivity
470
+
471
+ #### `useTranslate()`
472
+
473
+ React hook để translate trong components.
474
+
475
+ - **Returns:** Function tương tự `t(key, params?)`
476
+ - **Reactivity:** ✅ Có reactivity
477
+
478
+ #### `getLanguage()`
479
+
480
+ Standalone function để lấy current language.
481
+
482
+ - **Returns:** `ILang` - Current language
483
+
484
+ #### `setLanguage(language)`
485
+
486
+ Standalone function để thay đổi language.
487
+
488
+ - **Parameters:**
489
+ - `language`: `ILang` - Language code (inferred từ `ICustomLocales.language` nếu có)
490
+
491
+ #### `useLanguage()`
492
+
493
+ React hook để quản lý language.
494
+
495
+ - **Returns:** `{ language: ILang, setLanguage: (lang: ILang) => void }`
496
+ - `language`: Current language (inferred từ `ICustomLocales.language` nếu có)
497
+ - `setLanguage`: Function để thay đổi language (type-safe với `ICustomLocales.language`)
package/dist/fns.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { IAppLanguage, ILang, IResourceKey, ITemplateParams } from './types';
2
+ export declare const translate: (key: IResourceKey, params?: ITemplateParams, lang?: ILang) => string;
3
+ export declare const t: (key: IResourceKey, params?: ITemplateParams, lang?: ILang) => string;
4
+ export declare const getLanguage: () => IAppLanguage;
5
+ export declare const getSystemLanguage: () => ILang;
6
+ export declare const setLanguage: (language: ILang) => void;
@@ -0,0 +1,10 @@
1
+ import { IAppLanguage, ILang, IResourceKey, ITemplateParams } from './types';
2
+ export declare function useTranslate(): (key: IResourceKey, params?: ITemplateParams, lang?: ILang) => string;
3
+ type IUseLanguageResult = {
4
+ language: IAppLanguage;
5
+ systemLanguage: ILang;
6
+ setLanguage: (language: ILang) => void;
7
+ };
8
+ export declare function useLanguage(): IUseLanguageResult;
9
+ export declare const useIsUsingLocales: () => boolean;
10
+ export {};
@@ -0,0 +1,4 @@
1
+ export type * from './types';
2
+ export * from './fns';
3
+ export * from './provider';
4
+ export * from './hooks';