Compare commits

..

28 Commits

Author SHA1 Message Date
8ebd16a14e feat: fix resetting email and phone bug (#1579) 2023-02-23 18:06:13 +08:00
44ec854465 Refactor getClientIdLabel() and getClientSecretLabel() 2023-02-23 17:57:46 +08:00
26e87b0d98 feat: fix compatibility with lower version browsers like Chrome 87 (#1578) 2023-02-22 20:57:57 +08:00
7e0ea0b8d9 Fix missing accountItem crash bug 2023-02-22 12:16:05 +08:00
ace8e9da06 Refactor getAppIdRow() 2023-02-22 12:10:55 +08:00
aac8714d72 feat: handle aliyun SMS response error (#1577) 2023-02-21 20:08:23 +08:00
e71e41b343 feat: fix captcha none type bug (#1572) 2023-02-19 16:56:51 +08:00
6131286cbd Add getBuiltInAccountItems() 2023-02-19 09:45:06 +08:00
3bda8fb9dc Remove Migrator_1_245_0_PR_1557 2023-02-19 09:38:57 +08:00
11f55a474c refactor: New Crowdin translations (#1440)
* refactor: New Crowdin translations by Github Action

* refactor: New Crowdin Backend translations by Github Action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-02-18 23:46:44 +08:00
4806e76cf6 Fix demo mode default login 2023-02-18 23:43:22 +08:00
edbd3d4018 Add i18n banner 2023-02-18 18:09:43 +08:00
3f0a741e6c Improve i18n languages 2023-02-18 17:35:36 +08:00
d273fdd670 Use username as saml:NameID 2023-02-18 16:42:45 +08:00
3ae81716b9 Fix getLdaps() GET 2023-02-18 16:27:47 +08:00
3a70f4e788 Add Setting.isResponseDenied() 2023-02-18 16:21:12 +08:00
842d4865b2 Fix router error message's i18n 2023-02-18 16:11:23 +08:00
19fb7273bb fix: detect access denied by response message for demo mode (#1565)
fix: detect access denied by response message for demo mode
2023-02-18 12:12:54 +08:00
943bd82731 feat: fix migrator_1_245_0_PR_1557.go (#1564) 2023-02-18 10:58:14 +08:00
f2f962b893 fix: refactor functions and code (#1559) 2023-02-18 09:31:58 +08:00
eb72c9f273 feat: support multiple country codes for sending SMS (#1557)
* feat: support multiple country code

* feat: improve UI

* feat: migrate accountItem

* fix: Aliyun compatible

* fix: phone validate

* fix: typo
2023-02-16 22:53:28 +08:00
4605938f8e Refactor FetchFilter.js code 2023-02-16 22:50:08 +08:00
14fa914e6f feat: add IsDemoMode for frontend (#1555)
* feat: add `IsDemoMode` for frontend

* fix: add i18n

* fix: support autologin and go same page

* fix: use i18n for button text
2023-02-16 20:36:30 +08:00
e877045671 feat: fix CAS login crash bug (#1549) 2023-02-14 16:52:15 +08:00
29f1ec08a2 fix: fix CI error by auto waiting for localhost:7001 to start up (#1548) 2023-02-14 14:50:58 +08:00
389744a27d feat: change claims to claimsWithoutThirdIdp when gen token (#1552) 2023-02-14 09:33:46 +08:00
dc7b66822d feat: change token ExpiresIn to second (#1550) 2023-02-14 09:18:30 +08:00
efacf8226c fix: session Id error (#1554) 2023-02-13 22:58:26 +08:00
93 changed files with 1815 additions and 487 deletions

View File

@ -97,21 +97,20 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: 16
- name: back start
run: nohup go run ./main.go &
working-directory: ./
- name: front install
run: yarn install
working-directory: ./web
- name: front start
run: nohup yarn start &
working-directory: ./web
- name: back start
run: nohup go run ./main.go &
working-directory: ./
- name: Sleep for starting
run: sleep 90s
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
- uses: cypress-io/github-action@v4
with:
working-directory: ./web
wait-on: 'http://localhost:7001'
wait-on-timeout: 180
- uses: actions/upload-artifact@v3
if: failure()

View File

@ -21,19 +21,21 @@ type CaptchaProvider interface {
}
func GetCaptchaProvider(captchaType string) CaptchaProvider {
if captchaType == "Default" {
switch captchaType {
case "Default":
return NewDefaultCaptchaProvider()
} else if captchaType == "reCAPTCHA" {
case "reCAPTCHA":
return NewReCaptchaProvider()
} else if captchaType == "hCaptcha" {
return NewHCaptchaProvider()
} else if captchaType == "Aliyun Captcha" {
case "Aliyun Captcha":
return NewAliyunCaptchaProvider()
} else if captchaType == "GEETEST" {
case "hCaptcha":
return NewHCaptchaProvider()
case "GEETEST":
return NewGEETESTCaptchaProvider()
} else if captchaType == "Cloudflare Turnstile" {
case "Cloudflare Turnstile":
return NewCloudflareTurnstileProvider()
}
return nil
}

View File

@ -20,5 +20,5 @@ staticBaseUrl = "https://cdn.casbin.org"
isDemoMode = false
batchSize = 100
ldapServerPort = 389
languages = en,zh,es,fr,de,ja,ko,ru
languages = en,zh,es,fr,de,ja,ko,ru,vi
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}

View File

@ -105,6 +105,20 @@ func GetConfigDataSourceName() string {
return dataSourceName
}
func GetLanguage(language string) string {
if language == "" {
return "en"
}
language = language[0:2]
if strings.Contains(GetConfigString("languages"), language) {
return language
} else {
return "en"
}
}
func IsDemoMode() bool {
return strings.ToLower(GetConfigString("isDemoMode")) == "true"
}

View File

@ -58,7 +58,7 @@ type RequestForm struct {
EmailCode string `json:"emailCode"`
PhoneCode string `json:"phoneCode"`
PhonePrefix string `json:"phonePrefix"`
CountryCode string `json:"countryCode"`
AutoSignin bool `json:"autoSignin"`
@ -121,7 +121,7 @@ func (c *ApiController) Signup() {
}
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation, c.GetAcceptLanguage())
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.CountryCode, form.Affiliation, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
@ -137,7 +137,7 @@ func (c *ApiController) Signup() {
var checkPhone string
if application.IsSignupItemVisible("Phone") && form.Phone != "" {
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(c.T("account:Phone: %s"), checkResult)
@ -179,6 +179,7 @@ func (c *ApiController) Signup() {
Avatar: organization.DefaultAvatar,
Email: form.Email,
Phone: form.Phone,
CountryCode: form.CountryCode,
Address: []string{},
Affiliation: form.Affiliation,
IdCard: form.IdCard,
@ -255,7 +256,9 @@ func (c *ApiController) Logout() {
if accessToken == "" && redirectUri == "" {
c.ClearUserSession()
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
object.DeleteSessionId(util.GetSessionId(object.CasdoorOrganization, object.CasdoorApplication, user), c.Ctx.Input.CruSession.SessionID())
owner, username := util.GetOwnerAndNameFromId(user)
object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()

View File

@ -263,34 +263,32 @@ func (c *ApiController) Login() {
checkDest = form.Username
} else {
verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 {
responseText := fmt.Sprintf(c.T("auth:%s No phone prefix"), verificationCodeType)
c.ResponseError(responseText)
return
}
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone
}
checkDest = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
}
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
if user = object.GetUserByFields(form.Organization, form.Username); user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return
}
if verificationCodeType == "phone" {
form.CountryCode = user.GetCountryCode(form.CountryCode)
var ok bool
if checkDest, ok = util.GetE164Number(form.Username, form.CountryCode); !ok {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), form.CountryCode))
return
}
}
checkResult = object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage())
if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s - %s", verificationCodeType, checkResult)
c.ResponseError(responseText)
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
return
}
// disable the verification code
if strings.Contains(form.Username, "@") {
object.DisableVerificationCode(form.Username)
} else {
object.DisableVerificationCode(fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username))
}
object.DisableVerificationCode(checkDest)
} else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {

View File

@ -51,7 +51,7 @@ type LdapSyncResp struct {
func (c *ApiController) GetLdapUser() {
ldapServer := LdapServer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
if err != nil || util.IsStringsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -104,7 +104,7 @@ func (c *ApiController) GetLdapUser() {
// GetLdaps
// @Tag Account API
// @Title GetLdaps
// @router /get-ldaps [post]
// @router /get-ldaps [get]
func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner")
@ -115,11 +115,11 @@ func (c *ApiController) GetLdaps() {
// GetLdap
// @Tag Account API
// @Title GetLdap
// @router /get-ldap [post]
// @router /get-ldap [get]
func (c *ApiController) GetLdap() {
id := c.Input().Get("id")
if util.IsStrsEmpty(id) {
if util.IsStringsEmpty(id) {
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -140,7 +140,7 @@ func (c *ApiController) AddLdap() {
return
}
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
if util.IsStringsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -170,7 +170,7 @@ func (c *ApiController) AddLdap() {
func (c *ApiController) UpdateLdap() {
var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
if err != nil || util.IsStringsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("general:Missing parameter"))
return
}

View File

@ -80,7 +80,7 @@ func (c *ApiController) SendEmail() {
c.ResponseOk()
}
if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) {
if util.IsStringsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) {
c.ResponseError(fmt.Sprintf(c.T("service:Empty parameters for emailForm: %v"), emailForm))
return
}
@ -130,13 +130,13 @@ func (c *ApiController) SendSms() {
return
}
org := object.GetOrganization(smsForm.OrgId)
var invalidReceivers []string
for idx, receiver := range smsForm.Receivers {
if !util.IsPhoneCnValid(receiver) {
// The receiver phone format: E164 like +8613854673829 +441932567890
if !util.IsPhoneValid(receiver, "") {
invalidReceivers = append(invalidReceivers, receiver)
} else {
smsForm.Receivers[idx] = fmt.Sprintf("+%s%s", org.PhonePrefix, receiver)
smsForm.Receivers[idx] = receiver
}
}

View File

@ -62,6 +62,10 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
item := object.GetAccountItemByName("Phone", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.CountryCode != newUser.CountryCode {
item := object.GetAccountItemByName("Country code", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Region != newUser.Region {
item := object.GetAccountItemByName("Country/Region", organization)
itemsChanged = append(itemsChanged, item)

View File

@ -17,7 +17,6 @@ package controllers
import (
"fmt"
"strconv"
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
@ -56,11 +55,8 @@ func (c *ApiController) T(error string) string {
// GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string {
lang := c.Ctx.Request.Header.Get("Accept-Language")
if lang == "" || !strings.Contains(conf.GetConfigString("languages"), lang[0:2]) {
lang = "en"
}
return lang[0:2]
language := c.Ctx.Request.Header.Get("Accept-Language")
return conf.GetLanguage(language)
}
// SetTokenErrorHttpStatus ...

View File

@ -24,6 +24,13 @@ import (
"github.com/casdoor/casdoor/util"
)
const (
SignupVerification = "signup"
ResetVerification = "reset"
LoginVerification = "login"
ForgetVerification = "forget"
)
func (c *ApiController) getCurrentUser() *object.User {
var user *object.User
userId := c.GetSessionUsername()
@ -42,18 +49,15 @@ func (c *ApiController) getCurrentUser() *object.User {
func (c *ApiController) SendVerificationCode() {
destType := c.Ctx.Request.Form.Get("type")
dest := c.Ctx.Request.Form.Get("dest")
countryCode := c.Ctx.Request.Form.Get("countryCode")
checkType := c.Ctx.Request.Form.Get("checkType")
checkId := c.Ctx.Request.Form.Get("checkId")
checkKey := c.Ctx.Request.Form.Get("checkKey")
checkUser := c.Ctx.Request.Form.Get("checkUser")
applicationId := c.Ctx.Request.Form.Get("applicationId")
method := c.Ctx.Request.Form.Get("method")
checkUser := c.Ctx.Request.Form.Get("checkUser")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" {
c.ResponseError(c.T("general:Missing parameter") + ": type.")
return
}
if dest == "" {
c.ResponseError(c.T("general:Missing parameter") + ": dest.")
return
@ -62,98 +66,104 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
if checkType == "" {
c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
captchaProvider := captcha.GetCaptchaProvider(checkType)
if captchaProvider != nil {
if checkType != "none" {
if checkKey == "" {
c.ResponseError(c.T("general:Missing parameter") + ": checkKey.")
return
}
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
if err != nil {
if captchaProvider := captcha.GetCaptchaProvider(checkType); captchaProvider == nil {
c.ResponseError(c.T("general:don't support captchaProvider: ") + checkType)
return
} else if isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId); err != nil {
c.ResponseError(err.Error())
return
}
if !isHuman {
} else if !isHuman {
c.ResponseError(c.T("verification:Turing test failed."))
return
}
}
user := c.getCurrentUser()
application := object.GetApplication(applicationId)
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
organization := object.GetOrganization(util.GetId(application.Owner, application.Organization))
if organization == nil {
c.ResponseError(c.T("verification:Organization does not exist"))
return
}
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
c.ResponseError(c.T("general:Please login first"))
return
var user *object.User
// checkUser != "", means method is ForgetVerification
if checkUser != "" {
owner := application.Organization
user = object.GetUser(util.GetId(owner, checkUser))
}
sendResp := errors.New("invalid dest type")
if user == nil && checkUser != "" && checkUser != "true" {
name := application.Organization
user = object.GetUser(fmt.Sprintf("%s/%s", name, checkUser))
}
switch destType {
case "email":
if user != nil && util.GetMaskedEmail(user.Email) == dest {
dest = user.Email
}
if !util.IsEmailValid(dest) {
c.ResponseError(c.T("verification:Email is invalid"))
return
}
userByEmail := object.GetUserByEmail(organization.Name, dest)
if userByEmail == nil && method != "signup" && method != "reset" {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
if method == LoginVerification || method == ForgetVerification {
if user != nil && util.GetMaskedEmail(user.Email) == dest {
dest = user.Email
}
user = object.GetUserByEmail(organization.Name, dest)
if user == nil {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
} else if method == ResetVerification {
user = c.getCurrentUser()
}
provider := application.GetEmailProvider()
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
case "phone":
if user != nil && util.GetMaskedPhone(user.Phone) == dest {
dest = user.Phone
}
if !util.IsPhoneCnValid(dest) {
c.ResponseError(c.T("verification:Phone number is invalid"))
return
if method == LoginVerification || method == ForgetVerification {
if user != nil && util.GetMaskedPhone(user.Phone) == dest {
dest = user.Phone
}
if user = object.GetUserByPhone(organization.Name, dest); user == nil {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
countryCode = user.GetCountryCode(countryCode)
} else if method == ResetVerification {
if user = c.getCurrentUser(); user != nil {
countryCode = user.GetCountryCode(countryCode)
}
}
userByPhone := object.GetUserByPhone(organization.Name, dest)
if userByPhone == nil && method != "signup" && method != "reset" {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
dest = fmt.Sprintf("+%s%s", organization.PhonePrefix, dest)
provider := application.GetSmsProvider()
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, dest)
if phone, ok := util.GetE164Number(dest, countryCode); !ok {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), countryCode))
return
} else {
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone)
}
}
if sendResp != nil {
c.Data["json"] = Response{Status: "error", Msg: sendResp.Error()}
c.ResponseError(sendResp.Error())
} else {
c.Data["json"] = Response{Status: "ok"}
c.ResponseOk()
}
c.ServeJSON()
}
// ResetEmailOrPhone ...
@ -169,7 +179,8 @@ func (c *ApiController) ResetEmailOrPhone() {
destType := c.Ctx.Request.Form.Get("type")
dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
if util.IsStringsEmpty(destType, dest, code) {
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -177,7 +188,7 @@ func (c *ApiController) ResetEmailOrPhone() {
checkDest := dest
organization := object.GetOrganizationByUser(user)
if destType == "phone" {
if object.HasUserByField(user.Owner, "phone", user.Phone) {
if object.HasUserByField(user.Owner, "phone", dest) {
c.ResponseError(c.T("check:Phone already exists"))
return
}
@ -192,14 +203,12 @@ func (c *ApiController) ResetEmailOrPhone() {
c.ResponseError(errMsg)
return
}
phonePrefix := "86"
if organization != nil && organization.PhonePrefix != "" {
phonePrefix = organization.PhonePrefix
if checkDest, ok = util.GetE164Number(dest, user.GetCountryCode("")); !ok {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), user.CountryCode))
return
}
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" {
if object.HasUserByField(user.Owner, "email", user.Email) {
if object.HasUserByField(user.Owner, "email", dest) {
c.ResponseError(c.T("check:Email already exists"))
return
}
@ -215,8 +224,8 @@ func (c *ApiController) ResetEmailOrPhone() {
return
}
}
if ret := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(ret) != 0 {
c.ResponseError(ret)
if msg := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(msg) != 0 {
c.ResponseError(msg)
return
}
@ -233,8 +242,7 @@ func (c *ApiController) ResetEmailOrPhone() {
}
object.DisableVerificationCode(checkDest)
c.Data["json"] = Response{Status: "ok"}
c.ServeJSON()
c.ResponseOk()
}
// VerifyCaptcha ...

3
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/beego/beego v1.12.11
github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.30.1
github.com/casdoor/go-sms-sender v0.5.1
github.com/casdoor/go-sms-sender v0.5.2
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.2.0
github.com/casdoor/xorm-adapter/v3 v3.0.4
@ -30,6 +30,7 @@ require (
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/nyaruka/phonenumbers v1.1.5
github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.6.0

6
go.sum
View File

@ -94,8 +94,8 @@ github.com/casbin/casbin/v2 v2.1.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeKF0=
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casdoor/go-sms-sender v0.5.1 h1:1/Wp1OLkVAVY4lEGQhekSNetSAWhnPcxYPV7xpCZgC0=
github.com/casdoor/go-sms-sender v0.5.1/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI=
github.com/casdoor/go-sms-sender v0.5.2 h1:LRjqoXBAOsH4yxXeJ7kINMesyoyEx5pEoiJz9d7lGJA=
github.com/casdoor/go-sms-sender v0.5.2/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8=
@ -354,6 +354,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/nyaruka/phonenumbers v1.1.5 h1:vYy2DI+z5hdaemqVzXYJ4CVyK92IG484CirEY+40GTo=
github.com/nyaruka/phonenumbers v1.1.5/go.mod h1:yShPJHDSH3aTKzCbXyVxNpbl2kA+F+Ne5Pun/MvFRos=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=

View File

@ -30,24 +30,26 @@ func TestGenerateI18nFrontend(t *testing.T) {
enData := parseEnData("frontend")
writeI18nFile("frontend", "en", enData)
applyToOtherLanguage("frontend", "de", enData)
applyToOtherLanguage("frontend", "zh", enData)
applyToOtherLanguage("frontend", "es", enData)
applyToOtherLanguage("frontend", "fr", enData)
applyToOtherLanguage("frontend", "de", enData)
applyToOtherLanguage("frontend", "ja", enData)
applyToOtherLanguage("frontend", "ko", enData)
applyToOtherLanguage("frontend", "ru", enData)
applyToOtherLanguage("frontend", "zh", enData)
applyToOtherLanguage("frontend", "vi", enData)
}
func TestGenerateI18nBackend(t *testing.T) {
enData := parseEnData("backend")
writeI18nFile("backend", "en", enData)
applyToOtherLanguage("backend", "de", enData)
applyToOtherLanguage("backend", "zh", enData)
applyToOtherLanguage("backend", "es", enData)
applyToOtherLanguage("backend", "fr", enData)
applyToOtherLanguage("backend", "de", enData)
applyToOtherLanguage("backend", "ja", enData)
applyToOtherLanguage("backend", "ko", enData)
applyToOtherLanguage("backend", "ru", enData)
applyToOtherLanguage("backend", "zh", enData)
applyToOtherLanguage("backend", "vi", enData)
}

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -55,15 +54,17 @@
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

147
i18n/locales/vi/data.json Normal file
View File

@ -0,0 +1,147 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "该应用不允许注册新用户"
},
"auth": {
"%s No phone prefix": "%s 无此手机号前缀",
"Challenge method should be S256": "Challenge 方法应该为 S256",
"Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s",
"Failed to login in: %s": "登录失败: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "用户名过长最大允许长度为39个字符",
"Username must have at least 2 characters": "用户名至少要有2个字符",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试",
"Your region is not allow to signup by phone": "所在地区不支持手机号注册",
"password or code is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会",
"unsupported password type: %s": "不支持的密码类型: %s"
},
"general": {
"Missing parameter": "缺少参数",
"Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s 不存在"
"The user: %s doesn't exist": "用户: %s 不存在",
"don't support captchaProvider: ": "不支持验证码提供商: "
},
"ldap": {
"Ldap server exist": "LDAP服务器已存在"
@ -130,7 +131,7 @@
"Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商",
"Organization does not exist": "组织不存在",
"Phone number is invalid": "非法的手机号码",
"Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s",
"Turing test failed.": "验证码还未发送",
"Unable to get the email modify rule.": "无法获取邮箱修改规则",
"Unable to get the phone modify rule.": "无法获取手机号修改规则",

View File

@ -7,7 +7,7 @@
"websiteUrl": "",
"favicon": "",
"passwordType": "",
"phonePrefix": "",
"countryCodes": [""],
"defaultAvatar": "",
"tags": [""]
}
@ -107,6 +107,7 @@
"avatar": "",
"email": "",
"phone": "",
"countryCode": "",
"address": [],
"affiliation": "",
"tag": "",

View File

@ -42,7 +42,7 @@ func init() {
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
}
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string, lang string) string {
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, countryCode string, affiliation string, lang string) string {
if organization == nil {
return i18n.Translate(lang, "check:Organization does not exist")
}
@ -107,7 +107,9 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "check:Phone already exists")
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
} else if !util.IsPhoneAllowInRegin(countryCode, organization.CountryCodes) {
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
} else if !util.IsPhoneValid(phone, countryCode) {
return i18n.Translate(lang, "check:Phone number is invalid")
}
}

View File

@ -39,6 +39,39 @@ func InitDb() {
initWebAuthn()
}
func getBuiltInAccountItems() []*AccountItem {
return []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "CountryCode", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
}
}
func initBuiltInOrganization() bool {
organization := getOrganization("admin", "built-in")
if organization != nil {
@ -53,40 +86,12 @@ func initBuiltInOrganization() bool {
WebsiteUrl: "https://example.com",
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
PasswordType: "plain",
PhonePrefix: "86",
CountryCodes: []string{"CN"},
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru"},
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru", "vi"},
InitScore: 2000,
AccountItems: []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
},
AccountItems: getBuiltInAccountItems(),
}
AddOrganization(organization)
return false
@ -109,6 +114,7 @@ func initBuiltInUser() {
Avatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Email: "admin@example.com",
Phone: "12345678910",
CountryCode: "CN",
Address: []string{},
Affiliation: "Example Inc.",
Tag: "staff",

View File

@ -179,33 +179,7 @@ func initDefinedOrganization(organization *Organization) {
return
}
organization.CreatedTime = util.GetCurrentTime()
organization.AccountItems = []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
}
organization.AccountItems = getBuiltInAccountItems()
AddOrganization(organization)
}

View File

@ -329,7 +329,7 @@ func GetLdaps(owner string) []*Ldap {
}
func GetLdap(id string) *Ldap {
if util.IsStrsEmpty(id) {
if util.IsStringsEmpty(id) {
return nil
}

View File

@ -49,7 +49,7 @@ type Organization struct {
Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"`
@ -211,6 +211,10 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
}
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
if accountItem == nil {
return true, ""
}
switch accountItem.ModifyRule {
case "Admin":
if user == nil || !user.IsAdmin && !user.IsGlobalAdmin {

View File

@ -64,7 +64,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host)
subject := assertion.CreateElement("saml:Subject")
subject.CreateElement("saml:NameID").SetText(user.Email)
subject.CreateElement("saml:NameID").SetText(user.Name)
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer")
subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData")

View File

@ -14,7 +14,11 @@
package object
import "github.com/casdoor/go-sms-sender"
import (
"strings"
"github.com/casdoor/go-sms-sender"
)
func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
client, err := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
@ -25,6 +29,12 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
return err
}
if provider.Type == go_sms_sender.Aliyun {
for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+")
}
}
params := map[string]string{}
if provider.Type == go_sms_sender.TencentCloud {
params["0"] = content

View File

@ -27,7 +27,7 @@ import (
)
const (
hourMinutes = 60
hourSeconds = int(time.Hour / time.Second)
InvalidRequest = "invalid_request"
InvalidClient = "invalid_client"
InvalidGrant = "invalid_grant"
@ -306,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes,
ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope,
TokenType: "Bearer",
CodeChallenge: challenge,
@ -442,7 +442,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Code: util.GenerateClientId(),
AccessToken: newAccessToken,
RefreshToken: newRefreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes,
ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope,
TokenType: "Bearer",
}
@ -592,7 +592,7 @@ func GetPasswordToken(application *Application, username string, password string
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes,
ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -632,7 +632,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
User: nullUser.Name,
Code: util.GenerateClientId(),
AccessToken: accessToken,
ExpiresIn: application.ExpireInHours * hourMinutes,
ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -659,7 +659,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes,
ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,

View File

@ -265,8 +265,8 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claims.TokenType = "refresh-token"
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
}

View File

@ -46,7 +46,8 @@ type User struct {
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
Email string `xorm:"varchar(100) index" json:"email"`
EmailVerified bool `json:"emailVerified"`
Phone string `xorm:"varchar(100) index" json:"phone"`
Phone string `xorm:"varchar(20) index" json:"phone"`
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
Location string `xorm:"varchar(100)" json:"location"`
Address []string `json:"address"`
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
@ -454,7 +455,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
}
}
if isGlobalAdmin {
columns = append(columns, "name", "email", "phone")
columns = append(columns, "name", "email", "phone", "country_code")
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)

View File

@ -170,3 +170,18 @@ func ClearUserOAuthProperties(user *User, providerType string) bool {
return affected != 0
}
func (user *User) GetCountryCode(countryCode string) string {
if countryCode != "" {
return countryCode
}
if user != nil && user.CountryCode != "" {
return user.CountryCode
}
if org := GetOrganizationByUser(user); org != nil && len(org.CountryCodes) > 0 {
return org.CountryCodes[0]
}
return ""
}

View File

@ -122,11 +122,8 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
record.Owner = provider.Owner
record.Name = util.GenerateId()
record.CreatedTime = util.GetCurrentTime()
if user != nil {
record.User = user.GetId()
}
record.Provider = provider.Name
record.Provider = provider.Name
record.Receiver = dest
record.Code = code
record.Time = time.Now().Unix()

View File

@ -57,7 +57,7 @@ func AutoSigninFilter(ctx *context.Context) {
return
}
// "/page?username=abc&password=123"
// "/page?username=built-in/admin&password=123"
userId = ctx.Input.Query("username")
password := ctx.Input.Query("password")
if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" {

View File

@ -19,6 +19,8 @@ import (
"strings"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -46,8 +48,17 @@ func responseError(ctx *context.Context, error string, data ...interface{}) {
}
}
func getAcceptLanguage(ctx *context.Context) string {
language := ctx.Request.Header.Get("Accept-Language")
return conf.GetLanguage(language)
}
func T(ctx *context.Context, error string) string {
return i18n.Translate(getAcceptLanguage(ctx), error)
}
func denyRequest(ctx *context.Context) {
responseError(ctx, "Unauthorized operation")
responseError(ctx, T(ctx, "auth:Unauthorized operation"))
}
func getUsernameByClientIdSecret(ctx *context.Context) string {

View File

@ -119,8 +119,8 @@ func initAPI() {
beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha")
beego.Router("/api/get-ldap-user", &controllers.ApiController{}, "POST:GetLdapUser")
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "POST:GetLdaps")
beego.Router("/api/get-ldap", &controllers.ApiController{}, "POST:GetLdap")
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "GET:GetLdaps")
beego.Router("/api/get-ldap", &controllers.ApiController{}, "GET:GetLdap")
beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap")
beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap")
beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap")

View File

@ -14,6 +14,8 @@
package util
import "sort"
func DeleteVal(values []string, val string) []string {
newValues := []string{}
for _, v := range values {
@ -23,3 +25,8 @@ func DeleteVal(values []string, val string) []string {
}
return newValues
}
func ContainsString(values []string, val string) bool {
sort.Strings(values)
return sort.SearchStrings(values, val) != len(values)
}

View File

@ -145,7 +145,7 @@ func GetMd5Hash(text string) string {
return hex.EncodeToString(hash[:])
}
func IsStrsEmpty(strs ...string) bool {
func IsStringsEmpty(strs ...string) bool {
for _, str := range strs {
if len(str) == 0 {
return true
@ -227,7 +227,7 @@ func IsChinese(str string) bool {
}
func GetMaskedPhone(phone string) string {
return getMaskedPhone(phone)
return rePhone.ReplaceAllString(phone, "$1****$2")
}
func GetMaskedEmail(email string) string {

View File

@ -183,7 +183,7 @@ func TestIsStrsEmpty(t *testing.T) {
}
for _, scenery := range scenarios {
t.Run(scenery.description, func(t *testing.T) {
actual := IsStrsEmpty(scenery.input...)
actual := IsStringsEmpty(scenery.input...)
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
})
}

View File

@ -31,6 +31,6 @@ func GetCurrentUnixTime() string {
func IsTokenExpired(createdTime string, expiresIn int) bool {
createdTimeObj, _ := time.Parse(time.RFC3339, createdTime)
expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Minute)
expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Second)
return time.Now().After(expiresAtObj)
}

View File

@ -56,15 +56,15 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted now is valid for 60 minutes",
input: input{
createdTime: time.Now().Format(time.RFC3339),
expiresIn: 60,
expiresIn: 3600,
},
expected: false,
},
{
description: "Token emitted 60 minutes before now is valid for 60 minutes",
description: "Token emitted 60 minutes before now is valid for 61 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
expiresIn: 61,
expiresIn: 3660,
},
expected: false,
},
@ -72,7 +72,7 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted 2 hours before now is Expired after 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 60,
expiresIn: 3600,
},
expected: true,
},
@ -80,23 +80,23 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted 61 minutes before now is Expired after 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
expiresIn: 60,
expiresIn: 3600,
},
expected: true,
},
{
description: "Token emitted 2 hours before now is valid for 120 minutes",
description: "Token emitted 2 hours before now is valid for 121 minutes",
input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 121,
expiresIn: 7260,
},
expected: false,
},
{
description: "Token emitted 159 minutes before now is Expired after 60 minutes",
description: "Token emitted 159 minutes before now is Expired after 120 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
expiresIn: 120,
expiresIn: 7200,
},
expected: true,
},

View File

@ -17,16 +17,13 @@ package util
import (
"net/mail"
"regexp"
"github.com/nyaruka/phonenumbers"
)
var (
rePhoneCn *regexp.Regexp
rePhone *regexp.Regexp
)
var rePhone *regexp.Regexp
func init() {
// https://learnku.com/articles/31543
rePhoneCn, _ = regexp.Compile(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`)
rePhone, _ = regexp.Compile("(\\d{3})\\d*(\\d{4})")
}
@ -35,10 +32,19 @@ func IsEmailValid(email string) bool {
return err == nil
}
func IsPhoneCnValid(phone string) bool {
return rePhoneCn.MatchString(phone)
func IsPhoneValid(phone string, countryCode string) bool {
phoneNumber, err := phonenumbers.Parse(phone, countryCode)
if err != nil {
return false
}
return phonenumbers.IsValidNumber(phoneNumber)
}
func getMaskedPhone(phone string) string {
return rePhone.ReplaceAllString(phone, "$1****$2")
func IsPhoneAllowInRegin(countryCode string, allowRegions []string) bool {
return !ContainsString(allowRegions, countryCode)
}
func GetE164Number(phone string, countryCode string) (string, bool) {
phoneNumber, _ := phonenumbers.Parse(phone, countryCode)
return phonenumbers.Format(phoneNumber, phonenumbers.E164), phonenumbers.IsValidNumber(phoneNumber)
}

View File

@ -15,7 +15,6 @@ describe("Login test", () => {
"password": "123",
"autoSignin": true,
"type": "login",
"phonePrefix": "86",
},
}).then((Response) => {
expect(Response).property("body").property("status").to.equal("ok");
@ -40,7 +39,6 @@ describe("Login test", () => {
"password": "1234",
"autoSignin": true,
"type": "login",
"phonePrefix": "86",
},
}).then((Response) => {
expect(Response).property("body").property("status").to.equal("error");

View File

@ -34,7 +34,6 @@ Cypress.Commands.add('login', ()=>{
"password": "123",
"autoSignin": true,
"type": "login",
"phonePrefix": "86",
},
}).then((Response) => {
expect(Response).property("body").property("status").to.equal("ok");

View File

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/cssinjs": "^1.5.6",
"@ant-design/icons": "^4.7.0",
"@craco/craco": "^6.4.5",
"@crowdin/cli": "^3.7.10",
@ -21,6 +22,7 @@
"file-saver": "^2.0.5",
"i18n-iso-countries": "^7.0.0",
"i18next": "^19.8.9",
"libphonenumber-js": "^1.10.19",
"moment": "^2.29.1",
"qs": "^6.10.2",
"react": "^18.2.0",

View File

@ -78,6 +78,7 @@ class AccountTable extends React.Component {
{name: "Password", displayName: i18next.t("general:Password")},
{name: "Email", displayName: i18next.t("general:Email")},
{name: "Phone", displayName: i18next.t("general:Phone")},
{name: "Country code", displayName: i18next.t("user:Country code")},
{name: "Country/Region", displayName: i18next.t("user:Country/Region")},
{name: "Location", displayName: i18next.t("user:Location")},
{name: "Affiliation", displayName: i18next.t("user:Affiliation")},

View File

@ -261,7 +261,7 @@ class AdapterListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -16,8 +16,9 @@ import React, {Component} from "react";
import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
import {StyleProvider} from "@ant-design/cssinjs";
import {BarsOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
@ -671,6 +672,9 @@ class App extends Component {
return (
<React.Fragment>
{
this.renderBanner()
}
<FloatButton.BackTop />
<CustomGithubCorner />
{
@ -680,6 +684,32 @@ class App extends Component {
);
}
renderBanner() {
if (!Conf.IsDemoMode) {
return null;
}
const language = Setting.getLanguage();
if (language === "en" || language === "zh") {
return null;
}
return (
<Alert type="info" banner showIcon={false} closable message={
<div style={{textAlign: "center"}}>
<InfoCircleFilled style={{color: "rgb(87,52,211)"}} />
&nbsp;&nbsp;
{i18next.t("general:Found some texts still not translated? Please help us translate at")}
&nbsp;
<a target="_blank" rel="noreferrer" href={"https://crowdin.com/project/casdoor-site"}>
Crowdin
</a>
&nbsp;!&nbsp;🙏
</div>
} />
);
}
render() {
return (
<React.Fragment>
@ -700,9 +730,11 @@ class App extends Component {
},
algorithm: Setting.getAlgorithm(this.state.themeAlgorithm),
}}>
{
this.renderPage()
}
<StyleProvider hashPriority="high">
{
this.renderPage()
}
</StyleProvider>
</ConfigProvider>
</React.Fragment>
);

View File

@ -287,7 +287,7 @@ class ApplicationListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -228,7 +228,7 @@ class CertListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -14,6 +14,7 @@
export const ShowGithubCorner = false;
export const GithubRepo = "https://github.com/casdoor/casdoor";
export const IsDemoMode = false;
export const ForceLanguage = "";
export const DefaultLanguage = "en";

View File

@ -83,8 +83,8 @@ class EntryPage extends React.Component {
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signup"} {...props} />);}} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signup"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
</Switch>
</div>
);

View File

@ -201,7 +201,7 @@ class ModelListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -184,12 +184,20 @@ class OrganizationEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Phone prefix"), i18next.t("general:Phone prefix - Tooltip"))} :
{Setting.getLabel(i18next.t("general:Supported country code"), i18next.t("general:Supported country code - Tooltip"))} :
</Col>
<Col span={22} >
<Input addonBefore={"+"} value={this.state.organization.phonePrefix} onChange={e => {
this.updateOrganizationField("phonePrefix", e.target.value);
}} />
<Select virtual={false} mode={"multiple"} style={{width: "100%"}} value={this.state.organization.countryCodes ?? []}
options={Setting.getCountriesData().map(country => {
return Setting.getOption(
<>
{Setting.countryFlag(country)}
{`${country.name} +${country.phone}`}
</>,
country.code);
})} onChange={value => {
this.updateOrganizationField("countryCodes", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -257,22 +265,13 @@ class OrganizationEditPage extends React.Component {
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}}
value={this.state.organization.languages}
options={Setting.Countries.map((item) => {
return Setting.getOption(item.label, item.key);
})}
value={this.state.organization.languages ?? []}
onChange={(value => {
this.updateOrganizationField("languages", value);
})} >
{
[
{value: "en", label: "English"},
{value: "zh", label: "简体中文"},
{value: "es", label: "Español"},
{value: "fr", label: "Français"},
{value: "de", label: "Deutsch"},
{value: "ja", label: "日本語"},
{value: "ko", label: "한국어"},
{value: "ru", label: "Русский"},
].map((item, index) => <Option key={index} value={item.value}>{item.label}</Option>)
}
</Select>
</Col>
</Row>

View File

@ -33,11 +33,11 @@ class OrganizationListPage extends BaseListPage {
favicon: `${Setting.StaticBaseUrl}/img/favicon.png`,
passwordType: "plain",
PasswordSalt: "",
phonePrefix: "86",
countryCodes: ["CN"],
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
defaultApplication: "",
tags: [],
languages: ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
languages: ["en", "zh", "es", "fr", "de", "ja", "ko", "ru", "vi"],
masterPassword: "",
enableSoftDeletion: false,
isProfilePublic: true,
@ -284,7 +284,7 @@ class OrganizationListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -279,7 +279,7 @@ class PaymentListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -359,7 +359,7 @@ class PermissionListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -296,7 +296,7 @@ class ProductListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -82,20 +82,20 @@ class ProviderEditPage extends React.Component {
});
}
getClientIdLabel() {
switch (this.state.provider.category) {
getClientIdLabel(provider) {
switch (provider.category) {
case "Email":
return Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"));
case "SMS":
if (this.state.provider.type === "Volc Engine SMS") {
if (provider.type === "Volc Engine SMS") {
return Setting.getLabel(i18next.t("provider:Access key"), i18next.t("provider:Access key - Tooltip"));
} else if (this.state.provider.type === "Huawei Cloud SMS") {
} else if (provider.type === "Huawei Cloud SMS") {
return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip"));
} else {
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
}
case "Captcha":
if (this.state.provider.type === "Aliyun Captcha") {
if (provider.type === "Aliyun Captcha") {
return Setting.getLabel(i18next.t("provider:Access key"), i18next.t("provider:Access key - Tooltip"));
} else {
return Setting.getLabel(i18next.t("provider:Site key"), i18next.t("provider:Site key - Tooltip"));
@ -105,20 +105,20 @@ class ProviderEditPage extends React.Component {
}
}
getClientSecretLabel() {
switch (this.state.provider.category) {
getClientSecretLabel(provider) {
switch (provider.category) {
case "Email":
return Setting.getLabel(i18next.t("login:Password"), i18next.t("login:Password - Tooltip"));
case "SMS":
if (this.state.provider.type === "Volc Engine SMS") {
if (provider.type === "Volc Engine SMS") {
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:SecretAccessKey - Tooltip"));
} else if (this.state.provider.type === "Huawei Cloud SMS") {
} else if (provider.type === "Huawei Cloud SMS") {
return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));
} else {
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
}
case "Captcha":
if (this.state.provider.type === "Aliyun Captcha") {
if (provider.type === "Aliyun Captcha") {
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:SecretAccessKey - Tooltip"));
} else {
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
@ -128,40 +128,52 @@ class ProviderEditPage extends React.Component {
}
}
getAppIdRow() {
let text, tooltip;
if (this.state.provider.category === "SMS" && this.state.provider.type === "Tencent Cloud SMS") {
text = i18next.t("provider:App ID");
tooltip = i18next.t("provider:App ID - Tooltip");
} else if (this.state.provider.type === "WeCom" && this.state.provider.subType === "Internal") {
text = i18next.t("provider:Agent ID");
tooltip = i18next.t("provider:Agent ID - Tooltip");
} else if (this.state.provider.type === "Infoflow") {
text = i18next.t("provider:Agent ID");
tooltip = i18next.t("provider:Agent ID - Tooltip");
} else if (this.state.provider.category === "SMS" && this.state.provider.type === "Volc Engine SMS") {
text = i18next.t("provider:SMS account");
tooltip = i18next.t("provider:SMS account - Tooltip");
} else if (this.state.provider.category === "SMS" && this.state.provider.type === "Huawei Cloud SMS") {
text = i18next.t("provider:Channel No.");
tooltip = i18next.t("provider:Channel No. - Tooltip");
} else if (this.state.provider.category === "Email" && this.state.provider.type === "SUBMAIL") {
text = i18next.t("provider:App ID");
tooltip = i18next.t("provider:App ID - Tooltip");
} else {
return null;
getAppIdRow(provider) {
let text = "";
let tooltip = "";
if (provider.category === "OAuth") {
if (provider.type === "WeCom" && provider.subType === "Internal") {
text = i18next.t("provider:Agent ID");
tooltip = i18next.t("provider:Agent ID - Tooltip");
} else if (provider.type === "Infoflow") {
text = i18next.t("provider:Agent ID");
tooltip = i18next.t("provider:Agent ID - Tooltip");
}
} else if (provider.category === "SMS") {
if (provider.type === "Tencent Cloud SMS") {
text = i18next.t("provider:App ID");
tooltip = i18next.t("provider:App ID - Tooltip");
} else if (provider.type === "Volc Engine SMS") {
text = i18next.t("provider:SMS account");
tooltip = i18next.t("provider:SMS account - Tooltip");
} else if (provider.type === "Huawei Cloud SMS") {
text = i18next.t("provider:Channel No.");
tooltip = i18next.t("provider:Channel No. - Tooltip");
}
} else if (provider.category === "Email") {
if (provider.type === "SUBMAIL") {
text = i18next.t("provider:App ID");
tooltip = i18next.t("provider:App ID - Tooltip");
}
}
return <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(text, tooltip)} :
</Col>
<Col span={22} >
<Input value={this.state.provider.appId} onChange={e => {
this.updateProviderField("appId", e.target.value);
}} />
</Col>
</Row>;
if (text === "" && tooltip === "") {
return null;
} else {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(text, tooltip)} :
</Col>
<Col span={22} >
<Input value={provider.appId} onChange={e => {
this.updateProviderField("appId", e.target.value);
}} />
</Col>
</Row>
);
}
}
loadSamlConfiguration() {
@ -404,7 +416,7 @@ class ProviderEditPage extends React.Component {
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientIdLabel()}
{this.getClientIdLabel(this.state.provider)}
</Col>
<Col span={22} >
<Input value={this.state.provider.clientId} onChange={e => {
@ -414,7 +426,7 @@ class ProviderEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientSecretLabel()}
{this.getClientSecretLabel(this.state.provider)}
</Col>
<Col span={22} >
<Input value={this.state.provider.clientSecret} onChange={e => {
@ -750,7 +762,7 @@ class ProviderEditPage extends React.Component {
</React.Fragment>
) : null
}
{this.getAppIdRow()}
{this.getAppIdRow(this.state.provider)}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Provider URL"), i18next.t("provider:Provider URL - Tooltip"))} :

View File

@ -267,7 +267,7 @@ class ProviderListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -232,7 +232,7 @@ class RoleListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -18,16 +18,6 @@ import {Dropdown} from "antd";
import "./App.less";
import {GlobalOutlined} from "@ant-design/icons";
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
{label: "Español", key: "es", country: "ES", alt: "Español"},
{label: "Français", key: "fr", country: "FR", alt: "Français"},
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
];
function flagIcon(country, alt) {
return (
<img width={24} alt={alt} src={`${Setting.StaticBaseUrl}/flag-icons/${country}.svg`} />
@ -39,15 +29,15 @@ class SelectLanguageBox extends React.Component {
super(props);
this.state = {
classes: props,
languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
languages: props.languages ?? Setting.Countries.map(item => item.key),
};
Countries.forEach((country) => {
Setting.Countries.forEach((country) => {
new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`;
});
}
items = Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
items = Setting.Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
getOrganizationLanguages(languages) {
const select = [];

View File

@ -49,9 +49,9 @@ class SelectRegionBox extends React.Component {
}
>
{
Setting.getCountryNames().map((item) => (
Setting.getCountriesData().map((item) => (
<Option key={item.code} value={item.code} label={`${item.name} (${item.code})`} >
<img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} />
{Setting.countryFlag(item)}
{`${item.name} (${item.code})`}
</Option>
))

View File

@ -148,7 +148,7 @@ class SessionListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -23,6 +23,7 @@ import copy from "copy-to-clipboard";
import {authConfig} from "./auth/Auth";
import {Helmet} from "react-helmet";
import * as Conf from "./Conf";
import * as phoneNumber from "libphonenumber-js";
import * as path from "path-browserify";
export const ServerUrl = "";
@ -30,6 +31,17 @@ export const ServerUrl = "";
// export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static";
export const StaticBaseUrl = "https://cdn.casbin.org";
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
{label: "中文", key: "zh", country: "CN", alt: "中文"},
{label: "Español", key: "es", country: "ES", alt: "Español"},
{label: "Français", key: "fr", country: "FR", alt: "Français"},
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
{label: "TiếngViệt", key: "vi", country: "VI", alt: "TiếngViệt"},
];
export function getThemeData(organization, application) {
if (application?.themeData?.isEnabled) {
return application.themeData;
@ -188,20 +200,33 @@ export const OtherProviderInfo = {
},
};
export function getCountriesData() {
export function initCountries() {
const countries = require("i18n-iso-countries");
countries.registerLocale(require("i18n-iso-countries/langs/" + getLanguage() + ".json"));
return countries;
}
export function getCountryNames() {
const data = getCountriesData().getNames(getLanguage(), {select: "official"});
return Object.entries(data).map(items => {
return {code: items[0], name: items[1]};
export function getCountriesData(countryCodes = phoneNumber.getCountries()) {
return countryCodes?.map((countryCode) => {
if (phoneNumber.isSupportedCountry(countryCode)) {
const name = initCountries().getName(countryCode, getLanguage());
return {
code: countryCode,
name: name || "",
phone: phoneNumber.getCountryCallingCode(countryCode),
};
}
});
}
export function countryFlag(country) {
return <img src={`${StaticBaseUrl}/flag-icons/${country.code}.svg`} alt={country.name} height={20} style={{marginRight: 10}} />;
}
export function getPhoneCodeFromCountryCode(countryCode) {
return phoneNumber.isSupportedCountry(countryCode) ? phoneNumber.getCountryCallingCode(countryCode) : "";
}
export function initServerUrl() {
// const hostname = window.location.hostname;
// if (hostname === "localhost") {
@ -238,6 +263,13 @@ export function isProviderVisible(providerItem) {
return true;
}
export function isResponseDenied(data) {
if (data.msg === "Unauthorized operation" || data.msg === "未授权的操作") {
return true;
}
return false;
}
export function isProviderVisibleForSignUp(providerItem) {
if (providerItem.canSignUp === false) {
return false;
@ -299,16 +331,14 @@ export function isValidEmail(email) {
return emailRegex.test(email);
}
export function isValidPhone(phone) {
return phone !== "";
export function isValidPhone(phone, countryCode = "") {
if (countryCode !== "") {
return phoneNumber.isValidPhoneNumber(phone, countryCode);
}
// if (phone === "") {
// return false;
// }
//
// // https://learnku.com/articles/31543, `^s*$` filter empty email individually.
// const phoneRegex = /^\s*$|^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/;
// return phoneRegex.test(phone);
const phoneRegex = /[0-9]{4,15}$/;
return phoneRegex.test(phone);
}
export function isValidInvoiceTitle(invoiceTitle) {

View File

@ -289,7 +289,7 @@ class SyncerListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -254,7 +254,7 @@ class TokenListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -29,6 +29,7 @@ import SelectRegionBox from "./SelectRegionBox";
import WebAuthnCredentialTable from "./WebauthnCredentialTable";
import ManagedAccountTable from "./ManagedAccountTable";
import PropertyTable from "./propertyTable";
import PhoneNumberInput from "./common/PhoneNumberInput";
const {Option} = Select;
@ -286,11 +287,13 @@ class UserEditPage extends React.Component {
<Col style={{paddingRight: "20px"}} span={11} >
{Setting.isLocalAdminUser(this.props.account) ?
(<Input value={this.state.user.email}
style={{width: "280Px"}}
disabled={disabled}
onChange={e => {
this.updateUserField("email", e.target.value);
}} />) :
(<Select virtual={false} value={this.state.user.email}
style={{width: "280Px"}}
options={[Setting.getItem(this.state.user.email, this.state.user.email)]}
disabled={disabled}
onChange={e => {
@ -298,7 +301,7 @@ class UserEditPage extends React.Component {
}} />)
}
</Col>
<Col span={11} >
<Col span={Setting.isMobile() ? 22 : 11} >
{/* backend auto get the current user, so admin can not edit. Just self can reset*/}
{this.isSelf() ? <ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} /> : null}
</Col>
@ -307,24 +310,37 @@ class UserEditPage extends React.Component {
} else if (accountItem.name === "Phone") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
<Col style={{marginTop: "5px"}} span={Setting.isMobile() ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} :
</Col>
<Col style={{paddingRight: "20px"}} span={11} >
{Setting.isLocalAdminUser(this.props.account) ?
<Input value={this.state.user.phone} addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} /> :
(<Select virtual={false} value={`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`}
options={[Setting.getItem(`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`, this.state.user.phone)]}
<Input.Group compact style={{width: "280Px"}}>
<PhoneNumberInput
style={{width: "30%"}}
value={this.state.user.countryCode}
onChange={(value) => {
this.updateUserField("countryCode", value);
}}
countryCodes={this.state.application?.organizationObj.countryCodes}
/>
<Input value={this.state.user.phone}
style={{width: "70%"}}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} />
</Input.Group>
:
(<Select virtual={false} value={this.state.user.phone === "" ? null : `+${Setting.getPhoneCodeFromCountryCode(this.state.user.countryCode)} ${this.state.user.phone}`}
options={this.state.user.phone === "" ? null : [Setting.getItem(`+${Setting.getPhoneCodeFromCountryCode(this.state.user.countryCode)} ${this.state.user.phone}`, this.state.user.phone)]}
disabled={disabled}
style={{width: "280px"}}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} />)}
</Col>
<Col span={11} >
<Col span={Setting.isMobile() ? 24 : 11} >
{this.isSelf() ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
</Col>
</Row>

View File

@ -49,6 +49,7 @@ class UserListPage extends BaseListPage {
avatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
email: `${randomName}@example.com`,
phone: Setting.getRandomNumber(),
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
address: [],
affiliation: "Example Inc.",
tag: "staff",
@ -261,7 +262,7 @@ class UserListPage extends BaseListPage {
sorter: true,
...this.getColumnSearchProps("region"),
render: (text, record, index) => {
return Setting.getCountriesData().getName(record.region, Setting.getLanguage(), {select: "official"});
return Setting.initCountries().getName(record.region, Setting.getLanguage(), {select: "official"});
},
},
{
@ -403,7 +404,7 @@ class UserListPage extends BaseListPage {
this.getOrganization(users[0].owner);
}
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,
@ -431,7 +432,7 @@ class UserListPage extends BaseListPage {
this.getOrganization(users[0].owner);
}
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -254,7 +254,7 @@ class WebhookListPage extends BaseListPage {
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
if (Setting.isResponseDenied(res)) {
this.setState({
loading: false,
isAuthorized: false,

View File

@ -40,7 +40,6 @@ class CasLogout extends React.Component {
if (res.status === "ok") {
Setting.showMessage("success", "Logged out successfully");
this.props.onUpdateAccount(null);
this.onUpdateApplication(null);
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
@ -50,7 +49,6 @@ class CasLogout extends React.Component {
Setting.goToLinkSoft(this, `/cas/${this.state.owner}/${this.state.applicationName}/login`);
}
} else {
this.onUpdateApplication(null);
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});

View File

@ -140,7 +140,6 @@ class ForgetPage extends React.Component {
username: this.state.username,
name: this.state.name,
code: forms.step2.getFieldValue("emailCode"),
phonePrefix: this.getApplicationObj()?.organizationObj.phonePrefix,
type: "login",
}, oAuthParams).then(res => {
if (res.status === "ok") {

View File

@ -16,6 +16,7 @@ import React from "react";
import {Button, Checkbox, Col, Form, Input, Result, Row, Spin, Tabs} from "antd";
import {LockOutlined, UserOutlined} from "@ant-design/icons";
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
import * as Conf from "../Conf";
import * as AuthBackend from "./AuthBackend";
import * as OrganizationBackend from "../backend/OrganizationBackend";
import * as ApplicationBackend from "../backend/ApplicationBackend";
@ -189,7 +190,6 @@ class LoginPage extends React.Component {
} else {
values["type"] = this.state.type;
}
values["phonePrefix"] = this.getApplicationObj()?.organizationObj.phonePrefix;
if (oAuthParams !== null) {
values["samlRequest"] = oAuthParams.samlRequest;
@ -204,6 +204,7 @@ class LoginPage extends React.Component {
values["organization"] = this.getApplicationObj().organization;
}
}
postCodeLoginAction(res) {
const application = this.getApplicationObj();
const ths = this;
@ -364,7 +365,8 @@ class LoginPage extends React.Component {
title={i18next.t("application:Sign Up Error")}
subTitle={i18next.t("application:The application does not allow to sign up new account")}
extra={[
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
<Button type="primary" key="signin"
onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
{
i18next.t("login:Sign In")
}
@ -383,8 +385,12 @@ class LoginPage extends React.Component {
organization: application.organization,
application: application.name,
autoSignin: true,
username: Conf.ShowGithubCorner ? "admin" : "",
password: Conf.ShowGithubCorner ? "123" : "",
}}
onFinish={(values) => {
this.onFinish(values);
}}
onFinish={(values) => {this.onFinish(values);}}
style={{width: "300px"}}
size="large"
ref={this.form}
@ -424,7 +430,7 @@ class LoginPage extends React.Component {
{
validator: (_, value) => {
if (this.state.loginMethod === "verificationCode") {
if (this.state.email !== "" && !Setting.isValidEmail(this.state.username) && !Setting.isValidPhone(this.state.username)) {
if (!Setting.isValidEmail(this.state.username) && !Setting.isValidPhone(this.state.username)) {
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid Email or Phone!"));
}
@ -444,7 +450,7 @@ class LoginPage extends React.Component {
]}
>
<Input
id = "input"
id="input"
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder={(this.state.loginMethod === "verificationCode") ? i18next.t("login:Email or phone") : i18next.t("login:username, Email or phone")}
disabled={!application.enablePassword}
@ -774,13 +780,18 @@ class LoginPage extends React.Component {
const items = [
{label: i18next.t("login:Password"), key: "password"},
];
application.enableCodeSignin ? items.push({label: i18next.t("login:Verification Code"), key: "verificationCode"}) : null;
application.enableCodeSignin ? items.push({
label: i18next.t("login:Verification Code"),
key: "verificationCode",
}) : null;
application.enableWebAuthn ? items.push({label: i18next.t("login:WebAuthn"), key: "webAuthn"}) : null;
if (application.enableCodeSignin || application.enableWebAuthn) {
return (
<div>
<Tabs items={items} size={"small"} defaultActiveKey="password" onChange={(key) => {this.setState({loginMethod: key});}} centered>
<Tabs items={items} size={"small"} defaultActiveKey="password" onChange={(key) => {
this.setState({loginMethod: key});
}} centered>
</Tabs>
</div>
);
@ -823,7 +834,7 @@ class LoginPage extends React.Component {
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div>
<div className="login-form">
<div >
<div>
<div>
{
Setting.renderHelmet(application)

View File

@ -26,6 +26,7 @@ import SelectRegionBox from "../SelectRegionBox";
import CustomGithubCorner from "../CustomGithubCorner";
import SelectLanguageBox from "../SelectLanguageBox";
import {withRouter} from "react-router-dom";
import PhoneNumberInput from "../common/PhoneNumberInput";
const formItemLayout = {
labelCol: {
@ -68,6 +69,7 @@ class SignupPage extends React.Component {
application: null,
email: "",
phone: "",
countryCode: "",
emailCode: "",
phoneCode: "",
validEmail: false,
@ -157,7 +159,6 @@ class SignupPage extends React.Component {
onFinish(values) {
const application = this.getApplicationObj();
values.phonePrefix = application.organizationObj.phonePrefix;
AuthBackend.signup(values)
.then((res) => {
if (res.status === "ok") {
@ -378,35 +379,66 @@ class SignupPage extends React.Component {
} else if (signupItem.name === "Phone") {
return (
<React.Fragment>
<Form.Item
name="phone"
key="phone"
label={i18next.t("general:Phone")}
rules={[
{
required: required,
message: i18next.t("signup:Please input your phone number!"),
},
{
validator: (_, value) => {
if (this.state.phone !== "" && !Setting.isValidPhone(this.state.phone)) {
this.setState({validPhone: false});
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
}
<Form.Item label={i18next.t("general:Phone")} required>
<Input.Group compact>
<Form.Item
name="countryCode"
key="countryCode"
noStyle
rules={[
{
required: required,
message: i18next.t("signup:Please select your country code!"),
},
{
validator: (_, value) => {
if (this.state.phone !== "" && !Setting.isValidPhone(this.state.phone, this.state.countryCode)) {
this.setState({validPhone: false});
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
}
this.setState({validPhone: true});
return Promise.resolve();
},
},
]}
>
<Input
style={{
width: "100%",
}}
addonBefore={`+${this.getApplicationObj()?.organizationObj.phonePrefix}`}
onChange={e => this.setState({phone: e.target.value})}
/>
this.setState({validPhone: true});
return Promise.resolve();
},
},
]}
>
<PhoneNumberInput
showSearsh={true}
style={{width: "35%"}}
value={this.state.countryCode}
onChange={(value) => {this.setState({countryCode: value});}}
countryCodes={this.getApplicationObj().organizationObj.countryCodes}
/>
</Form.Item>
<Form.Item
name="phone"
key="phone"
noStyle
rules={[
{
required: required,
message: i18next.t("signup:Please input your phone number!"),
},
{
validator: (_, value) => {
if (this.state.phone !== "" && !Setting.isValidPhone(this.state.phone, this.state.countryCode)) {
this.setState({validPhone: false});
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));
}
this.setState({validPhone: true});
return Promise.resolve();
},
},
]}
>
<Input
style={{width: "65%"}}
onChange={e => this.setState({phone: e.target.value})}
/>
</Form.Item>
</Input.Group>
</Form.Item>
<Form.Item
name="phoneCode"

View File

@ -0,0 +1,58 @@
// Copyright 2023 The Casdoor 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 {Modal} from "antd";
import {ExclamationCircleFilled} from "@ant-design/icons";
import i18next from "i18next";
import * as Conf from "../Conf";
import * as Setting from "../Setting";
const {confirm} = Modal;
const {fetch: originalFetch} = window;
const demoModeCallback = (res) => {
res.json().then(data => {
if (Setting.isResponseDenied(data)) {
confirm({
title: i18next.t("general:This is a read-only demo site!"),
icon: <ExclamationCircleFilled />,
content: i18next.t("general:Go to writable demo site?"),
okText: i18next.t("user:OK"),
cancelText: i18next.t("general:Cancel"),
onOk() {
Setting.openLink(`https://demo.casdoor.com${location.pathname}${location.search}?username=built-in/admin&password=123`);
},
onCancel() {},
});
}
});
};
const requestFilters = [];
const responseFilters = [];
if (Conf.IsDemoMode) {
responseFilters.push(demoModeCallback);
}
window.fetch = async(url, option = {}) => {
requestFilters.forEach(filter => filter(url, option));
return new Promise((resolve, reject) => {
originalFetch(url, option).then(res => {
responseFilters.forEach(filter => filter(res.clone()));
resolve(res);
});
});
};

View File

@ -16,7 +16,7 @@ import * as Setting from "../Setting";
export function getLdaps(owner) {
return fetch(`${Setting.ServerUrl}/api/get-ldaps?owner=${owner}`, {
method: "POST",
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
@ -26,7 +26,7 @@ export function getLdaps(owner) {
export function getLdap(id) {
return fetch(`${Setting.ServerUrl}/api/get-ldap?id=${id}`, {
method: "POST",
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),

View File

@ -0,0 +1,58 @@
// Copyright 2023 The Casdoor 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 {Select} from "antd";
import * as Setting from "../Setting";
import React from "react";
const {Option} = Select;
export default function PhoneNumberInput(props) {
const {onChange, style, showSearch} = props;
const value = props.value ?? "CN";
const countryCodes = props.countryCodes ?? [];
const handleOnChange = (e) => {
onChange?.(e);
};
return (
<Select
virtual={false}
style={style}
value={value}
dropdownMatchSelectWidth={false}
optionLabelProp={"label"}
showSearch={showSearch}
onChange={handleOnChange}
filterOption={(input, option) =>
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
}
>
{
Setting.getCountriesData(countryCodes).map((country) => (
<Option key={country.code} value={country.code} label={`+${country.phone}`} >
<div style={{display: "flex", justifyContent: "space-between"}}>
<div>
{Setting.countryFlag(country)}
{`${country.name}`}
</div>
{`+${country.phone}`}
</div>
</Option>
))
}
</Select>
);
}

View File

@ -1,3 +1,17 @@
// Copyright 2022 The Casdoor 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 {Col, Row} from "antd";
import * as Setting from "../Setting";

View File

@ -13,26 +13,28 @@
// limitations under the License.
import i18n from "i18next";
import zh from "./locales/zh/data.json";
import en from "./locales/en/data.json";
import zh from "./locales/zh/data.json";
import es from "./locales/es/data.json";
import fr from "./locales/fr/data.json";
import de from "./locales/de/data.json";
import ja from "./locales/ja/data.json";
import ko from "./locales/ko/data.json";
import ru from "./locales/ru/data.json";
import ja from "./locales/ja/data.json";
import es from "./locales/es/data.json";
import vi from "./locales/vi/data.json";
import * as Conf from "./Conf";
import {initReactI18next} from "react-i18next";
const resources = {
en: en,
es: es,
zh: zh,
es: es,
fr: fr,
de: de,
ja: ja,
ko: ko,
ru: ru,
ja: ja,
vi: vi,
};
function initLanguage() {
@ -43,17 +45,20 @@ function initLanguage() {
} else {
const userLanguage = navigator.language;
switch (userLanguage) {
case "en":
language = "en";
break;
case "en-US":
language = "en";
break;
case "zh-CN":
language = "zh";
break;
case "zh":
language = "zh";
break;
case "en":
language = "en";
break;
case "en-US":
language = "en";
case "es":
language = "es";
break;
case "fr":
language = "fr";
@ -61,14 +66,17 @@ function initLanguage() {
case "de":
language = "de";
break;
case "ja":
language = "ja";
break;
case "ko":
language = "ko";
break;
case "ru":
language = "ru";
break;
case "ja":
language = "ja";
case "vi":
language = "vi";
break;
default:
language = Conf.DefaultLanguage;

View File

@ -22,6 +22,7 @@ import "./App.less";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import {BrowserRouter} from "react-router-dom";
import "./backend/FetchFilter";
const container = document.getElementById("root");

View File

@ -5,45 +5,45 @@
"Sign Up": "Registrieren"
},
"adapter": {
"Edit Adapter": "Edit Adapter",
"Failed to sync policies": "Failed to sync policies",
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Policies - Tooltip",
"Repeated policy rules": "Repeated policy rules",
"Sync": "Sync",
"Sync policies successfully": "Sync policies successfully"
"Edit Adapter": "Adapter bearbeiten",
"Failed to sync policies": "Fehler beim Synchronisieren der Richtlinien",
"New Adapter": "Neuer Adapter",
"Policies": "Richtlinien",
"Policies - Tooltip": "Richtlinien - Tooltip",
"Repeated policy rules": "Wiederholte Regeln der Richtlinien",
"Sync": "Synchronisation",
"Sync policies successfully": "Richtlinien erfolgreich synchronisiert"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL",
"Always": "Immer",
"Auto signin": "Automatische Anmeldung",
"Auto signin - Tooltip": "Automatische Anmeldung - Tooltip",
"Background URL": "Hintergrund-URL",
"Background URL - Tooltip": "Hintergrund-URL - Kurzinfo",
"Center": "Zentriert",
"Copy SAML metadata URL": "SAML-Metadaten URL kopieren",
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Copy signin page URL": "URL der Anmeldeseite kopieren",
"Copy signup page URL": "URL der Registrierungsseite kopieren",
"Edit Application": "Anwendung bearbeiten",
"Enable SAML compress": "Enable SAML compress",
"Enable SAML compress - Tooltip": "Enable SAML compress - Tooltip",
"Enable WebAuthn signin": "Enable WebAuthn signin",
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable SAML compress": "SAML-Komprimierung aktivieren",
"Enable SAML compress - Tooltip": "SAML-Komprimierung aktivieren - Tooltip",
"Enable WebAuthn signin": "WebAuthn Anmeldung aktivieren",
"Enable WebAuthn signin - Tooltip": "WebAuthn Anmeldung aktivieren - Tooltip",
"Enable code signin": "Code-Anmeldung aktivieren",
"Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable side panel": "Seitenmenü aktivieren",
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
"Enable signup": "Anmeldung aktivieren",
"Enable signup - Tooltip": "Whether to allow users to sign up",
"Failed to sign in": "Failed to sign in",
"Failed to sign in": "Anmeldung fehlgeschlagen",
"File uploaded successfully": "Datei erfolgreich hochgeladen",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip",
"Form CSS": "Formular-CSS",
"Form CSS - Edit": "Formular-CSS - Bearbeiten",
"Form CSS - Tooltip": "Formular-CSS - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types",
@ -181,6 +181,8 @@
"First name": "First name",
"Forget URL": "URL vergessen",
"Forget URL - Tooltip": "Unique string-style identifier",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Zuhause",
"Home - Tooltip": "Application homepage",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Telefon",
"Phone - Tooltip": "Phone",
"Phone prefix": "Telefonpräfix",
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Vorschau",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Products",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Zeitstempel",
"Tokens": "Token",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "Oder melden Sie sich mit einem anderen Konto an",
"Password": "Passwort",
"Password - Tooltip": "Passwort - Tooltip",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Bitte gib deinen Code ein!",
"Please input your password!": "Bitte geben Sie Ihr Passwort ein!",
"Please input your password, at least 6 characters!": "Bitte geben Sie Ihr Passwort ein, mindestens 6 Zeichen!",
"Please input your username, Email or phone!": "Bitte geben Sie Ihren Benutzernamen, E-Mail oder Telefon ein!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Anmelden",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Bitte geben Sie Ihre Telefonnummer ein!",
"Please input your real name!": "Bitte geben Sie Ihren persönlichen Namen ein!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Bitte wählen Sie Ihr Land/Ihre Region!",
"Terms of Use": "Nutzungsbedingungen",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code gesendet",
"Country code": "Country code",
"Country/Region": "Land/Region",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Benutzer bearbeiten",

View File

@ -181,6 +181,8 @@
"First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Forget URL - Tooltip",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Home",
"Home - Tooltip": "Home - Tooltip",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Phone",
"Phone - Tooltip": "Phone - Tooltip",
"Phone prefix": "Phone prefix",
"Phone prefix - Tooltip": "Phone prefix - Tooltip",
"Preview": "Preview",
"Preview - Tooltip": "Preview - Tooltip",
"Products": "Products",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "Or sign in with another account",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Please input your code!",
"Please input your password!": "Please input your password!",
"Please input your password, at least 6 characters!": "Please input your password, at least 6 characters!",
"Please input your username, Email or phone!": "Please input your username, Email or phone!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Sign In",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Please input your phone number!",
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Terms of Use": "Terms of Use",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code Sent",
"Country code": "Country code",
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region - Tooltip",
"Edit User": "Edit User",

View File

@ -181,6 +181,8 @@
"First name": "Nombre",
"Forget URL": "URL de olvido",
"Forget URL - Tooltip": "URL de olvido - Tooltip",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Inicio",
"Home - Tooltip": "Inicio - Tooltip",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permisos - Tooltip",
"Phone": "Teléfono",
"Phone - Tooltip": "Teléfono - Tooltip",
"Phone prefix": "Prefijo teléfonico",
"Phone prefix - Tooltip": "Prefijo teléfonico - Tooltip",
"Preview": "Previsualizar",
"Preview - Tooltip": "Previsualizar - Tooltip",
"Products": "Productos",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sincronizador",
"Syncers": "Sincronizadores",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "O inicia sesión con otra cuenta",
"Password": "Contraseña",
"Password - Tooltip": "Contraseña - Tooltip",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "¡Por favor ingrese su código!",
"Please input your password!": "¡Por favor ingrese su contraseña!",
"Please input your password, at least 6 characters!": "Su contraseña debe contener al menos 6 caracteres.",
"Please input your username, Email or phone!": "¡Ingrese su nombre de usuario, correo electrónico o teléfono!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Iniciar de sesión",
"Sign in with WebAuthn": "Iniciar de sesión con WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Por favor, ingrese su apellido!",
"Please input your phone number!": "Por favor, ingrese su número teléfonico!",
"Please input your real name!": "Por favor, ingrese un nombre real!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Por favor, seleccione su pais/region!",
"Terms of Use": "Términos de Uso",
"The input is not invoice Tax ID!": "El valor ingresado no es un número de identificación fiscal de factura!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Fallo la verificación del Captcha",
"Captcha Verify Success": "Captcha verificado con éxito",
"Code Sent": "Código enviado",
"Country code": "Country code",
"Country/Region": "Pais/Región",
"Country/Region - Tooltip": "Pais/Región - Tooltip",
"Edit User": "Editar usuario",

View File

@ -181,6 +181,8 @@
"First name": "First name",
"Forget URL": "Oublier l'URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Domicile",
"Home - Tooltip": "Application homepage",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Téléphone",
"Phone - Tooltip": "Phone",
"Phone prefix": "Préfixe du téléphone",
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Aperçu",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Products",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Synchronisateurs",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Horodatage",
"Tokens": "Jetons",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "Ou connectez-vous avec un autre compte",
"Password": "Mot de passe",
"Password - Tooltip": "Mot de passe - Info-bulle",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Veuillez saisir votre code !",
"Please input your password!": "Veuillez saisir votre mot de passe !",
"Please input your password, at least 6 characters!": "Veuillez entrer votre mot de passe, au moins 6 caractères !",
"Please input your username, Email or phone!": "Veuillez entrer votre nom d'utilisateur, votre e-mail ou votre téléphone!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Se connecter",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Veuillez entrer votre numéro de téléphone!",
"Please input your real name!": "Veuillez entrer votre nom personnel !",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Veuillez sélectionner votre pays/région!",
"Terms of Use": "Conditions d'utilisation",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code envoyé",
"Country code": "Country code",
"Country/Region": "Pays/Région",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Editer l'utilisateur",

View File

@ -181,6 +181,8 @@
"First name": "First name",
"Forget URL": "URLを忘れた",
"Forget URL - Tooltip": "Unique string-style identifier",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "ホーム",
"Home - Tooltip": "Application homepage",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "電話番号",
"Phone - Tooltip": "Phone",
"Phone prefix": "電話プレフィクス",
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "プレビュー",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Products",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "システム情報",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "タイムスタンプ",
"Tokens": "トークン",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "または別のアカウントでサインイン",
"Password": "パスワード",
"Password - Tooltip": "パスワード → ツールチップ",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "コードを入力してください!",
"Please input your password!": "パスワードを入力してください!",
"Please input your password, at least 6 characters!": "6文字以上でパスワードを入力してください",
"Please input your username, Email or phone!": "ユーザー名、メールアドレスまたは電話番号を入力してください。",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "サインイン",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "電話番号を入力してください!",
"Please input your real name!": "個人名を入力してください!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "あなたの国/地域を選択してください!",
"Terms of Use": "利用規約",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "コードを送信しました",
"Country code": "Country code",
"Country/Region": "国/地域",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "ユーザーを編集",

View File

@ -181,6 +181,8 @@
"First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Home",
"Home - Tooltip": "Application homepage",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Phone",
"Phone - Tooltip": "Phone",
"Phone prefix": "Phone prefix",
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Preview",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Products",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "Or sign in with another account",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Please input your code!",
"Please input your password!": "Please input your password!",
"Please input your password, at least 6 characters!": "Please input your password, at least 6 characters!",
"Please input your username, Email or phone!": "Please input your username, Email or phone!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Sign In",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Please input your phone number!",
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Terms of Use": "Terms of Use",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code Sent",
"Country code": "Country code",
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Edit User",

View File

@ -181,6 +181,8 @@
"First name": "Имя",
"Forget URL": "Забыть URL",
"Forget URL - Tooltip": "Unique string-style identifier",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Домашний",
"Home - Tooltip": "Application homepage",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Телефон",
"Phone - Tooltip": "Phone",
"Phone prefix": "Префикс телефона",
"Phone prefix - Tooltip": "Mobile phone number prefix, used to distinguish countries or regions",
"Preview": "Предпросмотр",
"Preview - Tooltip": "The form in which the password is stored in the database",
"Products": "Продукты",
@ -251,10 +251,13 @@
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Синхронизаторы",
"SysInfo": "Информация о системе",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Отметка времени",
"Tokens": "Жетоны",
"URL": "URL",
@ -309,10 +312,10 @@
"Or sign in with another account": "Или войти с помощью другой учетной записи",
"Password": "Пароль",
"Password - Tooltip": "Пароль - Подсказка",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Пожалуйста, введите ваш код!",
"Please input your password!": "Пожалуйста, введите ваш пароль!",
"Please input your password, at least 6 characters!": "Пожалуйста, введите ваш пароль, по крайней мере 6 символов!",
"Please input your username, Email or phone!": "Пожалуйста, введите ваше имя пользователя, адрес электронной почты или телефон!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Войти",
"Sign in with WebAuthn": "Sign in with WebAuthn",
@ -638,6 +641,7 @@
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Пожалуйста, введите ваш номер телефона!",
"Please input your real name!": "Пожалуйста, введите ваше личное имя!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Пожалуйста, выберите вашу страну/регион!",
"Terms of Use": "Условия использования",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Код отправлен",
"Country code": "Country code",
"Country/Region": "Страна/регион",
"Country/Region - Tooltip": "Country/Region",
"Edit User": "Изменить пользователя",

View File

@ -0,0 +1,805 @@
{
"account": {
"Logout": "Logout",
"My Account": "My Account",
"Sign Up": "Sign Up"
},
"adapter": {
"Edit Adapter": "Edit Adapter",
"Failed to sync policies": "Failed to sync policies",
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Policies - Tooltip",
"Repeated policy rules": "Repeated policy rules",
"Sync": "Sync",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL",
"Edit Application": "Edit Application",
"Enable SAML compress": "Enable SAML compress",
"Enable SAML compress - Tooltip": "Enable SAML compress - Tooltip",
"Enable WebAuthn signin": "Enable WebAuthn signin",
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",
"Enable signup - Tooltip": "Enable signup - Tooltip",
"Failed to sign in": "Failed to sign in",
"File uploaded successfully": "File uploaded successfully",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"New Application": "New Application",
"None": "None",
"Password ON": "Password ON",
"Password ON - Tooltip": "Password ON - Tooltip",
"Please input your application!": "Please input your application!",
"Please input your organization!": "Please input your organization!",
"Please select a HTML file": "Please select a HTML file",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Redirect URL": "Redirect URL",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
"Redirect URLs": "Redirect URLs",
"Redirect URLs - Tooltip": "Redirect URLs - Tooltip",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Right": "Right",
"Rule": "Rule",
"SAML Reply URL": "SAML Reply URL",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Sign Up Error": "Sign Up Error",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Signin session",
"Signup items": "Signup items",
"Signup items - Tooltip": "Signup items - Tooltip",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Token expire - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {
"Bit size": "Bit size",
"Bit size - Tooltip": "Bit size - Tooltip",
"Certificate": "Certificate",
"Certificate - Tooltip": "Certificate - Tooltip",
"Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
"Copy certificate": "Copy certificate",
"Copy private key": "Copy private key",
"Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download certificate": "Download certificate",
"Download private key": "Download private key",
"Edit Cert": "Edit Cert",
"Expire in years": "Expire in years",
"Expire in years - Tooltip": "Expire in years - Tooltip",
"New Cert": "New Cert",
"Private key": "Private key",
"Private key - Tooltip": "Private key - Tooltip",
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
"Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip"
},
"code": {
"Code You Received": "Code You Received",
"Email code": "Email code",
"Empty Code": "Empty Code",
"Enter your code": "Enter your code",
"Phone code": "Phone code",
"Please input your phone verification code!": "Please input your phone verification code!",
"Please input your verification code!": "Please input your verification code!",
"Send Code": "Send Code",
"Sending Code": "Sending Code",
"Submit and complete": "Submit and complete"
},
"forget": {
"Account": "Account",
"Change Password": "Change Password",
"Choose email or phone": "Choose email or phone",
"Confirm": "Confirm",
"Next Step": "Next Step",
"Password": "Password",
"Please input your username!": "Please input your username!",
"Reset": "Reset",
"Retrieve password": "Retrieve password",
"Unknown forget type: ": "Unknown forget type: ",
"Verify": "Verify"
},
"general": {
"Action": "Action",
"Adapter": "Adapter",
"Adapter - Tooltip": "Adapter - Tooltip",
"Adapters": "Adapters",
"Add": "Add",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "Affiliation URL - Tooltip",
"Application": "Application",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Avatar": "Avatar",
"Avatar - Tooltip": "Avatar - Tooltip",
"Back Home": "Back Home",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
"Cert - Tooltip": "Cert - Tooltip",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Created time": "Created time",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar",
"Default avatar - Tooltip": "Default avatar - Tooltip",
"Delete": "Delete",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Display name": "Display name",
"Display name - Tooltip": "Display name - Tooltip",
"Down": "Down",
"Edit": "Edit",
"Email": "Email",
"Email - Tooltip": "Email - Tooltip",
"Failed to add": "Failed to add",
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to save": "Failed to save",
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon - Tooltip",
"First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Forget URL - Tooltip",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Home",
"Home - Tooltip": "Home - Tooltip",
"ID": "ID",
"ID - Tooltip": "ID - Tooltip",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Is enabled - Tooltip",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
"Languages": "Languages",
"Languages - Tooltip": "Languages - Tooltip",
"Last name": "Last name",
"Logo": "Logo",
"Logo - Tooltip": "Logo - Tooltip",
"Master password": "Master password",
"Master password - Tooltip": "Master password - Tooltip",
"Menu": "Menu",
"Method": "Method",
"Model": "Model",
"Model - Tooltip": "Model - Tooltip",
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Name - Tooltip",
"OAuth providers": "OAuth providers",
"Organization": "Organization",
"Organization - Tooltip": "Organization - Tooltip",
"Organizations": "Organizations",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Password salt": "Password salt",
"Password salt - Tooltip": "Password salt - Tooltip",
"Password type": "Password type",
"Password type - Tooltip": "Password type - Tooltip",
"Payments": "Payments",
"Permissions": "Permissions",
"Permissions - Tooltip": "Permissions - Tooltip",
"Phone": "Phone",
"Phone - Tooltip": "Phone - Tooltip",
"Preview": "Preview",
"Preview - Tooltip": "Preview - Tooltip",
"Products": "Products",
"Provider": "Provider",
"Provider - Tooltip": "Provider - Tooltip",
"Providers": "Providers",
"Providers - Tooltip": "Providers - Tooltip",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Roles": "Roles",
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Signin URL",
"Signin URL - Tooltip": "Signin URL - Tooltip",
"Signup URL": "Signup URL",
"Signup URL - Tooltip": "Signup URL - Tooltip",
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country code": "Supported country code",
"Supported country code - Tooltip": "Supported country code - Tooltip",
"Swagger": "Swagger",
"Sync": "Sync",
"Syncers": "Syncers",
"SysInfo": "SysInfo",
"This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
"URL - Tooltip": "URL - Tooltip",
"Up": "Up",
"User": "User",
"User - Tooltip": "User - Tooltip",
"User containers": "User containers",
"User type": "User type",
"User type - Tooltip": "User type - Tooltip",
"Users": "Users",
"Users under all organizations": "Users under all organizations",
"Webhooks": "Webhooks",
"{total} in total": "{total} in total"
},
"ldap": {
"Address": "Address",
"Admin": "Admin",
"Admin - Tooltip": "Admin - Tooltip",
"Admin Password": "Admin Password",
"Admin Password - Tooltip": "Admin Password - Tooltip",
"Auto Sync": "Auto Sync",
"Auto Sync - Tooltip": "Auto Sync - Tooltip",
"Base DN": "Base DN",
"Base DN - Tooltip": "Base DN - Tooltip",
"CN": "CN",
"Edit LDAP": "Edit LDAP",
"Email": "Email",
"Group Id": "Group Id",
"ID": "ID",
"Last Sync": "Last Sync",
"Phone": "Phone",
"Server": "Server",
"Server Host": "Server Host",
"Server Host - Tooltip": "Server Host - Tooltip",
"Server Name": "Server Name",
"Server Name - Tooltip": "Server Name - Tooltip",
"Server Port": "Server Port",
"Server Port - Tooltip": "Server Port - Tooltip",
"Sync": "Sync",
"The Auto Sync option will sync all users to specify organization": "The Auto Sync option will sync all users to specify organization",
"UidNumber / Uid": "UidNumber / Uid"
},
"login": {
"Auto sign in": "Auto sign in",
"Continue with": "Continue with",
"Email or phone": "Email or phone",
"Forgot password?": "Forgot password?",
"Loading": "Loading",
"Logging out...": "Logging out...",
"No account?": "No account?",
"Or sign in with another account": "Or sign in with another account",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Please input your code!",
"Please input your password!": "Please input your password!",
"Please input your password, at least 6 characters!": "Please input your password, at least 6 characters!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Sign In",
"Sign in with WebAuthn": "Sign in with WebAuthn",
"Sign in with {type}": "Sign in with {type}",
"Signing in...": "Signing in...",
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
"The input is not valid Email or Phone!": "The input is not valid Email or Phone!",
"To access": "To access",
"Verification Code": "Verification Code",
"WebAuthn": "WebAuthn",
"sign up now": "sign up now",
"username, Email or phone": "username, Email or phone"
},
"model": {
"Edit Model": "Edit Model",
"Model text": "Model text",
"Model text - Tooltip": "Model text - Tooltip",
"New Model": "New Model"
},
"organization": {
"Account items": "Account items",
"Account items - Tooltip": "Account items - Tooltip",
"Default avatar": "Default avatar",
"Edit Organization": "Edit Organization",
"Favicon": "Favicon",
"Follow global theme": "Follow global theme",
"InitScore": "InitScore",
"Is profile public": "Is profile public",
"Is profile public - Tooltip": "Is profile public - Tooltip",
"Modify rule": "Modify rule",
"New Organization": "New Organization",
"Soft deletion": "Soft deletion",
"Soft deletion - Tooltip": "Soft deletion - Tooltip",
"Tags": "Tags",
"Tags - Tooltip": "Tags - Tooltip",
"The user's initScore - Tooltip": "The user's initScore - Tooltip",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
"Website URL - Tooltip": "Website URL - Tooltip"
},
"payment": {
"Confirm your invoice information": "Confirm your invoice information",
"Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
"Invoice actions": "Invoice actions",
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
"Invoice remark": "Invoice remark",
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
"Invoice tax ID": "Invoice tax ID",
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
"Invoice title": "Invoice title",
"Invoice title - Tooltip": "Invoice title - Tooltip",
"Invoice type": "Invoice type",
"Invoice type - Tooltip": "Invoice type - Tooltip",
"Issue Invoice": "Issue Invoice",
"Message": "Message",
"Message - Tooltip": "Message - Tooltip",
"New Payment": "New Payment",
"Organization": "Organization",
"Person Email": "Person Email",
"Person Email - Tooltip": "Person Email - Tooltip",
"Person ID card": "Person ID card",
"Person ID card - Tooltip": "Person ID card - Tooltip",
"Person name": "Person name",
"Person name - Tooltip": "Person name - Tooltip",
"Person phone": "Person phone",
"Person phone - Tooltip": "Person phone - Tooltip",
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
"Please pay the order first!": "Please pay the order first!",
"Price": "Price",
"Price - Tooltip": "Price - Tooltip",
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product - Tooltip",
"Result": "Result",
"Return to Website": "Return to Website",
"State": "State",
"State - Tooltip": "State - Tooltip",
"The payment has failed": "The payment has failed",
"The payment is still under processing": "The payment is still under processing",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"You have successfully completed the payment": "You have successfully completed the payment",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
"permission": {
"Actions": "Actions",
"Actions - Tooltip": "Actions - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Deny": "Deny",
"Edit Permission": "Edit Permission",
"Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Resource type",
"Resource type - Tooltip": "Resource type - Tooltip",
"Resources": "Resources",
"Resources - Tooltip": "Resources - Tooltip",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
"Buy": "Buy",
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Edit Product": "Edit Product",
"I have completed the payment": "I have completed the payment",
"Image": "Image",
"Image - Tooltip": "Image - Tooltip",
"New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip",
"Paypal": "Paypal",
"Placing order...": "Placing order...",
"Please provide your username in the remark": "Please provide your username in the remark",
"Please scan the QR code to pay": "Please scan the QR code to pay",
"Price": "Price",
"Price - Tooltip": "Price - Tooltip",
"Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip",
"Return URL": "Return URL",
"Return URL - Tooltip": "Return URL - Tooltip",
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip",
"Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
},
"provider": {
"Access key": "Access key",
"Access key - Tooltip": "Access key - Tooltip",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App ID - Tooltip",
"App key": "App key",
"App key - Tooltip": "App key - Tooltip",
"App secret": "App secret",
"AppSecret - Tooltip": "AppSecret - Tooltip",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Bucket - Tooltip",
"Can not parse Metadata": "Can not parse Metadata",
"Can signin": "Can signin",
"Can signup": "Can signup",
"Can unlink": "Can unlink",
"Category": "Category",
"Category - Tooltip": "Category - Tooltip",
"Channel No.": "Channel No.",
"Channel No. - Tooltip": "Channel No. - Tooltip",
"Client ID": "Client ID",
"Client ID - Tooltip": "Client ID - Tooltip",
"Client ID 2": "Client ID 2",
"Client ID 2 - Tooltip": "Client ID 2 - Tooltip",
"Client secret": "Client secret",
"Client secret - Tooltip": "Client secret - Tooltip",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "Client secret 2 - Tooltip",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Disable SSL - Tooltip",
"Domain": "Domain",
"Domain - Tooltip": "Domain - Tooltip",
"Edit Provider": "Edit Provider",
"Email Content": "Email Content",
"Email Content - Tooltip": "Email Content - Tooltip",
"Email Title": "Email Title",
"Email Title - Tooltip": "Email Title - Tooltip",
"Enable QR code": "Enable QR code",
"Enable QR code - Tooltip": "Enable QR code - Tooltip",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Host": "Host",
"Host - Tooltip": "Host - Tooltip",
"IdP": "IdP",
"IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - Tooltip",
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
"Metadata": "Metadata",
"Metadata - Tooltip": "Metadata - Tooltip",
"Method": "Method",
"Method - Tooltip": "Method - Tooltip",
"Name": "Name",
"New Provider": "New Provider",
"Parse": "Parse",
"Parse Metadata successfully": "Parse Metadata successfully",
"Path prefix": "Path prefix",
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Port - Tooltip",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "Provider URL - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID - Tooltip",
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account - Tooltip",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
"SP Entity ID": "SP Entity ID",
"Scene": "Scene",
"Scene - Tooltip": "Scene - Tooltip",
"Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip",
"Secret access key": "Secret access key",
"Secret key": "Secret key",
"Secret key - Tooltip": "Secret key - Tooltip",
"SecretAccessKey - Tooltip": "SecretAccessKey - Tooltip",
"Send Test Email": "Send Test Email",
"Sign Name": "Sign Name",
"Sign Name - Tooltip": "Sign Name - Tooltip",
"Sign request": "Sign request",
"Sign request - Tooltip": "Sign request - Tooltip",
"Signin HTML": "Signin HTML",
"Signin HTML - Edit": "Signin HTML - Edit",
"Signin HTML - Tooltip": "Signin HTML - Tooltip",
"Signup HTML": "Signup HTML",
"Signup HTML - Edit": "Signup HTML - Edit",
"Signup HTML - Tooltip": "Signup HTML - Tooltip",
"Site key": "Site key",
"Site key - Tooltip": "Site key - Tooltip",
"Sub type": "Sub type",
"Sub type - Tooltip": "Sub type - Tooltip",
"Template Code": "Template Code",
"Template Code - Tooltip": "Template Code - Tooltip",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of Use - Tooltip",
"Test Connection": "Test Connection",
"Test Email": "Test Email",
"Test Email - Tooltip": "Test Email - Tooltip",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
"Visible": "Visible",
"admin (share)": "admin (share)",
"alertType": "alertType"
},
"record": {
"Is Triggered": "Is Triggered"
},
"resource": {
"Application": "Application",
"Copy Link": "Copy Link",
"File name": "File name",
"File size": "File size",
"Format": "Format",
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
"Parent": "Parent",
"Tag": "Tag",
"Type": "Type",
"Upload a file...": "Upload a file...",
"User": "User"
},
"role": {
"Edit Role": "Edit Role",
"New Role": "New Role",
"Sub domains": "Sub domains",
"Sub domains - Tooltip": "Sub domains - Tooltip",
"Sub roles": "Sub roles",
"Sub roles - Tooltip": "Sub roles - Tooltip",
"Sub users": "Sub users",
"Sub users - Tooltip": "Sub users - Tooltip"
},
"signup": {
"Accept": "Accept",
"Agreement": "Agreement",
"Confirm": "Confirm",
"Decline": "Decline",
"Have account?": "Have account?",
"Please accept the agreement!": "Please accept the agreement!",
"Please click the below button to sign in": "Please click the below button to sign in",
"Please confirm your password!": "Please confirm your password!",
"Please input the correct ID card number!": "Please input the correct ID card number!",
"Please input your Email!": "Please input your Email!",
"Please input your ID card number!": "Please input your ID card number!",
"Please input your address!": "Please input your address!",
"Please input your affiliation!": "Please input your affiliation!",
"Please input your display name!": "Please input your display name!",
"Please input your first name!": "Please input your first name!",
"Please input your last name!": "Please input your last name!",
"Please input your phone number!": "Please input your phone number!",
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Terms of Use": "Terms of Use",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!",
"The input is not valid Phone!": "The input is not valid Phone!",
"Username": "Username",
"Username - Tooltip": "Username - Tooltip",
"Your account has been created!": "Your account has been created!",
"Your confirmed password is inconsistent with the password!": "Your confirmed password is inconsistent with the password!",
"sign in now": "sign in now"
},
"syncer": {
"Affiliation table": "Affiliation table",
"Affiliation table - Tooltip": "Affiliation table - Tooltip",
"Avatar base URL": "Avatar base URL",
"Avatar base URL - Tooltip": "Avatar base URL - Tooltip",
"Casdoor column": "Casdoor column",
"Column name": "Column name",
"Column type": "Column type",
"Database": "Database",
"Database - Tooltip": "Database - Tooltip",
"Database type": "Database type",
"Database type - Tooltip": "Database type - Tooltip",
"Edit Syncer": "Edit Syncer",
"Error text": "Error text",
"Error text - Tooltip": "Error text - Tooltip",
"Is hashed": "Is hashed",
"New Syncer": "New Syncer",
"Sync interval": "Sync interval",
"Sync interval - Tooltip": "Sync interval - Tooltip",
"Table": "Table",
"Table - Tooltip": "Table - Tooltip",
"Table columns": "Table columns",
"Table columns - Tooltip": "Table columns - Tooltip",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key - Tooltip"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"theme": {
"Blossom": "Blossom",
"Border radius": "Border radius",
"Compact": "Compact",
"Customize theme": "Customize theme",
"Dark": "Dark",
"Default": "Default",
"Document": "Document",
"Is compact": "Is compact",
"Primary color": "Primary color",
"Theme": "Theme",
"Theme - Tooltip": "Theme - Tooltip"
},
"token": {
"Access token": "Access token",
"Authorization code": "Authorization code",
"Edit Token": "Edit Token",
"Expires in": "Expires in",
"New Token": "New Token",
"Scope": "Scope",
"Token type": "Token type"
},
"user": {
"\" + destType + \" reset": "\" + destType + \" reset",
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "3rd-party logins - Tooltip",
"Address": "Address",
"Address - Tooltip": "Address - Tooltip",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Affiliation - Tooltip",
"Bio": "Bio",
"Bio - Tooltip": "Bio - Tooltip",
"Cancel": "Cancel",
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code Sent",
"Country code": "Country code",
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region - Tooltip",
"Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Homepage",
"Homepage - Tooltip": "Homepage - Tooltip",
"ID card": "ID card",
"Input your email": "Input your email",
"Input your phone number": "Input your phone number",
"Is admin": "Is admin",
"Is admin - Tooltip": "Is admin - Tooltip",
"Is deleted": "Is deleted",
"Is deleted - Tooltip": "Is deleted - Tooltip",
"Is forbidden": "Is forbidden",
"Is forbidden - Tooltip": "Is forbidden - Tooltip",
"Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is global admin - Tooltip",
"Keys": "Keys",
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
"New phone": "New phone",
"OK": "OK",
"Old Password": "Old Password",
"Password": "Password",
"Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Reset Phone...",
"Select a photo...": "Select a photo...",
"Set Password": "Set Password",
"Set new profile picture": "Set new profile picture",
"Set password...": "Set password...",
"Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip",
"Title": "Title",
"Title - Tooltip": "Title - Tooltip",
"Two passwords you typed do not match.": "Two passwords you typed do not match.",
"Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Upload a photo",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "input password"
},
"webhook": {
"Content type": "Content type",
"Content type - Tooltip": "Content type - Tooltip",
"Edit Webhook": "Edit Webhook",
"Events": "Events",
"Events - Tooltip": "Events - Tooltip",
"Headers": "Headers",
"Headers - Tooltip": "Headers - Tooltip",
"Is user extended": "Is user extended",
"Is user extended - Tooltip": "Is user extended - Tooltip",
"Method": "Method",
"Method - Tooltip": "Method - Tooltip",
"Name": "Name",
"New Webhook": "New Webhook",
"URL": "URL",
"URL - Tooltip": "URL - Tooltip",
"Value": "Value"
}
}

View File

@ -181,6 +181,8 @@
"First name": "名字",
"Forget URL": "忘记密码URL",
"Forget URL - Tooltip": "忘记密码URL",
"Found some texts still not translated? Please help us translate at": "发现有些文字尚未翻译?请移步这里帮我们翻译:",
"Go to writable demo site?": "跳转至可写演示站点?",
"Home": "首页",
"Home - Tooltip": "应用的首页",
"ID": "ID",
@ -218,8 +220,6 @@
"Permissions - Tooltip": "权限",
"Phone": "手机号",
"Phone - Tooltip": "手机号",
"Phone prefix": "手机号前缀",
"Phone prefix - Tooltip": "移动电话号码前缀,用于区分国家或地区",
"Preview": "预览",
"Preview - Tooltip": "预览",
"Products": "商品",
@ -251,10 +251,13 @@
"Successfully added": "添加成功",
"Successfully deleted": "删除成功",
"Successfully saved": "保存成功",
"Supported country code": "支持的国家代码",
"Supported country code - Tooltip": "支持发送短信的国家 - Tooltip",
"Swagger": "API文档",
"Sync": "同步",
"Syncers": "同步器",
"SysInfo": "系统信息",
"This is a read-only demo site!": "这是一个只读演示站点!",
"Timestamp": "时间戳",
"Tokens": "令牌",
"URL": "链接",
@ -309,10 +312,10 @@
"Or sign in with another account": "或者,登录其他账号",
"Password": "密码",
"Password - Tooltip": "密码",
"Please input your Email or Phone!": "请输入您的Email或手机号!",
"Please input your code!": "请输入您的验证码!",
"Please input your password!": "请输入您的密码!",
"Please input your password, at least 6 characters!": "请输入您的密码不少于6位",
"Please input your username, Email or phone!": "请输入您的用户名、Email或手机号",
"Redirecting, please wait.": "正在跳转, 请稍等.",
"Sign In": "登录",
"Sign in with WebAuthn": "WebAuthn登录",
@ -638,6 +641,7 @@
"Please input your last name!": "请输入您的姓氏!",
"Please input your phone number!": "请输入您的手机号码!",
"Please input your real name!": "请输入您的姓名!",
"Please select your country code!": "请选择国家代码!",
"Please select your country/region!": "请选择您的国家/地区",
"Terms of Use": "《用户协议》",
"The input is not invoice Tax ID!": "您输入的纳税人识别号有误!",
@ -724,6 +728,7 @@
"Captcha Verify Failed": "验证码校验失败",
"Captcha Verify Success": "验证码校验成功",
"Code Sent": "验证码已发送",
"Country code": "国家代码",
"Country/Region": "国家/地区",
"Country/Region - Tooltip": "国家/地区",
"Edit User": "编辑用户",

View File

@ -7829,6 +7829,11 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
libphonenumber-js@^1.10.19:
version "1.10.20"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.20.tgz#03c310adf83381eeceb4bd6830442fd14e64964d"
integrity sha512-kQovlKNdLcVzerbTPmJ+Fx4R+7/pYXmPDIllHjg7IxL4X6MsMG7jaT5opfYrBok0uqkByVif//JUR8e11l/V7w==
lilconfig@2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25"