From 17f68ccdec1e8b4ec983c164b74ef9135a923777 Mon Sep 17 00:00:00 2001 From: hexxa Date: Tue, 19 Oct 2021 15:25:35 +0800 Subject: [PATCH] feat(fe): enable customized language pack --- src/client/web/src/client/files.ts | 7 + src/client/web/src/client/files_mock.ts | 10 ++ src/client/web/src/client/index.ts | 2 + src/client/web/src/components/pane_login.tsx | 2 - .../web/src/components/pane_settings.tsx | 12 +- src/client/web/src/components/state_mgr.tsx | 2 - .../web/src/components/state_updater.ts | 68 ++++++++-- src/client/web/src/components/topbar.tsx | 2 - src/client/web/src/i18n/msger.ts | 126 +++++++++++++++++- 9 files changed, 209 insertions(+), 22 deletions(-) diff --git a/src/client/web/src/client/files.ts b/src/client/web/src/client/files.ts index 6940dd9..8f5019d 100644 --- a/src/client/web/src/client/files.ts +++ b/src/client/web/src/client/files.ts @@ -209,4 +209,11 @@ export class FilesClient extends BaseClient { }, }); }; + + download = (url: string): Promise => { + return this.do({ + method: "get", + url, + }); + } } diff --git a/src/client/web/src/client/files_mock.ts b/src/client/web/src/client/files_mock.ts index 20f9009..90a95f4 100644 --- a/src/client/web/src/client/files_mock.ts +++ b/src/client/web/src/client/files_mock.ts @@ -26,6 +26,7 @@ export interface FilesClientResps { listSharingsMockResp?: Response; isSharingMockResp?: Response; generateHashMockResp?: Response; + downloadMockResp: Response; } export const resps = { @@ -132,6 +133,11 @@ export const resps = { }, isSharingMockResp: { status: 200, statusText: "", data: {} }, generateHashMockResp: { status: 200, statusText: "", data: {} }, + downloadMockResp: { + status: 200, + statusText: "", + data: {}, + }, }; export class MockFilesClient { @@ -220,4 +226,8 @@ export class MockFilesClient { generateHash = (filePath: string): Promise => { return this.wrapPromise(this.resps.generateHashMockResp); }; + + download = (url: string): Promise => { + return this.wrapPromise(this.resps.downloadMockResp); + }; } diff --git a/src/client/web/src/client/index.ts b/src/client/web/src/client/index.ts index 4ac4750..cf72436 100644 --- a/src/client/web/src/client/index.ts +++ b/src/client/web/src/client/index.ts @@ -25,6 +25,7 @@ export interface Preferences { bg: BgConfig; cssURL: string; lanPackURL: string; + lan: string; } export interface User { id: string; @@ -131,6 +132,7 @@ export interface IFilesClient { isSharing: (dirPath: string) => Promise; listSharings: () => Promise>; generateHash: (filePath: string) => Promise; + download: (url: string) => Promise; } export interface ISettingsClient { diff --git a/src/client/web/src/components/pane_login.tsx b/src/client/web/src/components/pane_login.tsx index 688dd20..5c49e14 100644 --- a/src/client/web/src/components/pane_login.tsx +++ b/src/client/web/src/components/pane_login.tsx @@ -80,8 +80,6 @@ export class AuthPane extends React.Component { this.update(updater().updatePanes); this.update(updater().updateAdmin); this.update(updater().updateUI); - - updater().initLan(); this.update(updater().updateMsg); }); }; diff --git a/src/client/web/src/components/pane_settings.tsx b/src/client/web/src/components/pane_settings.tsx index 577f045..8df35b6 100644 --- a/src/client/web/src/components/pane_settings.tsx +++ b/src/client/web/src/components/pane_settings.tsx @@ -125,7 +125,16 @@ export class PaneSettings extends React.Component { setLan = (lan: string) => { 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() { @@ -416,7 +425,6 @@ export class PaneSettings extends React.Component { ])} /> - ); diff --git a/src/client/web/src/components/state_mgr.tsx b/src/client/web/src/components/state_mgr.tsx index 1293d3c..5fc25a5 100644 --- a/src/client/web/src/components/state_mgr.tsx +++ b/src/client/web/src/components/state_mgr.tsx @@ -65,8 +65,6 @@ export class StateMgr extends React.Component { this.update(updater().updatePanes); this.update(updater().updateAdmin); this.update(updater().updateUI); - - updater().initLan(); this.update(updater().updateMsg); }); }; diff --git a/src/client/web/src/components/state_updater.ts b/src/client/web/src/components/state_updater.ts index 093d5fa..15abc15 100644 --- a/src/client/web/src/components/state_updater.ts +++ b/src/client/web/src/components/state_updater.ts @@ -27,7 +27,7 @@ import { Up } from "../worker/upload_mgr"; import { alertMsg } from "../common/env"; import { LocalStorage } from "../common/localstorage"; -import { MsgPackage } from "../i18n/msger"; +import { MsgPackage, isValidLanPack } from "../i18n/msger"; function getCookieLanKey(user: string) { return `qs_${user}_lan`; @@ -359,6 +359,11 @@ export class Updater { // init panes return this.initPanes(); }) + .then(() => { + // init i18n + // TOOD: status is ignored, should return alert + return this.fetchLanPack(); + }) .then(() => { // init admin content if (this.props.login.userRole === roleAdmin) { @@ -520,25 +525,19 @@ export class Updater { 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) => { - const lanKey = getCookieLanKey(this.props.login.userName); - switch (lan) { case "en_US": this.props.msg.lan = "en_US"; 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; case "zh_CN": this.props.msg.lan = "zh_CN"; 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; default: alertMsg("language package not found"); @@ -585,8 +584,10 @@ export class Updater { this.props.login.preferences = { ...prefer }; }; - syncPreferences = async ():Promise => { - const resp = await this.usersClient.setPreferences(this.props.login.preferences); + syncPreferences = async (): Promise => { + const resp = await this.usersClient.setPreferences( + this.props.login.preferences + ); return resp.status; }; @@ -602,6 +603,47 @@ export class Updater { 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 => { + 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(resp.data); + return resp.status; + }; + updateBrowser = (prevState: ICoreState): ICoreState => { return { ...prevState, diff --git a/src/client/web/src/components/topbar.tsx b/src/client/web/src/components/topbar.tsx index cf67779..00c8c32 100644 --- a/src/client/web/src/components/topbar.tsx +++ b/src/client/web/src/components/topbar.tsx @@ -68,8 +68,6 @@ export class TopBar extends React.Component { this.props.update(updater().updatePanes); this.props.update(updater().updateAdmin); this.props.update(updater().updateUI); - - updater().initLan(); this.props.update(updater().updateMsg); }); }; diff --git a/src/client/web/src/i18n/msger.ts b/src/client/web/src/i18n/msger.ts index 3538f6f..30077d9 100644 --- a/src/client/web/src/i18n/msger.ts +++ b/src/client/web/src/i18n/msger.ts @@ -1,4 +1,4 @@ -import { Map } from "immutable"; +import { Map, Set } from "immutable"; import { msgs as enMsgs } from "./en_US"; 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(); + 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([ + "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", +]);