Skip to content

Commit c69dabc

Browse files
committed
feat(): 框架支持LANGUAGE常量
Closes CMDB_INSTANCE-3369
1 parent fc95fd9 commit c69dabc

File tree

4 files changed

+237
-1
lines changed

4 files changed

+237
-1
lines changed

packages/runtime/src/internal/compute/evaluate.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jest.mock("../devtools.js");
8282

8383
i18n.init({
8484
fallbackLng: "en",
85+
supportedLngs: ["en", "zh"],
8586
});
8687
i18n.addResourceBundle("en", getI18nNamespace("app", "hello"), {
8788
HELLO: "Hello",
@@ -255,6 +256,8 @@ const consoleError = jest.spyOn(console, "error");
255256

256257
describe("evaluate", () => {
257258
beforeEach(() => {
259+
i18n.changeLanguage("zh");
260+
258261
(_internalApiGetRuntimeContext as jest.Mock).mockReset();
259262
});
260263

@@ -291,7 +294,7 @@ describe("evaluate", () => {
291294
["<% I18N('HELLO') %>", "Hello"],
292295
["<% I18N('COUNT_ITEMS', { count: 5 }) %>", "Total 5 items"],
293296
["<% I18N('NOT_EXISTED') %>", "NOT_EXISTED"],
294-
["<% __WIDGET_I18N__('my-widget')('WORLD') %>", "World"],
297+
["<% __WIDGET_I18N__('my-widget')('WORLD') %>", "世界"],
295298
["<% I18N_TEXT({ en: 'hello', zh: '你好' }) %>", "你好"],
296299
["<% CTX.myFreeContext %>", "good"],
297300
["<% CTX.notExisted %>", undefined],
@@ -318,6 +321,7 @@ describe("evaluate", () => {
318321
["<% SIZE %>", 2],
319322
["<% STATE.myState %>", "better"],
320323
["<% FORM_STATE.myFormItem %>", "input"],
324+
["<% LANGUAGE %>", { en: false, zh: true }],
321325
])("evaluate(%j) should return %j", (raw, result) => {
322326
expect(evaluate(raw, runtimeContext)).toEqual(result);
323327
});

packages/runtime/src/internal/compute/evaluate.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import { isStrictMode, warnAboutStrictMode } from "../../isStrictMode.js";
4444
import { getFormStateStore } from "../FormRenderer/utils.js";
4545
import { resolveData } from "../data/resolveData.js";
4646
import { asyncComputeRealValue } from "./computeRealValue.js";
47+
import { getSupportedLanguages } from "./language.js";
4748

4849
const symbolForRaw = Symbol.for("pre.evaluated.raw");
4950
const symbolForContext = Symbol.for("pre.evaluated.context");
@@ -503,6 +504,10 @@ function lowLevelEvaluate(
503504
case "SYS":
504505
globalVariables[variableName] = getReadOnlyProxy(sys ?? {});
505506
break;
507+
case "LANGUAGE": {
508+
globalVariables[variableName] = getSupportedLanguages();
509+
break;
510+
}
506511
case "__WIDGET_FN__":
507512
globalVariables[variableName] = widgetFunctions;
508513
break;
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import i18n from "i18next";
2+
import { getSupportedLanguages } from "./language";
3+
4+
describe("getSupportedLanguages", () => {
5+
beforeEach(() => {
6+
// 重置 i18n 实例
7+
i18n.init({
8+
fallbackLng: "zh",
9+
supportedLngs: ["en", "zh"],
10+
resources: {
11+
en: {
12+
translation: {
13+
hello: "Hello",
14+
},
15+
},
16+
zh: {
17+
translation: {
18+
hello: "你好",
19+
},
20+
},
21+
},
22+
});
23+
});
24+
25+
describe("当 supportedLngs 是数组时", () => {
26+
it("应该正确返回支持的语言,当前语言为 zh", () => {
27+
i18n.changeLanguage("zh");
28+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
29+
});
30+
31+
it("应该正确返回支持的语言,当前语言为 en", () => {
32+
i18n.changeLanguage("en");
33+
expect(getSupportedLanguages()).toEqual({ en: true, zh: false });
34+
});
35+
36+
it("应该过滤掉 cimode 语言", () => {
37+
i18n.init({
38+
supportedLngs: ["en", "zh", "cimode"],
39+
resources: {
40+
en: { translation: {} },
41+
zh: { translation: {} },
42+
},
43+
});
44+
i18n.changeLanguage("zh");
45+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
46+
});
47+
48+
it("当当前语言为 cimode 时,应该返回所有语言都为 false", () => {
49+
i18n.changeLanguage("cimode");
50+
expect(getSupportedLanguages()).toEqual({ en: false, zh: false });
51+
});
52+
});
53+
54+
describe("当 supportedLngs 是字符串时", () => {
55+
it("应该正确处理单个语言字符串", () => {
56+
// 模拟 supportedLngs 为字符串的情况
57+
const originalOptions = i18n.options;
58+
i18n.options = {
59+
...originalOptions,
60+
supportedLngs: "en" as any,
61+
resources: {
62+
en: { translation: {} },
63+
},
64+
};
65+
i18n.changeLanguage("en");
66+
expect(getSupportedLanguages()).toEqual({ en: true });
67+
i18n.options = originalOptions;
68+
});
69+
70+
it("应该正确处理空字符串", () => {
71+
// 模拟 supportedLngs 为空字符串的情况
72+
const originalOptions = i18n.options;
73+
i18n.options = {
74+
...originalOptions,
75+
supportedLngs: "" as any,
76+
resources: {
77+
en: { translation: {} },
78+
zh: { translation: {} },
79+
},
80+
};
81+
i18n.changeLanguage("zh");
82+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
83+
i18n.options = originalOptions;
84+
});
85+
});
86+
87+
describe("当从 resources 中获取语言时", () => {
88+
it("应该从 resources 中提取语言列表", () => {
89+
i18n.init({
90+
resources: {
91+
en: { translation: {} },
92+
zh: { translation: {} },
93+
fr: { translation: {} },
94+
},
95+
});
96+
i18n.changeLanguage("fr");
97+
expect(getSupportedLanguages()).toEqual({
98+
en: false,
99+
zh: false,
100+
fr: true,
101+
});
102+
});
103+
104+
it("应该处理空的 resources", () => {
105+
i18n.init({
106+
resources: {},
107+
});
108+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
109+
});
110+
111+
it("应该处理 resources 为 null 的情况", () => {
112+
i18n.init({
113+
resources: null,
114+
});
115+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
116+
});
117+
});
118+
119+
describe("当没有配置语言时", () => {
120+
it("应该返回默认的 fallback 值", () => {
121+
i18n.init({});
122+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
123+
});
124+
125+
it("应该处理 i18n.options 为空的情况", () => {
126+
// 模拟 i18n.options 为空的情况
127+
const originalOptions = i18n.options;
128+
i18n.options = undefined;
129+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
130+
i18n.options = originalOptions;
131+
});
132+
133+
it("应该处理 supportedLngs 为 null 的情况", () => {
134+
i18n.init({
135+
supportedLngs: null,
136+
});
137+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
138+
});
139+
140+
it("应该处理 supportedLngs 为 undefined 的情况", () => {
141+
i18n.init({
142+
supportedLngs: undefined,
143+
});
144+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
145+
});
146+
});
147+
148+
describe("边界情况", () => {
149+
it("应该处理空数组的情况", () => {
150+
i18n.init({
151+
supportedLngs: [],
152+
});
153+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
154+
});
155+
156+
it("应该处理只包含 cimode 的数组", () => {
157+
i18n.init({
158+
supportedLngs: ["cimode"],
159+
});
160+
expect(getSupportedLanguages()).toEqual({ en: false, zh: true });
161+
});
162+
163+
it("应该处理多个语言的情况", () => {
164+
i18n.init({
165+
supportedLngs: ["en", "zh", "fr", "de"],
166+
resources: {
167+
en: { translation: {} },
168+
zh: { translation: {} },
169+
fr: { translation: {} },
170+
de: { translation: {} },
171+
},
172+
});
173+
i18n.changeLanguage("fr");
174+
expect(getSupportedLanguages()).toEqual({
175+
en: false,
176+
zh: false,
177+
fr: true,
178+
de: false,
179+
});
180+
});
181+
182+
it("应该处理当前语言不在支持列表中的情况", () => {
183+
i18n.init({
184+
supportedLngs: ["en", "zh"],
185+
resources: {
186+
en: { translation: {} },
187+
zh: { translation: {} },
188+
},
189+
});
190+
i18n.changeLanguage("fr");
191+
expect(getSupportedLanguages()).toEqual({ en: false, zh: false });
192+
});
193+
});
194+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import i18n from "i18next";
2+
3+
export interface SupportedLanguages {
4+
en: boolean;
5+
zh: boolean;
6+
[key: string]: boolean;
7+
}
8+
export function getSupportedLanguages(): SupportedLanguages {
9+
const opt = i18n?.options || {};
10+
11+
const s = opt.supportedLngs;
12+
let lngs: string[] = [];
13+
if (Array.isArray(s)) {
14+
lngs = s.filter((lng) => lng !== "cimode");
15+
}
16+
17+
if (typeof s === "string" && s) {
18+
lngs = [s];
19+
}
20+
21+
// fallback: 从 resources 中取 key
22+
if (opt.resources && typeof opt.resources === "object") {
23+
lngs = Object.keys(opt.resources);
24+
}
25+
26+
if (lngs?.length) {
27+
return Object.fromEntries(
28+
lngs.map((lng) => [[lng], i18n.language === lng])
29+
);
30+
}
31+
// fallback: 默认支持 zh
32+
return { en: false, zh: true };
33+
}

0 commit comments

Comments
 (0)