diff --git a/package-lock.json b/package-lock.json index 6b8915d..5991dd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7042,6 +7042,14 @@ } } }, + "html-parse-stringify2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz", + "integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=", + "requires": { + "void-elements": "^2.0.1" + } + }, "html-webpack-plugin": { "version": "4.0.0-alpha.2", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-alpha.2.tgz", @@ -7409,6 +7417,14 @@ "resolved": "http://registry.npm.taobao.org/https-browserify/download/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, + "i18next": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-15.0.5.tgz", + "integrity": "sha512-JqcoIM20BuYq0iQm8VLlq2pqhCNoG60+QuOxLE9PCDGZZc+in9kWofw1qLUazo4B53jJDca1ARd2YLmhXcx2uw==", + "requires": { + "@babel/runtime": "^7.3.1" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz", @@ -13455,6 +13471,15 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.3.tgz", "integrity": "sha512-GoqeM3Xadie7XUApXOjkY3Qhs8RkwB/Za4WMedBGrOKH1eTuKGyoAECff7jiVonJchOx6KZ9i8ILO5XIoHB+Tg==" }, + "react-i18next": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-10.2.0.tgz", + "integrity": "sha512-b+AryBB2QiLCEmk8PaNGMkowEZVSY032crKuQToa+W1lBgrNiWVKstmi0z4bZqqcK9GL4cJr8Mevw4XpPFvpeQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "html-parse-stringify2": "2.0.1" + } + }, "react-is": { "version": "16.6.3", "resolved": "http://registry.npm.taobao.org/react-is/download/react-is-16.6.3.tgz", @@ -16595,6 +16620,11 @@ "indexof": "0.0.1" } }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, "w3c-hr-time": { "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/w3c-hr-time/download/w3c-hr-time-1.0.1.tgz", diff --git a/package.json b/package.json index 1595036..769d291 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,11 @@ "antd": "^3.13.6", "axios": "^0.18.0", "classnames": "^2.2.6", + "i18next": "^15.0.5", "node-sass": "^4.11.0", "react": "^16.8.3", "react-dom": "^16.8.3", + "react-i18next": "^10.2.0", "react-loading-screen": "0.0.17", "react-scripts": "2.1.5", "typed.js": "^2.0.10" diff --git a/src/App.js b/src/App.js index 100e646..6cd421c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,6 @@ import React, { Component } from "react"; +import { withTranslation } from 'react-i18next'; + import "./App.scss"; import Typed from "typed.js"; import { getOSList, removeContainerById } from "./util/api"; @@ -8,10 +10,13 @@ import { getItem, rmItem } from "./util/util"; import { Button, Tooltip, Divider, Card, Modal } from "antd"; import SelectSystemConfig from "./components/SelectSystemConfig"; import SystemConfiguration from "./components/SystemConfiguration"; +import LanguageSwitcher from "./components/LanguageSwitcher"; class App extends Component { constructor(props) { super(props); + this.t = props.t; + const { isExistContainer, container } = this.isExistContainer(); this.state = { @@ -22,8 +27,8 @@ class App extends Component { timeout: 24, cpu: 1, memory: 512, - port: 80, // 内部端口(填写表单填写的端口号) - externalPort: 0, // 外部端口(后端返回的端口号) + port: 80, // Internal port (entered by user) + externalPort: 0, // External port (assigned by api) container, isExistContainer, screenLoading: false, @@ -38,7 +43,7 @@ class App extends Component { if (document.getElementsByClassName('app__desc-content').length > 0) { this.typed = new Typed(".app__desc-content", { strings: [ - `Want to experiment with something on a Linux distribution? Let's start!` + this.t('site.typed') ], typeSpeed: 50 }); @@ -52,7 +57,7 @@ class App extends Component { if (containerInfo) { containerInfo = JSON.parse(containerInfo); const curTime = Math.floor(new Date().getTime() / 1000); - // 检查是否过期 + // Check if it's still valid if (curTime < containerInfo.timeout) { return { isExistContainer: true, container: containerInfo }; } else { @@ -79,7 +84,6 @@ class App extends Component { this.setState({ osList }); }; - handleOSSelect = selectedOS => { const osList = [...this.state.osList]; @@ -103,7 +107,7 @@ class App extends Component { }; handleSelectAgain = async () => { - this.setState({ screenLoading: true, screenText: "删除中..." }); + this.setState({ screenLoading: true, screenText: this.t('prompt.purging') }); const { container } = this.state; const timestamp = Math.floor(new Date().getTime() / 1000); this.p3 = removeContainerById( @@ -152,13 +156,13 @@ class App extends Component {

- instantbox + {this.t('site.heading')}

- Ubuntu / CentOS / Arch Linux / Debian / Fedora / Alpine + {this.state.osList.map(os => os.label).join(' / ') || this.t('site.heading')}
$ @@ -166,9 +170,10 @@ class App extends Component {
+ - {isExistContainer ? "您已创建系统" : "选择系统配置"} + {isExistContainer ? this.t('prompt.created-os') : this.t('prompt.select-os')} {isExistContainer && ( @@ -192,7 +197,7 @@ class App extends Component {
{isExistContainer ? (
- +
) : ( @@ -225,21 +231,21 @@ class App extends Component {
{ window.open(this.state.container.shareUrl.replace('http://:', `http://${window.location.hostname}:`)); this.setState({ skipModalVisible: false }); }} - okText="确定" - cancelText="取消" + okText={this.t('keyword.ok')} + cancelText={this.t('keyword.cancel')} onCancel={() => this.setState({ skipModalVisible: false })} > -

系统已创建,是否跳转到系统页面?

+

{this.t('sentence.open-webshell')}

); } } -export default App; +export default withTranslation()(App); diff --git a/src/App.scss b/src/App.scss index 77075b2..21b6d11 100644 --- a/src/App.scss +++ b/src/App.scss @@ -18,6 +18,11 @@ &__start { margin: 80px 0; } + &__lang-switcher { + position: absolute; + top: 10px; + right: 12px; + } &__text-field { margin-top: 8px !important; input { diff --git a/src/App.test.js b/src/App.test.js index a754b20..5e3105b 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,9 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; +import i18n from './i18n'; it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(, div); + ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); }); diff --git a/src/components/LanguageSwitcher/LanguageSwitcher.js b/src/components/LanguageSwitcher/LanguageSwitcher.js new file mode 100644 index 0000000..3dd59d5 --- /dev/null +++ b/src/components/LanguageSwitcher/LanguageSwitcher.js @@ -0,0 +1,34 @@ +import React from "react"; +import { withTranslation } from 'react-i18next'; + +/** + * Switch between languages + */ +class LanguageSwitcher extends React.Component { + + constructor(props) { + super(props); + this.t = props.t; + } + + changeLanguage(e) { + e.preventDefault(); + + this.props.i18n.changeLanguage(e.target.dataset.locale) + } + + render() { + return
+

+ {this.t('language')} | + { + this.t('locale') === 'en' + ? 中文 + : English + } +

+
+ } +} + +export default withTranslation()(LanguageSwitcher); diff --git a/src/components/LanguageSwitcher/index.js b/src/components/LanguageSwitcher/index.js new file mode 100644 index 0000000..e8e5b57 --- /dev/null +++ b/src/components/LanguageSwitcher/index.js @@ -0,0 +1,3 @@ +import LanguageSwitcher from './LanguageSwitcher'; + +export default LanguageSwitcher; diff --git a/src/components/SelectSystemConfig/SelectForm.js b/src/components/SelectSystemConfig/SelectForm.js index 29b914b..f521218 100644 --- a/src/components/SelectSystemConfig/SelectForm.js +++ b/src/components/SelectSystemConfig/SelectForm.js @@ -1,4 +1,6 @@ import React from "react"; +import { withTranslation } from 'react-i18next'; + import "./SelectSystemConfig.scss"; import { Input, @@ -7,83 +9,8 @@ import { const FormItem = Form.Item; -const rules = { - port: [ - { - required: true, - message: "请输入端口号" - }, - { - validator: (rule, value, callback) => { - if ( - (/^\d+$/g.test(value) && value >= 1 && value <= 65535) || - value === "" - ) { - return callback(); - } - callback("端口号格式或范围有误"); - }, - message: "端口号范围: 1 ~ 65535" - } - ], - cpu: [ - { - required: true, - message: "请输入 CPU 核数" - }, - { - validator: (rule, value, callback) => { - if ( - (/^\d+$/g.test(value) && value >= 1 && value <= 4) || - value === "" - ) { - return callback(); - } - callback("CPU 格式或范围有误"); - }, - message: "CPU 范围:1 ~ 4" - } - ], - mem: [ - { - required: true, - message: "请输入空间大小" - }, - { - validator: (rule, value, callback) => { - if ( - (/^\d+$/g.test(value) && value >= 1 && value <= 3584) || - value === "" - ) { - return callback(); - } - callback("空间大小格式或范围有误"); - }, - message: "空间大小范围:1 ~ 3584" - } - ], - timeout: [ - { - required: true, - message: "请输入使用时长" - }, - { - validator: (rule, value, callback) => { - if ( - (/^\d+$/g.test(value) && value >= 1 && value <= 24) || - value === "" - ) { - return callback(); - } - callback("使用时长格式或范围有误"); - }, - message: "使用时长范围:1 ~ 24" - } - ] -}; - /** - * 填写系统性能参数的表单 + * A form for instantbox requests */ export class SelectForm extends React.Component { static propTypes = {}; @@ -91,13 +18,91 @@ export class SelectForm extends React.Component { constructor(props) { super(props); - + this.t = props.t; + this.state = {}; } componentDidMount = () => { this.props.getForm && this.props.getForm(this.props.form); - }; + } + + getRules = () => { + return { + port: [ + { + required: true, + message: this.t('prompt.enter-port') + }, + { + validator: (rule, value, callback) => { + if ( + (/^\d+$/g.test(value) && value >= 1 && value <= 65535) || + value === "" + ) { + return callback(); + } + callback(this.t('sentence.err-port')); + }, + message: this.t('sentence.msg-port') + } + ], + cpu: [ + { + required: true, + message: this.t('prompt.enter-cpu-core-count') + }, + { + validator: (rule, value, callback) => { + if ( + (/^\d+$/g.test(value) && value >= 1 && value <= 4) || + value === "" + ) { + return callback(); + } + callback(this.t('sentence.err-cpu-core-count')); + }, + message: this.t('sentence.msg-cpu-core-count') + } + ], + mem: [ + { + required: true, + message: this.t('prompt.enter-memory-in-mb') + }, + { + validator: (rule, value, callback) => { + if ( + (/^\d+$/g.test(value) && value >= 1 && value <= 3584) || + value === "" + ) { + return callback(); + } + callback(this.t('sentence.err-memory-in-mb')); + }, + message: this.t('sentence.msg-memory-in-mb') + } + ], + timeout: [ + { + required: true, + message: this.t('prompt.enter-ttl-in-hours') + }, + { + validator: (rule, value, callback) => { + if ( + (/^\d+$/g.test(value) && value >= 1 && value <= 24) || + value === "" + ) { + return callback(); + } + callback(this.t('sentence.err-ttl-in-hours')); + }, + message: this.t('sentence.msg-ttl-in-hours') + } + ] + } + } render() { const { getFieldDecorator } = this.props.form; @@ -105,35 +110,36 @@ export class SelectForm extends React.Component { labelCol: { span: 10 }, wrapperCol: { span: 14 } }; + const rules = this.getRules() return (
- + {getFieldDecorator("port", { initialValue: "80", rules: rules.port - })()} + })()} - + {getFieldDecorator("cpu", { initialValue: "1", rules: rules.cpu - })()} + })()} - + {getFieldDecorator("mem", { initialValue: "512", rules: rules.mem - })()} + })()} - + {getFieldDecorator("timeout", { initialValue: "24", rules: rules.timeout - })()} + })()} ); } } -export default Form.create()(SelectForm); +export default Form.create()(withTranslation()(SelectForm)); diff --git a/src/components/SelectSystemConfig/SelectSystemConfig.js b/src/components/SelectSystemConfig/SelectSystemConfig.js index 3915bb8..df202fb 100644 --- a/src/components/SelectSystemConfig/SelectSystemConfig.js +++ b/src/components/SelectSystemConfig/SelectSystemConfig.js @@ -1,6 +1,9 @@ import React, { Fragment } from "react"; -import "./SelectSystemConfig.scss"; import classNames from "classnames"; +import { withTranslation } from 'react-i18next'; + +import "./SelectSystemConfig.scss"; + import { Spin, Steps, @@ -20,14 +23,15 @@ const Step = Steps.Step; const Option = Select.Option; /** - * 选择系统配置 + * Select system configuration */ -export class SelectSystemConfig extends React.Component { +class SelectSystemConfig extends React.Component { static propTypes = {}; static defaultProps = {}; constructor(props) { super(props); + this.t = props.t; const currentStep = this.getCurrentStep(); const { isExistContainer, container } = this.isExistContainer(); @@ -43,19 +47,6 @@ export class SelectSystemConfig extends React.Component { container, skipModalVisible: false }; - - this.steps = [ - { - title: "系统", - desc: "选择系统", - content: "" - }, - { - title: "性能参数", - desc: "填写系统性能参数", - content: "" - } - ]; } getCurrentStep = () => { @@ -69,7 +60,7 @@ export class SelectSystemConfig extends React.Component { } containerInfo = JSON.parse(containerInfo); const curTime = Math.floor(new Date().getTime() / 1000); - // 未过期 + // Not expired if (curTime < containerInfo.timeout) { return { isExistContainer: true, container: containerInfo }; } @@ -89,14 +80,14 @@ export class SelectSystemConfig extends React.Component { const { currentStep, selectsObj, port } = this.state; if (currentStep === 0) { if (!selectsObj.length) { - return message.error("请先选择系统和系统版本"); + return message.error(this.t('sentence.err-empty-os')); } } if (currentStep === 1) { const result = !/\d./.test(port); if (!port || result) { - return message.error("请输入正确格式的端口号"); + return message.error(this.t('sentence.err-port')); } } this.setState({ currentStep: this.state.currentStep + 1 }); @@ -119,7 +110,7 @@ export class SelectSystemConfig extends React.Component { const { validateFields } = this._form; validateFields(async (err, values) => { if (err) { - return message.error("性能参数有误,请重新填写"); + return message.error(this.t('sentence.err-resources')); } this.setState({ modalVisible: true }); this._values = values; @@ -175,56 +166,65 @@ export class SelectSystemConfig extends React.Component { this._shareUrl = res.shareUrl; } else { this.setState({ okLoading: false }); - message.error("创建失败,请重试"); + message.error(this.t('sentence.err-creation')); } } }; generateStepsContent = () => { - this.steps[0].content = ( - - {this.props.osList.map((item, index) => { - const { selectsObj } = this.state; - let osCode; - if (selectsObj[index]) { - osCode = selectsObj[index].osCode; - } - const classes = classNames({ - isSelect: !!selectsObj[index] - }); - return ( - -
{item.label}
- {item.label} - -
- ); - })} -
- ); - - this.steps[1].content = ; + return [ + { + title: this.t('keyword.os'), + desc: this.t('prompt.choose-os'), + content: ( + + {this.props.osList.map((item, index) => { + const { selectsObj } = this.state; + let osCode; + if (selectsObj[index]) { + osCode = selectsObj[index].osCode; + } + const classes = classNames({ + isSelect: !!selectsObj[index] + }); + return ( + +
{item.label}
+ {item.label} + +
+ ); + })} +
+ ) + }, + { + title: this.t('keyword.resources'), + desc: this.t('prompt.choose-resources'), + content: + } + ]; }; getSystemVersion = () => { @@ -250,13 +250,13 @@ export class SelectSystemConfig extends React.Component { } = this.state; const { osList } = this.props; const { system, version } = this.getSystemVersion(); - this.generateStepsContent(); + const steps = this.generateStepsContent(); return (
- {this.steps.map(step => ( + {steps.map(step => (
- {this.steps[currentStep].content} + {steps[currentStep].content}
this.setState({ modalVisible: false })} - okText="确定" - cancelText="取消" + okText={this.t('keyword.confirm')} + cancelText={this.t('keyword.cancel')} > { return ( - + {title} ); }; /** - * 显示用户配置表单 + * Show current system configuration */ class SystemConfiguration extends React.Component { static propTypes = { /** - * 系统名称 + * OS name */ system: PropTypes.string, /** - * 系统版本号 + * OS version */ version: PropTypes.string, /** - * cpu 核数 + * CPU */ cpu: PropTypes.string, /** - * 空间大小 + * Memory */ mem: PropTypes.string, /** - * 使用时长 + * Time-to-live */ timeout: PropTypes.any, /** - * 内部端口号 + * Port exposed inside container */ innerPort: PropTypes.string, /** - * 外部端口号 + * Port that is publically accessible */ externalPort: PropTypes.any, /** - * 是否显示外部字段 - * 默认:false + * Should show innerPort + * Default: false */ showInnerPort: PropTypes.bool, /** - * 是否显示内部字段 - * 默认:false + * Should show external port + * Default: false */ showExternalPort: PropTypes.bool }; + static defaultProps = { showInnerPort: false, showExternalPort: false }; + constructor(props) { + super(props); + this.t = props.t; + } + getSystemVersion = () => { const { system, version } = this.props; return ( @@ -98,29 +104,28 @@ class SystemConfiguration extends React.Component { return (
- + {this.getSystemVersion()} - + {innerPort} - - {cpu} 核 + + {cpu} - + {mem} M({(mem / 1024).toFixed(2)} G) - - {timeout} 小时 + + {timeout} {showInnerPort && ( - + {innerPort} )} - {showExternalPort && ( - + {externalPort} )} @@ -129,4 +134,4 @@ class SystemConfiguration extends React.Component { } } -export default Form.create()(SystemConfiguration); +export default Form.create()(withTranslation()(SystemConfiguration)); diff --git a/src/components/SystemConfiguration/SystemConfiguration.scss b/src/components/SystemConfiguration/SystemConfiguration.scss deleted file mode 100644 index e69de29..0000000 diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100644 index 0000000..a864034 --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,21 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +const resources = { + en: require('./locale/en'), + zh: require('./locale/zh'), +} + +i18n + .use(initReactI18next) + .init({ + resources, + lng: 'en', + + interpolation: { + escapeValue: false, + }, + }); + + +export default i18n; diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json new file mode 100644 index 0000000..1aab17b --- /dev/null +++ b/src/i18n/locale/en.json @@ -0,0 +1,62 @@ +{ + "translation": { + "locale": "en", + "language": "English", + "site": { + "heading": "instantbox", + "typed": "Want to experiment with something on a Linux distribution? Let's start!" + }, + "keyword": { + "notice": "Notice", + "confirm": "Confirm", + "ok": "Yes", + "cancel": "Cancel", + "create": "Create", + "next": "Next", + "back": "Back", + "port": "Port", + "internal-port": "Port (internal)", + "external-port": "Port (public)", + "os": "OS", + "resources": "Resources", + "cpu-core-count": "CPU cores", + "memory": "Memory", + "memory-in-mb": "Memory (M)", + "ttl-in-hours": "Duration (H)" + }, + "prompt": { + "created-os": "Your instantbox", + "choose-os": "Choose OS", + "choose-os-version": "Choose version", + "select-os": "Choose OS", + "open-os": "Launch webshell", + "choose-resources": "Choose resources", + "selected-os-resources": "Selected OS", + "purge-os": "Purge", + "purging": "Purging...", + "enter-port": "Enter port", + "enter-cpu-core-count": "Enter CPU cores", + "enter-memory-in-mb": "Enter memory", + "enter-ttl-in-hours": "Enter duration" + }, + "sentence": { + "open-webshell": "Instantbox has been created. Launch webshell?", + "open-webshell-try-again": "Please click again if webshell encountered an error.", + "eg-port": "e.g. 80", + "eg-cpu-core-count": "e.g. 1", + "eg-memory-in-mb": "e.g. 512", + "eg-ttl-in-hours": "e.g. 24", + "err-resources": "Validation failed, please re-enter.", + "err-creation": "Failed to create instantbox. Please try again later.", + "err-port": "Failed to validate port", + "msg-port": "Port range: 1-65535", + "err-cpu-core-count": "Failed to validate CPU count", + "msg-cpu-core-count": "CPU core range:1-4", + "err-memory-in-mb": "Failed to validate memory", + "msg-memory-in-mb": "Memory range: 1-3584", + "err-ttl-in-hours": "Failed to validate duration", + "msg-ttl-in-hours": "Duration range: 1-24", + "err-empty-os": "Please choose an OS." + } + } +} \ No newline at end of file diff --git a/src/i18n/locale/zh.json b/src/i18n/locale/zh.json new file mode 100644 index 0000000..3ae53f9 --- /dev/null +++ b/src/i18n/locale/zh.json @@ -0,0 +1,62 @@ +{ + "translation": { + "locale": "zh", + "language": "中文", + "site": { + "heading": "instantbox", + "typed": "Want to experiment with something on a Linux distribution? Let's start!" + }, + "keyword": { + "notice": "提示", + "confirm": "确认", + "ok": "是", + "cancel": "取消", + "create": "创建", + "next": "下一步", + "back": "上一步", + "port": "端口", + "internal-port": "内部端口", + "external-port": "外部端口", + "os": "系统", + "resources": "性能参数", + "cpu-core-count": "CPU 核心", + "memory": "内存", + "memory-in-mb": "内存 (M)", + "ttl-in-hours": "使用时长 (H)" + }, + "prompt": { + "created-os": "您已创建系统", + "choose-os": "选择系统", + "choose-os-version": "选择系统版本", + "select-os": "选择系统配置", + "open-os": "打开已创建的系统", + "choose-resources": "填写系统性能参数", + "selected-os-resources": "所选系统及系统配置", + "purge-os": "删除系统", + "purging": "删除中...", + "enter-port": "请输入端口号", + "enter-cpu-core-count": "请输入 CPU 核数", + "enter-memory-in-mb": "请输入内存大小", + "enter-ttl-in-hours": "请输入使用时长" + }, + "sentence": { + "open-webshell": "系统已创建,是否跳转到系统页面?", + "open-webshell-try-again": "若打开的页面报错,请重新点击", + "eg-port": "如:80", + "eg-cpu-core-count": "如:1", + "eg-memory-in-mb": "如:512", + "eg-ttl-in-hours": "如:24", + "err-resources": "性能参数有误,请重新填写", + "err-creation": "创建失败,请重试", + "err-port": "端口号格式或范围有误", + "msg-port": "端口号范围: 1 ~ 65535", + "err-cpu-core-count": "CPU 格式或范围有误", + "msg-cpu-core-count": "CPU 范围:1 ~ 4", + "err-memory-in-mb": "内存大小格式或范围有误", + "msg-memory-in-mb": "内存大小范围:1 ~ 3584", + "err-ttl-in-hours": "使用时长格式或范围有误", + "msg-ttl-in-hours": "使用时长范围:1 ~ 24", + "err-empty-os": "请先选择系统和系统版本" + } + } +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index b9fed53..ec0f96f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,12 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import i18n from './i18n'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import 'antd/dist/antd.css'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render(, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/src/util/api.js b/src/util/api.js index 480e37f..9c173b6 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -3,7 +3,6 @@ import { getBaseUrl } from "./util"; axios.defaults.baseURL = getBaseUrl(); -// 请求拦截 axios.interceptors.request.use( function(config) { return config; @@ -13,12 +12,11 @@ axios.interceptors.request.use( } ); -// 响应拦截 axios.interceptors.response.use( function(res) { const data = res.data; return data; - //// 加上 message + //// Return error message // if (data.statusCode === 1) { // return data; // } else { @@ -31,8 +29,9 @@ axios.interceptors.response.use( ); /** - * 使 this.setState() 在异步请求中可以取消调用:https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html - * @param {promise} promise 请求对象 + * Allow requests to be cancelled + * Reference: https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html + * @param {promise} promise Request */ export const makeCancelable = promise => { let hasCanceled_ = false; @@ -50,8 +49,11 @@ export const makeCancelable = promise => { } }; }; + +/* + * Available API endpoints + */ const apiVersion = "/v2/superinspire"; -// 请求列表 const requestUrlList = { getOSList: `${apiVersion}/getOSList`, getOS: `${apiVersion}/getOS`, @@ -59,14 +61,14 @@ const requestUrlList = { }; /** - * 获取容器列表 + * Fetch OS list */ export const getOSList = () => { return makeCancelable(axios.get(requestUrlList.getOSList)); }; /** - * 获取容器跳转的地址 + * Create container */ export const getOSUrl = (osCode, timeout, cpu = 1, mem = 0.5, port = 80) => { return makeCancelable( @@ -83,7 +85,7 @@ export const getOSUrl = (osCode, timeout, cpu = 1, mem = 0.5, port = 80) => { }; /** - * 移除容器 + * Remove container */ export const removeContainerById = ( containerId,