Merge pull request #2581 from drafish/intl

UI Translation
pull/3121/head
yann300 2 years ago committed by GitHub
commit 81038a996b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      apps/remix-ide-e2e/src/tests/generalSettings.test.ts
  2. 7
      apps/remix-ide/src/app.js
  3. 2
      apps/remix-ide/src/app/components/plugin-manager-component.js
  4. 2
      apps/remix-ide/src/app/tabs/compile-tab.js
  5. 75
      apps/remix-ide/src/app/tabs/locale-module.js
  6. 196
      apps/remix-ide/src/app/tabs/locales/en-US.js
  7. 196
      apps/remix-ide/src/app/tabs/locales/zh-CN.js
  8. 6
      apps/remix-ide/src/app/tabs/search.tsx
  9. 13
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  10. 6
      apps/remix-ide/src/app/udapp/run-tab.js
  11. 2
      apps/remix-ide/src/remixAppManager.js
  12. 36
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  13. 7
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  14. 10
      libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx
  15. 13
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  16. 17
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  17. 22
      libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx
  18. 2
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  19. 4
      libs/remix-ui/locale-module/.babelrc
  20. 18
      libs/remix-ui/locale-module/.eslintrc.json
  21. 7
      libs/remix-ui/locale-module/README.md
  22. 2
      libs/remix-ui/locale-module/src/index.ts
  23. 0
      libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.module.css
  24. 56
      libs/remix-ui/locale-module/src/lib/remix-ui-locale-module.tsx
  25. 20
      libs/remix-ui/locale-module/tsconfig.json
  26. 13
      libs/remix-ui/locale-module/tsconfig.lib.json
  27. 29
      libs/remix-ui/locale-module/types/locale-module.ts
  28. 13
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  29. 7
      libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCardContainer.tsx
  30. 7
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCardContainer.tsx
  31. 59
      libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx
  32. 3
      libs/remix-ui/plugin-manager/src/lib/components/rootView.tsx
  33. 15
      libs/remix-ui/run-tab/src/lib/components/account.tsx
  34. 25
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  35. 7
      libs/remix-ui/run-tab/src/lib/components/deployButton.tsx
  36. 8
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  37. 3
      libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx
  38. 9
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  39. 5
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  40. 3
      libs/remix-ui/run-tab/src/lib/components/value.tsx
  41. 7
      libs/remix-ui/search/src/lib/components/Exclude.tsx
  42. 31
      libs/remix-ui/search/src/lib/components/Find.tsx
  43. 8
      libs/remix-ui/search/src/lib/components/Include.tsx
  44. 3
      libs/remix-ui/search/src/lib/components/OverWriteCheck.tsx
  45. 5
      libs/remix-ui/search/src/lib/components/Replace.tsx
  46. 9
      libs/remix-ui/search/src/lib/components/results/ResultItem.tsx
  47. 18
      libs/remix-ui/settings/src/lib/etherscan-settings.tsx
  48. 20
      libs/remix-ui/settings/src/lib/github-settings.tsx
  49. 51
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  50. 93
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  51. 15
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  52. 9
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  53. 21
      libs/remix-ui/terminal/src/lib/terminalWelcome.tsx
  54. 3
      libs/remix-ui/theme-module/src/lib/remix-ui-theme-module.tsx
  55. 4
      libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx
  56. 5
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  57. 68
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  58. 5
      nx.json
  59. 4
      package.json
  60. 2
      tsconfig.base.json
  61. 15
      workspace.json
  62. 105
      yarn.lock

@ -133,6 +133,30 @@ module.exports = {
.checkElementStyle(':root', '--info', remixIdeThemes.cyborg.info) .checkElementStyle(':root', '--info', remixIdeThemes.cyborg.info)
.checkElementStyle(':root', '--warning', remixIdeThemes.cyborg.warning) .checkElementStyle(':root', '--warning', remixIdeThemes.cyborg.warning)
.checkElementStyle(':root', '--danger', remixIdeThemes.cyborg.danger) .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')
} }
} }

@ -3,6 +3,7 @@ import { RunTab, makeUdapp } from './app/udapp'
import { RemixEngine } from './remixEngine' import { RemixEngine } from './remixEngine'
import { RemixAppManager } from './remixAppManager' import { RemixAppManager } from './remixAppManager'
import { ThemeModule } from './app/tabs/theme-module' import { ThemeModule } from './app/tabs/theme-module'
import { LocaleModule } from './app/tabs/locale-module'
import { NetworkModule } from './app/tabs/network-module' import { NetworkModule } from './app/tabs/network-module'
import { Web3ProviderModule } from './app/tabs/web3-provider' import { Web3ProviderModule } from './app/tabs/web3-provider'
import { CompileAndRun } from './app/tabs/compile-and-run' import { CompileAndRun } from './app/tabs/compile-and-run'
@ -142,7 +143,10 @@ class AppComponent {
this.gistHandler = new GistHandler() this.gistHandler = new GistHandler()
// ----------------- theme service --------------------------------- // ----------------- theme service ---------------------------------
this.themeModule = new ThemeModule() this.themeModule = new ThemeModule()
// ----------------- locale service ---------------------------------
this.localeModule = new LocaleModule()
Registry.getInstance().put({ api: this.themeModule, name: 'themeModule' }) Registry.getInstance().put({ api: this.themeModule, name: 'themeModule' })
Registry.getInstance().put({ api: this.localeModule, name: 'localeModule' })
// ----------------- editor service ---------------------------- // ----------------- editor service ----------------------------
const editor = new Editor() // wrapper around ace editor const editor = new Editor() // wrapper around ace editor
@ -238,6 +242,7 @@ class AppComponent {
blockchain, blockchain,
contentImport, contentImport,
this.themeModule, this.themeModule,
this.localeModule,
editor, editor,
fileManager, fileManager,
compilerMetadataGenerator, compilerMetadataGenerator,
@ -369,7 +374,7 @@ class AppComponent {
await this.appManager.activatePlugin(['layout']) await this.appManager.activatePlugin(['layout'])
await this.appManager.activatePlugin(['notification']) await this.appManager.activatePlugin(['notification'])
await this.appManager.activatePlugin(['editor']) 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(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['home'])

@ -99,7 +99,7 @@ class PluginManagerComponent extends ViewPlugin {
return ( return (
<div id='pluginManager'><PluginViewWrapper plugin={this} /></div> <div id='pluginManager'><PluginViewWrapper plugin={this} /></div>
); );
} }
getAndFilterPlugins = (filter) => { getAndFilterPlugins = (filter) => {

@ -100,7 +100,7 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent() this.renderComponent()
// @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval" // @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') const value = JSON.stringify(settings, null, '\t')
this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value)) this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value))
} }

@ -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)
}
}

@ -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',
}

@ -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': '无',
}

@ -21,8 +21,8 @@ export class SearchPlugin extends ViewPlugin {
constructor () { constructor () {
super(profile) super(profile)
} }
render() { render() {
return ( return (
<div id='searchTab'> <div id='searchTab'>
<SearchTab plugin={this}></SearchTab> <SearchTab plugin={this}></SearchTab>
@ -30,4 +30,4 @@ export class SearchPlugin extends ViewPlugin {
); );
} }
} }

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
@ -23,8 +24,8 @@ module.exports = class SettingsTab extends ViewPlugin {
config: any = {} config: any = {}
editor: any editor: any
private _deps: { private _deps: {
themeModule: any // eslint-disable-line themeModule: any
localeModule: any
} }
element: HTMLDivElement element: HTMLDivElement
public useMatomoAnalytics: any public useMatomoAnalytics: any
@ -37,7 +38,8 @@ module.exports = class SettingsTab extends ViewPlugin {
}) })
this.editor = editor this.editor = editor
this._deps = { 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 = document.createElement('div')
this.element.setAttribute('id', 'settingsTab') this.element.setAttribute('id', 'settingsTab')
@ -49,7 +51,7 @@ module.exports = class SettingsTab extends ViewPlugin {
this.renderComponent() this.renderComponent()
} }
render() { render() {
return <div id='settingsTab'> return <div id='settingsTab'>
<PluginViewWrapper plugin={this} /> <PluginViewWrapper plugin={this} />
</div> </div>
@ -62,6 +64,7 @@ module.exports = class SettingsTab extends ViewPlugin {
_deps={state._deps} _deps={state._deps}
useMatomoAnalytics={state.useMatomoAnalytics} useMatomoAnalytics={state.useMatomoAnalytics}
themeModule = {state._deps.themeModule} themeModule = {state._deps.themeModule}
localeModule={state._deps.localeModule}
/> />
} }
@ -80,4 +83,4 @@ module.exports = class SettingsTab extends ViewPlugin {
...this ...this
}) })
} }
} }

@ -69,7 +69,7 @@ export class RunTab extends ViewPlugin {
this.emit('clearAllInstancesReducer') this.emit('clearAllInstancesReducer')
} }
addInstance (address, abi, name) { addInstance (address, abi, name) {
this.emit('addInstanceReducer', address, abi, name) this.emit('addInstanceReducer', address, abi, name)
} }
@ -95,7 +95,7 @@ export class RunTab extends ViewPlugin {
} }
onReady (api) { onReady (api) {
this.REACT_API = api this.REACT_API = api
} }
async onInitDone () { async onInitDone () {
@ -174,7 +174,7 @@ export class RunTab extends ViewPlugin {
} }
} }
}) })
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
name: 'Optimism Provider', name: 'Optimism Provider',
isInjected: true, isInjected: true,

@ -6,7 +6,7 @@ const _paq = window._paq = window._paq || []
// requiredModule removes the plugin from the plugin manager list on UI // requiredModule removes the plugin from the plugin manager list on UI
const requiredModules = [ // services + layout views + system views 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', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',

@ -9,6 +9,7 @@ import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin' import DialogViewPlugin from './components/modals/dialogViewPlugin'
import { AppContext } from './context/context' import { AppContext } from './context/context'
import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel' import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import { IntlProvider } from 'react-intl'
interface IRemixAppUi { interface IRemixAppUi {
app: any app: any
@ -19,6 +20,7 @@ const RemixApp = (props: IRemixAppUi) => {
const [hideSidePanel, setHideSidePanel] = useState<boolean>(false) const [hideSidePanel, setHideSidePanel] = useState<boolean>(false)
const [maximiseTrigger, setMaximiseTrigger] = useState<number>(0) const [maximiseTrigger, setMaximiseTrigger] = useState<number>(0)
const [resetTrigger, setResetTrigger] = useState<number>(0) const [resetTrigger, setResetTrigger] = useState<number>(0)
const [locale, setLocale] = useState<{ name:string; messages:any }>({ name:'', messages:{} });
const sidePanelRef = useRef(null) const sidePanelRef = useRef(null)
useEffect(() => { useEffect(() => {
@ -28,6 +30,7 @@ const RemixApp = (props: IRemixAppUi) => {
props.app.activate() props.app.activate()
setListeners() setListeners()
}) })
setLocale(props.app.localeModule.currentLocale())
} }
if (props.app) { if (props.app) {
activateApp() activateApp()
@ -62,6 +65,9 @@ const RemixApp = (props: IRemixAppUi) => {
return prev + 1 return prev + 1
}) })
}) })
props.app.localeModule.events.on('localeChanged', (nextLocale) => {
setLocale(nextLocale)
})
} }
const value = { const value = {
@ -73,22 +79,24 @@ const RemixApp = (props: IRemixAppUi) => {
} }
return ( return (
<AppProvider value={value}> <IntlProvider locale={locale.name} messages={locale.messages}>
<OriginWarning></OriginWarning> <AppProvider value={value}>
<MatomoDialog hide={!appReady}></MatomoDialog> <OriginWarning></OriginWarning>
<MatomoDialog hide={!appReady}></MatomoDialog>
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE"> <div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
<div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div> <div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div>
<div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div> <div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div>
<DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={250} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar> <DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={250} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'> <div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'>
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel> <RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
</div>
</div> </div>
</div> <div>{props.app.hiddenPanel.render()}</div>
<div>{props.app.hiddenPanel.render()}</div> <AppDialogs></AppDialogs>
<AppDialogs></AppDialogs> <DialogViewPlugin></DialogViewPlugin>
<DialogViewPlugin></DialogViewPlugin> </AppProvider>
</AppProvider> </IntlProvider>
) )
} }

@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line 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 TxBrowser from './tx-browser/tx-browser' // eslint-disable-line
import StepManager from './step-manager/step-manager' // eslint-disable-line import StepManager from './step-manager/step-manager' // eslint-disable-line
import VmDebugger from './vm-debugger/vm-debugger' // eslint-disable-line import VmDebugger from './vm-debugger/vm-debugger' // eslint-disable-line
@ -386,9 +387,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div> <div>
<i className="fas fa-info-triangle" aria-hidden="true"></i> <i className="fas fa-info-triangle" aria-hidden="true"></i>
<span> <span>
When Debugging with a transaction hash, <FormattedMessage id='debugger.introduction' defaultMessage='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. 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: <a href="https://sourcify.dev" target="__blank" >https://sourcify.dev</a> & <a href="https://etherscan.io/contractsVerified" target="__blank">https://etherscan.io/contractsVerified</a> For supported networks, please see' />: <a href="https://sourcify.dev" target="__blank" >https://sourcify.dev</a> & <a href="https://etherscan.io/contractsVerified" target="__blank">https://etherscan.io/contractsVerified</a>
</span> </span>
</div> } </div> }
{ state.debugging && <StepManager stepManager={ stepManager } /> } { state.debugging && <StepManager stepManager={ stepManager } /> }

@ -1,5 +1,6 @@
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect, useRef } from 'react' //eslint-disable-line import React, { useState, useEffect, useRef } from 'react' //eslint-disable-line
import { useIntl, FormattedMessage } from 'react-intl'
import './tx-browser.css' import './tx-browser.css'
export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, transactionNumber, debugging }) => { export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, transactionNumber, debugging }) => {
@ -8,6 +9,9 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
}) })
const inputValue = useRef(null) const inputValue = useRef(null)
const intl = useIntl()
useEffect(() => { useEffect(() => {
setState(prevState => { setState(prevState => {
return { return {
@ -56,7 +60,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
disabled={!state.txNumber } disabled={!state.txNumber }
style={{ pointerEvents: 'none', color: 'white' }} style={{ pointerEvents: 'none', color: 'white' }}
> >
<span>{ debugging ? 'Stop' : 'Start' } debugging</span> <span><FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} defaultMessage={debugging ? 'Stop debugging' : 'Start debugging'} /></span>
</button> </button>
</div> </div>
) )
@ -72,7 +76,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
type='text' type='text'
onChange={({ target: { value } }) => txInputChanged(value)} onChange={({ target: { value } }) => txInputChanged(value)}
onInput={txInputOnInput} 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' data-id='debuggerTransactionInput'
disabled={debugging} disabled={debugging}
/> />
@ -80,7 +84,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
<div className='d-flex justify-content-center w-100 btn-group py-1'> <div className='d-flex justify-content-center w-100 btn-group py-1'>
<CustomTooltip <CustomTooltip
placement="bottom" placement="bottom"
tooltipText={debugging ? 'Stop debugging' : 'Start debugging'} tooltipText={<FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} defaultMessage={debugging ? 'Stop debugging' : 'Start debugging'} />}
tooltipId={'debuggingButtontooltip'} tooltipId={'debuggingButtontooltip'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
> >

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect, useRef, useContext } from 'react' import React, { useEffect, useRef, useContext } from 'react'
import { FormattedMessage } from 'react-intl'
import PluginButton from './pluginButton' import PluginButton from './pluginButton'
import { ThemeContext } from '../themeContext' import { ThemeContext } from '../themeContext'
import Carousel from 'react-multi-carousel' import Carousel from 'react-multi-carousel'
@ -39,7 +40,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
} }
return false; return false;
} }
const handleScroll = (e) => { const handleScroll = (e) => {
if (isDescendant(carouselRefDiv.current, e.target)) { if (isDescendant(carouselRefDiv.current, e.target)) {
e.stopPropagation() e.stopPropagation()
@ -75,19 +76,19 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
await plugin.appManager.activatePlugin(['solidity', 'sourcify']) await plugin.appManager.activatePlugin(['solidity', 'sourcify'])
plugin.verticalIcons.select('sourcify') plugin.verticalIcons.select('sourcify')
_paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify']) _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify'])
} }
const startSolidityUnitTesting = async () => { const startSolidityUnitTesting = async () => {
await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting']) await plugin.appManager.activatePlugin(['solidity', 'solidityUnitTesting'])
plugin.verticalIcons.select('solidityUnitTesting') plugin.verticalIcons.select('solidityUnitTesting')
_paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityUnitTesting']) _paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'solidityUnitTesting'])
} }
return ( return (
<div className="pl-2 w-100" id="hTFeaturedPlugins"> <div className="pl-2 w-100" id="hTFeaturedPlugins">
<label className="" style={{fontSize: "1.2rem"}}>Featured Plugins</label> <label className="" style={{fontSize: "1.2rem"}}><FormattedMessage id='home.featuredPlugins' defaultMessage='Featured Plugins' /></label>
<div ref={carouselRefDiv} className="w-100 d-flex flex-column"> <div ref={carouselRefDiv} className="w-100 d-flex flex-column">
<ThemeContext.Provider value={ themeFilter }> <ThemeContext.Provider value={ themeFilter }>
<Carousel <Carousel
ref={carouselRef} ref={carouselRef}
focusOnSelect={true} focusOnSelect={true}
customButtonGroup={ customButtonGroup={
@ -98,7 +99,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
draggable={true} draggable={true}
showDots={false} showDots={false}
responsive={ responsive={
{ {
superLargeDesktop: { superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 }, breakpoint: { max: 4000, min: 3000 },
items: itemsToShow items: itemsToShow

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer } from 'react' import React, { useState, useRef, useReducer } from 'react'
import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
const _paq = window._paq = window._paq || [] // eslint-disable-line const _paq = window._paq = window._paq || [] // eslint-disable-line
@ -53,7 +54,7 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
else { else {
workspace.addExternal(type + '/' + cleanUrl, content, url) workspace.addExternal(type + '/' + cleanUrl, content, url)
plugin.call('menuicons', 'select', 'filePanel') plugin.call('menuicons', 'select', 'filePanel')
} }
} catch (e) { } catch (e) {
toast(e.message) toast(e.message)
} }
@ -143,20 +144,20 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
</ModalDialog> </ModalDialog>
<Toaster message={state.toasterMsg} /> <Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 border-bottom d-flex flex-column" id="hTFileSection"> <div className="justify-content-start mt-1 p-2 border-bottom d-flex flex-column" id="hTFileSection">
<label style={{fontSize: "1rem"}}>Files</label> <label style={{fontSize: "1rem"}}><FormattedMessage id='home.file' defaultMessage='Files' /></label>
<button className="btn btn-primary p-2 border my-1" data-id="homeTabNewFile" style={{width: 'fit-content'}} onClick={() => createNewFile()}>New File</button> <button className="btn btn-primary p-2 border my-1" data-id="homeTabNewFile" style={{width: 'fit-content'}} onClick={() => createNewFile()}><FormattedMessage id='home.newFile' defaultMessage='New File' /></button>
<label className="btn p-2 border my-1" style={{width: 'fit-content'}} htmlFor="openFileInput">Open File</label> <label className="btn p-2 border my-1" style={{width: 'fit-content'}} htmlFor="openFileInput"><FormattedMessage id='home.openFile' defaultMessage='Open File' /></label>
<input title="open file" type="file" id="openFileInput" onChange={(event) => { <input title="open file" type="file" id="openFileInput" onChange={(event) => {
event.stopPropagation() event.stopPropagation()
plugin.verticalIcons.select('filePanel') plugin.verticalIcons.select('filePanel')
uploadFile(event.target) uploadFile(event.target)
}} multiple /> }} multiple />
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}>Connect to Localhost</button> <button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}><FormattedMessage id='home.connectToLocalhost' defaultMessage='Connect to Localhost' /></button>
<label className="pt-2">Load From</label> <label className="pt-2"><FormattedMessage id='home.loadFrom' defaultMessage='Load From' /></label>
<div className="d-flex"> <div className="d-flex">
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>GitHub</button> <button className="btn p-2 border mr-2" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>GitHub</button>
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button> <button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>'])}>IPFS</button> <button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>'])}>IPFS</button>
<button className="btn p-2 border" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>HTTPS</button> <button className="btn p-2 border" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>HTTPS</button>
</div> </div>
</div> </div>
@ -164,4 +165,4 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
) )
} }
export default HomeTabFile export default HomeTabFile

@ -1,25 +1,33 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react' import React from 'react'
import { FormattedMessage } from 'react-intl'
const _paq = window._paq = window._paq || [] // eslint-disable-line const _paq = window._paq = window._paq || [] // eslint-disable-line
function HomeTabScamAlert () { function HomeTabScamAlert () {
return ( return (
<div className="" id="hTScamAlertSection"> <div className="" id="hTScamAlertSection">
<label className="pl-2 text-danger" style={{fontSize: "1.2rem"}}>Scam Alert</label> <label className="pl-2 text-danger" style={{fontSize: "1.2rem"}}><FormattedMessage id='home.scamAlert' defaultMessage='Scam Alerts' /></label>
<div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex flex-column border border-danger"> <div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex flex-column border border-danger">
<span className="pl-4 mt-2"> <span className="pl-4 mt-2">
<i className="pr-2 text-danger fas fa-exclamation-triangle"></i> <i className="pr-2 text-danger fas fa-exclamation-triangle"></i>
<b>Scam Alerts:</b> <b><FormattedMessage id='home.scamAlert' defaultMessage='Scam Alerts' />:</b>
</span> </span>
<span className="pl-4 mt-1"> <span className="pl-4 mt-1">
The only URL Remix uses is remix.ethereum.org <FormattedMessage id='home.scamAlertText' defaultMessage='The only URL Remix uses is remix.ethereum.org' />
</span> </span>
<span className="pl-4 mt-1"> <span className="pl-4 mt-1">
Beware of online videos promoting "liquidity front runner bots": <FormattedMessage id='home.scamAlertText2' defaultMessage='Beware of online videos promoting "liquidity front runner bots"' />:
<a className="pl-2 remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'learnMore'])} target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">Learn more</a> <a className="pl-2 remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'learnMore'])} target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">
<FormattedMessage id='home.learnMore' defaultMessage='Learn more' />
</a>
</span> </span>
<span className="pl-4 mt-1"> <span className="pl-4 mt-1">
Additional safety tips: &nbsp;<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'safetyTips'])} target="__blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">here</a> <FormattedMessage id='home.scamAlertText3' defaultMessage='Additional safety tips' />
: &nbsp;
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'safetyTips'])} target="__blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">
<FormattedMessage id='home.here' defaultMessage='here' />
</a>
</span> </span>
</div> </div>
</div> </div>

@ -43,7 +43,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}) })
}) })
}, []) }, [])
return ( return (
<div className="d-flex flex-row w-100" data-id="remixUIHTAll"> <div className="d-flex flex-row w-100" data-id="remixUIHTAll">
<ThemeContext.Provider value={ state.themeQuality }> <ThemeContext.Provider value={ state.themeQuality }>

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -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": {}
}
]
}

@ -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).

@ -0,0 +1,2 @@
export * from './lib/remix-ui-locale-module';
export * from '../types/locale-module'

@ -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 (
<div className="border-top">
<div className="card-body pt-3 pb-2">
<h6 className="card-title"><FormattedMessage id='settings.locales' defaultMessage='Lanaguage' /></h6>
<div className="card-text locales-container">
{localeModule.getLocales()
? localeModule.getLocales().map((locale, idx) => (
<div
className="radio custom-control custom-radio mb-1 form-check"
key={idx}
>
<input
type="radio"
onChange={event => {
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()}
/>
<label
className="form-check-label custom-control-label"
data-id={`settingsTabLocaleLabel${locale.name}`}
htmlFor={locale.name}
>
{locale.name}
</label>
</div>
))
: null}
</div>
</div>
</div>
)
}
export default RemixUiLocaleModule;

@ -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"
}
]
}

@ -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"]
}

@ -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<any, any> {
currentLocaleState: Record<string, unknown>;
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 }

@ -1,5 +1,6 @@
/* eslint-disable jsx-a11y/anchor-has-content */ /* eslint-disable jsx-a11y/anchor-has-content */
import React, { useEffect, useRef, useState } from 'react' // eslint-disable-line import React, { useEffect, useRef, useState } from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
import { PluginRecord } from '../types' import { PluginRecord } from '../types'
import './panel.css' import './panel.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { OverlayTrigger, Tooltip } from 'react-bootstrap'
@ -33,7 +34,9 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
return ( return (
<header className='d-flex flex-column'> <header className='d-flex flex-column'>
<div className="swapitHeader px-3 pt-2 pb-0 d-flex flex-row"> <div className="swapitHeader px-3 pt-2 pb-0 d-flex flex-row">
<h6 className="pt-0 mb-1" data-id='sidePanelSwapitTitle'>{plugin?.profile.displayName || plugin?.profile.name}</h6> <h6 className="pt-0 mb-1" data-id='sidePanelSwapitTitle'>
<FormattedMessage id={plugin?.profile.name + '.displayName'} defaultMessage={plugin?.profile.displayName || plugin?.profile.name} />
</h6>
<div className="d-flex flex-row"> <div className="d-flex flex-row">
<div className="d-flex flex-row"> <div className="d-flex flex-row">
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && ( {plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && (
@ -64,21 +67,21 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
<div className="d-flex w-100 flex-row py-2"></div> <div className="d-flex w-100 flex-row py-2"></div>
<div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}> <div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
{plugin?.profile?.author && <span className="d-flex flex-row align-items-center"> {plugin?.profile?.author && <span className="d-flex flex-row align-items-center">
<label className="mb-0 pr-2">Author:</label> <label className="mb-0 pr-2"><FormattedMessage id='panel.author' defaultMessage='Author' />:</label>
<span> { plugin?.profile.author } </span> <span> { plugin?.profile.author } </span>
</span>} </span>}
{plugin?.profile?.maintainedBy && <span className="d-flex flex-row align-items-center"> {plugin?.profile?.maintainedBy && <span className="d-flex flex-row align-items-center">
<label className="mb-0 pr-2">Maintained by:</label> <label className="mb-0 pr-2"><FormattedMessage id='panel.maintainedBy' defaultMessage='Maintained by' />:</label>
<span> { plugin?.profile.maintainedBy } </span> <span> { plugin?.profile.maintainedBy } </span>
</span>} </span>}
{plugin?.profile?.documentation && <span className="d-flex flex-row align-items-center"> {plugin?.profile?.documentation && <span className="d-flex flex-row align-items-center">
<label className="mb-0 pr-2">Documentation:</label> <label className="mb-0 pr-2"><FormattedMessage id='panel.documentation' defaultMessage='Documentation' />:</label>
<span> <span>
<a href={plugin?.profile?.documentation} className="titleInfo p-0 mb-2" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a> <a href={plugin?.profile?.documentation} className="titleInfo p-0 mb-2" title="link to documentation" target="_blank" rel="noreferrer"><i aria-hidden="true" className="fas fa-book"></i></a>
</span> </span>
</span>} </span>}
{plugin?.profile?.description && <span className="d-flex flex-row align-items-baseline"> {plugin?.profile?.description && <span className="d-flex flex-row align-items-baseline">
<label className="mb-0 pr-2">Description:</label> <label className="mb-0 pr-2"><FormattedMessage id='panel.description' defaultMessage='Description' />:</label>
<span> { plugin?.profile.description } </span> <span> { plugin?.profile.description } </span>
</span>} </span>}
{plugin?.profile?.repo && <span className="d-flex flex-row align-items-center"> {plugin?.profile?.repo && <span className="d-flex flex-row align-items-center">

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import React from 'react' // eslint-disable-line no-use-before-define import React from 'react' // eslint-disable-line no-use-before-define
import { useIntl } from 'react-intl'
import { PluginManagerComponent } from '../../types' import { PluginManagerComponent } from '../../types'
import ActivePluginCard from './ActivePluginCard' import ActivePluginCard from './ActivePluginCard'
import ModuleHeading from './moduleHeading' import ModuleHeading from './moduleHeading'
@ -15,13 +16,15 @@ function ActivePluginCardContainer ({ pluginComponent }: ActivePluginCardContain
pluginComponent.deactivateP(pluginName) pluginComponent.deactivateP(pluginName)
} }
const intl = useIntl();
return ( return (
<React.Fragment> <React.Fragment>
{(pluginComponent.activePlugins && pluginComponent.activePlugins.length) ? <ModuleHeading headingLabel="Active Modules" count={pluginComponent.activePlugins.length} /> : null} {(pluginComponent.activePlugins && pluginComponent.activePlugins.length) ? <ModuleHeading headingLabel={intl.formatMessage({id: 'pluginManager.activeModules' , defaultMessage: 'Active Modules'})} count={pluginComponent.activePlugins.length} /> : null}
{pluginComponent.activePlugins && pluginComponent.activePlugins.map((profile, idx) => { {pluginComponent.activePlugins && pluginComponent.activePlugins.map((profile, idx) => {
return ( return (
<ActivePluginCard <ActivePluginCard
buttonText="Deactivate" buttonText={intl.formatMessage({id: 'pluginManager.deactivate' , defaultMessage: 'Deactivate'})}
profile={profile} profile={profile}
deactivatePlugin={deactivatePlugin} deactivatePlugin={deactivatePlugin}
key={idx} key={idx}

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
import React from 'react' // eslint-disable-line no-use-before-define import React from 'react' // eslint-disable-line no-use-before-define
import { useIntl } from 'react-intl'
import { PluginManagerComponent } from '../../types' import { PluginManagerComponent } from '../../types'
import InactivePluginCard from './InactivePluginCard' import InactivePluginCard from './InactivePluginCard'
import ModuleHeading from './moduleHeading' import ModuleHeading from './moduleHeading'
@ -16,13 +17,15 @@ function InactivePluginCardContainer ({ pluginComponent }: InactivePluginCardCon
pluginComponent.activateP(pluginName) pluginComponent.activateP(pluginName)
} }
const intl = useIntl();
return ( return (
<React.Fragment> <React.Fragment>
{(pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.length) ? <ModuleHeading headingLabel="Inactive Modules" count={pluginComponent.inactivePlugins.length} /> : null} {(pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.length) ? <ModuleHeading headingLabel={intl.formatMessage({id: 'pluginManager.inactiveModules' , defaultMessage: 'Inactive Modules'})} count={pluginComponent.inactivePlugins.length} /> : null}
{pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.map((profile, idx) => { {pluginComponent.inactivePlugins && pluginComponent.inactivePlugins.map((profile, idx) => {
return ( return (
<InactivePluginCard <InactivePluginCard
buttonText="Activate" buttonText={intl.formatMessage({id: 'pluginManager.activate' , defaultMessage: 'Activate'})}
profile={profile} profile={profile}
key={idx} key={idx}
activatePlugin={activatePlugin} activatePlugin={activatePlugin}

@ -3,7 +3,7 @@ import React, { useEffect, useReducer, useState } from 'react' // eslint-disable
import { ModalDialog } from '@remix-ui/modal-dialog' import { ModalDialog } from '@remix-ui/modal-dialog'
import { Toaster } from '@remix-ui/toaster' import { Toaster } from '@remix-ui/toaster'
import { IframePlugin, WebsocketPlugin } from '@remixproject/engine-web' import { IframePlugin, WebsocketPlugin } from '@remixproject/engine-web'
import { FormattedMessage, useIntl } from 'react-intl'
import { localPluginReducerActionType, localPluginToastReducer } from '../reducers/pluginManagerReducer' import { localPluginReducerActionType, localPluginToastReducer } from '../reducers/pluginManagerReducer'
import { canActivate, FormStateProps, PluginManagerComponent } from '../../types' import { canActivate, FormStateProps, PluginManagerComponent } from '../../types'
@ -93,12 +93,14 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
} }
} }
const intl = useIntl()
return ( return (
<><ModalDialog <><ModalDialog
handleHide={closeModal} handleHide={closeModal}
id="pluginManagerLocalPluginModalDialog" id="pluginManagerLocalPluginModalDialog"
hide={visible} hide={visible}
title="Local Plugin" title={intl.formatMessage({ id: 'pluginManager.localForm.title', defaultMessage: 'Local Plugin' })}
okLabel="OK" okLabel="OK"
okFn={ handleModalOkClick } okFn={ handleModalOkClick }
cancelLabel="Cancel" cancelLabel="Cancel"
@ -106,27 +108,36 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
> >
<form id="local-plugin-form"> <form id="local-plugin-form">
<div className="form-group"> <div className="form-group">
<label htmlFor="plugin-name">Plugin Name <small>(required)</small></label> <label htmlFor="plugin-name">
<FormattedMessage id='pluginManager.localForm.pluginName' defaultMessage='Plugin Name' />
&nbsp;
<small>(<FormattedMessage id='pluginManager.localForm.required' defaultMessage='required' />)</small>
</label>
<input <input
className="form-control" className="form-control"
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
value={ name || '' } value={ name || '' }
id="plugin-name" id="plugin-name"
data-id="localPluginName" data-id="localPluginName"
placeholder="Should be camelCase" /> placeholder={intl.formatMessage({ id: 'pluginManager.localForm.shouldBeCamelCase', defaultMessage: 'Should be camelCase' })} />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="plugin-displayname">Display Name</label> <label htmlFor="plugin-displayname">
<FormattedMessage id='pluginManager.localForm.displayName' defaultMessage='Display Name' />
</label>
<input <input
className="form-control" className="form-control"
onChange={e => setDisplayName(e.target.value)} onChange={e => setDisplayName(e.target.value)}
value={ displayName || '' } value={ displayName || '' }
id="plugin-displayname" id="plugin-displayname"
data-id="localPluginDisplayName" data-id="localPluginDisplayName"
placeholder="Name in the header" /> placeholder={intl.formatMessage({ id: 'pluginManager.localForm.nameInTheHeader', defaultMessage: 'Name in the header' })} />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="plugin-methods">Api (comma separated list of method names)</label> <label htmlFor="plugin-methods">
Api&nbsp;
(<FormattedMessage id='pluginManager.localForm.commaSeparatedMethod' defaultMessage='comma separated list of method names' />)
</label>
<input <input
className="form-control" className="form-control"
onChange={e => setMethods(e.target.value)} onChange={e => setMethods(e.target.value)}
@ -136,7 +147,11 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
placeholder="Methods" /> placeholder="Methods" />
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="plugin-methods">Plugins it can activate (comma separated list of plugin names)</label> <label htmlFor="plugin-methods">
<FormattedMessage id='pluginManager.localForm.pluginsItCanActivate' defaultMessage='Plugins it can activate' />
&nbsp;
(<FormattedMessage id='pluginManager.localForm.commaSeparatedPlugin' defaultMessage='comma separated list of plugin names' />)
</label>
<input <input
className="form-control" className="form-control"
onChange={e => setCanactivate(e.target.value)} onChange={e => setCanactivate(e.target.value)}
@ -147,7 +162,11 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="plugin-url">Url <small>(required)</small></label> <label htmlFor="plugin-url">Url&nbsp;
<small>
(<FormattedMessage id='pluginManager.localForm.required' defaultMessage='required' />)
</small>
</label>
<input <input
className="form-control" className="form-control"
onChange={e => setUrl(e.target.value)} onChange={e => setUrl(e.target.value)}
@ -156,7 +175,13 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
data-id="localPluginUrl" data-id="localPluginUrl"
placeholder="ex: https://localhost:8000" /> placeholder="ex: https://localhost:8000" />
</div> </div>
<h6>Type of connection <small>(required)</small></h6> <h6>
<FormattedMessage id='pluginManager.localForm.typeOfConnection' defaultMessage='Type of connection' />
&nbsp;
<small>
(<FormattedMessage id='pluginManager.localForm.required' defaultMessage='required' />)
</small>
</h6>
<div className="form-check form-group"> <div className="form-check form-group">
<div className="radio"> <div className="radio">
<input <input
@ -183,7 +208,13 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
<label className="form-check-label" htmlFor="ws">Websocket</label> <label className="form-check-label" htmlFor="ws">Websocket</label>
</div> </div>
</div> </div>
<h6>Location in remix <small>(required)</small></h6> <h6>
<FormattedMessage id='pluginManager.localForm.locationInRemix' defaultMessage='Location in remix' />
&nbsp;
<small>
(<FormattedMessage id='pluginManager.localForm.required' defaultMessage='required' />)
</small>
</h6>
<div className="form-check form-group"> <div className="form-check form-group">
<div className="radio"> <div className="radio">
<input <input
@ -195,7 +226,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
data-id='localPluginRadioButtonsidePanel' data-id='localPluginRadioButtonsidePanel'
checked={location === 'sidePanel'} checked={location === 'sidePanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
<label className="form-check-label" htmlFor="sidePanel">Side Panel</label> <label className="form-check-label" htmlFor="sidePanel"><FormattedMessage id='pluginManager.localForm.sidePanel' defaultMessage='Side Panel' /></label>
</div> </div>
<div className="radio"> <div className="radio">
<input <input
@ -207,7 +238,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
data-id='localPluginRadioButtonmainPanel' data-id='localPluginRadioButtonmainPanel'
checked={location === 'mainPanel'} checked={location === 'mainPanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
<label className="form-check-label" htmlFor="mainPanel">Main Panel</label> <label className="form-check-label" htmlFor="mainPanel"><FormattedMessage id='pluginManager.localForm.mainPanel' defaultMessage='Main Panel' /></label>
</div> </div>
<div className="radio"> <div className="radio">
<input <input
@ -219,7 +250,7 @@ function LocalPluginForm ({ closeModal, visible, pluginManager }: LocalPluginFor
data-id='localPluginRadioButtonnone' data-id='localPluginRadioButtonnone'
checked={location === 'none'} checked={location === 'none'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} /> onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')} />
<label className="form-check-label" htmlFor="none">None</label> <label className="form-check-label" htmlFor="none"><FormattedMessage id='pluginManager.localForm.none' defaultMessage='None' /></label>
</div> </div>
</div> </div>
</form> </form>

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { Fragment, ReactNode, useEffect, useState } from 'react' // eslint-disable-line no-use-before-define 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 { PluginManagerComponent, PluginManagerSettings } from '../../types'
import PermisssionsSettings from './permissionsSettings' import PermisssionsSettings from './permissionsSettings'
import { Profile } from '@remixproject/plugin-utils' import { Profile } from '@remixproject/plugin-utils'
@ -47,7 +48,7 @@ function RootView ({ pluginComponent, children }: RootViewProps) {
data-id="pluginManagerComponentSearchInput" data-id="pluginManagerComponentSearchInput"
/> />
<button onClick={openModal} className="remixui_pluginSearchButton btn bg-transparent text-dark border-0 mt-2 text-underline" data-id="pluginManagerComponentPluginSearchButton"> <button onClick={openModal} className="remixui_pluginSearchButton btn bg-transparent text-dark border-0 mt-2 text-underline" data-id="pluginManagerComponentPluginSearchButton">
Connect to a Local Plugin <FormattedMessage id='pluginManager.connectLocal' defaultMessage='Connect to a Local Plugin' />
</button> </button>
</header> </header>
{children} {children}

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { useEffect, useState, useRef } from 'react' import React, { useEffect, useState, useRef } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { AccountProps } from '../types' import { AccountProps } from '../types'
import { PassphrasePrompt } from './passphrase' import { PassphrasePrompt } from './passphrase'
@ -14,6 +15,8 @@ export function AccountUI (props: AccountProps) {
}) })
const messageRef = useRef('') const messageRef = useRef('')
const intl = useIntl()
useEffect(() => { useEffect(() => {
if (!selectedAccount && accounts.length > 0) props.setAccount(accounts[0]) if (!selectedAccount && accounts.length > 0) props.setAccount(accounts[0])
}, [accounts, selectedAccount]) }, [accounts, selectedAccount])
@ -79,7 +82,7 @@ export function AccountUI (props: AccountProps) {
message='Enter your passphrase for this account to sign the message' message='Enter your passphrase for this account to sign the message'
setPassphrase={props.setPassphrase} setPassphrase={props.setPassphrase}
/>, 'OK', () => { />, '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.signMessageWithAddress(selectedAccount, messageRef.current, signedMessagePrompt, props.passphrase)
props.setPassphrase('') props.setPassphrase('')
}, 'Cancel', null) }, '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) props.signMessageWithAddress(selectedAccount, messageRef.current, signedMessagePrompt)
}, 'Cancel', null) }, 'Cancel', null)
} }
@ -120,7 +123,7 @@ export function AccountUI (props: AccountProps) {
const signMessagePrompt = () => { const signMessagePrompt = () => {
return ( return (
<div> Enter a message to sign <div> <FormattedMessage id='udapp.enterAMessageToSign' defaultMessage='Enter a message to sign' />
<div> <div>
<textarea <textarea
id="prompt_text" id="prompt_text"
@ -139,9 +142,9 @@ export function AccountUI (props: AccountProps) {
const signedMessagePrompt = (msgHash: string, signedData: string) => { const signedMessagePrompt = (msgHash: string, signedData: string) => {
return ( return (
<div> <div>
<b>hash:</b><br /> <b><FormattedMessage id='udapp.hash' defaultMessage='hash' />:</b><br />
<span id="remixRunSignMsgHash" data-id="settingsRemixRunSignMsgHash">{msgHash}</span> <span id="remixRunSignMsgHash" data-id="settingsRemixRunSignMsgHash">{msgHash}</span>
<br /><b>signature:</b><br /> <br /><b><FormattedMessage id='udapp.signature' defaultMessage='signature' />:</b><br />
<span id="remixRunSignMsgSignature" data-id="settingsRemixRunSignMsgSignature">{signedData}</span> <span id="remixRunSignMsgSignature" data-id="settingsRemixRunSignMsgSignature">{signedData}</span>
</div> </div>
) )
@ -150,7 +153,7 @@ export function AccountUI (props: AccountProps) {
return ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel"> <label className="udapp_settingsLabel">
Account <FormattedMessage id='udapp.account' defaultMessage='Account' />
<CustomTooltip <CustomTooltip
placement={'top-start'} placement={'top-start'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { ContractDropdownProps, DeployMode } from '../types' import { ContractDropdownProps, DeployMode } from '../types'
import { ContractData, FuncABI } from '@remix-project/core-plugin' import { ContractData, FuncABI } from '@remix-project/core-plugin'
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
@ -7,7 +8,8 @@ import { ContractGUI } from './contractGUI'
import { CustomTooltip, deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' import { CustomTooltip, deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
export function ContractDropdownUI(props: ContractDropdownProps) { export function ContractDropdownUI (props: ContractDropdownProps) {
const intl = useIntl()
const [abiLabel, setAbiLabel] = useState<{ const [abiLabel, setAbiLabel] = useState<{
display: string, display: string,
content: string content: string
@ -254,7 +256,9 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<div className="udapp_container" data-id="contractDropdownContainer"> <div className="udapp_container" data-id="contractDropdownContainer">
<div className='d-flex justify-content-between'> <div className='d-flex justify-content-between'>
<div className="d-flex justify-content-between align-items-end"> <div className="d-flex justify-content-between align-items-end">
<label className="udapp_settingsLabel pr-1">Contract</label> <label className="udapp_settingsLabel pr-1">
<FormattedMessage id='udapp.contract' defaultMessage='Contract' />
</label>
<div className="d-flex">{compilerName && compilerName !== '' && <label style={{ maxHeight: '0.6rem', lineHeight: '1rem' }} data-id="udappCompiledBy">(Compiled by <span className="text-capitalize"> {compilerName}</span>)</label>}</div> <div className="d-flex">{compilerName && compilerName !== '' && <label style={{ maxHeight: '0.6rem', lineHeight: '1rem' }} data-id="udappCompiledBy">(Compiled by <span className="text-capitalize"> {compilerName}</span>)</label>}</div>
</div> </div>
{props.remixdActivated ? {props.remixdActivated ?
@ -293,10 +297,10 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
</div> </div>
<div> <div>
<div className="udapp_deployDropdown"> <div className="udapp_deployDropdown">
{((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? 'No compiled contracts' {((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? intl.formatMessage({id: 'udapp.noCompiledContracts', defaultMessage: 'No compiled contracts'})
: loadedContractData ? <div> : loadedContractData ? <div>
<ContractGUI <ContractGUI
title='Deploy' title={intl.formatMessage({id: 'udapp.deploy', defaultMessage: "Deploy"})}
isDeploy={true} isDeploy={true}
deployOption={deployOptions[currentFile] && deployOptions[currentFile][currentContract] ? deployOptions[currentFile][currentContract].options : null} deployOption={deployOptions[currentFile] && deployOptions[currentFile][currentContract] ? deployOptions[currentFile][currentContract].options : null}
initializerOptions={deployOptions[currentFile] && deployOptions[currentFile][currentContract] ? deployOptions[currentFile][currentContract].initializeOptions : null} initializerOptions={deployOptions[currentFile] && deployOptions[currentFile][currentContract] ? deployOptions[currentFile][currentContract].initializeOptions : null}
@ -329,14 +333,16 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
data-id="contractDropdownIpfsCheckboxLabel" data-id="contractDropdownIpfsCheckboxLabel"
className="m-0 form-check-label custom-control-label udapp_checkboxAlign" className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
> >
Publish to IPFS <FormattedMessage id='udapp.publishTo' defaultMessage='Publish to' /> IPFS
</label> </label>
</CustomTooltip> </CustomTooltip>
</div> </div>
</div> : '' </div> : ''
} }
</div> </div>
<div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>or</div> <div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>
<FormattedMessage id='udapp.or' defaultMessage='or' />
</div>
<div className="udapp_button udapp_atAddressSect "> <div className="udapp_button udapp_atAddressSect ">
<CustomTooltip <CustomTooltip
placement={'top-end'} placement={'top-end'}
@ -345,8 +351,9 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
tooltipText={atAddressOptions.title} tooltipText={atAddressOptions.title}
> >
<div id="runAndDeployAtAdressButtonContainer" onClick={loadFromAddress} data-title={atAddressOptions.title}> <div id="runAndDeployAtAdressButtonContainer" onClick={loadFromAddress} data-title={atAddressOptions.title}>
<button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} style={{ pointerEvents: 'none' }} onClick={loadFromAddress} data-title={atAddressOptions.title} <button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} style={{ pointerEvents: 'none' }} onClick={loadFromAddress} data-title={atAddressOptions.title}>
>At Address</button> <FormattedMessage id='udapp.atAddress' defaultMessage='At Address' />
</button>
</div> </div>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip
@ -358,7 +365,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<input <input
ref={atAddressValue} ref={atAddressValue}
className="udapp_input udapp_ataddressinput ataddressinput form-control" className="udapp_input udapp_ataddressinput ataddressinput form-control"
placeholder="Load contract from Address" placeholder={intl.formatMessage({id: 'udapp.loadContractFromAddress', defaultMessage: "Load contract from Address"})}
onChange={atAddressChanged} onChange={atAddressChanged}
/> />
</CustomTooltip> </CustomTooltip>

@ -1,4 +1,5 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { DeployButtonProps } from '../types' import { DeployButtonProps } from '../types'
import { ButtonGroup, Dropdown } from 'react-bootstrap' import { ButtonGroup, Dropdown } from 'react-bootstrap'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
@ -9,7 +10,7 @@ export function DeployButton (props: DeployButtonProps) {
const toggleOptions = () => { const toggleOptions = () => {
setShowOptions(!showOptions) setShowOptions(!showOptions)
} }
return ( return (
<> <>
{ props.deployOptions && (props.deployOptions || []).length > 0 ? { props.deployOptions && (props.deployOptions || []).length > 0 ?
@ -24,7 +25,7 @@ export function DeployButton (props: DeployButtonProps) {
}} key={index}> { props.selectedIndex === index ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>) }} key={index}> { props.selectedIndex === index ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
} }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> : </Dropdown> :
<CustomTooltip <CustomTooltip
placement="right-start" placement="right-start"
tooltipId="deployButtonTooltip" tooltipId="deployButtonTooltip"
@ -32,7 +33,7 @@ export function DeployButton (props: DeployButtonProps) {
tooltipText={props.buttonOptions.title} tooltipText={props.buttonOptions.title}
> >
<button onClick={props.handleActionClick} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}> <button onClick={props.handleActionClick} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}>
Deploy <FormattedMessage id='udapp.deploy' defaultMessage='Deploy' />
</button> </button>
</CustomTooltip> </CustomTooltip>
} }

@ -1,12 +1,12 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import { FormattedMessage } from 'react-intl'
import { EnvironmentProps } from '../types' import { EnvironmentProps } from '../types'
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper' import { CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper'
export function EnvironmentUI (props: EnvironmentProps) { export function EnvironmentUI (props: EnvironmentProps) {
const handleChangeExEnv = (env: string) => { const handleChangeExEnv = (env: string) => {
const provider = props.providers.providerList.find(exEnv => exEnv.value === env) const provider = props.providers.providerList.find(exEnv => exEnv.value === env)
const fork = provider.fork // can be undefined if connected to an external source (External Http Provider / injected) const fork = provider.fork // can be undefined if connected to an external source (External Http Provider / injected)
@ -23,12 +23,12 @@ export function EnvironmentUI (props: EnvironmentProps) {
'Arbitrum One Provider': 'https://bridge.arbitrum.io/' 'Arbitrum One Provider': 'https://bridge.arbitrum.io/'
} }
const isL2 = (provider) => provider && (provider.value === 'Optimism Provider' || provider.value === 'Arbitrum One Provider') const isL2 = (provider) => provider && (provider.value === 'Optimism Provider' || provider.value === 'Arbitrum One Provider')
return ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label id="selectExEnv" className="udapp_settingsLabel"> <label id="selectExEnv" className="udapp_settingsLabel">
Environment <CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder" <FormattedMessage id='udapp.environment' defaultMessage='Environment' />
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder"
tooltipText="Open chainlist and add a new provider for the chain you want to interact to."> tooltipText="Open chainlist and add a new provider for the chain you want to interact to.">
<a href='https://chainlist.org/' target='_blank'><i style={{ fontSize: 'medium' }} className={'ml-2 fad fa-plug'} aria-hidden="true"></i></a> <a href='https://chainlist.org/' target='_blank'><i style={{ fontSize: 'medium' }} className={'ml-2 fad fa-plug'} aria-hidden="true"></i></a>
</CustomTooltip> </CustomTooltip>
@ -60,7 +60,7 @@ export function EnvironmentUI (props: EnvironmentProps) {
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
<CustomTooltip placement={'bottom-start'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip" <CustomTooltip placement={'bottom-start'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip"
tooltipText={"Click for docs about Environment"}> tooltipText={<FormattedMessage id='udapp.environmentDocs' defaultMessage='Click for docs about Environment' />}>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info"></i></a> <a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info"></i></a>
</CustomTooltip> </CustomTooltip>
</div> </div>

@ -1,6 +1,7 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React from 'react' import React from 'react'
import { FormattedMessage } from 'react-intl'
import { GasPriceProps } from '../types' import { GasPriceProps } from '../types'
export function GasPriceUI (props: GasPriceProps) { export function GasPriceUI (props: GasPriceProps) {
@ -10,7 +11,7 @@ export function GasPriceUI (props: GasPriceProps) {
return ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel">Gas limit</label> <label className="udapp_settingsLabel"><FormattedMessage id='udapp.gasLimit' defaultMessage='Gas limit' /></label>
<CustomTooltip <CustomTooltip
placement={'right-end'} placement={'right-end'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"

@ -1,6 +1,7 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React from 'react' import React from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { InstanceContainerProps } from '../types' import { InstanceContainerProps } from '../types'
import { UniversalDappUI } from './universalDappUI' import { UniversalDappUI } from './universalDappUI'
@ -11,6 +12,8 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
props.clearInstances() props.clearInstances()
} }
const intl = useIntl()
return ( return (
<div className="udapp_instanceContainer mt-3 border-0 list-group-item"> <div className="udapp_instanceContainer mt-3 border-0 list-group-item">
<div className="d-flex justify-content-between align-items-center pl-2 mb-2"> <div className="d-flex justify-content-between align-items-center pl-2 mb-2">
@ -21,7 +24,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
tooltipText={"Autogenerated generic user interfaces for interaction with deployed contracts"} tooltipText={"Autogenerated generic user interfaces for interaction with deployed contracts"}
> >
<label className="udapp_deployedContracts"> <label className="udapp_deployedContracts">
Deployed Contracts <FormattedMessage id='udapp.deployedContracts' defaultMessage='Deployed Contracts' />
</label> </label>
</CustomTooltip> </CustomTooltip>
{ instanceList.length > 0 { instanceList.length > 0
@ -30,7 +33,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
placement="right" placement="right"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="deployAndRunClearInstancesTooltip" tooltipId="deployAndRunClearInstancesTooltip"
tooltipText={"Clear instances list and reset recorder"} tooltipText={<FormattedMessage id='udapp.deployAndRunClearInstances' defaultMessage='Clear instances list and reset recorder' />}
> >
<i className="mr-2 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} aria-hidden="true"> <i className="mr-2 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} aria-hidden="true">
</i> </i>
@ -56,7 +59,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
}) } }) }
</div> </div>
: <span className="mx-2 mt-3 alert alert-warning" data-id="deployAndRunNoInstanceText" role="alert"> : <span className="mx-2 mt-3 alert alert-warning" data-id="deployAndRunNoInstanceText" role="alert">
Currently you have no contract instances to interact with. <FormattedMessage id='udapp.deployAndRunNoInstanceText' defaultMessage='Currently you have no contract instances to interact with.' />
</span> </span>
} }
</div> </div>

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, {useRef, useState, useEffect} from 'react' import React, {useRef, useState, useEffect} from 'react'
import { FormattedMessage } from 'react-intl'
import { RecorderProps } from '../types' import { RecorderProps } from '../types'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
@ -30,7 +31,9 @@ export function RecorderUI (props: RecorderProps) {
<div className="udapp_cardContainer list-group-item border border-bottom"> <div className="udapp_cardContainer list-group-item border border-bottom">
<div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}> <div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}>
<div className="d-flex justify-content-center align-items-center"> <div className="d-flex justify-content-center align-items-center">
<label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label> <label className="mt-1 udapp_recorderSectionLabel">
<FormattedMessage id='udapp.transactionsRecorded' defaultMessage='Transactions recorded' />
</label>
<CustomTooltip <CustomTooltip
placement={'right'} placement={'right'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { BN } from 'ethereumjs-util' import { BN } from 'ethereumjs-util'
import { CustomTooltip, isNumeric } from '@remix-ui/helper' import { CustomTooltip, isNumeric } from '@remix-ui/helper'
import { ValueProps } from '../types' import { ValueProps } from '../types'
@ -47,7 +48,7 @@ export function ValueUI (props: ValueProps) {
return ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel" data-id="remixDRValueLabel">Value</label> <label className="udapp_settingsLabel" data-id="remixDRValueLabel"><FormattedMessage id='udapp.value' defaultMessage='Value' /></label>
<div className="udapp_gasValueContainer"> <div className="udapp_gasValueContainer">
<CustomTooltip <CustomTooltip
placement={'top-start'} placement={'top-start'}

@ -1,10 +1,13 @@
import React, { useContext, useEffect, useRef, useState } from 'react' import React, { useContext, useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { SearchContext } from '../context/context' import { SearchContext } from '../context/context'
export const Exclude = props => { export const Exclude = props => {
const { setExclude, cancelSearch, startSearch } = useContext(SearchContext) const { setExclude, cancelSearch, startSearch } = useContext(SearchContext)
const [excludeInput, setExcludeInput] = useState<string>('.*/**/*') const [excludeInput, setExcludeInput] = useState<string>('.*/**/*')
const intl = useIntl()
const change = async e => { const change = async e => {
setExcludeInput(e.target.value) setExcludeInput(e.target.value)
await cancelSearch() await cancelSearch()
@ -24,10 +27,10 @@ export const Exclude = props => {
return ( return (
<> <>
<div className="search_plugin_find-part pl-3"> <div className="search_plugin_find-part pl-3">
<label className='mt-2'>Files to exclude</label> <label className='mt-2'><FormattedMessage id='search.filesToExclude' defaultMessage='Files to exclude' /></label>
<input <input
id='search_exclude' id='search_exclude'
placeholder="Exclude ie .git/**/* ( Enter to exclude )" placeholder={intl.formatMessage({id: 'search.placeholder3', defaultMessage: "Exclude ie .git/**/* ( Enter to exclude )"})}
className="form-control" className="form-control"
onKeyUp={handleKeypress} onKeyUp={handleKeypress}
onChange={async (e) => change(e)} onChange={async (e) => change(e)}

@ -1,5 +1,6 @@
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React, { useContext, useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { useIntl } from 'react-intl'
import { SearchContext } from '../context/context' import { SearchContext } from '../context/context'
export const Find = () => { export const Find = () => {
@ -10,16 +11,18 @@ export const Find = () => {
state, state,
toggleCaseSensitive, toggleCaseSensitive,
toggleMatchWholeWord, toggleMatchWholeWord,
toggleUseRegex toggleUseRegex,
} = useContext(SearchContext) } = useContext(SearchContext)
const intl = useIntl()
const [inputValue, setInputValue] = useState('') const [inputValue, setInputValue] = useState('')
const change = async e => { const change = async (e) => {
setInputValue(e.target.value) setInputValue(e.target.value)
await cancelSearch() await cancelSearch()
} }
const handleKeypress = async e => { const handleKeypress = async (e) => {
if (e.charCode === 13 || e.keyCode === 13) { if (e.charCode === 13 || e.keyCode === 13) {
startSearch() startSearch()
} }
@ -36,15 +39,21 @@ export const Find = () => {
<div className="search_plugin_search-input"> <div className="search_plugin_search-input">
<input <input
id="search_input" id="search_input"
placeholder="Search ( Enter to search )" placeholder={intl.formatMessage({
id: 'search.placeholder1',
defaultMessage: 'Search ( Enter to search )',
})}
className="form-control" className="form-control"
value={inputValue} value={inputValue}
onChange={async e => await change(e)} onChange={async (e) => await change(e)}
onKeyUp={handleKeypress} onKeyUp={handleKeypress}
></input> ></input>
<div className="search_plugin_controls"> <div className="search_plugin_controls">
<CustomTooltip <CustomTooltip
tooltipText="Match Case" tooltipText={intl.formatMessage({
id: 'search.matchCase',
defaultMessage: 'Match Case',
})}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="searchCaseSensitiveTooltip" tooltipId="searchCaseSensitiveTooltip"
placement="top-start" placement="top-start"
@ -64,7 +73,10 @@ export const Find = () => {
></div> ></div>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip
tooltipText="Match Whole Word" tooltipText={intl.formatMessage({
id: 'search.matchWholeWord',
defaultMessage: 'Match Whole Word',
})}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="searchWholeWordTooltip" tooltipId="searchWholeWordTooltip"
placement="top-start" placement="top-start"
@ -84,7 +96,10 @@ export const Find = () => {
></div> ></div>
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip
tooltipText="Use Regular Expression" tooltipText={intl.formatMessage({
id: 'search.useRegularExpression',
defaultMessage: 'Use Regular Expression',
})}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="useRegularExpressionTooltip" tooltipId="useRegularExpressionTooltip"
placement="bottom-start" placement="bottom-start"

@ -1,9 +1,13 @@
import React, { useContext, useEffect, useRef, useState } from 'react' import React, { useContext, useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { SearchContext } from '../context/context' import { SearchContext } from '../context/context'
export const Include = props => { export const Include = props => {
const { setInclude, cancelSearch, startSearch } = useContext(SearchContext) const { setInclude, cancelSearch, startSearch } = useContext(SearchContext)
const [includeInput, setIncludeInput] = useState<string>('*.sol, *.js') const [includeInput, setIncludeInput] = useState<string>('*.sol, *.js')
const intl = useIntl()
const change = async e => { const change = async e => {
setIncludeInput(e.target.value) setIncludeInput(e.target.value)
await cancelSearch() await cancelSearch()
@ -22,10 +26,10 @@ export const Include = props => {
return ( return (
<> <>
<div className="search_plugin_find-part pl-3"> <div className="search_plugin_find-part pl-3">
<label className='mt-2'>Files to include</label> <label className='mt-2'><FormattedMessage id='search.filesToInclude' defaultMessage='Files to include' /></label>
<input <input
id='search_include' id='search_include'
placeholder="Include ie *.sol ( Enter to include )" placeholder={intl.formatMessage({id: 'search.placeholder2', defaultMessage: "Include ie *.sol ( Enter to include )"})}
className="form-control" className="form-control"
onChange={async(e) => change(e)} onChange={async(e) => change(e)}
onKeyUp={handleKeypress} onKeyUp={handleKeypress}

@ -1,4 +1,5 @@
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { FormattedMessage } from 'react-intl'
import { SearchContext } from '../context/context' import { SearchContext } from '../context/context'
export const OverWriteCheck = props => { export const OverWriteCheck = props => {
@ -24,7 +25,7 @@ export const OverWriteCheck = props => {
data-id="confirm_replace_label" data-id="confirm_replace_label"
className="form-check-label custom-control-label" className="form-check-label custom-control-label"
> >
replace without confirmation <FormattedMessage id='search.replaceWithoutConfirmation' defaultMessage='replace without confirmation' />
</label> </label>
</div> </div>
</div> </div>

@ -1,4 +1,5 @@
import React, { useContext, useRef } from 'react' import React, { useContext, useRef } from 'react'
import { useIntl } from 'react-intl'
import { SearchContext } from '../context/context' import { SearchContext } from '../context/context'
export const Replace = props => { export const Replace = props => {
@ -9,13 +10,15 @@ export const Replace = props => {
timeOutId.current = setTimeout(() => setReplace(e.target.value), 500) timeOutId.current = setTimeout(() => setReplace(e.target.value), 500)
} }
const intl = useIntl()
return ( return (
<> <>
<div className="search_plugin_find-part "> <div className="search_plugin_find-part ">
<label className='d-none'>replace in files</label> <label className='d-none'>replace in files</label>
<input <input
id='search_replace' id='search_replace'
placeholder="Replace" placeholder={intl.formatMessage({id: 'search.replace', defaultMessage: "Replace"})}
className="form-control" className="form-control"
onChange={change} onChange={change}
></input> ></input>

@ -1,5 +1,6 @@
import { useDialogDispatchers } from '@remix-ui/app' import { useDialogDispatchers } from '@remix-ui/app'
import React, { useContext, useEffect, useRef, useState } from 'react' import React, { useContext, useEffect, useRef, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { SearchContext } from '../../context/context' import { SearchContext } from '../../context/context'
import { SearchResult, SearchResultLine } from '../../types' import { SearchResult, SearchResultLine } from '../../types'
import { ResultFileName } from './ResultFileName' import { ResultFileName } from './ResultFileName'
@ -123,12 +124,14 @@ export const ResultItem = (props: ResultItemProps) => {
{loading ? <div className="loading">Loading...</div> : null} {loading ? <div className="loading">Loading...</div> : null}
{!toggleExpander && !loading ? ( {!toggleExpander && !loading ? (
<div className="search_plugin_wrap_summary"> <div className="search_plugin_wrap_summary">
{state.replaceEnabled? {state.replaceEnabled?
<div className="search_plugin_wrap_summary_replace"> <div className="search_plugin_wrap_summary_replace">
<div data-id={`replace-all-${props.file.filename}`} onClick={async() => replace()} className='btn btn-secondary mb-2 btn-sm'>Replace all</div> <div data-id={`replace-all-${props.file.filename}`} onClick={async() => replace()} className='btn btn-secondary mb-2 btn-sm'>
<FormattedMessage id='search.replaceAll' defaultMessage='Replace all' />
</div>
</div> </div>
:null} :null}
{lines.map((line, index) => ( {lines.map((line, index) => (
<ResultSummary <ResultSummary
setLoading={setLoading} setLoading={setLoading}
key={index} key={index}

@ -1,12 +1,14 @@
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { EtherscanSettingsProps } from '../types' import { EtherscanSettingsProps } from '../types'
import { etherscanTokenTitle, etherscanAccessTokenText, etherscanAccessTokenText2, etherscanTokenLink } from './constants' import { etherscanTokenTitle, etherscanAccessTokenText, etherscanAccessTokenText2, etherscanTokenLink } from './constants'
export function EtherscanSettings (props: EtherscanSettingsProps) { export function EtherscanSettings (props: EtherscanSettingsProps) {
const [etherscanToken, setEtherscanToken] = useState<string>("") const [etherscanToken, setEtherscanToken] = useState<string>("")
const intl = useIntl()
useEffect(() => { useEffect(() => {
if (props.config) { if (props.config) {
@ -28,13 +30,13 @@ export function EtherscanSettings (props: EtherscanSettingsProps) {
setEtherscanToken('') setEtherscanToken('')
props.removeToken() props.removeToken()
} }
return ( return (
<div className="border-top"> <div className="border-top">
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">{etherscanTokenTitle}</h6> <h6 className="card-title"><FormattedMessage id='settings.etherscanTokenTitle' defaultMessage={etherscanTokenTitle} /></h6>
<p className="mb-1">{etherscanAccessTokenText}</p> <p className="mb-1"><FormattedMessage id='settings.etherscanAccessTokenText' defaultMessage={etherscanAccessTokenText} /></p>
<p className="">{etherscanAccessTokenText2}</p> <p className=""><FormattedMessage id='settings.etherscanAccessTokenText2' defaultMessage={etherscanAccessTokenText2} /></p>
<p className="mb-1"><a className="text-primary" target="_blank" href={etherscanTokenLink}>{etherscanTokenLink}</a></p> <p className="mb-1"><a className="text-primary" target="_blank" href={etherscanTokenLink}>{etherscanTokenLink}</a></p>
<div> <div>
<label className="mb-0 pb-0">TOKEN:</label> <label className="mb-0 pb-0">TOKEN:</label>
@ -48,18 +50,20 @@ export function EtherscanSettings (props: EtherscanSettingsProps) {
<div> <div>
<div className="text-secondary mb-0 h6"> <div className="text-secondary mb-0 h6">
<div className="d-flex justify-content-end pt-2"> <div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="saveetherscantoken" data-id="settingsTabSaveEtherscanToken" onClick={saveEtherscanToken} value="Save" type="button" disabled={etherscanToken === ''}></input> <input className="btn btn-sm btn-primary ml-2" id="saveetherscantoken" data-id="settingsTabSaveEtherscanToken" onClick={saveEtherscanToken} value={intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'})} type="button" disabled={etherscanToken === ''}></input>
<CustomTooltip <CustomTooltip
tooltipText="Delete Etherscan token" tooltipText="Delete Etherscan token"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="removeetherscantokenTooltip" tooltipId="removeetherscantokenTooltip"
placement="left-start" placement="left-start"
> >
<button className="btn btn-sm btn-secondary ml-2" id="removeetherscantoken" data-id="settingsTabRemoveEtherscanToken" title="Delete Etherscan token" onClick={removeToken}>Remove</button></CustomTooltip> <button className="btn btn-sm btn-secondary ml-2" id="removeetherscantoken" data-id="settingsTabRemoveEtherscanToken" title="Delete Etherscan token" onClick={removeToken}>
<FormattedMessage id='settings.remove' defaultMessage='Remove' />
</button></CustomTooltip>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) )
} }

@ -1,6 +1,7 @@
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import { GithubSettingsProps } from '../types' import { GithubSettingsProps } from '../types'
import { gitAccessTokenTitle, gitAccessTokenText, gitAccessTokenText2, gitAccessTokenLink } from './constants' import { gitAccessTokenTitle, gitAccessTokenText, gitAccessTokenText2, gitAccessTokenLink } from './constants'
@ -9,13 +10,14 @@ export function GithubSettings (props: GithubSettingsProps) {
const [githubToken, setGithubToken] = useState<string>("") const [githubToken, setGithubToken] = useState<string>("")
const [githubUserName, setGithubUsername] = useState<string>("") const [githubUserName, setGithubUsername] = useState<string>("")
const [githubEmail, setGithubEmail] = useState<string>("") const [githubEmail, setGithubEmail] = useState<string>("")
const intl = useIntl()
useEffect(() => { useEffect(() => {
if (props.config) { if (props.config) {
const githubToken = props.config.get('settings/gist-access-token') || '' const githubToken = props.config.get('settings/gist-access-token') || ''
const githubUserName = props.config.get('settings/github-user-name') || '' const githubUserName = props.config.get('settings/github-user-name') || ''
const githubEmail = props.config.get('settings/github-email') || '' const githubEmail = props.config.get('settings/github-email') || ''
setGithubToken(githubToken) setGithubToken(githubToken)
setGithubUsername(githubUserName) setGithubUsername(githubUserName)
setGithubEmail(githubEmail) setGithubEmail(githubEmail)
@ -45,13 +47,13 @@ export function GithubSettings (props: GithubSettingsProps) {
setGithubEmail('') setGithubEmail('')
props.removeToken() props.removeToken()
} }
return ( return (
<div className="border-top"> <div className="border-top">
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">{gitAccessTokenTitle}</h6> <h6 className="card-title"><FormattedMessage id='settings.gitAccessTokenTitle' defaultMessage={gitAccessTokenTitle} /></h6>
<p className="mb-1">{gitAccessTokenText}</p> <p className="mb-1"><FormattedMessage id='settings.gitAccessTokenText' defaultMessage={gitAccessTokenText} /></p>
<p className="">{gitAccessTokenText2}</p> <p className=""><FormattedMessage id='settings.gitAccessTokenText2' defaultMessage={gitAccessTokenText2} /></p>
<p className="mb-1"><a className="text-primary" target="_blank" href={gitAccessTokenLink}>{gitAccessTokenLink}</a></p> <p className="mb-1"><a className="text-primary" target="_blank" href={gitAccessTokenLink}>{gitAccessTokenLink}</a></p>
<div> <div>
<label className="mb-0 pb-0">TOKEN:</label> <label className="mb-0 pb-0">TOKEN:</label>
@ -73,14 +75,16 @@ export function GithubSettings (props: GithubSettingsProps) {
<div className="text-secondary mb-0 h6"> <div className="text-secondary mb-0 h6">
<input id="githubemail" data-id="settingsTabGithubEmail" type="text" className="form-control" onChange={(e) => handleChangeEmailState(e)} value={ githubEmail } /> <input id="githubemail" data-id="settingsTabGithubEmail" type="text" className="form-control" onChange={(e) => handleChangeEmailState(e)} value={ githubEmail } />
<div className="d-flex justify-content-end pt-2"> <div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="savegisttoken" data-id="settingsTabSaveGistToken" onClick={saveGithubToken} value="Save" type="button"></input> <input className="btn btn-sm btn-primary ml-2" id="savegisttoken" data-id="settingsTabSaveGistToken" onClick={saveGithubToken} value={intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'})} type="button"></input>
<CustomTooltip <CustomTooltip
tooltipText="Delete Github Credentials" tooltipText="Delete Github Credentials"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="removegisttokenTooltip" tooltipId="removegisttokenTooltip"
placement="top-start" placement="top-start"
> >
<button className="btn btn-sm btn-secondary ml-2" id="removegisttoken" data-id="settingsTabRemoveGistToken" onClick={removeToken}>Remove</button> <button className="btn btn-sm btn-secondary ml-2" id="removegisttoken" data-id="settingsTabRemoveGistToken" onClick={removeToken}>
<FormattedMessage id='settings.remove' defaultMessage='Remove' />
</button>
</CustomTooltip> </CustomTooltip>
</div> </div>
</div> </div>
@ -88,4 +92,4 @@ export function GithubSettings (props: GithubSettingsProps) {
</div> </div>
</div> </div>
) )
} }

@ -7,6 +7,8 @@ import { ethereumVM, generateContractMetadat, personal, textWrapEventAction, use
import { initialState, toastInitialState, toastReducer, settingReducer } from './settingsReducer' import { initialState, toastInitialState, toastReducer, settingReducer } from './settingsReducer'
import { Toaster } from '@remix-ui/toaster'// eslint-disable-line import { Toaster } from '@remix-ui/toaster'// eslint-disable-line
import { RemixUiThemeModule, ThemeModule} from '@remix-ui/theme-module' import { RemixUiThemeModule, ThemeModule} from '@remix-ui/theme-module'
import { RemixUiLocaleModule, LocaleModule} from '@remix-ui/locale-module'
import { FormattedMessage, useIntl } from 'react-intl'
import { GithubSettings } from './github-settings' import { GithubSettings } from './github-settings'
import { EtherscanSettings } from './etherscan-settings' import { EtherscanSettings } from './etherscan-settings'
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
@ -18,6 +20,7 @@ export interface RemixUiSettingsProps {
_deps: any, _deps: any,
useMatomoAnalytics: boolean useMatomoAnalytics: boolean
themeModule: ThemeModule themeModule: ThemeModule
localeModule: LocaleModule
} }
export const RemixUiSettings = (props: RemixUiSettingsProps) => { export const RemixUiSettings = (props: RemixUiSettingsProps) => {
@ -33,7 +36,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const [ipfsProtocol, setipfsProtocol] = useState('') const [ipfsProtocol, setipfsProtocol] = useState('')
const [ipfsProjectId, setipfsProjectId] = useState('') const [ipfsProjectId, setipfsProjectId] = useState('')
const [ipfsProjectSecret, setipfsProjectSecret] = useState('') const [ipfsProjectSecret, setipfsProjectSecret] = useState('')
const intl = useIntl()
const initValue = () => { const initValue = () => {
const metadataConfig = props.config.get('settings/generate-contract-metadata') const metadataConfig = props.config.get('settings/generate-contract-metadata')
if (metadataConfig === undefined || metadataConfig === null) generateContractMetadat(props.config, true, dispatch) if (metadataConfig === undefined || metadataConfig === null) generateContractMetadat(props.config, true, dispatch)
@ -46,7 +49,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const displayErrors = props.config.get('settings/display-errors') const displayErrors = props.config.get('settings/display-errors')
if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, false, dispatch) if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, false, dispatch)
const useShowGas = props.config.get('settings/show-gas') const useShowGas = props.config.get('settings/show-gas')
if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, false, dispatch) if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, false, dispatch)
} }
@ -178,52 +181,61 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
}}>Reset to Default settings</button> }}><FormattedMessage id='settings.reset' defaultMessage='Reset to Default settings' /></button>
</div> </div>
</CustomTooltip> </CustomTooltip>
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">General settings</h6> <h6 className="card-title"><FormattedMessage id='settings.general' defaultMessage='General settings' /></h6>
<div className="mt-2 custom-control custom-checkbox mb-1"> <div className="mt-2 custom-control custom-checkbox mb-1">
<input onChange={onchangeGenerateContractMetadata} id="generatecontractmetadata" data-id="settingsTabGenerateContractMetadata" type="checkbox" className="custom-control-input" name="contractMetadata" checked={isMetadataChecked} /> <input onChange={onchangeGenerateContractMetadata} id="generatecontractmetadata" data-id="settingsTabGenerateContractMetadata" type="checkbox" className="custom-control-input" name="contractMetadata" checked={isMetadataChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/generate-contract-metadata')}`} data-id="settingsTabGenerateContractMetadataLabel" htmlFor="generatecontractmetadata">{generateContractMetadataText}</label> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/generate-contract-metadata')}`} data-id="settingsTabGenerateContractMetadataLabel" htmlFor="generatecontractmetadata">
<FormattedMessage id='settings.generateContractMetadataText' defaultMessage={generateContractMetadataText} />
</label>
</div> </div>
<div className="fmt-2 custom-control custom-checkbox mb-1"> <div className="fmt-2 custom-control custom-checkbox mb-1">
<input onChange={onchangeOption} className="custom-control-input" id="alwaysUseVM" data-id="settingsTabAlwaysUseVM" type="checkbox" name="ethereumVM" checked={isEthereumVMChecked} /> <input onChange={onchangeOption} className="custom-control-input" id="alwaysUseVM" data-id="settingsTabAlwaysUseVM" type="checkbox" name="ethereumVM" checked={isEthereumVMChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/always-use-vm')}`} htmlFor="alwaysUseVM">{ethereunVMText}</label> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/always-use-vm')}`} htmlFor="alwaysUseVM">
<FormattedMessage id='settings.ethereunVMText' defaultMessage={ethereunVMText} />
</label>
</div> </div>
<div className="mt-2 custom-control custom-checkbox mb-1"> <div className="mt-2 custom-control custom-checkbox mb-1">
<input id="editorWrap" className="custom-control-input" type="checkbox" onChange={textWrapEvent} checked={isEditorWrapChecked} /> <input id="editorWrap" className="custom-control-input" type="checkbox" onChange={textWrapEvent} checked={isEditorWrapChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/text-wrap')}`} htmlFor="editorWrap">{wordWrapText}</label> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/text-wrap')}`} htmlFor="editorWrap">
<FormattedMessage id='settings.wordWrapText' defaultMessage={wordWrapText} />
</label>
</div> </div>
<div className='custom-control custom-checkbox mb-1'> <div className='custom-control custom-checkbox mb-1'>
<input onChange={onchangeUseAutoComplete} id="settingsUseAutoComplete" type="checkbox" className="custom-control-input" checked={isAutoCompleteChecked} /> <input onChange={onchangeUseAutoComplete} id="settingsUseAutoComplete" type="checkbox" className="custom-control-input" checked={isAutoCompleteChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/auto-completion')}`} data-id="settingsAutoCompleteLabel" htmlFor="settingsUseAutoComplete"> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/auto-completion')}`} data-id="settingsAutoCompleteLabel" htmlFor="settingsUseAutoComplete">
<span>{useAutoCompleteText}</span> <span><FormattedMessage id='settings.useAutoCompleteText' defaultMessage={useAutoCompleteText} /></span>
</label> </label>
</div> </div>
<div className='custom-control custom-checkbox mb-1'> <div className='custom-control custom-checkbox mb-1'>
<input onChange={onchangeShowGasInEditor} id="settingsUseShowGas" type="checkbox" className="custom-control-input" checked={isShowGasInEditorChecked} /> <input onChange={onchangeShowGasInEditor} id="settingsUseShowGas" type="checkbox" className="custom-control-input" checked={isShowGasInEditorChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/show-gas')}`} data-id="settingsShowGasLabel" htmlFor="settingsUseShowGas"> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/show-gas')}`} data-id="settingsShowGasLabel" htmlFor="settingsUseShowGas">
<span>{useShowGasInEditorText}</span> <span><FormattedMessage id='settings.useShowGasInEditorText' defaultMessage={useShowGasInEditorText} /></span>
</label> </label>
</div> </div>
<div className='custom-control custom-checkbox mb-1'> <div className='custom-control custom-checkbox mb-1'>
<input onChange={onchangeDisplayErrors} id="settingsDisplayErrors" type="checkbox" className="custom-control-input" checked={displayErrorsChecked} /> <input onChange={onchangeDisplayErrors} id="settingsDisplayErrors" type="checkbox" className="custom-control-input" checked={displayErrorsChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/display-errors')}`} data-id="displayErrorsLabel" htmlFor="settingsDisplayErrors"> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/display-errors')}`} data-id="displayErrorsLabel" htmlFor="settingsDisplayErrors">
<span>{displayErrorsText}</span> <span><FormattedMessage id='settings.displayErrorsText' defaultMessage={displayErrorsText} /></span>
</label> </label>
</div> </div>
<div className="custom-control custom-checkbox mb-1"> <div className="custom-control custom-checkbox mb-1">
<input onChange={onchangePersonal} id="personal" type="checkbox" className="custom-control-input" checked={isPersonalChecked} /> <input onChange={onchangePersonal} id="personal" type="checkbox" className="custom-control-input" checked={isPersonalChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/personal-mode')}`} htmlFor="personal"> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/personal-mode')}`} htmlFor="personal">
<i className="fas fa-exclamation-triangle text-warning" aria-hidden="true"></i> <span> </span> <i className="fas fa-exclamation-triangle text-warning" aria-hidden="true"></i> <span> </span>
<span> </span>{enablePersonalModeText} {warnText} <span> </span>
<FormattedMessage id='settings.enablePersonalModeText' defaultMessage={enablePersonalModeText} />
&nbsp;
<FormattedMessage id='settings.warnText' defaultMessage={warnText} />
</label> </label>
</div> </div>
<div className="custom-control custom-checkbox mb-1"> <div className="custom-control custom-checkbox mb-1">
<input onChange={onchangeMatomoAnalytics} id="settingsMatomoAnalytics" type="checkbox" className="custom-control-input" checked={isMatomoChecked} /> <input onChange={onchangeMatomoAnalytics} id="settingsMatomoAnalytics" type="checkbox" className="custom-control-input" checked={isMatomoChecked} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/matomo-analytics')}`} htmlFor="settingsMatomoAnalytics"> <label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/matomo-analytics')}`} htmlFor="settingsMatomoAnalytics">
<span>{matomoAnalytics}</span> <span><FormattedMessage id='settings.matomoAnalytics' defaultMessage={matomoAnalytics} /></span>
<a href="https://medium.com/p/66ef69e14931/" target="_blank"> Analytics in Remix IDE</a> <span>&</span> <a target="_blank" href="https://matomo.org/free-software">Matomo</a> <a href="https://medium.com/p/66ef69e14931/" target="_blank"> Analytics in Remix IDE</a> <span>&</span> <a target="_blank" href="https://matomo.org/free-software">Matomo</a>
</label> </label>
</div> </div>
@ -254,7 +266,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const swarmSettings = () => ( const swarmSettings = () => (
<div className="border-top"> <div className="border-top">
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">{ swarmSettingsTitle }</h6> <h6 className="card-title"><FormattedMessage id='settings.swarm' defaultMessage={ swarmSettingsTitle } /></h6>
<div className="pt-2 pt-2 mb-0 pb-0"><label className="m-0">PRIVATE BEE ADDRESS:</label> <div className="pt-2 pt-2 mb-0 pb-0"><label className="m-0">PRIVATE BEE ADDRESS:</label>
<div className="text-secondary mb-0 h6"> <div className="text-secondary mb-0 h6">
<input id="swarmprivatebeeaddress" data-id="settingsPrivateBeeAddress" className="form-control" onChange={handleSavePrivateBeeAddress} value={privateBeeAddress} /> <input id="swarmprivatebeeaddress" data-id="settingsPrivateBeeAddress" className="form-control" onChange={handleSavePrivateBeeAddress} value={privateBeeAddress} />
@ -268,7 +280,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</div> </div>
</div> </div>
<div className="d-flex justify-content-end pt-2"> <div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="saveswarmsettings" data-id="settingsTabSaveSwarmSettings" onClick={() => saveSwarmSettings()} value="Save" type="button" disabled={privateBeeAddress === ''}></input> <input className="btn btn-sm btn-primary ml-2" id="saveswarmsettings" data-id="settingsTabSaveSwarmSettings" onClick={() => saveSwarmSettings()} value={intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'})} type="button" disabled={privateBeeAddress === ''}></input>
</div> </div>
</div> </div>
</div> </div>
@ -318,7 +330,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const ipfsSettings = () => ( const ipfsSettings = () => (
<div className="border-top"> <div className="border-top">
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">{ ipfsSettingsText }</h6> <h6 className="card-title"><FormattedMessage id='settings.ipfs' defaultMessage={ ipfsSettingsText } /></h6>
<div className="pt-2 mb-0"><label className="m-0">IPFS HOST:</label> <div className="pt-2 mb-0"><label className="m-0">IPFS HOST:</label>
<div className="text-secondary mb-0 h6"> <div className="text-secondary mb-0 h6">
<input placeholder='e.g. ipfs.infura.io' id="settingsIpfsUrl" data-id="settingsIpfsUrl" className="form-control" onChange={handleSaveIpfsUrl} value={ ipfsUrl } /> <input placeholder='e.g. ipfs.infura.io' id="settingsIpfsUrl" data-id="settingsIpfsUrl" className="form-control" onChange={handleSaveIpfsUrl} value={ ipfsUrl } />
@ -345,7 +357,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</div> </div>
</div> </div>
<div className="d-flex justify-content-end pt-2"> <div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="saveIpfssettings" data-id="settingsTabSaveIpfsSettings" onClick={() => saveIpfsSettings()} value="Save" type="button"></input> <input className="btn btn-sm btn-primary ml-2" id="saveIpfssettings" data-id="settingsTabSaveIpfsSettings" onClick={() => saveIpfsSettings()} value={intl.formatMessage({id: 'settings.save', defaultMessage: 'Save'})} type="button"></input>
</div> </div>
</div> </div>
</div>) </div>)
@ -354,14 +366,14 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
return ( return (
<div> <div>
{state.message ? <Toaster message= {state.message}/> : null} {state.message ? <Toaster message= {state.message}/> : null}
{generalConfig()} {generalConfig()}
<GithubSettings <GithubSettings
saveToken={(githubToken: string, githubUserName: string, githubEmail: string) => { saveToken={(githubToken: string, githubUserName: string, githubEmail: string) => {
saveTokenToast(props.config, dispatchToast, githubToken, "gist-access-token") saveTokenToast(props.config, dispatchToast, githubToken, "gist-access-token")
saveTokenToast(props.config, dispatchToast, githubUserName, "github-user-name") saveTokenToast(props.config, dispatchToast, githubUserName, "github-user-name")
saveTokenToast(props.config, dispatchToast, githubEmail, "github-email") saveTokenToast(props.config, dispatchToast, githubEmail, "github-email")
}} }}
removeToken={() => { removeToken={() => {
removeTokenToast(props.config, dispatchToast, "gist-access-token") removeTokenToast(props.config, dispatchToast, "gist-access-token")
removeTokenToast(props.config, dispatchToast, "github-user-name") removeTokenToast(props.config, dispatchToast, "github-user-name")
removeTokenToast(props.config, dispatchToast, "github-email") removeTokenToast(props.config, dispatchToast, "github-email")
@ -372,7 +384,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
saveToken={(etherscanToken: string) => { saveToken={(etherscanToken: string) => {
saveTokenToast(props.config, dispatchToast, etherscanToken, "etherscan-access-token") saveTokenToast(props.config, dispatchToast, etherscanToken, "etherscan-access-token")
}} }}
removeToken={() => { removeToken={() => {
removeTokenToast(props.config, dispatchToast, "etherscan-access-token") removeTokenToast(props.config, dispatchToast, "etherscan-access-token")
}} }}
config={props.config} config={props.config}
@ -380,6 +392,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
{swarmSettings()} {swarmSettings()}
{ipfsSettings()} {ipfsSettings()}
<RemixUiThemeModule themeModule={props._deps.themeModule} /> <RemixUiThemeModule themeModule={props._deps.themeModule} />
<RemixUiLocaleModule localeModule={props._deps.localeModule} />
</div> </div>
) )
} }

@ -1,4 +1,5 @@
import React, { useEffect, useState, useRef, useReducer } from 'react' // eslint-disable-line import React, { useEffect, useState, useRef, useReducer } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import semver from 'semver' import semver from 'semver'
import { CompilerContainerProps } from './types' import { CompilerContainerProps } from './types'
import { ConfigurationSettings } from '@remix-project/remix-lib-ts' import { ConfigurationSettings } from '@remix-project/remix-lib-ts'
@ -67,6 +68,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const [truffleCompilation, setTruffleCompilation] = useState(false) const [truffleCompilation, setTruffleCompilation] = useState(false)
const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState) const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)
const intl = useIntl()
useEffect(() => { useEffect(() => {
if (workspaceName) { if (workspaceName) {
api.setAppParameter('configFilePath', defaultPath) api.setAppParameter('configFilePath', defaultPath)
@ -569,7 +572,17 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const promptCompiler = () => { const promptCompiler = () => {
// custom url https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js // custom url https://solidity-blog.s3.eu-central-1.amazonaws.com/data/08preview/soljson.js
modal('Add a custom compiler', promptMessage('URL'), 'OK', addCustomCompiler, 'Cancel', () => { }) modal(
intl.formatMessage({
id: 'solidity.addACustomCompiler',
defaultMessage: 'Add a custom compiler',
}),
promptMessage('URL'),
'OK',
addCustomCompiler,
'Cancel',
() => {}
)
} }
const showCompilerLicense = () => { const showCompilerLicense = () => {
@ -723,12 +736,14 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<article> <article>
<div className='pt-0 remixui_compilerSection'> <div className='pt-0 remixui_compilerSection'>
<div className="mb-1"> <div className="mb-1">
<label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">Compiler</label> <label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
<FormattedMessage id='solidity.compiler' defaultMessage='Compiler' />
</label>
<CustomTooltip <CustomTooltip
placement="top" placement="top"
tooltipId="promptCompilerTooltip" tooltipId="promptCompilerTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText={"Add a custom compiler with URL"} tooltipText={<FormattedMessage id='solidity.addACustomCompilerWithURL' defaultMessage='Add a custom compiler with URL' />}
> >
<span className="far fa-plus border-0 p-0 ml-3" onClick={() => promptCompiler()}></span> <span className="far fa-plus border-0 p-0 ml-3" onClick={() => promptCompiler()}></span>
</CustomTooltip> </CustomTooltip>
@ -754,27 +769,37 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div> </div>
<div className="mb-2 flex-row-reverse remixui_nightlyBuilds custom-control custom-checkbox"> <div className="mb-2 flex-row-reverse remixui_nightlyBuilds custom-control custom-checkbox">
<input className="mr-2 custom-control-input" id="nightlies" type="checkbox" onChange={handleNightliesChange} checked={state.includeNightlies} /> <input className="mr-2 custom-control-input" id="nightlies" type="checkbox" onChange={handleNightliesChange} checked={state.includeNightlies} />
<label htmlFor="nightlies" data-id="compilerNightliesBuild" className="form-check-label custom-control-label">Include nightly builds</label> <label htmlFor="nightlies" data-id="compilerNightliesBuild" className="form-check-label custom-control-label">
<FormattedMessage id='solidity.includeNightlyBuilds' defaultMessage='Include nightly builds' />
</label>
</div> </div>
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox"> <div className="mt-2 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" type="checkbox" onChange={handleAutoCompile} data-id="compilerContainerAutoCompile" id="autoCompile" title="Auto compile" checked={state.autoCompile} /> <input className="remixui_autocompile custom-control-input" type="checkbox" onChange={handleAutoCompile} data-id="compilerContainerAutoCompile" id="autoCompile" title="Auto compile" checked={state.autoCompile} />
<label className="form-check-label custom-control-label" htmlFor="autoCompile">Auto compile</label> <label className="form-check-label custom-control-label" htmlFor="autoCompile">
<FormattedMessage id='solidity.autoCompile' defaultMessage='Auto compile' />
</label>
</div> </div>
<div className="mt-1 mb-2 remixui_compilerConfig custom-control custom-checkbox"> <div className="mt-1 mb-2 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" onChange={handleHideWarningsChange} id="hideWarningsBox" type="checkbox" title="Hide warnings" checked={state.hideWarnings} /> <input className="remixui_autocompile custom-control-input" onChange={handleHideWarningsChange} id="hideWarningsBox" type="checkbox" title="Hide warnings" checked={state.hideWarnings} />
<label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">Hide warnings</label> <label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">
<FormattedMessage id='solidity.hideWarnings' defaultMessage='Hide warnings' />
</label>
</div> </div>
{ {
isHardhatProject && isHardhatProject &&
<div className="mt-3 remixui_compilerConfig custom-control custom-checkbox"> <div className="mt-3 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" onChange={updatehhCompilation} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation" checked={hhCompilation} /> <input className="remixui_autocompile custom-control-input" onChange={updatehhCompilation} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation" checked={hhCompilation} />
<label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label> <label className="form-check-label custom-control-label" htmlFor="enableHardhat">
<FormattedMessage id='solidity.enableHardhat' defaultMessage='Enable Hardhat Compilation' />
</label>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/hardhat.html#enable-hardhat-compilation' target={'_blank'}> <a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/hardhat.html#enable-hardhat-compilation' target={'_blank'}>
<CustomTooltip <CustomTooltip
placement={'right'} placement={'right'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="overlay-tooltip-hardhat" tooltipId="overlay-tooltip-hardhat"
tooltipText={<span className="border bg-light text-dark p-1 pr-3" style={{ minWidth: '230px' }}>Learn how to use Hardhat Compilation</span>} tooltipText={<span className="border bg-light text-dark p-1 pr-3" style={{ minWidth: '230px' }}>
<FormattedMessage id='solidity.learnHardhat' defaultMessage='Learn how to use Hardhat Compilation' />
</span>}
> >
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i> <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
</CustomTooltip> </CustomTooltip>
@ -785,13 +810,17 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
isTruffleProject && isTruffleProject &&
<div className="mt-3 remixui_compilerConfig custom-control custom-checkbox"> <div className="mt-3 remixui_compilerConfig custom-control custom-checkbox">
<input className="remixui_autocompile custom-control-input" onChange={updateTruffleCompilation} id="enableTruffle" type="checkbox" title="Enable Truffle Compilation" checked={truffleCompilation} /> <input className="remixui_autocompile custom-control-input" onChange={updateTruffleCompilation} id="enableTruffle" type="checkbox" title="Enable Truffle Compilation" checked={truffleCompilation} />
<label className="form-check-label custom-control-label" htmlFor="enableTruffle">Enable Truffle Compilation</label> <label className="form-check-label custom-control-label" htmlFor="enableTruffle">
<FormattedMessage id='solidity.enableTruffle' defaultMessage='Enable Truffle Compilation' />
</label>
<a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/truffle.html#enable-truffle-compilation' target={'_blank'}> <a className="mt-1 text-nowrap" href='https://remix-ide.readthedocs.io/en/latest/truffle.html#enable-truffle-compilation' target={'_blank'}>
<CustomTooltip <CustomTooltip
placement={'right'} placement={'right'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="overlay-tooltip-truffle" tooltipId="overlay-tooltip-truffle"
tooltipText={<span className="border bg-light text-dark p-1 pr-3" style={{ minWidth: '230px' }}>Learn how to use Truffle Compilation</span>} tooltipText={<span className="border bg-light text-dark p-1 pr-3" style={{ minWidth: '230px' }}>
<FormattedMessage id='solidity.learnTruffle' defaultMessage='Learn how to use Truffle Compilation' />
</span>}
> >
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i> <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
</CustomTooltip> </CustomTooltip>
@ -801,7 +830,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div> </div>
<div className="d-flex px-4 remixui_compilerConfigSection justify-content-between" onClick={toggleConfigurations}> <div className="d-flex px-4 remixui_compilerConfigSection justify-content-between" onClick={toggleConfigurations}>
<div className="d-flex"> <div className="d-flex">
<label className="mt-1 remixui_compilerConfigSection">Advanced Configurations</label> <label className="mt-1 remixui_compilerConfigSection">
<FormattedMessage id='solidity.advancedConfigurations' defaultMessage='Advanced Configurations' />
</label>
</div> </div>
<div> <div>
<span data-id='scConfigExpander' onClick={toggleConfigurations}> <span data-id='scConfigExpander' onClick={toggleConfigurations}>
@ -812,11 +843,15 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}> <div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio"> <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" /> <input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" />
<label className="form-check-label custom-control-label" htmlFor="scManualConfig" data-id="scManualConfiguration">Compiler configuration</label> <label className="form-check-label custom-control-label" htmlFor="scManualConfig" data-id="scManualConfiguration">
<FormattedMessage id='solidity.compilerConfiguration' defaultMessage='Compiler configuration' />
</label>
</div> </div>
<div className={`flex-column 'd-flex'}`}> <div className={`flex-column 'd-flex'}`}>
<div className="mb-2 ml-4"> <div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label> <label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">
<FormattedMessage id='solidity.language' defaultMessage='Language' />
</label>
<CustomTooltip <CustomTooltip
placement="right-start" placement="right-start"
tooltipId="compilerLabelTooltip" tooltipId="compilerLabelTooltip"
@ -830,7 +865,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</CustomTooltip> </CustomTooltip>
</div> </div>
<div className="mb-2 ml-4"> <div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">EVM Version</label> <label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">
<FormattedMessage id='solidity.evmVersion' defaultMessage='EVM Version' />
</label>
<select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} disabled={state.useFileConfiguration} className="custom-select" id="evmVersionSelector"> <select value={state.evmVersion} onChange={(e) => handleEvmVersionChange(e.target.value)} disabled={state.useFileConfiguration} className="custom-select" id="evmVersionSelector">
{compileTabLogic.evmVersions.map((version, index) => (<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>{version}</option>))} {compileTabLogic.evmVersions.map((version, index) => (<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>{version}</option>))}
</select> </select>
@ -838,7 +875,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className="mt-1 mt-3 border-dark pb-3 ml-4 remixui_compilerConfig custom-control custom-checkbox"> <div className="mt-1 mt-3 border-dark pb-3 ml-4 remixui_compilerConfig custom-control custom-checkbox">
<div className="justify-content-between align-items-center d-flex"> <div className="justify-content-between align-items-center d-flex">
<input onChange={(e) => { handleOptimizeChange(e.target.checked) }} disabled={state.useFileConfiguration} className="custom-control-input" id="optimize" type="checkbox" checked={state.optimize} /> <input onChange={(e) => { handleOptimizeChange(e.target.checked) }} disabled={state.useFileConfiguration} className="custom-control-input" id="optimize" type="checkbox" checked={state.optimize} />
<label className="form-check-label custom-control-label" htmlFor="optimize">Enable optimization</label> <label className="form-check-label custom-control-label" htmlFor="optimize">
<FormattedMessage id='solidity.enableOptimization' defaultMessage='Enable optimization' />
</label>
<input <input
min="1" min="1"
className="custom-select ml-2 remixui_runs" className="custom-select ml-2 remixui_runs"
@ -855,7 +894,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div> </div>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio"> <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" /> <input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" />
<label className="form-check-label custom-control-label" htmlFor="scFileConfig" data-id="scFileConfiguration">Use configuration file</label> <label className="form-check-label custom-control-label" htmlFor="scFileConfig" data-id="scFileConfiguration">
<FormattedMessage id='solidity.useConfigurationFile' defaultMessage='Use configuration file' />
</label>
</div> </div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}> <div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{(!showFilePathInput && state.useFileConfiguration) && <CustomTooltip {(!showFilePathInput && state.useFileConfiguration) && <CustomTooltip
@ -885,7 +926,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
} }
}} }}
/> />
{!showFilePathInput && <button disabled={!state.useFileConfiguration} data-id="scConfigChangeFilePath" className="btn btn-sm btn-secondary" onClick={() => { setShowFilePathInput(true) }}>Change</button>} {!showFilePathInput && <button disabled={!state.useFileConfiguration} data-id="scConfigChangeFilePath" className="btn btn-sm btn-secondary" onClick={() => { setShowFilePathInput(true) }}>
<FormattedMessage id='solidity.change' defaultMessage='Change' />
</button>}
</div> </div>
</div> </div>
<div className="px-4"> <div className="px-4">
@ -899,8 +942,18 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div>} </div>}
> >
<span> <span>
{<i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i>} { <i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i> }
Compile {typeof state.compiledFileName === 'string' ? extractNameFromKey(state.compiledFileName) || '<no file selected>' : '<no file selected>'} <FormattedMessage id='solidity.compile' defaultMessage='Compile' />
{typeof state.compiledFileName === 'string'
? extractNameFromKey(state.compiledFileName) ||
`<${intl.formatMessage({
id: 'solidity.noFileSelected',
defaultMessage: 'no file selected',
})}>`
: `<${intl.formatMessage({
id: 'solidity.noFileSelected',
defaultMessage: 'no file selected',
})}>`}
</span> </span>
</CustomTooltip> </CustomTooltip>
</button> </button>
@ -921,7 +974,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div>} </div>}
> >
<span> <span>
Compile and Run script <FormattedMessage id='solidity.compileAndRunScript' defaultMessage='Compile and Run script' />
</span> </span>
</CustomTooltip> </CustomTooltip>
</button> </button>

@ -1,4 +1,5 @@
import React, { useState, useEffect, Fragment } from 'react' // eslint-disable-line import React, { useState, useEffect, Fragment } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { ContractSelectionProps } from './types' import { ContractSelectionProps } from './types'
import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
@ -12,6 +13,8 @@ export const ContractSelection = (props: ContractSelectionProps) => {
const [selectedContract, setSelectedContract] = useState('') const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null) const [storage, setStorage] = useState(null)
const intl = useIntl()
useEffect(() => { useEffect(() => {
if (contractList.length) { if (contractList.length) {
const compiledPathArr = compiledFileName.split('/') const compiledPathArr = compiledFileName.split('/')
@ -159,7 +162,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
{ {
Object.keys(contractProperties).map((propertyName, index) => { Object.keys(contractProperties).map((propertyName, index) => {
const copyDetails = <span className="remixui_copyDetails"><CopyToClipboard content={contractProperties[propertyName]} direction='top' /></span> const copyDetails = <span className="remixui_copyDetails"><CopyToClipboard content={contractProperties[propertyName]} direction='top' /></span>
const questionMark = <span className="remixui_questionMark"><i title={ help[propertyName] } className="fas fa-question-circle" aria-hidden="true"></i></span> const questionMark = <span className="remixui_questionMark"><i title={ intl.formatMessage({id: `solidity.${propertyName}`, defaultMessage: help[propertyName]}) } className="fas fa-question-circle" aria-hidden="true"></i></span>
return ( return (
<div className="remixui_log" key={index}> <div className="remixui_log" key={index}>
@ -201,10 +204,10 @@ export const ContractSelection = (props: ContractSelectionProps) => {
placement="right-start" placement="right-start"
tooltipId="publishOnIpfsTooltip" tooltipId="publishOnIpfsTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Publish on Ipfs" tooltipText={`${intl.formatMessage({id: 'solidity.publishOn', defaultMessage: 'Publish on'})} Ipfs`}
> >
<span> <span>
<span>Publish on Ipfs</span> <span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" /> <img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
</span> </span>
</CustomTooltip> </CustomTooltip>
@ -214,10 +217,10 @@ export const ContractSelection = (props: ContractSelectionProps) => {
placement="right-start" placement="right-start"
tooltipId="publishOnSwarmTooltip" tooltipId="publishOnSwarmTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Publish on Swarm" tooltipText={`${intl.formatMessage({id: 'solidity.publishOn', defaultMessage: 'Publish on'})} Swarm`}
> >
<span> <span>
<span>Publish on Swarm</span> <span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" /> <img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" />
</span> </span>
</CustomTooltip> </CustomTooltip>
@ -229,7 +232,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Display Contract Details" tooltipText="Display Contract Details"
> >
<span>Compilation Details</span> <span><FormattedMessage id='solidity.compilationDetails' defaultMessage='Compilation Details' /></span>
</CustomTooltip> </CustomTooltip>
</button> </button>
{/* Copy to Clipboard */} {/* Copy to Clipboard */}

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { registerCommandAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction' import { registerCommandAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction'
import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer' import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer'
import { getKeyOf, getValueOf, Objectfilter, matched } from './utils/utils' import { getKeyOf, getValueOf, Objectfilter, matched } from './utils/utils'
@ -76,6 +77,8 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const panelRef = useRef(null) const panelRef = useRef(null)
const terminalMenu = useRef(null) const terminalMenu = useRef(null)
const intl = useIntl ()
const scrollToBottom = () => { const scrollToBottom = () => {
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' }) messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
} }
@ -436,7 +439,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setIsOpen(!panels.terminal.minimized) setIsOpen(!panels.terminal.minimized)
}) })
return () => { return () => {
props.plugin.off('layout', 'change') props.plugin.off('layout', 'change')
@ -469,7 +472,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
htmlFor="listenNetworkCheck" htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput" data-id="listenNetworkCheckInput"
> >
listen on all transactions <FormattedMessage id='terminal.listen' defaultMessage='listen on all transactions' />
</label> </label>
</div> </div>
<div className="remix_ui_terminal_search d-flex align-items-center h-100"> <div className="remix_ui_terminal_search d-flex align-items-center h-100">
@ -482,7 +485,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
type="text" type="text"
className="remix_ui_terminal_filter border form-control" className="remix_ui_terminal_filter border form-control"
id="searchInput" id="searchInput"
placeholder="Search with transaction hash or address" placeholder={intl.formatMessage({id: 'terminal.search', defaultMessage: "Search with transaction hash or address"})}
data-id="terminalInputSearch" /> data-id="terminalInputSearch" />
</div> </div>
</div> </div>

@ -1,30 +1,31 @@
import React, { useEffect } from 'react' // eslint-disable-line import React, { useEffect } from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
const TerminalWelcomeMessage = ({ packageJson, storage }) => { const TerminalWelcomeMessage = ({ packageJson, storage }) => {
return ( return (
<div className="remix_ui_terminal_block px-4 " data-id="block_null"> <div className="remix_ui_terminal_block px-4 " data-id="block_null">
<div className="remix_ui_terminal_welcome"> Welcome to Remix {packageJson} </div><br /> <div className="remix_ui_terminal_welcome"> <FormattedMessage id='terminal.welcomeText1' defaultMessage='Welcome to' /> Remix {packageJson} </div><br />
<div className="">Your files are stored in {(window as any).remixFileSystem.name}, {storage} used</div><br /> <div className=""><FormattedMessage id='terminal.welcomeText2' defaultMessage='Your files are stored in' /> {(window as any).remixFileSystem.name}, {storage} <FormattedMessage id='terminal.used' defaultMessage='used' /></div><br />
<div>You can use this terminal to: </div> <div><FormattedMessage id='terminal.welcomeText3' defaultMessage='You can use this terminal to' />: </div>
<ul className='ml-0 mr-4'> <ul className='ml-0 mr-4'>
<li key="details-and-debug" >Check transactions details and start debugging.</li> <li key="details-and-debug" ><FormattedMessage id='terminal.welcomeText4' defaultMessage='Check transactions details and start debugging' />.</li>
<li key="run-javascript">Execute JavaScript scripts: <li key="run-javascript"><FormattedMessage id='terminal.welcomeText5' defaultMessage='Execute JavaScript scripts' />:
<br /> <br />
<i> - Input a script directly in the command line interface </i> <i> - <FormattedMessage id='terminal.welcomeText6' defaultMessage='Input a script directly in the command line interface' /> </i>
<br /> <br />
<i> - Select a Javascript file in the file explorer and then run \`remix.execute()\` or \`remix.exeCurrent()\` in the command line interface </i> <i> - <FormattedMessage id='terminal.welcomeText7' defaultMessage='Select a Javascript file in the file explorer and then run \`remix.execute()\` or \`remix.exeCurrent()\` in the command line interface' /> </i>
<br /> <br />
<i> - Right click on a JavaScript file in the file explorer and then click \`Run\` </i> <i> - <FormattedMessage id='terminal.welcomeText8' defaultMessage='Right click on a JavaScript file in the file explorer and then click \`Run\`' /> </i>
</li> </li>
</ul> </ul>
<div>The following libraries are accessible:</div> <div><FormattedMessage id='terminal.welcomeText9' defaultMessage='The following libraries are accessible' />:</div>
<ul className='ml-0 mr-4'> <ul className='ml-0 mr-4'>
<li key="web3-152"><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li> <li key="web3-152"><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.5.2</a></li>
<li key="ethers-console"><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li> <li key="ethers-console"><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li>
<li key="remix-console">remix</li> <li key="remix-console">remix</li>
</ul> </ul>
<div>Type the library name to see available commands.</div> <div><FormattedMessage id='terminal.welcomeText10' defaultMessage='Type the library name to see available commands' />.</div>
</div> </div>
) )
} }

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl'
import { ThemeModule } from '../../types/theme-module'; import { ThemeModule } from '../../types/theme-module';
import './remix-ui-theme-module.module.css'; import './remix-ui-theme-module.module.css';
@ -18,7 +19,7 @@ export function RemixUiThemeModule({ themeModule }: RemixUiThemeModuleProps) {
return ( return (
<div className="border-top"> <div className="border-top">
<div className="card-body pt-3 pb-2"> <div className="card-body pt-3 pb-2">
<h6 className="card-title">Themes</h6> <h6 className="card-title"><FormattedMessage id='settings.themes' defaultMessage='Themes' /></h6>
<div className="card-text themes-container"> <div className="card-text themes-container">
{themeModule.getThemes() {themeModule.getThemes()
? themeModule.getThemes().map((theme, idx) => ( ? themeModule.getThemes().map((theme, idx) => (

@ -1,4 +1,5 @@
import React, { useRef, useEffect } from 'react' // eslint-disable-line import React, { useRef, useEffect } from 'react' // eslint-disable-line
import { useIntl } from 'react-intl'
import { action, FileExplorerContextMenuProps } from '../types' import { action, FileExplorerContextMenuProps } from '../types'
import '../css/file-explorer-context-menu.css' import '../css/file-explorer-context-menu.css'
@ -14,6 +15,7 @@ const _paq = window._paq = window._paq || [] //eslint-disable-line
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => { export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props
const contextMenuRef = useRef(null) const contextMenuRef = useRef(null)
const intl = useIntl()
useEffect(() => { useEffect(() => {
contextMenuRef.current.focus() contextMenuRef.current.focus()
}, []) }, [])
@ -120,7 +122,7 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
break break
} }
hideContextMenu() hideContextMenu()
}}>{item.label || item.name}</li> }}>{intl.formatMessage({id: `filePanel.${item.id}`, defaultMessage: item.label || item.name})}</li>
}) })
} }

@ -1,5 +1,6 @@
import { CustomTooltip } from '@remix-ui/helper' import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect } from 'react' //eslint-disable-line import React, { useState, useEffect } from 'react' //eslint-disable-line
import { FormattedMessage } from 'react-intl'
import { Placement } from 'react-bootstrap/esm/Overlay' import { Placement } from 'react-bootstrap/esm/Overlay'
import { FileExplorerMenuProps } from '../types' import { FileExplorerMenuProps } from '../types'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
@ -69,7 +70,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
placement="right" placement="right"
tooltipId="uploadFileTooltip" tooltipId="uploadFileTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText={title} tooltipText={<FormattedMessage id={`filePanel.${action}`} defaultMessage={title} />}
key={`index-${action}-${placement}-${icon}`} key={`index-${action}-${placement}-${icon}`}
> >
<label <label
@ -93,7 +94,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
placement={placement as Placement} placement={placement as Placement}
tooltipId={`${action}-${title}-${icon}-${index}`} tooltipId={`${action}-${title}-${icon}-${index}`}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText={title} tooltipText={<FormattedMessage id={`filePanel.${action}`} defaultMessage={title} />}
key={`${action}-${title}-${index}`} key={`${action}-${title}-${index}`}
> >
<span <span

@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef, useContext, SyntheticEvent, ChangeEvent, KeyboardEvent } from 'react' // eslint-disable-line import React, { useState, useEffect, useRef, useContext, SyntheticEvent, ChangeEvent, KeyboardEvent } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { CustomIconsToggle, CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper' import { CustomIconsToggle, CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper'
import { FileExplorer } from './components/file-explorer' // eslint-disable-line import { FileExplorer } from './components/file-explorer' // eslint-disable-line
@ -28,6 +29,7 @@ export function Workspace () {
const workspaceRenameInput = useRef() const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef() const workspaceCreateInput = useRef()
const workspaceCreateTemplateInput = useRef() const workspaceCreateTemplateInput = useRef()
const intl = useIntl()
const cloneUrlRef = useRef<HTMLInputElement>() const cloneUrlRef = useRef<HTMLInputElement>()
const initGitRepoRef = useRef<HTMLInputElement>() const initGitRepoRef = useRef<HTMLInputElement>()
const filteredBranches = selectedWorkspace ? (selectedWorkspace.branches || []).filter(branch => branch.name.includes(branchFilter) && branch.name !== 'HEAD').slice(0, 20) : [] const filteredBranches = selectedWorkspace ? (selectedWorkspace.branches || []).filter(branch => branch.name.includes(branchFilter) && branch.name !== 'HEAD').slice(0, 20) : []
@ -70,19 +72,31 @@ export function Workspace () {
}, [currentWorkspace]) }, [currentWorkspace])
const renameCurrentWorkspace = () => { const renameCurrentWorkspace = () => {
global.modal('Rename Current Workspace', renameModalMessage(), 'OK', onFinishRenameWorkspace, '') global.modal(intl.formatMessage({id: 'filePanel.workspace.rename', defaultMessage: 'Rename Current Workspace'}), renameModalMessage(), 'OK', onFinishRenameWorkspace, '')
} }
const createWorkspace = () => { const createWorkspace = () => {
global.modal('Create Workspace', createModalMessage(), 'OK', onFinishCreateWorkspace, '') global.modal(intl.formatMessage({id: 'filePanel.workspace.create', defaultMessage: 'Create Workspace'}), createModalMessage(), 'OK', onFinishCreateWorkspace, '')
} }
const deleteCurrentWorkspace = () => { const deleteCurrentWorkspace = () => {
global.modal('Delete Current Workspace', 'Are you sure to delete the current workspace?', 'OK', onFinishDeleteWorkspace, '') global.modal(
intl.formatMessage({id: 'filePanel.workspace.delete', defaultMessage: 'Delete Current Workspace'}),
intl.formatMessage({id: 'filePanel.workspace.deleteConfirm', defaultMessage: 'Are you sure to delete the current workspace?'}),
'OK',
onFinishDeleteWorkspace,
''
)
} }
const cloneGitRepository = () => { const cloneGitRepository = () => {
global.modal('Clone Git Repository', cloneModalMessage(), 'OK', handleTypingUrl, '') global.modal(
intl.formatMessage({id: 'filePanel.workspace.clone', defaultMessage: 'Clone Git Repository'}),
cloneModalMessage(),
'OK',
handleTypingUrl,
''
)
} }
const downloadWorkspaces = async () => { const downloadWorkspaces = async () => {
@ -202,7 +216,7 @@ export function Workspace () {
// @ts-ignore // @ts-ignore
workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value + '_upgradeable'}_${Date.now()}` workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value + '_upgradeable'}_${Date.now()}`
} }
const toggleBranches = (isOpen: boolean) => { const toggleBranches = (isOpen: boolean) => {
setShowBranches(isOpen) setShowBranches(isOpen)
} }
@ -244,7 +258,7 @@ export function Workspace () {
const createModalMessage = () => { const createModalMessage = () => {
return ( return (
<> <>
<label id="selectWsTemplate" className="form-check-label" style={{fontWeight: "bolder"}}>Choose a template</label> <label id="selectWsTemplate" className="form-check-label" style={{fontWeight: "bolder"}}><FormattedMessage id='filePanel.workspace.chooseTemplate' defaultMessage='Choose a template' /></label>
<select name="wstemplate" className="mb-3 form-control custom-select" id="wstemplate" defaultValue='remixDefault' ref={workspaceCreateTemplateInput} onChange={updateWsName}> <select name="wstemplate" className="mb-3 form-control custom-select" id="wstemplate" defaultValue='remixDefault' ref={workspaceCreateTemplateInput} onChange={updateWsName}>
<optgroup style={{fontSize: "medium"}} label="General"> <optgroup style={{fontSize: "medium"}} label="General">
<option style={{fontSize: "small"}} value='remixDefault'>Default</option> <option style={{fontSize: "small"}} value='remixDefault'>Default</option>
@ -336,7 +350,13 @@ export function Workspace () {
const cloneModalMessage = () => { const cloneModalMessage = () => {
return ( return (
<> <>
<input type="text" data-id="modalDialogCustomPromptTextClone" placeholder='Enter git repository url' ref={cloneUrlRef} className="form-control" /> <input
type="text"
data-id="modalDialogCustomPromptTextClone"
placeholder={intl.formatMessage({id: 'filePanel.workspace.enterGitUrl', defaultMessage: 'Enter git repository url'})}
ref={cloneUrlRef}
className="form-control"
/>
</> </>
) )
} }
@ -346,7 +366,7 @@ export function Workspace () {
placement="right" placement="right"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Create" tooltipText={<FormattedMessage id='filePanel.workspace.create' defaultMessage='Create Workspace' />}
> >
<div <div
data-id='workspaceCreate' data-id='workspaceCreate'
@ -369,14 +389,14 @@ export function Workspace () {
className='far fa-plus pl-2' className='far fa-plus pl-2'
> >
</span> </span>
<span className="pl-3">Create</span> <span className="pl-3"><FormattedMessage id='filePanel.create' defaultMessage='Create' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
<CustomTooltip <CustomTooltip
placement="right-start" placement="right-start"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Delete Workspace" tooltipText={<FormattedMessage id='filePanel.workspace.delete' defaultMessage='Delete Workspace' />}
> >
<div <div
data-id='workspaceDelete' data-id='workspaceDelete'
@ -399,14 +419,14 @@ export function Workspace () {
className='far fa-trash pl-2' className='far fa-trash pl-2'
> >
</span> </span>
<span className="pl-3">{'Delete'}</span> <span className="pl-3"><FormattedMessage id='filePanel.delete' defaultMessage='Delete' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
<CustomTooltip <CustomTooltip
placement='right-start' placement='right-start'
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="workspaceRenametooltip" tooltipId="workspaceRenametooltip"
tooltipText="Rename Workspace" tooltipText={<FormattedMessage id='filePanel.workspace.rename' defaultMessage='Rename Workspace' />}
> >
<div onClick={() => { <div onClick={() => {
renameCurrentWorkspace() renameCurrentWorkspace()
@ -427,7 +447,7 @@ export function Workspace () {
}} }}
className='far fa-edit pl-2'> className='far fa-edit pl-2'>
</span> </span>
<span className="pl-3">{'Rename'}</span> <span className="pl-3"><FormattedMessage id='filePanel.rename' defaultMessage='Rename' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
<Dropdown.Divider className="border mb-0 mt-0" />, <Dropdown.Divider className="border mb-0 mt-0" />,
@ -435,7 +455,7 @@ export function Workspace () {
placement="right-start" placement="right-start"
tooltipId="cloneWorkspaceTooltip" tooltipId="cloneWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Clone Git Repository" tooltipText={<FormattedMessage id='filePanel.workspace.clone' defaultMessage='Clone Git Repository' />}
> >
<div <div
data-id='cloneGitRepository' data-id='cloneGitRepository'
@ -458,7 +478,7 @@ export function Workspace () {
className='fab fa-github pl-2' className='fab fa-github pl-2'
> >
</span> </span>
<span className="pl-3">{'Clone'}</span> <span className="pl-3"><FormattedMessage id='filePanel.clone' defaultMessage='Clone' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
<Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/>, <Dropdown.Divider className="border mt-0 mb-0 remixui_menuhr" style={{ pointerEvents: 'none' }}/>,
@ -466,7 +486,7 @@ export function Workspace () {
placement="right-start" placement="right-start"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Download Workspace" tooltipText={<FormattedMessage id='filePanel.workspace.download' defaultMessage='Download Workspace' />}
> >
<div <div
data-id='workspacesDownload' data-id='workspacesDownload'
@ -489,14 +509,14 @@ export function Workspace () {
className='far fa-download pl-2 ' className='far fa-download pl-2 '
> >
</span> </span>
<span className="pl-3">{'Download'}</span> <span className="pl-3"><FormattedMessage id='filePanel.download' defaultMessage='Download' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
<CustomTooltip <CustomTooltip
placement="right-start" placement="right-start"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Restore Workspace Backup" tooltipText={<FormattedMessage id='filePanel.workspace.restore' defaultMessage='Restore Workspace Backup' />}
> >
<div <div
data-id='workspacesRestore' data-id='workspacesRestore'
@ -519,7 +539,7 @@ export function Workspace () {
className='far fa-upload pl-2' className='far fa-upload pl-2'
> >
</span> </span>
<span className="pl-3">{'Restore'}</span> <span className="pl-3"><FormattedMessage id='filePanel.restore' defaultMessage='Restore' /></span>
</div> </div>
</CustomTooltip>, </CustomTooltip>,
] ]
@ -534,8 +554,8 @@ export function Workspace () {
<div className="mx-2 mb-2 d-flex flex-column"> <div className="mx-2 mb-2 d-flex flex-column">
<div className="d-flex justify-content-between"> <div className="d-flex justify-content-between">
<span className="d-flex align-items-end"> <span className="d-flex align-items-end">
<label className="pl-1 form-check-label" htmlFor="workspacesSelect"> <label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}>
WORKSPACES <FormattedMessage id='filePanel.workspace' defaultMessage='WORKSPACES' />
</label> </label>
</span> </span>
{currentWorkspace !== LOCALHOST ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75"> {currentWorkspace !== LOCALHOST ? (<span className="remixui_menu remixui_topmenu d-flex justify-content-between align-items-end w-75">
@ -543,7 +563,7 @@ export function Workspace () {
placement="top-end" placement="top-end"
tooltipId="createWorkspaceTooltip" tooltipId="createWorkspaceTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText="Create" tooltipText={<FormattedMessage id='filePanel.create' defaultMessage='Create' />}
> >
<span <span
hidden={currentWorkspace === LOCALHOST} hidden={currentWorkspace === LOCALHOST}
@ -616,7 +636,7 @@ export function Workspace () {
} }
</Dropdown.Item> </Dropdown.Item>
)) ))
} }
{ ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> } { ((global.fs.browser.workspaces.length <= 0) || currentWorkspace === NO_WORKSPACE) && <Dropdown.Item onClick={() => { switchWorkspace(NO_WORKSPACE) }}>{ <span className="pl-3">NO_WORKSPACE</span> }</Dropdown.Item> }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
@ -749,7 +769,7 @@ export function Workspace () {
</div> </div>
</Dropdown.Item> </Dropdown.Item>
) )
}) : }) :
<Dropdown.Item onClick={switchToNewBranch}> <Dropdown.Item onClick={switchToNewBranch}>
<div className="pl-1 pr-1" data-id="workspaceGitCreateNewBranch"> <div className="pl-1 pr-1" data-id="workspaceGitCreateNewBranch">
<i className="fas fa-code-branch pr-2"></i><span>Create branch: { branchFilter } from '{currentBranch}'</span> <i className="fas fa-code-branch pr-2"></i><span>Create branch: { branchFilter } from '{currentBranch}'</span>

@ -193,6 +193,9 @@
"remix-ui-file-decorators": { "remix-ui-file-decorators": {
"tags": [] "tags": []
}, },
"remix-ui-locale-module": {
"tags": []
},
"remix-ui-tooltip-popup": { "remix-ui-tooltip-popup": {
"tags": [] "tags": []
}, },
@ -215,4 +218,4 @@
] ]
} }
} }

@ -208,6 +208,7 @@
"react-bootstrap": "^1.6.4", "react-bootstrap": "^1.6.4",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draggable": "^4.4.4", "react-draggable": "^4.4.4",
"react-intl": "^6.0.4",
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-multi-carousel": "^2.8.2", "react-multi-carousel": "^2.8.2",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",
@ -348,5 +349,8 @@
"worker-loader": "^2.0.0", "worker-loader": "^2.0.0",
"yo-yo": "github:ioedeveloper/yo-yo", "yo-yo": "github:ioedeveloper/yo-yo",
"yo-yoify": "^3.7.3" "yo-yoify": "^3.7.3"
},
"resolutions": {
"@types/react": "^17.0.24"
} }
} }

@ -86,7 +86,7 @@
"@remix-ui/permission-handler": [ "@remix-ui/permission-handler": [
"libs/remix-ui/permission-handler/src/index.ts" "libs/remix-ui/permission-handler/src/index.ts"
], ],
"@remix-ui/file-decorators": ["libs/remix-ui/file-decorators/src/index.ts"], "@remix-ui/locale-module": ["libs/remix-ui/locale-module/src/index.ts"],
"@remix-ui/tooltip-popup": ["libs/remix-ui/tooltip-popup/src/index.ts"], "@remix-ui/tooltip-popup": ["libs/remix-ui/tooltip-popup/src/index.ts"],
"@remix-ui/drag-n-drop": ["libs/remix-ui/drag-n-drop/src/index.ts"] "@remix-ui/drag-n-drop": ["libs/remix-ui/drag-n-drop/src/index.ts"]
} }

@ -1456,6 +1456,21 @@
} }
} }
}, },
"remix-ui-locale-module": {
"root": "libs/remix-ui/locale-module",
"sourceRoot": "libs/remix-ui/locale-module/src",
"projectType": "library",
"architect": {
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"],
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"]
}
}
}
},
"remix-ui-tooltip-popup": { "remix-ui-tooltip-popup": {
"root": "libs/remix-ui/tooltip-popup", "root": "libs/remix-ui/tooltip-popup",
"sourceRoot": "libs/remix-ui/tooltip-popup/src", "sourceRoot": "libs/remix-ui/tooltip-popup/src",

@ -2465,6 +2465,76 @@
resolved "https://registry.yarnpkg.com/@f/svg-namespace/-/svg-namespace-1.0.1.tgz#f6f1a5ce5d3971a4ade91a11d22d4c459acd375f" resolved "https://registry.yarnpkg.com/@f/svg-namespace/-/svg-namespace-1.0.1.tgz#f6f1a5ce5d3971a4ade91a11d22d4c459acd375f"
integrity sha512-GQVBgTMtsxdOEKkgMNqiSJwBXpfDJgnMnWkpu/jv526piA6na7AjvKb+5fPkd42Ig5Aa2Dc5BVUrwF7R2nXhYw== integrity sha512-GQVBgTMtsxdOEKkgMNqiSJwBXpfDJgnMnWkpu/jv526piA6na7AjvKb+5fPkd42Ig5Aa2Dc5BVUrwF7R2nXhYw==
"@formatjs/ecma402-abstract@1.11.7":
version "1.11.7"
resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.7.tgz#47f1a854f679f813d9baa1ee55adae94880ec706"
integrity sha512-uNaok4XWMJBtPZk/veTDamFCm5HeWJUk2jwoVfH5/+wenQ60QHjH6T3UQ0GOOCz9jpKmed7vqOri7xSf//Dt7g==
dependencies:
"@formatjs/intl-localematcher" "0.2.28"
tslib "2.4.0"
"@formatjs/fast-memoize@1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.4.tgz#4b5ddce9eb7803ff0bd4052387672151a8b7f8a0"
integrity sha512-9ARYoLR8AEzXvj2nYrOVHY/h1dDMDWGTnKDLXSISF1uoPakSmfcZuSqjiqZX2wRkEUimPxdwTu/agyozBtZRHA==
dependencies:
tslib "2.4.0"
"@formatjs/icu-messageformat-parser@2.1.3":
version "2.1.3"
resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.3.tgz#d228ac26f22630689a1263e83192227f1d085bd3"
integrity sha512-hsdAn1dXcujW/G8DHw0iiIy7357pw10yOulCrL6xrQOKJAxT7m7EgpG0Hm1OW9xqaLEzqWyE/jA2AGVnOCaCQw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/icu-skeleton-parser" "1.3.9"
tslib "2.4.0"
"@formatjs/icu-skeleton-parser@1.3.9":
version "1.3.9"
resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.9.tgz#149badc16ffd15dd928f8047ae21aa9136e0ea73"
integrity sha512-s9THwwhiiSzbGSk73FP6Ur2MBwEj1vfgYDHKa5FiXGQMfYzdRdRvyH1dgqNgSFJPB6PM3DKtkloJLjpqpSDNUg==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
tslib "2.4.0"
"@formatjs/intl-displaynames@6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.0.2.tgz#f6349b5c75fd9ecc7c77c7e73101daee5dc69e3a"
integrity sha512-h9Id/6vbSHpARHKMVsjWag3KMZJpop9s67CZTd+AMxhjHb5xDG2b5rlSRJKx/UdIDQ+GzESK7a4fv32yylG3cw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/intl-localematcher" "0.2.28"
tslib "2.4.0"
"@formatjs/intl-listformat@7.0.2":
version "7.0.2"
resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.0.2.tgz#c07d370c9171dfc86c163addbfcb08f67ae26215"
integrity sha512-K+HXrYIvEcAH/dS8XXnSHQYC/z4w0eHjPlDx43HOoDY87/xV7rpHxFVXWXTgwLYC6iAPUO72Y/AaT9iq873juw==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/intl-localematcher" "0.2.28"
tslib "2.4.0"
"@formatjs/intl-localematcher@0.2.28":
version "0.2.28"
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.28.tgz#412ea7fefbfc7ed33cd6b43aa304fc14d816e564"
integrity sha512-FLsc6Gifs1np/8HnCn/7Q+lHMmenrD5fuDhRT82yj0gi9O19kfaFwjQUw1gZsyILuRyT93GuzdifHj7TKRhBcw==
dependencies:
tslib "2.4.0"
"@formatjs/intl@2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.3.0.tgz#848edf81c95d608757662b3feada0eb2dacc5424"
integrity sha512-mE8zGqP+Flrd8tS3AsdvSucnblqwR5gsGM4Bd5711abkabrz52F2TDrU88rVvVfCdHV4dFHFYEmUBVZZ4pATtg==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/fast-memoize" "1.2.4"
"@formatjs/icu-messageformat-parser" "2.1.3"
"@formatjs/intl-displaynames" "6.0.2"
"@formatjs/intl-listformat" "7.0.2"
intl-messageformat "10.1.0"
tslib "2.4.0"
"@fortawesome/fontawesome-free@^5.8.1": "@fortawesome/fontawesome-free@^5.8.1":
version "5.15.4" version "5.15.4"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5"
@ -4399,7 +4469,7 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.9.tgz#1cfb6d60ef3822c589f18e70f8b12f9a28ce8724"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ== integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/hoist-non-react-statics@^3.3.0": "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
@ -4609,7 +4679,7 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react@*", "@types/react@>=16.14.8", "@types/react@>=16.9.11", "@types/react@^17.0.24": "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@>=16.14.8", "@types/react@>=16.9.11", "@types/react@^17.0.24":
version "17.0.28" version "17.0.28"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.28.tgz#b40967e79af8f32182c6f6f2655ba196bafc7202" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.28.tgz#b40967e79af8f32182c6f6f2655ba196bafc7202"
integrity sha512-6OmflHgk2DlnsFi49kBW3/Dql1GT32bYSk+A6tFBDAt0T0bxotBdQwXkm77lVlczHwY6+Wu6IfpsGqArjOYtaA== integrity sha512-6OmflHgk2DlnsFi49kBW3/Dql1GT32bYSk+A6tFBDAt0T0bxotBdQwXkm77lVlczHwY6+Wu6IfpsGqArjOYtaA==
@ -13037,6 +13107,16 @@ interpret@^1.0.0, interpret@^1.4.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
intl-messageformat@10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.1.0.tgz#ffbbcbf1068af8466ad5497f78c30c3d96ef5505"
integrity sha512-diGMDv9Zo2Mggf6AkJszq/BIR5+rarkwcr4g5JGgREwbwAHY9hR/dYd8FbIgQx2RTxhJsABfAWCiENFLbaTZjg==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/fast-memoize" "1.2.4"
"@formatjs/icu-messageformat-parser" "2.1.3"
tslib "2.4.0"
intro.js@^4.1.0: intro.js@^4.1.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/intro.js/-/intro.js-4.3.0.tgz#39d072e638eae9c8491e225b8565ad63b6aaf1f7" resolved "https://registry.yarnpkg.com/intro.js/-/intro.js-4.3.0.tgz#39d072e638eae9c8491e225b8565ad63b6aaf1f7"
@ -19692,6 +19772,22 @@ react-fast-compare@^2.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-intl@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.0.4.tgz#635d7639aa7b70e837c75796535cf1d534016acf"
integrity sha512-eBIP4QuFOdr67+ZmNOA7WGzJ6dj0qgsGQbx3phzcel2B0kVLvfojTJuvYiFuLgbZTrRJMjHwYJZO5zsmibsfug==
dependencies:
"@formatjs/ecma402-abstract" "1.11.7"
"@formatjs/icu-messageformat-parser" "2.1.3"
"@formatjs/intl" "2.3.0"
"@formatjs/intl-displaynames" "6.0.2"
"@formatjs/intl-listformat" "7.0.2"
"@types/hoist-non-react-statics" "^3.3.1"
"@types/react" "16 || 17 || 18"
hoist-non-react-statics "^3.3.2"
intl-messageformat "10.1.0"
tslib "2.4.0"
react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1: react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -22906,6 +23002,11 @@ tslib@2.1.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tslib@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"

Loading…
Cancel
Save