@arronqzy/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 +42 -0
- package/package.json +45 -0
- package/src/core.ts +96 -0
- package/src/index.ts +37 -0
- package/src/locales/en-US.ts +1360 -0
- package/src/locales/zh-CN.ts +1358 -0
- package/src/react/I18nProvider.tsx +96 -0
- package/src/react/index.ts +2 -0
- package/src/types.ts +13 -0
- package/src/vue/index.ts +98 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @arronqzy/i18n
|
|
2
|
+
|
|
3
|
+
Abuilder 共享国际化包,默认支持 **zh-CN** / **en-US**,可同时用于 React 与 Vue。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @arronqzy/i18n
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## React
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { I18nProvider, useI18n } from "@arronqzy/i18n/react";
|
|
15
|
+
|
|
16
|
+
<I18nProvider locale="zh-CN">
|
|
17
|
+
<App />
|
|
18
|
+
</I18nProvider>
|
|
19
|
+
|
|
20
|
+
const { t, locale, setLocale } = useI18n();
|
|
21
|
+
t("panel.menubar.file");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Vue
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { abuilderI18n, useI18n, provideI18n } from "@arronqzy/i18n/vue";
|
|
28
|
+
|
|
29
|
+
app.use(abuilderI18n, { locale: "en-US" });
|
|
30
|
+
// 或在组件内 provideI18n({ locale: "zh-CN" })
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 语言解析优先级
|
|
34
|
+
|
|
35
|
+
1. 显式 `locale` prop
|
|
36
|
+
2. `localStorage`(默认 key:`abuilder.locale`)
|
|
37
|
+
3. 浏览器语言(`zh*` → zh-CN,否则 en-US)
|
|
38
|
+
4. 回退 `zh-CN`
|
|
39
|
+
|
|
40
|
+
## 许可证
|
|
41
|
+
|
|
42
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arronqzy/i18n",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Shared Chinese/English i18n for Abuilder (React + Vue)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"main": "./src/index.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.ts",
|
|
13
|
+
"./react": "./src/react/index.ts",
|
|
14
|
+
"./vue": "./src/vue/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"react": ">=18",
|
|
18
|
+
"vue": "^3.4.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependenciesMeta": {
|
|
21
|
+
"react": {
|
|
22
|
+
"optional": true
|
|
23
|
+
},
|
|
24
|
+
"vue": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^19.0.1",
|
|
30
|
+
"eslint": "^8.57.0",
|
|
31
|
+
"react": "^19",
|
|
32
|
+
"typescript": "^5.5.4",
|
|
33
|
+
"vue": "^3.5.13",
|
|
34
|
+
"@arronqzy/eslint-config": "0.0.0",
|
|
35
|
+
"@arronqzy/typescript-config": "1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
+
"build": "pnpm run typecheck"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Locale, MessageParams, NestedMessages } from "./types";
|
|
2
|
+
import { LOCALE_STORAGE_KEY } from "./types";
|
|
3
|
+
|
|
4
|
+
function getByPath(messages: NestedMessages, path: string): string | undefined {
|
|
5
|
+
const parts = path.split(".");
|
|
6
|
+
let cur: string | NestedMessages | undefined = messages;
|
|
7
|
+
for (const part of parts) {
|
|
8
|
+
if (cur == null || typeof cur === "string") return undefined;
|
|
9
|
+
cur = cur[part];
|
|
10
|
+
}
|
|
11
|
+
return typeof cur === "string" ? cur : undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function interpolate(template: string, params?: MessageParams): string {
|
|
15
|
+
if (!params) return template;
|
|
16
|
+
return template.replace(/\{(\w+)\}/g, (_, key: string) => {
|
|
17
|
+
const value = params[key];
|
|
18
|
+
return value == null ? "" : String(value);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createTranslator(messages: NestedMessages) {
|
|
23
|
+
return function t(key: string, params?: MessageParams): string {
|
|
24
|
+
const raw = getByPath(messages, key);
|
|
25
|
+
if (raw == null) {
|
|
26
|
+
if (shouldWarnMissingKey()) {
|
|
27
|
+
console.warn(`[i18n] missing key: ${key}`);
|
|
28
|
+
}
|
|
29
|
+
return key;
|
|
30
|
+
}
|
|
31
|
+
return interpolate(raw, params);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Avoid `@types/node`; bundlers may still inject `process.env`. */
|
|
36
|
+
function shouldWarnMissingKey(): boolean {
|
|
37
|
+
const g = globalThis as typeof globalThis & {
|
|
38
|
+
process?: { env?: Record<string, string | undefined> };
|
|
39
|
+
};
|
|
40
|
+
return g.process?.env?.NODE_ENV !== "production";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isLocale(value: unknown): value is Locale {
|
|
44
|
+
return value === "zh-CN" || value === "en-US";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function detectBrowserLocale(): Locale {
|
|
48
|
+
if (typeof navigator === "undefined") return "zh-CN";
|
|
49
|
+
const lang = (navigator.language || "").toLowerCase();
|
|
50
|
+
return lang.startsWith("zh") ? "zh-CN" : "en-US";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function readStoredLocale(storageKey = LOCALE_STORAGE_KEY): Locale | null {
|
|
54
|
+
if (typeof localStorage === "undefined") return null;
|
|
55
|
+
try {
|
|
56
|
+
const raw = localStorage.getItem(storageKey);
|
|
57
|
+
return isLocale(raw) ? raw : null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function writeStoredLocale(locale: Locale, storageKey = LOCALE_STORAGE_KEY): void {
|
|
64
|
+
if (typeof localStorage === "undefined") return;
|
|
65
|
+
try {
|
|
66
|
+
localStorage.setItem(storageKey, locale);
|
|
67
|
+
} catch {
|
|
68
|
+
/* ignore quota / private mode */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type ResolveLocaleOptions = {
|
|
73
|
+
/** Explicit prop wins when set */
|
|
74
|
+
locale?: Locale | null;
|
|
75
|
+
storageKey?: string;
|
|
76
|
+
/** When true, use browser language if nothing else (default true) */
|
|
77
|
+
detectBrowser?: boolean;
|
|
78
|
+
fallback?: Locale;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Priority: explicit locale → localStorage → browser → fallback (zh-CN)
|
|
83
|
+
*/
|
|
84
|
+
export function resolveLocale(options: ResolveLocaleOptions = {}): Locale {
|
|
85
|
+
const {
|
|
86
|
+
locale,
|
|
87
|
+
storageKey = LOCALE_STORAGE_KEY,
|
|
88
|
+
detectBrowser = true,
|
|
89
|
+
fallback = "zh-CN",
|
|
90
|
+
} = options;
|
|
91
|
+
if (isLocale(locale)) return locale;
|
|
92
|
+
const stored = readStoredLocale(storageKey);
|
|
93
|
+
if (stored) return stored;
|
|
94
|
+
if (detectBrowser) return detectBrowserLocale();
|
|
95
|
+
return fallback;
|
|
96
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
Locale,
|
|
3
|
+
MessageParams,
|
|
4
|
+
NestedMessages,
|
|
5
|
+
TranslateFn,
|
|
6
|
+
} from "./types";
|
|
7
|
+
export { LOCALES, LOCALE_STORAGE_KEY } from "./types";
|
|
8
|
+
export {
|
|
9
|
+
createTranslator,
|
|
10
|
+
interpolate,
|
|
11
|
+
resolveLocale,
|
|
12
|
+
detectBrowserLocale,
|
|
13
|
+
readStoredLocale,
|
|
14
|
+
writeStoredLocale,
|
|
15
|
+
isLocale,
|
|
16
|
+
} from "./core";
|
|
17
|
+
export { zhCN } from "./locales/zh-CN";
|
|
18
|
+
export { enUS } from "./locales/en-US";
|
|
19
|
+
export type { MessageCatalog } from "./locales/zh-CN";
|
|
20
|
+
|
|
21
|
+
import { createTranslator } from "./core";
|
|
22
|
+
import { zhCN } from "./locales/zh-CN";
|
|
23
|
+
import { enUS } from "./locales/en-US";
|
|
24
|
+
import type { Locale, NestedMessages } from "./types";
|
|
25
|
+
|
|
26
|
+
const catalogs: Record<Locale, NestedMessages> = {
|
|
27
|
+
"zh-CN": zhCN as unknown as NestedMessages,
|
|
28
|
+
"en-US": enUS as unknown as NestedMessages,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function getMessages(locale: Locale): NestedMessages {
|
|
32
|
+
return catalogs[locale];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function tForLocale(locale: Locale) {
|
|
36
|
+
return createTranslator(catalogs[locale]);
|
|
37
|
+
}
|