support multiple languages using i18n

This commit is contained in:
zema1 2018-05-30 21:46:08 +08:00
commit 52fd9fbb88
38 changed files with 683 additions and 212 deletions

View File

@ -35,7 +35,7 @@ npm run dev
## Screenshots
[Check here.](https://github.com/QingdaoU/OnlineJudge/tree/2.0)
[Check here.](https://github.com/QingdaoU/OnlineJudge)
## Browser Support

5
package-lock.json generated
View File

@ -16733,6 +16733,11 @@
"integrity": "sha1-aDvR0CbA07PJN9WHVnnpqH7GzY8=",
"dev": true
},
"vue-i18n": {
"version": "7.7.0",
"resolved": "http://registry.npm.taobao.org/vue-i18n/download/vue-i18n-7.7.0.tgz",
"integrity": "sha1-8xdYHg68Q8EoZiUBz6vW7NXn/Os="
},
"vue-katex": {
"version": "0.1.2",
"resolved": "http://registry.npm.taobao.org/vue-katex/download/vue-katex-0.1.2.tgz",

View File

@ -34,6 +34,7 @@
"vue-codemirror-lite": "^1.0.4",
"vue-cropper": "^0.2.9",
"vue-echarts": "^2.6.0",
"vue-i18n": "^7.7.0",
"vue-katex": "^0.1.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",

104
src/i18n/admin/en-US.js Normal file
View File

@ -0,0 +1,104 @@
export const m = {
// SideMenu.vue
Dashboard: 'Dashboard',
General: 'General',
User: 'User',
Announcement: 'Announcement',
System_Config: 'System Config',
Judge_Server: 'Judge Server',
Prune_Test_Case: 'Prune Test Case',
Problem: 'Problem',
Problem_List: 'Problem List',
Create_Problem: 'Create Problem',
Export_Import_Problem: 'Export Or Import Problem',
Contest: 'Contest',
Contest_List: 'Contest List',
Create_Contest: 'Create Contest',
// User.vue
User_User: 'User',
Import_User: 'Import User',
Generate_User: 'Generate User',
// User.vue-dialog
User_Info: 'User',
User_Username: 'Username',
User_Real_Name: 'Real Name',
User_Email: 'Email',
User_New_Password: 'New Password',
User_Type: 'User Type',
Problem_Permission: 'Problem Permission',
Two_Factor_Auth: 'Two Factor Auth',
Is_Disabled: 'Is Disabled',
// Announcement.vue
General_Announcement: 'Announcement',
Announcement_Title: 'Title',
Announcement_Content: 'Conten',
Announcement_Status: 'Status',
// Conf.vue
SMTP_Config: 'SMTP Config',
Server: 'Server',
Port: 'Port',
Email: 'Email',
Password: 'Password',
Website_Config: 'Web Config',
Base_Url: 'Base Url',
Name: 'Name',
Shortcut: 'Shortcut',
Footer: 'Footer',
Allow_Register: 'Allow Register',
Submission_List_Show_All: 'Submission List Show All',
// JudgeServer.vue
Judge_Server_Token: 'Judge Server Token',
Judge_Server_Info: 'Judge Server',
IP: 'IP',
Judger_Version: 'Judger Version',
Service_URL: 'Service URL',
Last_Heartbeat: 'Last Heartbeat',
Create_Time: 'Create Time',
// PruneTestCase
Test_Case_Prune_Test_Case: 'Prune Test Case',
// Problem.vue
Display_ID: 'Display ID',
Title: 'Title',
Description: 'Description',
Input_Description: 'Input Description',
Output_Description: 'Output Description',
Time_Limit: 'Time Limit',
Memory_limit: 'Memory limit',
Difficulty: 'Difficulty',
Visible: 'Visible',
Languages: 'Languages',
Input_Samples: 'Input Samples',
Output_Samples: 'Output Samples',
Add_Sample: 'Add Sample',
Code_Template: 'Code_Template',
Special_Judge: 'Special Judge',
Use_Special_Judge: 'Use Special Judge',
Special_Judge_Code: 'Special Judge Code',
SPJ_language: 'SPJ language',
Compile: 'Compile',
TestCase: 'TestCase',
Type: 'Type',
Input: 'Input',
Output: 'Output',
Score: 'Score',
Hint: 'Hint',
Source: 'Source',
// Contest.vue
ContestTitle: 'Title',
ContestDescription: 'Description',
Contest_Start_Time: 'Start Time',
Contest_End_Time: 'End Time',
Contest_Password: 'Password',
Contest_Rule_Type: 'Contest Rule Type',
Real_Time_Rank: 'Real Time Rank',
Contest_Status: 'Status',
Allowed_IP_Ranges: 'Allowed IP Ranges',
CIDR_Network: 'CIDR Network',
// Dashboard.vue
Last_Login: 'Last Login',
System_Overview: 'System Overview',
DashBoardJudge_Server: 'Judge Server',
HTTPS_Status: 'HTTPS Status',
Force_HTTPS: 'Force HTTPS',
CDN_HOST: 'CDN HOST'
}

104
src/i18n/admin/zh-CN.js Normal file
View File

@ -0,0 +1,104 @@
export const m = {
// SideMenu.vue
Dashboard: '仪表盘',
General: '常用设置',
User: '用户管理',
Announcement: '公告管理',
System_Config: '系统配置',
Judge_Server: '判题服务器',
Prune_Test_Case: '测试用例',
Problem: '问题',
Problem_List: '问题列表',
Create_Problem: '增加题目',
Export_Import_Problem: '导入导出题目',
Contest: '测试&练习',
Contest_List: '测试列表',
Create_Contest: '创建测试',
// User.vue
User_User: '用户',
Import_User: '导入用户',
Generate_User: '生成用户',
// User.vue-dialog
User_Info: '用户信息',
User_Username: '用户名',
User_Real_Name: '真实姓名',
User_Email: '用户邮箱',
User_New_Password: '用户密码',
User_Type: '用户类型',
Problem_Permission: '问题权限',
Two_Factor_Auth: '双因素认证',
Is_Disabled: '是否可用',
// Announcement.vue
General_Announcement: '公告',
Announcement_Title: '标题',
Announcement_Content: '内容',
Announcement_Status: '状态',
// Conf.vue
SMTP_Config: 'SMTP Config',
Server: '服务器',
Port: '端口',
Email: '邮箱',
Password: '授权码',
Website_Config: 'Web Config',
Base_Url: 'Base Url',
Name: '名称',
Shortcut: '简称',
Footer: '页脚',
Allow_Register: '是否允许注册',
Submission_List_Show_All: '显示全部题目的提交',
// JudgeServer.vue
Judge_Server_Token: '判题服务器接口',
Judge_Server_Info: '判题服务器',
IP: 'IP',
Judger_Version: '判题机版本',
Service_URL: '服务器URL',
Last_Heartbeat: '上一次心跳',
Create_Time: '创建时间',
// PruneTestCase
Test_Case_Prune_Test_Case: '精简测试用例',
// Problem.vue
Display_ID: 'Display ID',
Title: '题目',
Description: '描述',
Input_Description: '输入描述',
Output_Description: '输出描述',
Time_Limit: '时间限制',
Memory_limit: '内存限制',
Difficulty: '难度',
Visible: '是否可见',
Languages: '可选编程语言',
Input_Samples: '输入样例',
Output_Samples: '输出样例',
Add_Sample: '添加样例',
Code_Template: '代码模板',
Special_Judge: 'Special Judge',
Use_Special_Judge: '使用Special Judge',
Special_Judge_Code: 'Special Judge Code',
SPJ_language: 'SPJ language',
Compile: '编译',
TestCase: '测试用例',
Type: '测试类型',
Input: '输入',
Output: '输出',
Score: '分数',
Hint: '提示',
Source: '来源',
// Contest.vue
ContestTitle: '标题',
ContestDescription: '描述',
Contest_Start_Time: '开始时间',
Contest_End_Time: '结束时间',
Contest_Password: '密码',
Contest_Rule_Type: '规则',
Real_Time_Rank: '实时Rank',
Contest_Status: '状态',
Allowed_IP_Ranges: '允许的IP范围',
CIDR_Network: 'CIDR Network',
// Dashboard.vue
Last_Login: '最后登录状态',
System_Overview: '系统状况',
DashBoardJudge_Server: '判题服务器',
HTTPS_Status: 'HTTPS 状态',
Force_HTTPS: '强制 HTTPS',
CDN_HOST: 'CDN HOST'
}

105
src/i18n/oj/en-US.js Normal file
View File

@ -0,0 +1,105 @@
export const m = {
// Problem.vue
Description: 'Description',
Input: 'Input',
Output: 'Output',
Sample_Input: 'Sample Input',
Sample_Output: 'Sample Output',
Hint: 'Hint',
Source: 'Source',
Status: 'Status',
Information: 'Information',
Time_Limit: 'Time Limit',
Memory_Limit: 'Memory Limit',
Created: 'Created By',
Level: 'Level',
Score: 'Score',
Tags: 'Tags',
Show: 'Show',
// About.vue
Result_Explanation: 'Result Explanation',
Pending_Juding: 'You solution will be judged soon, please wait for result.',
Compile_Error: "Failed to compile your source code. Click on the link to see compiler's output.",
Accepted: 'Congratulations. Your solution is correct.',
Wrong_Answer: "Your program's output doesn't match judger's answer.",
Runtime_Error: 'Your program terminated abnormally. Possible reasons are: segment fault, divided by zero or exited with code other than 0.',
Time_Limit_Exceeded: 'The CPU time your program used has exceeded limit. Java has a triple time limit.',
Memory_Limit_Exceeded: 'The memory your program actually used has exceeded limit.',
System_Error: 'Oops, something has gone wrong with the judger. Please report this to administrator.',
// FAQ.vue
Frequently_Asked_Questions: 'Frequently Asked Questions',
// ContestDetail.vue
Problems: 'Problems',
Announcements: 'Announcements',
Submissions: 'Submissions',
Rankings: 'Rankings',
Overview: 'Overview',
Admin_Helper: 'Admin Helper',
// ContestProblemList
Problems_List: 'Problems List',
// NavBar.vue
Home: 'Home',
NavProblems: 'Problems',
Contests: 'Contests',
NavStatus: 'Status',
Rank: 'Rank',
ACM_Rank: 'ACM Rank',
OI_Rank: 'OI Rank',
About: 'About',
Judger: 'Judger',
FAQ: 'FAQ',
Login: 'Login',
Register: 'Register',
MyHome: 'Home',
MySubmissions: 'Submissions',
Settings: 'Settings',
Management: 'Management',
Logout: 'Logout',
// announcements.vue
Refresh: 'Refresh',
Back: 'Back',
No_Announcements: 'No Announcements',
// Setting.vue
Profile: 'Profile',
Account: 'Account',
Security: 'Security',
// AccoutSetting.vue
ChangePassword: 'Change Password',
ChangeEmail: 'Change Email',
Update_Password: 'Update Password',
// ProfileSetting.vue
Avatar_Setting: 'Avatar Setting',
Profile_Setting: 'Profile Setting',
// SecuritySettig
Sessions: 'Sessions',
Two_Factor_Authentication: 'Two Factor Authentication',
// Login.vue
LoginUsername: 'Username',
LoginPassword: 'Password',
TFA_Code: 'Code from your TFA app',
No_Account: 'No account? Register now!',
Forget_Password: 'Forget Password',
UserLogin: 'Login',
// Register.vue
RegisterUsername: 'Username',
Email_Address: 'Email Address',
RegisterPassword: 'Password',
Password_Again: 'Password Again',
Captcha: 'Captcha',
UserRegister: 'Register',
Already_Registed: 'Already registed? Login now!',
// ResetPassword.vue and ApplyResetPassword.vue
Reset_Password: 'Lost Password',
RPassword: 'Password',
RPassword_Again: 'Password Again',
RCaptcha: 'Captcha',
ApplyEmail: 'Your Email Address',
Send_Password_Reset_Email: 'Send Password Reset Email',
// UserHome.vue
UserHomeSolved: 'Solved',
UserHomeserSubmissions: 'Submissions',
UserHomeScore: 'Score',
List_Solved_Problems: 'List of solved problems',
UserHomeIntro: 'The guy is so lazy that has not solved any problem yet.'
}

105
src/i18n/oj/zh-CN.js Normal file
View File

@ -0,0 +1,105 @@
export const m = {
// Problem.vue
Description: '题目描述',
Input: '输入',
Output: '输出',
Sample_Input: '输入样例',
Sample_Output: '输出样例',
Hint: '提示',
Source: '题目来源',
Status: '状态',
Information: '题目信息',
Time_Limit: '时间限制',
Memory_Limit: '内存限制',
Created: '出题人',
Level: '难度',
Score: '分数',
Tags: '标签',
Show: '显示',
// About.vue
Result_Explanation: '结果解释',
Pending_Juding: '您的解答将很快被测评,请等待结果。',
Compile_Error: '无法编译您的源代码,点击链接查看编译器的输出。',
Accepted: '你的解题方法是正确的。',
Wrong_Answer: '你的程序输出结果与判题程序的答案不符。',
Runtime_Error: '您的程序异常终止可能的原因是段错误被零除或用非0的代码退出程序。',
Time_Limit_Exceeded: '您的程序使用的CPU时间已超出限制Java有三倍的时间限制。',
Memory_Limit_Exceeded: '程序实际使用的内存已超出限制。',
System_Error: '糟糕,判题程序出了问题。请报告给管理员。',
// FAQ.vue
Frequently_Asked_Questions: '常见问题',
// ContestDetail.vue
Problems: '题目',
Announcements: '公告',
Submissions: '提交信息',
Rankings: 'Rank排名',
Overview: '概要',
Admin_Helper: '管理员助手',
// ContestProblemList.vue
Problems_List: '问题列表',
// NavBar.vue
Home: '首页',
NavProblems: '问题',
Contests: '练习&比赛',
NavStatus: '状态',
Rank: '排名',
ACM_Rank: 'ACM 排名',
OI_Rank: 'OI 排名',
About: '关于',
Judger: '判题解释',
FAQ: '常见问题',
Login: '登录',
Register: '注册',
MyHome: '我的主页',
MySubmissions: '我的提交',
Settings: '我的设置',
Management: '后台管理',
Logout: '退出',
// announcements.vue
Refresh: '刷新',
Back: '返回',
No_Announcements: '暂无公告',
// Setting.vue
Profile: '个人信息设置',
Account: '账号设置',
Security: '安全设置',
// AccoutSetting.vue
ChangePassword: '更改密码',
ChangeEmail: '更改邮箱',
Update_Password: '更新密码',
// ProfileSetting.vue
Avatar_Setting: '头像设置',
Profile_Setting: '个人信息设置',
// SecuritySettig
Sessions: '登录记录',
Two_Factor_Authentication: '双因素认证',
// Login.vue
LoginUsername: '用户名',
LoginPassword: '密码',
TFA_Code: '双因素认证号码',
No_Account: '还没账号,立即注册!',
Forget_Password: '忘记密码',
UserLogin: '登录',
// Register.vue
RegisterUsername: '用户名',
Email_Address: '电子邮箱',
RegisterPassword: '密码',
Password_Again: '确认密码',
Captcha: '验证码',
UserRegister: '注册',
Already_Registed: '已经注册?现在登录!',
// ResetPassword.vue and ApplyResetPassword.vue
Reset_Password: '重置密码',
RPassword: '密码',
RPassword_Again: '确认密码',
RCaptcha: '验证码',
ApplyEmail: '电子邮箱',
Send_Password_Reset_Email: '发送重置密码到邮箱',
// UserHome.vue
UserHomeSolved: '已解决数量',
UserHomeserSubmissions: '提交次数',
UserHomeScore: '分数',
List_Solved_Problems: '已解决问题的列表',
UserHomeIntro: '这个家伙太懒了,还没有做题呢...'
}

View File

@ -4,24 +4,24 @@
<div class="logo">
<img src="../../../assets/logo.svg" alt="oj admin"/>
</div>
<el-menu-item index="/"><i class="el-icon-fa-dashboard"></i>Dashboard</el-menu-item>
<el-menu-item index="/"><i class="el-icon-fa-dashboard"></i>{{$t('m.Dashboard')}}</el-menu-item>
<el-submenu v-if="isSuperAdmin" index="general">
<template slot="title"><i class="el-icon-menu"></i>General</template>
<el-menu-item index="/user">User</el-menu-item>
<el-menu-item index="/announcement">Announcement</el-menu-item>
<el-menu-item index="/conf">System Config</el-menu-item>
<el-menu-item index="/judge-server">Judge Server</el-menu-item>
<el-menu-item index="/prune-test-case">Prune Test Case</el-menu-item>
<template slot="title"><i class="el-icon-menu"></i>{{$t('m.General')}}</template>
<el-menu-item index="/user">{{$t('m.User')}}</el-menu-item>
<el-menu-item index="/announcement">{{$t('m.Announcement')}}</el-menu-item>
<el-menu-item index="/conf">{{$t('m.System_Config')}}</el-menu-item>
<el-menu-item index="/judge-server">{{$t('m.Judge_Server')}}</el-menu-item>
<el-menu-item index="/prune-test-case">{{$t('m.Prune_Test_Case')}}</el-menu-item>
</el-submenu>
<el-submenu index="problem" v-if="hasProblemPermission">
<template slot="title"><i class="el-icon-fa-bars"></i>Problem</template>
<el-menu-item index="/problems">Problem List</el-menu-item>
<el-menu-item index="/problem/create">Create Problem</el-menu-item>
<template slot="title"><i class="el-icon-fa-bars"></i>{{$t('m.Problem')}}</template>
<el-menu-item index="/problems">{{$t('m.Problem_List')}}</el-menu-item>
<el-menu-item index="/problem/create">{{$t('m.Create_Problem')}}</el-menu-item>
</el-submenu>
<el-submenu index="contest">
<template slot="title"><i class="el-icon-fa-trophy"></i>Contest</template>
<el-menu-item index="/contest">Contest List</el-menu-item>
<el-menu-item index="/contest/create">Create Contest</el-menu-item>
<template slot="title"><i class="el-icon-fa-trophy"></i>{{$t('m.Contest')}}</template>
<el-menu-item index="/contest">{{$t('m.Contest_List')}}</el-menu-item>
<el-menu-item index="/contest/create">{{$t('m.Create_Contest')}}</el-menu-item>
</el-submenu>
</el-menu>
</template>
@ -48,7 +48,7 @@
<style scoped lang="less">
.vertical_menu {
overflow: auto;
width: 200px;
width: 205px;
height: 100%;
position: fixed !important;
z-index: 100;

View File

@ -17,6 +17,8 @@ import Save from './components/btn/Save.vue'
import Cancel from './components/btn/Cancel.vue'
import './style.less'
import VueI18n from 'vue-i18n'
// register global utility filters.
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
@ -32,6 +34,21 @@ Vue.component(Panel.name, Panel)
Vue.component(Save.name, Save)
Vue.component(Cancel.name, Cancel)
Vue.use(VueI18n)
// load language packages
const i18n = new VueI18n({
locale: 'zh-CN',
messages: {
'en-US': require('../../i18n/admin/en-US'),
'zh-CN': require('../../i18n/admin/zh-CN')
}
})
Vue.use(Element, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.prototype.$error = (msg) => {
Vue.prototype.$message({'message': msg, 'type': 'error'})
}
@ -48,4 +65,4 @@ Vue.prototype.$success = (msg) => {
}
}
new Vue(Vue.util.extend({router, store}, App)).$mount('#app')
new Vue(Vue.util.extend({router, store, i18n}, App)).$mount('#app')

View File

@ -17,6 +17,7 @@
<router-view></router-view>
</transition>
<div class="footer">
<el-button type="text" class="btn btn-success" @click="changeLocale()">简体中文 / English</el-button > |
Build Version: {{ version }}
</div>
</div>
@ -60,6 +61,14 @@
this.$router.push({name: 'login'})
})
}
},
changeLocale () {
// locale
if (this.$i18n.locale === 'zh-CN') {
this.$i18n.locale = 'en-US'
} else {
this.$i18n.locale = 'zh-CN'
}
}
},
computed: {

View File

@ -4,46 +4,46 @@
<el-form label-position="top">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="Title" required>
<el-input v-model="contest.title" placeholder="Tittle"></el-input>
<el-form-item :label="$t('m.ContestTitle')" required>
<el-input v-model="contest.title" :placeholder="$t('m.ContestTitle')"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="Description" required>
<el-form-item :label="$t('m.ContestDescription')" required>
<Simditor v-model="contest.description"></Simditor>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Start Time" required>
<el-form-item :label="$t('m.Contest_Start_Time')" required>
<el-date-picker
v-model="contest.start_time"
type="datetime"
placeholder="Start Time">
:placeholder="$t('m.Contest_Start_Time')">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="End Time" required>
<el-form-item :label="$t('m.Contest_End_Time')" required>
<el-date-picker
v-model="contest.end_time"
type="datetime"
placeholder="End Time">
:placeholder="$t('m.Contest_End_Time')">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Password">
<el-input v-model="contest.password" placeholder="Contest Password"></el-input>
<el-form-item :label="$t('m.Contest_Password')">
<el-input v-model="contest.password" :placeholder="$t('m.Contest_Password')"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Contest Rule Type">
<el-form-item :label="$t('m.Contest_Rule_Type')">
<el-radio class="radio" v-model="contest.rule_type" label="ACM" :disabled="disableRuleType">ACM</el-radio>
<el-radio class="radio" v-model="contest.rule_type" label="OI" :disabled="disableRuleType">OI</el-radio>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Real Time Rank">
<el-form-item :label="$t('m.Real_Time_Rank')">
<el-switch
v-model="contest.real_time_rank"
active-color="#13ce66"
@ -52,7 +52,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Status">
<el-form-item :label="$t('m.Contest_Status')">
<el-switch
v-model="contest.visible"
active-text=""
@ -61,11 +61,11 @@
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="Allowed IP Ranges">
<el-form-item :label="$t('m.Allowed_IP_Ranges')">
<div v-for="(range, index) in contest.allowed_ip_ranges" :key="index">
<el-row :gutter="20" style="margin-bottom: 15px">
<el-col :span="8">
<el-input v-model="range.value" placeholder="CIDR Network"></el-input>
<el-input v-model="range.value" :placeholder="$t('m.CIDR_Network')"></el-input>
</el-col>
<el-col :span="10">
<el-button plain icon="el-icon-fa-plus" @click="addIPRange"></el-button>

View File

@ -1,6 +1,6 @@
<template>
<div class="announcement view">
<Panel title="Announcement">
<Panel :title="$t('m.General_Announcement')">
<div class="list">
<el-table
v-loading="loading"
@ -74,17 +74,17 @@
<el-dialog :title="announcementDialogTitle" :visible.sync="showEditAnnouncementDialog"
@open="onOpenEditDialog" :close-on-click-modal="false">
<el-form label-position="top">
<el-form-item label="Title" required>
<el-form-item :label="$t('m.Announcement_Title')" required>
<el-input
v-model="announcement.title"
placeholder="Title" class="title-input">
:placeholder="$t('m.Announcement_Title')" class="title-input">
</el-input>
</el-form-item>
<el-form-item label="Content" required>
<el-form-item :label="$t('m.Announcement_Content')" required>
<Simditor v-model="announcement.content"></Simditor>
</el-form-item>
<div class="visible-box">
<span>Status</span>
<span>{{$t('m.Announcement_Status')}}</span>
<el-switch
v-model="announcement.visible"
active-text=""

View File

@ -1,25 +1,25 @@
<template>
<div class="view">
<Panel title="SMTP Config">
<Panel :title="$t('m.SMTP_Config')">
<el-form label-position="left" label-width="70px" :model="smtp">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="Server" required>
<el-form-item :label="$t('m.Server')" required>
<el-input v-model="smtp.server" placeholder="SMTP Server Address"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Port" required>
<el-form-item :label="$t('m.Port')" required>
<el-input type="number" v-model="smtp.port" placeholder="SMTP Server Port"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Email" required>
<el-form-item :label="$t('m.Email')" required>
<el-input type="email" v-model="smtp.email" placeholder="Account Used To Send Email"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Password" label-width="90px" required>
<el-form-item :label="$t('m.Password')" label-width="90px" required>
<el-input v-model="smtp.password" type="password" placeholder="SMTP Server Password"></el-input>
</el-form-item>
</el-col>
@ -39,33 +39,33 @@
v-if="saved" :loading="loadingBtnTest">Send Test Email</el-button>
</Panel>
<Panel title="Website Config">
<Panel :title="$t('m.Website_Config')">
<el-form label-position="left" label-width="100px" ref="form" :model="websiteConfig">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="Base Url" required>
<el-form-item :label="$t('m.Base_Url')" required>
<el-input v-model="websiteConfig.website_base_url" placeholder="Website Base Url"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Name" required>
<el-form-item :label="$t('m.Name')" required>
<el-input v-model="websiteConfig.website_name" placeholder="Website Name"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Shortcut" required>
<el-form-item :label="$t('m.Shortcut')" required>
<el-input v-model="websiteConfig.website_name_shortcut" placeholder="Website Name Shortcut"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="Footer" required>
<el-form-item :label="$t('m.Footer')" required>
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" v-model="websiteConfig.website_footer"
placeholder="Website Footer HTML"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-col :span="12">
<el-form-item label="Allow Register" label-width="200px">
<el-form-item :label="$t('m.Allow_Register')" label-width="200px">
<el-switch
v-model="websiteConfig.allow_register"
active-color="#13ce66"
@ -74,7 +74,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Submission List Show All" label-width="200px">
<el-form-item :label="$t('m.Submission_List_Show_All')" label-width="200px">
<el-switch
v-model="websiteConfig.submission_list_show_all"
active-color="#13ce66"

View File

@ -13,7 +13,7 @@
</el-row>
<hr/>
<div class="last-info">
<p class="last-info-title">Last Login</p>
<p class="last-info-title">{{$t('m.Last_Login')}}</p>
<el-form label-width="80px" class="last-info-body">
<el-form-item label="Time:">
<span>{{session.last_activity | localtime}}</span>
@ -30,19 +30,19 @@
</el-form>
</div>
</el-card>
<panel title="System Overview" v-if="isSuperAdmin">
<p>Judge Server: {{infoData.judge_server_count}}</p>
<p>HTTPS Status:
<panel :title="$t('m.System_Overview')" v-if="isSuperAdmin">
<p>{{$t('m.DashBoardJudge_Server')}}: {{infoData.judge_server_count}}</p>
<p>{{$t('m.HTTPS_Status')}}:
<el-tag :type="https ? 'success' : 'danger'" size="small">
{{ https ? 'Enabled' : 'Disabled'}}
</el-tag>
</p>
<p>Force HTTPS:
<p>{{$t('m.Force_HTTPS')}}:
<el-tag :type="forceHttps ? 'success' : 'danger'" size="small">
{{forceHttps ? 'Enabled' : 'Disabled'}}
</el-tag>
</p>
<p>CDN HOST:
<p>{{$t('m.CDN_HOST')}}:
<el-tag :type="cdn ? 'success' : 'warning'" size="small">
{{cdn ? cdn : 'Not Use'}}
</el-tag>
@ -70,7 +70,7 @@
</el-popover>
</span>
<el-collapse v-model="activeNames" v-for="release, index in releases" :key="'release' + index">
<el-collapse v-model="activeNames" v-for="(release, index) of releases" :key="'release' + index">
<el-collapse-item :name="index+1">
<template slot="title">
<div v-if="release.new_version">{{release.title}}
@ -81,7 +81,7 @@
<p>Level: {{release.level}}</p>
<p>Details: </p>
<div class="release-body">
<ul v-for="detail in release.details">
<ul v-for="detail in release.details" :key="detail">
<li v-html="detail"></li>
</ul>
</div>

View File

@ -1,9 +1,9 @@
<template>
<div class="view">
<Panel title="Judge Server Token">
<Panel :title="$t('m.Judge_Server_Token')">
<code>{{ token }}</code>
</Panel>
<Panel title="Judge Server">
<Panel :title="$t('m.Judge_Server_Info')">
<el-table
:data="servers"
:default-expand-all="true"
@ -11,14 +11,14 @@
<el-table-column
type="expand">
<template slot-scope="props">
<p>IP:
<p>{{$t('m.IP')}}:
<el-tag type="success">{{ props.row.ip }}</el-tag>&nbsp;&nbsp;
Judger Version:
{{$t('m.Judger_Version')}}:
<el-tag type="success">{{ props.row.judger_version }}</el-tag>
</p>
<p>Service URL: <code>{{ props.row.service_url }}</code></p>
<p>Last Heartbeat: {{ props.row.last_heartbeat | localtime}}</p>
<p>Create Time: {{ props.row.create_time | localtime }}</p>
<p>{{$t('m.Service_URL')}}: <code>{{ props.row.service_url }}</code></p>
<p>{{$t('m.Last_Heartbeat')}}: {{ props.row.last_heartbeat | localtime}}</p>
<p>{{$t('m.Create_Time')}}: {{ props.row.create_time | localtime }}</p>
</template>
</el-table-column>
<el-table-column

View File

@ -1,7 +1,7 @@
<template>
<div>
<panel>
<span slot="title">Prune Test Case
<span slot="title">{{$t('m.Test_Case_Prune_Test_Case')}}
<el-popover placement="right" trigger="hover">
These test cases are not owned by any problem, you can clean them safely.
<i slot="reference" class="el-icon-fa-question-circle import-user-icon"></i>

View File

@ -1,6 +1,6 @@
<template>
<div class="view">
<Panel title="User ">
<Panel :title="$t('m.User_User') ">
<div slot="header">
<el-row :gutter="20">
<el-col :span="8">
@ -68,7 +68,7 @@
</Panel>
<Panel>
<span slot="title">Import User
<span slot="title">{{$t('m.Import_User')}}
<el-popover placement="right" trigger="hover">
<p>Only support csv file without headers, check the <a
href="http://docs.onlinejudge.me/#/onlinejudge/guide/import_users">link</a> for details</p>
@ -120,7 +120,7 @@
</template>
</Panel>
<Panel title="Generate User">
<Panel :title="$t('m.Generate_User')">
<el-form :model="formGenerateUser" ref="formGenerateUser">
<el-row type="flex" justify="space-between">
<el-col :span="4">
@ -152,7 +152,7 @@
</el-row>
<el-form-item>
<el-button type="primary" @click="generateUser" icon="el-icon-fa-users" :loading="loadingGenerate">Generate
<el-button type="primary" @click="generateUser" icon="el-icon-fa-users" :loading="loadingGenerate">Generate & Export
</el-button>
<span class="userPreview" v-if="formGenerateUser.number_from && formGenerateUser.number_to &&
formGenerateUser.number_from <= formGenerateUser.number_to">
@ -168,31 +168,31 @@
</el-form>
</Panel>
<!--对话框-->
<el-dialog title="User" :visible.sync="showUserDialog" :close-on-click-modal="false">
<el-dialog :title="$t('m.User_Info')" :visible.sync="showUserDialog" :close-on-click-modal="false">
<el-form :model="user" label-width="120px" label-position="left">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="Username" required>
<el-form-item :label="$t('m.User_Username')" required>
<el-input v-model="user.username"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Real Name" required>
<el-form-item :label="$t('m.User_Real_Name')" required>
<el-input v-model="user.real_name"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Email" required>
<el-form-item :label="$t('m.User_Email')" required>
<el-input v-model="user.email"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="New Password">
<el-form-item :label="$t('m.User_New_Password')">
<el-input v-model="user.password"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="User Type">
<el-form-item :label="$t('m.User_Type')">
<el-select v-model="user.admin_type">
<el-option label="Regular User" value="Regular User"></el-option>
<el-option label="Admin" value="Admin"></el-option>
@ -201,7 +201,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Problem Permission">
<el-form-item :label="$t('m.Problem_Permission')">
<el-select v-model="user.problem_permission" :disabled="user.admin_type!=='Admin'">
<el-option label="None" value="None"></el-option>
<el-option label="Own" value="Own"></el-option>
@ -210,7 +210,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Two Factor Auth">
<el-form-item :label="$t('m.Two_Factor_Auth')">
<el-switch
v-model="user.two_factor_auth"
:disabled="!user.real_tfa"
@ -229,7 +229,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Is Disabled">
<el-form-item :label="$t('m.Is_Disabled')">
<el-switch
v-model="user.is_disabled"
active-text=""

View File

@ -5,50 +5,50 @@
<el-form ref="form" :model="problem" :rules="rules" label-position="top" label-width="70px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item prop="_id" label="Display ID"
<el-form-item prop="_id" :label="$t('m.Display_ID')"
:required="this.routeName === 'create-contest-problem' || this.routeName === 'edit-contet-problem'">
<el-input placeholder="Display ID" v-model="problem._id"></el-input>
<el-input :placeholder="$t('m.Display_ID')" v-model="problem._id"></el-input>
</el-form-item>
</el-col>
<el-col :span="18">
<el-form-item prop="title" label="Title" required>
<el-input placeholder="Title" v-model="problem.title"></el-input>
<el-form-item prop="title" :label="$t('m.Title')" required>
<el-input :placeholder="$t('m.Title')" v-model="problem.title"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item prop="description" label="Description" required>
<el-form-item prop="description" :label="$t('m.Description')" required>
<Simditor v-model="problem.description"></Simditor>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item prop="input_description" label="Input Description" required>
<el-form-item prop="input_description" :label="$t('m.Input_Description')" required>
<Simditor v-model="problem.input_description"></Simditor>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item prop="output_description" label="Output Description" required>
<el-form-item prop="output_description" :label="$t('m.Output_Description')" required>
<Simditor v-model="problem.output_description"></Simditor>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="Time Limit" required>
<el-input type="Number" placeholder="Time Limit" v-model="problem.time_limit"></el-input>
<el-form-item :label="$t('m.Time_Limit')" required>
<el-input type="Number" :placeholder="$t('m.Time_Limit')" v-model="problem.time_limit"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Memory limit" required>
<el-input type="Number" placeholder="Memory Limit" v-model="problem.memory_limit"></el-input>
<el-form-item :label="$t('m.Memory_limit')" required>
<el-input type="Number" :placeholder="$t('m.Memory_limit')" v-model="problem.memory_limit"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Difficulty">
<el-select class="difficulty-select" size="small" placeholder="Difficulty" v-model="problem.difficulty">
<el-form-item :label="$t('m.Difficulty')">
<el-select class="difficulty-select" size="small" :placeholder="$t('m.Difficulty')" v-model="problem.difficulty">
<el-option label="Low" value="Low"></el-option>
<el-option label="Mid" value="Mid"></el-option>
<el-option label="High" value="High"></el-option>
@ -58,7 +58,7 @@
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="Visible">
<el-form-item :label="$t('m.Visible')">
<el-switch
v-model="problem.visible"
active-text=""
@ -92,7 +92,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="Languages" :error="error.languages" required>
<el-form-item :label="$t('m.Languages')" :error="error.languages" required>
<el-checkbox-group v-model="problem.languages">
<el-tooltip class="spj-radio" v-for="lang in allLanguage.languages" :key="'spj'+lang.name" effect="dark"
:content="lang.description" placement="top-start">
@ -110,21 +110,21 @@
</el-button>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="Input Samples" required>
<el-form-item :label="$t('m.Input_Samples')" required>
<el-input
:rows="5"
type="textarea"
placeholder="Input Samples"
:placeholder="$t('m.Input_Samples')"
v-model="sample.input">
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Output Samples" required>
<el-form-item :label="$t('m.Output_Samples')" required>
<el-input
:rows="5"
type="textarea"
placeholder="Output Samples"
:placeholder="$t('m.Output_Samples')"
v-model="sample.output">
</el-input>
</el-form-item>
@ -134,10 +134,10 @@
</el-form-item>
</div>
<div class="add-sample-btn">
<button type="button" class="add-samples" @click="addSample()"><i class="el-icon-plus"></i>Add Sample
<button type="button" class="add-samples" @click="addSample()"><i class="el-icon-plus"></i>{{$t('m.Add_Sample')}}
</button>
</div>
<el-form-item label="Code Template">
<el-form-item :label="$t('m.Code_Template')">
<el-row>
<el-col :span="24" v-for="(v, k) in template" :key="'template'+k">
<el-form-item>
@ -149,15 +149,15 @@
</el-col>
</el-row>
</el-form-item>
<el-form-item label="Special Judge" :error="error.spj">
<el-form-item :label="$t('m.Special_Judge')" :error="error.spj">
<el-col :span="24">
<el-checkbox v-model="problem.spj" @click.native.prevent="switchSpj()">Use Special Judge</el-checkbox>
<el-checkbox v-model="problem.spj" @click.native.prevent="switchSpj()">{{$t('m.Use_Special_Judge')}}</el-checkbox>
</el-col>
</el-form-item>
<el-form-item v-if="problem.spj">
<Accordion title="Special Judge Code">
<Accordion :title="$t('m.Special_Judge_Code')">
<template slot="header">
<span>SPJ language</span>
<span>{{$t('m.SPJ_language')}}</span>
<el-radio-group v-model="problem.spj_language">
<el-tooltip class="spj-radio" v-for="lang in allLanguage.spj_languages" :key="lang.name" effect="dark"
:content="lang.description" placement="top-start">
@ -166,7 +166,7 @@
</el-radio-group>
<el-button type="primary" size="small" icon="el-icon-fa-random" @click="compileSPJ"
:loading="loadingCompile">
Compile
{{$t('m.Compile')}}
</el-button>
</template>
<code-mirror v-model="problem.spj_code" :mode="spjMode"></code-mirror>
@ -174,7 +174,7 @@
</el-form-item>
<el-row :gutter="20">
<el-col :span="4">
<el-form-item label="TestCase" :error="error.testcase">
<el-form-item :label="$t('m.TestCase')" :error="error.testcase">
<el-upload
action="/api/admin/test_case"
name="file"
@ -187,7 +187,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="Type">
<el-form-item :label="$t('m.Type')">
<el-radio-group v-model="problem.rule_type" :disabled="disableRuleType">
<el-radio label="ACM">ACM</el-radio>
<el-radio label="OI">OI</el-radio>
@ -200,19 +200,19 @@
style="width: 100%">
<el-table-column
prop="input_name"
label="Input">
:label="$t('m.Input')">
</el-table-column>
<el-table-column
prop="output_name"
label="Output">
:label="$t('m.Output')">
</el-table-column>
<el-table-column
prop="score"
label="Score">
:label="$t('m.Score')">
<template slot-scope="scope">
<el-input
size="small"
placeholder="Score"
:placeholder="$t('m.Score')"
v-model="scope.row.score"
:disabled="problem.rule_type !== 'OI'">
</el-input>
@ -221,11 +221,11 @@
</el-table>
</el-col>
</el-row>
<el-form-item style="margin-top: 20px" label="Hint">
<el-form-item style="margin-top: 20px" :label="$t('m.Hint')">
<Simditor v-model="problem.hint" placeholder=""></Simditor>
</el-form-item>
<el-form-item label="Source">
<el-input placeholder="Source" v-model="problem.source"></el-input>
<el-form-item :label="$t('m.Source')">
<el-input :placeholder="$t('m.Source')" v-model="problem.source"></el-input>
</el-form-item>
<save @click.native="submit()">Save</save>
</el-form>

View File

@ -6,7 +6,8 @@
<router-view></router-view>
</transition>
<div class="footer">
<p v-html="website.website_footer"></p>
<Button type="text" class="btn btn-success" @click="changeLocale()">简体中文 / English</Button>
<p v-html="website.website_footer"></p>
<p>Powered by <a href="https://github.com/QingdaoU/OnlineJudge">OnlineJudge</a>
<span v-if="version">&nbsp; Version: {{ version }}</span>
</p>
@ -40,7 +41,15 @@
this.getWebsiteConfig()
},
methods: {
...mapActions(['getWebsiteConfig', 'changeDomTitle'])
...mapActions(['getWebsiteConfig', 'changeDomTitle']),
changeLocale () {
// locale
if (this.$i18n.locale === 'zh-CN') {
this.$i18n.locale = 'en-US'
} else {
this.$i18n.locale = 'zh-CN'
}
}
},
computed: {
...mapState(['website'])

View File

@ -4,42 +4,42 @@
<div class="logo"><span>{{website.website_name}}</span></div>
<Menu-item name="/">
<Icon type="home"></Icon>
Home
{{$t('m.Home')}}
</Menu-item>
<Menu-item name="/problems">
<Icon type="ios-keypad"></Icon>
Problems
{{$t('m.NavProblems')}}
</Menu-item>
<Menu-item name="/contests">
<Icon type="trophy"></Icon>
Contests
{{$t('m.Contests')}}
</Menu-item>
<Menu-item name="/status">
<Icon type="ios-pulse-strong"></Icon>
Status
{{$t('m.NavStatus')}}
</Menu-item>
<Submenu name="">
<template slot="title">
<Icon type="podium"></Icon>
Rank
{{$t('m.Rank')}}
</template>
<Menu-item name="/acm-rank">
ACM Rank
{{$t('m.ACM_Rank')}}
</Menu-item>
<Menu-item name="/oi-rank">
OI Rank
{{$t('m.OI_Rank')}}
</Menu-item>
</Submenu>
<Submenu name="">
<template slot="title">
<Icon type="information-circled"></Icon>
About
{{$t('m.About')}}
</template>
<Menu-item name="/about">
Judger
{{$t('m.Judger')}}
</Menu-item>
<Menu-item name="/FAQ">
FAQ
{{$t('m.FAQ')}}
</Menu-item>
</Submenu>
<template v-if="!isAuthenticated">
@ -47,13 +47,13 @@
<Button type="ghost"
ref="loginBtn"
shape="circle"
@click="handleBtnClick('login')">Login
@click="handleBtnClick('login')">{{$t('m.Login')}}
</Button>
<Button v-if="website.allow_register"
type="ghost"
shape="circle"
@click="handleBtnClick('register')"
style="margin-left: 5px;">Register
style="margin-left: 5px;">{{$t('m.Register')}}
</Button>
</div>
</template>
@ -63,11 +63,11 @@
<Icon type="arrow-down-b"></Icon>
</Button>
<Dropdown-menu slot="list">
<Dropdown-item name="/user-home">Home</Dropdown-item>
<Dropdown-item name="/status?myself=1">Submissions</Dropdown-item>
<Dropdown-item name="/setting/profile">Settings</Dropdown-item>
<Dropdown-item v-if="isAdminRole" name="/admin">Management</Dropdown-item>
<Dropdown-item divided name="/logout">Logout</Dropdown-item>
<Dropdown-item name="/user-home">{{$t('m.MyHome')}}</Dropdown-item>
<Dropdown-item name="/status?myself=1">{{$t('m.MySubmissions')}}</Dropdown-item>
<Dropdown-item name="/setting/profile">{{$t('m.Settings')}}</Dropdown-item>
<Dropdown-item v-if="isAdminRole" name="/admin">{{$t('m.Management')}}</Dropdown-item>
<Dropdown-item divided name="/logout">{{$t('m.Logout')}}</Dropdown-item>
</Dropdown-menu>
</Dropdown>
</template>
@ -151,10 +151,12 @@
}
.drop-menu {
float: right;
margin-right: 30px;
position: absolute;
right: 10px;
&-title {
font-size: 16px;
font-size: 18px;
}
}
.btn-menu {

View File

@ -4,11 +4,11 @@ import App from './App.vue'
import router from './router'
import store from '@/store'
import VueClipboard from 'vue-clipboard2'
import locale from 'iview/src/locale/lang/en-US'
import VueAnalytics from 'vue-analytics'
import { GOOGLE_ANALYTICS_ID } from '@/utils/constants'
import iView from 'iview'
import locale from 'iview/dist/locale/en-US'
import 'iview/dist/styles/iview.css'
import Panel from '@oj/components/Panel.vue'
@ -18,6 +18,7 @@ import '@/styles/index.less'
import highlight from '@/plugins/highlight'
import katex from '@/plugins/katex'
import VueI18n from 'vue-i18n'
import filters from '@/utils/filters.js'
import ECharts from 'vue-echarts/components/ECharts.vue'
@ -39,6 +40,8 @@ Object.keys(filters).forEach(key => {
Vue.config.productionTip = false
Vue.use(iView, {locale})
Vue.use(VueI18n)
Vue.use(VueClipboard)
Vue.use(highlight)
Vue.use(katex)
@ -47,6 +50,15 @@ Vue.use(VueAnalytics, {
router
})
// load language packages
const i18n = new VueI18n({
locale: 'zh-CN',
messages: {
'en-US': require('../../i18n/oj/en-US'),
'zh-CN': require('../../i18n/oj/zh-CN')
}
})
Vue.component('ECharts', ECharts)
Vue.component(VerticalMenu.name, VerticalMenu)
Vue.component(VerticalMenuItem.name, VerticalMenuItem)
@ -60,4 +72,4 @@ Vue.prototype.$error = (s) => Vue.prototype.$Message.error(s)
Vue.prototype.$info = (s) => Vue.prototype.$Message.info(s)
Vue.prototype.$success = (s) => Vue.prototype.$Message.success(s)
new Vue(Vue.util.extend({router, store}, App)).$mount('#app')
new Vue(Vue.util.extend({router, store, i18n}, App)).$mount('#app')

View File

@ -36,41 +36,39 @@
<VerticalMenu @on-click="handleRoute">
<VerticalMenu-item :route="{name: 'contest-details', params: {contestID: contestID}}">
<Icon type="home"></Icon>
Overview
{{$t('m.Overview')}}
</VerticalMenu-item>
<VerticalMenu-item :disabled="contestMenuDisabled"
:route="{name: 'contest-announcement-list', params: {contestID: contestID}}">
<Icon type="chatbubble-working"></Icon>
Announcements
{{$t('m.Announcements')}}
</VerticalMenu-item>
<VerticalMenu-item :disabled="contestMenuDisabled"
:route="{name: 'contest-problem-list', params: {contestID: contestID}}">
<Icon type="ios-photos"></Icon>
Problems
{{$t('m.Problems')}}
</VerticalMenu-item>
<VerticalMenu-item v-if="OIContestRealTimePermission"
:disabled="contestMenuDisabled"
:route="{name: 'contest-submission-list'}">
<Icon type="navicon-round"></Icon>
Submissions
{{$t('m.Submissions')}}
</VerticalMenu-item>
<VerticalMenu-item v-if="OIContestRealTimePermission"
:disabled="contestMenuDisabled"
:route="{name: 'contest-rank', params: {contestID: contestID}}">
<Icon type="stats-bars"></Icon>
Rankings
{{$t('m.Rankings')}}
</VerticalMenu-item>
<VerticalMenu-item v-if="showAdminHelper"
:route="{name: 'acm-helper', params: {contestID: contestID}}">
<Icon type="ios-paw"></Icon>
Admin Helper
{{$t('m.Admin_Helper')}}
</VerticalMenu-item>
</VerticalMenu>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div>
<Panel>
<div slot="title">Problems List</div>
<div slot="title">{{$t('m.Problems_List')}}</div>
<Table v-if="contestRuleType == 'ACM' || OIContestRealTimePermission"
:columns="ACMTableColumns"
:data="problems"

View File

@ -4,13 +4,13 @@
{{title}}
</div>
<div slot="extra">
<Button v-if="listVisible" type="info" @click="init" :loading="btnLoading">Refresh</Button>
<Button v-else type="ghost" icon="ios-undo" @click="goBack">Back</Button>
<Button v-if="listVisible" type="info" @click="init" :loading="btnLoading">{{$t('m.Refresh')}}</Button>
<Button v-else type="ghost" icon="ios-undo" @click="goBack">{{$t('m.Back')}}</Button>
</div>
<transition-group name="announcement-animate" mode="in-out">
<div class="no-announcement" v-if="!announcements.length" key="no-announcement">
<p>No announcements</p>
<p>{{$t('m.No_Announcements')}}</p>
</div>
<template v-if="listVisible">
<ul class="announcements-container" key="list">
@ -163,7 +163,7 @@
.no-announcement {
text-align: center;
font-size: 16px;
}
}changeLocale
.announcement-animate-enter-active {
animation: fadeIn 1s;

View File

@ -6,7 +6,7 @@
<Button type="text" class="contest-title" @click="goContest">{{contests[index].title}}</Button>
</div>
<Carousel v-model="index" trigger="hover" autoplay :autoplay-speed="6000" class="contest">
<CarouselItem v-for="contest, index in contests" :key="index">
<CarouselItem v-for="(contest, index) of contests" :key="index">
<div class="contest-content">
<div class="contest-content-tags">
<Button type="info" shape="circle" size="small" icon="calendar">

View File

@ -15,20 +15,20 @@
<div slot="title">Result Explanation</div>
<div class="content">
<ul>
<li><b>Pending & Juding</b> : You solution will be judged soon, please wait for result</li>
<li><b>Compile Error</b> : Failed to compile your source code. Click on the link to see compiler's output.
<li><b>Pending & Juding</b> : {{$t('m.Pending_Juding')}}</li>
<li><b>Compile Error</b> : {{$t('m.Compile_Error')}}
</li>
<li><b>Accepted</b> : Congratulations. Your solution is correct.</li>
<li><b>Wrong Answer</b> : Your program's output doesn't match judger's answer.</li>
<li><b>Accepted</b> : {{$t('m.Accepted')}}</li>
<li><b>Wrong Answer</b> : {{$t('m.Wrong_Answer')}}</li>
<li>
<b>Runtime Error</b>
: Your program terminated abnormally. Possible reasons are: segment fault, divided by zero or exited with code other than 0.
: {{$t('m.Runtime_Error')}}
</li>
<li><b>Time Limit Exceeded</b>
: The CPU time your program used has exceeded limit. Java has a triple time limit.
: {{$t('m.Time_Limit_Exceeded')}}
</li>
<li><b>Memory Limit Exceeded</b> : The memory your program actually used has exceeded limit.</li>
<li><b>System Error</b> : Oops, something has gone wrong with the judger. Please report this to administrator.
<li><b>Memory Limit Exceeded</b> : {{$t('m.Memory_Limit_Exceeded')}}</li>
<li><b>System Error</b> : {{$t('m.System_Error')}}
</li>
</ul>
</div>

View File

@ -1,6 +1,6 @@
<template>
<panel>
<div slot="title">Frequently Asked Questions</div>
<div slot="title">{{$t('m.Frequently_Asked_Questions')}}</div>
<div class="content markdown-body">
<ul>
<li>Where is the input and the output?

View File

@ -5,19 +5,19 @@
<Panel :padding="40" shadow>
<div slot="title">{{problem.title}}</div>
<div id="problem-content" class="markdown-body" v-katex>
<p class="title">Description</p>
<p class="title">{{$t('m.Description')}}</p>
<p class="content" v-html=problem.description></p>
<p class="title">Input</p>
<!-- {{$t('m.music')}} -->
<p class="title">{{$t('m.Input')}}</p>
<p class="content" v-html=problem.input_description></p>
<p class="title">Output</p>
<p class="title">{{$t('m.Output')}}</p>
<p class="content" v-html=problem.output_description></p>
<div v-for="sample, index in problem.samples">
<div v-for="(sample, index) of problem.samples" :key="index">
<div class="flex-container sample">
<div class="sample-input">
<p class="title">Sample Input {{index + 1}}
<p class="title">{{$t('m.Sample_Input')}} {{index + 1}}
<a class="copy"
v-clipboard:copy="sample.input"
v-clipboard:success="onCopy"
@ -28,21 +28,21 @@
<pre>{{sample.input}}</pre>
</div>
<div class="sample-output">
<p class="title">Sample Output {{index + 1}}</p>
<p class="title">{{$t('m.Sample_Output')}} {{index + 1}}</p>
<pre>{{sample.output}}</pre>
</div>
</div>
</div>
<div v-if="problem.hint">
<p class="title">Hint</p>
<p class="title">{{$t('m.Hint')}}</p>
<Card dis-hover>
<div class="content" v-html=problem.hint></div>
</Card>
</div>
<div v-if="problem.source">
<p class="title">Source</p>
<p class="title">{{$t('m.Source')}}</p>
<p class="content">{{problem.source}}</p>
</div>
@ -56,7 +56,7 @@
<Col :span="10">
<div class="status" v-if="statusVisible">
<template v-if="!this.contestID || (this.contestID && OIContestRealTimePermission)">
<span>Status:</span>
<span>{{$t('m.Status')}}</span>
<Tag type="dot" :color="submissionStatus.color" @click.native="handleRoute('/status/'+submissionId)">
{{submissionStatus.text}}
</Tag>
@ -130,29 +130,29 @@
<Card id="info">
<div slot="title" class="header">
<Icon type="information-circled"></Icon>
<span class="card-title">Information</span>
<span class="card-title">{{$t('m.Information')}}</span>
</div>
<ul>
<li><p>ID</p>
<p>{{problem._id}}</p></li>
<li>
<p>Time Limit</p>
<p>{{$t('m.Time_Limit')}}</p>
<p>{{problem.time_limit}}MS</p></li>
<li>
<p>Memory Limit</p>
<p>{{$t('m.Memory_Limit')}}</p>
<p>{{problem.memory_limit}}MB</p></li>
<li>
<p>Created By</p>
<p>{{$t('m.Created')}}</p>
<p>{{problem.created_by.username}}</p></li>
<li v-if="problem.difficulty">
<p>Level</p>
<p>{{$t('m.Level')}}</p>
<p>{{problem.difficulty}}</p></li>
<li v-if="problem.total_score">
<p>Score</p>
<p>{{$t('m.Score')}}</p>
<p>{{problem.total_score}}</p>
</li>
<li>
<p>Tags</p>
<p>{{$t('m.Tags')}}</p>
<p>
<Poptip trigger="hover" placement="left-end">
<a>Show</a>

View File

@ -18,9 +18,9 @@
</div>
</div>
<Menu-item name="/setting/profile">Profile</Menu-item>
<Menu-item name="/setting/account">Account</Menu-item>
<Menu-item name="/setting/security">Security</Menu-item>
<Menu-item name="/setting/profile">{{$t('m.Profile')}}</Menu-item>
<Menu-item name="/setting/account">{{$t('m.Account')}}</Menu-item>
<Menu-item name="/setting/security">{{$t('m.Security')}}</Menu-item>
</Menu>
</div>
<div class="panel">

View File

@ -2,7 +2,7 @@
<div class="setting-main">
<div class="flex-container">
<div class="left">
<p class="section-title">Change Password</p>
<p class="section-title">{{$t('m.ChangePassword')}}</p>
<Form class="setting-content" ref="formPassword" :model="formPassword" :rules="rulePassword">
<FormItem label="Old Password" prop="old_password">
<Input v-model="formPassword.old_password" type="password"/>
@ -19,14 +19,14 @@
<FormItem v-if="visible.passwordAlert">
<Alert type="success">You will need to login again after 5 seconds..</Alert>
</FormItem>
<Button type="primary" @click="changePassword">Update Password</Button>
<Button type="primary" @click="changePassword">{{$t('m.Update_Password')}}</Button>
</Form>
</div>
<div class="middle separator"></div>
<div class="right">
<p class="section-title">Change Email</p>
<p class="section-title">{{$t('m.ChangeEmail')}}</p>
<Form class="setting-content" ref="formEmail" :model="formEmail" :rules="ruleEmail">
<FormItem label="Current Password" prop="password">
<Input v-model="formEmail.password" type="password"/>
@ -40,7 +40,7 @@
<FormItem v-if="visible.tfaRequired" label="Two Factor Auth" prop="tfa_code">
<Input v-model="formEmail.tfa_code"/>
</FormItem>
<Button type="primary" @click="changeEmail">Change Email</Button>
<Button type="primary" @click="changeEmail">{{$t('m.ChangeEmail')}}</Button>
</Form>
</div>
</div>

View File

@ -1,6 +1,6 @@
<template>
<div class="setting-main">
<div class="section-title">Avatar Setting</div>
<div class="section-title">{{$t('m.Avatar_Setting')}}</div>
<template v-if="!avatarOption.imgSrc">
<Upload type="drag"
class="mini-container"
@ -62,7 +62,7 @@
</div>
</Modal>
<div class="section-title">Profile Setting</div>
<div class="section-title">{{$t('m.Profile_Setting')}}</div>
<Form ref="formProfile" :model="formProfile">
<Row type="flex" :gutter="30" justify="space-around">
<Col :span="11">

View File

@ -1,6 +1,6 @@
<template>
<div class="setting-main">
<p class="section-title">Sessions</p>
<p class="section-title">{{$t('m.Sessions')}}</p>
<div class="flex-container setting-content">
<template v-for="session in sessions">
<Card :padding="20" class="flex-child">
@ -28,7 +28,7 @@
</template>
</div>
<p class="section-title">Two Factor Authentication</p>
<p class="section-title">{{$t('m.Two_Factor_Authentication')}}</p>
<div class="mini-container setting-content">
<Form>
<Alert v-if="TFAOpened"

View File

@ -31,7 +31,7 @@
</li>
<li>
<Button type="info" icon="refresh" @click="getSubmissions">Refresh</Button>
<Button type="info" icon="refresh" @click="getSubmissions">{{$t('m.Refresh')}}</Button>
</li>
</ul>
</div>

View File

@ -1,17 +1,17 @@
<template>
<Panel :padding="30" class="container">
<div slot="title" class="center">Lost Password</div>
<div slot="title" class="center">{{$t('m.Reset_Password')}}</div>
<template v-if="!successApply">
<Form :rules="ruleResetPassword" :model=formResetPassword ref="formResetPassword">
<Form-item prop="email">
<Input v-model="formResetPassword.email" placeholder="Your Email Address" size="large">
<Input v-model="formResetPassword.email" :placeholder="$t('m.ApplyEmail')" size="large">
<Icon type="ios-email-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item prop="captcha" style="margin-bottom:10px">
<div class="oj-captcha">
<div class="oj-captcha-code">
<Input v-model="formResetPassword.captcha" placeholder="Captcha" size="large">
<Input v-model="formResetPassword.captcha" :placeholder="$t('m.RCaptcha')" size="large">
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
</Input>
</div>
@ -26,7 +26,7 @@
<Button type="primary"
@click="sendEmail"
class="btn" long
:loading="btnLoading">Send Password Reset Email
:loading="btnLoading">{{$t('m.Send_Password_Reset_Email')}}
</Button>
</template>
<template v-else>

View File

@ -2,17 +2,17 @@
<div>
<Form ref="formLogin" :model="formLogin" :rules="ruleLogin">
<FormItem prop="username">
<Input type="text" v-model="formLogin.username" placeholder="Username" size="large" @on-enter="handleLogin">
<Input type="text" v-model="formLogin.username" :placeholder="$t('m.LoginUsername')" size="large" @on-enter="handleLogin">
<Icon type="ios-person-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="password">
<Input type="password" v-model="formLogin.password" placeholder="Password" size="large" @on-enter="handleLogin">
<Input type="password" v-model="formLogin.password" :placeholder="$t('m.LoginPassword')" size="large" @on-enter="handleLogin">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="tfa_code" v-if="tfaRequired">
<Input v-model="formLogin.tfa_code" placeholder="Code from your TFA app">
<Input v-model="formLogin.tfa_code" :placeholder="$t('m.TFA_Code')">
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
</Input>
</FormItem>
@ -23,10 +23,10 @@
@click="handleLogin"
class="btn" long
:loading="btnLoginLoading">
Login
{{$t('m.UserLogin')}}
</Button>
<a v-if="website.allow_register" @click.stop="handleBtnClick('register')">No account? Register now!</a>
<a @click.stop="goResetPassword" style="float: right">Forget Password</a>
<a v-if="website.allow_register" @click.stop="handleBtnClick('register')">{{$t('m.No_Account')}}</a>
<a @click.stop="goResetPassword" style="float: right">{{$t('m.Forget_Password')}}</a>
</div>
</div>
</template>

View File

@ -2,29 +2,29 @@
<div>
<Form ref="formRegister" :model="formRegister" :rules="ruleRegister">
<FormItem prop="username">
<Input type="text" v-model="formRegister.username" placeholder="Username" size="large" @on-enter="handleRegister">
<Input type="text" v-model="formRegister.username" :placeholder="$t('m.RegisterUsername')" size="large" @on-enter="handleRegister">
<Icon type="ios-person-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="email">
<Input v-model="formRegister.email" placeholder="Email Address" size="large" @on-enter="handleRegister">
<Input v-model="formRegister.email" :placeholder="$t('m.Email_Address')" size="large" @on-enter="handleRegister">
<Icon type="ios-email-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="password">
<Input type="password" v-model="formRegister.password" placeholder="Password" size="large" @on-enter="handleRegister">
<Input type="password" v-model="formRegister.password" :placeholder="$t('m.RegisterPassword')" size="large" @on-enter="handleRegister">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="passwordAgain">
<Input type="password" v-model="formRegister.passwordAgain" placeholder="Password Again" size="large" @on-enter="handleRegister">
<Input type="password" v-model="formRegister.passwordAgain" :placeholder="$t('m.Password_Again')" size="large" @on-enter="handleRegister">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</FormItem>
<FormItem prop="captcha" style="margin-bottom:10px">
<div class="oj-captcha">
<div class="oj-captcha-code">
<Input v-model="formRegister.captcha" placeholder="Captcha" size="large" @on-enter="handleRegister">
<Input v-model="formRegister.captcha" :placeholder="$t('m.Captcha')" size="large" @on-enter="handleRegister">
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
</Input>
</div>
@ -42,13 +42,13 @@
@click="handleRegister"
class="btn" long
:loading="btnRegisterLoading">
Register
{{$t('m.UserRegister')}}
</Button>
<Button
type="ghost"
@click="switchMode('login')"
class="btn" long>
Already registed? Login now!
{{$t('m.Already_Registed')}}
</Button>
</div>
</div>

View File

@ -1,22 +1,22 @@
<template>
<Panel :padding="30" class="container">
<div slot="title" class="center">Reset Password</div>
<div slot="title" class="center">{{$t('m.Reset_Password')}}</div>
<template v-if="!resetSuccess">
<Form :model=formResetPassword ref="formResetPassword" :rules="ruleResetPassword">
<Form-item prop="password">
<Input type="password" v-model="formResetPassword.password" placeholder="Password" size="large">
<Input type="password" v-model="formResetPassword.password" :placeholder="$t('m.RPassword')" size="large">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item prop="passwordAgain">
<Input type="password" v-model="formResetPassword.passwordAgain" placeholder="Password Again" size="large">
<Input type="password" v-model="formResetPassword.passwordAgain" :placeholder="$t('m.RPassword_Again')" size="large">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item prop="captcha" style="margin-bottom:10px">
<div id="captcha">
<div id="captchaCode">
<Input v-model="formResetPassword.captcha" placeholder="Captcha" size="large">
<Input v-model="formResetPassword.captcha" :placeholder="$t('m.RCaptcha')" size="large">
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
</Input>
</div>

View File

@ -16,20 +16,20 @@
<div class="flex-container">
<div class="left">
<p>Solved</p>
<p>{{$t('m.UserHomeSolved')}}</p>
<p class="emphasis">{{profile.accepted_number}}</p>
</div>
<div class="middle">
<p>Submissions</p>
<p>{{$t('m.UserHomeserSubmissions')}}</p>
<p class="emphasis">{{profile.submission_number}}</p>
</div>
<div class="right">
<p>Score</p>
<p>{{$t('m.UserHomeScore')}}</p>
<p class="emphasis">{{profile.total_score}}</p>
</div>
</div>
<div id="problems">
<div v-if="problems.length">List of solved problems
<div v-if="problems.length">{{$t('m.List_Solved_Problems')}}
<Poptip v-if="refreshVisible" trigger="hover" placement="right-start">
<Icon type="ios-help-outline"></Icon>
<div slot="content">
@ -38,9 +38,9 @@
</div>
</Poptip>
</div>
<p v-else>The guy is so lazy that has not solved any problem yet.</p>
<p v-else>{{$t('m.UserHomeIntro')}}</p>
<div class="btns">
<div class="problem-btn" v-for="problemID in problems">
<div class="problem-btn" v-for="problemID of problems" :key="problemID">
<Button type="ghost" @click="goProblem(problemID)">{{problemID}}</Button>
</div>
</div>