sheet-i18n 1.9.7 β 1.10.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.
- package/README.md +220 -29
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,6 +31,10 @@ npm install @sheet-i18n/cli -D
|
|
|
31
31
|
|
|
32
32
|
### `@sheet-i18n/react`
|
|
33
33
|
|
|
34
|
+
# @sheet-i18n/react β¨
|
|
35
|
+
|
|
36
|
+
[](https://npmjs.com/package/@sheet-i18n/react)
|
|
37
|
+
|
|
34
38
|
The **client-side i18n library** subpackage of sheet-i18n.
|
|
35
39
|
|
|
36
40
|
This package provides tools to handle translations in React applications using context and hooks. It simplifies internationalization workflows by offering functions and components to manage, access, and use locale-specific translation data.
|
|
@@ -42,11 +46,12 @@ This package provides tools to handle translations in React applications using c
|
|
|
42
46
|
- **`IntlProvider`**: _React Translation Provider_ for managing current locale.
|
|
43
47
|
- **`useTranslation`**: _Client Side Translation Hook_ for easy access to translation messages on the client side.
|
|
44
48
|
- **`getTranslation`**: _Static Translation Function_ for easy access to translation messages on Static module files.
|
|
49
|
+
- **`ruleFactory`**: _Rule Factory Function_ for creating custom translation rules with conditional logic.
|
|
45
50
|
|
|
46
51
|
## π Getting Started(Manually)
|
|
47
52
|
|
|
48
53
|
> β‘ **Strongly recommended to use the init CLI for setup**
|
|
49
|
-
> π [Please follow the Init CLI
|
|
54
|
+
> π [Please follow the Init CLI](https://www.npmjs.com/package/@sheet-i18n/cli)
|
|
50
55
|
>
|
|
51
56
|
> If you don't want to use the CLI, you can follow the `Manual Setup` below.
|
|
52
57
|
|
|
@@ -77,8 +82,8 @@ Prepare locale JSON files:
|
|
|
77
82
|
this store will be used as a core translations module.
|
|
78
83
|
|
|
79
84
|
```tsx
|
|
80
|
-
import ko from './ko.json';
|
|
81
85
|
import en from './en.json';
|
|
86
|
+
import ko from './ko.json';
|
|
82
87
|
|
|
83
88
|
import { I18nStore } from '@sheet-i18n/react';
|
|
84
89
|
|
|
@@ -166,6 +171,7 @@ The `I18nStore` manages translation states, ensuring consistency across locales.
|
|
|
166
171
|
| localeSet | Record<string, object> | | _(Static Loading Option)_ Preload all translation data for each locale in memory. Keys must match `supportedLocales`. |
|
|
167
172
|
| dynamicLoaders | Record<string, () => Promise<any>> | | _(Recommended for large locale sets)_ Dynamically load translation data on demand, reducing initial bundle size. |
|
|
168
173
|
| typeSafe | boolean | | Enable strict key checking and autocompletion (default: `false`). |
|
|
174
|
+
| |
|
|
169
175
|
|
|
170
176
|
> π‘ **typeSafe?** <br/>
|
|
171
177
|
> I18nStore doesn't enforce adherence to your locale JSON definitions by default. This means that you can add translation data even if it isnβt pre-defined in your locale JSON files. However, if you prefer to enforce strict type-safety, you can manually enable the typeSafe option which allows you to notice the auto-completed list in translation data.
|
|
@@ -174,7 +180,7 @@ The `I18nStore` manages translation states, ensuring consistency across locales.
|
|
|
174
180
|
supportedLocales in i18nStore`
|
|
175
181
|
>
|
|
176
182
|
> As a result, type auto-completion relies on having the complete localeSet defined at initialization. This means that using dynamicLoaders to fetch locales conditionally at runtime can be limiting when strict type-safety is enabled.
|
|
177
|
-
|
|
183
|
+
|
|
178
184
|
> ```tsx
|
|
179
185
|
> // typeSafe: true
|
|
180
186
|
> const YourComponent = () => {
|
|
@@ -234,9 +240,6 @@ For projects with many locale datasets, it's often preferable to load translatio
|
|
|
234
240
|
#### Example:
|
|
235
241
|
|
|
236
242
|
```tsx
|
|
237
|
-
import ko from './locales/ko.json';
|
|
238
|
-
import en from './locales/en.json';
|
|
239
|
-
|
|
240
243
|
export const i18nStore = new I18nStore({
|
|
241
244
|
supportedLocales: ['ko', 'en'],
|
|
242
245
|
defaultLocale: 'ko',
|
|
@@ -263,6 +266,8 @@ Generates React context, including the `IntlProvider` and `useTranslation`.
|
|
|
263
266
|
#### Parameters:
|
|
264
267
|
|
|
265
268
|
- **`i18nStore`**: Instance of `I18nStore`.
|
|
269
|
+
- **`plugins`** (optional): Plugin configuration object.
|
|
270
|
+
- **`rules`** (optional): Custom rules object for conditional translations. See [Plugins](#-plugins) section for details.
|
|
266
271
|
|
|
267
272
|
> β οΈ Caveats:
|
|
268
273
|
>
|
|
@@ -334,31 +339,93 @@ A function to access translations in the environment where cannot use react cont
|
|
|
334
339
|
|
|
335
340
|
### `t Function`
|
|
336
341
|
|
|
337
|
-
The t function is used to retrieve
|
|
342
|
+
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.
|
|
338
343
|
|
|
339
|
-
####
|
|
344
|
+
#### **Overview: `t()`, `t.rule()`, and `t.dynamic()`**
|
|
345
|
+
|
|
346
|
+
| Method | Use Case | Key Type | When to Use |
|
|
347
|
+
| ----------------------------------- | ------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------- |
|
|
348
|
+
| **`t(key, values?)`** | Standard translation with explicit keys | Static (known at build time) | When you know the translation key beforehand |
|
|
349
|
+
| **`t.rule(ruleKey, values?)`** | Conditional translation using custom rules | Determined by rule function | When you need conditional logic (pluralization, gender-based, etc.) |
|
|
350
|
+
| **`t.dynamic(id, values?, opts?)`** | Translation with runtime keys | Dynamic (from API, user input, etc.) | When the translation key comes from runtime data |
|
|
351
|
+
|
|
352
|
+
#### **`t()` - Standard Translation**
|
|
353
|
+
|
|
354
|
+
Retrieve translations for explicit keys that are known at build time.
|
|
340
355
|
|
|
341
|
-
|
|
356
|
+
##### Parameters:
|
|
357
|
+
|
|
358
|
+
- **`key`** (string): The translation key to retrieve the localized string.
|
|
359
|
+
- **`values`** (object, optional): An object containing key-value pairs to interpolate into the translation string.
|
|
360
|
+
|
|
361
|
+
##### Examples:
|
|
342
362
|
|
|
343
363
|
```tsx
|
|
344
|
-
const
|
|
364
|
+
const { t } = useTranslation('header');
|
|
365
|
+
|
|
366
|
+
// Simple translation
|
|
367
|
+
const loginText = t('login'); // "Login" or "λ‘κ·ΈμΈ"
|
|
368
|
+
|
|
369
|
+
// Translation with interpolation
|
|
370
|
+
const welcomeMessage = t('{username} shown', { username: 'John Doe' });
|
|
371
|
+
// Result: "John Doe shown"
|
|
372
|
+
|
|
373
|
+
// Interpolation with React components
|
|
374
|
+
const message = t('{username} shown', { username: <Username /> });
|
|
345
375
|
```
|
|
346
376
|
|
|
347
|
-
|
|
377
|
+
π‘ **Note**: The values object can contain any type of data, including React components.
|
|
378
|
+
|
|
379
|
+
#### **`t.rule()` - Conditional Translation with Custom Rules**
|
|
380
|
+
|
|
381
|
+
Use custom rules for conditional translations based on dynamic values. This method is available when custom rules are registered via the `createI18nContext` plugins option.
|
|
382
|
+
|
|
383
|
+
##### Parameters:
|
|
384
|
+
|
|
385
|
+
- **`ruleKey`** (string): The key of the registered custom rule.
|
|
386
|
+
- **`values`** (object, optional): An object containing values to be passed to the rule function and used for interpolation.
|
|
387
|
+
|
|
388
|
+
##### Examples:
|
|
348
389
|
|
|
349
390
|
```tsx
|
|
350
|
-
//
|
|
351
|
-
const
|
|
391
|
+
// Assuming custom rules are registered
|
|
392
|
+
const { t } = useTranslation('landing');
|
|
393
|
+
|
|
394
|
+
// Call rule with values
|
|
395
|
+
const message = t.rule('plural', { age: 21 });
|
|
396
|
+
// The rule function determines which translation key to use based on age
|
|
352
397
|
```
|
|
353
398
|
|
|
354
|
-
π‘ Note
|
|
399
|
+
π‘ **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()`.
|
|
400
|
+
|
|
401
|
+
For more details, see the [Plugins](#-plugins) section.
|
|
402
|
+
|
|
403
|
+
#### **`t.dynamic()` - Runtime Key Translation**
|
|
404
|
+
|
|
405
|
+
Resolve translation keys that are not known at build time (e.g., values coming from API responses, user input, or other runtime sources).
|
|
406
|
+
|
|
407
|
+
##### Parameters:
|
|
408
|
+
|
|
409
|
+
- **`id`** (string): A runtime string to match against the current locale's translation keys.
|
|
410
|
+
- **`values`** (object, optional): Interpolation values, identical to `t()`.
|
|
411
|
+
- **`opts`** (object, optional): Formatter options.
|
|
412
|
+
|
|
413
|
+
##### Examples:
|
|
355
414
|
|
|
356
415
|
```tsx
|
|
357
|
-
|
|
358
|
-
const
|
|
416
|
+
const { t } = useTranslation('header');
|
|
417
|
+
const { data } = useQuery(...queryOptions);
|
|
418
|
+
|
|
419
|
+
// Runtime key from API response
|
|
420
|
+
const deviceName = data?.device?.name; // e.g., "smartphone"
|
|
421
|
+
|
|
422
|
+
// Will translate the runtime key if it exists in the current locale
|
|
423
|
+
const label = t.dynamic(deviceName);
|
|
424
|
+
// If "smartphone" exists in locale JSON, returns translated value
|
|
425
|
+
// Otherwise, returns "smartphone" as fallback
|
|
359
426
|
```
|
|
360
427
|
|
|
361
|
-
|
|
428
|
+
π‘ **Tip**: Ensure your locale JSON contains possible runtime keys; otherwise `t.dynamic` will fall back to the provided id string.
|
|
362
429
|
|
|
363
430
|
## π Best Practices of Translation utilities
|
|
364
431
|
|
|
@@ -414,11 +481,6 @@ return <div>{t.dynamic(deviceName)}</div>;
|
|
|
414
481
|
|
|
415
482
|
The `getTranslation` function allows you to use translations outside of React components, such as in static configuration files, constants, or utility modules.
|
|
416
483
|
|
|
417
|
-
It provide two possible supports
|
|
418
|
-
|
|
419
|
-
1. **`t` (Synchronous Translation)** β Use when translation values are available **at runtime**.
|
|
420
|
-
2. **`t.promise` (Asynchronous Translation)** β Use when the moduleβs evaluation order is uncertain.
|
|
421
|
-
|
|
422
484
|
#### **β
Usage Scenarios**
|
|
423
485
|
|
|
424
486
|
#### **[Scenario 1]: Context where the call expression of `t` function is evaluated at runtime**
|
|
@@ -453,32 +515,46 @@ export default function App() {
|
|
|
453
515
|
|
|
454
516
|
- If the module is **imported dynamically in a client-side component**, the evaluation order of `getTranslation` and `t` call expression may not be guaranteed.
|
|
455
517
|
- Since JavaScript **evaluates modules at import time**, it may attempt to access translation values before `IntlProvider` has fully initialized the locale.
|
|
456
|
-
- In this case, use **`
|
|
518
|
+
- In this case, use **`getTranslation`** and **`watch`** CLI for easy translation workflow.
|
|
457
519
|
|
|
458
520
|
#### **Example**
|
|
459
521
|
|
|
460
522
|
```tsx
|
|
461
523
|
// module.ts
|
|
462
|
-
//
|
|
524
|
+
// the "t" function from "getTranslation" is used as the "marker" of "watch" CLI
|
|
463
525
|
import { getTranslation } from './i18nContext';
|
|
464
526
|
|
|
465
527
|
const { t } = getTranslation('header');
|
|
466
528
|
|
|
467
529
|
export const STATUS_CATEGORY = [
|
|
468
|
-
t
|
|
469
|
-
t
|
|
530
|
+
t('Total Energy Usage'), // Marked
|
|
531
|
+
t('Total Waste Usage'), // Marked
|
|
470
532
|
];
|
|
471
533
|
|
|
534
|
+
// Pre-requisite: install @sheet-i18n/cli and run `npx sheet-i18n watch`
|
|
535
|
+
// You can register your translation data with ease
|
|
536
|
+
Detected Translations:
|
|
537
|
+
|
|
538
|
+
π [Sheet Title]: header
|
|
539
|
+
- Total Energy Usage
|
|
540
|
+
- Total Waste Usage
|
|
541
|
+
|
|
542
|
+
# You can proceed using the shortcuts below:
|
|
543
|
+
- Press <Shift + R> to register the detected changes. // <-- you can register your translation data
|
|
544
|
+
- Press <Ctrl + C> to cancel the watch command.
|
|
545
|
+
|
|
472
546
|
// App.tsx
|
|
473
|
-
//
|
|
474
|
-
//
|
|
547
|
+
// After update of translation data, t.dynamic will resolve the translations
|
|
548
|
+
// 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.
|
|
475
549
|
import { STATUS_CATEGORY } from './module.ts';
|
|
476
550
|
|
|
477
551
|
export default function App() {
|
|
552
|
+
const { t } = useTranslation('header');
|
|
553
|
+
|
|
478
554
|
return (
|
|
479
555
|
<div>
|
|
480
556
|
{STATUS_CATEGORY.map((item, index) => {
|
|
481
|
-
return <div key={index}>{item}</div>;
|
|
557
|
+
return <div key={index}>{t.dynamic(item)}</div>;
|
|
482
558
|
})}
|
|
483
559
|
</div>
|
|
484
560
|
);
|
|
@@ -610,6 +686,117 @@ const customStorageManager = getLocaleStorageManager(
|
|
|
610
686
|
);
|
|
611
687
|
```
|
|
612
688
|
|
|
689
|
+
#### **Storage Options**
|
|
690
|
+
|
|
691
|
+
- **LocalStorage (Default)**: Persists across browser sessions and device restarts
|
|
692
|
+
- **SessionStorage**: Persists only for the current browser session
|
|
693
|
+
- **Custom Storage**: Implement your own persistence strategy (e.g., server-side storage, encrypted storage)
|
|
694
|
+
- **Memory Storage**: No persistence (for testing or temporary use cases)
|
|
695
|
+
|
|
696
|
+
## π Plugins
|
|
697
|
+
|
|
698
|
+
sheet-i18n provides an extensible plugin system that allows you to customize translation logic and easily implement conditional translations or complex translation rules.
|
|
699
|
+
|
|
700
|
+
### π― Custom Rules Plugin
|
|
701
|
+
|
|
702
|
+
**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.
|
|
703
|
+
|
|
704
|
+
#### **Key Features**
|
|
705
|
+
|
|
706
|
+
- β
**Conditional Translation**: Automatically select different translation keys based on values
|
|
707
|
+
- β
**Interpolation Support**: Same `{slot}` replacement functionality as the `t` function
|
|
708
|
+
- β
**Extensible**: Combine multiple rules to implement complex logic
|
|
709
|
+
|
|
710
|
+
##### **Key Points**
|
|
711
|
+
|
|
712
|
+
```tsx
|
|
713
|
+
import { ruleFactory } from '@sheet-i18n/react';
|
|
714
|
+
import { i18nStore } from './i18nStore';
|
|
715
|
+
|
|
716
|
+
// Step 1: Create factory service
|
|
717
|
+
// ruleFactory takes your i18nStore and returns an object with createRule method
|
|
718
|
+
const { createRule } = ruleFactory(i18nStore);
|
|
719
|
+
|
|
720
|
+
// Step 2: Use createRule to define custom rules
|
|
721
|
+
// `createRule` can take a rule function and support the current localeSet data use registered in the localeSet of `i18nStore`
|
|
722
|
+
// In the body of the rule function, you can defined the translation logic as you want
|
|
723
|
+
|
|
724
|
+
// π‘ Note: Highly recommended to return the translation key from the localeSet
|
|
725
|
+
// When you use `t.rule()` from the component, the translation key will be resolved and the translated text will be returned
|
|
726
|
+
// If you return the normal string, that string will be used as the translation key
|
|
727
|
+
// and If no matched translation key is found, the string will be used rendered
|
|
728
|
+
const myRule = createRule<{ count: number }>((values, localeSet) => {
|
|
729
|
+
if (values.count > 10) {
|
|
730
|
+
return localeSet.sheetTitle['many_items'];
|
|
731
|
+
}
|
|
732
|
+
return localeSet.sheetTitle['few_items'];
|
|
733
|
+
});
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
##### **Complete Flow Example**
|
|
737
|
+
|
|
738
|
+
```tsx
|
|
739
|
+
// Translation data example from your sheet
|
|
740
|
+
// en.json
|
|
741
|
+
{
|
|
742
|
+
...
|
|
743
|
+
"userSheet": {
|
|
744
|
+
"adult_message": "You are an adult. Your age is {age}",
|
|
745
|
+
"youth_message": "You are a youth. Your age is {age}"
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// 1. Initialize i18nStore
|
|
750
|
+
const i18nStore = new I18nStore({
|
|
751
|
+
/* ... */
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// 2. Create factory service (binds createRule to i18nStore)
|
|
755
|
+
const { createRule } = ruleFactory(i18nStore);
|
|
756
|
+
|
|
757
|
+
// 3. Define custom rules using createRule
|
|
758
|
+
export const customRules = {
|
|
759
|
+
ageRule: createRule<{ age: number }>((values, localeSet) => {
|
|
760
|
+
if (values.age > 20) {
|
|
761
|
+
return localeSet.userSheet['adult_message'];
|
|
762
|
+
}
|
|
763
|
+
return localeSet.userSheet['youth_message'];
|
|
764
|
+
}),
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
// 4. Register rules in context
|
|
768
|
+
const { useTranslation } = createI18nContext(i18nStore, { rules: customRules });
|
|
769
|
+
|
|
770
|
+
// 5. Use in components
|
|
771
|
+
const { t } = useTranslation('userSheet');
|
|
772
|
+
const translatedText = t.rule('ageRule', { age: 25 }); // 'You are an adult. Your age is 25'
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
π‘ **Core Concept**:
|
|
776
|
+
|
|
777
|
+
- `localeSet` contains the complete translation data for the current locale, accessible via `localeSet.landing['key_name']`.
|
|
778
|
+
- The returned translation key is processed the same way as the `t()` function to convert it to the final translated value.
|
|
779
|
+
- If the returned translation key contains `{slot}` placeholders, they will be automatically replaced using the `values` object passed to `t.rule()`.
|
|
780
|
+
|
|
781
|
+
π‘ **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
|
|
782
|
+
|
|
783
|
+
```ts
|
|
784
|
+
// Example
|
|
785
|
+
// you return the translation key from 'userSheet' in the localeSet
|
|
786
|
+
const customRules = {
|
|
787
|
+
ageRule: createRule<{ age: number }>((values, localeSet) => {
|
|
788
|
+
if (values.age > 20) {
|
|
789
|
+
return localeSet.userSheet['adult_message'];
|
|
790
|
+
}
|
|
791
|
+
return localeSet.userSheet['youth_message'];
|
|
792
|
+
}),
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// you should use the 't.rule' function from the same sheetTitle for the argument of `useTranslation`
|
|
796
|
+
const { t } = useTranslation('userSheet');
|
|
797
|
+
const translatedText = t.rule('ageRule', { age: 25 }); // 'You are an adult. Your age is 25'
|
|
798
|
+
```
|
|
799
|
+
|
|
613
800
|
</details>
|
|
614
801
|
|
|
615
802
|
---
|
|
@@ -620,6 +807,8 @@ const customStorageManager = getLocaleStorageManager(
|
|
|
620
807
|
|
|
621
808
|
### `@sheet-i18n/cli`
|
|
622
809
|
|
|
810
|
+
[](https://npmjs.com/package/@sheet-i18n/cli)
|
|
811
|
+
|
|
623
812
|
A CLI tool for efficient translation management using Google Sheets, with a focus on developer experience (DX).
|
|
624
813
|
|
|
625
814
|
## Features
|
|
@@ -991,6 +1180,8 @@ The configuration of export command is based on **`sheet.config.ts`** on your ro
|
|
|
991
1180
|
|
|
992
1181
|
### `sheet-i18n/importer`
|
|
993
1182
|
|
|
1183
|
+
[](https://npmjs.com/package/@sheet-i18n/importer)
|
|
1184
|
+
|
|
994
1185
|
The **server-side importer** subpackage allows you to interact with Google Sheets and export translations directly into your project. This is primarily used in server-side environments, such as Next.js API routes or other backend frameworks, where you want to fetch and store translations from a Google Spreadsheet to be served to clients or used within your server application.
|
|
995
1186
|
|
|
996
1187
|
```jsx
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sheet-i18n",
|
|
3
3
|
"description": "All-in-one i18n toolchain: seamlessly integrate Google Sheets, CLI, and react-i18n for easy translation workflows.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.10.0-canary.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/chltjdrhd777/sheet-i18n"
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"license": "ISC",
|
|
51
51
|
"homepage": "https://github.com/chltjdrhd777/sheet-i18n",
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@sheet-i18n/
|
|
53
|
+
"@sheet-i18n/react": "1.6.0-canary.0",
|
|
54
54
|
"@sheet-i18n/typescript": "0.2.2",
|
|
55
|
-
"@sheet-i18n/
|
|
55
|
+
"@sheet-i18n/importer": "1.9.5"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"tsup": "^6.0.0",
|