Skip to content

Commit

Permalink
feat(web): 允许用户指定系统语言 (#935)
Browse files Browse the repository at this point in the history
### 做了什么

在`common.yml`中增加可选配置项`systemLanguage`
```
# 设置系统语言 可选配置 类型为对象或字符串,默认值为对象类型
# 1.systemLanguage对象类型
# systemLanguage:
#   # 可选,默认为true。
#   # 如果true,则SCOW在用户未手动选择语言时,自动header判断语言,如判断失败使用default语言
#   # 如果为false,则SCOW首次进入系统时使用下方配置的default语言
#   autoDetectWhenUserNotSet: true
#   # 默认语言,默认为"zh_cn"
#   # 类型必须为当前系统合法语言["zh_cn","en"]的字符串枚举值
#   default: "zh_cn"

# 2.systemLanguage字符串类型
# 若systemLanguage配置为字符串,类型必须指定为当前系统合法语言["zh_cn","en"]的字符串枚举值
# SCOW直接使用此语言,不允许用户再进行语言切换
# systemLanguage: "zh_cn"
```

1.当配置为字符串类型时 `systemLanguage: "zh_cn"`
隐藏语言切换器,进入系统后设置语言cookie为“zh_cn”不再变更,系统一直使用中文显示

2.当配置为对象类型
```
systemLanguage:
  autoDetectWhenUserNotSet: false
  default: "zh_cn"
```
显示语言切换器。如果用户手动选择了语言,则一直使用用户选择的语言。否则,当用户没有选择过自己的语言时,语言为配置的"zh_cn"

3.当不配置或配置为对象类型
```
systemLanguage:
 autoDetectWhenUserNotSet: true 或不配置此项时
  default: "zh_cn"
```

显示语言切换器,如果用户手动选择了语言,则一直使用用户选择的语言。否则,当用户没有选择过自己的语言时,SCOW根据用户的accept-language
header判断用户浏览器或者系统所使用的语言。如果判断出来为SCOW支持的语言,则使用此语言,否则,使用配置的default语言"zh_cn"


**systemLanguage为对象,或systemLanguage为字符串时,配置的语言不在系统合法语言列表的枚举值中时,系统报错,无法启动**

![image](https://github.com/PKUHPC/SCOW/assets/43978285/3d23c593-1316-454d-99ea-b311c170663a)
  • Loading branch information
piccaSun authored Nov 13, 2023
1 parent af6a53d commit 5d2b75c
Show file tree
Hide file tree
Showing 74 changed files with 682 additions and 334 deletions.
6 changes: 6 additions & 0 deletions .changeset/eight-trees-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@scow/config": minor
"@scow/cli": minor
---

在 common.yml 中增加可选配置项 systemLanguage,指定的语言必须为系统当前合法语言["zh_cn", "en"]的枚举值,允许用户指定系统唯一语言不再进行语言切换,或允许用户指定进入 SCOW 时的默认语言
9 changes: 9 additions & 0 deletions .changeset/rotten-weeks-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@scow/portal-web": minor
"@scow/mis-web": minor
"@scow/lib-server": minor
"@scow/auth": minor
"@scow/lib-web": minor
---

增加用户指定系统语言功能,可以指定系统唯一语言不再进行语言切换,也可以指定进入 SCOW 时的默认初始语言
26 changes: 26 additions & 0 deletions apps/auth/config/common.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 创建用户、修改密码时,密码的规则。必须设置
passwordPattern:
# 正则表达式。下面为默认值
regex: ^(?=.*\d)(?=.*[a-zA-Z])(?=.*[`~!@#\$%^&*()_+\-[\];',./{}|:"<>?]).{8,}$

# 出错时的消息。下面为默认值
errorMessage: 必须包含字母、数字和符号,长度大于等于8位


# 设置系统语言 可选配置 类型为对象或字符串,默认值为对象类型
# 1.systemLanguage对象类型
# systemLanguage:
# # 可选,默认为true。
# # 如果true,则SCOW在用户未手动选择语言时,自动优先根据cookies, 其次根据浏览器header判断语言,判断失败使用下方配置的default语言。
# # 如果为false,则SCOW在首次进入系统用户未手动选择语言时使用下方配置的default语言,
# # 用户手动选择过语言之后优先从cookies中进行判断,cookies不存在合法语言信息则使用下方配置的默认语言。
# autoDetectWhenUserNotSet: true
# # 默认语言,选填。
# # 类型必须为当前系统合法语言["zh_cn","en"]的字符串枚举值
# # 若没有配置,则默认为"zh_cn"
# default: "zh_cn"

# 2.systemLanguage字符串类型
# 若systemLanguage配置为字符串,类型必须指定为当前系统合法语言["zh_cn","en"]的字符串枚举值
# SCOW直接使用此语言,不允许用户再进行语言切换
# systemLanguage: "zh_cn"
5 changes: 3 additions & 2 deletions apps/auth/src/auth/bindOtpHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
* See the Mulan PSL v2 for more details.
*/

import { getCommonConfig, getSystemLanguageConfig } from "@scow/config/build/common";
import { DEFAULT_PRIMARY_COLOR } from "@scow/config/build/ui";
import { getLanguageCookie } from "@scow/lib-server";
import { getCurrentLanguageId } from "@scow/lib-server";
import { FastifyReply, FastifyRequest } from "fastify";
import { join } from "path";
import { config, FAVICON_URL } from "src/config/env";
Expand All @@ -37,7 +38,7 @@ export async function renderBindOtpHtml(
const hostname = getHostname(req);

// 获取当前语言ID及对应的绑定OTP页面文本
const languageId = getLanguageCookie(req.raw);
const languageId = getCurrentLanguageId(req.raw, getSystemLanguageConfig(getCommonConfig().systemLanguage));
const authTexts: AuthTextsType = languages[languageId];

return rep.status(err ? 401 : 200).view("/otp/bindOtp.liquid", {
Expand Down
5 changes: 3 additions & 2 deletions apps/auth/src/auth/loginHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
* See the Mulan PSL v2 for more details.
*/

import { getCommonConfig, getSystemLanguageConfig } from "@scow/config/build/common";
import { DEFAULT_PRIMARY_COLOR } from "@scow/config/build/ui";
import { getI18nConfigCurrentText, getLanguageCookie } from "@scow/lib-server/build/i18n";
import { getCurrentLanguageId, getI18nConfigCurrentText } from "@scow/lib-server";
import { FastifyReply, FastifyRequest } from "fastify";
import { join } from "path";
import { createCaptcha } from "src/auth/captcha";
Expand Down Expand Up @@ -44,7 +45,7 @@ export async function serveLoginHtml(
: undefined;

// 获取当前语言ID及对应的登录页面文本
const languageId = getLanguageCookie(req.raw);
const languageId = getCurrentLanguageId(req.raw, getSystemLanguageConfig(getCommonConfig().systemLanguage));
const authTexts: AuthTextsType = languages[languageId];

// 获取sloganI18nText
Expand Down
12 changes: 7 additions & 5 deletions apps/auth/src/config/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export enum ScowLogoType {
}

// 创建配置文件中显示文字项的配置类型
export const createI18nStringSchema = (description: string, defaultValue?: string) => {
export const createI18nStringSchema = ({ description, defaultValue }: {description: string, defaultValue?: string}) => {
return Type.Union([
Type.String(),
Type.Object({
Expand Down Expand Up @@ -181,8 +181,9 @@ export const UiConfigSchema = Type.Object({
}, { default: {} }),
slogan: Type.Object({
color: Type.String({ description: "默认标语文字颜色", default: "white" }),
title: createI18nStringSchema("默认标语标题", ""),
texts: Type.Array(createI18nStringSchema(""), { description: "默认 slogan 正文数组", default: []}),
title: createI18nStringSchema({ description: "默认标语标题", defaultValue: "" }),
texts: Type.Array(createI18nStringSchema({ description: "默认 slogan 正文", defaultValue: "" })
, { description: "默认 slogan 正文数组", default: []}),
}, { default: {} }),
footerTextColor: Type.String({ description: "默认 footer 文字颜色", default: "white" }),
}),
Expand All @@ -196,8 +197,9 @@ export const UiConfigSchema = Type.Object({
})),
slogan: Type.Optional(Type.Object({
color: Type.String({ description: "默认标语文字颜色" }),
title: createI18nStringSchema("默认标语标题", ""),
texts: Type.Array(createI18nStringSchema(""), { description: "默认 slogan 正文数组" }),
title: createI18nStringSchema({ description: "默认标语标题", defaultValue: "" }),
texts: Type.Array(createI18nStringSchema({ description: "默认 slogan 正文", defaultValue: "" }),
{ description: "默认 slogan 正文数组" }),
})),
footerTextColor: Type.Optional(Type.String({ description: "默认 footer 文字颜色" })),
}))),
Expand Down
20 changes: 20 additions & 0 deletions apps/cli/assets/config/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,23 @@ passwordPattern:
# 出错时的消息。下面为默认值
# 此文本支持国际化
errorMessage: 必须包含字母、数字和符号,长度大于等于8位


# 设置系统语言 可选配置 类型为对象或字符串,默认值为对象类型
# 1.systemLanguage对象类型
# systemLanguage:
# # 可选,默认为true。
# # 如果true,则SCOW在用户未手动选择语言时,自动优先根据cookies, 其次根据浏览器header判断语言,判断失败使用下方配置的default语言。
# # 如果为false,则SCOW在首次进入系统用户未手动选择语言时使用下方配置的default语言,
# # 用户手动选择过语言之后优先从cookies中进行判断,cookies不存在合法语言信息则使用下方配置的默认语言。
# autoDetectWhenUserNotSet: true
# # 默认语言,选填。
# # 类型必须为当前系统合法语言["zh_cn","en"]的字符串枚举值
# # 若没有配置,则默认为"zh_cn"
# default: "zh_cn"

# 2.systemLanguage字符串类型
# 若systemLanguage配置为字符串,类型必须指定为当前系统合法语言["zh_cn","en"]的字符串枚举值
# SCOW直接使用此语言,不允许用户再进行语言切换
# systemLanguage: "zh_cn"

20 changes: 20 additions & 0 deletions apps/mis-server/config/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,23 @@ passwordPattern:

# 出错时的消息。下面为默认值
errorMessage: 必须包含字母、数字和符号,长度大于等于8位


# 设置系统语言 可选配置 类型为对象或字符串,默认值为对象类型
# 1.systemLanguage对象类型
# systemLanguage:
# # 可选,默认为true。
# # 如果true,则SCOW在用户未手动选择语言时,自动优先根据cookies, 其次根据浏览器header判断语言,判断失败使用下方配置的default语言。
# # 如果为false,则SCOW在首次进入系统用户未手动选择语言时使用下方配置的default语言,
# # 用户手动选择过语言之后优先从cookies中进行判断,cookies不存在合法语言信息则使用下方配置的默认语言。
# autoDetectWhenUserNotSet: true
# # 默认语言,选填。
# # 类型必须为当前系统合法语言["zh_cn","en"]的字符串枚举值
# # 若没有配置,则默认为"zh_cn"
# default: "zh_cn"

# 2.systemLanguage字符串类型
# 若systemLanguage配置为字符串,类型必须指定为当前系统合法语言["zh_cn","en"]的字符串枚举值
# SCOW直接使用此语言,不允许用户再进行语言切换
# systemLanguage: "zh_cn"

7 changes: 6 additions & 1 deletion apps/mis-web/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
const { envConfig, str, bool } = require("@scow/lib-config");
const { getClusterConfigs, getSortedClusterIds } = require("@scow/config/build/cluster");
const { getMisConfig } = require("@scow/config/build/mis");
const { getCommonConfig } = require("@scow/config/build/common");
const { getCommonConfig, getSystemLanguageConfig } = require("@scow/config/build/common");
const { getClusterTextsConfig } = require("@scow/config/build/clusterTexts");
const { DEFAULT_PRIMARY_COLOR, getUiConfig } = require("@scow/config/build/ui");
const { getAuditConfig } = require("@scow/config/build/audit");
Expand Down Expand Up @@ -94,6 +94,9 @@ const buildRuntimeConfig = async (phase, basePath) => {

const versionTag = readVersionFile()?.tag;

const systemLanguageConfig = getSystemLanguageConfig(getCommonConfig().systemLanguage);


/**
* @type {import ("./src/utils/config").ServerRuntimeConfig}
*/
Expand Down Expand Up @@ -161,6 +164,8 @@ const buildRuntimeConfig = async (phase, basePath) => {
...(misConfig.customChargeTypes || []),
],

SYSTEM_LANGUAGE_CONFIG: systemLanguageConfig,

};

if (!building) {
Expand Down
19 changes: 19 additions & 0 deletions apps/mis-web/config/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,22 @@ passwordPattern:

# 出错时的消息。下面为默认值
errorMessage: 必须包含字母、数字和符号,长度大于等于8位


# 设置系统语言 可选配置 类型为对象或字符串,默认值为对象类型
# 1.systemLanguage对象类型
# systemLanguage:
# # 可选,默认为true。
# # 如果true,则SCOW在用户未手动选择语言时,自动优先根据cookies, 其次根据浏览器header判断语言,判断失败使用下方配置的default语言。
# # 如果为false,则SCOW在首次进入系统用户未手动选择语言时使用下方配置的default语言,
# # 用户手动选择过语言之后优先从cookies中进行判断,cookies不存在合法语言信息则使用下方配置的默认语言。
# autoDetectWhenUserNotSet: true
# # 默认语言,选填。
# # 类型必须为当前系统合法语言["zh_cn","en"]的字符串枚举值
# # 若没有配置,则默认为"zh_cn"
# default: "zh_cn"

# 2.systemLanguage字符串类型
# 若systemLanguage配置为字符串,类型必须指定为当前系统合法语言["zh_cn","en"]的字符串枚举值
# SCOW直接使用此语言,不允许用户再进行语言切换
# systemLanguage: "zh_cn"
2 changes: 1 addition & 1 deletion apps/mis-web/src/components/ClusterSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/

import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { Select } from "antd";
import { prefix, useI18n, useI18nTranslateToString } from "src/i18n";
import { Cluster, publicConfig } from "src/utils/config";
Expand Down
2 changes: 1 addition & 1 deletion apps/mis-web/src/components/JobBillingTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/

import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { Table } from "antd";
import { ColumnsType } from "antd/es/table";
import { prefix, useI18n, useI18nTranslateToString } from "src/i18n";
Expand Down
15 changes: 9 additions & 6 deletions apps/mis-web/src/components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
* See the Mulan PSL v2 for more details.
*/

import { SYSTEM_DEFAULT_LANGUAGE } from "@scow/lib-web/build/utils/languages";
import { Select } from "antd";
import { useRouter } from "next/router";
import { setCookie } from "nookies";
Expand All @@ -23,7 +22,11 @@ const Container = styled.div`
white-space: nowrap;
`;

export const LanguageSwitcher = () => {
interface LanguageSwitcherProps {
initialLanguage: string;
}

export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({ initialLanguage }) => {

const [selectedLanguage, setSelectedLanguage] = useState("");

Expand All @@ -32,11 +35,11 @@ export const LanguageSwitcher = () => {
const router = useRouter();

useEffect(() => {
const initialLanguage = i18n.currentLanguage.id;
if (initialLanguage) {
setSelectedLanguage(initialLanguage);
const init = i18n.currentLanguage.id;
if (init) {
setSelectedLanguage(init);
} else {
const defaultLanguage = SYSTEM_DEFAULT_LANGUAGE;
const defaultLanguage = initialLanguage;
setSelectedLanguage(defaultLanguage);
setLanguageCookie(defaultLanguage);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/mis-web/src/layouts/AntdConfigProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

import "dayjs/locale/zh-cn";

import { SYSTEM_VALID_LANGUAGES } from "@scow/config/build/i18n";
import { AntdConfigProvider as LibAntdConfigProvider } from "@scow/lib-web/build/layouts/AntdConfigProvider";
import { useDarkMode } from "@scow/lib-web/build/layouts/darkMode";
import { SYSTEM_VALID_LANGUAGES } from "@scow/lib-web/build/utils/languages";
import { App, ConfigProvider, theme } from "antd";
import { Locale } from "antd/lib/locale";
import enUSlocale from "antd/locale/en_US";
Expand Down
12 changes: 10 additions & 2 deletions apps/mis-web/src/layouts/BaseLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ import { publicConfig } from "src/utils/config";
interface Props {
footerText: string;
versionTag: string | undefined;
initialLanguage: string;
}

export const BaseLayout = ({ footerText, versionTag, children }: PropsWithChildren<Props>) => {
export const BaseLayout =
({ footerText, versionTag, initialLanguage, children }: PropsWithChildren<Props>) => {

const userStore = useStore(UserStore);

const t = useI18nTranslateToString();
const languageId = useI18n().currentLanguage.id;

const systemLanguageConfig = publicConfig.SYSTEM_LANGUAGE_CONFIG;

const routes = useMemo(() => getAvailableRoutes(userStore.user, t), [userStore.user, t]);

return (
Expand All @@ -54,7 +58,11 @@ export const BaseLayout = ({ footerText, versionTag, children }: PropsWithChildr
link={publicConfig.PORTAL_URL}
linkText={t("layouts.route.navLinkText")}
/>
<LanguageSwitcher />
{
systemLanguageConfig.isUsingI18n ? (
<LanguageSwitcher initialLanguage={initialLanguage} />
) : undefined
}
</>
)}
>
Expand Down
2 changes: 1 addition & 1 deletion apps/mis-web/src/pageComponents/dashboard/StorageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import { ReloadOutlined } from "@ant-design/icons";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { Progress, Space } from "antd";
import React, { useCallback } from "react";
import { useAsync } from "react-async";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import { arrayContainsElement } from "@scow/lib-web/build/utils/array";
import { parseMinutes, TimeUnits } from "@scow/lib-web/build/utils/datetime";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { App, Divider, Form, InputNumber, Modal, Progress, Select } from "antd";
import { useRef, useState } from "react";
import { api } from "src/apis";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import { numberToMoney } from "@scow/lib-decimal";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { App, Form, Input, InputNumber, Modal, Select, Space, Table } from "antd";
import { ColumnsType } from "antd/es/table";
import { useState } from "react";
Expand Down
2 changes: 1 addition & 1 deletion apps/mis-web/src/pageComponents/job/RunningJobTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import { useDidUpdateEffect } from "@scow/lib-web/build/utils/hooks";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/i18n";
import { getI18nConfigCurrentText } from "@scow/lib-web/build/utils/systemLanguage";
import { Button, Form, Input, InputNumber, message, Popconfirm, Select, Space, Table } from "antd";
import React, { useCallback, useMemo, useRef, useState } from "react";
import { useAsync } from "react-async";
Expand Down
Loading

0 comments on commit 5d2b75c

Please sign in to comment.