Feature: finish i18n system

This commit is contained in:
PiEgg
2022-08-20 16:44:55 +08:00
parent 86012c0c7b
commit 428ffc7ef1
35 changed files with 1035 additions and 538 deletions

View File

@@ -17,7 +17,8 @@ import pasteTemplate from '~/main/utils/pasteTemplate'
import pkg from 'root/package.json'
import { handleCopyUrl } from '~/main/utils/common'
import { privacyManager } from '~/main/utils/privacyManager'
import { T } from '#/i18n'
// import { T } from '#/i18n'
import { T } from '~/main/i18n'
import { isMacOSVersionGreaterThanOrEqualTo } from '~/main/utils/getMacOSVersion'
let contextMenu: Menu | null
let menu: Menu | null

View File

@@ -9,7 +9,7 @@ import pasteTemplate from '~/main/utils/pasteTemplate'
import db, { GalleryDB } from '~/main/apis/core/datastore'
import { handleCopyUrl } from '~/main/utils/common'
import { handleUrlEncode } from '#/utils/common'
import { T } from '#/i18n/index'
import { T } from '~/main/i18n/index'
// import dayjs from 'dayjs'
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {

View File

@@ -15,7 +15,7 @@ import { IPicGo } from 'picgo'
import { showNotification, calcDurationRange } from '~/main/utils/common'
import { RENAME_FILE_NAME, TALKING_DATA_EVENT } from '~/universal/events/constants'
import logger from '@core/picgo/logger'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
import fse from 'fs-extra'
import path from 'path'
import { privacyManager } from '~/main/utils/privacyManager'

View File

@@ -10,17 +10,18 @@ import { CREATE_APP_MENU } from '@core/bus/constants'
import db from '~/main/apis/core/datastore'
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
import { app } from 'electron'
import { i18n } from '~/universal/i18n'
import { URLSearchParams } from 'url'
// import { i18n } from '~/main/i18n'
// import { URLSearchParams } from 'url'
const windowList = new Map<IWindowList, IWindowListItem>()
const handleWindowParams = (windowURL: string) => {
const [baseURL, hash = ''] = windowURL.split('#')
const search = new URLSearchParams()
const lang = i18n.getLanguage()
search.append('lang', lang)
return `${baseURL}?${search.toString()}#${hash}`
// const [baseURL, hash = ''] = windowURL.split('#')
// const search = new URLSearchParams()
// const lang = i18n.getLanguage()
// search.append('lang', lang)
// return `${baseURL}?${search.toString()}#${hash}`
return windowURL
}
windowList.set(IWindowList.TRAY_WINDOW, {

View File

@@ -4,7 +4,7 @@ import path from 'path'
import { app as APP } from 'electron'
import { getLogger } from '@core/utils/localLogger'
import dayjs from 'dayjs'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
const STORE_PATH = APP.getPath('userData')
const configFilePath = path.join(STORE_PATH, 'data.json')
const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json')

View File

@@ -1,7 +1,7 @@
import fs from 'fs-extra'
import { dbPathChecker, dbPathDir, getGalleryDBPath } from './dbChecker'
import { DBStore, JSONStore } from '@picgo/store'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
const STORE_PATH = dbPathDir()

View File

@@ -17,7 +17,7 @@ import {
SHOW_INPUT_BOX
} from '~/universal/events/constants'
import { DBStore } from '@picgo/store'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
// Cross-process support may be required in the future
class GuiApi implements IGuiApi {

View File

@@ -27,8 +27,7 @@ import {
OPEN_URL,
RELOAD_APP,
SHOW_PLUGIN_PAGE_MENU,
SET_MINI_WINDOW_POS,
CHANGE_LANGUAGE
SET_MINI_WINDOW_POS
} from '#/events/constants'
import {
uploadClipboardFiles,
@@ -38,7 +37,7 @@ import picgoCoreIPC from './picgoCoreIPC'
import { handleCopyUrl } from '~/main/utils/common'
import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildUploadPageMenu } from './remotes/menu'
import path from 'path'
import { i18n, T } from '~/universal/i18n'
import { T } from '~/main/i18n'
const STORE_PATH = app.getPath('userData')
@@ -224,10 +223,6 @@ export default {
const window = BrowserWindow.getFocusedWindow()
window?.setBounds(pos)
})
ipcMain.on(CHANGE_LANGUAGE, (evt: IpcMainEvent, lang: string) => {
lang = lang || 'zh-CN'
i18n.setLanguage(lang)
})
},
dispose () {}
}

View File

@@ -27,13 +27,16 @@ import {
PICGO_OPEN_FILE,
PASTE_TEXT,
OPEN_WINDOW,
DEFAULT_LOGO
DEFAULT_LOGO,
GET_LANGUAGE_LIST,
SET_CURRENT_LANGUAGE,
GET_CURRENT_LANGUAGE
} from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore'
import { IObject, IFilter } from '@picgo/store/dist/types'
import pasteTemplate from '../utils/pasteTemplate'
import { T } from '~/universal/i18n'
import { i18nManager, T } from '~/main/i18n'
// eslint-disable-next-line
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
@@ -373,6 +376,31 @@ const handleDefaultLogo = () => {
})
}
const handleI18n = () => {
ipcMain.on(GET_LANGUAGE_LIST, (event: IpcMainEvent) => {
event.sender.send(GET_LANGUAGE_LIST, i18nManager.languageList)
})
ipcMain.on(SET_CURRENT_LANGUAGE, (event: IpcMainEvent, language: string) => {
i18nManager.setCurrentLanguage(language)
const { lang, locales } = i18nManager.getCurrentLocales()
if (process.platform === 'darwin') {
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
trayWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
}
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)
settingWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
if (windowManager.has(IWindowList.MINI_WINDOW)) {
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)
miniWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales)
}
// event.sender.send(SET_CURRENT_LANGUAGE, lang, locales)
})
ipcMain.on(GET_CURRENT_LANGUAGE, (event: IpcMainEvent) => {
const { lang, locales } = i18nManager.getCurrentLocales()
event.sender.send(GET_CURRENT_LANGUAGE, lang, locales)
})
}
export default {
listen () {
handleGetPluginList()
@@ -387,6 +415,7 @@ export default {
handleOpenFile()
handleOpenWindow()
handleDefaultLogo()
handleI18n()
},
// TODO: separate to single file
handlePluginUninstall,

View File

@@ -12,7 +12,7 @@ import GuiApi from 'apis/gui'
import { PICGO_CONFIG_PLUGIN, PICGO_HANDLE_PLUGIN_ING, PICGO_TOGGLE_PLUGIN, SHOW_MAIN_PAGE_DONATION, SHOW_MAIN_PAGE_QRCODE } from '~/universal/events/constants'
import picgoCoreIPC from '~/main/events/picgoCoreIPC'
import { PicGo as PicGoCore } from 'picgo'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
interface GuiMenuItem {
label: string

95
src/main/i18n/index.ts Normal file
View File

@@ -0,0 +1,95 @@
import yaml from 'js-yaml'
import { ObjectAdapter, I18n } from '@picgo/i18n'
import path from 'path'
import fs from 'fs-extra'
class I18nManager {
private i18n: I18n | null = null
private builtinI18nFolder = path.join(__static, 'i18n')
private outterI18nFolder = ''
private localesMap: Map<string, ILocales> = new Map()
private currentLanguage: string = 'zh-CN'
readonly defaultLanguage: string = 'zh-CN'
private i18nFileList: II18nItem[] = [{
label: '简体中文',
value: 'zh-CN'
}, {
label: 'English',
value: 'en'
}]
setOutterI18nFolder (folder: string) {
this.outterI18nFolder = folder
}
addI18nFile (file: string, label: string) {
this.i18nFileList.push({
label,
value: file
})
}
private getLocales (lang: string): ILocales {
if (this.localesMap.has(lang)) {
return this.localesMap.get(lang)!
}
let localesPath = path.join(this.builtinI18nFolder, `${lang}.yml`)
if (!fs.existsSync(localesPath)) {
localesPath = path.join(this.outterI18nFolder, `${lang}.yml`)
if (!fs.existsSync(localesPath)) {
localesPath = path.join(this.builtinI18nFolder, `${this.defaultLanguage}.yml`)
}
}
try {
const localesString = fs.readFileSync(localesPath, 'utf8')
const locales = yaml.load(localesString) as unknown as ILocales
this.localesMap.set(lang, locales)
return locales
} catch (e) {
console.error(e)
// if error, use default language
localesPath = path.join(this.builtinI18nFolder, `${this.defaultLanguage}.yml`)
const localesString = fs.readFileSync(localesPath, 'utf8')
const locales = yaml.load(localesString) as unknown as ILocales
this.localesMap.set(lang, locales)
return locales
}
}
setCurrentLanguage (lang: string) {
const locales = this.getLocales(lang)
this.currentLanguage = lang
this.initI18n(lang, locales)
}
private initI18n (lang: string = this.defaultLanguage, locales: ILocales) {
const objectAdapter = new ObjectAdapter({
[lang]: locales
})
this.i18n = new I18n({
adapter: objectAdapter,
defaultLanguage: lang
})
}
T (key: ILocalesKey, args: IStringKeyMap = {}): string {
return this.i18n?.translate(key, args) || key
}
get languageList () {
return this.i18nFileList
}
getCurrentLocales () {
return {
lang: this.currentLanguage,
locales: this.getLocales(this.currentLanguage)
}
}
}
export const T = (key: ILocalesKey, args: IStringKeyMap = {}): string => {
return i18nManager.T(key, args)
}
export const i18nManager = new I18nManager()

View File

@@ -3,7 +3,7 @@ import ConfigStore from '~/main/apis/core/datastore'
import path from 'path'
import fse from 'fs-extra'
import { PicGo as PicGoCore } from 'picgo'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
// from v2.1.2
const updateShortKeyFromVersion212 = (db: typeof ConfigStore, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
// #557 极端情况可能会出现配置不存在,需要重新写入

View File

@@ -2,6 +2,9 @@ import fs from 'fs-extra'
import path from 'path'
import os from 'os'
import { dbPathChecker } from 'apis/core/datastore/dbChecker'
import yaml from 'js-yaml'
import { i18nManager } from '~/main/i18n'
// import { ILocales } from '~/universal/types/i18n'
const configPath = dbPathChecker()
const CONFIG_DIR = path.dirname(configPath)
@@ -11,13 +14,14 @@ function beforeOpen () {
resolveMacWorkFlow()
}
resolveClipboardImageGenerator()
resolveOtherI18nFiles()
}
/**
* macOS 右键菜单
*/
function resolveMacWorkFlow () {
const dest = `${os.homedir}/Library/Services/Upload pictures with PicGo.workflow`
const dest = `${os.homedir()}/Library/Services/Upload pictures with PicGo.workflow`
if (fs.existsSync(dest)) {
return true
} else {
@@ -29,6 +33,20 @@ function resolveMacWorkFlow () {
}
}
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
try {
const file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
const file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
}
} catch (e) {
console.error(e)
fs.copyFileSync(filePath1, filePath2)
}
}
/**
* 初始化剪贴板生成图片的脚本
*/
@@ -44,20 +62,6 @@ function resolveClipboardImageGenerator () {
})
}
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
try {
const file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
const file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
if (!file1 || !file2 || !file1.equals(file2)) {
fs.copyFileSync(filePath1, filePath2)
}
} catch (e) {
console.error(e)
fs.copyFileSync(filePath1, filePath2)
}
}
function getClipboardFiles () {
const files = [
'/linux.sh',
@@ -76,4 +80,34 @@ function resolveClipboardImageGenerator () {
}
}
/**
* 初始化其他语言文件
*/
function resolveOtherI18nFiles () {
const i18nFolder = path.join(CONFIG_DIR, 'i18n')
if (!fs.pathExistsSync(i18nFolder)) {
fs.mkdirSync(i18nFolder)
}
i18nManager.setOutterI18nFolder(i18nFolder)
const i18nFiles = fs.readdirSync(path.join(CONFIG_DIR, 'i18n'), {
withFileTypes: true
})
i18nFiles.forEach(item => {
if (item.isFile()) {
if (item.name.endsWith('.yml')) {
const i18nFilePath = path.join(i18nFolder, item.name)
const i18nFile = fs.readFileSync(i18nFilePath, 'utf8')
try {
const i18nFileObj = yaml.load(i18nFile) as unknown as ILocales
if (i18nFileObj?.LANG_DISPLAY_LABEL) {
i18nManager.addI18nFile(item.name.replace('.yml', ''), i18nFileObj.LANG_DISPLAY_LABEL)
}
} catch (e) {
console.error(e)
}
}
}
})
}
export default beforeOpen

View File

@@ -1,6 +1,6 @@
import db from '~/main/apis/core/datastore'
import { i18n } from '#/i18n'
import { i18nManager } from '~/main/i18n'
export const initI18n = () => {
const currentLanguage = db.get('settings.language') || 'zh-CN'
i18n.setLanguage(currentLanguage)
i18nManager.setCurrentLanguage(currentLanguage)
}

View File

@@ -1,6 +1,6 @@
import db from '~/main/apis/core/datastore'
import { showMessageBox } from '~/main/utils/common'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
class PrivacyManager {
async check () {

View File

@@ -2,7 +2,7 @@ import { dialog, shell } from 'electron'
import db from '~/main/apis/core/datastore'
import pkg from 'root/package.json'
import { lt } from 'semver'
import { T } from '~/universal/i18n'
import { T } from '~/main/i18n'
import { getLatestVersion } from '#/utils/getLatestVersion'
const version = pkg.version
// const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases'