@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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
} from "react";
|
|
10
|
+
import { createTranslator, isLocale, resolveLocale, writeStoredLocale } from "../core";
|
|
11
|
+
import { zhCN } from "../locales/zh-CN";
|
|
12
|
+
import { enUS } from "../locales/en-US";
|
|
13
|
+
import type { Locale, MessageParams, NestedMessages } from "../types";
|
|
14
|
+
import { LOCALE_STORAGE_KEY } from "../types";
|
|
15
|
+
|
|
16
|
+
const catalogs: Record<Locale, NestedMessages> = {
|
|
17
|
+
"zh-CN": zhCN as unknown as NestedMessages,
|
|
18
|
+
"en-US": enUS as unknown as NestedMessages,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type I18nContextValue = {
|
|
22
|
+
locale: Locale;
|
|
23
|
+
setLocale: (locale: Locale) => void;
|
|
24
|
+
t: (key: string, params?: MessageParams) => string;
|
|
25
|
+
messages: NestedMessages;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const I18nContext = createContext<I18nContextValue | null>(null);
|
|
29
|
+
|
|
30
|
+
export type I18nProviderProps = {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
/** Explicit locale; when omitted, resolve from storage / browser / zh-CN */
|
|
33
|
+
locale?: Locale | null;
|
|
34
|
+
storageKey?: string;
|
|
35
|
+
detectBrowser?: boolean;
|
|
36
|
+
onLocaleChange?: (locale: Locale) => void;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function I18nProvider({
|
|
40
|
+
children,
|
|
41
|
+
locale: localeProp,
|
|
42
|
+
storageKey = LOCALE_STORAGE_KEY,
|
|
43
|
+
detectBrowser = true,
|
|
44
|
+
onLocaleChange,
|
|
45
|
+
}: I18nProviderProps) {
|
|
46
|
+
const [localeState, setLocaleState] = useState<Locale>(() =>
|
|
47
|
+
resolveLocale({ locale: localeProp, storageKey, detectBrowser })
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (isLocale(localeProp)) setLocaleState(localeProp);
|
|
52
|
+
}, [localeProp]);
|
|
53
|
+
|
|
54
|
+
const locale = isLocale(localeProp) ? localeProp : localeState;
|
|
55
|
+
|
|
56
|
+
const setLocale = useCallback(
|
|
57
|
+
(next: Locale) => {
|
|
58
|
+
setLocaleState(next);
|
|
59
|
+
writeStoredLocale(next, storageKey);
|
|
60
|
+
onLocaleChange?.(next);
|
|
61
|
+
},
|
|
62
|
+
[onLocaleChange, storageKey]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const messages = catalogs[locale];
|
|
66
|
+
const t = useMemo(() => createTranslator(messages), [messages]);
|
|
67
|
+
|
|
68
|
+
const value = useMemo<I18nContextValue>(
|
|
69
|
+
() => ({ locale, setLocale, t, messages }),
|
|
70
|
+
[locale, setLocale, t, messages]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function useI18n(): I18nContextValue {
|
|
77
|
+
const ctx = useContext(I18nContext);
|
|
78
|
+
if (!ctx) {
|
|
79
|
+
throw new Error("useI18n must be used within I18nProvider");
|
|
80
|
+
}
|
|
81
|
+
return ctx;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Safe hook: returns zh-CN translator when outside provider (for utilities). */
|
|
85
|
+
export function useI18nOptional(): I18nContextValue {
|
|
86
|
+
const ctx = useContext(I18nContext);
|
|
87
|
+
if (ctx) return ctx;
|
|
88
|
+
const messages = catalogs["zh-CN"];
|
|
89
|
+
const t = createTranslator(messages);
|
|
90
|
+
return {
|
|
91
|
+
locale: "zh-CN",
|
|
92
|
+
setLocale: () => undefined,
|
|
93
|
+
t,
|
|
94
|
+
messages,
|
|
95
|
+
};
|
|
96
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Locale = "zh-CN" | "en-US";
|
|
2
|
+
|
|
3
|
+
export const LOCALES: Locale[] = ["zh-CN", "en-US"];
|
|
4
|
+
|
|
5
|
+
export const LOCALE_STORAGE_KEY = "abuilder.locale";
|
|
6
|
+
|
|
7
|
+
export type MessageParams = Record<string, string | number | boolean | null | undefined>;
|
|
8
|
+
|
|
9
|
+
export type TranslateFn = (key: string, params?: MessageParams) => string;
|
|
10
|
+
|
|
11
|
+
export type NestedMessages = {
|
|
12
|
+
[key: string]: string | NestedMessages;
|
|
13
|
+
};
|
package/src/vue/index.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
inject,
|
|
4
|
+
provide,
|
|
5
|
+
ref,
|
|
6
|
+
type App,
|
|
7
|
+
type InjectionKey,
|
|
8
|
+
type Ref,
|
|
9
|
+
} from "vue";
|
|
10
|
+
import { createTranslator, resolveLocale, writeStoredLocale } from "../core";
|
|
11
|
+
import { zhCN } from "../locales/zh-CN";
|
|
12
|
+
import { enUS } from "../locales/en-US";
|
|
13
|
+
import type { Locale, MessageParams, NestedMessages, TranslateFn } from "../types";
|
|
14
|
+
import { LOCALE_STORAGE_KEY } from "../types";
|
|
15
|
+
|
|
16
|
+
const catalogs: Record<Locale, NestedMessages> = {
|
|
17
|
+
"zh-CN": zhCN as unknown as NestedMessages,
|
|
18
|
+
"en-US": enUS as unknown as NestedMessages,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type I18nState = {
|
|
22
|
+
locale: Ref<Locale>;
|
|
23
|
+
setLocale: (locale: Locale) => void;
|
|
24
|
+
t: TranslateFn;
|
|
25
|
+
messages: Ref<NestedMessages>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const ABUILDER_I18N_KEY: InjectionKey<I18nState> = Symbol("abuilder-i18n");
|
|
29
|
+
|
|
30
|
+
export type CreateAbuilderI18nOptions = {
|
|
31
|
+
locale?: Locale | null;
|
|
32
|
+
storageKey?: string;
|
|
33
|
+
detectBrowser?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function createAbuilderI18n(options: CreateAbuilderI18nOptions = {}): I18nState {
|
|
37
|
+
const storageKey = options.storageKey ?? LOCALE_STORAGE_KEY;
|
|
38
|
+
const locale = ref(
|
|
39
|
+
resolveLocale({
|
|
40
|
+
locale: options.locale,
|
|
41
|
+
storageKey,
|
|
42
|
+
detectBrowser: options.detectBrowser ?? true,
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const messages = computed(() => catalogs[locale.value]);
|
|
47
|
+
|
|
48
|
+
const t: TranslateFn = (key, params) => createTranslator(messages.value)(key, params);
|
|
49
|
+
|
|
50
|
+
function setLocale(next: Locale) {
|
|
51
|
+
locale.value = next;
|
|
52
|
+
writeStoredLocale(next, storageKey);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
locale,
|
|
57
|
+
setLocale,
|
|
58
|
+
t,
|
|
59
|
+
messages,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function provideI18n(options?: CreateAbuilderI18nOptions): I18nState {
|
|
64
|
+
const i18n = createAbuilderI18n(options);
|
|
65
|
+
provide(ABUILDER_I18N_KEY, i18n);
|
|
66
|
+
return i18n;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Vue plugin: app.use(abuilderI18n, { locale: 'en-US' }) */
|
|
70
|
+
export const abuilderI18n = {
|
|
71
|
+
install(app: App, options: CreateAbuilderI18nOptions = {}) {
|
|
72
|
+
const i18n = createAbuilderI18n(options);
|
|
73
|
+
app.provide(ABUILDER_I18N_KEY, i18n);
|
|
74
|
+
app.config.globalProperties.$t = i18n.t;
|
|
75
|
+
app.config.globalProperties.$i18n = i18n;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export function useI18n(): I18nState {
|
|
80
|
+
const ctx = inject(ABUILDER_I18N_KEY, null);
|
|
81
|
+
if (!ctx) {
|
|
82
|
+
throw new Error("useI18n must be used after provideI18n / abuilderI18n plugin");
|
|
83
|
+
}
|
|
84
|
+
return ctx;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function useI18nOptional(): I18nState {
|
|
88
|
+
const ctx = inject(ABUILDER_I18N_KEY, null);
|
|
89
|
+
if (ctx) return ctx;
|
|
90
|
+
return createAbuilderI18n({ locale: "zh-CN", detectBrowser: false });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare module "vue" {
|
|
94
|
+
interface ComponentCustomProperties {
|
|
95
|
+
$t: TranslateFn;
|
|
96
|
+
$i18n: I18nState;
|
|
97
|
+
}
|
|
98
|
+
}
|