Skip to content

Commit

Permalink
feat: add partial i18n support
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Mar 18, 2024
1 parent 7a0ee05 commit def516b
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 7 deletions.
6 changes: 4 additions & 2 deletions libs/Universal-PicGo-Core/package.json
Expand Up @@ -30,8 +30,10 @@
"vite-plugin-node-polyfills": "^0.21.0"
},
"dependencies": {
"zhi-lib-base": "^0.8.0",
"universal-picgo-store": "workspace:*"
"@picgo/i18n": "^1.0.0",
"js-yaml": "^4.1.0",
"universal-picgo-store": "workspace:*",
"zhi-lib-base": "^0.8.0"
},
"publishConfig": {
"access": "public"
Expand Down
17 changes: 16 additions & 1 deletion libs/Universal-PicGo-Core/src/core/UniversalPicGo.ts
Expand Up @@ -30,6 +30,8 @@ import { IBuildInEvent } from "../utils/enums"
import DB from "../utils/db"
import { hasNodeEnv, win } from "universal-picgo-store"
import { ensureFileSync, pathExistsSync } from "../utils/nodeUtils"
import { I18nManager } from "../i18n"
import { getBrowserDirectoryPath } from "../utils/browserUtils"

/*
* 通用 PicGO 对象定义
Expand Down Expand Up @@ -223,7 +225,11 @@ class UniversalPicGo extends EventEmitter implements IPicGo {
}
} else {
if (this.configPath === "") {
this.baseDir = ""
this.configPath = `universal-picgo-config.json`
} else {
// 模拟 path.dirname 的功能,获取路径的目录部分赋值给 baseDir
this.baseDir = getBrowserDirectoryPath(this.configPath)
}
}
}
Expand All @@ -233,7 +239,16 @@ class UniversalPicGo extends EventEmitter implements IPicGo {
this._config = this.db.read(true) as IConfig
}

private init(): void {}
private init(): void {
try {
// init 18n at first
this.i18n = new I18nManager(this)
} catch (e: any) {
this.emit(IBuildInEvent.UPLOAD_PROGRESS, -1)
this.log.error(e)
throw e
}
}
}

export { UniversalPicGo }
118 changes: 118 additions & 0 deletions libs/Universal-PicGo-Core/src/i18n/en.ts
@@ -0,0 +1,118 @@
import { ILocales } from "./zh-CN"

/* eslint-disable no-template-curly-in-string */
export const EN: ILocales = {
UPLOAD_FAILED: "Upload failed",
CHECK_SETTINGS: "Please check your settings",
CHECK_SETTINGS_AND_NETWORK: "Please check your settings and network",
UPLOAD_FAILED_REASON: "Error code: ${code}, please open the browser and paste the address to see the reason",
SERVER_ERROR: "Server error, please try again later",
AUTH_FAILED: "Authentication failed",

// smms
PICBED_SMMS: "SM.MS",
PICBED_SMMS_TOKEN: "Set Token",
PICBED_SMMS_BACKUP_DOMAIN: "Set Backup Upload Domain",
PICBED_SMMS_MESSAGE_BACKUP_DOMAIN: "Ex. smms.app",

// Ali-cloud
PICBED_ALICLOUD: "Ali Cloud",
PICBED_ALICLOUD_ACCESSKEYID: "Set KeyId",
PICBED_ALICLOUD_ACCESSKEYSECRET: "Set KeySecret",
PICBED_ALICLOUD_BUCKET: "Set Bucket",
PICBED_ALICLOUD_AREA: "Set Area",
PICBED_ALICLOUD_PATH: "Set Path",
PICBED_ALICLOUD_CUSTOMURL: "Set Custom URL",
PICBED_ALICLOUD_OPTIONS: "Set URL Suffix",
PICBED_ALICLOUD_MESSAGE_AREA: "Ex. oss-cn-beijing",
PICBED_ALICLOUD_MESSAGE_PATH: "Ex. test/",
PICBED_ALICLOUD_MESSAGE_OPTIONS: "Ex. ?x-oss-process=xxx",
PICBED_ALICLOUD_MESSAGE_CUSTOMURL: "Ex. https://test.com",

// Tencent-cloud
PICBED_TENCENTCLOUD: "Tencent Cloud",
PICBED_TENCENTCLOUD_VERSION: "Choose COS version",
PICBED_TENCENTCLOUD_SECRETID: "Set SecretId",
PICBED_TENCENTCLOUD_SECRETKEY: "Set SecretKey",
PICBED_TENCENTCLOUD_APPID: "Set AppId",
PICBED_TENCENTCLOUD_BUCKET: "Set Bucket",
PICBED_TENCENTCLOUD_AREA: "Set Area",
PICBED_TENCENTCLOUD_ENDPOINT: "Set Endpoint",
PICBED_TENCENTCLOUD_PATH: "Set Path",
PICBED_TENCENTCLOUD_OPTIONS: "Set URL Suffix",
PICBED_TENCENTCLOUD_SLIM: "Set ImageSlim",
PICBED_TENCENTCLOUD_SLIM_TIP:
"Image extremely intelligent compression, please refer to the [document description](https://cloud.tencent.com/document/product/436/49259)",
PICBED_TENCENTCLOUD_SLIM_CONFIRM: "OPEN",
PICBED_TENCENTCLOUD_SLIM_CANCEL: "CLOSE",
PICBED_TENCENTCLOUD_CUSTOMURL: "Set Custom URL",
PICBED_TENCENTCLOUD_MESSAGE_APPID: "Ex. 1234567890",
PICBED_TENCENTCLOUD_MESSAGE_AREA: "Ex. ap-beijing",
PICBED_TENCENTCLOUD_MESSAGE_ENDPOINT: "Ex. cos-internal.accelerate.tencentcos.cn",
PICBED_TENCENTCLOUD_MESSAGE_PATH: "Ex. test/",
PICBED_TENCENTCLOUD_MESSAGE_CUSTOMURL: "Ex. http://test.com",
PICBED_TENCENTCLOUD_MESSAGE_OPTIONS: "Ex. ?imageMogr2",

// GitHub
PICBED_GITHUB: "GitHub",
PICBED_GITHUB_TOKEN: "Set Token",
PICBED_GITHUB_REPO: "Set Repo Name",
PICBED_GITHUB_PATH: "Set Path",
PICBED_GITHUB_BRANCH: "Set Branch",
PICBED_GITHUB_CUSTOMURL: "Set Custom URL",
PICBED_GITHUB_MESSAGE_REPO: "Ex. username/repo",
PICBED_GITHUB_MESSAGE_BRANCH: "Ex. main",
PICBED_GITHUB_MESSAGE_PATH: "Ex. test/",
PICBED_GITHUB_MESSAGE_CUSTOMURL: "Ex. https://test.com",

// qiniu
PICBED_QINIU: "Qiniu",
PICBED_QINIU_ACCESSKEY: "Set AccessKey",
PICBED_QINIU_SECRETKEY: "Set SecretKey",
PICBED_QINIU_BUCKET: "Set Bucket",
PICBED_QINIU_PATH: "Set Path",
PICBED_QINIU_URL: "Set URL",
PICBED_QINIU_OPTIONS: "Set URL Suffix",
PICBED_QINIU_AREA: "Set Area",
PICBED_QINIU_MESSAGE_PATH: "Ex. test/",
PICBED_QINIU_MESSAGE_AREA: "Ex. z0",
PICBED_QINIU_MESSAGE_OPTIONS: "Ex. ?imageslim",
PICBED_QINIU_MESSAGE_URL: "Ex. https://xxx.yyy.glb.clouddn.com",

// imgur
PICBED_IMGUR: "Imgur",
PICBED_IMGUR_CLIENTID: "Set ClientId",
PICBED_IMGUR_PROXY: "Set Proxy",
PICBED_IMGUR_MESSAGE_PROXY: "Ex. http://127.0.0.1:1080",

// upyun
PICBED_UPYUN: "Upyun",
PICBED_UPYUN_BUCKET: "Set Bucket",
PICBED_UPYUN_OPERATOR: "Set Operator",
PICBED_UPYUN_PASSWORD: "Set Operator Password",
PICBED_UPYUN_PATH: "Set Path",
PICBED_UPYUN_URL: "Set URL",
PICBED_UPYUN_OPTIONS: "Set URL Suffix",
PICBED_UPYUN_MESSAGE_OPERATOR: "Ex. me",
PICBED_UPYUN_MESSAGE_PASSWORD: "Please type the operator password",
PICBED_UPYUN_MESSAGE_URL: "Ex. http://xxx.test.upcdn.net",
PICBED_UPYUN_MESSAGE_OPTIONS: "Ex. !imgslim",
PICBED_UPYUN_MESSAGE_PATH: "Ex. test/",

// Plugin Handler
PLUGIN_HANDLER_PLUGIN_INSTALL_SUCCESS: "Plugin installed successfully",
PLUGIN_HANDLER_PLUGIN_INSTALL_FAILED: "Plugin installation failed",
PLUGIN_HANDLER_PLUGIN_INSTALL_FAILED_REASON:
"Plugin installation failed, error code is ${code}, error log is \n ${data}",
PLUGIN_HANDLER_PLUGIN_INSTALL_FAILED_PATH:
"Plugin installation failed, please enter a valid plugin name or valid installation path",
PLUGIN_HANDLER_PLUGIN_UNINSTALL_SUCCESS: "Plugin uninstalled successfully",
PLUGIN_HANDLER_PLUGIN_UNINSTALL_FAILED: "Plugin uninstall failed",
PLUGIN_HANDLER_PLUGIN_UNINSTALL_FAILED_REASON:
"Plugin uninstall failed, error code is ${code}, error log is \n ${data}",
PLUGIN_HANDLER_PLUGIN_UNINSTALL_FAILED_VALID: "Plugin uninstall failed, please enter a valid plugin name",
PLUGIN_HANDLER_PLUGIN_UPDATE_SUCCESS: "Plugin updated successfully",
PLUGIN_HANDLER_PLUGIN_UPDATE_FAILED: "Plugin update failed",
PLUGIN_HANDLER_PLUGIN_UPDATE_FAILED_REASON: "Plugin update failed, error code is ${code}, error log is \n ${data}",
PLUGIN_HANDLER_PLUGIN_UPDATE_FAILED_VALID: "Plugin update failed, please enter a valid plugin name",
}
123 changes: 123 additions & 0 deletions libs/Universal-PicGo-Core/src/i18n/index.ts
@@ -0,0 +1,123 @@
/*
* GNU GENERAL PUBLIC LICENSE
* Version 3, 29 June 2007
*
* Copyright (C) 2024 Terwer, Inc. <https://terwer.space/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/

import { II18nManager, ILocale, IPicGo, IStringKeyMap } from "../types"
import { ObjectAdapter, I18n } from "@picgo/i18n"
import { ZH_CN, ILocalesKey, ILocales } from "./zh-CN"
import { EN } from "./en"
import { ZH_TW } from "./zh-TW"
import yaml from "js-yaml"
import _ from "lodash-es"
import { hasNodeEnv, win } from "universal-picgo-store"
import { ensureFileSync, pathExistsSync } from "../utils/nodeUtils"
import { browserPathJoin } from "../utils/browserUtils"

const languageList: IStringKeyMap<IStringKeyMap<string>> = {
"zh-CN": ZH_CN,
"zh-TW": ZH_TW,
en: EN,
}

class I18nManager implements II18nManager {
private readonly i18n: I18n
private readonly objectAdapter: ObjectAdapter
private readonly ctx: IPicGo
constructor(ctx: IPicGo) {
this.ctx = ctx
this.objectAdapter = new ObjectAdapter(languageList)
let language = this.ctx.getConfig<string>("settings.language") || "zh-CN"
if (!languageList[language]) {
language = "zh-CN" // use default
}
this.i18n = new I18n({
adapter: this.objectAdapter,
defaultLanguage: language,
})
this.loadOutterI18n()
}

translate<T extends string>(key: ILocalesKey | T, args?: IStringKeyMap<string>): string {
return this.i18n.translate(key, args) || key
}

setLanguage(language: string): void {
this.i18n.setLanguage(language)
this.ctx.saveConfig({
"settings.language": language,
})
}

addLocale(language: string, locales: ILocale): boolean {
const originLocales = this.objectAdapter.getLocale(language)
if (!originLocales) {
return false
}
const newLocales = _.merge(originLocales, locales)
this.objectAdapter.setLocale(language, newLocales)
return true
}

addLanguage(language: string, locales: ILocale): boolean {
const originLocales = this.objectAdapter.getLocale(language)
if (originLocales) {
return false
}
this.objectAdapter.setLocale(language, locales)
languageList[language] = locales
return true
}

getLanguageList(): string[] {
return Object.keys(languageList)
}

// ===================================================================================================================
private getOutterI18nFolder(): string {
let i18nFolder: string
if (hasNodeEnv) {
const fs = win.fs
const path = win.require("path")
i18nFolder = path.join(this.ctx.baseDir, "i18n-cli")
if (!pathExistsSync(fs, path, i18nFolder)) {
ensureFileSync(fs, path, i18nFolder)
}
} else {
i18nFolder = browserPathJoin(this.ctx.baseDir, "i18n-cli")
}

return i18nFolder
}

private loadOutterI18n(): void {
const i18nFolder = this.getOutterI18nFolder()
if (hasNodeEnv) {
const fs = win.fs
const path = win.require("path")
const files = fs.readdirSync(i18nFolder, {
withFileTypes: true,
})
files.forEach((file: any) => {
if (file.isFile() && file.name.endsWith(".yml")) {
const i18nFilePath = path.join(i18nFolder, file.name)
const i18nFile = fs.readFileSync(i18nFilePath, "utf8")
try {
const i18nFileObj = yaml.load(i18nFile) as ILocales
languageList[file.name.replace(/\.yml$/, "")] = i18nFileObj
} catch (e) {
console.error(e)
}
}
})
} else {
throw new Error("load outer i18n is not support in browser")
}
}
}

export { I18nManager }

0 comments on commit def516b

Please sign in to comment.