diff --git a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts index ef6213ea23..9b02df154c 100644 --- a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts +++ b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts @@ -133,6 +133,30 @@ module.exports = { .checkElementStyle(':root', '--info', remixIdeThemes.cyborg.info) .checkElementStyle(':root', '--warning', remixIdeThemes.cyborg.warning) .checkElementStyle(':root', '--danger', remixIdeThemes.cyborg.danger) + }, + + 'Should load zh-CN locale ': function (browser) { + browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000) + .scrollAndClick('*[data-id="settingsTabLocaleLabelzh-CN"]') + .pause(2000) + .assert.containsText('*[data-id="sidePanelSwapitTitle"]', '设置') + .assert.containsText('*[data-id="listenNetworkCheckInput"]', '监听所有交易') + .assert.containsText('*[data-id="settingsTabGenerateContractMetadataLabel"]', '生成合约元数据') + .assert.containsText('*[data-id="settingsAutoCompleteLabel"]', '在编辑器中启用代码自动补全') + .assert.containsText('*[data-id="settingsShowGasLabel"]', '在编辑器中展示 gas 预算') + .assert.containsText('*[data-id="displayErrorsLabel"]', '编辑代码时展示错误提示') + }, + + 'Should load en-US locale ': function (browser) { + browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000) + .scrollAndClick('*[data-id="settingsTabLocaleLabelen-US"]') + .pause(2000) + .assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SETTINGS') + .assert.containsText('*[data-id="listenNetworkCheckInput"]', 'listen on all transactions') + .assert.containsText('*[data-id="settingsTabGenerateContractMetadataLabel"]', 'Generate contract metadata') + .assert.containsText('*[data-id="settingsAutoCompleteLabel"]', 'Enable code completion in editor') + .assert.containsText('*[data-id="settingsShowGasLabel"]', 'Display gas estimates in editor') + .assert.containsText('*[data-id="displayErrorsLabel"]', 'Display errors in editor while typing') } } diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 7126e01283..6c1ebf6801 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -3,6 +3,7 @@ import { RunTab, makeUdapp } from './app/udapp' import { RemixEngine } from './remixEngine' import { RemixAppManager } from './remixAppManager' import { ThemeModule } from './app/tabs/theme-module' +import { LocaleModule } from './app/tabs/locale-module' import { NetworkModule } from './app/tabs/network-module' import { Web3ProviderModule } from './app/tabs/web3-provider' import { CompileAndRun } from './app/tabs/compile-and-run' @@ -142,7 +143,10 @@ class AppComponent { this.gistHandler = new GistHandler() // ----------------- theme service --------------------------------- this.themeModule = new ThemeModule() + // ----------------- locale service --------------------------------- + this.localeModule = new LocaleModule() Registry.getInstance().put({ api: this.themeModule, name: 'themeModule' }) + Registry.getInstance().put({ api: this.localeModule, name: 'localeModule' }) // ----------------- editor service ---------------------------- const editor = new Editor() // wrapper around ace editor @@ -238,6 +242,7 @@ class AppComponent { blockchain, contentImport, this.themeModule, + this.localeModule, editor, fileManager, compilerMetadataGenerator, @@ -369,7 +374,7 @@ class AppComponent { await this.appManager.activatePlugin(['layout']) await this.appManager.activatePlugin(['notification']) await this.appManager.activatePlugin(['editor']) - await this.appManager.activatePlugin(['permissionhandler', 'theme', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) + await this.appManager.activatePlugin(['permissionhandler', 'theme', 'locale', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter']) await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['home']) diff --git a/apps/remix-ide/src/app/components/plugin-manager-component.js b/apps/remix-ide/src/app/components/plugin-manager-component.js index 00013ee545..0013283861 100644 --- a/apps/remix-ide/src/app/components/plugin-manager-component.js +++ b/apps/remix-ide/src/app/components/plugin-manager-component.js @@ -99,7 +99,7 @@ class PluginManagerComponent extends ViewPlugin { return (
); - + } getAndFilterPlugins = (filter) => { diff --git a/apps/remix-ide/src/app/tabs/compile-tab.js b/apps/remix-ide/src/app/tabs/compile-tab.js index db61a6370f..04e33277e0 100644 --- a/apps/remix-ide/src/app/tabs/compile-tab.js +++ b/apps/remix-ide/src/app/tabs/compile-tab.js @@ -100,7 +100,7 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA this.renderComponent() // @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" const value = JSON.stringify(settings, null, '\t') - + this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value)) } diff --git a/apps/remix-ide/src/app/tabs/locale-module.js b/apps/remix-ide/src/app/tabs/locale-module.js new file mode 100644 index 0000000000..9a2c1b3b04 --- /dev/null +++ b/apps/remix-ide/src/app/tabs/locale-module.js @@ -0,0 +1,75 @@ +import { Plugin } from '@remixproject/engine' +import { EventEmitter } from 'events' +import { QueryParams } from '@remix-project/remix-lib' +import * as packageJson from '../../../../../package.json' +import Registry from '../state/registry' +import enUS from './locales/en-US' +import zhCN from './locales/zh-CN' +const _paq = window._paq = window._paq || [] + +const locales = [ + { name: 'en-US', messages: enUS }, + { name: 'zh-CN', messages: zhCN }, +] + +const profile = { + name: 'locale', + events: ['localeChanged'], + methods: ['switchLocale', 'getLocales', 'currentLocale'], + version: packageJson.version, + kind: 'locale' +} + +export class LocaleModule extends Plugin { + constructor () { + super(profile) + this.events = new EventEmitter() + this._deps = { + config: Registry.getInstance().get('config') && Registry.getInstance().get('config').api + } + this.locales = {} + locales.map((locale) => { + this.locales[locale.name.toLocaleLowerCase()] = locale + }) + this._paq = _paq + let queryLocale = (new QueryParams()).get().locale + queryLocale = queryLocale && queryLocale.toLocaleLowerCase() + queryLocale = this.locales[queryLocale] ? queryLocale : null + let currentLocale = (this._deps.config && this._deps.config.get('settings/locale')) || null + currentLocale = currentLocale && currentLocale.toLocaleLowerCase() + currentLocale = this.locales[currentLocale] ? currentLocale : null + this.currentLocaleState = { queryLocale, currentLocale } + this.active = queryLocale || currentLocale || 'en-us' + this.forced = !!queryLocale + } + + /** Return the active locale */ + currentLocale () { + return this.locales[this.active] + } + + /** Returns all locales as an array */ + getLocales () { + return Object.keys(this.locales).map(key => this.locales[key]) + } + + /** + * Change the current locale + * @param {string} [localeName] - The name of the locale + */ + switchLocale (localeName) { + localeName = localeName && localeName.toLocaleLowerCase() + if (localeName && !Object.keys(this.locales).includes(localeName)) { + throw new Error(`Locale ${localeName} doesn't exist`) + } + const next = localeName || this.active // Name + if (next === this.active) return // --> exit out of this method + _paq.push(['trackEvent', 'localeModule', 'switchTo', next]) + const nextLocale = this.locales[next] // Locale + if (!this.forced) this._deps.config.set('settings/locale', next) + + if (localeName) this.active = localeName + this.emit('localeChanged', nextLocale) + this.events.emit('localeChanged', nextLocale) + } +} diff --git a/apps/remix-ide/src/app/tabs/locales/en-US.js b/apps/remix-ide/src/app/tabs/locales/en-US.js new file mode 100644 index 0000000000..640ca2736d --- /dev/null +++ b/apps/remix-ide/src/app/tabs/locales/en-US.js @@ -0,0 +1,196 @@ +export default { + 'panel.author': 'Author', + 'panel.maintainedBy': 'Maintained By', + 'panel.documentation': 'Documentation', + 'panel.description': 'Description', + + 'settings.displayName': 'Settings', + 'settings.reset': 'Reset to Default settings', + 'settings.general': 'General settings', + 'settings.generateContractMetadataText': 'Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.', + 'settings.ethereunVMText': 'Always use Javascript VM at load', + 'settings.wordWrapText': 'Word wrap in editor', + 'settings.useAutoCompleteText': 'Enable code completion in editor.', + 'settings.useShowGasInEditorText': 'Display gas estimates in editor.', + 'settings.displayErrorsText': 'Display errors in editor while typing.', + 'settings.matomoAnalytics': 'Enable Matomo Analytics. We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI. See more about ', + 'settings.enablePersonalModeText': ' Enable Personal Mode for web3 provider. Transaction sent over Web3 will use the web3.personal API.\n', + 'settings.warnText': 'Be sure the endpoint is opened before enabling it. \nThis mode allows a user to provide a passphrase in the Remix interface without having to unlock the account. Although this is very convenient, you should completely trust the backend you are connected to (Geth, Parity, ...). Remix never persists any passphrase'.split('\n').map(s => s.trim()).join(' '), + 'settings.gitAccessTokenTitle': 'GitHub Access Token', + 'settings.gitAccessTokenText': 'Manage the access token used to publish to Gist and retrieve GitHub contents.', + 'settings.gitAccessTokenText2': 'Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only \'create gist\' permission.', + 'settings.etherscanTokenTitle': 'EtherScan Access Token', + 'settings.etherscanAccessTokenText': 'Manage the api key used to interact with Etherscan.', + 'settings.etherscanAccessTokenText2': 'Go to Etherscan api key page (link below) to create a new api key and save it in Remix.', + 'settings.save': 'Save', + 'settings.remove': 'Remove', + 'settings.themes': 'Themes', + 'settings.locales': 'Lanaguage', + 'settings.swarm': 'Swarm Settings', + 'settings.ipfs': 'IPFS Settings', + + 'filePanel.displayName': 'File explorer', + 'filePanel.workspace': 'WORKSPACES', + 'filePanel.create': 'Create', + 'filePanel.clone': 'Clone', + 'filePanel.download': 'Download', + 'filePanel.restore': 'Restore', + 'filePanel.workspace.create': 'Create Workspace', + 'filePanel.workspace.rename': 'Rename Workspace', + 'filePanel.workspace.delete': 'Delete Workspace', + 'filePanel.workspace.deleteConfirm': 'Are you sure to delete the current workspace?', + 'filePanel.workspace.name': 'Workspace name', + 'filePanel.workspace.chooseTemplate': 'Choose a template', + 'filePanel.workspace.download': 'Download Workspace', + 'filePanel.workspace.restore': 'Restore Workspace Backup', + 'filePanel.workspace.clone': 'Clone Git Repository', + 'filePanel.workspace.enterGitUrl': 'Enter git repository url', + 'filePanel.newFile': 'New File', + 'filePanel.newFolder': 'New Folder', + 'filePanel.rename': 'Rename', + 'filePanel.delete': 'Delete', + 'filePanel.deleteAll': 'Delete All', + 'filePanel.run': 'Run', + 'filePanel.pushChangesToGist': 'Push changes to gist', + 'filePanel.publishFolderToGist': 'Publish folder to gist', + 'filePanel.publishFileToGist': 'Publish file to gist', + 'filePanel.copy': 'Copy', + 'filePanel.paste': 'Paste', + 'filePanel.compile': 'Compile', + 'filePanel.compileForNahmii': 'Compile for Nahmii', + 'filePanel.createNewFile': 'Create New File', + 'filePanel.createNewFolder': 'Create New Folder', + 'filePanel.publishToGist': 'Publish all the current workspace files (only root) to a github gist', + 'filePanel.uploadFile': 'Load a local file into current workspace', + 'filePanel.updateGist': 'Update the current [gist] explorer', + + 'home.scamAlert': 'Scam Alert', + 'home.scamAlertText': 'The only URL Remix uses is remix.ethereum.org', + 'home.scamAlertText2': 'Beware of online videos promoting "liquidity front runner bots"', + 'home.scamAlertText3': 'Additional safety tips', + 'home.learnMore': 'Learn more', + 'home.here': 'here', + 'home.featuredPlugins': 'Featured Plugins', + 'home.files': 'Files', + 'home.newFile': 'New File', + 'home.openFile': 'Open File', + 'home.connectToLocalhost': 'Connect to Localhost', + 'home.loadFrom': 'LOAD FROM', + 'home.resources': 'Resources', + + 'terminal.listen': 'listen on all transactions', + 'terminal.search': 'Search with transaction hash or address', + 'terminal.used': 'used', + 'terminal.welcomeText1': 'Welcome to', + 'terminal.welcomeText2': 'Your files are stored in', + 'terminal.welcomeText3': 'You can use this terminal to', + 'terminal.welcomeText4': 'Check transactions details and start debugging', + 'terminal.welcomeText5': 'Execute JavaScript scripts', + 'terminal.welcomeText6': 'Input a script directly in the command line interface', + 'terminal.welcomeText7': 'Select a Javascript file in the file explorer and then run `remix.execute()` or `remix.exeCurrent()` in the command line interface', + 'terminal.welcomeText8': 'Right click on a JavaScript file in the file explorer and then click `Run`', + 'terminal.welcomeText9': 'The following libraries are accessible', + 'terminal.welcomeText10': 'Type the library name to see available commands', + + 'debugger.displayName': 'Debugger', + 'debugger.debuggerConfiguration': 'Debugger Configuration', + 'debugger.stopDebugging': 'Stop debugging', + 'debugger.startDebugging': 'Start debugging', + 'debugger.placeholder': 'Transaction hash, should start with 0x', + 'debugger.introduction': `When Debugging with a transaction hash, + if the contract is verified, Remix will try to fetch the source code from Sourcify or Etherscan. Put in your Etherscan API key in the Remix settings. + For supported networks, please see`, + + 'udapp.displayName': 'Deploy & run transactions', + 'udapp.gasLimit': 'Gas limit', + 'udapp.account': 'Account', + 'udapp.value': 'Value', + 'udapp.contract': 'Contract', + 'udapp.signAMessage': 'Sign a message', + 'udapp.enterAMessageToSign': 'Enter a message to sign', + 'udapp.hash': 'hash', + 'udapp.signature': 'signature', + 'udapp.signedMessage': 'Signed Message', + 'udapp.environment': 'Environment', + 'udapp.environmentDocs': 'Click for docs about Environment', + 'udapp.deploy': 'Deploy', + 'udapp.publishTo': 'Publish to', + 'udapp.or': 'or', + 'udapp.atAddress': 'At Address', + 'udapp.noCompiledContracts': 'No compiled contracts', + 'udapp.loadContractFromAddress': 'Load contract from Address', + 'udapp.deployedContracts': 'Deployed Contracts', + 'udapp.deployAndRunClearInstances': 'Clear instances list and reset recorder', + 'udapp.deployAndRunNoInstanceText': 'Currently you have no contract instances to interact with.', + 'udapp.transactionsRecorded': 'Transactions recorded', + + 'search.displayName': 'Search in files', + 'search.replace': 'Replace', + 'search.replaceAll': 'Replace All', + 'search.placeholder1': 'Search ( Enter to search )', + 'search.placeholder2': 'Include ie *.sol ( Enter to include )', + 'search.placeholder3': 'Exclude ie .git/**/* ( Enter to exclude )', + 'search.matchCase': 'Match Case', + 'search.matchWholeWord': 'Match Whole Word', + 'search.useRegularExpression': 'Use Regular Expression', + 'search.replaceWithoutConfirmation': 'replace without confirmation', + 'search.filesToInclude': 'Files to include', + 'search.filesToExclude': 'Files to exclude', + + 'solidity.displayName': 'Solidity compiler', + 'solidity.compiler': 'Compiler', + 'solidity.addACustomCompiler': 'Add a custom compiler', + 'solidity.addACustomCompilerWithURL': 'Add a custom compiler with URL', + 'solidity.includeNightlyBuilds': 'Include nightly builds', + 'solidity.autoCompile': 'Auto compile', + 'solidity.hideWarnings': 'Hide warnings', + 'solidity.enableHardhat': 'Enable Hardhat Compilation', + 'solidity.learnHardhat': 'Learn how to use Hardhat Compilation', + 'solidity.enableTruffle': 'Enable Truffle Compilation', + 'solidity.learnTruffle': 'Learn how to use Truffle Compilation', + 'solidity.advancedConfigurations': 'Advanced Configurations', + 'solidity.compilerConfiguration': 'Compiler configuration', + 'solidity.compilationDetails': 'Compilation Details', + 'solidity.language': 'Language', + 'solidity.evmVersion': 'EVM Version', + 'solidity.enableOptimization': 'Enable optimization', + 'solidity.useConfigurationFile': 'Use configuration file', + 'solidity.change': 'Change', + 'solidity.compile': 'Compile', + 'solidity.noFileSelected': 'no file selected', + 'solidity.compileAndRunScript': 'Compile and Run script', + 'solidity.publishOn': 'Publish on', + 'solidity.Assembly': 'Assembly opcodes describing the contract including corresponding solidity source code', + 'solidity.Opcodes': 'Assembly opcodes describing the contract', + 'solidity.name': 'Name of the compiled contract', + 'solidity.metadata': 'Contains all informations related to the compilation', + 'solidity.bytecode': 'Bytecode being executed during contract creation', + 'solidity.abi': 'ABI: describing all the functions (input/output params, scope, ...)', + 'solidity.web3Deploy': 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract', + 'solidity.metadataHash': 'Hash representing all metadata information', + 'solidity.functionHashes': 'List of declared function and their corresponding hash', + 'solidity.gasEstimates': 'Gas estimation for each function call', + 'solidity.Runtime Bytecode': 'Bytecode storing the state and being executed during normal contract call', + 'solidity.swarmLocation': 'Swarm url where all metadata information can be found (contract needs to be published first)', + + 'pluginManager.displayName': 'Plugin manager', + 'pluginManager.activate': 'Activate', + 'pluginManager.deactivate': 'Deactivate', + 'pluginManager.activeModules': 'Active Modules', + 'pluginManager.inactiveModules': 'Inactive Modules', + 'pluginManager.connectLocal': 'Connect to a Local Plugin', + 'pluginManager.localForm.title': 'Local Plugin', + 'pluginManager.localForm.pluginName': 'Plugin Name', + 'pluginManager.localForm.shouldBeCamelCase': 'Should be camelCase', + 'pluginManager.localForm.displayName': 'Display Name', + 'pluginManager.localForm.nameInTheHeader': 'Name in the header', + 'pluginManager.localForm.required': 'required', + 'pluginManager.localForm.commaSeparatedMethod': 'comma separated list of method names', + 'pluginManager.localForm.commaSeparatedPlugin': 'comma separated list of plugin names', + 'pluginManager.localForm.pluginsItCanActivate': 'Plugins it can activate', + 'pluginManager.localForm.typeOfConnection': 'Type of connection', + 'pluginManager.localForm.locationInRemix': 'Location in remix', + 'pluginManager.localForm.sidePanel': 'Side Panel', + 'pluginManager.localForm.mainPanel': 'Main Panel', + 'pluginManager.localForm.none': 'None', +} diff --git a/apps/remix-ide/src/app/tabs/locales/zh-CN.js b/apps/remix-ide/src/app/tabs/locales/zh-CN.js new file mode 100644 index 0000000000..a69d823b0f --- /dev/null +++ b/apps/remix-ide/src/app/tabs/locales/zh-CN.js @@ -0,0 +1,196 @@ +export default { + 'panel.author': '作者', + 'panel.maintainedBy': '维护者', + 'panel.documentation': '文档', + 'panel.description': '描述', + + 'settings.displayName': '设置', + 'settings.reset': '恢复默认设置', + 'settings.general': '常规设置', + 'settings.generateContractMetadataText': '生成合约元数据. 在contract文件夹中生成JSON文件. 允许指定合约依赖的库地址. 如果未指定任何内容,Remix将自动部署库.', + 'settings.ethereunVMText': '加载时始终使用以太坊虚拟机', + 'settings.wordWrapText': '文本换行', + 'settings.useAutoCompleteText': '在编辑器中启用代码自动补全.', + 'settings.useShowGasInEditorText': '在编辑器中展示 gas 预算.', + 'settings.displayErrorsText': '编辑代码时展示错误提示.', + 'settings.matomoAnalytics': '启用 Matomo 分析. 我们不会收集个人身份信息 (PII). 收集的信息只会用于提升 UX & UI. 查看更多关于 ', + 'settings.enablePersonalModeText': '为web3提供器启用私有模式. 通过Web3发送的交易将使用web3.personal API.\n', + 'settings.warnText': '在启用之前请确认访问端结点已经开放. \n此模式允许在Remix界面中提供密码而无需解锁账号. 虽然这很方便,但你应当完全信任所连接的后端节点 (Geth, Parity, ...). Remix不会持久化保存任何密码.'.split('\n').map(s => s.trim()).join(' '), + 'settings.gitAccessTokenTitle': 'Github 访问令牌', + 'settings.gitAccessTokenText': '管理用于发布到 Gist 以及读取 Github 内容的 GitHub 访问令牌.', + 'settings.gitAccessTokenText2': '前往 github (参见下方链接) 创建一个新的token,然后保存到Remix中. 确保这个token只有 \'create gist\' 权限.', + 'settings.etherscanTokenTitle': 'EtherScan 访问 Token', + 'settings.etherscanAccessTokenText': '管理用于与Etherscan交互的api密钥.', + 'settings.etherscanAccessTokenText2': '前往 Etherscan api 密钥页面 (参见下方链接),创建一个新的api密钥并保存到Remix中.', + 'settings.save': '保存', + 'settings.remove': '删除', + 'settings.themes': '主题', + 'settings.locales': '语言', + 'settings.swarm': 'Swarm 设置', + 'settings.ipfs': 'IPFS 设置', + + 'filePanel.displayName': '文件浏览器', + 'filePanel.workspace': '工作空间', + 'filePanel.create': '新建', + 'filePanel.clone': '克隆', + 'filePanel.download': '下载', + 'filePanel.restore': '恢复', + 'filePanel.workspace.create': '新建工作空间', + 'filePanel.workspace.rename': '重命名工作空间', + 'filePanel.workspace.delete': '删除工作空间', + 'filePanel.workspace.deleteConfirm': '确定要删除当前工作空间?', + 'filePanel.workspace.name': '工作空间名称', + 'filePanel.workspace.chooseTemplate': '选择一个工作空间模板', + 'filePanel.workspace.download': '下载工作空间', + 'filePanel.workspace.restore': '恢复工作空间', + 'filePanel.workspace.clone': '克隆 Git 仓库', + 'filePanel.workspace.enterGitUrl': '输入 Git 仓库地址', + 'filePanel.newFile': '新建文件', + 'filePanel.newFolder': '新建文件夹', + 'filePanel.rename': '重命名', + 'filePanel.delete': '删除', + 'filePanel.deleteAll': '删除所有', + 'filePanel.run': '运行', + 'filePanel.pushChangesToGist': '将修改推送到gist', + 'filePanel.publishFolderToGist': '将当前目录发布到gist', + 'filePanel.publishFileToGist': '将当前文件发布到gist', + 'filePanel.copy': '复制', + 'filePanel.paste': '黏贴', + 'filePanel.compile': '编译', + 'filePanel.compileForNahmii': 'Nahmii编译', + 'filePanel.createNewFile': '新建文件', + 'filePanel.createNewFolder': '新建文件夹', + 'filePanel.publishToGist': '将当前工作空间下所有文件发布到github gist', + 'filePanel.uploadFile': '加载本地文件到当前工作空间', + 'filePanel.updateGist': '更新当前 [gist] 浏览', + + 'home.scamAlert': '诈骗警报', + 'home.scamAlertText': 'Remix 唯一使用的 URL 是 remix.ethereum.org', + 'home.scamAlertText2': '注意那些宣传 "liquidity front runner bots" 的在线视频', + 'home.scamAlertText3': '其他安全提示', + 'home.learnMore': '了解更多', + 'home.here': '这里', + 'home.featuredPlugins': '精选插件', + 'home.files': '文件', + 'home.newFile': '新建文件', + 'home.openFile': '打开本地文件', + 'home.connectToLocalhost': '连接本地主机', + 'home.loadFrom': '从以下来源导入', + 'home.resources': '资源', + + 'terminal.listen': '监听所有交易', + 'terminal.search': '按交易哈希或地址搜索', + 'terminal.used': '已使用', + 'terminal.welcomeText1': '欢迎使用', + 'terminal.welcomeText2': '您的文件储存在', + 'terminal.welcomeText3': '您可使用此终端', + 'terminal.welcomeText4': '查看交易详情并启动调试', + 'terminal.welcomeText5': '执行 JavaScript 脚本', + 'terminal.welcomeText6': '直接在命令行界面输入脚本', + 'terminal.welcomeText7': '在文件浏览器中选择一个 Javascript 文件,然后在命令行界面运行 `remix.execute()` 或 `remix.exeCurrent()` ', + 'terminal.welcomeText8': '在文件浏览器中右键点击一个 JavaScript 文件,然后点击 `Run`', + 'terminal.welcomeText9': '可以访问以下库', + 'terminal.welcomeText10': '输入库名查看可用的指令', + + 'debugger.displayName': '调试器', + 'debugger.debuggerConfiguration': '调试器配置', + 'debugger.stopDebugging': '停止调试', + 'debugger.startDebugging': '开始调试', + 'debugger.placeholder': '交易哈希, 应该以 0x 开头', + 'debugger.introduction': `当使用交易哈希调试时, + 如果该合约已被验证, Remix 会试图从 Sourcify 或 Etherscan 获取源码. 在 Remix 中设置您的 Etherscan API key. + 有关受支持的网络,请参阅`, + + 'udapp.displayName': '部署 & 发交易', + 'udapp.gasLimit': 'Gas 上限', + 'udapp.account': '账户', + 'udapp.value': '以太币数量', + 'udapp.contract': '合约', + 'udapp.signAMessage': '给一个消息签名', + 'udapp.enterAMessageToSign': '输入一个需要签名的消息', + 'udapp.hash': '哈希', + 'udapp.signature': '签名', + 'udapp.signedMessage': '已签名的消息', + 'udapp.environment': '环境', + 'udapp.environmentDocs': '点击查看环境文档', + 'udapp.deploy': '部署', + 'udapp.publishTo': '发布到', + 'udapp.or': '或', + 'udapp.atAddress': 'At Address', + 'udapp.noCompiledContracts': '没有已编译的合约', + 'udapp.loadContractFromAddress': '加载此地址的合约', + 'udapp.deployedContracts': '已部署的合约', + 'udapp.deployAndRunClearInstances': '清空合约实例并重置交易记录', + 'udapp.deployAndRunNoInstanceText': '当前您没有可交互的合约实例.', + 'udapp.transactionsRecorded': '已记录的交易', + + 'search.displayName': '全文搜索', + 'search.replace': '替换', + 'search.replaceAll': '替换所有', + 'search.placeholder1': '搜索 ( 回车搜索 )', + 'search.placeholder2': '包含 ie *.sol ( 回车包含 )', + 'search.placeholder3': '排除 ie .git/**/* ( 回车排除 )', + 'search.matchCase': '大小写匹配', + 'search.matchWholeWord': '全字匹配', + 'search.useRegularExpression': '使用正则表达式', + 'search.replaceWithoutConfirmation': '替换无需确认', + 'search.filesToInclude': '文件包含', + 'search.filesToExclude': '文件排除', + + 'solidity.displayName': 'Solidity 编译器', + 'solidity.compiler': '编译器', + 'solidity.addACustomCompiler': '添加一个自定义编译器', + 'solidity.addACustomCompilerWithURL': '通过URL添加一个自定义编译器', + 'solidity.includeNightlyBuilds': '包含每日构造版本', + 'solidity.autoCompile': '自动编译', + 'solidity.hideWarnings': '隐藏警告', + 'solidity.enableHardhat': '启用 Hardhat 编译', + 'solidity.learnHardhat': '学习怎么使用 Hardhat 编译', + 'solidity.enableTruffle': '启用 Truffle 编译', + 'solidity.learnTruffle': '学习怎么使用 Truffle 编译', + 'solidity.advancedConfigurations': '高级配置', + 'solidity.compilerConfiguration': '编译器配置', + 'solidity.compilationDetails': '编译详情', + 'solidity.language': '语言', + 'solidity.evmVersion': 'EVM 版本', + 'solidity.enableOptimization': '启用优化', + 'solidity.useConfigurationFile': '使用配置文件', + 'solidity.change': '修改', + 'solidity.compile': '编译', + 'solidity.noFileSelected': '未选中文件', + 'solidity.compileAndRunScript': '编译且执行脚本', + 'solidity.publishOn': '发布到', + 'solidity.Assembly': '合约的汇编操作码,包含对应的solidity源程序', + 'solidity.Opcodes': '合约的汇编操作码', + 'solidity.name': '已编译合约的名称', + 'solidity.metadata': '包含编译相关的全部信息', + 'solidity.bytecode': '合约创建时执行的字节码', + 'solidity.abi': 'ABI: 全部合约函数的描述 (输入/输出 参数, 作用域, ...)', + 'solidity.web3Deploy': '拷贝/粘贴这部分代码到任何 JavaScript/Web3 控制台都可以部署此合约', + 'solidity.metadataHash': '元数据的哈希值', + 'solidity.functionHashes': '合约定义的函数清单,包含对应的哈希', + 'solidity.gasEstimates': '每个函数调用的Gas估算值', + 'solidity.Runtime Bytecode': '用于保存状态并在合约调用期执行的字节码', + 'solidity.swarmLocation': '可以找到所有元数据信息的Swarm url (首先需要发布合约) ', + + 'pluginManager.displayName': '插件管理', + 'pluginManager.activate': '启用', + 'pluginManager.deactivate': '停用', + 'pluginManager.activeModules': '启用的模块', + 'pluginManager.inactiveModules': '停用的模块', + 'pluginManager.connectLocal': '连接本地插件', + 'pluginManager.localForm.title': '本地插件', + 'pluginManager.localForm.pluginName': '插件名称', + 'pluginManager.localForm.shouldBeCamelCase': '应当采用驼峰命名法', + 'pluginManager.localForm.displayName': '展示名称', + 'pluginManager.localForm.nameInTheHeader': '标题中展示的名称', + 'pluginManager.localForm.required': '必填', + 'pluginManager.localForm.commaSeparatedMethod': '英文逗号分隔方法名称', + 'pluginManager.localForm.commaSeparatedPlugin': '英文逗号分隔插件名称', + 'pluginManager.localForm.pluginsItCanActivate': '能激活该插件的插件', + 'pluginManager.localForm.typeOfConnection': '连接类型', + 'pluginManager.localForm.locationInRemix': '在Remix中的位置', + 'pluginManager.localForm.sidePanel': '侧面板', + 'pluginManager.localForm.mainPanel': '主面板', + 'pluginManager.localForm.none': '无', +} diff --git a/apps/remix-ide/src/app/tabs/search.tsx b/apps/remix-ide/src/app/tabs/search.tsx index f9a00045cc..6705428a5f 100644 --- a/apps/remix-ide/src/app/tabs/search.tsx +++ b/apps/remix-ide/src/app/tabs/search.tsx @@ -21,8 +21,8 @@ export class SearchPlugin extends ViewPlugin { constructor () { super(profile) } - - render() { + + render() { return (
@@ -30,4 +30,4 @@ export class SearchPlugin extends ViewPlugin { ); } -} \ No newline at end of file +} diff --git a/apps/remix-ide/src/app/tabs/settings-tab.tsx b/apps/remix-ide/src/app/tabs/settings-tab.tsx index dbc73012be..c0d2e7d072 100644 --- a/apps/remix-ide/src/app/tabs/settings-tab.tsx +++ b/apps/remix-ide/src/app/tabs/settings-tab.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import React from 'react' // eslint-disable-line import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' @@ -23,8 +24,8 @@ module.exports = class SettingsTab extends ViewPlugin { config: any = {} editor: any private _deps: { - themeModule: any // eslint-disable-line - + themeModule: any + localeModule: any } element: HTMLDivElement public useMatomoAnalytics: any @@ -37,7 +38,8 @@ module.exports = class SettingsTab extends ViewPlugin { }) this.editor = editor this._deps = { - themeModule: Registry.getInstance().get('themeModule').api + themeModule: Registry.getInstance().get('themeModule').api, + localeModule: Registry.getInstance().get('localeModule').api } this.element = document.createElement('div') this.element.setAttribute('id', 'settingsTab') @@ -49,7 +51,7 @@ module.exports = class SettingsTab extends ViewPlugin { this.renderComponent() } - render() { + render() { return
@@ -62,6 +64,7 @@ module.exports = class SettingsTab extends ViewPlugin { _deps={state._deps} useMatomoAnalytics={state.useMatomoAnalytics} themeModule = {state._deps.themeModule} + localeModule={state._deps.localeModule} /> } @@ -80,4 +83,4 @@ module.exports = class SettingsTab extends ViewPlugin { ...this }) } -} \ No newline at end of file +} diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index f9a49bb7aa..1a4cade676 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -69,7 +69,7 @@ export class RunTab extends ViewPlugin { this.emit('clearAllInstancesReducer') } - addInstance (address, abi, name) { + addInstance (address, abi, name) { this.emit('addInstanceReducer', address, abi, name) } @@ -95,7 +95,7 @@ export class RunTab extends ViewPlugin { } onReady (api) { - this.REACT_API = api + this.REACT_API = api } async onInitDone () { @@ -174,7 +174,7 @@ export class RunTab extends ViewPlugin { } } }) - + await this.call('blockchain', 'addProvider', { name: 'Optimism Provider', isInjected: true, diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 0eac9055ff..4d79ab2705 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -6,7 +6,7 @@ const _paq = window._paq = window._paq || [] // requiredModule removes the plugin from the plugin manager list on UI const requiredModules = [ // services + layout views + system views - 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', + 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index 21b6da8ba7..c9517b2b19 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -9,6 +9,7 @@ import AppDialogs from './components/modals/dialogs' import DialogViewPlugin from './components/modals/dialogViewPlugin' import { AppContext } from './context/context' import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel' +import { IntlProvider } from 'react-intl' interface IRemixAppUi { app: any @@ -19,6 +20,7 @@ const RemixApp = (props: IRemixAppUi) => { const [hideSidePanel, setHideSidePanel] = useState(false) const [maximiseTrigger, setMaximiseTrigger] = useState(0) const [resetTrigger, setResetTrigger] = useState(0) + const [locale, setLocale] = useState<{ name:string; messages:any }>({ name:'', messages:{} }); const sidePanelRef = useRef(null) useEffect(() => { @@ -28,6 +30,7 @@ const RemixApp = (props: IRemixAppUi) => { props.app.activate() setListeners() }) + setLocale(props.app.localeModule.currentLocale()) } if (props.app) { activateApp() @@ -62,6 +65,9 @@ const RemixApp = (props: IRemixAppUi) => { return prev + 1 }) }) + props.app.localeModule.events.on('localeChanged', (nextLocale) => { + setLocale(nextLocale) + }) } const value = { @@ -73,22 +79,24 @@ const RemixApp = (props: IRemixAppUi) => { } return ( - - - + + + + -
-
{props.app.menuicons.render()}
-
{props.app.sidePanel.render()}
- -
- +
+
{props.app.menuicons.render()}
+
{props.app.sidePanel.render()}
+ +
+ +
-
-
{props.app.hiddenPanel.render()}
- - - +
{props.app.hiddenPanel.render()}
+ + + + ) } diff --git a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx index ae29aac379..f7135f74bb 100644 --- a/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line +import { FormattedMessage } from 'react-intl' import TxBrowser from './tx-browser/tx-browser' // eslint-disable-line import StepManager from './step-manager/step-manager' // eslint-disable-line import VmDebugger from './vm-debugger/vm-debugger' // eslint-disable-line @@ -386,9 +387,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
- When Debugging with a transaction hash, - if the contract is verified, Remix will try to fetch the source code from Sourcify or Etherscan. Put in your Etherscan API key in the Remix settings. - For supported networks, please see: https://sourcify.dev & https://etherscan.io/contractsVerified + : https://sourcify.dev & https://etherscan.io/contractsVerified
} { state.debugging && } diff --git a/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx b/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx index 57f46d0b59..ea0632c909 100644 --- a/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx +++ b/libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx @@ -1,5 +1,6 @@ import { CustomTooltip } from '@remix-ui/helper' import React, { useState, useEffect, useRef } from 'react' //eslint-disable-line +import { useIntl, FormattedMessage } from 'react-intl' import './tx-browser.css' export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, transactionNumber, debugging }) => { @@ -8,6 +9,9 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t }) const inputValue = useRef(null) + + const intl = useIntl() + useEffect(() => { setState(prevState => { return { @@ -56,7 +60,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t disabled={!state.txNumber } style={{ pointerEvents: 'none', color: 'white' }} > - { debugging ? 'Stop' : 'Start' } debugging +
) @@ -72,7 +76,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t type='text' onChange={({ target: { value } }) => txInputChanged(value)} onInput={txInputOnInput} - placeholder={'Transaction hash, should start with 0x'} + placeholder={intl.formatMessage({id: 'debugger.placeholder', defaultMessage: 'Transaction hash, should start with 0x'})} data-id='debuggerTransactionInput' disabled={debugging} /> @@ -80,7 +84,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
} tooltipId={'debuggingButtontooltip'} tooltipClasses="text-nowrap" > diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx index 2bae3dc53b..9fa324e9b2 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React, { useEffect, useRef, useContext } from 'react' +import { FormattedMessage } from 'react-intl' import PluginButton from './pluginButton' import { ThemeContext } from '../themeContext' import Carousel from 'react-multi-carousel' @@ -39,7 +40,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { } return false; } - + const handleScroll = (e) => { if (isDescendant(carouselRefDiv.current, e.target)) { e.stopPropagation() @@ -75,19 +76,19 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { await plugin.appManager.activatePlugin(['solidity', 'sourcify']) plugin.verticalIcons.select('sourcify') _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify']) - } + } const startSolidityUnitTesting = async () => { await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting']) plugin.verticalIcons.select('solidityUnitTesting') _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityUnitTesting']) } - + return (
- +
-
- - - + + + { event.stopPropagation() plugin.verticalIcons.select('filePanel') uploadFile(event.target) }} multiple /> - - + +
- +
@@ -164,4 +165,4 @@ function HomeTabFile ({plugin}: HomeTabFileProps) { ) } -export default HomeTabFile \ No newline at end of file +export default HomeTabFile diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx index 50fa8598f2..b789a9b4f5 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx @@ -1,25 +1,33 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React from 'react' +import { FormattedMessage } from 'react-intl' + const _paq = window._paq = window._paq || [] // eslint-disable-line function HomeTabScamAlert () { return ( diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx index a7b720513f..e7002db1e6 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx @@ -43,7 +43,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { }) }) }, []) - + return (
diff --git a/libs/remix-ui/locale-module/.babelrc b/libs/remix-ui/locale-module/.babelrc new file mode 100644 index 0000000000..09d67939cc --- /dev/null +++ b/libs/remix-ui/locale-module/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@nrwl/react/babel"], + "plugins": [] +} diff --git a/libs/remix-ui/locale-module/.eslintrc.json b/libs/remix-ui/locale-module/.eslintrc.json new file mode 100644 index 0000000000..50e59482cf --- /dev/null +++ b/libs/remix-ui/locale-module/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/remix-ui/locale-module/README.md b/libs/remix-ui/locale-module/README.md new file mode 100644 index 0000000000..affdd5db4e --- /dev/null +++ b/libs/remix-ui/locale-module/README.md @@ -0,0 +1,7 @@ +# remix-ui-locale-module + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test remix-ui-locale-module` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/remix-ui/locale-module/src/index.ts b/libs/remix-ui/locale-module/src/index.ts new file mode 100644 index 0000000000..fdda78b342 --- /dev/null +++ b/libs/remix-ui/locale-module/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/remix-ui-locale-module'; +export * from '../types/locale-module' diff --git a/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.module.css b/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.module.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx b/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx new file mode 100644 index 0000000000..0e68991102 --- /dev/null +++ b/libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx @@ -0,0 +1,56 @@ +import React, { useEffect, useState } from 'react'; +import { FormattedMessage } from 'react-intl' +import { LocaleModule } from '../../types/locale-module'; +import './remix-ui-locale-module.module.css'; + +export interface RemixUiLocaleModuleProps { + localeModule: LocaleModule; +} + +export function RemixUiLocaleModule({ localeModule }: RemixUiLocaleModuleProps) { + const [localeName, setLocaleName] = useState('') + + useEffect(() => { + localeModule.switchLocale() + }, [localeName, localeModule]) + + return ( +
+
+
+
+ {localeModule.getLocales() + ? localeModule.getLocales().map((locale, idx) => ( +
+ { + localeModule.switchLocale(locale.name); + setLocaleName(locale.name); + }} + className="align-middle custom-control-input" + name="locale" + id={locale.name} + data-id={`settingsTabLocale${locale.name}`} + checked={localeModule.active === locale.name.toLocaleLowerCase()} + /> + +
+ )) + : null} +
+
+
+ ) +} + +export default RemixUiLocaleModule; diff --git a/libs/remix-ui/locale-module/tsconfig.json b/libs/remix-ui/locale-module/tsconfig.json new file mode 100644 index 0000000000..8bd701c578 --- /dev/null +++ b/libs/remix-ui/locale-module/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/remix-ui/locale-module/tsconfig.lib.json b/libs/remix-ui/locale-module/tsconfig.lib.json new file mode 100644 index 0000000000..b560bc4dec --- /dev/null +++ b/libs/remix-ui/locale-module/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/remix-ui/locale-module/types/locale-module.ts b/libs/remix-ui/locale-module/types/locale-module.ts new file mode 100644 index 0000000000..535d6864e0 --- /dev/null +++ b/libs/remix-ui/locale-module/types/locale-module.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Plugin } from "@remixproject/engine/lib/abstract"; +import { EventEmitter } from "events"; +export interface LocaleModule extends Plugin { + currentLocaleState: Record; + new(): any; + events: EventEmitter; + _deps: { + config: any; + }; + _paq: any + element: HTMLDivElement; + locales: {[key: string]: Locale}; + active: string; + forced: boolean; + render(): HTMLDivElement; + renderComponent(): void; + /** Return the active locale */ + currentLocale(): Locale; + /** Returns all locales as an array */ + getLocales(): Locale[]; + /** + * Change the current locale + * @param {string} [localeName] - The name of the locale + */ + switchLocale(localeName?: string): void; +} + +interface Locale { name: string, messages: any } diff --git a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx index 8afe3c151e..0ef434bfba 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx +++ b/libs/remix-ui/panel/src/lib/plugins/panel-header.tsx @@ -1,5 +1,6 @@ /* eslint-disable jsx-a11y/anchor-has-content */ import React, { useEffect, useRef, useState } from 'react' // eslint-disable-line +import { FormattedMessage } from 'react-intl' import { PluginRecord } from '../types' import './panel.css' import { OverlayTrigger, Tooltip } from 'react-bootstrap' @@ -33,7 +34,9 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => { return (
-
{plugin?.profile.displayName || plugin?.profile.name}
+
+ +
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && ( @@ -64,21 +67,21 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
{plugin?.profile?.author && - + { plugin?.profile.author } } {plugin?.profile?.maintainedBy && - + { plugin?.profile.maintainedBy } } {plugin?.profile?.documentation && - + } {plugin?.profile?.description && - + { plugin?.profile.description } } {plugin?.profile?.repo && diff --git a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCardContainer.tsx b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCardContainer.tsx index 4c8f3dc58c..d83f4c09e9 100644 --- a/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCardContainer.tsx +++ b/libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCardContainer.tsx @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { Profile } from '@remixproject/plugin-utils' import React from 'react' // eslint-disable-line no-use-before-define +import { useIntl } from 'react-intl' import { PluginManagerComponent } from '../../types' import ActivePluginCard from './ActivePluginCard' import ModuleHeading from './moduleHeading' @@ -15,13 +16,15 @@ function ActivePluginCardContainer ({ pluginComponent }: ActivePluginCardContain pluginComponent.deactivateP(pluginName) } + const intl = useIntl(); + return ( - {(pluginComponent.activePlugins && pluginComponent.activePlugins.length) ? : null} + {(pluginComponent.activePlugins && pluginComponent.activePlugins.length) ? : null} {pluginComponent.activePlugins && pluginComponent.activePlugins.map((profile, idx) => { return ( - {(pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.length) ? : null} + {(pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.length) ? : null} {pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.map((profile, idx) => { return (
- + setName(e.target.value)} value={ name || '' } id="plugin-name" data-id="localPluginName" - placeholder="Should be camelCase" /> + placeholder={intl.formatMessage({ id: 'pluginManager.localForm.shouldBeCamelCase', defaultMessage: 'Should be camelCase' })} />
- + setDisplayName(e.target.value)} value={ displayName || '' } id="plugin-displayname" data-id="localPluginDisplayName" - placeholder="Name in the header" /> + placeholder={intl.formatMessage({ id: 'pluginManager.localForm.nameInTheHeader', defaultMessage: 'Name in the header' })} />
- + setMethods(e.target.value)} @@ -136,7 +147,11 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor placeholder="Methods" />
- + setCanactivate(e.target.value)} @@ -147,7 +162,11 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
- + setUrl(e.target.value)} @@ -156,7 +175,13 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor data-id="localPluginUrl" placeholder="ex: https://localhost:8000" />
-
Type of connection (required)
+
+ +   + + () + +
Websocket
-
Location in remix (required)
+
+ +   + + () + +
setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> - +
setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> - +
setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> - +
diff --git a/libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx b/libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx index 1359e04ec3..a5cb24a855 100644 --- a/libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx +++ b/libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React, { Fragment, ReactNode, useEffect, useState } from 'react' // eslint-disable-line no-use-before-define +import { FormattedMessage } from 'react-intl' import { PluginManagerComponent, PluginManagerSettings } from '../../types' import PermisssionsSettings from './permissionsSettings' import { Profile } from '@remixproject/plugin-utils' @@ -47,7 +48,7 @@ function RootView ({ pluginComponent, children }: RootViewProps) { data-id="pluginManagerComponentSearchInput" />
{children} diff --git a/libs/remix-ui/run-tab/src/lib/components/account.tsx b/libs/remix-ui/run-tab/src/lib/components/account.tsx index a74652ca61..6456827d49 100644 --- a/libs/remix-ui/run-tab/src/lib/components/account.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/account.tsx @@ -1,5 +1,6 @@ // eslint-disable-next-line no-use-before-define import React, { useEffect, useState, useRef } from 'react' +import { FormattedMessage, useIntl } from 'react-intl' import { CopyToClipboard } from '@remix-ui/clipboard' import { AccountProps } from '../types' import { PassphrasePrompt } from './passphrase' @@ -14,6 +15,8 @@ export function AccountUI (props: AccountProps) { }) const messageRef = useRef('') + const intl = useIntl() + useEffect(() => { if (!selectedAccount && accounts.length > 0) props.setAccount(accounts[0]) }, [accounts, selectedAccount]) @@ -79,7 +82,7 @@ export function AccountUI (props: AccountProps) { message='Enter your passphrase for this account to sign the message' setPassphrase={props.setPassphrase} />, 'OK', () => { - props.modal('Sign a message', signMessagePrompt(), 'OK', () => { + props.modal(intl.formatMessage({id: 'udapp.signAMessage', defaultMessage: 'Sign a message'}), signMessagePrompt(), 'OK', () => { props.signMessageWithAddress(selectedAccount, messageRef.current, signedMessagePrompt, props.passphrase) props.setPassphrase('') }, 'Cancel', null) @@ -88,7 +91,7 @@ export function AccountUI (props: AccountProps) { }) } - props.modal('Sign a message', signMessagePrompt(), 'OK', () => { + props.modal(intl.formatMessage({id: 'udapp.signAMessage', defaultMessage: 'Sign a message'}), signMessagePrompt(), 'OK', () => { props.signMessageWithAddress(selectedAccount, messageRef.current, signedMessagePrompt) }, 'Cancel', null) } @@ -120,7 +123,7 @@ export function AccountUI (props: AccountProps) { const signMessagePrompt = () => { return ( -
Enter a message to sign +