https://github.com/legend80s/i18n-enhancer
Make internationalization 🌍 as type safe and DX joyful 🤗 as it's meant to be!
https://github.com/legend80s/i18n-enhancer
i18n i18next javascript react-i18next react-intl
Last synced: about 2 months ago
JSON representation
Make internationalization 🌍 as type safe and DX joyful 🤗 as it's meant to be!
- Host: GitHub
- URL: https://github.com/legend80s/i18n-enhancer
- Owner: legend80s
- License: mit
- Created: 2024-12-19T06:25:27.000Z (5 months ago)
- Default Branch: master
- Last Pushed: 2024-12-31T01:38:21.000Z (5 months ago)
- Last Synced: 2025-02-15T04:42:05.784Z (3 months ago)
- Topics: i18n, i18next, javascript, react-i18next, react-intl
- Language: TypeScript
- Homepage:
- Size: 81.1 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README-zh-CN.md
- License: LICENSE
Awesome Lists containing this project
README
🌍 i18n-enhancer
[英文](https://github.com/legend80s/i18n-enhancer/blob/master/README.md) | 中文
> 🛡️ 翻译即类型,让国际化开发更安全、更高效、代码更少!
>
> Make internationalization as **type safe 🛡️** and **DX joyful 🥳** as it's meant to be!## 特性 🌟
本工具是 `react-i18next` / `i18next` 的类型增强器,给 `useTranslate` 提供了 **精确的类型**,使得 **key** 和 **插值变量名** 能有智能提示,这些类型均来自你提供的翻译文本,你无需提供额外的类型。这就是“**翻译即类型**”。
- **翻译即类型**:无需额外类型声明,只需提供翻译文本,即可获得精确的类型。
- **不会增加运行时成本**:没有任何核心运行时修改。
- **不会增加额外包大小**:仅加了类型,因此不会额外增加包大小。## 安装 📦
```bash
npm install react-i18next i18next --savenpm install i18n-enhancer --save
```## 使用 📝
### `react-i18next` 用户
in Component:
```diff typescript
import { useTranslation } from 'react-i18next';
+ import { enhance } from 'i18n-enhancer/react-i18next';
+ type ITranslationsEn = typeof import('./en').default;+ const { useT } = enhance(useTranslation)
const Hello = () => {
- const { t } = useTranslation(...);
+ const { t } = useT(...);return
{t('hi')}
;
}
```out of Component:
```diff typescript
import i18n from '@/src/locales/i18n';
+ import { enhanceI18n } from 'i18n-enhancer/react-i18next';
+ type ITranslationsEn = typeof import('./en').default;+ const { t } = enhanceI18n(i18n)
function sayHello = () => {
- const { t } = i18n.t;return
{t('hi')}
;
}
```## Example
### 1. 初始化 `react-i18next`
[可选] 增加 `parseMissingKeyHandler` 减少冗余翻译:
```diff typescript
// src/locales/i18n.tsimport i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
+ import { getLast } from 'i18n-enhancer/utils';i18n.use(initReactI18next).init({
...+ // 当翻译键不存在时,返回键最后一部分作为默认值,意味着如果你用中文做键那么你无需再添加中文翻译
+ parseMissingKeyHandler: (key, defaultValue) => {
+ return defaultValue ?? getLast(key);
+ },
});export default i18n;
``````typescript
// zh
{
// 无需提供中文翻译,你可以尝试注释下面一行,因为设置了 `parseMissingKeyHandler` 会用点最后的部分当做兜底翻译。是不是很棒!
'shopping.去支付': '去支付','shopping.orderSummaryText': '请确认您的订单信息',
'shopping.总共': '总共 {{ total }} 元',
} as const
```### 2. 创建 `react-i18next` **enhancer**
```typescript
import React from 'react';
import { useTranslation } from 'react-i18next';
import { enhance } from 'i18n-enhancer/react-i18next';// 导入你不常开发的的语言翻译包。比如你的应用是面向中文用户,则导入英语即可。
// 好处:类型推导会帮助你检测出没有英文翻译的 Key,其次如果你采用中文当做key,则无需提供中文翻译,
// 因为我们已经设置好了 `parseMissingKeyHandler` 当 key 不存在将用其最后一部分当做翻译兜底。
type ITranslationsEn = typeof import('./en').default;const { useT } = enhance(useTranslation);
```### 3. 使用 `useT` 替代 `useTranslation`
> `useT` 是 `useTranslation` 的类型增强版,它使用你的**翻译键**作为入参类型,**翻译值**作为返回类型。
```typescript
const Shopping: React.FC = () => {
const { t } = useT();return (
{t('shopping.去支付')}
{t('shopping.orderSummaryText')}
{t('shopping.总共', { total: 100 })}
);
};
```**🧙♂️ 魔法开启**,当我们使用 `i18n-enhancer` 的 `useT`,假如我们的翻译如下:
```typescript
// en
{
'shopping.去支付': 'Checkout',
'shopping.orderSummaryText': 'Please review your order details below.',
'shopping.总共': 'Total {{ total }} RMB',
} as const
```当你输入 `t('shopping.` 你会看到所有翻译的 key 都会提示出来。
并且如果你的光标悬浮到 `t` 函数上,你会看到如下提示:
```typescript
t('shopping.去支付'): 'Checkout';
t('shopping.orderSummaryText'): 'Please review your order details below.';
```**是的,你会看到居然将翻译文本都反显出来了** 而不仅仅是普普通通的 `string`!(当然前提是翻译必须增加 `as const`)。
插值变量能否也提示?再来点小小的震撼,当输入以下代码,你会发现 `total` 这个变量名也会自动提示:
```typescript
t('shopping.总共', { total: number }): 'Total {{ total }} RMB';
```**It is truly i18n type safe 🛡️.**
最后当然 `` 会按照预期渲染:
for English:
```jsx
Checkout
Please review your order details below.
Total 100 RMB```
在中文环境:
```jsx
去支付
请确认您的订单信息
总共 100 元```
## 更多 API
`enhance` 返回的 `i18n` 对原始 `i18n` 做了功能增强:
### `enhance().resolvedLanguage`
获取当前语言。
### `enhance().isEnglish: boolean`
判断当前语言是否是英文。
其他 API 仍然来自原始的 `i18n`,比如 `i18n.changeLanguage` 等仍然可以使用。
## FAQ
### 如何在非 React 组件中使用
比如 Node.js 中或普通函数中:
```typescript
import initializedI18n from '@src/locales/i18n';
import { enhanceI18n } from 'i18n-enhancer/react-i18next';type ITranslationsEn = typeof import('./en').default;
const i18n = enhanceI18n(initializedI18n);
// 切换语言
i18n.changeLanguage('en');// 判断当前语言是否是英文
i18n.isEnglish; // true// 翻译
i18n.t('shopping.checkout'); // Checkout
```## 限制
### 1. 不支持嵌套翻译
比如:
```typescript
{
shopping: {
checkout: 'Checkout',
}
}
```也不打算支持,因为我们认为平铺的 `key` 更容易查找,因为定义和使用一样,可读性也更好。
### 2. `t` 的第二个参数无法通过翻译内是否有插值来决定是否必选,一律可选
> TS 本身不支持
比如:
```typescript
{
'shopping.checkout': 'Checkout {{total}}',
}
``````typescript
// 不会提示类型错误
t('shopping.checkout')// 只有传入不存在的参数,或参数不够才会提示类型错误
t('shopping.checkout', { notExist: 100 })
```