Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Commit

Permalink
Merge pull request #26 from foroughi1380/main
Browse files Browse the repository at this point in the history
Add support for Multi-Language
  • Loading branch information
hossinasaadi authored Nov 8, 2022
2 parents 5705948 + a73f20e commit 749be4a
Show file tree
Hide file tree
Showing 36 changed files with 714 additions and 199 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ x-ui-*.tar.gz
main
release/
access.log
.cache
79 changes: 79 additions & 0 deletions web/assets/js/langs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
supportLangs = [
{
name : "English",
value : "en-US",
icon : "🇺🇸"
},
{
name : "汉语",
value : "zh-Hans",
icon : "🇨🇳"
},
]

function getLang(){
let lang = getCookie('lang')

if (! lang){
if (window.navigator){
lang = window.navigator.language || window.navigator.userLanguage;

if (isSupportLang(lang)){
setCookie('lang' , lang , 150)
}else{
setCookie('lang' , 'en-US' , 150)
window.location.reload();
}
}else{
setCookie('lang' , 'en-US' , 150)
window.location.reload();
}
}

return lang;
}

function setLang(lang){

if (!isSupportLang(lang)){
lang = 'en-US';
}

setCookie('lang' , lang , 150)
window.location.reload();
}

function isSupportLang(lang){
for (l of supportLangs){
if (l.value === lang){
return true;
}
}

return false;
}



function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}

function setCookie(cname, cvalue, exdays) {
const d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
let expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
8 changes: 4 additions & 4 deletions web/assets/js/util/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ function safeBase64(str) {

function formatSecond(second) {
if (second < 60) {
return second.toFixed(0) + ' ';
return second.toFixed(0) + ' s';
} else if (second < 3600) {
return (second / 60).toFixed(0) + ' 分钟';
return (second / 60).toFixed(0) + ' m';
} else if (second < 3600 * 24) {
return (second / 3600).toFixed(0) + ' 小时';
return (second / 3600).toFixed(0) + ' h';
} else {
return (second / 3600 / 24).toFixed(0) + ' ';
return (second / 3600 / 24).toFixed(0) + ' d';
}
}

Expand Down
12 changes: 11 additions & 1 deletion web/controller/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type BaseController struct {
func (a *BaseController) checkLogin(c *gin.Context) {
if !session.IsLogin(c) {
if isAjax(c) {
pureJsonMsg(c, false, "登录时效已过,请重新登录")
pureJsonMsg(c, false, I18n(c , "pages.login.loginAgain"))
} else {
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
}
Expand All @@ -21,3 +21,13 @@ func (a *BaseController) checkLogin(c *gin.Context) {
c.Next()
}
}


func I18n(c *gin.Context , name string, data ...string) string{
anyfunc, _ := c.Get("I18n")
i18n, _ := anyfunc.(func(key string, params ...string) (string, error))

message, _ := i18n(name)

return message;
}
16 changes: 8 additions & 8 deletions web/controller/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (a *InboundController) getInbounds(c *gin.Context) {
user := session.GetLoginUser(c)
inbounds, err := a.inboundService.GetInbounds(user.Id)
if err != nil {
jsonMsg(c, "获取", err)
jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err)
return
}
jsonObj(c, inbounds, nil)
Expand All @@ -64,15 +64,15 @@ func (a *InboundController) addInbound(c *gin.Context) {
inbound := &model.Inbound{}
err := c.ShouldBind(inbound)
if err != nil {
jsonMsg(c, "添加", err)
jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err)
return
}
user := session.GetLoginUser(c)
inbound.UserId = user.Id
inbound.Enable = true
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
err = a.inboundService.AddInbound(inbound)
jsonMsg(c, "添加", err)
jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
Expand All @@ -81,11 +81,11 @@ func (a *InboundController) addInbound(c *gin.Context) {
func (a *InboundController) delInbound(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, "删除", err)
jsonMsg(c, I18n(c , "delete"), err)
return
}
err = a.inboundService.DelInbound(id)
jsonMsg(c, "删除", err)
jsonMsg(c, I18n(c , "delete"), err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
Expand All @@ -94,19 +94,19 @@ func (a *InboundController) delInbound(c *gin.Context) {
func (a *InboundController) updateInbound(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, "修改", err)
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
return
}
inbound := &model.Inbound{
Id: id,
}
err = c.ShouldBind(inbound)
if err != nil {
jsonMsg(c, "修改", err)
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
return
}
err = a.inboundService.UpdateInbound(inbound)
jsonMsg(c, "修改", err)
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
Expand Down
12 changes: 6 additions & 6 deletions web/controller/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,30 @@ func (a *IndexController) index(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, "xui/")
return
}
html(c, "login.html", "登录", nil)
html(c, "login.html", "pages.login.title", nil)
}

func (a *IndexController) login(c *gin.Context) {
var form LoginForm
err := c.ShouldBind(&form)
if err != nil {
pureJsonMsg(c, false, "数据格式错误")
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.invalidFormData"))
return
}
if form.Username == "" {
pureJsonMsg(c, false, "请输入用户名")
pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyUsername"))
return
}
if form.Password == "" {
pureJsonMsg(c, false, "请输入密码")
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.emptyPassword"))
return
}
user := a.userService.CheckUser(form.Username, form.Password)
timeStr := time.Now().Format("2006-01-02 15:04:05")
if user == nil {
job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
pureJsonMsg(c, false, "用户名或密码错误")
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.wrongUsernameOrPassword"))
return
} else {
logger.Infof("%s login success,Ip Address:%s\n", form.Username, getRemoteIp(c))
Expand All @@ -71,7 +71,7 @@ func (a *IndexController) login(c *gin.Context) {

err = session.SetLoginUser(c, user)
logger.Info("user", user.Id, "login success")
jsonMsg(c, "登录", err)
jsonMsg(c, I18n(c , "pages.login.toasts.successLogin"), err)
}

func (a *IndexController) logout(c *gin.Context) {
Expand Down
4 changes: 2 additions & 2 deletions web/controller/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (a *ServerController) getXrayVersion(c *gin.Context) {

versions, err := a.serverService.GetXrayVersions()
if err != nil {
jsonMsg(c, "获取版本", err)
jsonMsg(c, I18n(c , "getVersion"), err)
return
}

Expand All @@ -81,5 +81,5 @@ func (a *ServerController) getXrayVersion(c *gin.Context) {
func (a *ServerController) installXray(c *gin.Context) {
version := c.Param("version")
err := a.serverService.UpdateXray(version)
jsonMsg(c, "安装 xray", err)
jsonMsg(c, I18n(c , "install") + " xray", err)
}
16 changes: 8 additions & 8 deletions web/controller/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
func (a *SettingController) getAllSetting(c *gin.Context) {
allSetting, err := a.settingService.GetAllSetting()
if err != nil {
jsonMsg(c, "获取设置", err)
jsonMsg(c, I18n(c , "pages.setting.toasts.getSetting"), err)
return
}
jsonObj(c, allSetting, nil)
Expand All @@ -50,27 +50,27 @@ func (a *SettingController) updateSetting(c *gin.Context) {
allSetting := &entity.AllSetting{}
err := c.ShouldBind(allSetting)
if err != nil {
jsonMsg(c, "修改设置", err)
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
return
}
err = a.settingService.UpdateAllSetting(allSetting)
jsonMsg(c, "修改设置", err)
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
}

func (a *SettingController) updateUser(c *gin.Context) {
form := &updateUserForm{}
err := c.ShouldBind(form)
if err != nil {
jsonMsg(c, "修改用户", err)
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
return
}
user := session.GetLoginUser(c)
if user.Username != form.OldUsername || user.Password != form.OldPassword {
jsonMsg(c, "修改用户", errors.New("原用户名或原密码错误"))
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.originalUserPassIncorrect")))
return
}
if form.NewUsername == "" || form.NewPassword == "" {
jsonMsg(c, "修改用户", errors.New("新用户名和新密码不能为空"))
jsonMsg(c,I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.userPassMustBeNotEmpty")))
return
}
err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword)
Expand All @@ -79,10 +79,10 @@ func (a *SettingController) updateUser(c *gin.Context) {
user.Password = form.NewPassword
session.SetLoginUser(c, user)
}
jsonMsg(c, "修改用户", err)
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), err)
}

func (a *SettingController) restartPanel(c *gin.Context) {
err := a.panelService.RestartPanel(time.Second * 3)
jsonMsg(c, "重启面板", err)
jsonMsg(c, I18n(c , "pages.setting.restartPanel"), err)
}
6 changes: 3 additions & 3 deletions web/controller/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
if err == nil {
m.Success = true
if msg != "" {
m.Msg = msg + "成功"
m.Msg = msg + I18n(c , "success")
}
} else {
m.Success = false
m.Msg = msg + "失败: " + err.Error()
logger.Warning(msg+"失败: ", err)
m.Msg = msg + I18n(c , "fail") + ": " + err.Error()
logger.Warning(msg + I18n(c , "fail") + ": ", err)
}
c.JSON(http.StatusOK, m)
}
Expand Down
6 changes: 3 additions & 3 deletions web/controller/xui.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
}

func (a *XUIController) index(c *gin.Context) {
html(c, "index.html", "系统状态", nil)
html(c, "index.html", "pages.index.title", nil)
}

func (a *XUIController) inbounds(c *gin.Context) {
html(c, "inbounds.html", "入站列表", nil)
html(c, "inbounds.html", "pages.inbounds.title", nil)
}

func (a *XUIController) setting(c *gin.Context) {
html(c, "setting.html", "设置", nil)
html(c, "setting.html", "pages.setting.title", nil)
}
2 changes: 1 addition & 1 deletion web/html/common/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
display: none;
}
</style>
<title>{{.title}}</title>
<title>{{ i18n .title}}</title>
</head>
{{end}}
1 change: 1 addition & 0 deletions web/html/common/js.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<script src="{{ .base_path }}assets/js/util/utils.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/models.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/langs.js"></script>
<script>
const basePath = '{{ .base_path }}';
axios.defaults.baseURL = basePath;
Expand Down
6 changes: 3 additions & 3 deletions web/html/common/prompt_modal.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{define "promptModal"}}
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
:closable="true" @ok="promptModal.ok" :mask-closable="false"
:ok-text="promptModal.okText" cancel-text="取消">
:ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'>
<a-input id="prompt-modal-input" :type="promptModal.type"
v-model="promptModal.value"
:autosize="{minRows: 10, maxRows: 20}"
Expand All @@ -15,7 +15,7 @@
title: '',
type: '',
value: '',
okText: '确定',
okText: '{{ i18n "sure"}}',
visible: false,
keyEnter(e) {
if (this.type !== 'textarea') {
Expand All @@ -38,7 +38,7 @@
title='',
type='text',
value='',
okText='确定',
okText='{{ i18n "sure"}}',
confirm=() => {},
}) {
this.title = title;
Expand Down
Loading

0 comments on commit 749be4a

Please sign in to comment.