react-scoped-i18n 0.0.4 → 0.0.5
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 +85 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,112 +1,114 @@
|
|
|
1
1
|
# react-scoped-i18n 🌐
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A React i18n library where **translations live next to the components that render them** - no keys, no JSON files, fully type-safe at compile time.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Works with **React** and **React Native** (_vanilla & Expo_).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## The problem with key-based i18n
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Every other i18n library makes you name things. You write a key in your component, then jump to a JSON file to write the actual string, then come back. Keys get stale. Typos go unnoticed until runtime. Your translation file becomes a graveyard of strings you're not sure are still used.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
`react-scoped-i18n` flips this: **translations are just code, written inline, where you need them.**
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
```tsx
|
|
16
|
+
const { t } = useI18n();
|
|
17
|
+
|
|
18
|
+
const name = `Oto`;
|
|
19
|
+
|
|
20
|
+
return <Heading>
|
|
21
|
+
{t({
|
|
22
|
+
en: `Welcome back, ${name}!`,
|
|
23
|
+
es: `¡Bienvenido de nuevo, ${name}!`,
|
|
24
|
+
})}
|
|
25
|
+
</Heading>;
|
|
26
|
+
```
|
|
16
27
|
|
|
28
|
+
No keys. No files. `ctrl+f` on any rendered string takes you straight to the component.
|
|
17
29
|
|
|
18
30
|
---
|
|
19
31
|
|
|
20
|
-
##
|
|
32
|
+
## Type safety that actually catches bugs
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
- Very minimal setup with out-of-the-box number & date formatting
|
|
24
|
-
- Fully type-safe:
|
|
25
|
-
- - missing translations or unsupported languages are compile-time errors
|
|
26
|
-
- - return types of `t()` are inferred from translation values
|
|
27
|
-
- Utilize the widely supported [Internationalization API (Intl)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) for number, currency, date and time formatting
|
|
28
|
-
- Usage is entirely in the runtime; no build-time transforms, no new syntax is required for string interpolation or dynamic translations generated at runtime, everything is plain JS/TS
|
|
34
|
+
Forget to add a translation for a language you support? **TypeScript error.** Reference an unsupported language? **TypeScript error.** At compile time, not in production.
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
```tsx
|
|
37
|
+
return <Heading>
|
|
38
|
+
{t({
|
|
39
|
+
en: `Welcome back, ${name}!`,
|
|
40
|
+
// TS Error: Property 'es' is missing - your app supports Spanish
|
|
41
|
+
})}
|
|
42
|
+
</Heading>;
|
|
43
|
+
```
|
|
36
44
|
|
|
37
45
|
---
|
|
38
46
|
|
|
39
|
-
##
|
|
47
|
+
## Getting started
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
- **[Installation & Usage](/docs/usage.md)** - 30 second setup
|
|
50
|
+
- **[API Reference](/docs/api.md)** - full reference
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
import { useI18n } from "@/i18n";
|
|
45
|
-
import { Heading, Button } from "@/components";
|
|
52
|
+
---
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
## Key features
|
|
55
|
+
|
|
56
|
+
- **Colocated translations** - live in the component, not a separate file
|
|
57
|
+
- **Compile-time safety** - missing or unsupported languages are TypeScript errors
|
|
58
|
+
- **No build-time transforms** - no Babel plugins, no magic, lives in React runtime (React Context)
|
|
59
|
+
- **No custom syntax** - plain JS template literals, no `{count, plural, ...}` to memorise
|
|
60
|
+
- **Out-of-the-box formatting** - numbers, currencies, dates and times via the native [Intl API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
|
|
61
|
+
- **Hot-reload friendly** - language switches are reflected immediately
|
|
62
|
+
- **Minimal setup** - takes 30 seconds
|
|
63
|
+
|
|
64
|
+
---
|
|
49
65
|
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
### Interpolation
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
export const WelcomeMessage = () => {
|
|
72
|
+
const { t } = useI18n();
|
|
50
73
|
const name = `John`;
|
|
51
74
|
|
|
52
75
|
return (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
})}
|
|
61
|
-
</Heading>
|
|
62
|
-
<Button>{
|
|
63
|
-
// "commons" object is used for commonly used, shared translations
|
|
64
|
-
t(commons.continue)
|
|
65
|
-
}</Button>
|
|
66
|
-
</>
|
|
76
|
+
<Heading>
|
|
77
|
+
{t({
|
|
78
|
+
en: `Welcome to the website, ${name}!`,
|
|
79
|
+
es: `¡Bienvenido al sitio web, ${name}!`,
|
|
80
|
+
sl: `Dobrodošli na spletno stran, ${name}!`,
|
|
81
|
+
})}
|
|
82
|
+
</Heading>
|
|
67
83
|
);
|
|
68
84
|
};
|
|
69
85
|
```
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
<summary>
|
|
73
|
-
Number Formatting Basic Example:
|
|
74
|
-
</summary>
|
|
87
|
+
### Number & currency formatting
|
|
75
88
|
|
|
76
89
|
```tsx
|
|
77
|
-
import { useI18n } from "@/i18n";
|
|
78
|
-
import { Text } from "@/components";
|
|
79
|
-
|
|
80
90
|
export const PriceTag = () => {
|
|
81
91
|
const { t, format } = useI18n();
|
|
82
92
|
|
|
83
93
|
const price = 19.99;
|
|
84
94
|
|
|
85
|
-
const currency = `USD`;
|
|
86
|
-
|
|
87
95
|
return (
|
|
88
96
|
<Text>
|
|
89
97
|
{t({
|
|
90
|
-
en: `The price is ${format.currency(price,
|
|
91
|
-
es: `El precio es ${format.currency(price,
|
|
92
|
-
sl: `Cena je ${format.currency(price,
|
|
98
|
+
en: `The price is ${format.currency(price, "USD")}.`,
|
|
99
|
+
es: `El precio es ${format.currency(price, "USD")}.`,
|
|
100
|
+
sl: `Cena je ${format.currency(price, "USD")}.`,
|
|
93
101
|
})}
|
|
94
102
|
</Text>
|
|
95
103
|
);
|
|
96
104
|
};
|
|
97
105
|
```
|
|
98
|
-
</details>
|
|
99
106
|
|
|
107
|
+
### Pluralization
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
<summary>
|
|
103
|
-
Pluralization Basic Example:
|
|
104
|
-
</summary>
|
|
109
|
+
Full ICU category support (`one`, `two`, `many`, etc.), including a `negative` shorthand and the ability to target specific numbers.
|
|
105
110
|
|
|
106
111
|
```tsx
|
|
107
|
-
import { useI18n } from "@/i18n";
|
|
108
|
-
import { Text } from "@/components";
|
|
109
|
-
|
|
110
112
|
export const Apples = () => {
|
|
111
113
|
const { tPlural } = useI18n();
|
|
112
114
|
|
|
@@ -119,7 +121,7 @@ export const Apples = () => {
|
|
|
119
121
|
negative: `You are in apple debt...`,
|
|
120
122
|
one: `You have one apple.`,
|
|
121
123
|
many: `You have ${count} apples.`,
|
|
122
|
-
42: `You have the perfect number of apples!`,
|
|
124
|
+
42: `You have the perfect number of apples!`,
|
|
123
125
|
},
|
|
124
126
|
es: {
|
|
125
127
|
one: `Tienes una manzana.`,
|
|
@@ -127,21 +129,35 @@ export const Apples = () => {
|
|
|
127
129
|
},
|
|
128
130
|
sl: {
|
|
129
131
|
one: `Imaš eno jabolko.`,
|
|
130
|
-
two: `Imaš dve jabolki.`, //
|
|
132
|
+
two: `Imaš dve jabolki.`, // handling the Slovenian dual form
|
|
131
133
|
many: `Imaš ${count} jabolk.`,
|
|
132
|
-
}
|
|
134
|
+
},
|
|
133
135
|
})}
|
|
134
136
|
</Text>
|
|
135
|
-
)
|
|
137
|
+
);
|
|
136
138
|
};
|
|
137
139
|
```
|
|
138
|
-
</details>
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
### Shared / common translations
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const { t, commons } = useI18n();
|
|
145
|
+
|
|
146
|
+
return <Button>{t(commons.continue)}</Button>;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Who this is for
|
|
152
|
+
|
|
153
|
+
`react-scoped-i18n` is built for **small teams and indie developers** who:
|
|
141
154
|
|
|
142
|
-
|
|
155
|
+
- Write and maintain their own translations (or work directly with translators in code)
|
|
156
|
+
- Support a small-to-medium number of languages
|
|
157
|
+
- Want TypeScript to enforce correctness, not just assist with autocomplete
|
|
158
|
+
- Prefer reading code over managing JSON files
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
If your workflow involves external translation platforms like Crowdin or Lokalise (or third party translators touching files directly), this approach is likely not for you.
|
|
145
161
|
|
|
146
162
|
---
|
|
147
163
|
|