finovision-i18n 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 +85 -0
- package/dist/i18n.cjs +1 -0
- package/dist/i18n.d.ts +62 -0
- package/dist/i18n.esm.js +1 -0
- package/dist/init.d.ts +33 -0
- package/package.json +59 -0
- package/src/core.ts +114 -0
- package/src/i18n.ts +50 -0
- package/src/index.ts +32 -0
- package/src/init.ts +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# finovision-i18n
|
|
2
|
+
|
|
3
|
+
i18next 运行时(精简版,参照 Coze `@coze-arch/i18n`)。
|
|
4
|
+
|
|
5
|
+
**文案与类型**在兄弟包 [`finovision-i18n-resource`](../fv-i18n-resource)(等同 `studio-i18n-resource`),本包只负责 init + 类型安全的 `I18n.t()`。
|
|
6
|
+
|
|
7
|
+
## 目录
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
多语言测试/
|
|
11
|
+
├── fv-i18n-resource/ # 文案 JSON + locale-data.d.ts + defaultConfig
|
|
12
|
+
└── fv-i18n/ # 本包 finovision-i18n
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 安装与构建
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 先构建资源包
|
|
19
|
+
cd ../fv-i18n-resource
|
|
20
|
+
npm install
|
|
21
|
+
npm run build
|
|
22
|
+
|
|
23
|
+
# 再构建运行时(Rollup → dist/i18n.esm.js、dist/init.esm.js)
|
|
24
|
+
cd ../fv-i18n
|
|
25
|
+
npm install
|
|
26
|
+
npm run build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
开发调试可执行 `npm run build:dev`(不压缩)。
|
|
30
|
+
|
|
31
|
+
产物(与 `finovision-i18n-resource` 对齐):
|
|
32
|
+
|
|
33
|
+
| 文件 | 说明 |
|
|
34
|
+
|------|------|
|
|
35
|
+
| `dist/i18n.esm.js` | ESM 主入口 |
|
|
36
|
+
| `dist/i18n.cjs` | CJS 主入口 |
|
|
37
|
+
| `dist/i18n.d.ts` | 主入口类型 |
|
|
38
|
+
| `dist/init.d.ts` | `finovision-i18n/init` 类型(运行时代码与主入口相同 bundle) |
|
|
39
|
+
|
|
40
|
+
`i18next`、`finovision-i18n-resource` 等为 **external**,不打进包内。
|
|
41
|
+
|
|
42
|
+
## 单元测试
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd fv-i18n
|
|
46
|
+
npm test
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
会先构建 `fv-i18n-resource`、刷新 `file:` 依赖、编译本包,再运行 `test/i18n.test.ts`(覆盖 `core` / `init` / `I18n` 与资源包联调)。
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm run test:unit # 仅跑测试(需已 build)
|
|
53
|
+
npm run typecheck
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Vue 用法
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// main.ts
|
|
60
|
+
import { createApp } from 'vue';
|
|
61
|
+
import { initI18nInstance, Locale } from 'finovision-i18n/init';
|
|
62
|
+
import App from './App.vue';
|
|
63
|
+
|
|
64
|
+
await initI18nInstance({
|
|
65
|
+
lng: (localStorage.getItem('i18next') as 'en' | 'zh-CN') ?? Locale.ZhCN,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
createApp(App).mount('#app');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<script setup lang="ts">
|
|
73
|
+
import { I18n } from 'finovision-i18n';
|
|
74
|
+
|
|
75
|
+
const title = I18n.t('404_title');
|
|
76
|
+
const toast = I18n.t('AddSuccessToast', { name: 'Bot' });
|
|
77
|
+
</script>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 分层(对照 Coze)
|
|
81
|
+
|
|
82
|
+
| Coze | 本仓库 |
|
|
83
|
+
|------|--------|
|
|
84
|
+
| studio-i18n-resource | `finovision-i18n-resource` |
|
|
85
|
+
| intl + FlowIntl + raw | `finovision-i18n`(`core` + `i18n` + `init`) |
|
package/dist/i18n.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("i18next-icu"),t=require("i18next"),n=require("i18next-browser-languagedetector"),r=require("finovision-i18n-resource");class a{constructor(){this.initialized=!1,this.instance=t.createInstance(),this.instance.use(e)}get isInitialized(){return this.initialized}registerPlugins(e){e.forEach(e=>this.instance.use(e))}setup(e){return new Promise((t,n)=>{this.instance.init({keySeparator:!1,...e},(e,r)=>{this.initialized=!e,e?n(e):t(r)})})}getLanguage(){return this.instance.language}setLanguage(e,t){this.instance.changeLanguage(a.normalizeLng(e),t)}setLanguageAsync(e){return this.instance.changeLanguage(a.normalizeLng(e))}static normalizeLng(e){return"en-US"===e?"en":e}translate(e,t,n){return this.initialized?e?.trim()?String(void 0!==n?this.instance.t(e,n,t??{}):this.instance.t(e,t??{})):"":n??e}}const i=new a,s=i.instance,o=new class{get language(){return i.getLanguage()}setLang(e,t){i.setLanguage(e,t)}setLangAsync(e){return i.setLanguageAsync(e)}t(e,t,n){return i.translate(e,t,n)}};Object.defineProperty(exports,"DEFAULT_I18N_NAMESPACE",{enumerable:!0,get:()=>r.DEFAULT_I18N_NAMESPACE}),Object.defineProperty(exports,"Locale",{enumerable:!0,get:()=>r.Locale}),Object.defineProperty(exports,"SUPPORTED_LOCALES",{enumerable:!0,get:()=>r.SUPPORTED_LOCALES}),Object.defineProperty(exports,"buildDefaultConfig",{enumerable:!0,get:()=>r.buildDefaultConfig}),Object.defineProperty(exports,"defaultConfig",{enumerable:!0,get:()=>r.defaultConfig}),Object.defineProperty(exports,"loadLocale",{enumerable:!0,get:()=>r.loadLocale}),exports.I18n=o,exports.I18nCore=a,exports.getLanguage=function(){return i.getLanguage()},exports.i18nCore=i,exports.i18nextInstance=s,exports.initI18nInstance=async function(e){const{lng:t=r.Locale.ZhCN,detect:a=!0,ns:s=r.DEFAULT_I18N_NAMESPACE,...o}=e??{};a&&i.registerPlugins([n]),await i.setup({lng:t,fallbackLng:t,ns:s,defaultNS:s,resources:r.defaultConfig,...a?{detection:{order:["querystring","localStorage","navigator"],lookupQuerystring:"lng",lookupLocalStorage:"i18next",caches:["localStorage"]}}:{},react:{useSuspense:!1},keySeparator:!1,...o})},exports.normalizeLng=function(e){return a.normalizeLng(e)},exports.registerPlugins=function(e){i.registerPlugins(e)},exports.setLanguage=function(e,t){i.setLanguage(e,t)},exports.setLanguageAsync=function(e){return i.setLanguageAsync(e)},exports.setupI18next=function(e){return i.setup(e)},exports.translate=function(e,t,n){return i.translate(e,t,n)};
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as i18next from 'i18next';
|
|
2
|
+
import { i18n, Module, InitOptions, TFunction, Callback } from 'i18next';
|
|
3
|
+
import { I18nKeysNoOptionsType, I18nKeysHasOptionsType, LocaleData, I18nOptionsMap, SupportedLocale } from 'finovision-i18n-resource';
|
|
4
|
+
export { DEFAULT_I18N_NAMESPACE, I18nKeysHasOptionsType, I18nKeysNoOptionsType, I18nOptionsMap, InterpolationValue, Locale, LocaleData, SUPPORTED_LOCALES, SupportedLocale, buildDefaultConfig, defaultConfig, loadLocale } from 'finovision-i18n-resource';
|
|
5
|
+
|
|
6
|
+
type I18nOptions<K extends LocaleData> = K extends keyof I18nOptionsMap ? I18nOptionsMap[K] : never;
|
|
7
|
+
declare class TypedI18n {
|
|
8
|
+
get language(): string;
|
|
9
|
+
setLang(lng: string, callback?: () => void): void;
|
|
10
|
+
setLangAsync(lng: string): Promise<i18next.TFunction<"translation", undefined>>;
|
|
11
|
+
t<K extends I18nKeysNoOptionsType>(key: K, options?: Record<string, unknown>, fallback?: string): string;
|
|
12
|
+
t<K extends I18nKeysHasOptionsType>(key: K, options: I18nOptions<K>, fallback?: string): string;
|
|
13
|
+
}
|
|
14
|
+
declare const I18n: TypedI18n;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 初始化配置:已知字段 + 其余 i18next 选项(顶层透传,无需包在 extra 里)
|
|
18
|
+
*/
|
|
19
|
+
interface I18nConfig extends Record<string, unknown> {
|
|
20
|
+
/** 初始语言,默认 `Locale.ZhCN` */
|
|
21
|
+
lng?: SupportedLocale;
|
|
22
|
+
/** i18next 命名空间,默认 {@link DEFAULT_I18N_NAMESPACE} */
|
|
23
|
+
ns?: string;
|
|
24
|
+
/**
|
|
25
|
+
* 是否启用浏览器语言检测(querystring、localStorage、navigator)
|
|
26
|
+
* 默认 `true`
|
|
27
|
+
*/
|
|
28
|
+
detect?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** 在 createApp / mount 之前 await 一次 */
|
|
31
|
+
declare function initI18nInstance(options?: I18nConfig): Promise<void>;
|
|
32
|
+
|
|
33
|
+
/** i18next 实例封装,全应用共享单例 */
|
|
34
|
+
declare class I18nCore {
|
|
35
|
+
readonly instance: i18n;
|
|
36
|
+
private initialized;
|
|
37
|
+
constructor();
|
|
38
|
+
/** 是否已完成 init */
|
|
39
|
+
get isInitialized(): boolean;
|
|
40
|
+
registerPlugins(plugins: Module[] | unknown[]): void;
|
|
41
|
+
setup(config: InitOptions): Promise<TFunction>;
|
|
42
|
+
getLanguage(): string;
|
|
43
|
+
setLanguage(lng: string, callback?: Callback): void;
|
|
44
|
+
setLanguageAsync(lng: string): Promise<TFunction>;
|
|
45
|
+
/** en-US → en */
|
|
46
|
+
static normalizeLng(lng: string): string;
|
|
47
|
+
translate(key: string, options?: Record<string, unknown>, fallback?: string): string;
|
|
48
|
+
}
|
|
49
|
+
/** 全局唯一 i18next 内核实例 */
|
|
50
|
+
declare const i18nCore: I18nCore;
|
|
51
|
+
/** 兼容旧版函数式 API,委托给 {@link i18nCore} */
|
|
52
|
+
declare function registerPlugins(plugins: Module[] | unknown[]): void;
|
|
53
|
+
declare function setupI18next(config: InitOptions): Promise<TFunction>;
|
|
54
|
+
declare function getLanguage(): string;
|
|
55
|
+
declare function setLanguage(lng: string, callback?: Callback): void;
|
|
56
|
+
declare function setLanguageAsync(lng: string): Promise<TFunction>;
|
|
57
|
+
declare function normalizeLng(lng: string): string;
|
|
58
|
+
declare function translate(key: string, options?: Record<string, unknown>, fallback?: string): string;
|
|
59
|
+
declare const i18nextInstance: i18n;
|
|
60
|
+
|
|
61
|
+
export { I18n, I18nCore, getLanguage, i18nCore, i18nextInstance, initI18nInstance, normalizeLng, registerPlugins, setLanguage, setLanguageAsync, setupI18next, translate };
|
|
62
|
+
export type { I18nConfig };
|
package/dist/i18n.esm.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import n from"i18next-icu";import e from"i18next";import t from"i18next-browser-languagedetector";import{defaultConfig as i,DEFAULT_I18N_NAMESPACE as a,Locale as r}from"finovision-i18n-resource";export{DEFAULT_I18N_NAMESPACE,Locale,SUPPORTED_LOCALES,buildDefaultConfig,defaultConfig,loadLocale}from"finovision-i18n-resource";class s{constructor(){this.initialized=!1,this.instance=e.createInstance(),this.instance.use(n)}get isInitialized(){return this.initialized}registerPlugins(n){n.forEach(n=>this.instance.use(n))}setup(n){return new Promise((e,t)=>{this.instance.init({keySeparator:!1,...n},(n,i)=>{this.initialized=!n,n?t(n):e(i)})})}getLanguage(){return this.instance.language}setLanguage(n,e){this.instance.changeLanguage(s.normalizeLng(n),e)}setLanguageAsync(n){return this.instance.changeLanguage(s.normalizeLng(n))}static normalizeLng(n){return"en-US"===n?"en":n}translate(n,e,t){return this.initialized?n?.trim()?String(void 0!==t?this.instance.t(n,t,e??{}):this.instance.t(n,e??{})):"":t??n}}const g=new s;function o(n){g.registerPlugins(n)}function u(n){return g.setup(n)}function c(){return g.getLanguage()}function l(n,e){g.setLanguage(n,e)}function L(n){return g.setLanguageAsync(n)}function f(n){return s.normalizeLng(n)}function h(n,e,t){return g.translate(n,e,t)}const m=g.instance,d=new class{get language(){return g.getLanguage()}setLang(n,e){g.setLanguage(n,e)}setLangAsync(n){return g.setLanguageAsync(n)}t(n,e,t){return g.translate(n,e,t)}};async function p(n){const{lng:e=r.ZhCN,detect:s=!0,ns:o=a,...u}=n??{};s&&g.registerPlugins([t]),await g.setup({lng:e,fallbackLng:e,ns:o,defaultNS:o,resources:i,...s?{detection:{order:["querystring","localStorage","navigator"],lookupQuerystring:"lng",lookupLocalStorage:"i18next",caches:["localStorage"]}}:{},react:{useSuspense:!1},keySeparator:!1,...u})}export{d as I18n,s as I18nCore,c as getLanguage,g as i18nCore,m as i18nextInstance,p as initI18nInstance,f as normalizeLng,o as registerPlugins,l as setLanguage,L as setLanguageAsync,u as setupI18next,h as translate};
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { I18nKeysNoOptionsType, I18nKeysHasOptionsType, LocaleData, I18nOptionsMap, SupportedLocale } from 'finovision-i18n-resource';
|
|
2
|
+
export { I18nKeysHasOptionsType, I18nKeysNoOptionsType, I18nOptionsMap, Locale, LocaleData, SupportedLocale } from 'finovision-i18n-resource';
|
|
3
|
+
import * as i18next from 'i18next';
|
|
4
|
+
|
|
5
|
+
type I18nOptions<K extends LocaleData> = K extends keyof I18nOptionsMap ? I18nOptionsMap[K] : never;
|
|
6
|
+
declare class TypedI18n {
|
|
7
|
+
get language(): string;
|
|
8
|
+
setLang(lng: string, callback?: () => void): void;
|
|
9
|
+
setLangAsync(lng: string): Promise<i18next.TFunction<"translation", undefined>>;
|
|
10
|
+
t<K extends I18nKeysNoOptionsType>(key: K, options?: Record<string, unknown>, fallback?: string): string;
|
|
11
|
+
t<K extends I18nKeysHasOptionsType>(key: K, options: I18nOptions<K>, fallback?: string): string;
|
|
12
|
+
}
|
|
13
|
+
declare const I18n: TypedI18n;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 初始化配置:已知字段 + 其余 i18next 选项(顶层透传,无需包在 extra 里)
|
|
17
|
+
*/
|
|
18
|
+
interface I18nConfig extends Record<string, unknown> {
|
|
19
|
+
/** 初始语言,默认 `Locale.ZhCN` */
|
|
20
|
+
lng?: SupportedLocale;
|
|
21
|
+
/** i18next 命名空间,默认 {@link DEFAULT_I18N_NAMESPACE} */
|
|
22
|
+
ns?: string;
|
|
23
|
+
/**
|
|
24
|
+
* 是否启用浏览器语言检测(querystring、localStorage、navigator)
|
|
25
|
+
* 默认 `true`
|
|
26
|
+
*/
|
|
27
|
+
detect?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** 在 createApp / mount 之前 await 一次 */
|
|
30
|
+
declare function initI18nInstance(options?: I18nConfig): Promise<void>;
|
|
31
|
+
|
|
32
|
+
export { I18n, initI18nInstance };
|
|
33
|
+
export type { I18nConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "finovision-i18n",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Simplified i18next runtime with type-safe t() (uses finovision-i18n-resource)",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/i18n.cjs",
|
|
10
|
+
"module": "./dist/i18n.esm.js",
|
|
11
|
+
"types": "./dist/i18n.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/i18n.d.ts",
|
|
15
|
+
"import": "./dist/i18n.esm.js",
|
|
16
|
+
"require": "./dist/i18n.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./init": {
|
|
19
|
+
"types": "./dist/init.d.ts",
|
|
20
|
+
"import": "./dist/i18n.esm.js",
|
|
21
|
+
"require": "./dist/i18n.cjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"src"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"clean": "node -e \"import('node:fs').then(fs=>fs.rmSync('dist',{recursive:true,force:true}))\"",
|
|
30
|
+
"build": "npm run clean && cross-env NODE_ENV=production rollup -c",
|
|
31
|
+
"build:dev": "npm run clean && rollup -c",
|
|
32
|
+
"build:resource": "npm run build --prefix ../fv-i18n-resource",
|
|
33
|
+
"reinstall:resource": "npm install",
|
|
34
|
+
"pretest": "npm run build:resource && npm run reinstall:resource && npm run build:dev",
|
|
35
|
+
"test": "tsx --test test/*.test.ts",
|
|
36
|
+
"test:unit": "tsx --test test/*.test.ts",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"finovision-i18n-resource": "file:../fv-i18n-resource",
|
|
42
|
+
"i18next": "^23.16.0",
|
|
43
|
+
"i18next-browser-languagedetector": "^8.0.4",
|
|
44
|
+
"i18next-icu": "^2.3.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
48
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
49
|
+
"@rollup/plugin-typescript": "^12.1.0",
|
|
50
|
+
"@types/node": "^22.10.0",
|
|
51
|
+
"cross-env": "^7.0.3",
|
|
52
|
+
"rollup": "^4.27.0",
|
|
53
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
54
|
+
"tslib": "^2.8.0",
|
|
55
|
+
"tsx": "^4.19.0",
|
|
56
|
+
"typescript": "~5.8.2"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT"
|
|
59
|
+
}
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import ICU from 'i18next-icu';
|
|
2
|
+
import i18next, {
|
|
3
|
+
type Callback,
|
|
4
|
+
type i18n,
|
|
5
|
+
type InitOptions,
|
|
6
|
+
type Module,
|
|
7
|
+
type TFunction,
|
|
8
|
+
} from 'i18next';
|
|
9
|
+
|
|
10
|
+
/** i18next 实例封装,全应用共享单例 */
|
|
11
|
+
export class I18nCore {
|
|
12
|
+
readonly instance: i18n;
|
|
13
|
+
private initialized = false;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.instance = i18next.createInstance();
|
|
17
|
+
this.instance.use(ICU);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 是否已完成 init */
|
|
21
|
+
get isInitialized(): boolean {
|
|
22
|
+
return this.initialized;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
registerPlugins(plugins: Module[] | unknown[]): void {
|
|
26
|
+
plugins.forEach((p) => this.instance.use(p as Module));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setup(config: InitOptions): Promise<TFunction> {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
this.instance.init(
|
|
32
|
+
{
|
|
33
|
+
keySeparator: false,
|
|
34
|
+
...config,
|
|
35
|
+
},
|
|
36
|
+
(err, t) => {
|
|
37
|
+
this.initialized = !err;
|
|
38
|
+
if (err) reject(err);
|
|
39
|
+
else resolve(t);
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getLanguage(): string {
|
|
46
|
+
return this.instance.language;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setLanguage(lng: string, callback?: Callback): void {
|
|
50
|
+
this.instance.changeLanguage(I18nCore.normalizeLng(lng), callback);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setLanguageAsync(lng: string): Promise<TFunction> {
|
|
54
|
+
return this.instance.changeLanguage(I18nCore.normalizeLng(lng));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** en-US → en */
|
|
58
|
+
static normalizeLng(lng: string): string {
|
|
59
|
+
return lng === 'en-US' ? 'en' : lng;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
translate(
|
|
63
|
+
key: string,
|
|
64
|
+
options?: Record<string, unknown>,
|
|
65
|
+
fallback?: string,
|
|
66
|
+
): string {
|
|
67
|
+
if (!this.initialized) {
|
|
68
|
+
return fallback ?? key;
|
|
69
|
+
}
|
|
70
|
+
if (!key?.trim()) return '';
|
|
71
|
+
if (fallback !== undefined) {
|
|
72
|
+
return String(this.instance.t(key, fallback, options ?? {}));
|
|
73
|
+
}
|
|
74
|
+
return String(this.instance.t(key, options ?? {}));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 全局唯一 i18next 内核实例 */
|
|
79
|
+
export const i18nCore = new I18nCore();
|
|
80
|
+
|
|
81
|
+
/** 兼容旧版函数式 API,委托给 {@link i18nCore} */
|
|
82
|
+
export function registerPlugins(plugins: Module[] | unknown[]): void {
|
|
83
|
+
i18nCore.registerPlugins(plugins);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function setupI18next(config: InitOptions): Promise<TFunction> {
|
|
87
|
+
return i18nCore.setup(config);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getLanguage(): string {
|
|
91
|
+
return i18nCore.getLanguage();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function setLanguage(lng: string, callback?: Callback): void {
|
|
95
|
+
i18nCore.setLanguage(lng, callback);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function setLanguageAsync(lng: string): Promise<TFunction> {
|
|
99
|
+
return i18nCore.setLanguageAsync(lng);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function normalizeLng(lng: string): string {
|
|
103
|
+
return I18nCore.normalizeLng(lng);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function translate(
|
|
107
|
+
key: string,
|
|
108
|
+
options?: Record<string, unknown>,
|
|
109
|
+
fallback?: string,
|
|
110
|
+
): string {
|
|
111
|
+
return i18nCore.translate(key, options, fallback);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const i18nextInstance = i18nCore.instance;
|
package/src/i18n.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型安全 I18n 门面(等同 Coze FlowIntl,类型来自 finovision-i18n-resource)
|
|
3
|
+
*/
|
|
4
|
+
import type {
|
|
5
|
+
I18nOptionsMap,
|
|
6
|
+
I18nKeysHasOptionsType,
|
|
7
|
+
I18nKeysNoOptionsType,
|
|
8
|
+
LocaleData,
|
|
9
|
+
} from 'finovision-i18n-resource';
|
|
10
|
+
|
|
11
|
+
import { i18nCore } from './core';
|
|
12
|
+
|
|
13
|
+
type I18nOptions<K extends LocaleData> = K extends keyof I18nOptionsMap
|
|
14
|
+
? I18nOptionsMap[K]
|
|
15
|
+
: never;
|
|
16
|
+
|
|
17
|
+
class TypedI18n {
|
|
18
|
+
get language(): string {
|
|
19
|
+
return i18nCore.getLanguage();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setLang(lng: string, callback?: () => void): void {
|
|
23
|
+
i18nCore.setLanguage(lng, callback);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setLangAsync(lng: string) {
|
|
27
|
+
return i18nCore.setLanguageAsync(lng);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
t<K extends I18nKeysNoOptionsType>(
|
|
31
|
+
key: K,
|
|
32
|
+
options?: Record<string, unknown>,
|
|
33
|
+
fallback?: string,
|
|
34
|
+
): string;
|
|
35
|
+
t<K extends I18nKeysHasOptionsType>(
|
|
36
|
+
key: K,
|
|
37
|
+
options: I18nOptions<K>,
|
|
38
|
+
fallback?: string,
|
|
39
|
+
): string;
|
|
40
|
+
t<K extends LocaleData>(
|
|
41
|
+
key: K,
|
|
42
|
+
options?: I18nOptions<K> | Record<string, unknown>,
|
|
43
|
+
fallback?: string,
|
|
44
|
+
): string {
|
|
45
|
+
return i18nCore.translate(key, options, fallback);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const I18n = new TypedI18n();
|
|
50
|
+
export { TypedI18n };
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { I18n } from './i18n';
|
|
2
|
+
export { initI18nInstance, type I18nConfig } from './init';
|
|
3
|
+
export {
|
|
4
|
+
I18nCore,
|
|
5
|
+
i18nCore,
|
|
6
|
+
normalizeLng,
|
|
7
|
+
translate,
|
|
8
|
+
getLanguage,
|
|
9
|
+
setLanguage,
|
|
10
|
+
setLanguageAsync,
|
|
11
|
+
registerPlugins,
|
|
12
|
+
setupI18next,
|
|
13
|
+
i18nextInstance,
|
|
14
|
+
} from './core';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
I18nOptionsMap,
|
|
18
|
+
I18nKeysHasOptionsType,
|
|
19
|
+
I18nKeysNoOptionsType,
|
|
20
|
+
LocaleData,
|
|
21
|
+
SupportedLocale,
|
|
22
|
+
InterpolationValue,
|
|
23
|
+
} from 'finovision-i18n-resource';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
defaultConfig,
|
|
27
|
+
DEFAULT_I18N_NAMESPACE,
|
|
28
|
+
Locale,
|
|
29
|
+
SUPPORTED_LOCALES,
|
|
30
|
+
loadLocale,
|
|
31
|
+
buildDefaultConfig,
|
|
32
|
+
} from 'finovision-i18n-resource';
|
package/src/init.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 应用入口初始化
|
|
3
|
+
*/
|
|
4
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
5
|
+
import {
|
|
6
|
+
defaultConfig,
|
|
7
|
+
DEFAULT_I18N_NAMESPACE,
|
|
8
|
+
Locale,
|
|
9
|
+
type SupportedLocale,
|
|
10
|
+
} from 'finovision-i18n-resource';
|
|
11
|
+
|
|
12
|
+
import { i18nCore } from './core';
|
|
13
|
+
import { I18n } from './i18n';
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
I18nKeysNoOptionsType,
|
|
17
|
+
I18nKeysHasOptionsType,
|
|
18
|
+
LocaleData,
|
|
19
|
+
I18nOptionsMap,
|
|
20
|
+
SupportedLocale,
|
|
21
|
+
Locale,
|
|
22
|
+
} from 'finovision-i18n-resource';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 初始化配置:已知字段 + 其余 i18next 选项(顶层透传,无需包在 extra 里)
|
|
26
|
+
*/
|
|
27
|
+
export interface I18nConfig extends Record<string, unknown> {
|
|
28
|
+
/** 初始语言,默认 `Locale.ZhCN` */
|
|
29
|
+
lng?: SupportedLocale;
|
|
30
|
+
/** i18next 命名空间,默认 {@link DEFAULT_I18N_NAMESPACE} */
|
|
31
|
+
ns?: string;
|
|
32
|
+
/**
|
|
33
|
+
* 是否启用浏览器语言检测(querystring、localStorage、navigator)
|
|
34
|
+
* 默认 `true`
|
|
35
|
+
*/
|
|
36
|
+
detect?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** 在 createApp / mount 之前 await 一次 */
|
|
40
|
+
export async function initI18nInstance(options?: I18nConfig): Promise<void> {
|
|
41
|
+
const {
|
|
42
|
+
lng = Locale.ZhCN,
|
|
43
|
+
detect = true,
|
|
44
|
+
ns = DEFAULT_I18N_NAMESPACE,
|
|
45
|
+
...rest
|
|
46
|
+
} = options ?? {};
|
|
47
|
+
|
|
48
|
+
if (detect) {
|
|
49
|
+
i18nCore.registerPlugins([LanguageDetector]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await i18nCore.setup({
|
|
53
|
+
lng,
|
|
54
|
+
fallbackLng: lng,
|
|
55
|
+
ns,
|
|
56
|
+
defaultNS: ns,
|
|
57
|
+
resources: defaultConfig,
|
|
58
|
+
...(detect
|
|
59
|
+
? {
|
|
60
|
+
detection: {
|
|
61
|
+
order: ['querystring', 'localStorage', 'navigator'],
|
|
62
|
+
lookupQuerystring: 'lng',
|
|
63
|
+
lookupLocalStorage: 'i18next',
|
|
64
|
+
caches: ['localStorage'],
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
: {}),
|
|
68
|
+
react: { useSuspense: false },
|
|
69
|
+
keySeparator: false,
|
|
70
|
+
...rest,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { I18n };
|