i18n-typed-store 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -10,6 +10,10 @@ Type-safe translation store for managing i18n locales with full TypeScript suppo
10
10
  - ✅ **Pluralization support** - Built-in plural form selector using `Intl.PluralRules`
11
11
  - ✅ **Flexible module loading** - Support for any module format (ESM, CommonJS, dynamic imports)
12
12
  - ✅ **Zero runtime dependencies** - Lightweight and framework-agnostic
13
+ - ✅ **React integration** - Hooks and components for React applications
14
+ - ✅ **SSR/SSG support** - Built-in utilities for server-side rendering
15
+ - ✅ **Fallback locales** - Automatic merging with fallback translations
16
+ - ✅ **Caching** - Built-in translation caching for better performance
13
17
 
14
18
  ## Installation
15
19
 
@@ -55,24 +59,212 @@ type TranslationData = {
55
59
  };
56
60
  };
57
61
 
58
- // Create the store
59
- const storeFactory = createTranslationStore(
62
+ // Create the store factory
63
+ const storeFactory = createTranslationStore({
60
64
  translations,
61
65
  locales,
62
- async (locale, translation) => {
66
+ loadModule: async (locale, translation) => {
63
67
  // Load translation module dynamically
64
68
  const module = await import(`./locales/${locale}/${translation}.json`);
65
- return module.default;
69
+ return module;
66
70
  },
67
- (module, locale, translation) => module // Extract translation data
68
- );
71
+ extractTranslation: (module, locale, translation) => {
72
+ // Extract translation data from module
73
+ return module.default || module;
74
+ },
75
+ defaultLocale: 'en',
76
+ useFallback: true,
77
+ fallbackLocale: 'en',
78
+ });
69
79
 
70
80
  // Create typed store
71
81
  const store = storeFactory.type<TranslationData>();
72
82
 
73
83
  // Load and use translations
74
- await store.common.load('en');
75
- console.log(store.common.translation?.title); // Type-safe access
84
+ await store.translations.common.load('en');
85
+ const title = store.translations.common.currentTranslation?.title; // Type-safe access
86
+ ```
87
+
88
+ ### React Usage
89
+
90
+ ```tsx
91
+ import { createTranslationStore } from 'i18n-typed-store';
92
+ import { I18nTypedStoreProvider, useI18nTranslation, useI18nLocale } from 'i18n-typed-store/react';
93
+
94
+ // Setup store (same as above)
95
+ const storeFactory = createTranslationStore({
96
+ translations: { common: 'common' },
97
+ locales: { en: 'en', ru: 'ru' },
98
+ loadModule: async (locale, translation) => {
99
+ return await import(`./locales/${locale}/${translation}.json`);
100
+ },
101
+ extractTranslation: (module) => module.default,
102
+ defaultLocale: 'en',
103
+ });
104
+
105
+ type TranslationData = {
106
+ common: { greeting: string; title: string };
107
+ };
108
+
109
+ const store = storeFactory.type<TranslationData>();
110
+
111
+ // In your App component
112
+ function App() {
113
+ return (
114
+ <I18nTypedStoreProvider store={store}>
115
+ <MyComponent />
116
+ </I18nTypedStoreProvider>
117
+ );
118
+ }
119
+
120
+ // In your components
121
+ function MyComponent() {
122
+ const translations = useI18nTranslation('common');
123
+ const { locale, setLocale } = useI18nLocale();
124
+
125
+ if (!translations) {
126
+ return <div>Loading...</div>;
127
+ }
128
+
129
+ return (
130
+ <div>
131
+ <h1>{translations.title}</h1>
132
+ <p>{translations.greeting}</p>
133
+ <button onClick={() => setLocale('ru')}>Switch to Russian</button>
134
+ </div>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### React Suspense Support
140
+
141
+ ```tsx
142
+ import { Suspense } from 'react';
143
+ import { useI18nTranslationLazy } from 'i18n-typed-store/react';
144
+
145
+ function MyComponent() {
146
+ // This hook throws a promise if translation is not loaded (for Suspense)
147
+ const translations = useI18nTranslationLazy('common');
148
+
149
+ return (
150
+ <div>
151
+ <h1>{translations.title}</h1>
152
+ <p>{translations.greeting}</p>
153
+ </div>
154
+ );
155
+ }
156
+
157
+ function App() {
158
+ return (
159
+ <I18nTypedStoreProvider store={store} suspenseMode="first-load-locale">
160
+ <Suspense fallback={<div>Loading translations...</div>}>
161
+ <MyComponent />
162
+ </Suspense>
163
+ </I18nTypedStoreProvider>
164
+ );
165
+ }
166
+ ```
167
+
168
+ ### SSR with Next.js
169
+
170
+ ```typescript
171
+ // lib/i18n.ts
172
+ import { createTranslationStore } from 'i18n-typed-store';
173
+ import { getLocaleFromRequest, initializeStore } from 'i18n-typed-store/react';
174
+
175
+ const translations = { common: 'common', errors: 'errors' } as const;
176
+ const locales = { en: 'en', ru: 'ru' } as const;
177
+
178
+ export const storeFactory = createTranslationStore({
179
+ translations,
180
+ locales,
181
+ loadModule: async (locale, translation) => {
182
+ return await import(`./locales/${locale}/${translation}.json`);
183
+ },
184
+ extractTranslation: (module) => module.default,
185
+ defaultLocale: 'en',
186
+ });
187
+
188
+ type TranslationData = {
189
+ common: { title: string };
190
+ errors: { notFound: string };
191
+ };
192
+
193
+ export type Store = ReturnType<typeof storeFactory.type<TranslationData>>;
194
+ ```
195
+
196
+ ```typescript
197
+ // pages/_app.tsx or app/layout.tsx
198
+ import { I18nTypedStoreProvider } from 'i18n-typed-store/react';
199
+ import { storeFactory } from '../lib/i18n';
200
+
201
+ const store = storeFactory.type<TranslationData>();
202
+
203
+ function MyApp({ Component, pageProps }: AppProps) {
204
+ return (
205
+ <I18nTypedStoreProvider store={store}>
206
+ <Component {...pageProps} />
207
+ </I18nTypedStoreProvider>
208
+ );
209
+ }
210
+ ```
211
+
212
+ ```typescript
213
+ // pages/index.tsx (getServerSideProps)
214
+ import type { GetServerSidePropsContext } from 'next';
215
+ import { getLocaleFromRequest, initializeStore } from 'i18n-typed-store/react';
216
+ import { storeFactory } from '../lib/i18n';
217
+
218
+ export async function getServerSideProps(context: GetServerSidePropsContext) {
219
+ const locale = getLocaleFromRequest(context, {
220
+ defaultLocale: 'en',
221
+ availableLocales: ['en', 'ru'],
222
+ cookieName: 'locale',
223
+ queryParamName: 'locale',
224
+ });
225
+
226
+ const store = storeFactory.type<TranslationData>();
227
+ initializeStore(store, locale);
228
+
229
+ // Preload translations if needed
230
+ await store.translations.common.load(locale);
231
+
232
+ return {
233
+ props: {
234
+ locale,
235
+ // You can pass translations as props or use context
236
+ },
237
+ };
238
+ }
239
+ ```
240
+
241
+ ```typescript
242
+ // app/page.tsx (App Router)
243
+ import { getLocaleFromRequest, initializeStore } from 'i18n-typed-store/react';
244
+ import { storeFactory } from '../lib/i18n';
245
+ import { headers, cookies } from 'next/headers';
246
+
247
+ export default async function Page() {
248
+ const headersList = await headers();
249
+ const cookieStore = await cookies();
250
+
251
+ const locale = getLocaleFromRequest(
252
+ {
253
+ headers: Object.fromEntries(headersList),
254
+ cookies: Object.fromEntries(cookieStore),
255
+ },
256
+ {
257
+ defaultLocale: 'en',
258
+ availableLocales: ['en', 'ru'],
259
+ }
260
+ );
261
+
262
+ const store = storeFactory.type<TranslationData>();
263
+ initializeStore(store, locale);
264
+ await store.translations.common.load(locale);
265
+
266
+ return <div>...</div>;
267
+ }
76
268
  ```
77
269
 
78
270
  ## Core API
@@ -82,34 +274,51 @@ console.log(store.common.translation?.title); // Type-safe access
82
274
  Creates a type-safe translation store with lazy loading support.
83
275
 
84
276
  ```typescript
85
- const storeFactory = createTranslationStore<T, L, Module>(
86
- translations: T,
87
- locales: L,
88
- loadModule: (locale: keyof L, translation: keyof T) => Promise<Module>,
89
- extractTranslation: (module: Module, locale: keyof L, translation: keyof T) => unknown
90
- );
277
+ function createTranslationStore<T, L, Module>(options: {
278
+ translations: T;
279
+ locales: L;
280
+ loadModule: (locale: keyof L, namespace: keyof T) => Promise<Module>;
281
+ extractTranslation: (module: Module, locale: keyof L, namespace: keyof T) => unknown | Promise<unknown>;
282
+ defaultLocale: keyof L;
283
+ useFallback?: boolean;
284
+ fallbackLocale?: keyof L;
285
+ deleteOtherLocalesAfterLoad?: boolean;
286
+ loadFromCache?: boolean;
287
+ changeLocaleEventName?: string;
288
+ }): {
289
+ type<M extends { [K in keyof T]: any }>(): TranslationStore<T, L, M>;
290
+ }
91
291
  ```
92
292
 
93
- **Parameters:**
293
+ **Options:**
94
294
 
95
295
  - `translations` - Object with translation keys (e.g., `{ common: 'common', errors: 'errors' }`)
96
296
  - `locales` - Object with locale keys (e.g., `{ en: 'en', ru: 'ru' }`)
97
297
  - `loadModule` - Async function to load a translation module
98
- - `extractTranslation` - Function to extract translation data from the loaded module. Receives the module, locale, and translation key as parameters, allowing for locale-specific or translation-specific extraction logic.
298
+ - `extractTranslation` - Function to extract translation data from the loaded module. Receives the module, locale, and namespace key as parameters
299
+ - `defaultLocale` - Default locale key to use
300
+ - `useFallback` - Whether to use fallback locale for missing translations (default: `false`)
301
+ - `fallbackLocale` - Fallback locale key (default: `defaultLocale`)
302
+ - `deleteOtherLocalesAfterLoad` - Whether to delete translations for other locales after loading (default: `false`)
303
+ - `loadFromCache` - Whether to load translations from cache by default (default: `true`)
304
+ - `changeLocaleEventName` - Event name for locale change events (default: `'change-locale'`)
99
305
 
100
306
  **Returns:** Object with `type<M>()` method that creates a typed store.
101
307
 
102
308
  **Example:**
103
309
 
104
310
  ```typescript
105
- const storeFactory = createTranslationStore(
106
- { common: 'common' },
107
- { en: 'en', ru: 'ru' },
108
- async (locale, translation) => {
311
+ const storeFactory = createTranslationStore({
312
+ translations: { common: 'common' },
313
+ locales: { en: 'en', ru: 'ru' },
314
+ loadModule: async (locale, translation) => {
109
315
  return await import(`./locales/${locale}/${translation}.json`);
110
316
  },
111
- (module, locale, translation) => module.default
112
- );
317
+ extractTranslation: (module, locale, translation) => module.default,
318
+ defaultLocale: 'en',
319
+ useFallback: true,
320
+ fallbackLocale: 'en',
321
+ });
113
322
 
114
323
  type TranslationData = {
115
324
  common: { title: string; description: string };
@@ -118,10 +327,10 @@ type TranslationData = {
118
327
  const store = storeFactory.type<TranslationData>();
119
328
 
120
329
  // Load translation
121
- await store.common.load('en');
330
+ await store.translations.common.load('en');
122
331
 
123
332
  // Access translation (type-safe)
124
- const title = store.common.translation?.title;
333
+ const title = store.translations.common.currentTranslation?.title;
125
334
  ```
126
335
 
127
336
  ### `createTranslationModuleMap`
@@ -129,11 +338,11 @@ const title = store.common.translation?.title;
129
338
  Creates a map of translation module loaders for all combinations of translations and locales.
130
339
 
131
340
  ```typescript
132
- const moduleMap = createTranslationModuleMap<T, L, Module>(
341
+ function createTranslationModuleMap<T, L, Module>(
133
342
  translations: T,
134
343
  locales: L,
135
344
  loadModule: (locale: keyof L, translation: keyof T) => Promise<Module>
136
- );
345
+ ): Record<keyof T, Record<keyof L, () => Promise<Module>>>
137
346
  ```
138
347
 
139
348
  **Example:**
@@ -157,7 +366,10 @@ const module = await loader();
157
366
  Creates a plural form selector function for a specific locale using `Intl.PluralRules`.
158
367
 
159
368
  ```typescript
160
- const selectPlural = createPluralSelector(locale: string);
369
+ function createPluralSelector(
370
+ locale: string,
371
+ options?: { strict?: boolean }
372
+ ): (count: number, variants: PluralVariants) => string
161
373
  ```
162
374
 
163
375
  **Example:**
@@ -194,43 +406,147 @@ selectPlural(2, variants); // => 'яблока'
194
406
  selectPlural(5, variants); // => 'яблок'
195
407
  ```
196
408
 
409
+ ## React API
410
+
411
+ ### `I18nTypedStoreProvider`
412
+
413
+ Provider component that wraps your application to provide translation store context.
414
+
415
+ ```tsx
416
+ <I18nTypedStoreProvider
417
+ store={store}
418
+ suspenseMode="first-load-locale"
419
+ >
420
+ {children}
421
+ </I18nTypedStoreProvider>
422
+ ```
423
+
424
+ **Props:**
425
+
426
+ - `store` - Translation store instance
427
+ - `suspenseMode` - Suspense mode: `'once'` | `'first-load-locale'` | `'change-locale'` (default: `'first-load-locale'`)
428
+ - `children` - React children
429
+
430
+ ### `useI18nTranslation`
431
+
432
+ Hook for accessing translations with automatic loading. Returns `undefined` if translation is not yet loaded.
433
+
434
+ ```tsx
435
+ const translations = useI18nTranslation('common', fromCache?: boolean);
436
+ ```
437
+
438
+ ### `useI18nTranslationLazy`
439
+
440
+ Hook for accessing translations with React Suspense support. Throws a promise if translation is not loaded.
441
+
442
+ ```tsx
443
+ const translations = useI18nTranslationLazy('common', fromCache?: boolean);
444
+ ```
445
+
446
+ ### `useI18nLocale`
447
+
448
+ Hook for accessing and managing the current locale.
449
+
450
+ ```tsx
451
+ const { locale, setLocale } = useI18nLocale();
452
+ ```
453
+
454
+ ### `Safe`
455
+
456
+ Component that safely extracts strings from translation objects, catching errors.
457
+
458
+ ```tsx
459
+ <Safe
460
+ errorComponent={<span>N/A</span>}
461
+ errorHandler={(error) => console.error(error)}
462
+ >
463
+ {() => translations.common.pages.main.title}
464
+ </Safe>
465
+ ```
466
+
467
+ ## SSR API
468
+
469
+ ### `getLocaleFromRequest`
470
+
471
+ Gets locale from SSR request context (query params, cookies, headers).
472
+
473
+ ```typescript
474
+ function getLocaleFromRequest<L extends Record<string, string>>(
475
+ context: RequestContext,
476
+ options: {
477
+ defaultLocale: string;
478
+ availableLocales: readonly string[];
479
+ headerName?: string;
480
+ cookieName?: string;
481
+ queryParamName?: string;
482
+ parseAcceptLanguage?: boolean;
483
+ }
484
+ ): keyof L
485
+ ```
486
+
487
+ **Example:**
488
+
489
+ ```typescript
490
+ const locale = getLocaleFromRequest(context, {
491
+ defaultLocale: 'en',
492
+ availableLocales: ['en', 'ru'],
493
+ cookieName: 'locale',
494
+ queryParamName: 'locale',
495
+ headerName: 'accept-language',
496
+ parseAcceptLanguage: true,
497
+ });
498
+ ```
499
+
500
+ ### `initializeStore`
501
+
502
+ Initializes translation store with a specific locale for SSR.
503
+
504
+ ```typescript
505
+ function initializeStore<T, L, M>(
506
+ store: TranslationStore<T, L, M>,
507
+ locale: keyof L
508
+ ): void
509
+ ```
510
+
197
511
  ## Advanced Usage
198
512
 
199
513
  ### Working with Dynamic Imports
200
514
 
201
515
  ```typescript
202
- const storeFactory = createTranslationStore(
516
+ const storeFactory = createTranslationStore({
203
517
  translations,
204
518
  locales,
205
- async (locale, translation) => {
519
+ loadModule: async (locale, translation) => {
206
520
  // Dynamic import with error handling
207
521
  try {
208
522
  const module = await import(
209
523
  `./locales/${locale}/${translation}.json`
210
524
  );
211
- return module.default;
525
+ return module;
212
526
  } catch (error) {
213
527
  console.error(`Failed to load ${translation} for ${locale}`);
214
528
  throw error;
215
529
  }
216
530
  },
217
- (module, locale, translation) => module
218
- );
531
+ extractTranslation: (module, locale, translation) => {
532
+ return module.default || module;
533
+ },
534
+ defaultLocale: 'en',
535
+ });
219
536
  ```
220
537
 
221
538
  ### Custom Module Extraction
222
539
 
223
- The `extractTranslation` function receives the module, locale, and translation key, allowing for advanced extraction logic:
540
+ The `extractTranslation` function receives the module, locale, and namespace key, allowing for advanced extraction logic:
224
541
 
225
542
  ```typescript
226
- const storeFactory = createTranslationStore(
543
+ const storeFactory = createTranslationStore({
227
544
  translations,
228
545
  locales,
229
- async (locale, translation) => {
230
- // Load module that exports default
546
+ loadModule: async (locale, translation) => {
231
547
  return await import(`./locales/${locale}/${translation}.ts`);
232
548
  },
233
- (module, locale, translation) => {
549
+ extractTranslation: (module, locale, translation) => {
234
550
  // Extract from module.default or module
235
551
  // You can use locale and translation parameters for custom logic
236
552
  if (locale === 'en' && translation === 'common') {
@@ -238,8 +554,9 @@ const storeFactory = createTranslationStore(
238
554
  return module.default?.en || module.default;
239
555
  }
240
556
  return module.default || module;
241
- }
242
- );
557
+ },
558
+ defaultLocale: 'en',
559
+ });
243
560
  ```
244
561
 
245
562
  ### Handling Multiple Translation Namespaces
@@ -262,12 +579,34 @@ type TranslationData = {
262
579
  const store = storeFactory.type<TranslationData>();
263
580
 
264
581
  // Load specific translations
265
- await store.common.load('en');
266
- await store.ui.load('en');
582
+ await store.translations.common.load('en');
583
+ await store.translations.ui.load('en');
267
584
 
268
585
  // Access translations
269
- const title = store.common.translation?.title;
270
- const saveButton = store.ui.translation?.buttons.save;
586
+ const title = store.translations.common.currentTranslation?.title;
587
+ const saveButton = store.translations.ui.currentTranslation?.buttons.save;
588
+ ```
589
+
590
+ ### Using Fallback Locales
591
+
592
+ When `useFallback` is enabled, missing translations are automatically filled from the fallback locale:
593
+
594
+ ```typescript
595
+ const storeFactory = createTranslationStore({
596
+ translations: { common: 'common' },
597
+ locales: { en: 'en', ru: 'ru' },
598
+ loadModule: async (locale, translation) => {
599
+ return await import(`./locales/${locale}/${translation}.json`);
600
+ },
601
+ extractTranslation: (module) => module.default,
602
+ defaultLocale: 'en',
603
+ useFallback: true,
604
+ fallbackLocale: 'en',
605
+ });
606
+
607
+ // If 'ru' translation is missing some keys, they will be filled from 'en'
608
+ await store.translations.common.load('ru');
609
+ // Result: merged translation with 'en' as fallback
271
610
  ```
272
611
 
273
612
  ## Type Safety
@@ -276,16 +615,16 @@ The library provides complete type safety:
276
615
 
277
616
  ```typescript
278
617
  // ✅ TypeScript knows all available translation keys
279
- const title = store.common.translation?.title;
618
+ const title = store.translations.common.currentTranslation?.title;
280
619
 
281
620
  // ❌ TypeScript error: 'invalidKey' doesn't exist
282
- const invalid = store.common.translation?.invalidKey;
621
+ const invalid = store.translations.common.currentTranslation?.invalidKey;
283
622
 
284
623
  // ✅ TypeScript knows all available locales
285
- await store.common.load('en');
624
+ await store.translations.common.load('en');
286
625
 
287
626
  // ❌ TypeScript error: 'fr' is not a valid locale
288
- await store.common.load('fr');
627
+ await store.translations.common.load('fr');
289
628
  ```
290
629
 
291
630
  ## Pluralization
@@ -307,61 +646,6 @@ The library uses `Intl.PluralRules` for plural form selection, supporting all Un
307
646
  - Arabic (zero/one/two/few/many/other)
308
647
  - And many more...
309
648
 
310
- ## API Reference
311
-
312
- ### `createTranslationStore`
313
-
314
- ```typescript
315
- function createTranslationStore<
316
- T extends Record<string, string>,
317
- L extends Record<string, string>,
318
- Module = unknown
319
- >(
320
- translations: T,
321
- locales: L,
322
- loadModule: (locale: keyof L, translation: keyof T) => Promise<Module>,
323
- extractTranslation: (module: Module, locale: keyof L, translation: keyof T) => unknown
324
- ): {
325
- type<M extends { [K in keyof T]: Record<string, unknown> }>(): TranslationStore<T, L, M>;
326
- }
327
- ```
328
-
329
- ### `createTranslationModuleMap`
330
-
331
- ```typescript
332
- function createTranslationModuleMap<
333
- T extends Record<string, string>,
334
- L extends Record<string, string>,
335
- Module = unknown
336
- >(
337
- translations: T,
338
- locales: L,
339
- loadModule: (locale: keyof L, translation: keyof T) => Promise<Module>
340
- ): Record<keyof T, Record<keyof L, () => Promise<Module>>>
341
- ```
342
-
343
- ### `createPluralSelector`
344
-
345
- ```typescript
346
- function createPluralSelector(locale: string): (
347
- count: number,
348
- variants: PluralVariants
349
- ) => string
350
- ```
351
-
352
- ### `PluralVariants`
353
-
354
- ```typescript
355
- type PluralVariants = {
356
- zero?: string;
357
- one?: string;
358
- two?: string;
359
- few?: string;
360
- many?: string;
361
- other?: string;
362
- };
363
- ```
364
-
365
649
  ## Examples
366
650
 
367
651
  ### Example: E-commerce Application
@@ -396,24 +680,25 @@ type TranslationData = {
396
680
  };
397
681
  };
398
682
 
399
- const storeFactory = createTranslationStore(
683
+ const storeFactory = createTranslationStore({
400
684
  translations,
401
685
  locales,
402
- async (locale, translation) => {
686
+ loadModule: async (locale, translation) => {
403
687
  return await import(`./locales/${locale}/${translation}.json`);
404
688
  },
405
- (module, locale, translation) => module.default
406
- );
689
+ extractTranslation: (module) => module.default,
690
+ defaultLocale: 'en',
691
+ });
407
692
 
408
693
  const store = storeFactory.type<TranslationData>();
409
694
 
410
695
  // Load translations
411
- await store.products.load('en');
412
- await store.cart.load('en');
696
+ await store.translations.products.load('en');
697
+ await store.translations.cart.load('en');
413
698
 
414
699
  // Use translations
415
- const productTitle = store.products.translation?.title;
416
- const cartTitle = store.cart.translation?.title;
700
+ const productTitle = store.translations.products.currentTranslation?.title;
701
+ const cartTitle = store.translations.cart.currentTranslation?.title;
417
702
  ```
418
703
 
419
704
  ### Example: Pluralization in Product List
@@ -435,6 +720,56 @@ getProductCountText(1); // => "1 product"
435
720
  getProductCountText(5); // => "5 products"
436
721
  ```
437
722
 
723
+ ## API Reference
724
+
725
+ ### `createTranslationStore`
726
+
727
+ ```typescript
728
+ function createTranslationStore<
729
+ T extends Record<string, string>,
730
+ L extends Record<string, string>,
731
+ Module = unknown
732
+ >(options: CreateTranslationStoreOptions<T, L, Module>): {
733
+ type<M extends { [K in keyof T]: any }>(): TranslationStore<T, L, M>;
734
+ }
735
+ ```
736
+
737
+ ### `createTranslationModuleMap`
738
+
739
+ ```typescript
740
+ function createTranslationModuleMap<
741
+ T extends Record<string, string>,
742
+ L extends Record<string, string>,
743
+ Module = unknown
744
+ >(
745
+ translations: T,
746
+ locales: L,
747
+ loadModule: (locale: keyof L, translation: keyof T) => Promise<Module>
748
+ ): Record<keyof T, Record<keyof L, () => Promise<Module>>>
749
+ ```
750
+
751
+ ### `createPluralSelector`
752
+
753
+ ```typescript
754
+ function createPluralSelector(
755
+ locale: string,
756
+ options?: { strict?: boolean }
757
+ ): (count: number, variants: PluralVariants) => string
758
+ ```
759
+
760
+ ### `PluralVariants`
761
+
762
+ ```typescript
763
+ type PluralVariants = {
764
+ zero?: string;
765
+ one?: string;
766
+ two?: string;
767
+ few?: string;
768
+ many?: string;
769
+ other?: string;
770
+ };
771
+ ```
772
+
438
773
  ## Contributing
439
774
 
440
775
  Contributions are welcome! Please feel free to submit a Pull Request.