feat(fe): enable customized language pack

This commit is contained in:
hexxa 2021-10-19 15:25:35 +08:00 committed by Hexxa
parent 5a44d9f125
commit 17f68ccdec
9 changed files with 209 additions and 22 deletions

View file

@ -209,4 +209,11 @@ export class FilesClient extends BaseClient {
}, },
}); });
}; };
download = (url: string): Promise<Response> => {
return this.do({
method: "get",
url,
});
}
} }

View file

@ -26,6 +26,7 @@ export interface FilesClientResps {
listSharingsMockResp?: Response<ListSharingsResp>; listSharingsMockResp?: Response<ListSharingsResp>;
isSharingMockResp?: Response; isSharingMockResp?: Response;
generateHashMockResp?: Response; generateHashMockResp?: Response;
downloadMockResp: Response;
} }
export const resps = { export const resps = {
@ -132,6 +133,11 @@ export const resps = {
}, },
isSharingMockResp: { status: 200, statusText: "", data: {} }, isSharingMockResp: { status: 200, statusText: "", data: {} },
generateHashMockResp: { status: 200, statusText: "", data: {} }, generateHashMockResp: { status: 200, statusText: "", data: {} },
downloadMockResp: {
status: 200,
statusText: "",
data: {},
},
}; };
export class MockFilesClient { export class MockFilesClient {
@ -220,4 +226,8 @@ export class MockFilesClient {
generateHash = (filePath: string): Promise<Response> => { generateHash = (filePath: string): Promise<Response> => {
return this.wrapPromise(this.resps.generateHashMockResp); return this.wrapPromise(this.resps.generateHashMockResp);
}; };
download = (url: string): Promise<Response> => {
return this.wrapPromise(this.resps.downloadMockResp);
};
} }

View file

@ -25,6 +25,7 @@ export interface Preferences {
bg: BgConfig; bg: BgConfig;
cssURL: string; cssURL: string;
lanPackURL: string; lanPackURL: string;
lan: string;
} }
export interface User { export interface User {
id: string; id: string;
@ -131,6 +132,7 @@ export interface IFilesClient {
isSharing: (dirPath: string) => Promise<Response>; isSharing: (dirPath: string) => Promise<Response>;
listSharings: () => Promise<Response<ListSharingsResp>>; listSharings: () => Promise<Response<ListSharingsResp>>;
generateHash: (filePath: string) => Promise<Response>; generateHash: (filePath: string) => Promise<Response>;
download: (url: string) => Promise<Response>;
} }
export interface ISettingsClient { export interface ISettingsClient {

View file

@ -80,8 +80,6 @@ export class AuthPane extends React.Component<Props, State, {}> {
this.update(updater().updatePanes); this.update(updater().updatePanes);
this.update(updater().updateAdmin); this.update(updater().updateAdmin);
this.update(updater().updateUI); this.update(updater().updateUI);
updater().initLan();
this.update(updater().updateMsg); this.update(updater().updateMsg);
}); });
}; };

View file

@ -125,7 +125,16 @@ export class PaneSettings extends React.Component<Props, State, {}> {
setLan = (lan: string) => { setLan = (lan: string) => {
updater().setLan(lan); updater().setLan(lan);
this.props.update(updater().updateMsg); updater()
.syncPreferences()
.then((status: number) => {
if (status === 200) {
alertMsg(this.props.msg.pkg.get("update.ok"));
} else {
alertMsg(this.props.msg.pkg.get("update.fail"));
}
this.props.update(updater().updateMsg);
});
}; };
render() { render() {
@ -416,7 +425,6 @@ export class PaneSettings extends React.Component<Props, State, {}> {
])} ])}
/> />
</div> </div>
</div> </div>
</div> </div>
); );

View file

@ -65,8 +65,6 @@ export class StateMgr extends React.Component<Props, State, {}> {
this.update(updater().updatePanes); this.update(updater().updatePanes);
this.update(updater().updateAdmin); this.update(updater().updateAdmin);
this.update(updater().updateUI); this.update(updater().updateUI);
updater().initLan();
this.update(updater().updateMsg); this.update(updater().updateMsg);
}); });
}; };

View file

@ -27,7 +27,7 @@ import { Up } from "../worker/upload_mgr";
import { alertMsg } from "../common/env"; import { alertMsg } from "../common/env";
import { LocalStorage } from "../common/localstorage"; import { LocalStorage } from "../common/localstorage";
import { MsgPackage } from "../i18n/msger"; import { MsgPackage, isValidLanPack } from "../i18n/msger";
function getCookieLanKey(user: string) { function getCookieLanKey(user: string) {
return `qs_${user}_lan`; return `qs_${user}_lan`;
@ -359,6 +359,11 @@ export class Updater {
// init panes // init panes
return this.initPanes(); return this.initPanes();
}) })
.then(() => {
// init i18n
// TOOD: status is ignored, should return alert
return this.fetchLanPack();
})
.then(() => { .then(() => {
// init admin content // init admin content
if (this.props.login.userRole === roleAdmin) { if (this.props.login.userRole === roleAdmin) {
@ -520,25 +525,19 @@ export class Updater {
return resp.status === 200; return resp.status === 200;
}; };
initLan = () => {
const lanKey = getCookieLanKey(this.props.login.userName);
const lanSaved = LocalStorage.get(lanKey);
this.setLan(lanSaved === "" ? "en_US" : lanSaved);
};
setLan = (lan: string) => { setLan = (lan: string) => {
const lanKey = getCookieLanKey(this.props.login.userName);
switch (lan) { switch (lan) {
case "en_US": case "en_US":
this.props.msg.lan = "en_US"; this.props.msg.lan = "en_US";
this.props.msg.pkg = MsgPackage.get(lan); this.props.msg.pkg = MsgPackage.get(lan);
LocalStorage.set(lanKey, "en_US"); this.props.login.preferences.lan = "en_US";
this.props.login.preferences.lanPackURL = "";
break; break;
case "zh_CN": case "zh_CN":
this.props.msg.lan = "zh_CN"; this.props.msg.lan = "zh_CN";
this.props.msg.pkg = MsgPackage.get(lan); this.props.msg.pkg = MsgPackage.get(lan);
LocalStorage.set(lanKey, "zh_CN"); this.props.login.preferences.lan = "zh_CN";
this.props.login.preferences.lanPackURL = "";
break; break;
default: default:
alertMsg("language package not found"); alertMsg("language package not found");
@ -585,8 +584,10 @@ export class Updater {
this.props.login.preferences = { ...prefer }; this.props.login.preferences = { ...prefer };
}; };
syncPreferences = async ():Promise<number> => { syncPreferences = async (): Promise<number> => {
const resp = await this.usersClient.setPreferences(this.props.login.preferences); const resp = await this.usersClient.setPreferences(
this.props.login.preferences
);
return resp.status; return resp.status;
}; };
@ -602,6 +603,47 @@ export class Updater {
return resp.status; return resp.status;
}; };
// initLan = () => {
// const lanKey = getCookieLanKey(this.props.login.userName);
// const lanSaved = LocalStorage.get(lanKey);
// this.setLan(lanSaved === "" ? "en_US" : lanSaved);
// };
fetchLanPack = async (): Promise<number> => {
const url = this.props.login.preferences.lanPackURL;
if (url === "") {
const lan = this.props.login.preferences.lan;
if (lan == "en_US" || lan == "zh_CN") {
// fallback to build-in language pack
this.props.msg.lan = lan;
this.props.msg.pkg = MsgPackage.get(lan);
} else {
// fallback to english
this.props.msg.lan = "en_US";
this.props.msg.pkg = MsgPackage.get("en_US");
}
return 404;
}
const resp = await this.filesClient.download(url);
let isValid = true;
if (resp == null || resp.data == null) {
isValid = false;
} else if (!isValidLanPack(resp.data)) {
isValid = false;
}
if (!isValid) {
this.props.msg.lan = "en_US";
this.props.msg.pkg = MsgPackage.get("en_US");
this.props.login.preferences.lanPackURL = "";
return 400;
}
this.props.msg.lan = resp.data.lan;
this.props.msg.pkg = Map<string, string>(resp.data);
return resp.status;
};
updateBrowser = (prevState: ICoreState): ICoreState => { updateBrowser = (prevState: ICoreState): ICoreState => {
return { return {
...prevState, ...prevState,

View file

@ -68,8 +68,6 @@ export class TopBar extends React.Component<Props, State, {}> {
this.props.update(updater().updatePanes); this.props.update(updater().updatePanes);
this.props.update(updater().updateAdmin); this.props.update(updater().updateAdmin);
this.props.update(updater().updateUI); this.props.update(updater().updateUI);
updater().initLan();
this.props.update(updater().updateMsg); this.props.update(updater().updateMsg);
}); });
}; };

View file

@ -1,4 +1,4 @@
import { Map } from "immutable"; import { Map, Set } from "immutable";
import { msgs as enMsgs } from "./en_US"; import { msgs as enMsgs } from "./en_US";
import { msgs as cnMsgs } from "./zh_CN"; import { msgs as cnMsgs } from "./zh_CN";
@ -25,3 +25,127 @@ export class MsgPackage {
} }
} }
} }
export function isValidLanPack(lanPackObject: any): boolean {
const topLevelkeys = Set(Object.keys(lanPackObject));
if (!topLevelkeys.has("lan") && !topLevelkeys.has("pkg")) {
return false;
}
const gotKeys = Set(Object.keys(lanPackObject.pkg));
let missingKeys = Set<string>();
lanPackKeys.forEach((key: string) => {
if (!gotKeys.has(key)) {
missingKeys = missingKeys.add(key);
}
});
// TODO: provide better error report?
if (missingKeys.size > 0) {
console.error(missingKeys);
}
return missingKeys.size > 0;
}
export const lanPackKeys = Set<string>([
"stateMgr.cap.fail",
"browser.upload.del.fail",
"browser.folder.add.fail",
"browser.del.fail",
"browser.move.fail",
"browser.share.add.fail",
"browser.share.del.fail",
"browser.share.del",
"browser.share.add",
"browser.share.title",
"browser.share.desc",
"browser.upload.title",
"browser.upload.desc",
"browser.folder.name",
"browser.folder.add",
"browser.upload",
"browser.delete",
"browser.paste",
"browser.select",
"browser.deselect",
"browser.selectAll",
"browser.stop",
"browser.location",
"browser.item.title",
"browser.used",
"panes.close",
"login.logout.fail",
"login.username",
"login.captcha",
"login.pwd",
"login.login",
"login.logout",
"settings.pwd.notSame",
"settings.pwd.empty",
"settings.pwd.notChanged",
"update",
"settings.pwd.old",
"settings.pwd.new1",
"settings.pwd.new2",
"settings",
"settings.chooseLan",
"settings.pwd.update",
"admin",
"update.ok",
"update.fail",
"delete.fail",
"delete.ok",
"delete",
"spaceLimit",
"uploadLimit",
"downloadLimit",
"add.fail",
"add.ok",
"role.delete.warning",
"user.id",
"user.add",
"user.name",
"user.role",
"user.password",
"add",
"admin.users",
"role.add",
"role.name",
"admin.roles",
"zhCN",
"enUS",
"move.fail",
"share.404.title",
"share.404.desc",
"upload.404.title",
"upload.404.desc",
"detail",
"refresh",
"refresh-hint",
"pane.login",
"pane.admin",
"pane.settings",
"logout.confirm",
"unauthed",
"err.tooManyUploads",
"login.role",
"user.profile",
"user.downLimit",
"user.upLimit",
"user.spaceLimit",
"cfg.siteName",
"cfg.siteDesc",
"cfg.bg",
"cfg.bg.url",
"cfg.bg.repeat",
"cfg.bg.pos",
"cfg.bg.align",
"reset",
"bg.url.alert",
"bg.pos.alert",
"bg.align.alert",
"prefer.theme",
"prefer.theme.url",
"settings.customLan",
"settings.lanPackURL",
]);