@sheet-i18n/react 1.5.3 β†’ 1.6.0-canary.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.
Files changed (2) hide show
  1. package/README.md +201 -23
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -13,6 +13,7 @@ This package provides tools to handle translations in React applications using c
13
13
  - **`IntlProvider`**: _React Translation Provider_ for managing current locale.
14
14
  - **`useTranslation`**: _Client Side Translation Hook_ for easy access to translation messages on the client side.
15
15
  - **`getTranslation`**: _Static Translation Function_ for easy access to translation messages on Static module files.
16
+ - **`ruleFactory`**: _Rule Factory Function_ for creating custom translation rules with conditional logic.
16
17
 
17
18
  ## πŸš€ Getting Started(Manually)
18
19
 
@@ -232,6 +233,8 @@ Generates React context, including the `IntlProvider` and `useTranslation`.
232
233
  #### Parameters:
233
234
 
234
235
  - **`i18nStore`**: Instance of `I18nStore`.
236
+ - **`plugins`** (optional): Plugin configuration object.
237
+ - **`rules`** (optional): Custom rules object for conditional translations. See [Plugins](#-plugins) section for details.
235
238
 
236
239
  > ⚠️ Caveats:
237
240
  >
@@ -303,31 +306,93 @@ A function to access translations in the environment where cannot use react cont
303
306
 
304
307
  ### `t Function`
305
308
 
306
- The t function is used to retrieve a translation string based on a key and optionally interpolate variables. It provides flexibility to include dynamic values, such as plain strings or React components, for enhanced localization capabilities.
309
+ The `t` function is used to retrieve translation strings based on keys and optionally interpolate variables. It provides flexibility to include dynamic values, such as plain strings or React components, for enhanced localization capabilities.
307
310
 
308
- #### Parameters:
311
+ #### **Overview: `t()`, `t.rule()`, and `t.dynamic()`**
312
+
313
+ | Method | Use Case | Key Type | When to Use |
314
+ | ----------------------------------- | ------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------- |
315
+ | **`t(key, values?)`** | Standard translation with explicit keys | Static (known at build time) | When you know the translation key beforehand |
316
+ | **`t.rule(ruleKey, values?)`** | Conditional translation using custom rules | Determined by rule function | When you need conditional logic (pluralization, gender-based, etc.) |
317
+ | **`t.dynamic(id, values?, opts?)`** | Translation with runtime keys | Dynamic (from API, user input, etc.) | When the translation key comes from runtime data |
318
+
319
+ #### **`t()` - Standard Translation**
320
+
321
+ Retrieve translations for explicit keys that are known at build time.
322
+
323
+ ##### Parameters:
309
324
 
310
- - key (string): The translation key to retrieve the localized string.
325
+ - **`key`** (string): The translation key to retrieve the localized string.
326
+ - **`values`** (object, optional): An object containing key-value pairs to interpolate into the translation string.
327
+
328
+ ##### Examples:
311
329
 
312
330
  ```tsx
313
- const translatedMessage = t('login'); // login is key
331
+ const { t } = useTranslation('header');
332
+
333
+ // Simple translation
334
+ const loginText = t('login'); // "Login" or "둜그인"
335
+
336
+ // Translation with interpolation
337
+ const welcomeMessage = t('{username} shown', { username: 'John Doe' });
338
+ // Result: "John Doe shown"
339
+
340
+ // Interpolation with React components
341
+ const message = t('{username} shown', { username: <Username /> });
314
342
  ```
315
343
 
316
- - values (object, optional): An object containing key-value pairs to interpolate into the translation string.
344
+ πŸ’‘ **Note**: The values object can contain any type of data, including React components.
345
+
346
+ #### **`t.rule()` - Conditional Translation with Custom Rules**
347
+
348
+ Use custom rules for conditional translations based on dynamic values. This method is available when custom rules are registered via the `createI18nContext` plugins option.
349
+
350
+ ##### Parameters:
351
+
352
+ - **`ruleKey`** (string): The key of the registered custom rule.
353
+ - **`values`** (object, optional): An object containing values to be passed to the rule function and used for interpolation.
354
+
355
+ ##### Examples:
317
356
 
318
357
  ```tsx
319
- // John Doe shown
320
- const translatedMessage = t('{username} shown', { username: 'John Doe' });
358
+ // Assuming custom rules are registered
359
+ const { t } = useTranslation('landing');
360
+
361
+ // Call rule with values
362
+ const message = t.rule('plural', { age: 21 });
363
+ // The rule function determines which translation key to use based on age
321
364
  ```
322
365
 
323
- πŸ’‘ Note: The values object can contain any type of data, including React components.
366
+ πŸ’‘ **Note**: `t.rule()` works exactly like `t()` in terms of interpolation. If the translation key returned by the rule function contains `{slot}` placeholders, they will be automatically replaced using the values object passed to `t.rule()`.
367
+
368
+ For more details, see the [Plugins](#-plugins) section.
369
+
370
+ #### **`t.dynamic()` - Runtime Key Translation**
371
+
372
+ Resolve translation keys that are not known at build time (e.g., values coming from API responses, user input, or other runtime sources).
373
+
374
+ ##### Parameters:
375
+
376
+ - **`id`** (string): A runtime string to match against the current locale's translation keys.
377
+ - **`values`** (object, optional): Interpolation values, identical to `t()`.
378
+ - **`opts`** (object, optional): Formatter options.
379
+
380
+ ##### Examples:
324
381
 
325
382
  ```tsx
326
- // <Username /> shown
327
- const translatedMessage = t('{username} shown', { username: <Username /> });
383
+ const { t } = useTranslation('header');
384
+ const { data } = useQuery(...queryOptions);
385
+
386
+ // Runtime key from API response
387
+ const deviceName = data?.device?.name; // e.g., "smartphone"
388
+
389
+ // Will translate the runtime key if it exists in the current locale
390
+ const label = t.dynamic(deviceName);
391
+ // If "smartphone" exists in locale JSON, returns translated value
392
+ // Otherwise, returns "smartphone" as fallback
328
393
  ```
329
394
 
330
- <br/>
395
+ πŸ’‘ **Tip**: Ensure your locale JSON contains possible runtime keys; otherwise `t.dynamic` will fall back to the provided id string.
331
396
 
332
397
  ## πŸ“„ Best Practices of Translation utilities
333
398
 
@@ -383,11 +448,6 @@ return <div>{t.dynamic(deviceName)}</div>;
383
448
 
384
449
  The `getTranslation` function allows you to use translations outside of React components, such as in static configuration files, constants, or utility modules.
385
450
 
386
- It provide two possible supports
387
-
388
- 1. **`t` (Synchronous Translation)** – Use when translation values are available **at runtime**.
389
- 2. **`t.promise` (Asynchronous Translation)** – Use when the module’s evaluation order is uncertain.
390
-
391
451
  #### **βœ… Usage Scenarios**
392
452
 
393
453
  #### **[Scenario 1]: Context where the call expression of `t` function is evaluated at runtime**
@@ -422,32 +482,46 @@ export default function App() {
422
482
 
423
483
  - If the module is **imported dynamically in a client-side component**, the evaluation order of `getTranslation` and `t` call expression may not be guaranteed.
424
484
  - Since JavaScript **evaluates modules at import time**, it may attempt to access translation values before `IntlProvider` has fully initialized the locale.
425
- - In this case, use **`t.promise`** to ensure the translation resolves asynchronously.
485
+ - In this case, use **`getTranslation`** and **`watch`** CLI for easy translation workflow.
426
486
 
427
487
  #### **Example**
428
488
 
429
489
  ```tsx
430
490
  // module.ts
431
- // The "t" result in the array will be resolved asynchronously
491
+ // the "t" function from "getTranslation" is used as the "marker" of "watch" CLI
432
492
  import { getTranslation } from './i18nContext';
433
493
 
434
494
  const { t } = getTranslation('header');
435
495
 
436
496
  export const STATUS_CATEGORY = [
437
- t.promise('Total Energy Usage'),
438
- t.promise('Total Waste Usage'),
497
+ t('Total Energy Usage'), // Marked
498
+ t('Total Waste Usage'), // Marked
439
499
  ];
440
500
 
501
+ // Pre-requisite: install @sheet-i18n/cli and run `npx sheet-i18n watch`
502
+ // You can register your translation data with ease
503
+ Detected Translations:
504
+
505
+ πŸ“„ [Sheet Title]: header
506
+ - Total Energy Usage
507
+ - Total Waste Usage
508
+
509
+ # You can proceed using the shortcuts below:
510
+ - Press <Shift + R> to register the detected changes. // <-- you can register your translation data
511
+ - Press <Ctrl + C> to cancel the watch command.
512
+
441
513
  // App.tsx
442
- // So, t.promise ensure the current client-side locale data
443
- // is fully initialized before the translations are resolved
514
+ // After update of translation data, t.dynamic will resolve the translations
515
+ // If you want to translate the text with type safety, use "t" function which works like t.dynamic after register and import your translation data.
444
516
  import { STATUS_CATEGORY } from './module.ts';
445
517
 
446
518
  export default function App() {
519
+ const { t } = useTranslation('header');
520
+
447
521
  return (
448
522
  <div>
449
523
  {STATUS_CATEGORY.map((item, index) => {
450
- return <div key={index}>{item}</div>;
524
+ return <div key={index}>{t.dynamic(item)}</div>;
451
525
  })}
452
526
  </div>
453
527
  );
@@ -586,6 +660,110 @@ const customStorageManager = getLocaleStorageManager(
586
660
  - **Custom Storage**: Implement your own persistence strategy (e.g., server-side storage, encrypted storage)
587
661
  - **Memory Storage**: No persistence (for testing or temporary use cases)
588
662
 
663
+ ## πŸ”Œ Plugins
664
+
665
+ sheet-i18n provides an extensible plugin system that allows you to customize translation logic and easily implement conditional translations or complex translation rules.
666
+
667
+ ### 🎯 Custom Rules Plugin
668
+
669
+ **Custom Rules** is a plugin that enables dynamic selection of different translation keys based on conditions. This allows you to implement various scenarios such as pluralization, gender-based translations, age-based message changes, and more.
670
+
671
+ #### **Key Features**
672
+
673
+ - βœ… **Conditional Translation**: Automatically select different translation keys based on values
674
+ - βœ… **Interpolation Support**: Same `{slot}` replacement functionality as the `t` function
675
+ - βœ… **Extensible**: Combine multiple rules to implement complex logic
676
+
677
+ ##### **Key Points**
678
+
679
+ ```tsx
680
+ import { ruleFactory } from '@sheet-i18n/react';
681
+ import { i18nStore } from './i18nStore';
682
+
683
+ // Step 1: Create factory service
684
+ // ruleFactory takes your i18nStore and returns an object with createRule method
685
+ const { createRule } = ruleFactory(i18nStore);
686
+
687
+ // Step 2: Use createRule to define custom rules
688
+ // `createRule` can take a rule function and support the current localeSet data use registered in the localeSet of `i18nStore`
689
+ // In the body of the rule function, you can defined the translation logic as you want
690
+
691
+ // πŸ’‘ Note: Highly recommended to return the translation key from the localeSet
692
+ // When you use `t.rule()` from the component, the translation key will be resolved and the translated text will be returned
693
+ // If you return the normal string, that string will be used as the translation key
694
+ // and If no matched translation key is found, the string will be used rendered
695
+ const myRule = createRule<{ count: number }>((values, localeSet) => {
696
+ if (values.count > 10) {
697
+ return localeSet.sheetTitle['many_items'];
698
+ }
699
+ return localeSet.sheetTitle['few_items'];
700
+ });
701
+ ```
702
+
703
+ ##### **Complete Flow Example**
704
+
705
+ ```tsx
706
+ // Translation data example from your sheet
707
+ // en.json
708
+ {
709
+ ...
710
+ "userSheet": {
711
+ "adult_message": "You are an adult. Your age is {age}",
712
+ "youth_message": "You are a youth. Your age is {age}"
713
+ }
714
+ }
715
+
716
+ // 1. Initialize i18nStore
717
+ const i18nStore = new I18nStore({
718
+ /* ... */
719
+ });
720
+
721
+ // 2. Create factory service (binds createRule to i18nStore)
722
+ const { createRule } = ruleFactory(i18nStore);
723
+
724
+ // 3. Define custom rules using createRule
725
+ export const customRules = {
726
+ ageRule: createRule<{ age: number }>((values, localeSet) => {
727
+ if (values.age > 20) {
728
+ return localeSet.userSheet['adult_message'];
729
+ }
730
+ return localeSet.userSheet['youth_message'];
731
+ }),
732
+ };
733
+
734
+ // 4. Register rules in context
735
+ const { useTranslation } = createI18nContext(i18nStore, { rules: customRules });
736
+
737
+ // 5. Use in components
738
+ const { t } = useTranslation('userSheet');
739
+ const translatedText = t.rule('ageRule', { age: 25 }); // 'You are an adult. Your age is 25'
740
+ ```
741
+
742
+ πŸ’‘ **Core Concept**:
743
+
744
+ - `localeSet` contains the complete translation data for the current locale, accessible via `localeSet.landing['key_name']`.
745
+ - The returned translation key is processed the same way as the `t()` function to convert it to the final translated value.
746
+ - If the returned translation key contains `{slot}` placeholders, they will be automatically replaced using the `values` object passed to `t.rule()`.
747
+
748
+ πŸ’‘ **Important**: If you want to use auto-replaced interpolation, you should use the same sheetTitle in the `useTranslation` hook and the localeSet in the `t.rule` function
749
+
750
+ ```ts
751
+ // Example
752
+ // you return the translation key from 'userSheet' in the localeSet
753
+ const customRules = {
754
+ ageRule: createRule<{ age: number }>((values, localeSet) => {
755
+ if (values.age > 20) {
756
+ return localeSet.userSheet['adult_message'];
757
+ }
758
+ return localeSet.userSheet['youth_message'];
759
+ }),
760
+ };
761
+
762
+ // you should use the 't.rule' function from the same sheetTitle for the argument of `useTranslation`
763
+ const { t } = useTranslation('userSheet');
764
+ const translatedText = t.rule('ageRule', { age: 25 }); // 'You are an adult. Your age is 25'
765
+ ```
766
+
589
767
  ## License πŸ“œ
590
768
 
591
769
  This project is licensed under the ISC License. See the LICENSE file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sheet-i18n/react",
3
- "version": "1.5.3",
3
+ "version": "1.6.0-canary.0",
4
4
  "description": "i18n client logic based on react",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -32,8 +32,8 @@
32
32
  "@sheet-i18n/typescript-config": "1.8.1"
33
33
  },
34
34
  "dependencies": {
35
- "@sheet-i18n/react-core": "1.5.2",
36
- "@sheet-i18n/react-client": "1.5.3"
35
+ "@sheet-i18n/react-core": "1.6.0-canary.0",
36
+ "@sheet-i18n/react-client": "1.6.0-canary.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup",