From 87173167506ed82a05cc728fc2316202af1fef31 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 15:45:46 +0800 Subject: [PATCH 01/16] feat: i18n code-editor --- packages/code-editor/package.json | 1 + .../code-editor/src/CodeEditorCollection.js | 5 +++-- .../MonacoEditor/modelSessionManager/index.js | 21 ++++++++++--------- .../code-editor/src/customTabs/Invalid.js | 8 ++++--- .../src/customTabs/Settings/index.js | 3 ++- packages/code-editor/src/customTabs/index.js | 4 +++- packages/i18n/src/locales/en.js | 20 ++++++++++++++++++ 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/packages/code-editor/package.json b/packages/code-editor/package.json index a666e69b..0a9b25d6 100644 --- a/packages/code-editor/package.json +++ b/packages/code-editor/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@obsidians/file-ops": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/notification": "^0.3.0", "@obsidians/ui-components": "^0.3.0", "color-convert": "^2.0.1", diff --git a/packages/code-editor/src/CodeEditorCollection.js b/packages/code-editor/src/CodeEditorCollection.js index 9169fbbc..0422c8c2 100644 --- a/packages/code-editor/src/CodeEditorCollection.js +++ b/packages/code-editor/src/CodeEditorCollection.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import { Tabs } from '@obsidians/ui-components' import fileOps from '@obsidians/file-ops' +import { t } from '@obsidians/i18n' import MonacoEditorContainer from './MonacoEditor/MonacoEditorContainer' import modelSessionManager from './MonacoEditor/modelSessionManager' @@ -83,8 +84,8 @@ export default class CodeEditorCollection extends Component { const { response } = await fileOps.current.showMessageBox({ // title: `Do you want to save the changes you made for ${path}?`, - message: 'Your changes will be lost if you close this item without saving.', - buttons: ['Save', 'Cancel', `Don't Save`] + message: t('editor.lost'), + buttons: [t('button.save'), t('button.cancel'), t('button.dontSave')] }) clicked = response diff --git a/packages/code-editor/src/MonacoEditor/modelSessionManager/index.js b/packages/code-editor/src/MonacoEditor/modelSessionManager/index.js index e5b4fad5..8391a438 100644 --- a/packages/code-editor/src/MonacoEditor/modelSessionManager/index.js +++ b/packages/code-editor/src/MonacoEditor/modelSessionManager/index.js @@ -1,7 +1,8 @@ import React from 'react' import * as monaco from 'monaco-editor' -import fileOps from '@obsidians/file-ops' +import fileOps from '@obsidians/file-ops' import notification from '@obsidians/notification' +import { t } from '@obsidians/i18n' import MonacoEditorModelSession from './MonacoEditorModelSession' @@ -25,7 +26,7 @@ class ModelSessionManager { constructor () { this._editorContainer = undefined this._editor = undefined - + this.modeDetector = defaultModeDetector this.CustomTabs = {} this.customTabTitles = {} @@ -72,7 +73,7 @@ class ModelSessionManager { async newModelSession (filePath, remote = false, mode = this.modeDetector(filePath)) { if (!filePath) { - throw new Error('Empty path for "newModelSession"') + throw new Error(t('editor.error.emptyPath')) } if (!this.sessions[filePath]) { @@ -91,7 +92,7 @@ class ModelSessionManager { async saveFile (filePath) { if (!this.sessions[filePath]) { - throw new Error(`File "${filePath}" is not open in the current workspace.`) + throw new Error(t('editor.error.fileNotOpen', { path: filePath })) } this._editorContainer.fileSaving(filePath) await fileOps.current.writeFile(filePath, this.sessions[filePath].value) @@ -100,19 +101,19 @@ class ModelSessionManager { async saveCurrentFile () { if (!this.currentFilePath) { - throw new Error('No current file open.') + throw new Error(t('editor.error.noFile')) } try { await this.saveFile(this.currentFilePath) } catch (e) { console.warn(e) - notification.error('Save Failed', e.message) + notification.error(t('editor.error.saveFailed'), e.message) } } undo () { if (!this.currentFilePath || !this.sessions[this.currentFilePath]) { - throw new Error('No current file open.') + throw new Error(t('editor.error.noFile')) } window.model = this.sessions[this.currentFilePath].model this.sessions[this.currentFilePath].model.undo() @@ -120,14 +121,14 @@ class ModelSessionManager { redo () { if (!this.currentFilePath || !this.sessions[this.currentFilePath]) { - throw new Error('No current file open.') + throw new Error(t('editor.error.noFile')) } this.sessions[this.currentFilePath].model.redo() } delete () { if (!this.currentFilePath || !this.sessions[this.currentFilePath]) { - throw new Error('No current file open.') + throw new Error(t('editor.error.noFile')) } const model = this.sessions[this.currentFilePath].model if (this._editor) { @@ -141,7 +142,7 @@ class ModelSessionManager { selectAll () { if (!this.currentFilePath || !this.sessions[this.currentFilePath]) { - throw new Error('No current file open.') + throw new Error(t('editor.error.noFile')) } const model = this.sessions[this.currentFilePath].model this._editor?.setSelection(model.getFullModelRange()) diff --git a/packages/code-editor/src/customTabs/Invalid.js b/packages/code-editor/src/customTabs/Invalid.js index e87a6e51..3ff0c245 100644 --- a/packages/code-editor/src/customTabs/Invalid.js +++ b/packages/code-editor/src/customTabs/Invalid.js @@ -4,6 +4,8 @@ import { Container } from 'reactstrap' +import { t } from '@obsidians/i18n' + export default class Invalid extends Component { render () { const fullName = this.props.eosProject.fullName @@ -11,12 +13,12 @@ export default class Invalid extends Component {
-

Invalid Project

+

{t('editor.project.invalid')}


- Cannot read project settings for {fullName}. + {t('editor.project.cantReadSetting')} {fullName}.

-

Make sure you have the read/write permission for xxxx.

+

{t('editor.project.makeSurePermission')} xxxx.

diff --git a/packages/code-editor/src/customTabs/Settings/index.js b/packages/code-editor/src/customTabs/Settings/index.js index 91c33ab2..ad404c3a 100644 --- a/packages/code-editor/src/customTabs/Settings/index.js +++ b/packages/code-editor/src/customTabs/Settings/index.js @@ -1,4 +1,5 @@ import React, { Component } from 'react' +import { t } from '@obsidians/i18n' export default class Settings extends Component { render () { @@ -6,7 +7,7 @@ export default class Settings extends Component {
-

Project Settings

+

{t('editor.project.setting')}

diff --git a/packages/code-editor/src/customTabs/index.js b/packages/code-editor/src/customTabs/index.js index 168641f8..3462769e 100644 --- a/packages/code-editor/src/customTabs/index.js +++ b/packages/code-editor/src/customTabs/index.js @@ -1,3 +1,5 @@ +import { t } from '@obsidians/i18n' + import modelSessionManager from '../MonacoEditor/modelSessionManager' import Markdown from './Markdown' @@ -8,6 +10,6 @@ export function useBuiltinCustomTabs(tabs) { modelSessionManager.registerCustomTab('markdown', Markdown) } if (tabs.indexOf('settings') > -1) { - modelSessionManager.registerCustomTab('settings', Settings, 'Project Settings') + modelSessionManager.registerCustomTab('settings', Settings, t('editor.project.setting')) } } \ No newline at end of file diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 5ad0f2b6..0acc33b1 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -7,6 +7,26 @@ const translation = { start: 'Get Started', skip: 'Skip', }, + editor: { + lost: 'Your changes will be lost if you close this item without saving.', + project: { + setting: 'Project Settings2', + invalid: 'Invalid Project', + cantReadSetting: 'Cannot read project settings for', + makeSurePermission: 'Make sure you have the read/write permission for', + }, + error: { + emptyPath: 'Empty path for "newModelSession"', + fileNotOpen: 'File "{{path}}" is not open in the current workspace.', + noFile: 'No current file open.', + saveFailed: 'Save Failed', + }, + }, + button: { + save: 'Save', + cancel: 'Cancel', + dontSave: `Don't Save`, + } } export default { From 5225cdc256471ba1103cda3a7b0b0a6182c38733 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 17:13:23 +0800 Subject: [PATCH 02/16] feat: i18n docker --- packages/docker/package.json | 1 + .../docker/src/DockerImageInputSelector.js | 15 +++---- packages/docker/src/DockerImageManager.js | 17 ++++---- packages/docker/src/DockerImageSelector.js | 11 +++--- packages/docker/src/DownloadImageButton.js | 11 +++--- packages/docker/src/ListItemDocker.js | 21 +++++----- packages/docker/src/ListItemDockerImage.js | 11 +++--- packages/i18n/src/locales/en.js | 39 ++++++++++++++++++- 8 files changed, 84 insertions(+), 42 deletions(-) diff --git a/packages/docker/package.json b/packages/docker/package.json index c1d8e411..dc8ab62a 100644 --- a/packages/docker/package.json +++ b/packages/docker/package.json @@ -19,6 +19,7 @@ "dependencies": { "@obsidians/ipc": "^0.3.0", "@obsidians/file-ops": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/notification": "^0.3.0", "@obsidians/platform": "^0.3.0", "@obsidians/terminal": "^0.3.0", diff --git a/packages/docker/src/DockerImageInputSelector.js b/packages/docker/src/DockerImageInputSelector.js index 1e4c6893..66e9ae01 100644 --- a/packages/docker/src/DockerImageInputSelector.js +++ b/packages/docker/src/DockerImageInputSelector.js @@ -3,6 +3,7 @@ import React, { PureComponent } from 'react' import { DropdownInput, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import platform from '@obsidians/platform' @@ -40,25 +41,25 @@ export default class DockerImageInputSelector extends PureComponent { badgeProps = selected => { if (this.state.loading) { - return { badge: Loading... } + return { badge: {t('loading')} } } if (!selected) { if (this.state.versions.length) { - return { badge: 'not selected', badgeColor: 'danger' } + return { badge: t('docker.badge.noSelect'), badgeColor: 'danger' } } return } if (!this.state.versions.find(v => v.id === selected) && !this.props.extraOptions?.find(i => i.id === selected)) { - return { badge: 'not installed', badgeColor: 'danger' } + return { badge: t('docker.badge.noInstall'), badgeColor: 'danger' } } } render () { let { versions } = this.state - let placeholder = `Select a version of ${this.props.noneName || this.imageName}` + let placeholder = t('docker.image.select', { name: this.props.noneName || this.imageName }) if (!versions.length) { - placeholder = `(No ${this.props.noneName || this.imageName} installed)` + placeholder = t('docker.image.noInstall', { name: this.props.noneName || this.imageName }) versions.push({ id: 'none', display: placeholder, disabled: true }) } @@ -66,7 +67,7 @@ export default class DockerImageInputSelector extends PureComponent { if (platform.isDesktop) { options = [ { - group: <>Installed, + group: <>{t('docker.installed')}, children: this.state.versions, }, { @@ -78,7 +79,7 @@ export default class DockerImageInputSelector extends PureComponent { } else { options = [ { - group: <>Versions, + group: <>{t('docker.image.versions')}, children: this.state.versions, } ] diff --git a/packages/docker/src/DockerImageManager.js b/packages/docker/src/DockerImageManager.js index 7802868e..e3b95593 100644 --- a/packages/docker/src/DockerImageManager.js +++ b/packages/docker/src/DockerImageManager.js @@ -5,6 +5,7 @@ import { Modal, DeleteButton, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import DockerImageChannel from './DockerImageChannel' import DownloadImageButton from './DownloadImageButton' @@ -67,14 +68,14 @@ export default class DockerImageManager extends PureComponent { return ( - Loading... + {t('loading')} ) } if (!this.state.installed.length) { - const none = `(No ${this.props.noneName || this.props.imageName} installed)` + const none = t('docker.image.noInstall', { name: this.props.noneName || this.props.imageName }) return ( {none} @@ -91,7 +92,7 @@ export default class DockerImageManager extends PureComponent { this.deleteVersion(v.Tag)} - textConfirm='Click again to uninstall' + textConfirm={t('docker.image.uninstallConfirm')} /> @@ -102,8 +103,8 @@ export default class DockerImageManager extends PureComponent { render () { const imageName = this.channel.imageName const { - modalTitle = `${imageName} Manager`, - downloadingTitle = `Downloading ${imageName}`, + modalTitle = t('docker.image.manager', { name: imageName }), + downloadingTitle = t('docker.image.manager', { name: imageName }), } = this.props return ( @@ -123,9 +124,9 @@ export default class DockerImageManager extends PureComponent { - - - + + + diff --git a/packages/docker/src/DockerImageSelector.js b/packages/docker/src/DockerImageSelector.js index 90b320f8..7aebcce4 100644 --- a/packages/docker/src/DockerImageSelector.js +++ b/packages/docker/src/DockerImageSelector.js @@ -6,6 +6,7 @@ import { DropdownMenu, DropdownItem, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import platform from '@obsidians/platform' @@ -43,12 +44,12 @@ export default class DockerImageSelector extends PureComponent { renderItems = () => { if (this.state.loading) { return ( - Loading... + {t('loading')} ) } if (!this.state.versions.length) { - const none = `(No ${this.props.noneName || this.imageName} installed)` + const none = t('docker.image.noInstall', { name: this.props.noneName || this.imageName }) return ( {none} ) @@ -87,9 +88,9 @@ export default class DockerImageSelector extends PureComponent { if (!HeaderDockerItems) { if (platform.isDesktop) { - HeaderDockerItems = <>Installed + HeaderDockerItems = <>{t('docker.installed')} } else { - HeaderDockerItems = <>Versions + HeaderDockerItems = <>{t('docker.image.versions')} } } @@ -106,7 +107,7 @@ export default class DockerImageSelector extends PureComponent { style={{ maxWidth: 240 }} > {icon} - {this.props.title || this.imageName} ({this.props.selected?.toString() || 'none'}) + {this.props.title || this.imageName} ({this.props.selected?.toString() || t('docker.image.none')}) {children} diff --git a/packages/docker/src/DownloadImageButton.js b/packages/docker/src/DownloadImageButton.js index a787275b..94995dbf 100644 --- a/packages/docker/src/DownloadImageButton.js +++ b/packages/docker/src/DownloadImageButton.js @@ -7,6 +7,7 @@ import { DropdownItem, UncontrolledButtonDropdown } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import Terminal from '@obsidians/terminal' @@ -64,13 +65,13 @@ export default class DownloadImageButton extends PureComponent { if (loading) { return ( - Loading... + {t('loading')} ) } if (!versions.length) { - return (None) + return ({t('docker.image.none')}) } return versions.map(({ name }) => ( @@ -81,8 +82,8 @@ export default class DownloadImageButton extends PureComponent { render () { const imageName = this.channel.imageName const { - installDropdownHeader = 'Available Versions', - downloadingTitle = `Downloading ${imageName}`, + installDropdownHeader = t('docker.image.availableVersions'), + downloadingTitle = t('docker.image.downloading', { name: imageName }), size, color = 'secondary', right, @@ -102,7 +103,7 @@ export default class DownloadImageButton extends PureComponent { color={color} onClick={() => this.fetchRemoteVersions()} > - Install + {t('docker.install')} {installDropdownHeader} diff --git a/packages/docker/src/ListItemDocker.js b/packages/docker/src/ListItemDocker.js index 43a536ef..0c297946 100644 --- a/packages/docker/src/ListItemDocker.js +++ b/packages/docker/src/ListItemDocker.js @@ -3,6 +3,7 @@ import { Button, ListGroupItem } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import fileOps from '@obsidians/file-ops' import notification from '@obsidians/notification' @@ -35,7 +36,7 @@ export default class ListItemDocker extends PureComponent { } else if (version) { this.mounted && this.setState({ docker: 'INSTALLED', version }) if (process.env.OS_IS_LINUX) { - notification.error('Fail to run docker', 'Make sure the non-root user has privileges to run docker') + notification.error(t('docker.error.fail'), t('docker.error.linuxPrivileges')) } } else { this.mounted && this.setState({ docker: 'NONE', version: '' }) @@ -49,7 +50,7 @@ export default class ListItemDocker extends PureComponent { this.mounted && this.setState({ docker: 'STARTED' }) } else { this.mounted && this.setState({ docker: 'INSTALLED' }) - notification.error('Fail to run docker', 'Something went wrong when starting Docker, please try again.') + notification.error(t('docker.error.fail'), t('docker.error.unknown')) } this.props.onStartedDocker() } @@ -70,9 +71,9 @@ export default class ListItemDocker extends PureComponent { renderSubtitle = () => { switch (this.state.docker) { case '': - return Loading... + return {t('loading')} case 'NONE': - return Docker is required to start a local node. + return {t('docker.required')} default: return {this.state.version} } @@ -83,28 +84,28 @@ export default class ListItemDocker extends PureComponent { case '': return null case 'NONE': - return + return case 'INSTALLED': if (process.env.OS_IS_LINUX) { return ( ) } else { - return + return } case 'STARTING': return ( ) default: - return + return } } - + installDocker = () => { if (process.env.OS_IS_LINUX) { fileOps.current.openLink('https://docs.docker.com/engine/install/ubuntu') diff --git a/packages/docker/src/ListItemDockerImage.js b/packages/docker/src/ListItemDockerImage.js index c9a8a5a4..11f61a1f 100644 --- a/packages/docker/src/ListItemDockerImage.js +++ b/packages/docker/src/ListItemDockerImage.js @@ -4,6 +4,7 @@ import { Badge, ListGroupItem, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import fileOps from '@obsidians/file-ops' @@ -65,13 +66,13 @@ export default class ListItemDockerImage extends PureComponent { renderSubtitle = subtitle => { switch (this.state.status) { case '': - return Loading... + return {t('loading')} case 'NO_DOCKER': case 'NONE': case 'INSTALLING': return {subtitle} default: - return Installed: {this.state.versions.map(v => {v})} + return {t('docker.installed')}: {this.state.versions.map(v => {v})} } } @@ -80,7 +81,7 @@ export default class ListItemDockerImage extends PureComponent { case '': return null case 'NO_DOCKER': - return + return case 'NONE': return ( - Installing + {t('docker.installing')} ) default: - return + return } } diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 0acc33b1..c02c88f2 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -1,5 +1,5 @@ const translation = { - loading: 'Loading', + loading: 'Loading...', loggingIn: 'Logging in...', welcome: { welcome: 'Welcome to {{projectName}}', @@ -10,7 +10,7 @@ const translation = { editor: { lost: 'Your changes will be lost if you close this item without saving.', project: { - setting: 'Project Settings2', + setting: 'Project Settings', invalid: 'Invalid Project', cantReadSetting: 'Cannot read project settings for', makeSurePermission: 'Make sure you have the read/write permission for', @@ -22,6 +22,41 @@ const translation = { saveFailed: 'Save Failed', }, }, + docker: { + need: 'Need Docker', + required: 'Docker is required to start a local node.', + install: 'Install', + installing: 'Installing', + installed: 'Installed', + start: 'Start Docker', + starting: 'Starting Docker', + started: 'Started', + privileges: 'Need Privileges', + image: { + select: 'Select a version of {{name}}', + noInstall:'(No {{name}} installed)', + versions: 'Versions', + manager: '{{name}} Manager', + downloading: 'Downloading {{name}}', + uninstallConfirm: 'Click again to uninstall', + none: 'None', + availableVersions: 'Available Versions', + }, + badge: { + noSelect: 'not selected', + noInstall: 'not installed', + }, + table: { + version: 'version', + created: 'created', + size: 'size', + }, + error: { + fail: 'Fail to run docker', + linuxPrivileges: 'Make sure the non-root user has privileges to run docker', + unknown: 'Something went wrong when starting Docker, please try again' + } + }, button: { save: 'Save', cancel: 'Cancel', From 22ae6612618e68674c9631420377f719543eddd3 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 17:19:59 +0800 Subject: [PATCH 03/16] feat: i18n file-ops --- packages/file-ops/package.json | 1 + packages/file-ops/src/ElectronFileOps.js | 3 ++- packages/file-ops/src/WebFileOps.js | 8 +++++--- packages/i18n/src/locales/en.js | 14 ++++++++++---- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/file-ops/package.json b/packages/file-ops/package.json index e950e447..45c05807 100644 --- a/packages/file-ops/package.json +++ b/packages/file-ops/package.json @@ -7,6 +7,7 @@ "main": "src/index.js", "dependencies": { "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "filehound": "^1.17.4", "fs-extra": "^9.1.0", "lodash": "^4.17.20", diff --git a/packages/file-ops/src/ElectronFileOps.js b/packages/file-ops/src/ElectronFileOps.js index 73a23560..6422b793 100644 --- a/packages/file-ops/src/ElectronFileOps.js +++ b/packages/file-ops/src/ElectronFileOps.js @@ -1,4 +1,5 @@ import { IpcChannel } from '@obsidians/ipc' +import { t } from '@obsidians/i18n' import FileOps from './FileOps' @@ -43,7 +44,7 @@ export default class ElectronFileOps extends FileOps { async chooseFolder (defaultPath = this.workspace) { const result = await this.electron.remote.dialog.showOpenDialog({ - buttonLabel: 'Open', + buttonLabel: t('file.open'), defaultPath: this.path.isAbsolute(defaultPath) ? defaultPath : this.path.join(this.workspace, defaultPath), properties: ['openDirectory', 'createDirectory'] }) diff --git a/packages/file-ops/src/WebFileOps.js b/packages/file-ops/src/WebFileOps.js index b1cc9399..2e1b79c7 100644 --- a/packages/file-ops/src/WebFileOps.js +++ b/packages/file-ops/src/WebFileOps.js @@ -1,3 +1,5 @@ +import { t } from '@obsidians/i18n' + import path from 'path-browserify' import FileOps from './FileOps' import AwsS3Fs from './AwsS3Fs' @@ -29,7 +31,7 @@ export default class WebFileOps extends FileOps { // { name: 'all', extensions: ['cpp', 'hpp', 'wasm', 'abi', 'md', 'js', 'json', 'c', 'h', 'o'] } ] }) - + if (result && result.filePaths && result.filePaths[0]) { const filePath = result.filePaths[0] return { key: filePath, path: filePath } @@ -40,7 +42,7 @@ export default class WebFileOps extends FileOps { async chooseFolder (defaultPath = this.workspace) { const result = await this.electron.remote.dialog.showOpenDialog({ - buttonLabel: 'Open', + buttonLabel: t('file.open'), defaultPath: this.path.isAbsolute(defaultPath) ? defaultPath : this.path.join(this.workspace, defaultPath), properties: ['openDirectory', 'createDirectory'] }) @@ -70,7 +72,7 @@ export default class WebFileOps extends FileOps { try { await this.fs.ensureDir(folderPath) } catch (e) { - throw new Error(`Fail to create the folder ${folderPath}.`) + throw new Error(`${t('file.error.createFolder')} ${folderPath}.`) } } diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index c02c88f2..12bd3a55 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -1,6 +1,11 @@ const translation = { loading: 'Loading...', loggingIn: 'Logging in...', + button: { + save: 'Save', + cancel: 'Cancel', + dontSave: `Don't Save`, + }, welcome: { welcome: 'Welcome to {{projectName}}', message: '{{projectName}} is a graphic IDE for developing smart contracts on the {{chainName}} blockchain. To get started, please install the prerequisite tools for {{projectName}}.', @@ -57,10 +62,11 @@ const translation = { unknown: 'Something went wrong when starting Docker, please try again' } }, - button: { - save: 'Save', - cancel: 'Cancel', - dontSave: `Don't Save`, + file: { + open: 'Open', + error: { + createFolder: 'Fail to create the folder', + }, } } From a08987342c47172e54ed873bc105b6e9adeccfc3 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 17:22:39 +0800 Subject: [PATCH 04/16] feat: i18n filetree --- packages/filetree/package.json | 1 + packages/filetree/src/index.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/filetree/package.json b/packages/filetree/package.json index e2d7c448..90f00fe9 100644 --- a/packages/filetree/package.json +++ b/packages/filetree/package.json @@ -19,6 +19,7 @@ "dependencies": { "@obsidians/file-ops": "^0.3.0", "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/ui-components": "^0.3.0", "chokidar": "^3.5.1", "filehound": "^1.17.4", diff --git a/packages/filetree/src/index.js b/packages/filetree/src/index.js index d59b15da..d4348346 100644 --- a/packages/filetree/src/index.js +++ b/packages/filetree/src/index.js @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import fileOps from '@obsidians/file-ops' +import { t } from '@obsidians/i18n' import { Treebeard, decorators, theme } from 'react-treebeard' @@ -69,7 +70,7 @@ export default class FileTree extends PureComponent { this.loadTree(this.props.projectManager) } } - + componentWillUnmount () { this.props.projectManager.offRefreshDirectory() } @@ -184,7 +185,7 @@ export default class FileTree extends PureComponent { if (!Object.keys(this.state.treeData).length) { return ( - Loading... + {t('loading')} ) } From b453e4d355cafd6481a3f1cfd56f247a4b962dd9 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 17:31:52 +0800 Subject: [PATCH 05/16] feat: i18n global --- packages/global/package.json | 1 + packages/global/src/modals/AboutModal.js | 9 +++++---- packages/global/src/modals/AutoUpdateModal.js | 9 +++++---- packages/i18n/src/locales/en.js | 13 +++++++++++++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/global/package.json b/packages/global/package.json index 17fbd552..1d47f9d0 100644 --- a/packages/global/package.json +++ b/packages/global/package.json @@ -19,6 +19,7 @@ "dependencies": { "@obsidians/file-ops": "^0.3.0", "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/platform": "^0.3.0", "@obsidians/ui-components": "^0.3.0", "electron-updater": "^4.3.5" diff --git a/packages/global/src/modals/AboutModal.js b/packages/global/src/modals/AboutModal.js index d5227d28..d9f4ea30 100644 --- a/packages/global/src/modals/AboutModal.js +++ b/packages/global/src/modals/AboutModal.js @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react' import { Modal } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import fileOps from '@obsidians/file-ops' import platform from '@obsidians/platform' @@ -42,15 +43,15 @@ export default class AboutModal extends PureComponent { return (
this.handleClick()}/>

{process.env.PROJECT_NAME} v{fileOps.current.getAppVersion()}

-
contact us
-

Website: fileOps.current.openLink('https://www.obsidians.io')}>https://www.obsidians.io

+
{t('global.about.contact')}
+

{t('global.about.website')}: fileOps.current.openLink('https://www.obsidians.io')}>https://www.obsidians.io

{this.props.children}
diff --git a/packages/global/src/modals/AutoUpdateModal.js b/packages/global/src/modals/AutoUpdateModal.js index 3f2a0d0f..e9c34ad8 100644 --- a/packages/global/src/modals/AutoUpdateModal.js +++ b/packages/global/src/modals/AutoUpdateModal.js @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react' import { Modal } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import globalModalManager from './globalModalManager' @@ -37,13 +38,13 @@ export default class AutoUpdateModal extends PureComponent { return ( - You can restart and update to the new version. Do you want to do it right now? + {t('global.update.message')} ) } diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 12bd3a55..df91ffb5 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -5,6 +5,7 @@ const translation = { save: 'Save', cancel: 'Cancel', dontSave: `Don't Save`, + close: 'Close', }, welcome: { welcome: 'Welcome to {{projectName}}', @@ -67,6 +68,18 @@ const translation = { error: { createFolder: 'Fail to create the folder', }, + }, + global: { + about: { + title: 'About', + contact: 'Contact us', + website: 'Website', + }, + update: { + available: '{{project}} v{{version}} is now available', + confirm: 'Restart and Update', + cancel: 'Later', + } } } From 4b44cdb172d41d09cd0a285cb8862e256d66cafc Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 17:34:34 +0800 Subject: [PATCH 06/16] feat: i18n ipc --- packages/i18n/src/locales/en.js | 4 ++++ packages/ipc/package.json | 1 + packages/ipc/src/HttpIpcChannel/HttpClient.js | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index df91ffb5..65814e1d 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -79,7 +79,11 @@ const translation = { available: '{{project}} v{{version}} is now available', confirm: 'Restart and Update', cancel: 'Later', + message: 'You can restart and update to the new version. Do you want to do it right now?', } + }, + ipc: { + needLogin: 'Need login to perform this operation.' } } diff --git a/packages/ipc/package.json b/packages/ipc/package.json index d29d641b..cca19ba5 100644 --- a/packages/ipc/package.json +++ b/packages/ipc/package.json @@ -8,6 +8,7 @@ "browser": "src/index.js", "dependencies": { "@obsidians/auth": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/platform": "^0.3.0", "qs": "^6.9.6" }, diff --git a/packages/ipc/src/HttpIpcChannel/HttpClient.js b/packages/ipc/src/HttpIpcChannel/HttpClient.js index c5d88efb..626a395b 100644 --- a/packages/ipc/src/HttpIpcChannel/HttpClient.js +++ b/packages/ipc/src/HttpIpcChannel/HttpClient.js @@ -1,5 +1,6 @@ import qs from 'qs' import Auth from '@obsidians/auth' +import { t } from '@obsidians/i18n' import BuildService from './BuildService' const PROJECT = process.env.PROJECT @@ -130,7 +131,7 @@ export default class HttpClient { } catch (e) {} if (response.status === 401) { - throw new Error(result.message || 'Need login to perform this operation.') + throw new Error(result.message || t('ipc.needLogin')) } else if (response.status >= 400) { throw new Error(result.message || result) } From 23f0efdf7b43f94e5787fe9c40d347b8869d4f96 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 18:25:58 +0800 Subject: [PATCH 07/16] feat: i18n keypair --- packages/docker/src/DockerImageSelector.js | 2 +- packages/docker/src/DownloadImageButton.js | 2 +- packages/i18n/src/locales/en.js | 58 +++++++++++++++++++- packages/keypair/package.json | 1 + packages/keypair/src/CreateKeypairModal.js | 19 ++++--- packages/keypair/src/ImportKeypairModal.js | 25 +++++---- packages/keypair/src/KeypairButton.js | 4 +- packages/keypair/src/KeypairInputSelector.js | 3 +- packages/keypair/src/KeypairManagerModal.js | 35 ++++++------ packages/keypair/src/KeypairNameModal.js | 13 +++-- packages/keypair/src/KeypairSelector.js | 5 +- packages/keypair/src/RevealSecretModal.js | 9 +-- packages/keypair/src/keypairManager.js | 5 +- 13 files changed, 123 insertions(+), 58 deletions(-) diff --git a/packages/docker/src/DockerImageSelector.js b/packages/docker/src/DockerImageSelector.js index 7aebcce4..fc7ec6ee 100644 --- a/packages/docker/src/DockerImageSelector.js +++ b/packages/docker/src/DockerImageSelector.js @@ -107,7 +107,7 @@ export default class DockerImageSelector extends PureComponent { style={{ maxWidth: 240 }} > {icon} - {this.props.title || this.imageName} ({this.props.selected?.toString() || t('docker.image.none')}) + {this.props.title || this.imageName} ({this.props.selected?.toString() || t('none')}) {children} diff --git a/packages/docker/src/DownloadImageButton.js b/packages/docker/src/DownloadImageButton.js index 94995dbf..94b866ce 100644 --- a/packages/docker/src/DownloadImageButton.js +++ b/packages/docker/src/DownloadImageButton.js @@ -71,7 +71,7 @@ export default class DownloadImageButton extends PureComponent { } if (!versions.length) { - return ({t('docker.image.none')}) + return ({t('none')}) } return versions.map(({ name }) => ( diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 65814e1d..fa64f2cb 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -1,6 +1,7 @@ const translation = { loading: 'Loading...', loggingIn: 'Logging in...', + none: 'None', button: { save: 'Save', cancel: 'Cancel', @@ -45,7 +46,6 @@ const translation = { manager: '{{name}} Manager', downloading: 'Downloading {{name}}', uninstallConfirm: 'Click again to uninstall', - none: 'None', availableVersions: 'Available Versions', }, badge: { @@ -84,7 +84,61 @@ const translation = { }, ipc: { needLogin: 'Need login to perform this operation.' - } + }, + keypair: { + title: 'Keypair', + name: 'Name', + address: 'Address', + private: 'Private Key', + no: 'No', + noKeypair: 'No keypairs', + reveal: 'Reveal {{name}}', + view: 'View', + create: { + title: 'Create Keypair', + confirm: 'Create', + creating: 'Creating...', + regenerate: 'Regenerate', + success: 'Create {{name}} Successful', + successMessage: 'A new {{name}} is created and saved in {{project}}.' + }, + import: { + title: 'Import Keypair', + confirm: 'Import', + importing: 'Importing...', + label: 'Enter the {{name}} you want to import', + success: 'Import {{name}} Successful', + successMessage: 'The {{name}} is imported to {{project}}.' + }, + delete: { + success: 'Delete {{name}} Successful', + successMessage: 'The {{name}} is removed from {{project}}.' + }, + modify: { + title: 'Modify Keypair Name', + saving: 'Saving...', + }, + manager: { + title: 'Keypair Manager', + doNot: 'DO NOT', + useOnMainnet: 'use on mainnet!', + forDevOnly: 'For development purpose only.', + saveUnencrypted: 'For convenience in development, the private keys are saved unencrypted.' + }, + input: { + name: 'Name', + placeholder: 'Please enter a name for the keypair', + }, + error: { + create: 'Create Keypair Failed', + import: 'Import Keypair Failed', + duplicateName: 'The keypair name {{name}} has already been used.', + duplicateKey: 'Keypair for {{address}} already exists.', + notValid: 'Not a valid {{secret}}', + noKeypair: 'No keypair for {{address}}', + noSecret: 'No {{key}} for {{address}}', + }, + }, } export default { diff --git a/packages/keypair/package.json b/packages/keypair/package.json index a46161fa..424ae32b 100644 --- a/packages/keypair/package.json +++ b/packages/keypair/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/notification": "^0.3.0", "@obsidians/redux": "^0.3.0", "@obsidians/ui-components": "^0.3.0", diff --git a/packages/keypair/src/CreateKeypairModal.js b/packages/keypair/src/CreateKeypairModal.js index ecceec11..a4b032d9 100644 --- a/packages/keypair/src/CreateKeypairModal.js +++ b/packages/keypair/src/CreateKeypairModal.js @@ -5,6 +5,7 @@ import { Badge, DebouncedFormGroup, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import notification from '@obsidians/notification' @@ -44,8 +45,8 @@ export default class CreateKeypairModal extends PureComponent { if (this.props.keypairs.find(kp => kp.name === name)) { notification.error( - `Create Keypair Failed`, - `The keypair name ${name} has already been used.` + t('keypair.error.create'), + t('keypair.error.duplicateName', { name }) ) return } @@ -67,24 +68,24 @@ export default class CreateKeypairModal extends PureComponent { return ( this.setState({ name })} />
- Address + {t('keypair.address')}
{address} diff --git a/packages/keypair/src/ImportKeypairModal.js b/packages/keypair/src/ImportKeypairModal.js index e132a13c..2e9e1b8b 100644 --- a/packages/keypair/src/ImportKeypairModal.js +++ b/packages/keypair/src/ImportKeypairModal.js @@ -4,6 +4,7 @@ import { Modal, DebouncedFormGroup, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import notification from '@obsidians/notification' import { kp } from '@obsidians/sdk' @@ -40,10 +41,10 @@ export default class ImportKeypairModal extends PureComponent { this.setState({ keypair, valid: true, - feedback: `Address: ${keypair.address}` + feedback: `${t('keypair.address')}: ${keypair.address}` }) } catch (e) { - this.setState({ keypair: null, valid: false, feedback: `Not a valid ${this.props.secretName.toLowerCase()}` }) + this.setState({ keypair: null, valid: false, feedback: t('keypair.error.notValid', { secret: this.props.secretName.toLowerCase() }) }) } } } @@ -58,16 +59,16 @@ export default class ImportKeypairModal extends PureComponent { if (this.props.keypairs.find(kp => kp.name === name)) { notification.error( - `Import Keypair Failed`, - `The keypair name ${name} has already been used.` + t('keypair.error.import'), + t('keypair.error.duplicateName', { name }) ) return } if (this.props.keypairs.find(kp => kp.address === keypair.address)) { notification.error( - `Import Keypair Failed`, - `Keypair for ${keypair.address} already exists.` + t('keypair.error.import'), + t('keypair.error.duplicateKey', { address: keypair.address }) ) return } @@ -90,20 +91,20 @@ export default class ImportKeypairModal extends PureComponent { return ( this.setState({ name })} />
) @@ -109,7 +110,7 @@ export default class KeypairManagerModal extends PureComponent { return ( ) @@ -127,7 +128,7 @@ export default class KeypairManagerModal extends PureComponent { @@ -48,7 +49,7 @@ export default class ProjectList extends PureComponent {
versioncreatedsize{t('docker.table.version')}{t('docker.table.created')}{t('docker.table.size')}
- Loading... + {t('loading')}
- (No {`${keypairText.toLowerCase()}s`}) + ({t('keypair.no')} {`${keypairText.toLowerCase()}s`})
- {keypair.name ? keypair.name : (None)} + {keypair.name ? keypair.name : ({t('none')})} { !this.props.modifyNameDisabled && this.revealSecret(keypair)} />
@@ -183,9 +184,9 @@ export default class KeypairManagerModal extends PureComponent {
-
DO NOT use on mainnet! For development purpose only.
+
{t('keypair.manager.doNot')} {t('keypair.manager.useOnMainnet')} {t('keypair.manager.forDevOnly')}
- For convenience in development, the private keys are saved unencrypted. + {t('keypair.manager.saveUnencrypted')}
@@ -197,7 +198,7 @@ export default class KeypairManagerModal extends PureComponent { ref={this.modal} title={title} textActions={actions ? textActions : []} - textCancel='Close' + textCancel={t('button.close')} onActions={[this.createKeypair, this.importKeypair]} > {warningComponent} diff --git a/packages/keypair/src/KeypairNameModal.js b/packages/keypair/src/KeypairNameModal.js index a9a6a2c3..27e56b9e 100644 --- a/packages/keypair/src/KeypairNameModal.js +++ b/packages/keypair/src/KeypairNameModal.js @@ -4,6 +4,7 @@ import { Modal, DebouncedFormGroup, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import keypairManager from './keypairManager' @@ -20,7 +21,7 @@ export default class KeypairNameModal extends PureComponent { this.modal = React.createRef() this.input = React.createRef() } - + openModal ({ address, name }) { this.modal.current.openModal() @@ -44,17 +45,17 @@ export default class KeypairNameModal extends PureComponent { return ( this.setState({ name })} /> diff --git a/packages/keypair/src/KeypairSelector.js b/packages/keypair/src/KeypairSelector.js index c0b6ca2c..c5533447 100644 --- a/packages/keypair/src/KeypairSelector.js +++ b/packages/keypair/src/KeypairSelector.js @@ -7,6 +7,7 @@ import { DropdownMenu, DropdownItem, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import keypairManager from './keypairManager' import KeypairManagerModal from './KeypairManagerModal' @@ -61,11 +62,11 @@ export default class KeypairSelector extends PureComponent { renderItems = (iconComponent) => { if (this.state.loading) { - return Loading... + return {t('loading')} } if (!this.state.keypairs.length) { - const none = `(No ${this.props.noneName})` + const none = `(${t('keypair.no')} ${this.props.noneName})` return {none} } diff --git a/packages/keypair/src/RevealSecretModal.js b/packages/keypair/src/RevealSecretModal.js index 3c35e1de..06984e71 100644 --- a/packages/keypair/src/RevealSecretModal.js +++ b/packages/keypair/src/RevealSecretModal.js @@ -4,6 +4,7 @@ import { Modal, Badge, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import keypairManager from './keypairManager' @@ -13,7 +14,7 @@ export default class RevealSecretModal extends PureComponent { this.state = { address: '', secret: '' } this.modal = React.createRef() } - + async openModal (keypair) { this.setState({ address: keypair.address }) @@ -30,12 +31,12 @@ export default class RevealSecretModal extends PureComponent { return (
- Address + {t('keypair.address')}
{address} diff --git a/packages/keypair/src/keypairManager.js b/packages/keypair/src/keypairManager.js index b9c32a50..dcd56125 100644 --- a/packages/keypair/src/keypairManager.js +++ b/packages/keypair/src/keypairManager.js @@ -3,6 +3,7 @@ import redux from '@obsidians/redux' import notification from '@obsidians/notification' import { kp } from '@obsidians/sdk' +import { t } from '@obsidians/i18n' class KeypairManager { constructor () { @@ -68,7 +69,7 @@ class KeypairManager { async getKeypair (address) { const keypair = await this.channel.invoke('get', address) if (!keypair) { - throw new Error(`No keypair for ${address}`) + throw new Error(t('keypair.error.noKeypair', { address })) } return keypair } @@ -76,7 +77,7 @@ class KeypairManager { async getSecret (address, key = 'secret') { const keypair = await this.getKeypair(address) if (!keypair[key]) { - throw new Error(`No ${key} for ${address}`) + throw new Error(t('keypair.error.noSecret', { key, address })) } return keypair[key] } From f87abdab89d207431e2d834a76627138f2cff3b1 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 22:31:36 +0800 Subject: [PATCH 08/16] feat: i18n navbar --- packages/i18n/src/locales/en.js | 7 +++++++ packages/navbar/package.json | 1 + packages/navbar/src/NavDropdown.js | 7 ++++--- packages/navbar/src/NavLinkContent.js | 3 ++- packages/navbar/src/User.js | 11 ++++++----- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index fa64f2cb..c1f85821 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -139,6 +139,13 @@ const translation = { noSecret: 'No {{key}} for {{address}}', }, }, + user: { + myProjects: 'My Projects', + login: 'Login', + loginAs: 'Logged in as', + loginProvider: 'Login {{provider}}', + logout: 'Log out', + } } export default { diff --git a/packages/navbar/package.json b/packages/navbar/package.json index 8e457368..08b48e97 100644 --- a/packages/navbar/package.json +++ b/packages/navbar/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@obsidians/auth": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/platform": "^0.3.0", "@obsidians/ui-components": "^0.3.0", "react-contextmenu": "^2.14.0" diff --git a/packages/navbar/src/NavDropdown.js b/packages/navbar/src/NavDropdown.js index ef2d92da..37079049 100644 --- a/packages/navbar/src/NavDropdown.js +++ b/packages/navbar/src/NavDropdown.js @@ -8,6 +8,7 @@ import { DropdownMenu, DropdownItem, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import { ContextMenuTrigger } from 'react-contextmenu' @@ -26,7 +27,7 @@ export default class NavDropdown extends Component { onToggle = event => { if (this.props.onToggle) { if (this.props.onToggle(event)) { - return + return } } this.setState({ dropdown: !this.state.dropdown }) @@ -34,7 +35,7 @@ export default class NavDropdown extends Component { renderDropdownList = list => { if (!list.length) { - return (None) + return ({t('none')}) } return list.map(this.renderDropdownItem) } @@ -45,7 +46,7 @@ export default class NavDropdown extends Component { } else if (item.header) { return {item.header} } else if (item.none) { - return (None) + return ({t('none')}) } const { id, name, icon } = item diff --git a/packages/navbar/src/NavLinkContent.js b/packages/navbar/src/NavLinkContent.js index 3c03601d..428d28d6 100644 --- a/packages/navbar/src/NavLinkContent.js +++ b/packages/navbar/src/NavLinkContent.js @@ -1,4 +1,5 @@ import React from 'react' +import { t } from '@obsidians/i18n' export default function (props) { const { title, selected, icon, Icon, width, noneIcon = 'fas fa-file-times' } = props @@ -14,7 +15,7 @@ export default function (props) { subtitle = selected } else if (!selected) { iconComponent = - subtitle = '(None)' + subtitle = `(${t('none')})` } else { iconComponent = subtitle = selected diff --git a/packages/navbar/src/User.js b/packages/navbar/src/User.js index 4e07e7b3..4e7e6756 100644 --- a/packages/navbar/src/User.js +++ b/packages/navbar/src/User.js @@ -12,6 +12,7 @@ import { withRouter } from 'react-router' import platform from '@obsidians/platform' import Auth from '@obsidians/auth' +import { t } from '@obsidians/i18n' class User extends Component { state = { @@ -67,7 +68,7 @@ class User extends Component { return ( this.props.history.push(`/local`)}> - My Projects + {t('user.myProjects')} ) @@ -78,7 +79,7 @@ class User extends Component { if (username) { return ( - Logged in as + {t('user.loginAs')} this.props.history.push(`/${username}`)}> {username} @@ -86,7 +87,7 @@ class User extends Component { {this.renderExtraLoggedInOptions()} Auth.logout(this.props.history)}> - Log out + {t('user.logout')} ) @@ -97,7 +98,7 @@ class User extends Component { {this.renderLoginButton()} this.props.history.push(`/local`)}> - My Projects + {t('user.myProjects')} ) @@ -110,7 +111,7 @@ class User extends Component { key={`login-${provider}`} onClick={() => Auth.login(this.props.history, provider)} > - { providers.length > 1 ? `Login ${provider}` : 'Login' } + { providers.length > 1 ? t('user.loginProvider', { provider }) : t('user.login') } )) } From f5e3e7d79a5488aecd6c0bfe3c9b32059800b753 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 22:37:42 +0800 Subject: [PATCH 09/16] feat: i18n queue --- packages/i18n/src/locales/en.js | 11 ++++++++++- packages/queue/package.json | 1 + packages/queue/src/QueueButton.js | 15 ++++++++------- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index c1f85821..dd50af5e 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -2,6 +2,7 @@ const translation = { loading: 'Loading...', loggingIn: 'Logging in...', none: 'None', + pending: 'Pending', button: { save: 'Save', cancel: 'Cancel', @@ -145,7 +146,15 @@ const translation = { loginAs: 'Logged in as', loginProvider: 'Login {{provider}}', logout: 'Log out', - } + }, + queue: { + transaction: { + transaction: 'Transaction', + transactions: 'Transactions', + all: 'All Transactions', + recent: 'Recent Transactions', + }, + }, } export default { diff --git a/packages/queue/package.json b/packages/queue/package.json index ecd5bd20..c1b5fe09 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -17,6 +17,7 @@ "prepare": "yarn build" }, "dependencies": { + "@obsidians/i18n": "^0.3.0", "@obsidians/redux": "^0.3.0", "@obsidians/ui-components": "^0.3.0", "moment": "^2.29.1" diff --git a/packages/queue/src/QueueButton.js b/packages/queue/src/QueueButton.js index b57ddd4f..f35cc9e9 100644 --- a/packages/queue/src/QueueButton.js +++ b/packages/queue/src/QueueButton.js @@ -7,6 +7,7 @@ import { DropdownMenu, DropdownItem, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import BaseQueueManager from './BaseQueueManager' @@ -43,7 +44,7 @@ export default class QueueButton extends PureComponent { pendingItems.push() pendingItems.unshift( - Pending + {t('pending')} ) } @@ -54,19 +55,19 @@ export default class QueueButton extends PureComponent { )).slice(0, 15) if (!txsItems.length) { - txsItems.push((None)) + txsItems.push(({t('none')})) } // txsItems.push() txsItems.unshift( - Recent Transactions + {t('queue.transaction.recent')} ) const allTransactions = (
- All Transactions... + {t('queue.transaction.all')}...
) @@ -86,7 +87,7 @@ export default class QueueButton extends PureComponent { return <> - {icon}Transactions + {icon}{t('queue.transaction.transactions')} {this.renderDropdownItems(BaseQueueManager.pending, txs?.toJS() || [], QueueItem)} @@ -94,7 +95,7 @@ export default class QueueButton extends PureComponent { From 3758d5c4d1f5fa0cfa1d06f579bc1eca81bd1a8f Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 22:41:20 +0800 Subject: [PATCH 10/16] feat: i18n terminal --- packages/i18n/src/locales/en.js | 4 ++++ packages/terminal/package.json | 1 + packages/terminal/src/index.js | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index dd50af5e..9589da46 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -155,6 +155,10 @@ const translation = { recent: 'Recent Transactions', }, }, + terminal: { + copied: 'Copied', + copiedMesage: 'The selection content is copied to the clipboard.' + }, } export default { diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 7841f345..9a459d35 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -19,6 +19,7 @@ "dependencies": { "@obsidians/file-ops": "^0.3.0", "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/notification": "^0.3.0", "@obsidians/pty": "^0.10.0", "@obsidians/ui-components": "^0.3.0", diff --git a/packages/terminal/src/index.js b/packages/terminal/src/index.js index 78983c54..47ede896 100644 --- a/packages/terminal/src/index.js +++ b/packages/terminal/src/index.js @@ -10,6 +10,7 @@ import chalk from 'chalk' import { getColor } from '@obsidians/ui-components' import notification from '@obsidians/notification' import fileOps from '@obsidians/file-ops' +import { t } from '@obsidians/i18n' import 'xterm/css/xterm.css' @@ -156,7 +157,7 @@ export default class Terminal extends PureComponent { if (this.props.onCopied) { this.props.onCopied() } - notification.success('Copied', 'The selection content is copied to the clipboard.') + notification.success(t('terminal.copied'), t('terminal.copiedMesage')) this.term.clearSelection() this.focus() }) From 214601bd3c836e0a28de8d86b578359a21632f16 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 22:52:03 +0800 Subject: [PATCH 11/16] feat: i18n ui-components --- packages/i18n/src/locales/en.js | 3 +++ packages/ui-components/src/Modal.js | 6 ++++-- packages/ui-components/src/buttons/DeleteButton.js | 4 +++- packages/ui-components/src/inputs/Dropdown.js | 4 +++- packages/ui-components/src/inputs/DropdownInput.js | 4 +++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 9589da46..7658e39a 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -3,11 +3,14 @@ const translation = { loggingIn: 'Logging in...', none: 'None', pending: 'Pending', + noOptions: 'No options', button: { save: 'Save', cancel: 'Cancel', dontSave: `Don't Save`, close: 'Close', + confirm: 'Confirm', + confirmDelete: 'Click again to delete', }, welcome: { welcome: 'Welcome to {{projectName}}', diff --git a/packages/ui-components/src/Modal.js b/packages/ui-components/src/Modal.js index 6d647331..5356ac18 100644 --- a/packages/ui-components/src/Modal.js +++ b/packages/ui-components/src/Modal.js @@ -11,6 +11,8 @@ import { Alert } from 'reactstrap' +import { t } from '@obsidians/i18n' + export default class BaseModal extends PureComponent { static propTypes = { wide: PropTypes.bool, @@ -63,7 +65,7 @@ export default class BaseModal extends PureComponent { toggle = () => this.closeModal() - renderConfirmButton = ({ onConfirm, colorConfirm = 'primary', confirmDisabled, textConfirm = 'Confirm', pending }) => { + renderConfirmButton = ({ onConfirm, colorConfirm = 'primary', confirmDisabled, textConfirm = t('button.confirm'), pending }) => { if (!onConfirm) { return null } @@ -105,7 +107,7 @@ export default class BaseModal extends PureComponent { textActions, colorActions = [], onActions, - textCancel = 'Cancel', + textCancel = t('button.cancel'), colorCancel = 'default', noCancel, onAdditionAction, diff --git a/packages/ui-components/src/buttons/DeleteButton.js b/packages/ui-components/src/buttons/DeleteButton.js index 9bde8a5e..713eb595 100644 --- a/packages/ui-components/src/buttons/DeleteButton.js +++ b/packages/ui-components/src/buttons/DeleteButton.js @@ -6,6 +6,8 @@ import { Tooltip, } from 'reactstrap' +import { t } from '@obsidians/i18n' + import IconButton from './IconButton' const deleteButtons = {} @@ -52,7 +54,7 @@ export default class DeleteButton extends PureComponent { render () { const { color = 'danger', - textConfirm = 'Click again to delete', + textConfirm = t('button.confirmDelete'), icon = 'far fa-trash-alt', className = '', } = this.props diff --git a/packages/ui-components/src/inputs/Dropdown.js b/packages/ui-components/src/inputs/Dropdown.js index a7a96845..513ea45e 100644 --- a/packages/ui-components/src/inputs/Dropdown.js +++ b/packages/ui-components/src/inputs/Dropdown.js @@ -7,13 +7,15 @@ import { DropdownItem, } from 'reactstrap' +import { t } from '@obsidians/i18n' + export default function Dropdown (props) { const [dropdownOpen, setDropdownOpen] = useState(false) const toggleDropDown = () => setDropdownOpen(!dropdownOpen) let dropdownItems = null if (!props.items || !props.items.length) { - dropdownItems = (None) + dropdownItems = ({t('none')}) } else { dropdownItems = props.items.map(item => ( {placeholder} } From f012950619ae7f57fd390eb2fef2dbb82d3f63db Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 22:58:33 +0800 Subject: [PATCH 12/16] feat: i18n user --- packages/i18n/src/locales/en.js | 8 ++++++++ packages/user/package.json | 1 + packages/user/src/ProjectList.js | 5 +++-- packages/user/src/UserHomepage.js | 9 +++++---- packages/user/src/UserProfile.js | 8 ++++---- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 7658e39a..25cb9c15 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -145,10 +145,18 @@ const translation = { }, user: { myProjects: 'My Projects', + noProject: 'No Project', login: 'Login', loginAs: 'Logged in as', loginProvider: 'Login {{provider}}', logout: 'Log out', + notLogin: 'not logged in', + new: 'New', + open: 'Open', + user: 'User', + notFound: 'Not Found', + projects: 'Projects', + noDescription: 'No description', }, queue: { transaction: { diff --git a/packages/user/package.json b/packages/user/package.json index 6c2a4c3e..60a60ccc 100644 --- a/packages/user/package.json +++ b/packages/user/package.json @@ -19,6 +19,7 @@ "dependencies": { "@obsidians/auth": "^0.3.0", "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/platform": "^0.3.0", "@obsidians/redux": "^0.3.0", "@obsidians/ui-components": "^0.3.0", diff --git a/packages/user/src/ProjectList.js b/packages/user/src/ProjectList.js index 38ccea84..915280bb 100644 --- a/packages/user/src/ProjectList.js +++ b/packages/user/src/ProjectList.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react' import { Link } from 'react-router-dom' import { ProjectPath } from '@obsidians/workspace' +import { t } from '@obsidians/i18n' export default class ProjectList extends PureComponent { renderProjectRow = (project, index) => { @@ -35,7 +36,7 @@ export default class ProjectList extends PureComponent {
- Loading... + {t('loading')}
- +
(No Project)({t('user.noProject')})
diff --git a/packages/user/src/UserHomepage.js b/packages/user/src/UserHomepage.js index 16430db9..ae488816 100644 --- a/packages/user/src/UserHomepage.js +++ b/packages/user/src/UserHomepage.js @@ -6,6 +6,7 @@ import { LoadingScreen, CenterScreen, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import platform from '@obsidians/platform' import redux, { connect } from '@obsidians/redux' @@ -91,7 +92,7 @@ class UserHomepage extends PureComponent { color='success' onClick={() => actions.newProject()} > - New + {t('user.new')} ) } @@ -107,7 +108,7 @@ class UserHomepage extends PureComponent { onClick={() => actions.openProject()} > - Open + {t('user.open')} ) } @@ -129,7 +130,7 @@ class UserHomepage extends PureComponent { if (loading) { return } else if (notfound) { - return User {user} Not Found + return {t('user.user')} {user} {t('user.notFound')} } return ( @@ -141,7 +142,7 @@ class UserHomepage extends PureComponent {

- Projects + {t('user.projects')}

diff --git a/packages/user/src/UserProfile.js b/packages/user/src/UserProfile.js index f2afa1ea..66b948b3 100644 --- a/packages/user/src/UserProfile.js +++ b/packages/user/src/UserProfile.js @@ -3,8 +3,8 @@ import React, { PureComponent } from 'react' import { Media, Button, - IconButton, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import platform from '@obsidians/platform' import Auth from '@obsidians/auth' @@ -41,7 +41,7 @@ export default class UserProfile extends PureComponent { if (!profile?.username) { return <> - (not logged in) + ({t('user.notLogin')}) {/*

Log in to synchronize your projects on the cloud.

*/}

{this.renderLoginButton()}

@@ -72,7 +72,7 @@ export default class UserProfile extends PureComponent { key={`user-profile-login-${provider}`} onClick={() => Auth.login(this.props.history, provider)} > - { providers.length > 1 ? `Login ${provider}` : 'Login' } + { providers.length > 1 ? t('user.loginProvider', { provider }) : t('user.login') } )) } @@ -81,7 +81,7 @@ export default class UserProfile extends PureComponent { if (desc) { return desc } - return (No description) + return ({t('user.noDescription')}) } render () { From 953088ed4bb8426410b0b670d4f1fa57d597ccf0 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 25 Mar 2021 23:52:43 +0800 Subject: [PATCH 13/16] feat: i18n workspace --- packages/file-ops/src/ElectronFileOps.js | 2 +- packages/file-ops/src/WebFileOps.js | 4 +- packages/i18n/src/locales/en.js | 54 +++++++++++++++++++ packages/workspace/package.json | 1 + .../src/ProjectManager/LocalProjectManager.js | 25 ++++----- .../ProjectManager/RemoteProjectManager.js | 19 +++---- packages/workspace/src/ProjectSettings.js | 2 +- packages/workspace/src/WorkspaceLoader.js | 3 +- packages/workspace/src/actions.js | 3 +- .../components/AbstractProjectSettingsTab.js | 11 ++-- .../components/CreateFileOrFolderModals.js | 13 ++--- .../src/components/NewProjectModal.js | 21 ++++---- .../src/components/ProjectInvalid.js | 5 +- .../src/components/ProjectLoading.js | 3 +- .../workspace/src/components/contextMenu.js | 19 +++---- packages/workspace/src/navbarItem.js | 11 ++-- 16 files changed, 131 insertions(+), 65 deletions(-) diff --git a/packages/file-ops/src/ElectronFileOps.js b/packages/file-ops/src/ElectronFileOps.js index 6422b793..a8221e1a 100644 --- a/packages/file-ops/src/ElectronFileOps.js +++ b/packages/file-ops/src/ElectronFileOps.js @@ -44,7 +44,7 @@ export default class ElectronFileOps extends FileOps { async chooseFolder (defaultPath = this.workspace) { const result = await this.electron.remote.dialog.showOpenDialog({ - buttonLabel: t('file.open'), + buttonLabel: t('workspace.open.title'), defaultPath: this.path.isAbsolute(defaultPath) ? defaultPath : this.path.join(this.workspace, defaultPath), properties: ['openDirectory', 'createDirectory'] }) diff --git a/packages/file-ops/src/WebFileOps.js b/packages/file-ops/src/WebFileOps.js index 2e1b79c7..b2f37251 100644 --- a/packages/file-ops/src/WebFileOps.js +++ b/packages/file-ops/src/WebFileOps.js @@ -42,7 +42,7 @@ export default class WebFileOps extends FileOps { async chooseFolder (defaultPath = this.workspace) { const result = await this.electron.remote.dialog.showOpenDialog({ - buttonLabel: t('file.open'), + buttonLabel: t('workspace.open.title'), defaultPath: this.path.isAbsolute(defaultPath) ? defaultPath : this.path.join(this.workspace, defaultPath), properties: ['openDirectory', 'createDirectory'] }) @@ -72,7 +72,7 @@ export default class WebFileOps extends FileOps { try { await this.fs.ensureDir(folderPath) } catch (e) { - throw new Error(`${t('file.error.createFolder')} ${folderPath}.`) + throw new Error(`${t('workspace.error.createFolder')} ${folderPath}.`) } } diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 25cb9c15..a96e1a39 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -3,6 +3,8 @@ const translation = { loggingIn: 'Logging in...', none: 'None', pending: 'Pending', + creating: 'Creating', + deleting: 'Deleting', noOptions: 'No options', button: { save: 'Save', @@ -11,6 +13,10 @@ const translation = { close: 'Close', confirm: 'Confirm', confirmDelete: 'Click again to delete', + delete: 'Delete', + create: 'Create', + choose: 'Choose', + trash: 'Move To Trash', }, welcome: { welcome: 'Welcome to {{projectName}}', @@ -170,6 +176,54 @@ const translation = { copied: 'Copied', copiedMesage: 'The selection content is copied to the clipboard.' }, + workspace: { + others: 'Others', + fileName: 'File name', + folderName: 'Folder name', + projectName: 'Project name', + projectPath: 'project path', + projectLocation: 'Project location', + projectLoading: 'Loading Project', + tempalte: 'Template', + new: { + file: 'New File', + folder: 'New Folder', + project: 'Create Project', + projectTitle: 'Create a New Project', + success: 'Successful', + successMessage: 'New project {{name}} is created.', + }, + open: { + title: 'Open', + folder: 'Open Containing Folder', + project: 'Open Project', + terminal: 'Open in Terminal', + }, + delete: { + project: 'Delete Project', + projectTitle: 'Are you sure to delete this project?', + fileConfirm: 'Are you sure you want to delete {{file}}?', + }, + remove: { + title: 'Remove', + success: 'Remove Project Successful', + successMessage: 'Project {{name}} is removed' + }, + error: { + createFile: 'Cannot Create File', + createFolder: 'Cannot Create Folder', + createProject: 'Cannot Create the Project', + projectNotFound: 'Project Not Found', + projectNotFoundDetail: 'Cannot find the project', + noMainFile: 'No main file in project settings', + noProject: 'No Project', + noProjectMessage: 'Please open a project first.', + fileExists: 'File {{file}} already exists.', + folderExists: 'Folder {{folder}} already exists.', + createFileFailed: 'Fail to create the file {{file}}.', + createFolderFailed: 'Fail to create the folder {{folder}}.', + }, + }, } export default { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 46281975..05441e2d 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -22,6 +22,7 @@ "@obsidians/file-ops": "^0.3.0", "@obsidians/filetree": "^0.3.0", "@obsidians/ipc": "^0.3.0", + "@obsidians/i18n": "^0.3.0", "@obsidians/notification": "^0.3.0", "@obsidians/platform": "^0.3.0", "@obsidians/redux": "^0.3.0", diff --git a/packages/workspace/src/ProjectManager/LocalProjectManager.js b/packages/workspace/src/ProjectManager/LocalProjectManager.js index 6d01b18f..45a516ae 100644 --- a/packages/workspace/src/ProjectManager/LocalProjectManager.js +++ b/packages/workspace/src/ProjectManager/LocalProjectManager.js @@ -1,6 +1,7 @@ import fileOps from '@obsidians/file-ops' import notification from '@obsidians/notification' import { modelSessionManager } from '@obsidians/code-editor' +import { t } from '@obsidians/i18n' import BaseProjectManager from './BaseProjectManager' @@ -56,7 +57,7 @@ export default class LocalProjectManager extends BaseProjectManager { if (this.projectSettings?.get('main')) { return this.pathForProjectFile(this.projectSettings.get('main')) } - throw new Error('No main file in project settings') + throw new Error(t('workspace.error.noMainFile')) } async isMainValid () { @@ -69,7 +70,7 @@ export default class LocalProjectManager extends BaseProjectManager { async checkSettings () { if (!this.project || !this.projectRoot) { - notification.error('No Project', 'Please open a project first.') + notification.error(t('workspace.error.noProject'), t('workspace.error.noProjectMessage')) return } @@ -79,16 +80,16 @@ export default class LocalProjectManager extends BaseProjectManager { async createNewFile (basePath, name) { const filePath = fileOps.current.path.join(basePath, name) if (await fileOps.current.isFile(filePath)) { - throw new Error(`File ${filePath} already exists.`) + throw new Error(t('workspace.error.fileExists', { file: filePath })) } - + try { await fileOps.current.fs.ensureFile(filePath) } catch (e) { if (e.code === 'EISDIR') { - throw new Error(`Folder ${filePath} already exists.`) + throw new Error(t('workspace.error.folderExists', { folder: filePath })) } else { - throw new Error(`Fail to create the file ${filePath}.`) + throw new Error(t('workspace.error.createFileFailed', { file: filePath })) } } } @@ -96,24 +97,24 @@ export default class LocalProjectManager extends BaseProjectManager { async createNewFolder (basePath, name) { const folderPath = fileOps.current.path.join(basePath, name) if (await fileOps.current.isDirectory(folderPath)) { - throw new Error(`Folder ${folderPath} already exists.`) + throw new Error(t('workspace.error.folderExists', { folder: folderPath })) } - + try { await fileOps.current.fs.ensureDir(folderPath) } catch (e) { if (e.code === 'EISDIR') { - throw new Error(`File ${folderPath} already exists.`) + throw new Error(t('workspace.error.fileExists', { file: folderPath })) } else { - throw new Error(`Fail to create the folder ${folderPath}.`) + throw new Error(t('workspace.error.createFolderFailed', { folder: folderPath })) } } } async deleteFile (node) { const { response } = await fileOps.current.showMessageBox({ - message: `Are you sure you want to delete ${node.path}?`, - buttons: ['Move to Trash', 'Cancel'] + message: t('workspace.delete.fileConfirm', { file: node.path }), + buttons: [t('button.trash'), t('button.cancel')] }) if (response === 0) { await fileOps.current.deleteFile(node.path) diff --git a/packages/workspace/src/ProjectManager/RemoteProjectManager.js b/packages/workspace/src/ProjectManager/RemoteProjectManager.js index 0decab02..e7893f65 100644 --- a/packages/workspace/src/ProjectManager/RemoteProjectManager.js +++ b/packages/workspace/src/ProjectManager/RemoteProjectManager.js @@ -1,5 +1,6 @@ import fileOps from '@obsidians/file-ops' import notification from '@obsidians/notification' +import { t } from '@obsidians/i18n' import BaseProjectManager from './BaseProjectManager' @@ -75,7 +76,7 @@ export default class RemoteProjectManager extends BaseProjectManager { if (this.projectSettings?.get('main')) { return this.pathForProjectFile(this.projectSettings.get('main')) } - throw new Error('No main file in project settings') + throw new Error(t('workspace.error.noMainFile')) } async isMainValid () { @@ -88,7 +89,7 @@ export default class RemoteProjectManager extends BaseProjectManager { async checkSettings () { if (!this.project || !this.projectRoot) { - notification.error('No Project', 'Please open a project first.') + notification.error(t('workspace.error.noProject'), t('workspace.error.noProjectMessage')) return } @@ -98,13 +99,13 @@ export default class RemoteProjectManager extends BaseProjectManager { async createNewFile (basePath, name) { const filePath = fileOps.current.path.join(basePath, name) if (await fileOps.current.isFile(filePath)) { - throw new Error(`File ${this.pathInProject(filePath)} already exists.`) + throw new Error(t('workspace.error.fileExists', { file: this.pathInProject(filePath) })) } - + try { await fileOps.current.fs.ensureFile(filePath) } catch (e) { - throw new Error(`Fail to create the file ${this.pathInProject(filePath)}.`) + throw new Error(t('workspace.error.createFileFailed', { file: this.pathInProject(filePath) })) } await this.refreshDirectory(basePath) @@ -112,11 +113,11 @@ export default class RemoteProjectManager extends BaseProjectManager { async createNewFolder (basePath, name) { const folderPath = fileOps.current.path.join(basePath, name) - + try { await fileOps.current.fs.ensureDir(folderPath) } catch (e) { - throw new Error(`Fail to create the folder ${this.pathInProject(folderPath)}.`) + throw new Error(t('workspace.error.createFolderFailed', { folder: this.pathInProject(folderPath) })) } await this.refreshDirectory(basePath) @@ -124,8 +125,8 @@ export default class RemoteProjectManager extends BaseProjectManager { async deleteFile (node) { const { response } = await fileOps.current.showMessageBox({ - message: `Are you sure you want to delete ${node.pathInProject}?`, - buttons: ['Delete', 'Cancel'] + message: t('workspace.delete.fileConfirm', { file: node.pathInProject }), + buttons: [t('button.delete'), t('button.cancel')] }) if (response === 0) { if (node.children) { diff --git a/packages/workspace/src/ProjectSettings.js b/packages/workspace/src/ProjectSettings.js index 2e10f0c3..adc80935 100644 --- a/packages/workspace/src/ProjectSettings.js +++ b/packages/workspace/src/ProjectSettings.js @@ -27,7 +27,7 @@ export default class ProjectSettings { async writeSettings(rawSettings) { const settings = this.trimSettings(rawSettings) - + const settingsJson = JSON.stringify(settings, null, 2) await fileOps.current.writeFile(this.settingFilePath, settingsJson) } diff --git a/packages/workspace/src/WorkspaceLoader.js b/packages/workspace/src/WorkspaceLoader.js index e3f73130..e364e8a6 100644 --- a/packages/workspace/src/WorkspaceLoader.js +++ b/packages/workspace/src/WorkspaceLoader.js @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react' import { Button } from '@obsidians/ui-components' import platform from '@obsidians/platform' +import { t } from '@obsidians/i18n' import Workspace from './components/Workspace' import WorkspaceContext from './WorkspaceContext' @@ -81,7 +82,7 @@ export default class WorkspaceLoader extends PureComponent { + >{t('workspace.remove.title')} ) } else { diff --git a/packages/workspace/src/actions.js b/packages/workspace/src/actions.js index e59b95de..145b203e 100644 --- a/packages/workspace/src/actions.js +++ b/packages/workspace/src/actions.js @@ -3,6 +3,7 @@ import fileOps from '@obsidians/file-ops' import redux from '@obsidians/redux' import notification from '@obsidians/notification' import platform from '@obsidians/platform' +import { t } from '@obsidians/i18n' import BaseProjectManager from './ProjectManager/BaseProjectManager' @@ -79,7 +80,7 @@ export class ProjectActions { this.history.replace(`/${author}`) } redux.dispatch('REMOVE_PROJECT', { id, type: 'local' }) - notification.info('Remove Project Successful', `Project ${name} is removed`) + notification.info(t('workspace.remove.success'), t('workspace.remove.successMessage', { name })) } } diff --git a/packages/workspace/src/components/AbstractProjectSettingsTab.js b/packages/workspace/src/components/AbstractProjectSettingsTab.js index a5df8e65..d4c8f724 100644 --- a/packages/workspace/src/components/AbstractProjectSettingsTab.js +++ b/packages/workspace/src/components/AbstractProjectSettingsTab.js @@ -4,6 +4,7 @@ import { Modal, Button, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import debounce from 'lodash/debounce' @@ -42,14 +43,14 @@ class DeleteButton extends PureComponent { } return <> -

Others

- +

{t('workspace.others')}

+ diff --git a/packages/workspace/src/components/CreateFileOrFolderModals.js b/packages/workspace/src/components/CreateFileOrFolderModals.js index 0b226d4f..4251b4a0 100644 --- a/packages/workspace/src/components/CreateFileOrFolderModals.js +++ b/packages/workspace/src/components/CreateFileOrFolderModals.js @@ -4,6 +4,7 @@ import { Modal, DebouncedFormGroup, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import notification from '@obsidians/notification' @@ -36,14 +37,14 @@ export default class CreateFileOrFolderModals extends PureComponent { try { await this.props.projectManager.createNewFile(basePath, name) } catch (e) { - notification.error('Cannot Create File', e.message) + notification.error(t('workspace.error.createFile'), e.message) return } } else if (this.state.type === 'folder') { try { await this.props.projectManager.createNewFolder(basePath, name) } catch (e) { - notification.error('Cannot Create Folder', e.message) + notification.error(t('workspace.error.createFolder'), e.message) return } } @@ -55,15 +56,15 @@ export default class CreateFileOrFolderModals extends PureComponent { return ( Create a new {this.state.type} in {this.state.baseName}
} - placeholder={this.state.type === 'file' ? 'File name' : 'Folder name'} + placeholder={this.state.type === 'file' ? t('workspace.fileName') : t('workspace.folderName')} maxLength='50' value={this.state.name} onChange={name => this.setState({ name })} diff --git a/packages/workspace/src/components/NewProjectModal.js b/packages/workspace/src/components/NewProjectModal.js index a18bc4cd..08da4737 100644 --- a/packages/workspace/src/components/NewProjectModal.js +++ b/packages/workspace/src/components/NewProjectModal.js @@ -11,6 +11,7 @@ import { DebouncedFormGroup, DropdownInput, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' import platform from '@obsidians/platform' import fileOps from '@obsidians/file-ops' @@ -89,12 +90,12 @@ export default class NewProjectModal extends PureComponent { try { const created = await BaseProjectManager.channel.invoke('post', '', options) if (notify) { - notification.success('Successful', `New project ${options.name} is created.`) + notification.success(t('workspace.new.success'), t('workspace.new.successMessage', { name: options.name })) } return created } catch (e) { if (notify) { - notification.error('Cannot Create the Project', e.message) + notification.error(t('workspace.error.createProject'), e.message) } return false } @@ -105,14 +106,14 @@ export default class NewProjectModal extends PureComponent { return null } - let placeholder = 'Project path' + let placeholder = t('workspace.projectPath') if (!this.state.projectRoot) { placeholder = this.path.join(fileOps.current.workspace, this.state.name || '') } return ( - + @@ -136,7 +137,7 @@ export default class NewProjectModal extends PureComponent { } return ( this.setState({ template, group })} @@ -156,15 +157,15 @@ export default class NewProjectModal extends PureComponent { {this.renderLocation()} this.setState({ name, invalid })} {...projectNameProps} diff --git a/packages/workspace/src/components/ProjectInvalid.js b/packages/workspace/src/components/ProjectInvalid.js index 5edea3d0..df0a7d76 100644 --- a/packages/workspace/src/components/ProjectInvalid.js +++ b/packages/workspace/src/components/ProjectInvalid.js @@ -2,12 +2,13 @@ import React from 'react' import { Screen, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' export default function ProjectInvalid ({ projectRoot, children }) { return ( -

Project Not Found

-

Cannot find the project {projectRoot}

+

{t('workspace.error.projectNotFound')}

+

{t('workspace.error.projectNotFoundDetail')} {projectRoot}

{ children &&
} {children}
diff --git a/packages/workspace/src/components/ProjectLoading.js b/packages/workspace/src/components/ProjectLoading.js index 3bc4fb82..1ad03fe3 100644 --- a/packages/workspace/src/components/ProjectLoading.js +++ b/packages/workspace/src/components/ProjectLoading.js @@ -2,11 +2,12 @@ import React from 'react' import { CenterScreen, } from '@obsidians/ui-components' +import { t } from '@obsidians/i18n' export default function ({ projectRoot }) { return ( - Loading Project {projectRoot}... + {t('workspace.projectLoading')} {projectRoot}... ) } diff --git a/packages/workspace/src/components/contextMenu.js b/packages/workspace/src/components/contextMenu.js index 896c1046..57182a2e 100644 --- a/packages/workspace/src/components/contextMenu.js +++ b/packages/workspace/src/components/contextMenu.js @@ -1,5 +1,6 @@ import platform from '@obsidians/platform' import fileOps from '@obsidians/file-ops' +import { t } from '@obsidians/i18n' const handlers = {} @@ -25,24 +26,24 @@ export const registerHandlers = ({ newFile, newFolder, deleteFile }) => { let contextMenu if (platform.isDesktop) { contextMenu = [ - { text: 'New File', onClick: node => handlers.newFile(node) }, - { text: 'New Folder', onClick: node => handlers.newFolder(node) }, + { text: t('workspace.new.file'), onClick: node => handlers.newFile(node) }, + { text: t('workspace.new.folder'), onClick: node => handlers.newFolder(node) }, null, - { text: 'Open' }, + { text: t('workspace.open.title') }, null, - { text: 'Open Containing Folder', onClick: showInFinder }, - { text: 'Open in Terminal', onClick: openInTerminal }, + { text: t('workspace.open.folder'), onClick: showInFinder }, + { text: t('workspace.open.terminal') , onClick: openInTerminal }, null, - { text: 'Delete', onClick: node => handlers.deleteFile(node) }, + { text: t('workspace.button.delete'), onClick: node => handlers.deleteFile(node) }, ] } else { contextMenu = [ - { text: 'New File', onClick: node => handlers.newFile(node) }, - { text: 'New Folder', onClick: node => handlers.newFolder(node) }, + { text: t('workspace.new.file'), onClick: node => handlers.newFile(node) }, + { text: t('workspace.new.folder'), onClick: node => handlers.newFolder(node) }, // null, // { text: 'Download' }, null, - { text: 'Delete', onClick: node => handlers.deleteFile(node) }, + { text: t('workspace.button.delete'), onClick: node => handlers.deleteFile(node) }, ] } diff --git a/packages/workspace/src/navbarItem.js b/packages/workspace/src/navbarItem.js index 298edc46..7d3e5a06 100644 --- a/packages/workspace/src/navbarItem.js +++ b/packages/workspace/src/navbarItem.js @@ -1,5 +1,6 @@ import platform from '@obsidians/platform' import fileOps from '@obsidians/file-ops' +import { t } from '@obsidians/i18n' import actions from './actions' const projectContextMenus = id => { @@ -13,16 +14,16 @@ const projectContextMenus = id => { return [ { - text: `Open Containing Folder`, + text: t('workspace.open.folder'), onClick: project => fileOps.current.openItem(project.path), }, { - text: `Open in Terminal`, + text: t('workspace.open.terminal'), onClick: project => fileOps.current.openInTerminal(project.path), }, null, { - text: `Remove`, + text: t('workspace.remove.title'), onClick: project => actions.removeProject(project), }, ] @@ -38,14 +39,14 @@ export default function navbarItem (projects, selected, username) { if (platform.isDesktop) { projectDropdown.unshift({ id: 'open-project', - name: 'Open Project...', + name: `${t('workspace.open.project')}...`, icon: 'fas fa-folder-plus', onClick: () => actions.openProject(), }) } projectDropdown.unshift({ id: 'new-project', - name: 'Create Project...', + name: `${t('workspace.new.project')}...`, icon: 'fas fa-plus', onClick: () => actions.newProject(), }) From 4da73b63f735842259913c3ffaaa5fa436becedb Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 26 Mar 2021 00:29:53 +0800 Subject: [PATCH 14/16] feat: i18n translate zh --- packages/i18n/src/locales/en.js | 9 +- packages/i18n/src/locales/zh.js | 210 ++++++++++++++++++ packages/user/src/UserHomepage.js | 4 +- .../workspace/src/components/contextMenu.js | 4 +- 4 files changed, 215 insertions(+), 12 deletions(-) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index a96e1a39..0dc7cd5b 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -73,12 +73,6 @@ const translation = { unknown: 'Something went wrong when starting Docker, please try again' } }, - file: { - open: 'Open', - error: { - createFolder: 'Fail to create the folder', - }, - }, global: { about: { title: 'About', @@ -157,8 +151,6 @@ const translation = { loginProvider: 'Login {{provider}}', logout: 'Log out', notLogin: 'not logged in', - new: 'New', - open: 'Open', user: 'User', notFound: 'Not Found', projects: 'Projects', @@ -186,6 +178,7 @@ const translation = { projectLoading: 'Loading Project', tempalte: 'Template', new: { + title: 'New', file: 'New File', folder: 'New Folder', project: 'Create Project', diff --git a/packages/i18n/src/locales/zh.js b/packages/i18n/src/locales/zh.js index 371a48d0..7978a389 100644 --- a/packages/i18n/src/locales/zh.js +++ b/packages/i18n/src/locales/zh.js @@ -1,12 +1,222 @@ const translation = { loading: '加载中', loggingIn: '登陆中...', + none: '空', + pending: '等待中', + creating: '创建中', + deleting: '删除中', + noOptions: '无选项', + button: { + save: '保存', + cancel: '取消', + dontSave: '不保存', + close: '关闭', + confirm: '确认', + confirmDelete: '再次点击删除', + delete: '删除', + create: '创建', + choose: '选择', + trash: '移除至垃圾桶', + }, welcome: { welcome: '欢迎使用 {{projectName}}', message: '{{projectName}} 是一个在 {{chainName}} 链上的集成开发环境,提供图形界面帮助开发者进行开发。请安装以下依赖工具来使用 {{projectName}}。', start: '开始使用', skip: '跳过', }, + editor: { + lost: '如果你关闭此页面,未保存的内容将会丢失。', + project: { + setting: '项目设置', + invalid: '无效项目', + cantReadSetting: '无法读取此项目设置', + makeSurePermission: '请确认当前用户有读/写权限', + }, + error: { + emptyPath: 'Empty path for "newModelSession"', + fileNotOpen: '文件 "{{path}}" 无法在当前工作区打开。', + noFile: '没有打开文件.', + saveFailed: '保存失败', + }, + }, + docker: { + need: '需要 Docker', + required: '需要 Docker 来启动本地节点。', + install: '安装', + installing: '正在安装', + installed: '已安装', + start: '启动 Docker', + starting: '正在启动 Docker', + started: '已启动', + privileges: '需要权限', + image: { + select: '选择 {{name}} 的版本', + noInstall:'({{name}} 没有安装)', + versions: '版本', + manager: '{{name}} 管理器', + downloading: '正在下载 {{name}}', + uninstallConfirm: '再次点击卸载', + availableVersions: '可选版本', + }, + badge: { + noSelect: '未选择', + noInstall: '未安装', + }, + table: { + version: '版本', + created: '创建于', + size: '大小', + }, + error: { + fail: '启动 docker 失败', + linuxPrivileges: '请确保非 root 用户拥有运行 docker 的权限', + unknown: '启动 Docker 时遇到来一些问题,请再次尝试' + } + }, + global: { + about: { + title: '关于', + contact: '联系我们', + website: '网站', + }, + update: { + available: '{{project}} 发现新版本 v{{version}}', + confirm: '重启并更新', + cancel: '稍后更新', + message: '你可以重启并更新到新版本,是否现在更新?', + } + }, + ipc: { + needLogin: '需要登陆来进行此操作。' + }, + keypair: { + title: '密钥对', + name: '名称', + address: '地址', + private: '私钥', + no: '无', + noKeypair: '没有密钥对', + reveal: '展示 {{name}}', + view: '查看', + create: { + title: '创建密钥对', + confirm: '创建', + creating: '正在创建...', + regenerate: '重新生成', + success: '成功创建 {{name}}', + successMessage: '新的密钥对 {{name}} 已创建并保存在 {{project}}。' + }, + import: { + title: '导入密钥对', + confirm: '导入', + importing: '正在导入...', + label: '输入 {{name}}', + success: '成功导入 {{name}}', + successMessage: '{{name}} 已导入至 {{project}}。' + }, + delete: { + success: '成功删除 {{name}}', + successMessage: '{{name}} 已从 {{project}} 移除。' + }, + modify: { + title: '修改密钥对名称', + saving: '保存中...', + }, + manager: { + title: '密钥管理器', + doNot: '请勿', + useOnMainnet: '在主网中使用!', + forDevOnly: '仅用于开发。', + saveUnencrypted: '为了方便开发,私钥未加密保存。' + }, + input: { + name: '名称', + placeholder: '请输入密钥对的名称', + }, + error: { + create: '创建密钥对失败', + import: '创建密钥对失败', + duplicateName: '此密钥对名称 {{name}} 已存在。', + duplicateKey: '此密钥对 {{address}} 已存在。', + notValid: '不合法 {{secret}}', + noKeypair: '没有密钥对 {{address}}', + noSecret: '此 {{address}} 没有 {{key}}', + }, + }, + user: { + myProjects: '我的项目', + noProject: '没有项目', + login: '登陆', + loginAs: '登陆身份', + loginProvider: '登陆至 {{provider}}', + logout: '登出', + notLogin: '未登陆', + user: '用户', + notFound: '未找到', + projects: '项目', + noDescription: '无简介', + }, + queue: { + transaction: { + transaction: '交易', + transactions: '交易', + all: '所有交易', + recent: '最近交易', + }, + }, + terminal: { + copied: '已复制', + copiedMesage: '选中的内容将复制到剪贴板。' + }, + workspace: { + others: '其他', + fileName: '文件名称', + folderName: '文件夹名称', + projectName: '项目名称', + projectPath: '项目路径', + projectLocation: '项目路径', + projectLoading: '项目读取中', + template: '模版', + new: { + title: '创建', + file: '创建文件', + folder: '创建文件夹', + project: '创建项目呢', + projectTitle: '创建新项目', + success: '成功', + successMessage: '新项目 {{name}} 已创建。', + }, + open: { + title: '打开', + folder: '打开文件夹', + project: '打开项目', + terminal: '在终端中打开', + }, + delete: { + project: '删除项目', + projectTitle: '是否确定删除当前项目?', + fileConfirm: '是否确定删除文件 {{file}} ?', + }, + remove: { + title: '移除', + success: '成功移除项目', + successMessage: '项目 {{name}} 已移除' + }, + error: { + createFile: '无法创建文件', + createFolder: '无法创建文件夹', + createProject: '无法创建项目', + projectNotFound: '项目未找到', + projectNotFoundDetail: '无法找到该项目', + noMainFile: '项目设置中没有指定主文件', + noProject: '没有项目', + noProjectMessage: '请首先打开项目。', + fileExists: '文件 {{file}} 已存在。', + folderExists: '文件夹 {{folder}} 已存在。', + createFileFailed: '创建文件 {{file}} 失败。', + createFolderFailed: '创建文件夹 {{folder}} 失败。', + }, + }, } export default { diff --git a/packages/user/src/UserHomepage.js b/packages/user/src/UserHomepage.js index ae488816..8b1978c5 100644 --- a/packages/user/src/UserHomepage.js +++ b/packages/user/src/UserHomepage.js @@ -92,7 +92,7 @@ class UserHomepage extends PureComponent { color='success' onClick={() => actions.newProject()} > - {t('user.new')} + {t('workspace.new.title')} ) } @@ -108,7 +108,7 @@ class UserHomepage extends PureComponent { onClick={() => actions.openProject()} > - {t('user.open')} + {t('workspace.open.title')} ) } diff --git a/packages/workspace/src/components/contextMenu.js b/packages/workspace/src/components/contextMenu.js index 57182a2e..3fa0cfd9 100644 --- a/packages/workspace/src/components/contextMenu.js +++ b/packages/workspace/src/components/contextMenu.js @@ -34,7 +34,7 @@ if (platform.isDesktop) { { text: t('workspace.open.folder'), onClick: showInFinder }, { text: t('workspace.open.terminal') , onClick: openInTerminal }, null, - { text: t('workspace.button.delete'), onClick: node => handlers.deleteFile(node) }, + { text: t('button.delete'), onClick: node => handlers.deleteFile(node) }, ] } else { contextMenu = [ @@ -43,7 +43,7 @@ if (platform.isDesktop) { // null, // { text: 'Download' }, null, - { text: t('workspace.button.delete'), onClick: node => handlers.deleteFile(node) }, + { text: t('button.delete'), onClick: node => handlers.deleteFile(node) }, ] } From 8482b36e963aca0ca96148a145f4df9192d557d6 Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 26 Mar 2021 01:33:56 +0800 Subject: [PATCH 15/16] feat: i18n add error --- packages/i18n/src/locales/en.js | 1 + packages/i18n/src/locales/zh.js | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 0dc7cd5b..25eb3efa 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -6,6 +6,7 @@ const translation = { creating: 'Creating', deleting: 'Deleting', noOptions: 'No options', + error: 'Error', button: { save: 'Save', cancel: 'Cancel', diff --git a/packages/i18n/src/locales/zh.js b/packages/i18n/src/locales/zh.js index 7978a389..ec0b9eaa 100644 --- a/packages/i18n/src/locales/zh.js +++ b/packages/i18n/src/locales/zh.js @@ -6,6 +6,7 @@ const translation = { creating: '创建中', deleting: '删除中', noOptions: '无选项', + error: '错误', button: { save: '保存', cancel: '取消', From b921ab4c87596511ba884c2dcb9ad0d12646fb2b Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 26 Mar 2021 01:57:23 +0800 Subject: [PATCH 16/16] feat: i18n add success --- packages/i18n/src/locales/en.js | 1 + packages/i18n/src/locales/zh.js | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/i18n/src/locales/en.js b/packages/i18n/src/locales/en.js index 25eb3efa..6660f57b 100644 --- a/packages/i18n/src/locales/en.js +++ b/packages/i18n/src/locales/en.js @@ -7,6 +7,7 @@ const translation = { deleting: 'Deleting', noOptions: 'No options', error: 'Error', + success: 'Success', button: { save: 'Save', cancel: 'Cancel', diff --git a/packages/i18n/src/locales/zh.js b/packages/i18n/src/locales/zh.js index ec0b9eaa..6d626725 100644 --- a/packages/i18n/src/locales/zh.js +++ b/packages/i18n/src/locales/zh.js @@ -7,6 +7,7 @@ const translation = { deleting: '删除中', noOptions: '无选项', error: '错误', + success: '成功', button: { save: '保存', cancel: '取消',