mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00
feat: support i18n
Signed-off-by: RobotHuang <1183598761@qq.com>
This commit is contained in:
parent
a09a8b2af0
commit
e301121c21
@ -3,7 +3,7 @@ httpport = 8000
|
||||
runmode = dev
|
||||
SessionOn = true
|
||||
copyrequestbody = true
|
||||
dataSourceName = root:123@tcp(localhost:3306)/
|
||||
dataSourceName = root:123456@tcp(localhost:3306)/
|
||||
dbName = casdoor
|
||||
AuthState = "casdoor"
|
||||
UseProxy = false
|
@ -8,16 +8,18 @@
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"antd": "^4.7.2",
|
||||
"i18next": "^19.8.9",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^16.14.0",
|
||||
"react-device-detect": "^1.14.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-github-corner": "^2.5.0",
|
||||
"react-i18next": "^11.8.7",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.4.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "set PORT=7001 && react-scripts start",
|
||||
"start": "PORT=7001 && export PORT=7001 && react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
@ -35,6 +35,8 @@ import Face from "./auth/Face";
|
||||
import LoginPage from "./auth/LoginPage";
|
||||
import * as AuthBackend from "./auth/AuthBackend";
|
||||
import AuthCallback from "./auth/AuthCallback";
|
||||
import SelectLanguageBox from './SelectLanguageBox';
|
||||
import i18next from 'i18next';
|
||||
|
||||
const { Header, Footer } = Layout;
|
||||
|
||||
@ -56,6 +58,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
Setting.setLanguage();
|
||||
this.updateMenuKey();
|
||||
this.getAccount();
|
||||
}
|
||||
@ -127,11 +130,11 @@ class App extends Component {
|
||||
<Menu onClick={this.handleRightDropdownClick.bind(this)}>
|
||||
<Menu.Item key="201">
|
||||
<SettingOutlined />
|
||||
My Account
|
||||
{i18next.t("account:My Account")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="202">
|
||||
<LogoutOutlined />
|
||||
Logout
|
||||
{i18next.t("account:Logout")}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
@ -162,14 +165,14 @@ class App extends Component {
|
||||
res.push(
|
||||
<Menu.Item key="100" style={{float: 'right', marginRight: '20px'}}>
|
||||
<Link to="/register">
|
||||
Register
|
||||
{i18next.t("account:Register")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="101" style={{float: 'right'}}>
|
||||
<Link to="/login">
|
||||
Login
|
||||
{i18next.t("account:Login")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
@ -190,7 +193,7 @@ class App extends Component {
|
||||
res.push(
|
||||
<Menu.Item key="0">
|
||||
<Link to="/">
|
||||
Home
|
||||
{i18next.t("general:Home")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
@ -199,33 +202,32 @@ class App extends Component {
|
||||
res.push(
|
||||
<Menu.Item key="1">
|
||||
<Link to="/organizations">
|
||||
Organizations
|
||||
{i18next.t("general:Organizations")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="2">
|
||||
<Link to="/users">
|
||||
Users
|
||||
{i18next.t("general:Users")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="3">
|
||||
<Link to="/providers">
|
||||
Providers
|
||||
{i18next.t("general:Providers")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="4">
|
||||
<Link to="/applications">
|
||||
Applications
|
||||
{i18next.t("general:Applications")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -305,6 +307,7 @@ class App extends Component {
|
||||
textAlign: 'center',
|
||||
}
|
||||
}>
|
||||
<SelectLanguageBox/>
|
||||
Made with <span style={{color: 'rgb(255, 255, 255)'}}>❤️</span> by <a style={{fontWeight: "bold", color: "black"}} target="_blank" href="https://casbin.org">Casbin</a>
|
||||
</Footer>
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ import * as Setting from "./Setting";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import Face from "./auth/Face";
|
||||
import i18next from "i18next";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -89,13 +90,13 @@ class ApplicationEditPage extends React.Component {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
Edit Application
|
||||
<Button type="primary" onClick={this.submitApplicationEdit.bind(this)}>Save</Button>
|
||||
{i18next.t("application:Edit Application")}
|
||||
<Button type="primary" onClick={this.submitApplicationEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Name:
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.application.name} onChange={e => {
|
||||
@ -105,7 +106,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Display Name:
|
||||
{i18next.t("general:Display Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.application.displayName} onChange={e => {
|
||||
@ -130,7 +131,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={1}>
|
||||
Preview:
|
||||
{i18next.t("general:Preview")}:
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<a target="_blank" href={this.state.application.logo}>
|
||||
@ -142,7 +143,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Organization:
|
||||
{i18next.t("general:Organization")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: '100%'}} value={this.state.application.organization} onChange={(value => {this.updateApplicationField('organization', value);})}>
|
||||
@ -154,7 +155,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Enable Password:
|
||||
{i18next.t("application:Enable Password")}:
|
||||
</Col>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.application.enablePassword} onChange={checked => {
|
||||
@ -164,7 +165,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Providers:
|
||||
{i18next.t("general:Providers")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select mode="tags" style={{width: '100%'}}
|
||||
@ -180,7 +181,7 @@ class ApplicationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Face Preview:
|
||||
{i18next.t("application:Face Preview")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<a style={{marginBottom: '10px'}} target="_blank" href={`/doors/${this.state.application.name}`}>
|
||||
@ -237,7 +238,7 @@ class ApplicationEditPage extends React.Component {
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" size="large" onClick={this.submitApplicationEdit.bind(this)}>Save</Button>
|
||||
<Button type="primary" size="large" onClick={this.submitApplicationEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
@ -18,6 +18,7 @@ import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class ApplicationListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -85,7 +86,7 @@ class ApplicationListPage extends React.Component {
|
||||
renderTable(applications) {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '150px',
|
||||
@ -99,7 +100,7 @@ class ApplicationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Created Time',
|
||||
title: i18next.t("general:Created Time"),
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
width: '160px',
|
||||
@ -109,7 +110,7 @@ class ApplicationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Display Name',
|
||||
title: i18next.t("general:Display Name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
// width: '100px',
|
||||
@ -129,7 +130,7 @@ class ApplicationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Organization',
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: 'organization',
|
||||
key: 'organization',
|
||||
width: '200px',
|
||||
@ -143,7 +144,7 @@ class ApplicationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Providers',
|
||||
title: i18next.t("general:Providers"),
|
||||
dataIndex: 'providers',
|
||||
key: 'providers',
|
||||
width: '200px',
|
||||
@ -157,19 +158,19 @@ class ApplicationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: '',
|
||||
key: 'op',
|
||||
width: '170px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/applications/${record.name}`)}>Edit</Button>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/applications/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete application: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteApplication(index)}
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
@ -182,8 +183,8 @@ class ApplicationListPage extends React.Component {
|
||||
<Table columns={columns} dataSource={applications} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
Applications
|
||||
<Button type="primary" size="small" onClick={this.addApplication.bind(this)}>Add</Button>
|
||||
{i18next.t("general:Applications")}
|
||||
<Button type="primary" size="small" onClick={this.addApplication.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={applications === null}
|
||||
|
@ -16,6 +16,7 @@ import React from "react";
|
||||
import {Button, Card, Col, Input, Row} from 'antd';
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
class OrganizationEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -61,13 +62,13 @@ class OrganizationEditPage extends React.Component {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
Edit Organization
|
||||
<Button type="primary" onClick={this.submitOrganizationEdit.bind(this)}>Save</Button>
|
||||
{i18next.t("organization:Edit Organization")}
|
||||
<Button type="primary" onClick={this.submitOrganizationEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Name:
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.organization.name} onChange={e => {
|
||||
@ -77,7 +78,7 @@ class OrganizationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Display Name:
|
||||
{i18next.t("general:Display Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.organization.displayName} onChange={e => {
|
||||
@ -87,7 +88,7 @@ class OrganizationEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Website URL:
|
||||
{i18next.t("organization:Website URL")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.organization.websiteUrl} onChange={e => {
|
||||
@ -137,7 +138,7 @@ class OrganizationEditPage extends React.Component {
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" size="large" onClick={this.submitOrganizationEdit.bind(this)}>Save</Button>
|
||||
<Button type="primary" size="large" onClick={this.submitOrganizationEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
@ -18,6 +18,7 @@ import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class OrganizationListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -83,7 +84,7 @@ class OrganizationListPage extends React.Component {
|
||||
renderTable(organizations) {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '120px',
|
||||
@ -97,7 +98,7 @@ class OrganizationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Created Time',
|
||||
title: i18next.t("general:Created Time"),
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
width: '160px',
|
||||
@ -107,14 +108,14 @@ class OrganizationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Display Name',
|
||||
title: i18next.t("general:Display Name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
// width: '100px',
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: 'Website URL',
|
||||
title: i18next.t("organization:Website URL"),
|
||||
dataIndex: 'websiteUrl',
|
||||
key: 'websiteUrl',
|
||||
width: '300px',
|
||||
@ -128,19 +129,19 @@ class OrganizationListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: '',
|
||||
key: 'op',
|
||||
width: '170px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}`)}>Edit</Button>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete organization: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteOrganization(index)}
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
@ -153,8 +154,8 @@ class OrganizationListPage extends React.Component {
|
||||
<Table columns={columns} dataSource={organizations} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
Organizations
|
||||
<Button type="primary" size="small" onClick={this.addOrganization.bind(this)}>Add</Button>
|
||||
{i18next.t("general:Organizations")}
|
||||
<Button type="primary" size="small" onClick={this.addOrganization.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={organizations === null}
|
||||
|
@ -16,6 +16,7 @@ import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select} from 'antd';
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -63,13 +64,13 @@ class ProviderEditPage extends React.Component {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
Edit Provider
|
||||
<Button type="primary" onClick={this.submitProviderEdit.bind(this)}>Save</Button>
|
||||
{i18next.t("provider:Edit Provider")}
|
||||
<Button type="primary" onClick={this.submitProviderEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Name:
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.name} onChange={e => {
|
||||
@ -79,7 +80,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Display Name:
|
||||
{i18next.t("general:Display Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.displayName} onChange={e => {
|
||||
@ -89,7 +90,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Type:
|
||||
{i18next.t("provider:Type")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: '100%'}} value={this.state.provider.type} onChange={(value => {this.updateProviderField('type', value);})}>
|
||||
@ -106,7 +107,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Client ID:
|
||||
{i18next.t("provider:Client ID")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.clientId} onChange={e => {
|
||||
@ -116,7 +117,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Client Secret:
|
||||
{i18next.t("provider:Client Secret")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.clientSecret} onChange={e => {
|
||||
@ -126,7 +127,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Provider URL:
|
||||
{i18next.t("provider:Provider URL")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.providerUrl} onChange={e => {
|
||||
@ -176,7 +177,7 @@ class ProviderEditPage extends React.Component {
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" size="large" onClick={this.submitProviderEdit.bind(this)}>Save</Button>
|
||||
<Button type="primary" size="large" onClick={this.submitProviderEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
@ -19,6 +19,7 @@ import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as Provider from "./auth/Provider";
|
||||
import i18next from "i18next";
|
||||
|
||||
class ProviderListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -87,7 +88,7 @@ class ProviderListPage extends React.Component {
|
||||
renderTable(providers) {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '120px',
|
||||
@ -101,7 +102,7 @@ class ProviderListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Created Time',
|
||||
title: i18next.t("general:Created Time"),
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
width: '160px',
|
||||
@ -111,14 +112,14 @@ class ProviderListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Display Name',
|
||||
title: i18next.t("general:Display Name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
// width: '100px',
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: 'Type',
|
||||
title: i18next.t("provider:Type"),
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: '80px',
|
||||
@ -130,7 +131,7 @@ class ProviderListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Client Id',
|
||||
title: i18next.t("provider:Client ID"),
|
||||
dataIndex: 'clientId',
|
||||
key: 'clientId',
|
||||
width: '150px',
|
||||
@ -144,7 +145,7 @@ class ProviderListPage extends React.Component {
|
||||
// sorter: (a, b) => a.clientSecret.localeCompare(b.clientSecret),
|
||||
// },
|
||||
{
|
||||
title: 'Provider Url',
|
||||
title: i18next.t("provider:Provider URL"),
|
||||
dataIndex: 'providerUrl',
|
||||
key: 'providerUrl',
|
||||
width: '150px',
|
||||
@ -160,19 +161,19 @@ class ProviderListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: '',
|
||||
key: 'op',
|
||||
width: '170px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/providers/${record.name}`)}>Edit</Button>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/providers/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete provider: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteProvider(index)}
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
@ -185,8 +186,8 @@ class ProviderListPage extends React.Component {
|
||||
<Table columns={columns} dataSource={providers} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
Providers
|
||||
<Button type="primary" size="small" onClick={this.addProvider.bind(this)}>Add</Button>
|
||||
{i18next.t("general:Providers")}
|
||||
<Button type="primary" size="small" onClick={this.addProvider.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={providers === null}
|
||||
|
38
web/src/SelectLanguageBox.js
Normal file
38
web/src/SelectLanguageBox.js
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class SelectLanguageBox extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div align="center">
|
||||
<div class="box" style={{width: "600px"}}>
|
||||
<a href="javascript:void(0)" onClick={() => Setting.changeLanguage("en")} class="lang-selector">English</a>/
|
||||
<a href="javascript:void(0)" onClick={() => Setting.changeLanguage("zh")} class="lang-selector">简体中文</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SelectLanguageBox;
|
@ -15,6 +15,8 @@
|
||||
import {message} from "antd";
|
||||
import React from "react";
|
||||
import {isMobile as isMobileDevice} from "react-device-detect";
|
||||
import "./i18n";
|
||||
import i18next from "i18next";
|
||||
|
||||
export let ServerUrl = "";
|
||||
|
||||
@ -135,3 +137,17 @@ export function getAvatarColor(s) {
|
||||
}
|
||||
return colorList[random % 4];
|
||||
}
|
||||
|
||||
export function setLanguage() {
|
||||
let language = localStorage.getItem('language');
|
||||
if (language === undefined) {
|
||||
language = "en"
|
||||
}
|
||||
i18next.changeLanguage(language)
|
||||
}
|
||||
|
||||
export function changeLanguage(language) {
|
||||
localStorage.setItem("language", language)
|
||||
i18next.changeLanguage(language)
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import * as UserBackend from "./backend/UserBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import {LinkOutlined} from "@ant-design/icons";
|
||||
import i18next from "i18next";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@ -77,13 +78,13 @@ class UserEditPage extends React.Component {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
Edit User
|
||||
<Button type="primary" onClick={this.submitUserEdit.bind(this)}>Save</Button>
|
||||
{i18next.t("user:Edit User")}
|
||||
<Button type="primary" onClick={this.submitUserEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Organization:
|
||||
{i18next.t("general:Organization")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: '100%'}} value={this.state.user.owner} onChange={(value => {this.updateUserField('owner', value);})}>
|
||||
@ -103,7 +104,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Name:
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.name} onChange={e => {
|
||||
@ -113,7 +114,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Display Name:
|
||||
{i18next.t("general:Display Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.displayName} onChange={e => {
|
||||
@ -123,7 +124,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Avatar:
|
||||
{i18next.t("general:Avatar")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
@ -138,7 +139,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={1}>
|
||||
Preview:
|
||||
{i18next.t("general:Preview")}:
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<a target="_blank" href={this.state.user.avatar}>
|
||||
@ -150,7 +151,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Password Type:
|
||||
{i18next.t("general:Password Type")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.passwordType} onChange={e => {
|
||||
@ -160,7 +161,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Password:
|
||||
{i18next.t("general:Password")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.password} onChange={e => {
|
||||
@ -170,7 +171,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Email:
|
||||
{i18next.t("general:Email")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.email} onChange={e => {
|
||||
@ -180,7 +181,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Phone:
|
||||
{i18next.t("general:Phone")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.phone} onChange={e => {
|
||||
@ -190,7 +191,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Affiliation:
|
||||
{i18next.t("user:Affiliation")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.affiliation} onChange={e => {
|
||||
@ -200,7 +201,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Tag:
|
||||
{i18next.t("user:Tag")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.user.tag} onChange={e => {
|
||||
@ -221,7 +222,7 @@ class UserEditPage extends React.Component {
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Is Admin:
|
||||
{i18next.t("user:Is Admin")}:
|
||||
</Col>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.user.isAdmin} onChange={checked => {
|
||||
@ -231,7 +232,7 @@ class UserEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={2}>
|
||||
Is Global Admin:
|
||||
{i18next.t("user:Is Global Admin")}:
|
||||
</Col>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.user.isGlobalAdmin} onChange={checked => {
|
||||
@ -289,7 +290,7 @@ class UserEditPage extends React.Component {
|
||||
<Col span={2}>
|
||||
</Col>
|
||||
<Col span={18}>
|
||||
<Button type="primary" size="large" onClick={this.submitUserEdit.bind(this)}>Save</Button>
|
||||
<Button type="primary" size="large" onClick={this.submitUserEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
@ -18,6 +18,7 @@ import {Button, Col, Popconfirm, Row, Switch, Table} from 'antd';
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class UserListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -90,7 +91,7 @@ class UserListPage extends React.Component {
|
||||
renderTable(users) {
|
||||
const columns = [
|
||||
{
|
||||
title: 'Organization',
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: 'owner',
|
||||
key: 'owner',
|
||||
width: '120px',
|
||||
@ -104,7 +105,7 @@ class UserListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '100px',
|
||||
@ -118,7 +119,7 @@ class UserListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Created Time',
|
||||
title: i18next.t("general:Created Time"),
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
width: '160px',
|
||||
@ -142,14 +143,14 @@ class UserListPage extends React.Component {
|
||||
// sorter: (a, b) => a.password.localeCompare(b.password),
|
||||
// },
|
||||
{
|
||||
title: 'Display Name',
|
||||
title: i18next.t("general:Display Name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
// width: '100px',
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: 'Avatar',
|
||||
title: i18next.t("general:Avatar"),
|
||||
dataIndex: 'avatar',
|
||||
key: 'avatar',
|
||||
width: '100px',
|
||||
@ -162,7 +163,7 @@ class UserListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
title: i18next.t("general:Email"),
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
width: '160px',
|
||||
@ -183,21 +184,21 @@ class UserListPage extends React.Component {
|
||||
// sorter: (a, b) => a.phone.localeCompare(b.phone),
|
||||
// },
|
||||
{
|
||||
title: 'Affiliation',
|
||||
title: i18next.t("user:Affiliation"),
|
||||
dataIndex: 'affiliation',
|
||||
key: 'affiliation',
|
||||
width: '120px',
|
||||
sorter: (a, b) => a.affiliation.localeCompare(b.affiliation),
|
||||
},
|
||||
{
|
||||
title: 'Tag',
|
||||
title: i18next.t("user:Tag"),
|
||||
dataIndex: 'tag',
|
||||
key: 'tag',
|
||||
width: '100px',
|
||||
sorter: (a, b) => a.tag.localeCompare(b.tag),
|
||||
},
|
||||
{
|
||||
title: 'Is Admin',
|
||||
title: i18next.t("user:Is Admin"),
|
||||
dataIndex: 'isAdmin',
|
||||
key: 'isAdmin',
|
||||
width: '120px',
|
||||
@ -209,7 +210,7 @@ class UserListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Is Global Admin',
|
||||
title: i18next.t("user:Is Global Admin"),
|
||||
dataIndex: 'isGlobalAdmin',
|
||||
key: 'isGlobalAdmin',
|
||||
width: '120px',
|
||||
@ -221,19 +222,19 @@ class UserListPage extends React.Component {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: '',
|
||||
key: 'op',
|
||||
width: '170px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/users/${record.owner}/${record.name}`)}>Edit</Button>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/users/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete user: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteUser(index)}
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
@ -246,8 +247,8 @@ class UserListPage extends React.Component {
|
||||
<Table columns={columns} dataSource={users} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
||||
title={() => (
|
||||
<div>
|
||||
Users
|
||||
<Button type="primary" size="small" onClick={this.addUser.bind(this)}>Add</Button>
|
||||
{i18next.t("general:Users")}
|
||||
<Button type="primary" size="small" onClick={this.addUser.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={users === null}
|
||||
|
@ -16,6 +16,7 @@ import React from "react";
|
||||
import {Card, Col, Row} from "antd";
|
||||
import * as Setting from "../Setting";
|
||||
import SingleCard from "./SingleCard";
|
||||
import i18next from "i18next";
|
||||
|
||||
class HomePage extends React.Component {
|
||||
constructor(props) {
|
||||
@ -29,10 +30,10 @@ class HomePage extends React.Component {
|
||||
let items = [];
|
||||
if (Setting.isAdminUser(this.props.account)) {
|
||||
items = [
|
||||
{link: "/organizations", name: "Organizations", organizer: "User containers"},
|
||||
{link: "/users", name: "Users", organizer: "Users under all organizations"},
|
||||
{link: "/providers", name: "Providers", organizer: "OAuth providers"},
|
||||
{link: "/applications", name: "Applications", organizer: "Applications that requires authentication"},
|
||||
{link: "/organizations", name: i18next.t("general:Organizations"), organizer: i18next.t("general:User containers")},
|
||||
{link: "/users", name: i18next.t("general:Users"), organizer: i18next.t("general:Users under all organizations")},
|
||||
{link: "/providers", name: i18next.t("general:Providers"), organizer: i18next.t("general:OAuth providers")},
|
||||
{link: "/applications", name: i18next.t("general:Applications"), organizer: i18next.t("general:Applications that requires authentication")},
|
||||
];
|
||||
}
|
||||
|
||||
|
39
web/src/i18n.js
Normal file
39
web/src/i18n.js
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import i18n from 'i18next'
|
||||
import zh from './locales/zh.json'
|
||||
import en from './locales/en.json'
|
||||
|
||||
const resources = {
|
||||
en: en,
|
||||
zh: zh
|
||||
};
|
||||
|
||||
i18n
|
||||
.init({
|
||||
lng: "en",
|
||||
|
||||
resources: resources,
|
||||
|
||||
keySeparator: false,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false
|
||||
},
|
||||
|
||||
saveMissing: true,
|
||||
})
|
||||
|
||||
export default i18n;
|
64
web/src/locales/en.json
Normal file
64
web/src/locales/en.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"general":
|
||||
{
|
||||
"Home": "Home",
|
||||
"Organizations": "Organizations",
|
||||
"Providers": "Providers",
|
||||
"Users": "Users",
|
||||
"Applications": "Applications",
|
||||
"Save": "Save",
|
||||
"Add": "Add",
|
||||
"Action": "Action",
|
||||
"Edit": "Edit",
|
||||
"Delete": "Delete",
|
||||
"Organization": "Organization",
|
||||
"Created Time": "Created Time",
|
||||
"Name": "Name",
|
||||
"Display Name": "Display Name",
|
||||
"Avatar": "Avatar",
|
||||
"Preview": "Preview",
|
||||
"Password Type": "Password Type",
|
||||
"Password": "Password",
|
||||
"Email": "Email",
|
||||
"Phone": "Phone",
|
||||
"Logo": "Logo",
|
||||
"User containers": "User containers",
|
||||
"Users under all organizations": "Users under all organizations",
|
||||
"OAuth providers": "OAuth providers",
|
||||
"Applications that requires authentication": "Applications that requires authentication"
|
||||
},
|
||||
"account":
|
||||
{
|
||||
"My Account": "My Account",
|
||||
"Login": "Login",
|
||||
"Logout": "Logout",
|
||||
"Register": "Register"
|
||||
},
|
||||
"organization":
|
||||
{
|
||||
"Edit Organization": "Edit Organization",
|
||||
"Website URL": "Website URL"
|
||||
},
|
||||
"provider":
|
||||
{
|
||||
"Type": "Type",
|
||||
"Client ID": "Client ID",
|
||||
"Client Secret": "Client Secret",
|
||||
"Provider URL": "Provider URL",
|
||||
"Edit Provider": "Edit Provider"
|
||||
},
|
||||
"user":
|
||||
{
|
||||
"Edit User": "Edit User",
|
||||
"Affiliation": "Affiliation",
|
||||
"Tag": "Tag",
|
||||
"Is Admin": "Is Admin",
|
||||
"Is Global Admin": "Is Global Admin"
|
||||
},
|
||||
"application":
|
||||
{
|
||||
"Edit Application": "Edit Application",
|
||||
"Enable Password": "Enable Password",
|
||||
"Face Preview": "Face Preview"
|
||||
}
|
||||
}
|
66
web/src/locales/zh.json
Normal file
66
web/src/locales/zh.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"general":
|
||||
{
|
||||
"Home": "主页",
|
||||
"Organizations": "组织",
|
||||
"Providers": "第三方",
|
||||
"Users": "用户",
|
||||
"Applications": "应用",
|
||||
"Save": "保存",
|
||||
"Add": "添加",
|
||||
"Action": "行为",
|
||||
"Edit": "修改",
|
||||
"Delete": "删除",
|
||||
"Organization": "组织",
|
||||
"Created Time": "创建时间",
|
||||
"Name": "名字",
|
||||
"Display Name": "展示的名字",
|
||||
"Avatar": "头像",
|
||||
"Preview": "预览",
|
||||
"Password Type": "密码类型",
|
||||
"Password": "密码",
|
||||
"Email": "电子邮件",
|
||||
"Phone": "手机",
|
||||
"Logo": "Logo",
|
||||
"User containers": "用户容器",
|
||||
"Users under all organizations": "所有组织里的用户",
|
||||
"OAuth providers": "OAuth提供方",
|
||||
"Applications that requires authentication": "需要鉴权的应用"
|
||||
},
|
||||
"account":
|
||||
{
|
||||
"My Account": "我的账户",
|
||||
"Login": "登录",
|
||||
"Logout": "登出",
|
||||
"Register": "注册"
|
||||
},
|
||||
"organization":
|
||||
{
|
||||
"Edit Organization": "修改组织",
|
||||
"Website URL": "网页地址"
|
||||
},
|
||||
"provider":
|
||||
{
|
||||
"Name": "名字",
|
||||
"Display Name": "展示的名字",
|
||||
"Type": "类型",
|
||||
"Client ID": "Client ID",
|
||||
"Client Secret": "Client Secret",
|
||||
"Provider URL": "第三方URL",
|
||||
"Edit Provider": "修改第三方"
|
||||
},
|
||||
"user":
|
||||
{
|
||||
"Edit User": "修改用户",
|
||||
"Affiliation": "联盟",
|
||||
"Tag": "标签",
|
||||
"Is Admin": "是管理员",
|
||||
"Is Global Admin": "是全局管理员"
|
||||
},
|
||||
"application":
|
||||
{
|
||||
"Edit Application": "修改应用",
|
||||
"Enable Password": "开启密码",
|
||||
"Face Preview": "图标预览"
|
||||
}
|
||||
}
|
@ -1156,6 +1156,13 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.3.1":
|
||||
version "7.12.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d"
|
||||
integrity sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.10.4", "@babel/template@^7.4.0", "@babel/template@^7.8.6":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.npm.taobao.org/@babel/template/download/@babel/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
|
||||
@ -5360,6 +5367,13 @@ html-minifier-terser@^5.0.1:
|
||||
relateurl "^0.2.7"
|
||||
terser "^4.6.3"
|
||||
|
||||
html-parse-stringify2@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a"
|
||||
integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=
|
||||
dependencies:
|
||||
void-elements "^2.0.1"
|
||||
|
||||
html-webpack-plugin@4.0.0-beta.11:
|
||||
version "4.0.0-beta.11"
|
||||
resolved "https://registry.npm.taobao.org/html-webpack-plugin/download/html-webpack-plugin-4.0.0-beta.11.tgz?cache=0&sync_timestamp=1602970416532&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715"
|
||||
@ -5459,6 +5473,13 @@ https-browserify@^1.0.0:
|
||||
resolved "https://registry.npm.taobao.org/https-browserify/download/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||
|
||||
i18next@^19.8.9:
|
||||
version "19.8.9"
|
||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.8.9.tgz#ee3175cf1edcd142b45eca49d23079edaa1e6fcc"
|
||||
integrity sha512-cyLeJv7tg3MP+bRNbNe9CleiBGfkCeEKr0DKY2MBPYE1vIQGu4mQ3qlTCmW1tcZi9fMqSI4G2mGBO+JwoIQSbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
|
||||
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz?cache=0&sync_timestamp=1594184325364&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficonv-lite%2Fdownload%2Ficonv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
@ -9290,6 +9311,14 @@ react-github-corner@^2.5.0:
|
||||
resolved "https://registry.npm.taobao.org/react-github-corner/download/react-github-corner-2.5.0.tgz#e350d0c69f69c075bc0f1d2a6f1df6ee91da31f2"
|
||||
integrity sha1-41DQxp9pwHW8Dx0qbx327pHaMfI=
|
||||
|
||||
react-i18next@^11.8.7:
|
||||
version "11.8.7"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.8.7.tgz#845f31e01e2aa92a3954c374ebcc4bd54df1cace"
|
||||
integrity sha512-IDK/a73cJN4J2C6yTqmDS/r8iikwKaN283hltqps8UiRaFVTvlSxE85HkamEFD6lULG4hFZExecXJ/hA27DW3Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
html-parse-stringify2 "2.0.1"
|
||||
|
||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npm.taobao.org/react-is/download/react-is-16.13.1.tgz?cache=0&sync_timestamp=1602081887213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-is%2Fdownload%2Freact-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
@ -11117,6 +11146,11 @@ vm-browserify@^1.0.1:
|
||||
resolved "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
|
||||
integrity sha1-eGQcSIuObKkadfUR56OzKobl3aA=
|
||||
|
||||
void-elements@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
|
||||
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
|
||||
|
||||
w3c-hr-time@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npm.taobao.org/w3c-hr-time/download/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
|
||||
|
Loading…
x
Reference in New Issue
Block a user