Compare commits

...

31 Commits

Author SHA1 Message Date
fd61b963d5 feat: [SAML + long button crash] fix Disabling "Enable password" leads to white app page when SAML provider is active (#1691)
* fix: saml long button crush

* fix: sue svg

* Update Setting.js

* Update LoginButton.js

* Update ProviderButton.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-03-26 23:56:43 +08:00
a8937d3046 feat: refactor agreement modal and create folders to classify components (#1686)
* refactor: refactor agreement modal and create folders to classify components

* fix: i18

* fix: i18

* fix: i18n
2023-03-26 18:44:47 +08:00
32b05047dc Update system info API swagger 2023-03-26 10:19:59 +08:00
117ee509cf feat: fix name format in application login: GetClientCredentialsToken() (#1639) 2023-03-25 23:02:08 +08:00
daf3d374b5 fix: adjust error result position (#1683) 2023-03-25 09:36:23 +08:00
337ee2faef feat: fix the bug that autoSignin generates two callback AJAX calls (#1682) 2023-03-24 23:17:54 +08:00
989fec72bf Add /api/user API for Flarum's FoF Passport plugin 2023-03-24 01:02:04 +08:00
76eb606335 Support AAD tenant auth URL 2023-03-23 22:37:53 +08:00
c6146a9149 feat: fix bug that login by saml provider can not find application (#1676) 2023-03-23 21:38:33 +08:00
f191488338 feat: support popup mode in OAuth (#1668)
* feat: support `popup` mode in OAuth

* feat: sendMessage when popup window closed

* fix: fix param name error
2023-03-22 00:15:17 +08:00
da7336a9a4 feat: support getting versionInfo in docker (#1673)
* feat: support getting versionInfo in docker

* fix: fix build

* fix: fix build

* fix: fix system
2023-03-21 20:46:17 +08:00
b3806070ac Finish Go i18n texts 2023-03-20 01:10:48 +08:00
c7b9a77b4a Remove Go i18n duplicates 2023-03-19 22:09:19 +08:00
4c4ad8320d feat: optimize getEnforcer, only load filtered polices (#1669) 2023-03-19 20:13:48 +08:00
89d29c2519 Add "empty" to i18n 2023-03-19 20:13:07 +08:00
98f962f818 Support i18n of id 2023-03-19 19:56:56 +08:00
5989c4ff34 Remove i18n duplicates 2023-03-19 01:03:12 +08:00
1de76e4da9 Finish i18n of vi 2023-03-19 00:13:16 +08:00
4e62c255b3 Finish i18n of ru 2023-03-18 23:05:37 +08:00
7ee54cb089 Finish i18n of ko 2023-03-18 22:24:06 +08:00
bea03635a1 Finish i18n of ja 2023-03-18 21:46:17 +08:00
2bc4cd9337 Fix i18n issue 2023-03-18 20:42:02 +08:00
ed9ceaefe1 Finish i18n of de 2023-03-18 20:31:31 +08:00
3dec2fdc18 Fix i18n issue 2023-03-18 19:05:58 +08:00
31e4813df9 Finish i18n of fr 2023-03-18 18:54:05 +08:00
263f804ab8 Finish i18n of es 2023-03-18 17:24:31 +08:00
d383de256b Refactor to parseAllWords() 2023-03-18 14:16:15 +08:00
28d24cc913 Improve i18n 2023-03-18 12:18:42 +08:00
bd5c706317 Fix i18n code 2023-03-18 00:17:07 +08:00
fba0021e22 Fix enableLinkWithEmail UI 2023-03-17 23:47:13 +08:00
aba17e2bc1 feat: revert PR: "fix: disable cookie for static files" (#1666)
This reverts commit 312412ffe4.
2023-03-17 17:27:12 +08:00
115 changed files with 7525 additions and 6745 deletions

View File

@ -132,7 +132,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: -1
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
@ -192,6 +192,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with: with:
context: .
target: STANDARD target: STANDARD
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
@ -201,6 +202,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with: with:
context: .
target: ALLINONE target: ALLINONE
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true

View File

@ -9,7 +9,7 @@ FROM golang:1.17.5 AS BACK
WORKDIR /go/src/casdoor WORKDIR /go/src/casdoor
COPY . . COPY . .
RUN ./build.sh RUN ./build.sh
RUN go test -v -run TestGetVersionInfo ./util/system_test.go ./util/system.go > version_info.txt
FROM alpine:latest AS STANDARD FROM alpine:latest AS STANDARD
LABEL MAINTAINER="https://casdoor.org/" LABEL MAINTAINER="https://casdoor.org/"
@ -34,6 +34,7 @@ WORKDIR /
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
ENTRYPOINT ["/server"] ENTRYPOINT ["/server"]
@ -61,6 +62,7 @@ COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
COPY --from=FRONT /web/build ./web/build COPY --from=FRONT /web/build ./web/build
ENTRYPOINT ["/bin/bash"] ENTRYPOINT ["/bin/bash"]

View File

@ -87,6 +87,7 @@ p, *, *, POST, /api/logout, *, *
p, *, *, GET, /api/logout, *, * p, *, *, GET, /api/logout, *, *
p, *, *, GET, /api/get-account, *, * p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, * p, *, *, GET, /api/userinfo, *, *
p, *, *, GET, /api/user, *, *
p, *, *, POST, /api/webhook, *, * p, *, *, POST, /api/webhook, *, *
p, *, *, GET, /api/get-webhook-event, *, * p, *, *, GET, /api/get-webhook-event, *, *
p, *, *, *, /api/login/oauth, *, * p, *, *, *, /api/login/oauth, *, *

View File

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

View File

@ -50,6 +50,7 @@ type RequestForm struct {
Region string `json:"region"` Region string `json:"region"`
Application string `json:"application"` Application string `json:"application"`
ClientId string `json:"clientId"`
Provider string `json:"provider"` Provider string `json:"provider"`
Code string `json:"code"` Code string `json:"code"`
State string `json:"state"` State string `json:"state"`
@ -103,7 +104,7 @@ type Captcha struct {
// @router /signup [post] // @router /signup [post]
func (c *ApiController) Signup() { func (c *ApiController) Signup() {
if c.GetSessionUsername() != "" { if c.GetSessionUsername() != "" {
c.ResponseError(c.T("account:Please sign out first before signing up"), c.GetSessionUsername()) c.ResponseError(c.T("account:Please sign out first"), c.GetSessionUsername())
return return
} }
@ -211,7 +212,7 @@ func (c *ApiController) Signup() {
affected := object.AddUser(user) affected := object.AddUser(user)
if !affected { if !affected {
c.ResponseError(c.T("account:Invalid information"), util.StructToJson(user)) c.ResponseError(c.T("account:Failed to add user"), util.StructToJson(user))
return return
} }
@ -369,6 +370,43 @@ func (c *ApiController) GetUserinfo() {
c.ServeJSON() c.ServeJSON()
} }
// GetUserinfo2
// LaravelResponse
// @Title UserInfo2
// @Tag Account API
// @Description return Laravel compatible user information according to OAuth 2.0
// @Success 200 {object} LaravelResponse The Response object
// @router /user [get]
func (c *ApiController) GetUserinfo2() {
user, ok := c.RequireSignedInUser()
if !ok {
return
}
// this API is used by "Api URL" of Flarum's FoF Passport plugin
// https://github.com/FriendsOfFlarum/passport
type LaravelResponse struct {
Id string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
EmailVerifiedAt string `json:"email_verified_at"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
response := LaravelResponse{
Id: user.Id,
Name: user.Name,
Email: user.Email,
EmailVerifiedAt: user.CreatedTime,
CreatedAt: user.CreatedTime,
UpdatedAt: user.UpdatedTime,
}
c.Data["json"] = response
c.ServeJSON()
}
// GetCaptcha ... // GetCaptcha ...
// @Tag Login API // @Tag Login API
// @Title GetCaptcha // @Title GetCaptcha

View File

@ -237,7 +237,7 @@ func (c *ApiController) Login() {
if form.Username != "" { if form.Username != "" {
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" { if c.GetSessionUsername() != "" {
c.ResponseError(c.T("account:Please sign out first before signing in"), c.GetSessionUsername()) c.ResponseError(c.T("account:Please sign out first"), c.GetSessionUsername())
return return
} }
} }
@ -308,7 +308,7 @@ func (c *ApiController) Login() {
} }
if !isHuman { if !isHuman {
c.ResponseError(c.T("auth:Turing test failed.")) c.ResponseError(c.T("verification:Turing test failed."))
return return
} }
} }
@ -334,7 +334,13 @@ func (c *ApiController) Login() {
util.SafeGoroutine(func() { object.AddRecord(record) }) util.SafeGoroutine(func() { object.AddRecord(record) })
} }
} else if form.Provider != "" { } else if form.Provider != "" {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) var application *object.Application
if form.ClientId != "" {
application = object.GetApplicationByClientId(form.ClientId)
} else {
application = object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
}
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application)) c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return return
@ -368,7 +374,7 @@ func (c *ApiController) Login() {
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl) idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
if idProvider == nil { if idProvider == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The provider type: %s is not supported"), provider.Type)) c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type))
return return
} }
@ -410,7 +416,7 @@ func (c *ApiController) Login() {
// Sign in via OAuth (want to sign up but already have account) // Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden { if user.IsForbidden {
c.ResponseError(c.T("auth:The user is forbidden to sign in, please contact the administrator")) c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
} }
resp = c.HandleLoggedIn(application, user, &form) resp = c.HandleLoggedIn(application, user, &form)

View File

@ -195,7 +195,7 @@ func (c *ApiController) UploadResource() {
} }
} }
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer) fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer, c.GetAcceptLanguage())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -21,9 +21,8 @@ import (
// GetSystemInfo // GetSystemInfo
// @Title GetSystemInfo // @Title GetSystemInfo
// @Tag System API // @Tag System API
// @Description get user's system info // @Description get system info like CPU and memory usage
// @Param id query string true "The id ( owner/name ) of the user" // @Success 200 {object} util.SystemInfo The Response object
// @Success 200 {object} object.SystemInfo The Response object
// @router /get-system-info [get] // @router /get-system-info [get]
func (c *ApiController) GetSystemInfo() { func (c *ApiController) GetSystemInfo() {
_, ok := c.RequireAdmin() _, ok := c.RequireAdmin()
@ -43,14 +42,19 @@ func (c *ApiController) GetSystemInfo() {
// GetVersionInfo // GetVersionInfo
// @Title GetVersionInfo // @Title GetVersionInfo
// @Tag System API // @Tag System API
// @Description get local git repo's latest release version info // @Description get version info like Casdoor release version and commit ID
// @Success 200 {string} local latest version hash of Casdoor // @Success 200 {object} util.VersionInfo The Response object
// @router /get-version-info [get] // @router /get-version-info [get]
func (c *ApiController) GetVersionInfo() { func (c *ApiController) GetVersionInfo() {
versionInfo, err := util.GetVersionInfo() versionInfo, err := util.GetVersionInfo()
if err != nil {
c.ResponseError(err.Error()) if versionInfo.Version == "" {
return versionInfo, err = util.GetVersionInfoFromFile()
if err != nil {
c.ResponseError(err.Error())
return
}
} }
c.ResponseOk(versionInfo) c.ResponseOk(versionInfo)

View File

@ -96,7 +96,7 @@ func (c *ApiController) SendVerificationCode() {
application := object.GetApplication(applicationId) application := object.GetApplication(applicationId)
organization := object.GetOrganization(util.GetId(application.Owner, application.Organization)) organization := object.GetOrganization(util.GetId(application.Owner, application.Organization))
if organization == nil { if organization == nil {
c.ResponseError(c.T("verification:Organization does not exist")) c.ResponseError(c.T("check:Organization does not exist"))
return return
} }
@ -112,7 +112,7 @@ func (c *ApiController) SendVerificationCode() {
switch destType { switch destType {
case "email": case "email":
if !util.IsEmailValid(dest) { if !util.IsEmailValid(dest) {
c.ResponseError(c.T("verification:Email is invalid")) c.ResponseError(c.T("check:Email is invalid"))
return return
} }

View File

@ -99,7 +99,7 @@ func getAllFilePathsInFolder(folder string, fileSuffix string) []string {
return res return res
} }
func parseEnData(category string) *I18nData { func parseAllWords(category string) *I18nData {
var paths []string var paths []string
if category == "backend" { if category == "backend" {
paths = getAllFilePathsInFolder("../", ".go") paths = getAllFilePathsInFolder("../", ".go")
@ -136,3 +136,11 @@ func parseEnData(category string) *I18nData {
return &data return &data
} }
func applyToOtherLanguage(category string, language string, newData *I18nData) {
oldData := readI18nFile(category, language)
println(oldData)
applyData(newData, oldData)
writeI18nFile(category, language, newData)
}

View File

@ -17,42 +17,34 @@
package i18n package i18n
import ( import "testing"
"testing"
)
func applyToOtherLanguage(category string, language string, i18nData *I18nData) {
newData := readI18nFile(category, language)
println(newData)
applyData(i18nData, newData)
writeI18nFile(category, language, i18nData)
}
func TestGenerateI18nFrontend(t *testing.T) { func TestGenerateI18nFrontend(t *testing.T) {
enData := parseEnData("frontend") data := parseAllWords("frontend")
writeI18nFile("frontend", "en", enData)
applyToOtherLanguage("frontend", "zh", enData) applyToOtherLanguage("frontend", "en", data)
applyToOtherLanguage("frontend", "es", enData) applyToOtherLanguage("frontend", "zh", data)
applyToOtherLanguage("frontend", "fr", enData) applyToOtherLanguage("frontend", "es", data)
applyToOtherLanguage("frontend", "de", enData) applyToOtherLanguage("frontend", "fr", data)
applyToOtherLanguage("frontend", "ja", enData) applyToOtherLanguage("frontend", "de", data)
applyToOtherLanguage("frontend", "ko", enData) applyToOtherLanguage("frontend", "id", data)
applyToOtherLanguage("frontend", "ru", enData) applyToOtherLanguage("frontend", "ja", data)
applyToOtherLanguage("frontend", "vi", enData) applyToOtherLanguage("frontend", "ko", data)
applyToOtherLanguage("frontend", "ru", data)
applyToOtherLanguage("frontend", "vi", data)
} }
func TestGenerateI18nBackend(t *testing.T) { func TestGenerateI18nBackend(t *testing.T) {
enData := parseEnData("backend") data := parseAllWords("backend")
writeI18nFile("backend", "en", enData)
applyToOtherLanguage("backend", "zh", enData) applyToOtherLanguage("backend", "en", data)
applyToOtherLanguage("backend", "es", enData) applyToOtherLanguage("backend", "zh", data)
applyToOtherLanguage("backend", "fr", enData) applyToOtherLanguage("backend", "es", data)
applyToOtherLanguage("backend", "de", enData) applyToOtherLanguage("backend", "fr", data)
applyToOtherLanguage("backend", "ja", enData) applyToOtherLanguage("backend", "de", data)
applyToOtherLanguage("backend", "ko", enData) applyToOtherLanguage("backend", "id", data)
applyToOtherLanguage("backend", "ru", enData) applyToOtherLanguage("backend", "ja", data)
applyToOtherLanguage("backend", "vi", enData) applyToOtherLanguage("backend", "ko", data)
applyToOtherLanguage("backend", "ru", data)
applyToOtherLanguage("backend", "vi", data)
} }

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "Konnte den Benutzer nicht hinzufügen",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "Init-Score konnte nicht abgerufen werden, Fehler: %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "Bitte melden Sie sich zuerst ab",
"Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "Die Anwendung erlaubt es nicht, sich für ein neues Konto anzumelden"
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Die Challenge-Methode sollte S256 sein",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Es konnte kein Benutzer erstellt werden, da die Benutzerinformationen ungültig sind: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Konnte nicht anmelden: %s",
"Invalid token": "Invalid token", "Invalid token": "Ungültiges Token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "Erwarteter Zustand: %s, aber erhalten: %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 via %%s, please use another way to sign up": "Das Konto für den Anbieter: %s und Benutzernamen: %s (%s) existiert nicht und darf nicht über %%s als neues Konto erstellt werden. Bitte nutzen Sie einen anderen Weg, um sich anzumelden",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) existiert nicht und es ist nicht erlaubt, ein neues Konto anzumelden. Bitte wenden Sie sich an Ihren 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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) ist bereits mit einem anderen Konto verknüpft: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "Die Anwendung: %s existiert nicht",
"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 login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
"The provider type: %s is not supported": "The provider type: %s is not supported", "The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "Nicht autorisierte Operation",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "DisplayName ist kein gültiger Vorname",
"Email already exists": "Email already exists", "Email already exists": "E-Mail existiert bereits",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "E-Mail darf nicht leer sein",
"Email is invalid": "Email is invalid", "Email is invalid": "E-Mail ist ungültig",
"Empty username.": "Empty username.", "Empty username.": "Leerer Benutzername.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "Vorname darf nicht leer sein",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "Nachname darf nicht leer sein",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organisation existiert nicht",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "Das Passwort muss mindestens 6 Zeichen enthalten",
"Phone already exists": "Phone already exists", "Phone already exists": "Telefon existiert bereits",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Das Telefon darf nicht leer sein",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Die Telefonnummer ist ungültig",
"Session outdated, please login again": "Session outdated, please login again", "Session outdated, please login again": "Sitzung abgelaufen, bitte erneut anmelden",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "Dem Benutzer ist der Zugang verboten, bitte kontaktieren Sie den 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.", "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.": "Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden.",
"Username already exists": "Username already exists", "Username already exists": "Benutzername existiert bereits",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Benutzername darf keine Leerzeichen enthalten",
"Username cannot start with a digit": "Username cannot start with a digit", "Username cannot start with a digit": "Benutzername darf nicht mit einer Ziffer beginnen",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Benutzername ist zu lang (das Maximum beträgt 39 Zeichen).",
"Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein", "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", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Sie haben zu oft das falsche Passwort oder den falschen Code eingegeben. Bitte warten Sie %d Minuten und versuchen Sie es erneut",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone", "Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "Das Passwort oder der Code ist falsch. Du hast noch %d Versuche übrig",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "Nicht unterstützter Passworttyp: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Fehlender Parameter",
"Please login first": "Please login first", "Please login first": "Bitte zuerst einloggen",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "Unterstütze captchaProvider nicht:"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Es gibt einen LDAP-Server"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Bitte verlinken Sie zuerst",
"This application has no providers": "This application has no providers", "This application has no providers": "Diese Anwendung hat keine Anbieter",
"This application has no providers of type": "This application has no providers of type", "This application has no providers of type": "Diese Anwendung hat keine Anbieter des Typs",
"This provider can't be unlinked": "This provider can't be unlinked", "This provider can't be unlinked": "Dieser Anbieter kann nicht entkoppelt werden",
"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 are not the global admin, you can't unlink other users": "Sie sind nicht der globale Administrator, Sie können keine anderen Benutzer trennen",
"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" "You can't unlink yourself, you are not a member of any application": "Du kannst dich nicht abmelden, du bist kein Mitglied einer Anwendung"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "Nur der Administrator kann das %s ändern.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "Das %s ist unveränderlich.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unbekannte Änderungsregel %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Ungültige Anwendungs-ID",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "Der Anbieter %s existiert nicht"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "Benutzer ist null für Tag: Avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Benutzername oder vollständiger Dateipfad sind leer: Benutzername = %s, vollständiger Dateipfad = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "Anwendung %s wurde nicht gefunden"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "Der Anbieter %s ist keine Kategorie von SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "Leere Parameter für Email-Formular: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "Ungültige E-Mail-Empfänger: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "Ungültige Telefonempfänger: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "Der Objektschlüssel %s ist nicht erlaubt",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "Der Anbieter-Typ %s wird nicht unterstützt"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "Leerer clientId oder clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Ungültige Anwendung oder falsches clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Ungültige 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", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Weiterleitungs-URI: %s ist nicht in der Liste erlaubter Weiterleitungs-URIs vorhanden",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "Token nicht gefunden, ungültiger Zugriffs-Token"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Anzeigename darf nicht leer sein",
"New password cannot contain blank space.": "New password cannot contain blank space.", "New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
"New password must have at least 6 characters": "New password must have at least 6 characters" "New password must have at least 6 characters": "Das neue Passwort muss mindestens 6 Zeichen haben"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Fehler beim Importieren von Benutzern"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "Es wurde keine Anwendung für die Benutzer-ID gefunden: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "Kein Anbieter für die Kategorie %s gefunden für die Anwendung: %s",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "Der Anbieter: %s wurde nicht gefunden"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Der Code wurde noch nicht versendet!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "Ungültiger Captcha-Anbieter.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "Die Telefonnummer ist in Ihrer Region %s ungültig",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "Turing-Test fehlgeschlagen.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "Nicht in der Lage, die E-Mail-Änderungsregel zu erhalten.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "Nicht in der Lage, die Telefon-Änderungsregel zu erhalten.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "Unbekannter Typ",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "Falscher Parameter",
"Unknown type": "Unknown type", "Wrong verification code!": "Falscher Bestätigungscode!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Es wurden keine Anmeldeinformationen für diesen Benutzer gefunden",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "Bitte rufen Sie zuerst WebAuthnSigninBegin auf"
} }
} }

View File

@ -1,9 +1,8 @@
{ {
"account": { "account": {
"Failed to add user": "Failed to add user",
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Please sign out first": "Please sign out first",
"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" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
@ -17,10 +16,7 @@
"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 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 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 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 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", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
}, },
@ -126,9 +122,7 @@
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "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", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "No se pudo agregar el usuario",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "Error al obtener el puntaje de inicio, error: %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "Por favor, cierra sesión primero",
"Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "La aplicación no permite registrarse con una cuenta nueva"
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "El método de desafío debe ser S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "No se pudo crear el usuario, la información del usuario es inválida: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "No se ha podido iniciar sesión en: %s",
"Invalid token": "Invalid token", "Invalid token": "Token inválido",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "Estado esperado: %s, pero se obtuvo: %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 via %%s, please use another way to sign up": "La cuenta para el proveedor: %s y nombre de usuario: %s (%s) no existe y no está permitido registrarse como una cuenta nueva a través de %%s, por favor use otro método para registrarse",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "La cuenta para el proveedor: %s y el nombre de usuario: %s (%s) no existe y no se permite registrarse como una nueva cuenta, por favor contacte a su soporte de TI",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "La cuenta para proveedor: %s y nombre de usuario: %s (%s) ya está vinculada a otra cuenta: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "La aplicación: %s no existe",
"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 login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
"The provider type: %s is not supported": "The provider type: %s is not supported", "The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "Operación no autorizada",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Los servicios %s y %s no coinciden"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Afiliación no puede estar en blanco",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "El nombre de pantalla no es un nombre real válido",
"Email already exists": "Email already exists", "Email already exists": "El correo electrónico ya existe",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "El correo electrónico no puede estar vacío",
"Email is invalid": "Email is invalid", "Email is invalid": "El correo electrónico no es válido",
"Empty username.": "Empty username.", "Empty username.": "Nombre de usuario vacío.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "El nombre no puede estar en blanco",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "El apellido no puede estar en blanco",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "La organización no existe",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "La contraseña debe tener al menos 6 caracteres",
"Phone already exists": "Phone already exists", "Phone already exists": "El teléfono ya existe",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Teléfono no puede estar vacío",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "El número de teléfono no es válido",
"Session outdated, please login again": "Session outdated, please login again", "Session outdated, please login again": "Sesión expirada, por favor vuelva a iniciar sesión",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "El usuario no está autorizado a iniciar sesión, por favor contacte al administrador",
"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.", "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.": "El nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones o subrayados consecutivos, y no puede comenzar ni terminar con un guión o subrayado.",
"Username already exists": "Username already exists", "Username already exists": "El nombre de usuario ya existe",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Nombre de usuario no puede contener espacios en blanco",
"Username cannot start with a digit": "Username cannot start with a digit", "Username cannot start with a digit": "El nombre de usuario no puede empezar con un dígito",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "El nombre de usuario es demasiado largo (el máximo es de 39 caracteres).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Nombre de usuario debe tener al menos 2 caracteres",
"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", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Has ingresado la contraseña o código incorrecto demasiadas veces, por favor espera %d minutos e intenta de nuevo",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone", "Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "Contraseña o código incorrecto, tienes %d intentos restantes",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "Tipo de contraseña no compatible: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Parámetro faltante",
"Please login first": "Please login first", "Please login first": "Por favor, inicia sesión primero",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "El usuario: %s no existe",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "No apoyo a captchaProvider"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "El servidor LDAP existe"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Por favor, enlaza primero",
"This application has no providers": "This application has no providers", "This application has no providers": "Esta aplicación no tiene proveedores",
"This application has no providers of type": "This application has no providers of type", "This application has no providers of type": "Esta aplicación no tiene proveedores del tipo",
"This provider can't be unlinked": "This provider can't be unlinked", "This provider can't be unlinked": "Este proveedor no se puede desvincular",
"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 are not the global admin, you can't unlink other users": "No eres el administrador global, no puedes desvincular a otros usuarios",
"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" "You can't unlink yourself, you are not a member of any application": "No puedes desvincularte, no eres miembro de ninguna aplicación"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "Solo el administrador puede modificar los %s.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "El %s es inmutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Regla de modificación desconocida %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Identificación de aplicación no válida",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "El proveedor: %s no existe"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "El usuario es nulo para la etiqueta: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nombre de usuario o ruta completa de archivo está vacío: nombre de usuario = %s, ruta completa de archivo = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "Aplicación %s no encontrada"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "La categoría del proveedor %s no es SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "Parámetros vacíos para el formulario de correo electrónico: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "Receptores de correo electrónico no válidos: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "Receptores de teléfono no válidos: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "El objectKey: %s no está permitido",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "El tipo de proveedor: %s no es compatible"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "ClienteId o clienteSecret vacío",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Solicitud inválida o clientSecret incorrecto",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Identificador de cliente no válido",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "El URI de redirección: %s no existe en la lista de URI de redirección permitidos",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "Token no encontrado, accessToken inválido"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
"New password cannot contain blank space.": "New password cannot contain blank space.", "New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
"New password must have at least 6 characters": "New password must have at least 6 characters" "New password must have at least 6 characters": "La nueva contraseña debe tener al menos 6 caracteres"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Error al importar usuarios"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "No se encuentra ninguna aplicación para el Id de usuario: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "No se encuentra un proveedor para la categoría: %s para la aplicación: %s",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "El proveedor: %s no se encuentra"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "¡El código aún no ha sido enviado!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "Proveedor de captcha no válido.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "El número de teléfono es inválido en tu región %s",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "El test de Turing falló.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "No se puede obtener la regla de modificación de correo electrónico.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "No se pudo obtener la regla de modificación del teléfono.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "Tipo desconocido",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "Parámetro incorrecto",
"Unknown type": "Unknown type", "Wrong verification code!": "¡Código de verificación incorrecto!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "No se encontraron credenciales para este usuario",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "Por favor, llama primero a WebAuthnSigninBegin"
} }
} }

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "Échec d'ajout d'utilisateur",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "Obtention du score initiale échouée, erreur : %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "Veuillez vous déconnecter en premier",
"Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "L'application ne permet pas de créer un nouveau compte"
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "La méthode de défi doit être S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Échec de la création de l'utilisateur, les informations utilisateur sont invalides : %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Échec de la connexion : %s",
"Invalid token": "Invalid token", "Invalid token": "Jeton invalide",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "État attendu : %s, mais obtenu : %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 via %%s, please use another way to sign up": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire en tant que nouveau compte via %%s, veuillez utiliser une autre méthode pour vous inscrire",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire comme nouveau compte, veuillez contacter votre support informatique",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Le compte du fournisseur : %s et le nom d'utilisateur : %s (%s) sont déjà liés à un autre compte : %s (%s)",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "L'application : %s n'existe pas",
"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 login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
"The provider type: %s is not supported": "The provider type: %s is not supported", "The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "Opération non autorisée",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Affiliation ne peut pas être vide",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "DisplayName n'est pas un nom réel valide",
"Email already exists": "Email already exists", "Email already exists": "E-mail déjà existant",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "L'e-mail ne peut pas être vide",
"Email is invalid": "Email is invalid", "Email is invalid": "L'adresse e-mail est invalide",
"Empty username.": "Empty username.", "Empty username.": "Nom d'utilisateur vide.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "Le nom de famille ne peut pas être vide",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "L'organisation n'existe pas",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "Le mot de passe doit comporter au moins 6 caractères",
"Phone already exists": "Phone already exists", "Phone already exists": "Le téléphone existe déjà",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Le téléphone ne peut pas être vide",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Le numéro de téléphone est invalide",
"Session outdated, please login again": "Session outdated, please login again", "Session outdated, please login again": "Session expirée, veuillez vous connecter à nouveau",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "L'utilisateur est interdit de se connecter, veuillez contacter l'administrateur",
"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.", "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.": "Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, des traits soulignés ou des tirets, ne peut pas avoir de tirets ou de traits soulignés consécutifs et ne peut pas commencer ou se terminer par un tiret ou un trait souligné.",
"Username already exists": "Username already exists", "Username already exists": "Nom d'utilisateur existe déjà",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Nom d'utilisateur ne peut pas contenir d'espaces blancs",
"Username cannot start with a digit": "Username cannot start with a digit", "Username cannot start with a digit": "Nom d'utilisateur ne peut pas commencer par un chiffre",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Nom d'utilisateur est trop long (maximum de 39 caractères).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Le nom d'utilisateur doit comporter au moins 2 caractères",
"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", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Vous avez entré le mauvais mot de passe ou code plusieurs fois, veuillez attendre %d minutes et réessayer",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone", "Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "Le mot de passe ou le code est incorrect, il vous reste %d chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "Type de mot de passe non pris en charge : %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Paramètre manquant",
"Please login first": "Please login first", "Please login first": "Veuillez d'abord vous connecter",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "Ne pas prendre en charge la captchaProvider"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Le serveur LDAP existe"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Veuillez d'abord faire le lien",
"This application has no providers": "This application has no providers", "This application has no providers": "Cette application n'a aucun fournisseur",
"This application has no providers of type": "This application has no providers of type", "This application has no providers of type": "Cette application ne dispose d'aucun fournisseur de type",
"This provider can't be unlinked": "This provider can't be unlinked", "This provider can't be unlinked": "Ce fournisseur ne peut pas être dissocié",
"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 are not the global admin, you can't unlink other users": "Vous n'êtes pas l'administrateur global, vous ne pouvez pas détacher d'autres utilisateurs",
"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" "You can't unlink yourself, you are not a member of any application": "Vous ne pouvez pas vous désolidariser, car vous n'êtes membre d'aucune application"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "Seul l'administrateur peut modifier le %s.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "Le %s est immuable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Règle de modification inconnue %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Identifiant d'application invalide",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "Le fournisseur : %s n'existe pas"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "L'utilisateur est nul pour la balise : avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nom d'utilisateur ou chemin complet du fichier est vide : nom d'utilisateur = %s, chemin complet du fichier = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "L'application %s n'a pas été trouvée"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "La catégorie du fournisseur %s n'est pas SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "Paramètres vides pour emailForm : %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "Destinataires d'e-mail invalides : %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "Destinataires de téléphone invalide : %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "La clé d'objet : %s n'est pas autorisée",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "Le type de fournisseur : %s n'est pas pris en charge"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "clientId ou clientSecret vide",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Application invalide ou clientSecret incorrect",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Identifiant de client invalide",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI de redirection: %s n'existe pas dans la liste des URI de redirection autorisés",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "Jeton non trouvé, accessToken invalide"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
"New password cannot contain blank space.": "New password cannot contain blank space.", "New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
"New password must have at least 6 characters": "New password must have at least 6 characters" "New password must have at least 6 characters": "Le nouveau mot de passe doit comporter au moins 6 caractères"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Échec de l'importation des utilisateurs"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "Aucune application n'a été trouvée pour l'identifiant d'utilisateur : %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "Aucun fournisseur pour la catégorie: %s n'est trouvé pour l'application: %s",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "Le fournisseur : %s n'a pas été trouvé"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Le code n'a pas encore été envoyé !",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "Fournisseur de captcha invalide.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "Le numéro de téléphone n'est pas valide dans votre région %s",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "Le test de Turing a échoué.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "Incapable d'obtenir la règle de modification de courriel.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "Impossible d'obtenir la règle de modification de téléphone.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "Type inconnu",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "Mauvais paramètre",
"Unknown type": "Unknown type", "Wrong verification code!": "Mauvais code de vérification !",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Aucune référence trouvée pour cet utilisateur",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "Veuillez d'abord appeler WebAuthnSigninBegin"
} }
} }

140
i18n/locales/id/data.json Normal file
View File

@ -0,0 +1,140 @@
{
"account": {
"Failed to add user": "Gagal menambahkan pengguna",
"Get init score failed, error: %w": "Gagal mendapatkan nilai init, kesalahan: %w",
"Please sign out first": "Silakan keluar terlebih dahulu",
"The application does not allow to sign up new account": "Aplikasi tidak memperbolehkan untuk mendaftar akun baru"
},
"auth": {
"Challenge method should be S256": "Metode tantangan harus S256",
"Failed to create user, user information is invalid: %s": "Gagal membuat pengguna, informasi pengguna tidak valid: %s",
"Failed to login in: %s": "Gagal masuk: %s",
"Invalid token": "Token tidak valid",
"State expected: %s, but got: %s": "Diharapkan: %s, tapi diperoleh: %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": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru melalui %%s, silakan gunakan cara lain untuk mendaftar",
"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": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru, silakan hubungi dukungan IT Anda",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk provider: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
"The application: %s does not exist": "Aplikasi: %s tidak ada",
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak sah",
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s"
},
"cas": {
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
},
"check": {
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
"DisplayName is not valid real name": "DisplayName bukanlah nama asli yang valid",
"Email already exists": "Email sudah ada",
"Email cannot be empty": "Email tidak boleh kosong",
"Email is invalid": "Email tidak valid",
"Empty username.": "Nama pengguna kosong.",
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
"Ldap user name or password incorrect": "Nama pengguna atau kata sandi Ldap salah",
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server ldap Anda",
"Organization does not exist": "Organisasi tidak ada",
"Password must have at least 6 characters": "Kata sandi harus memiliki minimal 6 karakter",
"Phone already exists": "Telepon sudah ada",
"Phone cannot be empty": "Telepon tidak boleh kosong",
"Phone number is invalid": "Nomor telepon tidak valid",
"Session outdated, please login again": "Sesi kedaluwarsa, silakan masuk lagi",
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang masuk, silakan hubungi 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.": "Nama pengguna hanya bisa menggunakan karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki dua tanda hubung atau garis bawah berurutan, dan tidak boleh diawali atau diakhiri dengan tanda hubung atau garis bawah.",
"Username already exists": "Nama pengguna sudah ada",
"Username cannot be an email address": "Username tidak bisa menjadi alamat email",
"Username cannot contain white spaces": "Username tidak boleh mengandung spasi",
"Username cannot start with a digit": "Username tidak dapat dimulai dengan angka",
"Username is too long (maximum is 39 characters).": "Nama pengguna terlalu panjang (maksimum 39 karakter).",
"Username must have at least 2 characters": "Nama pengguna harus memiliki setidaknya 2 karakter",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda telah memasukkan kata sandi atau kode yang salah terlalu banyak kali, mohon tunggu selama %d menit dan coba lagi",
"Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon",
"password or code is incorrect, you have %d remaining chances": "Kata sandi atau kode salah, Anda memiliki %d kesempatan tersisa",
"unsupported password type: %s": "jenis sandi tidak didukung: %s"
},
"general": {
"Missing parameter": "Parameter hilang",
"Please login first": "Silahkan login terlebih dahulu",
"The user: %s doesn't exist": "Pengguna: %s tidak ada",
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:"
},
"ldap": {
"Ldap server exist": "Server ldap ada"
},
"link": {
"Please link first": "Tolong tautkan terlebih dahulu",
"This application has no providers": "Aplikasi ini tidak memiliki penyedia",
"This application has no providers of type": " Aplikasi ini tidak memiliki penyedia tipe ",
"This provider can't be unlinked": "Pemberi layanan ini tidak dapat dipisahkan",
"You are not the global admin, you can't unlink other users": "Anda bukan admin global, Anda tidak dapat memutuskan tautan pengguna lain",
"You can't unlink yourself, you are not a member of any application": "Anda tidak dapat memutuskan tautan diri sendiri, karena Anda bukan anggota dari aplikasi apa pun"
},
"organization": {
"Only admin can modify the %s.": "Hanya admin yang dapat memodifikasi %s.",
"The %s is immutable.": "%s tidak dapat diubah.",
"Unknown modify rule %s.": "Aturan modifikasi tidak diketahui %s."
},
"provider": {
"Invalid application id": "ID aplikasi tidak valid",
"the provider: %s does not exist": "provider: %s tidak ada"
},
"resource": {
"User is nil for tag: avatar": "Pengguna kosong untuk tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nama pengguna atau path lengkap file kosong: nama_pengguna = %s, path_lengkap_file = %s"
},
"saml": {
"Application %s not found": "Aplikasi %s tidak ditemukan"
},
"saml_sp": {
"provider %s's category is not SAML": "kategori penyedia %s bukan SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Parameter kosong untuk emailForm: %v",
"Invalid Email receivers: %s": "Penerima email tidak valid: %s",
"Invalid phone receivers: %s": "Penerima telepon tidak valid: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Kunci objek: %s tidak diizinkan",
"The provider type: %s is not supported": "Jenis penyedia: %s tidak didukung"
},
"token": {
"Empty clientId or clientSecret": "Kosong clientId atau clientSecret",
"Grant_type: %s is not supported in this application": "Jenis grant (grant_type) %s tidak didukung dalam aplikasi ini",
"Invalid application or wrong clientSecret": "Aplikasi tidak valid atau clientSecret salah",
"Invalid client_id": "Invalid client_id = ID klien tidak valid",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI pengalihan: %s tidak ada dalam daftar URI Pengalihan yang diizinkan",
"Token not found, invalid accessToken": "Token tidak ditemukan, accessToken tidak valid"
},
"user": {
"Display name cannot be empty": "Nama tampilan tidak boleh kosong",
"New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong.",
"New password must have at least 6 characters": "Kata sandi baru harus memiliki setidaknya 6 karakter"
},
"user_upload": {
"Failed to import users": "Gagal mengimpor pengguna"
},
"util": {
"No application is found for userId: %s": "Tidak ditemukan aplikasi untuk userId: %s",
"No provider for category: %s is found for application: %s": "Tidak ditemukan penyedia untuk kategori: %s untuk aplikasi: %s",
"The provider: %s is not found": "Penyedia: %s tidak ditemukan"
},
"verification": {
"Code has not been sent yet!": "Kode belum dikirimkan!",
"Invalid captcha provider.": "Penyedia captcha tidak valid.",
"Phone number is invalid in your region %s": "Nomor telepon tidak valid di wilayah anda %s",
"Turing test failed.": "Tes Turing gagal.",
"Unable to get the email modify rule.": "Tidak dapat memperoleh aturan modifikasi email.",
"Unable to get the phone modify rule.": "Tidak dapat memodifikasi aturan telepon.",
"Unknown type": "Tipe tidak diketahui",
"Wrong parameter": "Parameter yang salah",
"Wrong verification code!": "Kode verifikasi salah!",
"You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!",
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
},
"webauthn": {
"Found no credentials for this user": "Tidak ditemukan kredensial untuk pengguna ini",
"Please call WebAuthnSigninBegin first": "Harap panggil WebAuthnSigninBegin terlebih dahulu"
}
}

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "ユーザーの追加に失敗しました",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "イニットスコアの取得に失敗しました。エラー:%w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "最初にサインアウトしてください",
"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": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "チャレンジメソッドはS256である必要があります",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "ユーザーの作成に失敗しました。ユーザー情報が無効です:%s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "ログインできませんでした:%s",
"Invalid token": "Invalid token", "Invalid token": "無効なトークン",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "期待される状態: %s、実際には%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 via %%s, please use another way to sign up": "プロバイダーのアカウント:%s とユーザー名:%s%sが存在せず、新しいアカウントを %%s 経由でサインアップすることはできません。他の方法でサインアップしてください",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "プロバイダー名:%sとユーザー名%s%sのアカウントは存在しません。新しいアカウントとしてサインアップすることはできません。 ITサポートに連絡してください",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "プロバイダのアカウント:%s とユーザー名:%s (%s) は既に別のアカウント:%s (%s) にリンクされています",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "アプリケーション: %sは存在しません",
"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 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": "プロバイダー:%sはアプリケーションでは有効化されていません",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "不正操作",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "サービス%sと%sは一致しません"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "所属は空白にできません",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "表示名は空白にできません",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "表示名は有効な実名ではありません",
"Email already exists": "Email already exists", "Email already exists": "メールは既に存在します",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "メールが空白にできません",
"Email is invalid": "Email is invalid", "Email is invalid": "電子メールは無効です",
"Empty username.": "Empty username.", "Empty username.": "空のユーザー名。",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "ファーストネームは空白にできません",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "姓は空白にできません",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "組織は存在しません",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "パスワードは少なくとも6つの文字が必要です",
"Phone already exists": "Phone already exists", "Phone already exists": "電話はすでに存在しています",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "電話は空っぽにできません",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "電話番号が無効です",
"Session outdated, please login again": "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 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.", "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 already exists": "ユーザー名はすでに存在しています",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "ユーザ名にはスペースを含めることはできません",
"Username cannot start with a digit": "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 is too long (maximum is 39 characters).": "ユーザー名が長すぎます最大39文字",
"Username must have at least 2 characters": "Username must have at least 2 characters", "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": "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": "あなたは間違ったパスワードまたはコードを何度も入力しました。%d 分間待ってから再度お試しください",
"Your region is not allow to signup by phone": "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", "password or code is incorrect, you have %d remaining chances": "パスワードまたはコードが間違っています。あと%d回の試行機会があります",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "サポートされていないパスワードタイプ:%s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "不足しているパラメーター",
"Please login first": "Please login first", "Please login first": "最初にログインしてください",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "そのユーザー:%sは存在しません",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "captchaProviderをサポートしないでください"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "LDAPサーバーは存在します"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "最初にリンクしてください",
"This application has no providers": "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 application has no providers of type": "「このアプリケーションには、タイプのプロバイダーがありません」と翻訳されます",
"This provider can't be unlinked": "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 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" "You can't unlink yourself, you are not a member of any application": "あなたは自分自身をアンリンクすることはできません、あなたはどのアプリケーションのメンバーでもありません"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "管理者のみが%sを変更できます。",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "%sは不変です。",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "未知の変更ルール%s"
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "アプリケーションIDが無効です",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "プロバイダー%sは存在しません"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "ユーザーはタグ「アバター」に対してnilです",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "ユーザー名または完全なファイルパスが空です:ユーザー名 = %s、完全なファイルパス = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "アプリケーション%sは見つかりません"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "プロバイダ %s のカテゴリはSAMLではありません"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "EmailFormの空のパラメーターv",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "無効な電子メール受信者:%s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "電話受信者が無効です:%s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "オブジェクトキー %s は許可されていません",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "プロバイダータイプ:%sはサポートされていません"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "クライアントIDまたはクライアントシークレットが空です",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "grant_type%sはこのアプリケーションでサポートされていません",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "無効なアプリケーションまたは誤ったクライアントシークレットです",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "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", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "リダイレクトURI%sは許可されたリダイレクトURIリストに存在しません",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "トークンが見つかりません。無効なアクセストークンです"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "表示名は空にできません",
"New password cannot contain blank space.": "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" "New password must have at least 6 characters": "新しいパスワードは少なくとも6文字必要です"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "ユーザーのインポートに失敗しました"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "ユーザーIDに対するアプリケーションが見つかりません %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "アプリケーション:%sのカテゴリ%sのプロバイダが見つかりません",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "プロバイダー:%sが見つかりません"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "まだコードが送信されていません!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "無効なCAPTCHAプロバイダー。",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "電話番号はあなたの地域で無効です %s",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "チューリングテストは失敗しました。",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "電子メール変更規則を取得できません。",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "電話の変更ルールを取得できません。",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "不明なタイプ",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "誤ったパラメータ",
"Unknown type": "Unknown type", "Wrong verification code!": "誤った検証コードです!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "あなたは%d分であなたのコードを確認する必要があります",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "このユーザーの資格情報が見つかりませんでした",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "最初にWebAuthnSigninBeginを呼び出してください"
} }
} }

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "사용자 추가 실패",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "초기 점수 획득 실패, 오류: %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "먼저 로그아웃해주세요",
"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": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "도전 방식은 S256이어야 합니다",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "사용자를 만들지 못했습니다. 사용자 정보가 잘못되었습니다: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "로그인에 실패했습니다.: %s",
"Invalid token": "Invalid token", "Invalid token": "유효하지 않은 토큰",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "예상한 상태: %s, 실제 상태: %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 via %%s, please use another way to sign up": "제공자 계정: %s와 사용자 이름: %s (%s)은(는) 존재하지 않으며 %%s를 통해 새 계정으로 가입하는 것이 허용되지 않습니다. 다른 방법으로 가입하십시오",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
"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 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": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "무단 조작",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "DisplayName는 유효한 실제 이름이 아닙니다",
"Email already exists": "Email already exists", "Email already exists": "이메일이 이미 존재합니다",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "이메일은 비어 있을 수 없습니다",
"Email is invalid": "Email is invalid", "Email is invalid": "이메일이 유효하지 않습니다",
"Empty username.": "Empty username.", "Empty username.": "빈 사용자 이름.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "이름은 공백일 수 없습니다",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "성은 비어 있을 수 없습니다",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "조직은 존재하지 않습니다",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "암호는 적어도 6자 이상이어야 합니다",
"Phone already exists": "Phone already exists", "Phone already exists": "전화기는 이미 존재합니다",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "전화는 비워 둘 수 없습니다",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "전화번호가 유효하지 않습니다",
"Session outdated, please login again": "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 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.", "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 already exists": "사용자 이름이 이미 존재합니다",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "사용자 이름에는 공백이 포함될 수 없습니다",
"Username cannot start with a digit": "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 is too long (maximum is 39 characters).": "사용자 이름이 너무 깁니다 (최대 39자).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "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": "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": "올바르지 않은 비밀번호나 코드를 여러 번 입력했습니다. %d분 동안 기다리신 후 다시 시도해주세요",
"Your region is not allow to signup by phone": "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", "password or code is incorrect, you have %d remaining chances": "암호 또는 코드가 올바르지 않습니다. %d번의 기회가 남아 있습니다",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "지원되지 않는 암호 유형: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "누락된 매개변수",
"Please login first": "Please login first", "Please login first": "먼저 로그인 하십시오",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "LDAP 서버가 존재합니다"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "먼저 링크해주세요",
"This application has no providers": "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 application has no providers of type": "이 응용 프로그램은 타입의 공급자가 없습니다",
"This provider can't be unlinked": "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 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" "You can't unlink yourself, you are not a member of any application": "당신은 어떤 애플리케이션의 회원이 아니기 때문에 스스로 링크를 해제할 수 없습니다"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "관리자만 %s을(를) 수정할 수 있습니다.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "%s 는 변경할 수 없습니다.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "미확인 수정 규칙 %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "잘못된 애플리케이션 ID입니다",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "제공자 %s가 존재하지 않습니다"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "사용자는 아바타 태그에 대해 nil입니다",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "사용자 이름 또는 전체 파일 경로가 비어 있습니다: 사용자 이름 = %s, 전체 파일 경로 = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "어플리케이션 %s을(를) 찾을 수 없습니다"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "제공 업체 %s의 카테고리는 SAML이 아닙니다"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "이메일 형식의 빈 매개 변수: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "잘못된 이메일 수신자: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "잘못된 전화 수신자: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "객체 키 : %s 는 허용되지 않습니다",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "제공자 유형: %s은/는 지원되지 않습니다"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "클라이언트 ID 또는 클라이언트 비밀번호가 비어 있습니다",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "그랜트 유형: %s은(는) 이 어플리케이션에서 지원되지 않습니다",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "잘못된 어플리케이션 또는 올바르지 않은 클라이언트 시크릿입니다",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "잘못된 클라이언트 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", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "허용된 Redirect URI 목록에서 %s이(가) 존재하지 않습니다",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "토큰을 찾을 수 없습니다. 잘못된 액세스 토큰입니다"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
"New password cannot contain blank space.": "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" "New password must have at least 6 characters": "새로운 비밀번호는 최소 6자 이상이어야 합니다"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "사용자 가져오기를 실패했습니다"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "어플리케이션을 찾을 수 없습니다. userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "어플리케이션 %s에서 %s 카테고리를 위한 공급자가 찾을 수 없습니다",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "제공자: %s를 찾을 수 없습니다"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "코드는 아직 전송되지 않았습니다!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "잘못된 captcha 제공자입니다.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "전화 번호가 당신의 지역 %s에서 유효하지 않습니다",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "튜링 테스트 실패.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "이메일 수정 규칙을 가져올 수 없습니다.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "전화 수정 규칙을 가져올 수 없습니다.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "알 수 없는 유형",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "잘못된 매개 변수입니다",
"Unknown type": "Unknown type", "Wrong verification code!": "잘못된 인증 코드입니다!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "당신은 %d분 안에 코드를 검증해야 합니다!",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "이 사용자의 자격 증명을 찾을 수 없습니다",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "WebAuthnSigninBegin을 먼저 호출해주세요"
} }
} }

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "Не удалось добавить пользователя",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "Не удалось получить исходный балл, ошибка: %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "Пожалуйста, сначала выйдите из системы",
"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": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Метод испытаний должен быть S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Не удалось создать пользователя, информация о пользователе недействительна: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Не удалось войти в систему: %s",
"Invalid token": "Invalid token", "Invalid token": "Недействительный токен",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "Ожидался статус: %s, но получен: %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 via %%s, please use another way to sign up": "Аккаунт провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован через %%s, пожалуйста, используйте другой способ регистрации",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "Приложение: %s не существует",
"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 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": "Провайдер: %s не включен для приложения",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "Несанкционированная операция",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Сервисы %s и %s не совпадают"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "Имя отображения не может быть пустым",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "DisplayName не является действительным именем",
"Email already exists": "Email already exists", "Email already exists": "Электронная почта уже существует",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "Электронная почта не может быть пустой",
"Email is invalid": "Email is invalid", "Email is invalid": "Адрес электронной почты недействительный",
"Empty username.": "Empty username.", "Empty username.": "Пустое имя пользователя.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "Имя не может быть пустым",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "Фамилия не может быть пустой",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Организация не существует",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
"Phone already exists": "Phone already exists", "Phone already exists": "Телефон уже существует",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Телефон не может быть пустым",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Номер телефона является недействительным",
"Session outdated, please login again": "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 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.", "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 already exists": "Имя пользователя уже существует",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
"Username cannot start with a digit": "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 is too long (maximum is 39 characters).": "Имя пользователя слишком длинное (максимальная длина - 39 символов).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "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": "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": "Вы ввели неправильный пароль или код слишком много раз, пожалуйста, подождите %d минут и попробуйте снова",
"Your region is not allow to signup by phone": "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", "password or code is incorrect, you have %d remaining chances": "Неправильный пароль или код, у вас осталось %d попыток",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "неподдерживаемый тип пароля: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Отсутствующий параметр",
"Please login first": "Please login first", "Please login first": "Пожалуйста, сначала войдите в систему",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "Пользователь %s не существует",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "не поддерживайте captchaProvider:"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "LDAP-сервер существует"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Пожалуйста, сначала установите ссылку",
"This application has no providers": "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 application has no providers of type": "Это приложение не имеет провайдеров данного типа",
"This provider can't be unlinked": "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 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" "You can't unlink yourself, you are not a member of any application": "Вы не можете отвязаться, так как вы не являетесь участником никакого приложения"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "Только администратор может изменять %s.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "%s неизменяемый.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Неизвестное изменение правила %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Неверный идентификатор приложения",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "провайдер: %s не существует"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "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" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Имя пользователя или полный путь к файлу пусты: имя_пользователя = %s, полный_путь_к_файлу = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "Приложение %s не найдено"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "категория провайдера %s не является SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "Некорректные получатели электронной почты: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "Некорректные получатели телефонных звонков: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "Объект «objectKey: %s» не разрешен",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "Тип поставщика: %s не поддерживается"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "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", "Grant_type: %s is not supported in this application": "Тип предоставления: %s не поддерживается в данном приложении",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Недействительное приложение или неправильный clientSecret",
"Invalid client_id": "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", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI перенаправления: %s не существует в списке разрешенных URI перенаправления",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "Токен не найден, недействительный accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Отображаемое имя не может быть пустым",
"New password cannot contain blank space.": "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" "New password must have at least 6 characters": "Новый пароль должен содержать не менее 6 символов"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Не удалось импортировать пользователей"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "Не найдено заявки для пользователя с идентификатором: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "Нет поставщика для категории: %s для приложения: %s",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "Поставщик: %s не найден"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Код еще не был отправлен!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "Недействительный поставщик CAPTCHA.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "Тест Тьюринга не удался.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "Невозможно получить правило изменения телефона.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "Неизвестный тип",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "Неправильный параметр",
"Unknown type": "Unknown type", "Wrong verification code!": "Неправильный код подтверждения!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Не найдено учетных данных для этого пользователя",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "Пожалуйста, сначала вызовите WebAuthnSigninBegin"
} }
} }

View File

@ -1,146 +1,140 @@
{ {
"account": { "account": {
"Get init score failed, error: %w": "Get init score failed, error: %w", "Failed to add user": "Không thể thêm người dùng",
"Invalid information": "Invalid information", "Get init score failed, error: %w": "Lấy điểm khởi đầu thất bại, lỗi: %w",
"Please sign out first before signing in": "Please sign out first before signing in", "Please sign out first": "Vui lòng đăng xuất trước",
"Please sign out first before signing up": "Please sign out first before signing up", "The application does not allow to sign up new account": "Ứng dụng không cho phép đăng ký tài khoản mới"
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Phương pháp thách thức nên là S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Không thể to người dùng, thông tin người dùng không hợp lệ: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Đăng nhập không thành công: %s",
"Invalid token": "Invalid token", "Invalid token": "Mã thông báo không hợp lệ",
"State expected: %s, but got: %s": "State expected: %s, but got: %s", "State expected: %s, but got: %s": "Trạng thái dự kiến: %s, nhưng nhận được: %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 via %%s, please use another way to sign up": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký làm tài khoản mới qua %%s, vui lòng sử dụng cách khác để đăng ký",
"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) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký như một tài khoản mới, vui lòng liên hệ với bộ phận hỗ trợ công nghệ thông tin của bạn",
"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 account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) đã được liên kết với tài khoản khác: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist", "The application: %s does not exist": "Ứng dụng: %s không tồn tại",
"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 login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
"The provider type: %s is not supported": "The provider type: %s is not supported", "The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "Unauthorized operation": "Hoạt động không được ủy quyền",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s"
"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": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s %s không khớp"
}, },
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Tình trạng liên kết không thể để trống",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "Tên hiển thị không thể để trống",
"DisplayName is not valid real name": "DisplayName is not valid real name", "DisplayName is not valid real name": "DisplayName không phải là tên thật hợp lệ",
"Email already exists": "Email already exists", "Email already exists": "Email đã tồn tại",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "Email không thể để trống",
"Email is invalid": "Email is invalid", "Email is invalid": "Địa chỉ email không hợp lệ",
"Empty username.": "Empty username.", "Empty username.": "Tên đăng nhập trống.",
"FirstName cannot be blank": "FirstName cannot be blank", "FirstName cannot be blank": "Tên không được để trống",
"LastName cannot be blank": "LastName cannot be blank", "LastName cannot be blank": "Họ không thể để trống",
"Ldap user name or password incorrect": "Ldap user name or password incorrect", "Ldap user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server", "Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Tổ chức không tồn tại",
"Password must have at least 6 characters": "Password must have at least 6 characters", "Password must have at least 6 characters": "Mật khẩu phải ít nhất 6 ký tự",
"Phone already exists": "Phone already exists", "Phone already exists": "Điện thoại đã tồn tại",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Điện thoại không thể để trống",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Số điện thoại không hợp lệ",
"Session outdated, please login again": "Session outdated, please login again", "Session outdated, please login again": "Phiên làm việc hết hạn, vui lòng đăng nhập lại",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "Người dùng bị cấm đăng nhập, vui lòng liên hệ với quản trị viên",
"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.", "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.": "Tên người dùng chỉ có thể chứa các ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có hai ký tự gạch dưới hoặc gạch ngang liền kề và không được bắt đầu hoặc kết thúc bằng dấu gạch dưới hoặc gạch ngang.",
"Username already exists": "Username already exists", "Username already exists": "Tên đăng nhập đã tồn tại",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Tên người dùng không thể chứa khoảng trắng",
"Username cannot start with a digit": "Username cannot start with a digit", "Username cannot start with a digit": "Tên người dùng không thể bắt đầu bằng chữ số",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Tên đăng nhập quá dài (tối đa là 39 ký tự).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Tên đăng nhập phải có ít nhất 2 ký tự",
"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", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Bạn đã nhập sai mật khẩu hoặc mã quá nhiều lần, vui lòng đợi %d phút và thử lại",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone", "Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "Mật khẩu hoặc mã không chính xác, bạn còn %d lần cơ hội",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "Loại mật khẩu không được hỗ trợ: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Thiếu tham số",
"Please login first": "Please login first", "Please login first": "Vui lòng đăng nhập trước",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "Người dùng: %s không tồn tại",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "Không hỗ trợ captchaProvider:"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Máy chủ Ldap tồn tại"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Vui lòng kết nối trước tiên",
"This application has no providers": "This application has no providers", "This application has no providers": "Ứng dụng này không có nhà cung cấp",
"This application has no providers of type": "This application has no providers of type", "This application has no providers of type": "Ứng dụng này không có nhà cung cấp loại nào",
"This provider can't be unlinked": "This provider can't be unlinked", "This provider can't be unlinked": "Nhà cung cấp này không thể được tách ra",
"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 are not the global admin, you can't unlink other users": "Bạn không phải là quản trị viên toàn cầu, bạn không thể hủy liên kết người dùng khác",
"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" "You can't unlink yourself, you are not a member of any application": "Bạn không thể hủy liên kết của mình, bởi vì bạn không phải là thành viên của bất kỳ ứng dụng nào"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.", "Only admin can modify the %s.": "Chỉ những người quản trị mới có thể sửa đổi %s.",
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "%s không thể thay đổi được.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Quy tắc thay đổi không xác định %s."
}, },
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Sai ID ứng dụng",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar", "User is nil for tag: avatar": "Người dùng không có giá trị cho thẻ: hình đại diện",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Tên người dùng hoặc đường dẫn tệp đầy đủ trống: tên người dùng = %s, đường dẫn tệp đầy đủ = %s"
}, },
"saml": { "saml": {
"Application %s not found": "Application %s not found" "Application %s not found": "Ứng dụng %s không tìm thấy"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML" "provider %s's category is not SAML": "Danh mục của nhà cung cấp %s không phải là SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v", "Empty parameters for emailForm: %v": "Tham số trống cho emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s", "Invalid Email receivers: %s": "Người nhận Email không hợp lệ: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s" "Invalid phone receivers: %s": "Người nhận điện thoại không hợp lệ: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "Khóa đối tượng: %s không được phép",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "Loại nhà cung cấp: %s không được hỗ trợ"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "Empty clientId or clientSecret": "ClientId hoặc clientSecret trống",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "Loại cấp phép: %s không được hỗ trợ trong ứng dụng này",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Đơn đăng ký không hợp lệ hoặc sai clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Client_id không hợp lệ",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Đường dẫn chuyển hướng URI: %s không tồn tại trong danh sách URI được phép chuyển hướng",
"Token not found, invalid accessToken": "Token not found, invalid accessToken" "Token not found, invalid accessToken": "Token không tìm thấy, accessToken không hợp lệ"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Tên hiển thị không thể trống",
"New password cannot contain blank space.": "New password cannot contain blank space.", "New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
"New password must have at least 6 characters": "New password must have at least 6 characters" "New password must have at least 6 characters": "Mật khẩu mới phải có ít nhất 6 ký tự"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Không thể nhập người dùng"
}, },
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "No application is found for userId: %s": "Không tìm thấy ứng dụng cho ID người dùng: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s", "No provider for category: %s is found for application: %s": "Không tìm thấy nhà cung cấp cho danh mục: %s cho ứng dụng: %s",
"The provider: %s is not found": "The provider: %s is not found" "The provider: %s is not found": "Nhà cung cấp: %s không được tìm thấy"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Mã chưa được gửi đến!",
"Email is invalid": "Email is invalid", "Invalid captcha provider.": "Nhà cung cấp captcha không hợp lệ.",
"Invalid captcha provider.": "Invalid captcha provider.", "Phone number is invalid in your region %s": "Số điện thoại không hợp lệ trong vùng của bạn %s",
"Organization does not exist": "Organization does not exist", "Turing test failed.": "Kiểm định Turing thất bại.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s", "Unable to get the email modify rule.": "Không thể lấy quy tắc sửa đổi email.",
"Turing test failed.": "Turing test failed.", "Unable to get the phone modify rule.": "Không thể thay đổi quy tắc trên điện thoại.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unknown type": "Loại không xác định",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Wrong parameter": "Tham số không đúng",
"Unknown type": "Unknown type", "Wrong verification code!": "Mã xác thực sai!",
"Wrong parameter": "Wrong parameter", "You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!",
"Wrong verification code!": "Wrong verification code!", "the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước"
"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": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Không tìm thấy thông tin xác thực cho người dùng này",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first" "Please call WebAuthnSigninBegin first": "Vui lòng gọi WebAuthnSigninBegin trước"
} }
} }

View File

@ -1,31 +1,27 @@
{ {
"account": { "account": {
"Failed to add user": "添加用户失败",
"Get init score failed, error: %w": "初始化分数失败: %w", "Get init score failed, error: %w": "初始化分数失败: %w",
"Invalid information": "无效信息", "Please sign out first": "请先退出登录",
"Please sign out first before signing in": "请在登录前先退出登录",
"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": { "auth": {
"Challenge method should be S256": "Challenge 方法应该为 S256", "Challenge method should be S256": "Challenge方法应该为S256",
"Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s", "Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s",
"Failed to login in: %s": "登录失败: %s", "Failed to login in: %s": "登录失败: %s",
"Invalid token": "无效token", "Invalid token": "无效token",
"State expected: %s, but got: %s": "期望状态为: %s, 实际状态为: %s", "State expected: %s, but got: %s": "期望状态为: %s, 实际状态为: %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": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %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": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册",
"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": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持", "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": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s 与用户名: %s (%s) 已经与其他账户绑定: %s (%s)", "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s与用户名: %s (%s)已经与其他账户绑定: %s (%s)",
"The application: %s does not exist": "应用 %s 不存在", "The application: %s does not exist": "应用%s不存在",
"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": "不支持该类型的提供商: %s", "The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
"The provider: %s is not enabled for the application": "该应用的提供商: %s 未被启用",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登录,请联系管理员",
"Turing test failed.": "人机验证失败",
"Unauthorized operation": "未授权的操作", "Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s" "Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "服务 %s%s 不匹配" "Service %s and %s do not match": "服务%s%s不匹配"
}, },
"check": { "check": {
"Affiliation cannot be blank": "工作单位不可为空", "Affiliation cannot be blank": "工作单位不可为空",
@ -61,7 +57,7 @@
"general": { "general": {
"Missing parameter": "缺少参数", "Missing parameter": "缺少参数",
"Please login first": "请先登录", "Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s 不存在", "The user: %s doesn't exist": "用户: %s不存在",
"don't support captchaProvider: ": "不支持验证码提供商: " "don't support captchaProvider: ": "不支持验证码提供商: "
}, },
"ldap": { "ldap": {
@ -76,13 +72,13 @@
"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": { "organization": {
"Only admin can modify the %s.": "仅允许管理员可以修改 %s", "Only admin can modify the %s.": "仅允许管理员可以修改%s",
"The %s is immutable.": "%s 是不可变的", "The %s is immutable.": "%s是不可变的",
"Unknown modify rule %s.": "未知的修改规则: %s" "Unknown modify rule %s.": "未知的修改规则: %s"
}, },
"provider": { "provider": {
"Invalid application id": "无效的应用ID", "Invalid application id": "无效的应用ID",
"the provider: %s does not exist": "提供商: %s 不存在" "the provider: %s does not exist": "提供商: %s不存在"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "上传头像时用户为空", "User is nil for tag: avatar": "上传头像时用户为空",
@ -92,15 +88,15 @@
"Application %s not found": "未找到应用: %s" "Application %s not found": "未找到应用: %s"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "提供商: %s 不是SAML类型" "provider %s's category is not SAML": "提供商: %s不是SAML类型"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "邮件参数为空: %v", "Empty parameters for emailForm: %v": "邮件参数为空: %v",
"Invalid Email receivers: %s": " 无效的邮箱收件人: %s", "Invalid Email receivers: %s": "无效的邮箱收件人: %s",
"Invalid phone receivers: %s": "无效的手机短信收信人: %s" "Invalid phone receivers: %s": "无效的手机短信收信人: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "objectKey: %s 被禁止", "The objectKey: %s is not allowed": "objectKey: %s被禁止",
"The provider type: %s is not supported": "不支持的提供商类型: %s" "The provider type: %s is not supported": "不支持的提供商类型: %s"
}, },
"token": { "token": {
@ -108,7 +104,7 @@
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s", "Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret", "Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
"Invalid client_id": "无效的ClientId", "Invalid client_id": "无效的ClientId",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s 在许可跳转列表中未找到", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s在许可跳转列表中未找到",
"Token not found, invalid accessToken": "未查询到对应token, accessToken无效" "Token not found, invalid accessToken": "未查询到对应token, accessToken无效"
}, },
"user": { "user": {
@ -120,15 +116,13 @@
"Failed to import users": "导入用户失败" "Failed to import users": "导入用户失败"
}, },
"util": { "util": {
"No application is found for userId: %s": "未找到用户: %s 的应用", "No application is found for userId: %s": "未找到用户: %s的应用",
"No provider for category: %s is found for application: %s": "未找到类别为: %s 的提供商来满足应用: %s", "No provider for category: %s is found for application: %s": "未找到类别为: %s的提供商来满足应用: %s",
"The provider: %s is not found": "未找到提供商: %s" "The provider: %s is not found": "未找到提供商: %s"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "验证码还未发送", "Code has not been sent yet!": "验证码还未发送",
"Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商", "Invalid captcha provider.": "非法的验证码提供商",
"Organization does not exist": "组织不存在",
"Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s", "Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s",
"Turing test failed.": "验证码还未发送", "Turing test failed.": "验证码还未发送",
"Unable to get the email modify rule.": "无法获取邮箱修改规则", "Unable to get the email modify rule.": "无法获取邮箱修改规则",
@ -140,7 +134,7 @@
"the user does not exist, please sign up first": "用户不存在,请先注册" "the user does not exist, please sign up first": "用户不存在,请先注册"
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "该用户没有 WebAuthn 凭据", "Found no credentials for this user": "该用户没有WebAuthn凭据",
"Please call WebAuthnSigninBegin first": "请先调用 WebAuthnSigninBegin" "Please call WebAuthnSigninBegin first": "请先调用WebAuthnSigninBegin函数"
} }
} }

View File

@ -12,7 +12,7 @@
"defaultAvatar": "", "defaultAvatar": "",
"defaultApplication": "", "defaultApplication": "",
"tags": [], "tags": [],
"languages": ["en", "zh", "es", "fr", "de", "ja", "ko", "ru", "vi"], "languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"],
"masterPassword": "", "masterPassword": "",
"initScore": 2000, "initScore": 2000,
"enableSoftDeletion": false, "enableSoftDeletion": false,

View File

@ -55,7 +55,7 @@ func main() {
beego.SetStaticPath("/swagger", "swagger") beego.SetStaticPath("/swagger", "swagger")
beego.SetStaticPath("/files", "files") beego.SetStaticPath("/files", "files")
// https://studygolang.com/articles/2303 // https://studygolang.com/articles/2303
beego.InsertFilter("*", beego.BeforeStatic, routers.StaticFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter)

View File

@ -63,19 +63,19 @@ func getPermanentAvatarUrl(organization string, username string, url string, upl
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false) uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
if upload { if upload {
DownloadAndUpload(url, fullFilePath) DownloadAndUpload(url, fullFilePath, "en")
} }
return uploadedFileUrl return uploadedFileUrl
} }
func DownloadAndUpload(url string, fullFilePath string) { func DownloadAndUpload(url string, fullFilePath string, lang string) {
fileBuffer, err := downloadFile(url) fileBuffer, err := downloadFile(url)
if err != nil { if err != nil {
panic(err) panic(err)
} }
_, _, err = UploadFileSafe(defaultStorageProvider, fullFilePath, fileBuffer) _, _, err = UploadFileSafe(defaultStorageProvider, fullFilePath, fileBuffer, lang)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -89,7 +89,7 @@ func initBuiltInOrganization() bool {
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"}, CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")), DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Tags: []string{}, Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru", "vi"}, Languages: []string{"en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"},
InitScore: 2000, InitScore: 2000,
AccountItems: getBuiltInAccountItems(), AccountItems: getBuiltInAccountItems(),
EnableSoftDeletion: false, EnableSoftDeletion: false,

View File

@ -20,6 +20,7 @@ import (
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config" "github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/log"
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
xormadapter "github.com/casdoor/xorm-adapter/v3" xormadapter "github.com/casdoor/xorm-adapter/v3"
@ -50,27 +51,30 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
panic(err) panic(err)
} }
policyFilter := xormadapter.Filter{} // Init an enforcer instance without specifying a model or adapter.
// If you specify an adapter, it will load all policies, which is a
if !HasRoleDefinition(m) { // heavy process that can slow down the application.
policyFilter.Ptype = []string{"p"} enforcer, err := casbin.NewEnforcer(&log.DefaultLogger{}, false)
err = adapter.LoadFilteredPolicy(m, policyFilter)
if err != nil {
panic(err)
}
}
enforcer, err := casbin.NewEnforcer(m, adapter)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// load Policy with a specific Permission enforcer.InitWithModelAndAdapter(m, nil)
policyFilter.V5 = []string{permission.GetId()} enforcer.SetAdapter(adapter)
policyFilter := xormadapter.Filter{
V5: []string{permission.GetId()},
}
if !HasRoleDefinition(m) {
policyFilter.Ptype = []string{"p"}
}
err = enforcer.LoadFilteredPolicy(policyFilter) err = enforcer.LoadFilteredPolicy(policyFilter)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return enforcer return enforcer
} }

View File

@ -36,13 +36,11 @@ func ParseSamlResponse(samlResponse string, providerType string) (string, error)
return "", err return "", err
} }
assertionInfo, err := sp.RetrieveAssertionInfo(samlResponse) assertionInfo, err := sp.RetrieveAssertionInfo(samlResponse)
if err != nil {
panic(err) return assertionInfo.NameID, err
}
return assertionInfo.NameID, nil
} }
func GenerateSamlLoginUrl(id, relayState, lang string) (string, string, error) { func GenerateSamlLoginUrl(id, relayState, lang string) (auth string, method string, err error) {
provider := GetProvider(id) provider := GetProvider(id)
if provider.Category != "SAML" { if provider.Category != "SAML" {
return "", "", fmt.Errorf(i18n.Translate(lang, "saml_sp:provider %s's category is not SAML"), provider.Name) return "", "", fmt.Errorf(i18n.Translate(lang, "saml_sp:provider %s's category is not SAML"), provider.Name)
@ -51,8 +49,7 @@ func GenerateSamlLoginUrl(id, relayState, lang string) (string, string, error) {
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
auth := ""
method := ""
if provider.EnableSignAuthnRequest { if provider.EnableSignAuthnRequest {
post, err := sp.BuildAuthBodyPost(relayState) post, err := sp.BuildAuthBodyPost(relayState)
if err != nil { if err != nil {

View File

@ -106,11 +106,11 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
return fileUrl, objectKey return fileUrl, objectKey
} }
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) { func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) {
endpoint := getProviderEndpoint(provider) endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint) storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil { if storageProvider == nil {
return "", "", fmt.Errorf("the provider type: %s is not supported", provider.Type) return "", "", fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
} }
if provider.Domain == "" { if provider.Domain == "" {
@ -128,7 +128,7 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
return fileUrl, objectKey, nil return fileUrl, objectKey, nil
} }
func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) { func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer, lang string) (string, string, error) {
// check fullFilePath is there security issue // check fullFilePath is there security issue
if strings.Contains(fullFilePath, "..") { if strings.Contains(fullFilePath, "..") {
return "", "", fmt.Errorf("the fullFilePath: %s is not allowed", fullFilePath) return "", "", fmt.Errorf("the fullFilePath: %s is not allowed", fullFilePath)
@ -139,7 +139,7 @@ func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.B
var err error var err error
times := 0 times := 0
for { for {
fileUrl, objectKey, err = uploadFile(provider, fullFilePath, fileBuffer) fileUrl, objectKey, err = uploadFile(provider, fullFilePath, fileBuffer, lang)
if err != nil { if err != nil {
times += 1 times += 1
if times >= 5 { if times >= 5 {

View File

@ -613,7 +613,8 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
nullUser := &User{ nullUser := &User{
Owner: application.Owner, Owner: application.Owner,
Id: application.GetId(), Id: application.GetId(),
Name: fmt.Sprintf("app/%s", application.Name), Name: application.Name,
Type: "application",
} }
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", scope, host) accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", scope, host)

View File

@ -50,6 +50,7 @@ func initAPI() {
beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout") beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout")
beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount") beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount")
beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo") beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo")
beego.Router("/api/user", &controllers.ApiController{}, "GET:GetUserinfo2")
beego.Router("/api/unlink", &controllers.ApiController{}, "POST:Unlink") beego.Router("/api/unlink", &controllers.ApiController{}, "POST:Unlink")
beego.Router("/api/get-saml-login", &controllers.ApiController{}, "GET:GetSamlLogin") beego.Router("/api/get-saml-login", &controllers.ApiController{}, "GET:GetSamlLogin")
beego.Router("/api/acs", &controllers.ApiController{}, "POST:HandleSamlLogin") beego.Router("/api/acs", &controllers.ApiController{}, "POST:HandleSamlLogin")

View File

@ -2058,22 +2058,13 @@
"tags": [ "tags": [
"System API" "System API"
], ],
"description": "get user's system info", "description": "get system info like CPU and memory usage",
"operationId": "ApiController.GetSystemInfo", "operationId": "ApiController.GetSystemInfo",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the user",
"required": true,
"type": "string"
}
],
"responses": { "responses": {
"200": { "200": {
"description": "The Response object", "description": "The Response object",
"schema": { "schema": {
"$ref": "#/definitions/object.SystemInfo" "$ref": "#/definitions/util.SystemInfo"
} }
} }
} }
@ -2323,11 +2314,14 @@
"tags": [ "tags": [
"System API" "System API"
], ],
"description": "get local git repo's latest release version info", "description": "get version info like Casdoor release version and commit ID",
"operationId": "ApiController.GetVersionInfo", "operationId": "ApiController.GetVersionInfo",
"responses": { "responses": {
"200": { "200": {
"description": "{string} local latest version hash of Casdoor" "description": "The Response object",
"schema": {
"$ref": "#/definitions/util.VersionInfo"
}
} }
} }
} }
@ -3494,6 +3488,23 @@
"operationId": "ApiController.UploadResource" "operationId": "ApiController.UploadResource"
} }
}, },
"/api/user": {
"get": {
"tags": [
"Account API"
],
"description": "return Laravel compatible user information according to OAuth 2.0",
"operationId": "ApiController.UserInfo2",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/LaravelResponse"
}
}
}
}
},
"/api/userinfo": { "/api/userinfo": {
"get": { "get": {
"tags": [ "tags": [
@ -3627,14 +3638,18 @@
} }
}, },
"definitions": { "definitions": {
"2268.0xc000528cf0.false": { "2306.0xc0000a7410.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"2302.0xc000528d20.false": { "2340.0xc0000a7440.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"LaravelResponse": {
"title": "LaravelResponse",
"type": "object"
},
"Response": { "Response": {
"title": "Response", "title": "Response",
"type": "object" "type": "object"
@ -3682,6 +3697,9 @@
"captchaType": { "captchaType": {
"type": "string" "type": "string"
}, },
"clientId": {
"type": "string"
},
"clientSecret": { "clientSecret": {
"type": "string" "type": "string"
}, },
@ -3758,10 +3776,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"$ref": "#/definitions/2268.0xc000528cf0.false" "$ref": "#/definitions/2306.0xc0000a7410.false"
}, },
"data2": { "data2": {
"$ref": "#/definitions/2302.0xc000528d20.false" "$ref": "#/definitions/2340.0xc0000a7440.false"
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@ -4830,10 +4848,6 @@
} }
} }
}, },
"object.SystemInfo": {
"title": "SystemInfo",
"type": "object"
},
"object.TableColumn": { "object.TableColumn": {
"title": "TableColumn", "title": "TableColumn",
"type": "object", "type": "object",
@ -5475,6 +5489,43 @@
"title": "CredentialCreationResponse", "title": "CredentialCreationResponse",
"type": "object" "type": "object"
}, },
"util.SystemInfo": {
"title": "SystemInfo",
"type": "object",
"properties": {
"cpuUsage": {
"type": "array",
"items": {
"type": "number",
"format": "double"
}
},
"memoryTotal": {
"type": "integer",
"format": "int64"
},
"memoryUsed": {
"type": "integer",
"format": "int64"
}
}
},
"util.VersionInfo": {
"title": "VersionInfo",
"type": "object",
"properties": {
"commitId": {
"type": "string"
},
"commitOffset": {
"type": "integer",
"format": "int64"
},
"version": {
"type": "string"
}
}
},
"webauthn.Credential": { "webauthn.Credential": {
"title": "Credential", "title": "Credential",
"type": "object" "type": "object"

View File

@ -1340,19 +1340,13 @@ paths:
get: get:
tags: tags:
- System API - System API
description: get user's system info description: get system info like CPU and memory usage
operationId: ApiController.GetSystemInfo operationId: ApiController.GetSystemInfo
parameters:
- in: query
name: id
description: The id ( owner/name ) of the user
required: true
type: string
responses: responses:
"200": "200":
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/object.SystemInfo' $ref: '#/definitions/util.SystemInfo'
/api/get-token: /api/get-token:
get: get:
tags: tags:
@ -1515,11 +1509,13 @@ paths:
get: get:
tags: tags:
- System API - System API
description: get local git repo's latest release version info description: get version info like Casdoor release version and commit ID
operationId: ApiController.GetVersionInfo operationId: ApiController.GetVersionInfo
responses: responses:
"200": "200":
description: '{string} local latest version hash of Casdoor' description: The Response object
schema:
$ref: '#/definitions/util.VersionInfo'
/api/get-webhook: /api/get-webhook:
get: get:
tags: tags:
@ -2288,6 +2284,17 @@ paths:
tags: tags:
- Resource API - Resource API
operationId: ApiController.UploadResource operationId: ApiController.UploadResource
/api/user:
get:
tags:
- Account API
description: return Laravel compatible user information according to OAuth 2.0
operationId: ApiController.UserInfo2
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/LaravelResponse'
/api/userinfo: /api/userinfo:
get: get:
tags: tags:
@ -2374,12 +2381,15 @@ paths:
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
definitions: definitions:
2268.0xc000528cf0.false: 2306.0xc0000a7410.false:
title: "false" title: "false"
type: object type: object
2302.0xc000528d20.false: 2340.0xc0000a7440.false:
title: "false" title: "false"
type: object type: object
LaravelResponse:
title: LaravelResponse
type: object
Response: Response:
title: Response title: Response
type: object type: object
@ -2413,6 +2423,8 @@ definitions:
type: string type: string
captchaType: captchaType:
type: string type: string
clientId:
type: string
clientSecret: clientSecret:
type: string type: string
code: code:
@ -2464,9 +2476,9 @@ definitions:
type: object type: object
properties: properties:
data: data:
$ref: '#/definitions/2268.0xc000528cf0.false' $ref: '#/definitions/2306.0xc0000a7410.false'
data2: data2:
$ref: '#/definitions/2302.0xc000528d20.false' $ref: '#/definitions/2340.0xc0000a7440.false'
msg: msg:
type: string type: string
name: name:
@ -3183,9 +3195,6 @@ definitions:
type: string type: string
user: user:
type: string type: string
object.SystemInfo:
title: SystemInfo
type: object
object.TableColumn: object.TableColumn:
title: TableColumn title: TableColumn
type: object type: object
@ -3617,6 +3626,32 @@ definitions:
protocol.CredentialCreationResponse: protocol.CredentialCreationResponse:
title: CredentialCreationResponse title: CredentialCreationResponse
type: object type: object
util.SystemInfo:
title: SystemInfo
type: object
properties:
cpuUsage:
type: array
items:
type: number
format: double
memoryTotal:
type: integer
format: int64
memoryUsed:
type: integer
format: int64
util.VersionInfo:
title: VersionInfo
type: object
properties:
commitId:
type: string
commitOffset:
type: integer
format: int64
version:
type: string
webauthn.Credential: webauthn.Credential:
title: Credential title: Credential
type: object type: object

View File

@ -15,8 +15,13 @@
package util package util
import ( import (
"bufio"
"os"
"path" "path"
"regexp"
"runtime" "runtime"
"strconv"
"strings"
"time" "time"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
@ -140,3 +145,47 @@ func GetVersionInfo() (*VersionInfo, error) {
} }
return res, nil return res, nil
} }
func GetVersionInfoFromFile() (*VersionInfo, error) {
res := &VersionInfo{
Version: "",
CommitId: "",
CommitOffset: -1,
}
_, filename, _, _ := runtime.Caller(0)
rootPath := path.Dir(path.Dir(filename))
file, err := os.Open(path.Join(rootPath, "version_info.txt"))
if err != nil {
return res, err
}
defer file.Close()
// Read file contents line by line
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// Use regular expressions to match strings
re := regexp.MustCompile(`\{([^{}]+)\}`)
versionInfo := scanner.Text()
matches := re.FindStringSubmatch(versionInfo)
if len(matches) > 1 {
split := strings.Split(matches[1], " ")
version := split[0]
commitId := split[1]
commitOffset, _ := strconv.Atoi(split[2])
res = &VersionInfo{
Version: version,
CommitId: commitId,
CommitOffset: commitOffset,
}
break
}
}
if err := scanner.Err(); err != nil {
return res, err
}
return res, nil
}

View File

@ -40,7 +40,7 @@ func TestGetMemoryUsage(t *testing.T) {
t.Log(used, total) t.Log(used, total)
} }
func TestGetGitRepoVersion(t *testing.T) { func TestGetVersionInfo(t *testing.T) {
versionInfo, err := GetVersionInfo() versionInfo, err := GetVersionInfo()
assert.Nil(t, err) assert.Nil(t, err)
t.Log(versionInfo) t.Log(versionInfo)
@ -90,3 +90,9 @@ func TestGetVersion(t *testing.T) {
assert.Equal(t, 3, aheadCnt) assert.Equal(t, 3, aheadCnt)
assert.Equal(t, "v1.257.0", releaseVersion) assert.Equal(t, "v1.257.0", releaseVersion)
} }
func TestFromFile(t *testing.T) {
versionInfo, err := GetVersionInfoFromFile()
assert.Nil(t, err)
t.Log(versionInfo)
}

View File

@ -21,7 +21,7 @@ import i18next from "i18next";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import * as ModelBackend from "./backend/ModelBackend"; import * as ModelBackend from "./backend/ModelBackend";
import PolicyTable from "./common/PoliciyTable"; import PolicyTable from "./table/PoliciyTable";
require("codemirror/theme/material-darker.css"); require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript"); require("codemirror/mode/javascript/javascript");

View File

@ -52,7 +52,7 @@ import PaymentEditPage from "./PaymentEditPage";
import PaymentResultPage from "./PaymentResultPage"; import PaymentResultPage from "./PaymentResultPage";
import AccountPage from "./account/AccountPage"; import AccountPage from "./account/AccountPage";
import HomePage from "./basic/HomePage"; import HomePage from "./basic/HomePage";
import CustomGithubCorner from "./CustomGithubCorner"; import CustomGithubCorner from "./common/CustomGithubCorner";
import * as Conf from "./Conf"; import * as Conf from "./Conf";
import * as Auth from "./auth/Auth"; import * as Auth from "./auth/Auth";
@ -60,7 +60,7 @@ import EntryPage from "./EntryPage";
import ResultPage from "./auth/ResultPage"; import ResultPage from "./auth/ResultPage";
import * as AuthBackend from "./auth/AuthBackend"; import * as AuthBackend from "./auth/AuthBackend";
import AuthCallback from "./auth/AuthCallback"; import AuthCallback from "./auth/AuthCallback";
import SelectLanguageBox from "./SelectLanguageBox"; import LanguageSelect from "./common/select/LanguageSelect";
import i18next from "i18next"; import i18next from "i18next";
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage"; import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
import SamlCallback from "./auth/SamlCallback"; import SamlCallback from "./auth/SamlCallback";
@ -70,7 +70,7 @@ import SystemInfo from "./SystemInfo";
import AdapterListPage from "./AdapterListPage"; import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage"; import AdapterEditPage from "./AdapterEditPage";
import {withTranslation} from "react-i18next"; import {withTranslation} from "react-i18next";
import SelectThemeBox from "./SelectThemeBox"; import ThemeSelect from "./common/select/ThemeSelect";
import SessionListPage from "./SessionListPage"; import SessionListPage from "./SessionListPage";
const {Header, Footer, Content} = Layout; const {Header, Footer, Content} = Layout;
@ -332,12 +332,12 @@ class App extends Component {
{ {
this.renderAvatar() this.renderAvatar()
} }
&nbsp; &nbsp;
&nbsp; &nbsp;
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} &nbsp; <DownOutlined /> {Setting.isMobile() ? null : Setting.getNameAtLeast(this.state.account.displayName)} &nbsp; <DownOutlined />
&nbsp; &nbsp;
&nbsp; &nbsp;
&nbsp; &nbsp;
</div> </div>
</Dropdown> </Dropdown>
); );
@ -352,7 +352,7 @@ class App extends Component {
return ( return (
<React.Fragment> <React.Fragment>
{this.renderRightDropdown()} {this.renderRightDropdown()}
<SelectThemeBox <ThemeSelect
themeAlgorithm={this.state.themeAlgorithm} themeAlgorithm={this.state.themeAlgorithm}
onChange={(nextThemeAlgorithm) => { onChange={(nextThemeAlgorithm) => {
this.setState({ this.setState({
@ -360,7 +360,7 @@ class App extends Component {
logo: this.getLogo(nextThemeAlgorithm), logo: this.getLogo(nextThemeAlgorithm),
}); });
}} /> }} />
<SelectLanguageBox languages={this.state.account.organization.languages} /> <LanguageSelect languages={this.state.account.organization.languages} />
</React.Fragment> </React.Fragment>
); );
} }
@ -452,7 +452,7 @@ class App extends Component {
"/payments" "/payments"
)); ));
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:SysInfo")}</Link>, res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
"/sysinfo" "/sysinfo"
)); ));
} }
@ -672,9 +672,9 @@ class App extends Component {
return ( return (
<React.Fragment> <React.Fragment>
{ {/* { */}
this.renderBanner() {/* this.renderBanner() */}
} {/* } */}
<FloatButton.BackTop /> <FloatButton.BackTop />
<CustomGithubCorner /> <CustomGithubCorner />
{ {

View File

@ -25,9 +25,9 @@ import * as ResourceBackend from "./backend/ResourceBackend";
import SignupPage from "./auth/SignupPage"; import SignupPage from "./auth/SignupPage";
import LoginPage from "./auth/LoginPage"; import LoginPage from "./auth/LoginPage";
import i18next from "i18next"; import i18next from "i18next";
import UrlTable from "./UrlTable"; import UrlTable from "./table/UrlTable";
import ProviderTable from "./ProviderTable"; import ProviderTable from "./table/ProviderTable";
import SignupTable from "./SignupTable"; import SignupTable from "./table/SignupTable";
import PromptPage from "./auth/PromptPage"; import PromptPage from "./auth/PromptPage";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
@ -372,7 +372,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Password ON"), i18next.t("application:Password ON - Tooltip"))} : {Setting.getLabel(i18next.t("application:Enable password"), i18next.t("application:Enable password - Tooltip"))} :
</Col> </Col>
<Col span={1} > <Col span={1} >
<Switch checked={this.state.application.enablePassword} onChange={checked => { <Switch checked={this.state.application.enablePassword} onChange={checked => {
@ -430,6 +430,16 @@ class ApplicationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Enable Email linking"), i18next.t("application:Enable Email linking - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableLinkWithEmail} onChange={checked => {
this.updateApplicationField("enableLinkWithEmail", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <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:Signup URL"), i18next.t("general:Signup URL - Tooltip"))} : {Setting.getLabel(i18next.t("general:Signup URL"), i18next.t("general:Signup URL - Tooltip"))} :
@ -472,7 +482,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("provider:Terms of Use"), i18next.t("provider:Terms of Use - Tooltip"))} : {Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.application.termsOfUse} style={{marginBottom: "10px"}} onChange={e => { <Input value={this.state.application.termsOfUse} style={{marginBottom: "10px"}} onChange={e => {
@ -553,7 +563,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("application:SAML Reply URL"), i18next.t("application:Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip"))} : {Setting.getLabel(i18next.t("application:SAML reply URL"), i18next.t("application:Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.application.samlReplyUrl} onChange={e => { <Input prefix={<LinkOutlined />} value={this.state.application.samlReplyUrl} onChange={e => {
@ -563,7 +573,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Enable SAML compress"), i18next.t("application:Enable SAML compress - Tooltip"))} : {Setting.getLabel(i18next.t("application:Enable SAML compression"), i18next.t("application:Enable SAML compression - Tooltip"))} :
</Col> </Col>
<Col span={1} > <Col span={1} >
<Switch checked={this.state.application.enableSamlCompress} onChange={checked => { <Switch checked={this.state.application.enableSamlCompress} onChange={checked => {
@ -605,16 +615,6 @@ class ApplicationEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col span={(Setting.isMobile()) ? 19 : 6}>
{Setting.getLabel(i18next.t("application:Enable link accounts that with the same email"), i18next.t("application:Enable link accounts that with the same email - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableLinkWithEmail} onChange={checked => {
this.updateApplicationField("enableLinkWithEmail", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <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:Preview"), i18next.t("general:Preview - Tooltip"))} : {Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :

View File

@ -176,7 +176,7 @@ class ApplicationListPage extends BaseListPage {
render: (text, record, index) => { render: (text, record, index) => {
const providers = text; const providers = text;
if (providers.length === 0) { if (providers.length === 0) {
return "(empty)"; return `(${i18next.t("general:empty")})`;
} }
const half = Math.floor((providers.length + 1) / 2); const half = Math.floor((providers.length + 1) / 2);

View File

@ -97,7 +97,7 @@ class CertEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("cert:Scope"), i18next.t("cert:Scope - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Scope"), i18next.t("cert:Scope - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.scope} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.cert.scope} onChange={(value => {
@ -113,7 +113,7 @@ class CertEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("cert:Type"), i18next.t("cert:Type - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Type"), i18next.t("cert:Type - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.type} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.cert.type} onChange={(value => {

View File

@ -111,7 +111,7 @@ class CertListPage extends BaseListPage {
...this.getColumnSearchProps("displayName"), ...this.getColumnSearchProps("displayName"),
}, },
{ {
title: i18next.t("cert:Scope"), title: i18next.t("provider:Scope"),
dataIndex: "scope", dataIndex: "scope",
key: "scope", key: "scope",
filterMultiple: false, filterMultiple: false,
@ -122,7 +122,7 @@ class CertListPage extends BaseListPage {
sorter: true, sorter: true,
}, },
{ {
title: i18next.t("cert:Type"), title: i18next.t("provider:Type"),
dataIndex: "type", dataIndex: "type",
key: "type", key: "type",
filterMultiple: false, filterMultiple: false,

View File

@ -87,7 +87,7 @@ class LdapEditPage extends React.Component {
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitLdapEdit(true)}>{i18next.t("general:Save & Exit")}</Button> <Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitLdapEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
<Button style={{marginLeft: "20px"}} <Button style={{marginLeft: "20px"}}
onClick={() => Setting.goToLink(`/ldap/sync/${this.state.organizationName}/${this.state.ldapId}`)}> onClick={() => Setting.goToLink(`/ldap/sync/${this.state.organizationName}/${this.state.ldapId}`)}>
{i18next.t("ldap:Sync")} LDAP {i18next.t("general:Sync")} LDAP
</Button> </Button>
</div> </div>
} style={{marginLeft: "5px"}} type="inner"> } style={{marginLeft: "5px"}} type="inner">
@ -109,7 +109,7 @@ class LdapEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}> <Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:ID"), i18next.t("general:ID - Tooltip"))} : {Setting.getLabel(i18next.t("general:ID"), i18next.t("general:ID - Tooltip"))} :
</Col> </Col>
<Col span={21}> <Col span={21}>
<Input value={this.state.ldap.id} disabled={true} /> <Input value={this.state.ldap.id} disabled={true} />
@ -117,7 +117,7 @@ class LdapEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}> <Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Server Name"), i18next.t("ldap:Server Name - Tooltip"))} : {Setting.getLabel(i18next.t("ldap:Server name"), i18next.t("ldap:Server name - Tooltip"))} :
</Col> </Col>
<Col span={21}> <Col span={21}>
<Input value={this.state.ldap.serverName} onChange={e => { <Input value={this.state.ldap.serverName} onChange={e => {
@ -127,7 +127,7 @@ class LdapEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}> <Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Server Host"), i18next.t("ldap:Server Host - Tooltip"))} : {Setting.getLabel(i18next.t("ldap:Server host"), i18next.t("ldap:Server host - Tooltip"))} :
</Col> </Col>
<Col span={21}> <Col span={21}>
<Input value={this.state.ldap.host} onChange={e => { <Input value={this.state.ldap.host} onChange={e => {
@ -137,7 +137,7 @@ class LdapEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}> <Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Server Port"), i18next.t("ldap:Server Port - Tooltip"))} : {Setting.getLabel(i18next.t("ldap:Server port"), i18next.t("ldap:Server port - Tooltip"))} :
</Col> </Col>
<Col span={21}> <Col span={21}>
<InputNumber min={0} max={65535} formatter={value => value.replace(/\$\s?|(,*)/g, "")} <InputNumber min={0} max={65535} formatter={value => value.replace(/\$\s?|(,*)/g, "")}

View File

@ -55,7 +55,7 @@ class LdapListPage extends React.Component {
renderTable(ldaps) { renderTable(ldaps) {
const columns = [ const columns = [
{ {
title: i18next.t("ldap:Server Name"), title: i18next.t("ldap:Server name"),
dataIndex: "serverName", dataIndex: "serverName",
key: "serverName", key: "serverName",
width: "200px", width: "200px",
@ -137,7 +137,7 @@ class LdapListPage extends React.Component {
<div> <div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} <Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}}
type="primary" type="primary"
onClick={() => Setting.goToLink(`/ldap/sync/${record.id}`)}>{i18next.t("ldap:Sync")}</Button> onClick={() => Setting.goToLink(`/ldap/sync/${record.id}`)}>{i18next.t("general:Sync")}</Button>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} <Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}}
onClick={() => Setting.goToLink(`/ldap/${record.id}`)}>{i18next.t("general:Edit")}</Button> onClick={() => Setting.goToLink(`/ldap/${record.id}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal <PopconfirmModal

View File

@ -151,7 +151,7 @@ class LdapSyncPage extends React.Component {
}, },
}, },
{ {
title: i18next.t("ldap:Group Id"), title: i18next.t("ldap:Group ID"),
dataIndex: "groupId", dataIndex: "groupId",
key: "groupId", key: "groupId",
width: "140px", width: "140px",
@ -160,21 +160,21 @@ class LdapSyncPage extends React.Component {
onFilter: (value, record) => record.groupId.indexOf(value) === 0, onFilter: (value, record) => record.groupId.indexOf(value) === 0,
}, },
{ {
title: i18next.t("ldap:Email"), title: i18next.t("general:Email"),
dataIndex: "email", dataIndex: "email",
key: "email", key: "email",
width: "240px", width: "240px",
sorter: (a, b) => a.email.localeCompare(b.email), sorter: (a, b) => a.email.localeCompare(b.email),
}, },
{ {
title: i18next.t("ldap:Phone"), title: i18next.t("general:Phone"),
dataIndex: "phone", dataIndex: "phone",
key: "phone", key: "phone",
width: "160px", width: "160px",
sorter: (a, b) => a.phone.localeCompare(b.phone), sorter: (a, b) => a.phone.localeCompare(b.phone),
}, },
{ {
title: i18next.t("ldap:Address"), title: i18next.t("user:Address"),
dataIndex: "address", dataIndex: "address",
key: "address", key: "address",
sorter: (a, b) => a.address.localeCompare(b.address), sorter: (a, b) => a.address.localeCompare(b.address),
@ -205,7 +205,7 @@ class LdapSyncPage extends React.Component {
onConfirm={() => this.syncUsers()} onConfirm={() => this.syncUsers()}
> >
<Button type="primary" style={{marginLeft: "10px"}}> <Button type="primary" style={{marginLeft: "10px"}}>
{i18next.t("ldap:Sync")} {i18next.t("general:Sync")}
</Button> </Button>
</Popconfirm> </Popconfirm>
<Button style={{marginLeft: "20px"}} <Button style={{marginLeft: "20px"}}

View File

@ -21,8 +21,8 @@ import * as Setting from "./Setting";
import * as Conf from "./Conf"; import * as Conf from "./Conf";
import i18next from "i18next"; import i18next from "i18next";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import LdapTable from "./LdapTable"; import LdapTable from "./table/LdapTable";
import AccountTable from "./AccountTable"; import AccountTable from "./table/AccountTable";
import ThemeEditor from "./common/theme/ThemeEditor"; import ThemeEditor from "./common/theme/ThemeEditor";
const {Option} = Select; const {Option} = Select;
@ -276,7 +276,7 @@ class OrganizationEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:InitScore"), i18next.t("organization:The user's initScore - Tooltip"))} : {Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :
</Col> </Col>
<Col span={4} > <Col span={4} >
<InputNumber value={this.state.organization.initScore} onChange={value => { <InputNumber value={this.state.organization.initScore} onChange={value => {

View File

@ -143,7 +143,7 @@ class OrganizationListPage extends BaseListPage {
...this.getColumnSearchProps("displayName"), ...this.getColumnSearchProps("displayName"),
}, },
{ {
title: i18next.t("organization:Favicon"), title: i18next.t("general:Favicon"),
dataIndex: "favicon", dataIndex: "favicon",
key: "favicon", key: "favicon",
width: "50px", width: "50px",
@ -192,7 +192,7 @@ class OrganizationListPage extends BaseListPage {
...this.getColumnSearchProps("passwordSalt"), ...this.getColumnSearchProps("passwordSalt"),
}, },
{ {
title: i18next.t("organization:Default avatar"), title: i18next.t("general:Default avatar"),
dataIndex: "defaultAvatar", dataIndex: "defaultAvatar",
key: "defaultAvatar", key: "defaultAvatar",
width: "120px", width: "120px",

View File

@ -133,7 +133,7 @@ class PaymentEditPage extends React.Component {
<Descriptions.Item label={i18next.t("payment:Person ID card")} span={3}>{this.state.payment?.personIdCard}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Person ID card")} span={3}>{this.state.payment?.personIdCard}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Person Email")} span={3}>{this.state.payment?.personEmail}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Person Email")} span={3}>{this.state.payment?.personEmail}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Person phone")} span={3}>{this.state.payment?.personPhone}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Person phone")} span={3}>{this.state.payment?.personPhone}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Invoice type")} span={3}>{this.state.payment?.invoiceType === "Individual" ? i18next.t("payment:Individual") : i18next.t("payment:Organization")}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Invoice type")} span={3}>{this.state.payment?.invoiceType === "Individual" ? i18next.t("payment:Individual") : i18next.t("general:Organization")}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Invoice title")} span={3}>{this.state.payment?.invoiceTitle}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Invoice title")} span={3}>{this.state.payment?.invoiceTitle}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Invoice tax ID")} span={3}>{this.state.payment?.invoiceTaxId}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Invoice tax ID")} span={3}>{this.state.payment?.invoiceTaxId}</Descriptions.Item>
<Descriptions.Item label={i18next.t("payment:Invoice remark")} span={3}>{this.state.payment?.invoiceRemark}</Descriptions.Item> <Descriptions.Item label={i18next.t("payment:Invoice remark")} span={3}>{this.state.payment?.invoiceRemark}</Descriptions.Item>
@ -195,7 +195,7 @@ class PaymentEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("payment:Type"), i18next.t("payment:Type - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Type"), i18next.t("payment:Type - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input disabled={true} value={this.state.payment.type} onChange={e => { <Input disabled={true} value={this.state.payment.type} onChange={e => {
@ -215,7 +215,7 @@ class PaymentEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("payment:Price"), i18next.t("payment:Price - Tooltip"))} : {Setting.getLabel(i18next.t("product:Price"), i18next.t("product:Price - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input disabled={true} value={this.state.payment.price} onChange={e => { <Input disabled={true} value={this.state.payment.price} onChange={e => {
@ -235,7 +235,7 @@ class PaymentEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("payment:State"), i18next.t("payment:State - Tooltip"))} : {Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input disabled={true} value={this.state.payment.state} onChange={e => { <Input disabled={true} value={this.state.payment.state} onChange={e => {
@ -312,7 +312,7 @@ class PaymentEditPage extends React.Component {
{ {
[ [
{id: "Individual", name: i18next.t("payment:Individual")}, {id: "Individual", name: i18next.t("payment:Individual")},
{id: "Organization", name: i18next.t("payment:Organization")}, {id: "Organization", name: i18next.t("general:Organization")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) ].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
} }
</Select> </Select>

View File

@ -167,7 +167,7 @@ class PaymentListPage extends BaseListPage {
// ...this.getColumnSearchProps('displayName'), // ...this.getColumnSearchProps('displayName'),
// }, // },
{ {
title: i18next.t("payment:Type"), title: i18next.t("provider:Type"),
dataIndex: "type", dataIndex: "type",
key: "type", key: "type",
width: "140px", width: "140px",
@ -189,7 +189,7 @@ class PaymentListPage extends BaseListPage {
...this.getColumnSearchProps("productDisplayName"), ...this.getColumnSearchProps("productDisplayName"),
}, },
{ {
title: i18next.t("payment:Price"), title: i18next.t("product:Price"),
dataIndex: "price", dataIndex: "price",
key: "price", key: "price",
width: "120px", width: "120px",
@ -205,7 +205,7 @@ class PaymentListPage extends BaseListPage {
...this.getColumnSearchProps("currency"), ...this.getColumnSearchProps("currency"),
}, },
{ {
title: i18next.t("payment:State"), title: i18next.t("general:State"),
dataIndex: "state", dataIndex: "state",
key: "state", key: "state",
width: "120px", width: "120px",

View File

@ -264,7 +264,7 @@ class PermissionEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} : {Setting.getLabel(i18next.t("general:Resources"), i18next.t("permission:Resources - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources} <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources}
@ -346,7 +346,7 @@ class PermissionEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("permission:State"), i18next.t("permission:State - Tooltip"))} : {Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} disabled={!Setting.isLocalAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => { <Select virtual={false} disabled={!Setting.isLocalAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => {

View File

@ -175,7 +175,7 @@ class PermissionListPage extends BaseListPage {
sorter: true, sorter: true,
}, },
{ {
title: i18next.t("permission:Resources"), title: i18next.t("general:Resources"),
dataIndex: "resources", dataIndex: "resources",
key: "resources", key: "resources",
// width: '100px', // width: '100px',
@ -270,7 +270,7 @@ class PermissionListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("permission:State"), title: i18next.t("general:State"),
dataIndex: "state", dataIndex: "state",
key: "state", key: "state",
filterMultiple: false, filterMultiple: false,

View File

@ -22,7 +22,7 @@ export const PopconfirmModal = (props) => {
title={props.title} title={props.title}
onConfirm={props.onConfirm} onConfirm={props.onConfirm}
disabled={props.disabled} disabled={props.disabled}
okText={i18next.t("user:OK")} okText={i18next.t("general:OK")}
cancelText={i18next.t("general:Cancel")} cancelText={i18next.t("general:Cancel")}
> >
<Button style={{marginBottom: "10px"}} disabled={props.disabled} type="primary" danger>{i18next.t("general:Delete")}</Button> <Button style={{marginBottom: "10px"}} disabled={props.disabled} type="primary" danger>{i18next.t("general:Delete")}</Button>

View File

@ -155,8 +155,8 @@ class ProductBuyPage extends React.Component {
text = i18next.t("product:Alipay"); text = i18next.t("product:Alipay");
} else if (provider.type === "WeChat Pay") { } else if (provider.type === "WeChat Pay") {
text = i18next.t("product:WeChat Pay"); text = i18next.t("product:WeChat Pay");
} else if (provider.type === "Paypal") { } else if (provider.type === "PayPal") {
text = i18next.t("product:Paypal"); text = i18next.t("product:PayPal");
} }
return ( return (
@ -216,7 +216,7 @@ class ProductBuyPage extends React.Component {
</span> </span>
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Detail")}><span style={{fontSize: 16}}>{Setting.getLanguageText(product?.detail)}</span></Descriptions.Item> <Descriptions.Item label={i18next.t("product:Detail")}><span style={{fontSize: 16}}>{Setting.getLanguageText(product?.detail)}</span></Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Tag")}><span style={{fontSize: 16}}>{product?.tag}</span></Descriptions.Item> <Descriptions.Item label={i18next.t("user:Tag")}><span style={{fontSize: 16}}>{product?.tag}</span></Descriptions.Item>
<Descriptions.Item label={i18next.t("product:SKU")}><span style={{fontSize: 16}}>{product?.name}</span></Descriptions.Item> <Descriptions.Item label={i18next.t("product:SKU")}><span style={{fontSize: 16}}>{product?.name}</span></Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Image")} span={3}> <Descriptions.Item label={i18next.t("product:Image")} span={3}>
<img src={product?.image} alt={product?.name} height={90} style={{marginBottom: "20px"}} /> <img src={product?.image} alt={product?.name} height={90} style={{marginBottom: "20px"}} />

View File

@ -135,7 +135,7 @@ class ProductEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("product:Tag"), i18next.t("product:Tag - Tooltip"))} : {Setting.getLabel(i18next.t("user:Tag"), i18next.t("product:Tag - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.product.tag} onChange={e => { <Input value={this.state.product.tag} onChange={e => {
@ -155,7 +155,7 @@ class ProductEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("product:Description"), i18next.t("product:Description - Tooltip"))} : {Setting.getLabel(i18next.t("general:Description"), i18next.t("general:Description - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.product.description} onChange={e => { <Input value={this.state.product.description} onChange={e => {
@ -165,7 +165,7 @@ class ProductEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("product:Currency"), i18next.t("product:Currency - Tooltip"))} : {Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.product.currency} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.product.currency} onChange={(value => {

View File

@ -126,7 +126,7 @@ class ProductListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("product:Tag"), title: i18next.t("user:Tag"),
dataIndex: "tag", dataIndex: "tag",
key: "tag", key: "tag",
width: "160px", width: "160px",
@ -134,7 +134,7 @@ class ProductListPage extends BaseListPage {
...this.getColumnSearchProps("tag"), ...this.getColumnSearchProps("tag"),
}, },
{ {
title: i18next.t("product:Currency"), title: i18next.t("payment:Currency"),
dataIndex: "currency", dataIndex: "currency",
key: "currency", key: "currency",
width: "120px", width: "120px",
@ -182,7 +182,7 @@ class ProductListPage extends BaseListPage {
render: (text, record, index) => { render: (text, record, index) => {
const providers = text; const providers = text;
if (providers.length === 0) { if (providers.length === 0) {
return "(empty)"; return `(${i18next.t("general:empty")})`;
} }
const half = Math.floor((providers.length + 1) / 2); const half = Math.floor((providers.length + 1) / 2);

View File

@ -19,12 +19,12 @@ import * as ProviderBackend from "./backend/ProviderBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import {authConfig} from "./auth/Auth"; import {authConfig} from "./auth/Auth";
import * as ProviderEditTestEmail from "./TestEmailWidget"; import * as ProviderEditTestEmail from "./common/TestEmailWidget";
import * as ProviderEditTestSms from "./TestSmsWidget"; import * as ProviderEditTestSms from "./common/TestSmsWidget";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import {CaptchaPreview} from "./common/CaptchaPreview"; import {CaptchaPreview} from "./common/CaptchaPreview";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import {CountryCodeSelect} from "./common/CountryCodeSelect"; import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
const {Option} = Select; const {Option} = Select;
const {TextArea} = Input; const {TextArea} = Input;
@ -110,10 +110,10 @@ class ProviderEditPage extends React.Component {
getClientSecretLabel(provider) { getClientSecretLabel(provider) {
switch (provider.category) { switch (provider.category) {
case "Email": case "Email":
return Setting.getLabel(i18next.t("login:Password"), i18next.t("login:Password - Tooltip")); return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
case "SMS": case "SMS":
if (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")); return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
} else if (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")); return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));
} else { } else {
@ -121,7 +121,7 @@ class ProviderEditPage extends React.Component {
} }
case "Captcha": case "Captcha":
if (provider.type === "Aliyun Captcha") { if (provider.type === "Aliyun Captcha") {
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:SecretAccessKey - Tooltip")); return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
} else { } else {
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip")); return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
} }
@ -130,6 +130,24 @@ class ProviderEditPage extends React.Component {
} }
} }
getProviderSubTypeOptions(type) {
if (type === "WeCom" || type === "Infoflow") {
return (
[
{id: "Internal", name: i18next.t("provider:Internal")},
{id: "Third-party", name: i18next.t("provider:Third-party")},
]
);
} else if (type === "Aliyun Captcha") {
return [
{id: "nc", name: i18next.t("provider:Sliding Validation")},
{id: "ic", name: i18next.t("provider:Intelligent Validation")},
];
} else {
return [];
}
}
getAppIdRow(provider) { getAppIdRow(provider) {
let text = ""; let text = "";
let tooltip = ""; let tooltip = "";
@ -225,7 +243,7 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.provider.owner} onChange={(value => {this.updateProviderField("owner", value);})}> <Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.provider.owner} onChange={(value => {this.updateProviderField("owner", value);})}>
{Setting.isAdminUser(this.props.account) ? <Option key={"admin"} value={"admin"}>{i18next.t("provider:admin (share)")}</Option> : null} {Setting.isAdminUser(this.props.account) ? <Option key={"admin"} value={"admin"}>{i18next.t("provider:admin (Shared)")}</Option> : null}
{ {
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
} }
@ -315,7 +333,7 @@ class ProviderEditPage extends React.Component {
this.updateProviderField("subType", value); this.updateProviderField("subType", value);
}}> }}>
{ {
Setting.getProviderSubTypeOptions(this.state.provider.type).map((providerSubType, index) => <Option key={index} value={providerSubType.id}>{providerSubType.name}</Option>) this.getProviderSubTypeOptions(this.state.provider.type).map((providerSubType, index) => <Option key={index} value={providerSubType.id}>{providerSubType.name}</Option>)
} }
</Select> </Select>
</Col> </Col>
@ -324,14 +342,17 @@ class ProviderEditPage extends React.Component {
this.state.provider.type !== "WeCom" ? null : ( this.state.provider.type !== "WeCom" ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Method"), i18next.t("provider:Method - Tooltip"))} : {Setting.getLabel(i18next.t("general:Method"), i18next.t("provider:Method - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.method} onChange={value => { <Select virtual={false} style={{width: "100%"}} value={this.state.provider.method} onChange={value => {
this.updateProviderField("method", value); this.updateProviderField("method", value);
}}> }}>
{ {
[{name: "Normal"}, {name: "Silent"}].map((method, index) => <Option key={index} value={method.name}>{method.name}</Option>) [
{id: "Normal", name: i18next.t("provider:Normal")},
{id: "Silent", name: i18next.t("provider:Silent")},
].map((method, index) => <Option key={index} value={method.name}>{method.name}</Option>)
} }
</Select> </Select>
</Col> </Col>
@ -484,7 +505,7 @@ class ProviderEditPage extends React.Component {
) )
} }
{ {
this.state.provider.type !== "Adfs" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : ( this.state.provider.type !== "Adfs" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
@ -531,7 +552,7 @@ class ProviderEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:The prefix path of the file - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:Path prefix - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.provider.pathPrefix} onChange={e => { <Input value={this.state.provider.pathPrefix} onChange={e => {
@ -626,7 +647,7 @@ class ProviderEditPage extends React.Component {
}} /> }} />
</Col> </Col>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} > <Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
{i18next.t("provider:Test Connection")} {i18next.t("provider:Test SMTP Connection")}
</Button> </Button>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" <Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary"
disabled={!Setting.isValidEmail(this.state.provider.receiver)} disabled={!Setting.isValidEmail(this.state.provider.receiver)}
@ -653,7 +674,7 @@ class ProviderEditPage extends React.Component {
} }
<Row style={{marginTop: "20px"}} > <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("provider:Template Code"), i18next.t("provider:Template Code - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Template code"), i18next.t("provider:Template code - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.provider.templateCode} onChange={e => { <Input value={this.state.provider.templateCode} onChange={e => {
@ -720,9 +741,9 @@ class ProviderEditPage extends React.Component {
<Button type="primary" onClick={() => { <Button type="primary" onClick={() => {
try { try {
this.loadSamlConfiguration(); this.loadSamlConfiguration();
Setting.showMessage("success", i18next.t("provider:Parse Metadata successfully")); Setting.showMessage("success", i18next.t("provider:Parse metadata successfully"));
} catch (err) { } catch (err) {
Setting.showMessage("error", i18next.t("provider:Can not parse Metadata")); Setting.showMessage("error", i18next.t("provider:Can not parse metadata"));
} }
}}> }}>
{i18next.t("provider:Parse")} {i18next.t("provider:Parse")}

View File

@ -156,7 +156,7 @@ class RecordListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("record:Is Triggered"), title: i18next.t("record:Is triggered"),
dataIndex: "isTriggered", dataIndex: "isTriggered",
key: "isTriggered", key: "isTriggered",
width: "140px", width: "140px",

View File

@ -100,7 +100,7 @@ class ResourceListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("resource:Application"), title: i18next.t("general:Application"),
dataIndex: "application", dataIndex: "application",
key: "application", key: "application",
width: "80px", width: "80px",
@ -115,7 +115,7 @@ class ResourceListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("resource:User"), title: i18next.t("general:User"),
dataIndex: "user", dataIndex: "user",
key: "user", key: "user",
width: "80px", width: "80px",
@ -156,7 +156,7 @@ class ResourceListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("resource:Tag"), title: i18next.t("user:Tag"),
dataIndex: "tag", dataIndex: "tag",
key: "tag", key: "tag",
width: "80px", width: "80px",
@ -171,7 +171,7 @@ class ResourceListPage extends BaseListPage {
// sorter: (a, b) => a.fileName.localeCompare(b.fileName), // sorter: (a, b) => a.fileName.localeCompare(b.fileName),
// }, // },
{ {
title: i18next.t("resource:Type"), title: i18next.t("provider:Type"),
dataIndex: "fileType", dataIndex: "fileType",
key: "fileType", key: "fileType",
width: "80px", width: "80px",
@ -227,7 +227,7 @@ class ResourceListPage extends BaseListPage {
<div> <div>
<Button type="normal" onClick={() => { <Button type="normal" onClick={() => {
copy(record.url); copy(record.url);
Setting.showMessage("success", i18next.t("resource:Link copied to clipboard successfully")); Setting.showMessage("success", i18next.t("provider:Link copied to clipboard successfully"));
}} }}
> >
{i18next.t("resource:Copy Link")} {i18next.t("resource:Copy Link")}
@ -248,8 +248,8 @@ class ResourceListPage extends BaseListPage {
<PopconfirmModal <PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`} title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteResource(index)} onConfirm={() => this.deleteResource(index)}
okText={i18next.t("user:OK")} okText={i18next.t("general:OK")}
cancelText={i18next.t("user:Cancel")} cancelText={i18next.t("general:Cancel")}
> >
</PopconfirmModal> </PopconfirmModal>
</div> </div>

View File

@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Checkbox, Form, Modal, Select, Tag, Tooltip, message, theme} from "antd"; import {Select, Tag, Tooltip, message, theme} from "antd";
import {QuestionCircleTwoTone} from "@ant-design/icons"; import {QuestionCircleTwoTone} from "@ant-design/icons";
import {isMobile as isMobileDevice} from "react-device-detect"; import {isMobile as isMobileDevice} from "react-device-detect";
import "./i18n"; import "./i18n";
@ -38,6 +38,7 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
{label: "Español", key: "es", country: "ES", alt: "Español"}, {label: "Español", key: "es", country: "ES", alt: "Español"},
{label: "Français", key: "fr", country: "FR", alt: "Français"}, {label: "Français", key: "fr", country: "FR", alt: "Français"},
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"}, {label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
{label: "Indonesia", key: "id", country: "ID", alt: "Indonesia"},
{label: "日本語", key: "ja", country: "JP", alt: "日本語"}, {label: "日本語", key: "ja", country: "JP", alt: "日本語"},
{label: "한국어", key: "ko", country: "KR", alt: "한국어"}, {label: "한국어", key: "ko", country: "KR", alt: "한국어"},
{label: "Русский", key: "ru", country: "RU", alt: "Русский"}, {label: "Русский", key: "ru", country: "RU", alt: "Русский"},
@ -551,10 +552,6 @@ export function addRow(array, row, position = "end") {
return position === "end" ? [...array, row] : [row, ...array]; return position === "end" ? [...array, row] : [row, ...array];
} }
export function prependRow(array, row) {
return [row, ...array];
}
export function deleteRow(array, i) { export function deleteRow(array, i) {
// return array = array.slice(0, i).concat(array.slice(i + 1)); // return array = array.slice(0, i).concat(array.slice(i + 1));
return [...array.slice(0, i), ...array.slice(i + 1)]; return [...array.slice(0, i), ...array.slice(i + 1)];
@ -584,76 +581,6 @@ export function isMobile() {
return isMobileDevice; return isMobileDevice;
} }
export function getTermsOfUseContent(url, setTermsOfUseContent) {
fetch(url, {
method: "GET",
}).then(r => {
r.text().then(setTermsOfUseContent);
});
}
export function isAgreementRequired(application) {
if (application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
if (!agreementItem || agreementItem.rule === "None" || !agreementItem.rule) {
return false;
}
if (agreementItem.required) {
return true;
}
}
return false;
}
export function isDefaultTrue(application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
return isAgreementRequired(application) && agreementItem.rule === "Signin (Default True)";
}
export function renderAgreement(required, onClick, noStyle, layout, initialValue) {
return (
<Form.Item
name="agreement"
key="agreement"
valuePropName="checked"
rules={[
{
required: required,
message: i18next.t("signup:Please accept the agreement!"),
},
]}
{...layout}
noStyle={noStyle}
initialValue={initialValue}
>
<Checkbox style={{float: "left"}}>
{i18next.t("signup:Accept")}&nbsp;
<a onClick={onClick}>
{i18next.t("signup:Terms of Use")}
</a>
</Checkbox>
</Form.Item>
);
}
export function renderModal(isOpen, onOk, onCancel, doc) {
return (
<Modal
title={i18next.t("signup:Terms of Use")}
open={isOpen}
width={"55vw"}
closable={false}
okText={i18next.t("signup:Accept")}
cancelText={i18next.t("signup:Decline")}
onOk={onOk}
onCancel={onCancel}
>
<iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={doc} />
</Modal>
);
}
export function getFormattedDate(date) { export function getFormattedDate(date) {
if (date === undefined) { if (date === undefined) {
return null; return null;
@ -672,6 +599,22 @@ export function getShortName(s) {
return s.split("/").slice(-1)[0]; return s.split("/").slice(-1)[0];
} }
export function getNameAtLeast(s) {
s = getShortName(s);
if (s.length >= 6) {
return s;
}
return (
<React.Fragment>
&nbsp;
{s}
&nbsp;
&nbsp;
</React.Fragment>
);
}
export function getShortText(s, maxLength = 35) { export function getShortText(s, maxLength = 35) {
if (s.length > maxLength) { if (s.length > maxLength) {
return `${s.slice(0, maxLength)}...`; return `${s.slice(0, maxLength)}...`;
@ -714,14 +657,6 @@ export function getAvatarColor(s) {
return colorList[hash % 4]; return colorList[hash % 4];
} }
export function getLogo(theme) {
if (theme === "Dark") {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256_dark.png`;
} else {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256.png`;
}
}
export function getLanguageText(text) { export function getLanguageText(text) {
if (!text.includes("|")) { if (!text.includes("|")) {
return text; return text;
@ -746,11 +681,6 @@ export function setLanguage(language) {
i18next.changeLanguage(language); i18next.changeLanguage(language);
} }
export function setTheme(themeKey) {
localStorage.setItem("theme", themeKey);
dispatchEvent(new Event("changeTheme"));
}
export function getAcceptLanguage() { export function getAcceptLanguage() {
if (i18next.language === null || i18next.language === "") { if (i18next.language === null || i18next.language === "") {
return "en;q=0.9,en;q=0.8"; return "en;q=0.9,en;q=0.8";
@ -851,7 +781,7 @@ export function getProviderTypeOptions(category) {
{id: "OneDrive", name: "OneDrive"}, {id: "OneDrive", name: "OneDrive"},
{id: "Oura", name: "Oura"}, {id: "Oura", name: "Oura"},
{id: "Patreon", name: "Patreon"}, {id: "Patreon", name: "Patreon"},
{id: "Paypal", name: "Paypal"}, {id: "PayPal", name: "PayPal"},
{id: "SalesForce", name: "SalesForce"}, {id: "SalesForce", name: "SalesForce"},
{id: "Shopify", name: "Shopify"}, {id: "Shopify", name: "Shopify"},
{id: "Soundcloud", name: "Soundcloud"}, {id: "Soundcloud", name: "Soundcloud"},
@ -931,24 +861,6 @@ export function getProviderTypeOptions(category) {
} }
} }
export function getProviderSubTypeOptions(type) {
if (type === "WeCom" || type === "Infoflow") {
return (
[
{id: "Internal", name: "Internal"},
{id: "Third-party", name: "Third-party"},
]
);
} else if (type === "Aliyun Captcha") {
return [
{id: "nc", name: "Sliding Validation"},
{id: "ic", name: "Intelligent Validation"},
];
} else {
return [];
}
}
export function renderLogo(application) { export function renderLogo(application) {
if (application === null) { if (application === null) {
return null; return null;
@ -1222,94 +1134,3 @@ export function inIframe() {
return true; return true;
} }
} }
export function getSyncerTableColumns(syncer) {
switch (syncer.type) {
case "Keycloak":
return [
{
"name": "ID",
"type": "string",
"casdoorName": "Id",
"isHashed": true,
"values": [
],
},
{
"name": "USERNAME",
"type": "string",
"casdoorName": "Name",
"isHashed": true,
"values": [
],
},
{
"name": "LAST_NAME+FIRST_NAME",
"type": "string",
"casdoorName": "DisplayName",
"isHashed": true,
"values": [
],
},
{
"name": "EMAIL",
"type": "string",
"casdoorName": "Email",
"isHashed": true,
"values": [
],
},
{
"name": "EMAIL_VERIFIED",
"type": "boolean",
"casdoorName": "EmailVerified",
"isHashed": true,
"values": [
],
},
{
"name": "FIRST_NAME",
"type": "string",
"casdoorName": "FirstName",
"isHashed": true,
"values": [
],
},
{
"name": "LAST_NAME",
"type": "string",
"casdoorName": "LastName",
"isHashed": true,
"values": [
],
},
{
"name": "CREATED_TIMESTAMP",
"type": "string",
"casdoorName": "CreatedTime",
"isHashed": true,
"values": [
],
},
{
"name": "ENABLED",
"type": "boolean",
"casdoorName": "IsForbidden",
"isHashed": true,
"values": [
],
},
];
default:
return [];
}
}

View File

@ -19,7 +19,7 @@ import * as SyncerBackend from "./backend/SyncerBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import SyncerTableColumnTable from "./SyncerTableColumnTable"; import SyncerTableColumnTable from "./table/SyncerTableColumnTable";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
@ -80,6 +80,97 @@ class SyncerEditPage extends React.Component {
}); });
} }
getSyncerTableColumns(syncer) {
switch (syncer.type) {
case "Keycloak":
return [
{
"name": "ID",
"type": "string",
"casdoorName": "Id",
"isHashed": true,
"values": [
],
},
{
"name": "USERNAME",
"type": "string",
"casdoorName": "Name",
"isHashed": true,
"values": [
],
},
{
"name": "LAST_NAME+FIRST_NAME",
"type": "string",
"casdoorName": "DisplayName",
"isHashed": true,
"values": [
],
},
{
"name": "EMAIL",
"type": "string",
"casdoorName": "Email",
"isHashed": true,
"values": [
],
},
{
"name": "EMAIL_VERIFIED",
"type": "boolean",
"casdoorName": "EmailVerified",
"isHashed": true,
"values": [
],
},
{
"name": "FIRST_NAME",
"type": "string",
"casdoorName": "FirstName",
"isHashed": true,
"values": [
],
},
{
"name": "LAST_NAME",
"type": "string",
"casdoorName": "LastName",
"isHashed": true,
"values": [
],
},
{
"name": "CREATED_TIMESTAMP",
"type": "string",
"casdoorName": "CreatedTime",
"isHashed": true,
"values": [
],
},
{
"name": "ENABLED",
"type": "boolean",
"casdoorName": "IsForbidden",
"isHashed": true,
"values": [
],
},
];
default:
return [];
}
}
renderSyncer() { renderSyncer() {
return ( return (
<Card size="small" title={ <Card size="small" title={
@ -120,7 +211,7 @@ class SyncerEditPage extends React.Component {
<Select virtual={false} style={{width: "100%"}} value={this.state.syncer.type} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.syncer.type} onChange={(value => {
this.updateSyncerField("type", value); this.updateSyncerField("type", value);
const syncer = this.state.syncer; const syncer = this.state.syncer;
syncer["tableColumns"] = Setting.getSyncerTableColumns(this.state.syncer); syncer["tableColumns"] = this.getSyncerTableColumns(this.state.syncer);
syncer.table = (value === "Keycloak") ? "user_entity" : this.state.syncer.table; syncer.table = (value === "Keycloak") ? "user_entity" : this.state.syncer.table;
this.setState({ this.setState({
syncer: syncer, syncer: syncer,

View File

@ -67,14 +67,14 @@ class SystemInfo extends React.Component {
} }
render() { render() {
const cpuUi = this.state.systemInfo.cpuUsage?.length <= 0 ? i18next.t("system:Get CPU Usage Failed") : const cpuUi = this.state.systemInfo.cpuUsage?.length <= 0 ? i18next.t("system:Failed to get CPU usage") :
this.state.systemInfo.cpuUsage.map((usage, i) => { this.state.systemInfo.cpuUsage.map((usage, i) => {
return ( return (
<Progress key={i} percent={Number(usage.toFixed(1))} /> <Progress key={i} percent={Number(usage.toFixed(1))} />
); );
}); });
const memUi = this.state.systemInfo.memoryUsed && this.state.systemInfo.memoryTotal && this.state.systemInfo.memoryTotal <= 0 ? i18next.t("system:Get Memory Usage Failed") : const memUi = this.state.systemInfo.memoryUsed && this.state.systemInfo.memoryTotal && this.state.systemInfo.memoryTotal <= 0 ? i18next.t("system:Failed to get memory usage") :
<div> <div>
{Setting.getFriendlyFileSize(this.state.systemInfo.memoryUsed)} / {Setting.getFriendlyFileSize(this.state.systemInfo.memoryTotal)} {Setting.getFriendlyFileSize(this.state.systemInfo.memoryUsed)} / {Setting.getFriendlyFileSize(this.state.systemInfo.memoryTotal)}
<br /> <br /> <br /> <br />
@ -82,7 +82,7 @@ class SystemInfo extends React.Component {
</div>; </div>;
const link = this.state.versionInfo?.version !== "" ? `https://github.com/casdoor/casdoor/releases/tag/${this.state.versionInfo?.version}` : ""; const link = this.state.versionInfo?.version !== "" ? `https://github.com/casdoor/casdoor/releases/tag/${this.state.versionInfo?.version}` : "";
let versionText = this.state.versionInfo?.version !== "" ? this.state.versionInfo?.version : i18next.t("system:Unknown Version"); let versionText = this.state.versionInfo?.version !== "" ? this.state.versionInfo?.version : i18next.t("system:Unknown version");
if (this.state.versionInfo?.commitOffset > 0) { if (this.state.versionInfo?.commitOffset > 0) {
versionText += ` (ahead+${this.state.versionInfo?.commitOffset})`; versionText += ` (ahead+${this.state.versionInfo?.commitOffset})`;
} }
@ -111,7 +111,7 @@ class SystemInfo extends React.Component {
<br /> <br />
{i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a> {i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a>
<br /> <br />
{i18next.t("system:Official Website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a> {i18next.t("system:Official website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a>
<br /> <br />
{i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a> {i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a>
</Card> </Card>
@ -139,7 +139,7 @@ class SystemInfo extends React.Component {
<br /> <br />
{i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a> {i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a>
<br /> <br />
{i18next.t("system:Official Website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a> {i18next.t("system:Official website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a>
<br /> <br />
{i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a> {i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a>
</Card> </Card>

View File

@ -141,7 +141,7 @@ class TokenEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("token:Scope")}: {i18next.t("provider:Scope")}:
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.token.scope} onChange={e => { <Input value={this.state.token.scope} onChange={e => {

View File

@ -178,7 +178,7 @@ class TokenListPage extends BaseListPage {
...this.getColumnSearchProps("expiresIn"), ...this.getColumnSearchProps("expiresIn"),
}, },
{ {
title: i18next.t("token:Scope"), title: i18next.t("provider:Scope"),
dataIndex: "scope", dataIndex: "scope",
key: "scope", key: "scope",
width: "110px", width: "110px",

View File

@ -18,18 +18,18 @@ import * as UserBackend from "./backend/UserBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import CropperDiv from "./CropperDiv.js"; import CropperDivModal from "./common/modal/CropperDivModal.js";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import PasswordModal from "./PasswordModal"; import PasswordModal from "./common/modal/PasswordModal";
import ResetModal from "./ResetModal"; import ResetModal from "./common/modal/ResetModal";
import AffiliationSelect from "./common/AffiliationSelect"; import AffiliationSelect from "./common/select/AffiliationSelect";
import OAuthWidget from "./common/OAuthWidget"; import OAuthWidget from "./common/OAuthWidget";
import SamlWidget from "./common/SamlWidget"; import SamlWidget from "./common/SamlWidget";
import SelectRegionBox from "./SelectRegionBox"; import RegionSelect from "./common/select/RegionSelect";
import WebAuthnCredentialTable from "./WebauthnCredentialTable"; import WebAuthnCredentialTable from "./table/WebauthnCredentialTable";
import ManagedAccountTable from "./ManagedAccountTable"; import ManagedAccountTable from "./table/ManagedAccountTable";
import PropertyTable from "./propertyTable"; import PropertyTable from "./table/propertyTable";
import {CountryCodeSelect} from "./common/CountryCodeSelect"; import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
const {Option} = Select; const {Option} = Select;
@ -253,7 +253,7 @@ class UserEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<CropperDiv buttonText={`${i18next.t("user:Upload a photo")}...`} title={i18next.t("user:Upload a photo")} user={this.state.user} organization={this.state.organizations.find(organization => organization.name === this.state.organizationName)} /> <CropperDivModal buttonText={`${i18next.t("user:Upload a photo")}...`} title={i18next.t("user:Upload a photo")} user={this.state.user} organization={this.state.organizations.find(organization => organization.name === this.state.organizationName)} />
</Row> </Row>
</Col> </Col>
</Row> </Row>
@ -341,7 +341,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("user:Country/Region"), i18next.t("user:Country/Region - Tooltip"))} : {Setting.getLabel(i18next.t("user:Country/Region"), i18next.t("user:Country/Region - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<SelectRegionBox defaultValue={this.state.user.region} onChange={(value) => { <RegionSelect defaultValue={this.state.user.region} onChange={(value) => {
this.updateUserField("region", value); this.updateUserField("region", value);
}} /> }} />
</Col> </Col>

View File

@ -19,7 +19,7 @@ import * as WebhookBackend from "./backend/WebhookBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import WebhookHeaderTable from "./WebhookHeaderTable"; import WebhookHeaderTable from "./table/WebhookHeaderTable";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
@ -180,7 +180,7 @@ class WebhookEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("webhook:URL"), i18next.t("webhook:URL - Tooltip"))} : {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.webhook.url} onChange={e => { <Input prefix={<LinkOutlined />} value={this.state.webhook.url} onChange={e => {
@ -190,7 +190,7 @@ class WebhookEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <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("webhook:Method"), i18next.t("webhook:Method - Tooltip"))} : {Setting.getLabel(i18next.t("general:Method"), i18next.t("webhook:Method - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.webhook.method} onChange={(value => {this.updateWebhookField("method", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.webhook.method} onChange={(value => {this.updateWebhookField("method", value);})}>

View File

@ -117,7 +117,7 @@ class WebhookListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("webhook:URL"), title: i18next.t("general:URL"),
dataIndex: "url", dataIndex: "url",
key: "url", key: "url",
width: "300px", width: "300px",
@ -134,7 +134,7 @@ class WebhookListPage extends BaseListPage {
}, },
}, },
{ {
title: i18next.t("webhook:Method"), title: i18next.t("general:Method"),
dataIndex: "method", dataIndex: "method",
key: "method", key: "method",
width: "120px", width: "120px",

View File

@ -139,7 +139,7 @@ class AuthCallback extends React.Component {
window.location.href = newUrl.toString(); window.location.href = newUrl.toString();
} }
} else { } else {
Setting.showMessage("error", `Failed to log in: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
} }
}); });
return; return;

View File

@ -22,7 +22,7 @@ import i18next from "i18next";
import {SendCodeInput} from "../common/SendCodeInput"; import {SendCodeInput} from "../common/SendCodeInput";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons"; import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../common/CustomGithubCorner";
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
const {Option} = Select; const {Option} = Select;
@ -32,7 +32,6 @@ class ForgetPage extends React.Component {
this.state = { this.state = {
classes: props, classes: props,
applicationName: props.applicationName ?? props.match.params?.applicationName, applicationName: props.applicationName ?? props.match.params?.applicationName,
application: null,
msg: null, msg: null,
userId: "", userId: "",
username: "", username: "",
@ -49,11 +48,11 @@ class ForgetPage extends React.Component {
} }
componentDidMount() { componentDidMount() {
if (this.getApplicationObj() === null) { if (this.getApplicationObj() === undefined) {
if (this.state.applicationName !== undefined) { if (this.state.applicationName !== undefined) {
this.getApplication(); this.getApplication();
} else { } else {
Setting.showMessage("error", i18next.t("forget:Unknown forget type: ") + this.state.type); Setting.showMessage("error", i18next.t("forget:Unknown forget type") + ": " + this.state.type);
} }
} }
} }
@ -66,14 +65,11 @@ class ForgetPage extends React.Component {
ApplicationBackend.getApplication("admin", this.state.applicationName) ApplicationBackend.getApplication("admin", this.state.applicationName)
.then((application) => { .then((application) => {
this.onUpdateApplication(application); this.onUpdateApplication(application);
this.setState({
application: application,
});
}); });
} }
getApplicationObj() { getApplicationObj() {
return this.props.application ?? this.state.application; return this.props.application;
} }
onUpdateApplication(application) { onUpdateApplication(application) {
@ -393,7 +389,7 @@ class ForgetPage extends React.Component {
<Input.Password <Input.Password
disabled={this.state.userId === ""} disabled={this.state.userId === ""}
prefix={<LockOutlined />} prefix={<LockOutlined />}
placeholder={i18next.t("forget:Password")} placeholder={i18next.t("general:Password")}
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@ -420,7 +416,7 @@ class ForgetPage extends React.Component {
<Input.Password <Input.Password
disabled={this.state.userId === ""} disabled={this.state.userId === ""}
prefix={<CheckCircleOutlined />} prefix={<CheckCircleOutlined />}
placeholder={i18next.t("forget:Confirm")} placeholder={i18next.t("signup:Confirm")}
/> />
</Form.Item> </Form.Item>
<br /> <br />
@ -436,6 +432,9 @@ class ForgetPage extends React.Component {
render() { render() {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
if (application === undefined) {
return null;
}
if (application === null) { if (application === null) {
return Util.renderMessageLarge(this, this.state.msg); return Util.renderMessageLarge(this, this.state.msg);
} }

View File

@ -14,11 +14,10 @@
import i18next from "i18next"; import i18next from "i18next";
import {createButton} from "react-social-login-buttons"; import {createButton} from "react-social-login-buttons";
import {StaticBaseUrl} from "../Setting";
function LoginButton({type, align = "center", style = {background: "#ffffff", color: "#000000"}, activeStyle = {background: "#ededee"}}) { function LoginButton({type, logoUrl, align = "center", style = {background: "#ffffff", color: "#000000"}, activeStyle = {background: "#ededee"}}) {
function Icon({width = 24, height = 24, color}) { function Icon({width = 24, height = 24, color}) {
return <img src={`${StaticBaseUrl}/buttons/${type.toLowerCase()}.svg`} alt={`Sign in with ${type}`} style={{width: width, height: height}} />; return <img src={logoUrl} alt={`Sign in with ${type}`} style={{width: width, height: height}} />;
} }
const config = { const config = {
text: `Sign in with ${type}`, text: `Sign in with ${type}`,

View File

@ -24,12 +24,13 @@ import * as Provider from "./Provider";
import * as ProviderButton from "./ProviderButton"; import * as ProviderButton from "./ProviderButton";
import * as Util from "./Util"; import * as Util from "./Util";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as AgreementModal from "../common/modal/AgreementModal";
import SelfLoginButton from "./SelfLoginButton"; import SelfLoginButton from "./SelfLoginButton";
import i18next from "i18next"; import i18next from "i18next";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../common/CustomGithubCorner";
import {SendCodeInput} from "../common/SendCodeInput"; import {SendCodeInput} from "../common/SendCodeInput";
import SelectLanguageBox from "../SelectLanguageBox"; import LanguageSelect from "../common/select/LanguageSelect";
import {CaptchaModal} from "../common/CaptchaModal"; import {CaptchaModal} from "../common/modal/CaptchaModal";
import RedirectForm from "../common/RedirectForm"; import RedirectForm from "../common/RedirectForm";
class LoginPage extends React.Component { class LoginPage extends React.Component {
@ -38,10 +39,9 @@ class LoginPage extends React.Component {
this.state = { this.state = {
classes: props, classes: props,
type: props.type, type: props.type,
applicationName: props.applicationName !== undefined ? props.applicationName : (props.match === undefined ? null : props.match.params.applicationName), applicationName: props.applicationName ?? (props.match?.params?.applicationName ?? null),
owner: props.owner !== undefined ? props.owner : (props.match === undefined ? null : props.match.params.owner), owner: props.owner ?? (props.match?.params?.owner ?? null),
application: null, mode: props.mode ?? (props.match?.params?.mode ?? null), // "signup" or "signin"
mode: props.mode !== undefined ? props.mode : (props.match === undefined ? null : props.match.params.mode), // "signup" or "signin"
msg: null, msg: null,
username: null, username: null,
validEmailOrPhone: false, validEmailOrPhone: false,
@ -58,21 +58,19 @@ class LoginPage extends React.Component {
}; };
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) { if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
this.state.owner = props.match?.params.owner; this.state.owner = props.match?.params?.owner;
this.state.applicationName = props.match?.params.casApplicationName; this.state.applicationName = props.match?.params?.casApplicationName;
} }
this.form = React.createRef(); this.form = React.createRef();
} }
componentDidMount() { componentDidMount() {
if (this.getApplicationObj() === null) { if (this.getApplicationObj() === undefined) {
if (this.state.type === "login" || this.state.type === "cas") { if (this.state.type === "login" || this.state.type === "cas" || this.state.type === "saml") {
this.getApplication(); this.getApplication();
} else if (this.state.type === "code") { } else if (this.state.type === "code") {
this.getApplicationLogin(); this.getApplicationLogin();
} else if (this.state.type === "saml") {
this.getSamlApplication();
} else { } else {
Setting.showMessage("error", `Unknown authentication type: ${this.state.type}`); Setting.showMessage("error", `Unknown authentication type: ${this.state.type}`);
} }
@ -80,14 +78,35 @@ class LoginPage extends React.Component {
} }
componentDidUpdate(prevProps, prevState, snapshot) { componentDidUpdate(prevProps, prevState, snapshot) {
if (this.state.application && !prevState.application) { if (prevProps.application !== this.props.application) {
const captchaProviderItems = this.getCaptchaProviderItems(this.state.application); const captchaProviderItems = this.getCaptchaProviderItems(this.props.application);
if (captchaProviderItems) {
if (!captchaProviderItems) { this.setState({enableCaptchaModal: captchaProviderItems.some(providerItem => providerItem.rule === "Always")});
return;
} }
this.setState({enableCaptchaModal: captchaProviderItems.some(providerItem => providerItem.rule === "Always")}); if (this.props.account && this.props.account.owner === this.props.application?.organization) {
const params = new URLSearchParams(this.props.location.search);
const silentSignin = params.get("silentSignin");
if (silentSignin !== null) {
this.sendSilentSigninData("signing-in");
const values = {};
values["application"] = this.props.application.name;
this.login(values);
}
if (params.get("popup") === "1") {
window.addEventListener("beforeunload", () => {
this.sendPopupData({type: "windowClosed"}, params.get("redirect_uri"));
});
}
if (this.props.application.enableAutoSignin) {
const values = {};
values["application"] = this.props.application.name;
this.login(values);
}
}
} }
} }
@ -96,47 +115,37 @@ class LoginPage extends React.Component {
AuthBackend.getApplicationLogin(oAuthParams) AuthBackend.getApplicationLogin(oAuthParams)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.onUpdateApplication(res.data); const application = res.data;
this.setState({ this.onUpdateApplication(application);
application: res.data,
});
} else { } else {
// Setting.showMessage("error", res.msg);
this.onUpdateApplication(null); this.onUpdateApplication(null);
this.setState({ this.setState({
application: res.data,
msg: res.msg, msg: res.msg,
}); });
} }
}); });
return null;
} }
getApplication() { getApplication() {
if (this.state.applicationName === null) { if (this.state.applicationName === null) {
return; return null;
} }
if (this.state.owner === null || this.state.owner === undefined || this.state.owner === "") { if (this.state.owner === null || this.state.type === "saml") {
ApplicationBackend.getApplication("admin", this.state.applicationName) ApplicationBackend.getApplication("admin", this.state.applicationName)
.then((application) => { .then((application) => {
this.onUpdateApplication(application); this.onUpdateApplication(application);
this.setState({
application: application,
}, () => Setting.getTermsOfUseContent(this.state.application.termsOfUse, res => {
this.setState({termsOfUseContent: res});
}));
}); });
} else { } else {
OrganizationBackend.getDefaultApplication("admin", this.state.owner) OrganizationBackend.getDefaultApplication("admin", this.state.owner)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.onUpdateApplication(res.data); const application = res.data;
this.onUpdateApplication(application);
this.setState({ this.setState({
application: res.data,
applicationName: res.data.name, applicationName: res.data.name,
}, () => Setting.getTermsOfUseContent(this.state.application.termsOfUse, res => { });
this.setState({termsOfUseContent: res});
}));
} else { } else {
this.onUpdateApplication(null); this.onUpdateApplication(null);
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
@ -145,21 +154,8 @@ class LoginPage extends React.Component {
} }
} }
getSamlApplication() {
if (this.state.applicationName === null) {
return;
}
ApplicationBackend.getApplication(this.state.owner, this.state.applicationName)
.then((application) => {
this.onUpdateApplication(application);
this.setState({
application: application,
});
});
}
getApplicationObj() { getApplicationObj() {
return this.props.application ?? this.state.application; return this.props.application;
} }
onUpdateAccount(account) { onUpdateAccount(account) {
@ -183,24 +179,25 @@ class LoginPage extends React.Component {
} }
populateOauthValues(values) { populateOauthValues(values) {
if (this.getApplicationObj()?.organization) {
values["organization"] = this.getApplicationObj().organization;
}
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null && oAuthParams.responseType !== null && oAuthParams.responseType !== "") {
values["type"] = oAuthParams.responseType;
} else {
values["type"] = this.state.type;
}
if (oAuthParams !== null) { values["type"] = oAuthParams?.responseType ?? this.state.type;
if (oAuthParams?.samlRequest) {
values["samlRequest"] = oAuthParams.samlRequest; values["samlRequest"] = oAuthParams.samlRequest;
}
if (values["samlRequest"] !== null && values["samlRequest"] !== "" && values["samlRequest"] !== undefined) {
values["type"] = "saml"; values["type"] = "saml";
values["relayState"] = oAuthParams.relayState; values["relayState"] = oAuthParams.relayState;
} }
}
if (this.getApplicationObj()?.organization) { sendPopupData(message, redirectUri) {
values["organization"] = this.getApplicationObj().organization; const params = new URLSearchParams(this.props.location.search);
if (params.get("popup") === "1") {
window.opener.postMessage(message, redirectUri);
} }
} }
@ -227,7 +224,7 @@ class LoginPage extends React.Component {
Setting.goToLinkSoft(ths, `/prompt/${application.name}?redirectUri=${oAuthParams.redirectUri}&code=${code}&state=${oAuthParams.state}`); Setting.goToLinkSoft(ths, `/prompt/${application.name}?redirectUri=${oAuthParams.redirectUri}&code=${code}&state=${oAuthParams.state}`);
} }
} else { } else {
Setting.showMessage("error", `${i18next.t("application:Failed to log in")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
} }
}); });
} else { } else {
@ -243,6 +240,7 @@ class LoginPage extends React.Component {
} }
} else { } else {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`); Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
this.sendPopupData({type: "loginSuccess", data: {code: code, state: oAuthParams.state}}, oAuthParams.redirectUri);
} }
} }
} }
@ -290,14 +288,13 @@ class LoginPage extends React.Component {
window.location.href = newUrl.toString(); window.location.href = newUrl.toString();
} }
} else { } else {
Setting.showMessage("error", `${i18next.t("application:Failed to log in")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
} }
}); });
} else { } else {
// OAuth // OAuth
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
this.populateOauthValues(values); this.populateOauthValues(values);
AuthBackend.login(values, oAuthParams) AuthBackend.login(values, oAuthParams)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
@ -310,7 +307,6 @@ class LoginPage extends React.Component {
Setting.goToLink(link); Setting.goToLink(link);
} else if (responseType === "code") { } else if (responseType === "code") {
this.postCodeLoginAction(res); this.postCodeLoginAction(res);
// Setting.showMessage("success", `Authorization code: ${res.data}`);
} else if (responseType === "token" || responseType === "id_token") { } else if (responseType === "token" || responseType === "id_token") {
const accessToken = res.data; const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
@ -328,7 +324,7 @@ class LoginPage extends React.Component {
} }
} }
} else { } else {
Setting.showMessage("error", `${i18next.t("application:Failed to log in")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
} }
}); });
} }
@ -369,9 +365,11 @@ class LoginPage extends React.Component {
if (application.enablePassword) { if (application.enablePassword) {
let loginWidth = 320; let loginWidth = 320;
if (Setting.getLanguage() === "fr") { if (Setting.getLanguage() === "fr") {
loginWidth += 10; loginWidth += 20;
} else if (Setting.getLanguage() === "es") { } else if (Setting.getLanguage() === "es") {
loginWidth += 40; loginWidth += 40;
} else if (Setting.getLanguage() === "ru") {
loginWidth += 10;
} }
return ( return (
@ -428,7 +426,7 @@ class LoginPage extends React.Component {
if (this.state.loginMethod === "verificationCode") { if (this.state.loginMethod === "verificationCode") {
if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) { if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) {
this.setState({validEmailOrPhone: false}); this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid Email or Phone!")); return Promise.reject(i18next.t("login:The input is not valid Email or phone number!"));
} }
if (Setting.isValidEmail(value)) { if (Setting.isValidEmail(value)) {
@ -461,25 +459,17 @@ class LoginPage extends React.Component {
this.renderPasswordOrCodeInput() this.renderPasswordOrCodeInput()
} }
</Row> </Row>
<Form.Item> <div style={{display: "inline-flex", justifyContent: "space-between", width: "320px", marginBottom: AgreementModal.isAgreementRequired(application) ? "5px" : "25px"}}>
{ <Form.Item name="autoSignin" valuePropName="checked" noStyle>
Setting.isAgreementRequired(application) ? <Checkbox style={{float: "left"}} disabled={!application.enablePassword}>
Setting.renderAgreement(true, () => { {i18next.t("login:Auto sign in")}
this.setState({ </Checkbox>
isTermsOfUseVisible: true, </Form.Item>
});
}, true, {}, Setting.isDefaultTrue(application)) : (
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
<Checkbox style={{float: "left"}} disabled={!application.enablePassword}>
{i18next.t("login:Auto sign in")}
</Checkbox>
</Form.Item>
)
}
{ {
Setting.renderForgetLink(application, i18next.t("login:Forgot password?")) Setting.renderForgetLink(application, i18next.t("login:Forgot password?"))
} }
</Form.Item> </div>
{AgreementModal.isAgreementRequired(application) ? AgreementModal.renderAgreementFormItem(application, true, {}, this) : null}
<Form.Item> <Form.Item>
<Button <Button
type="primary" type="primary"
@ -624,31 +614,12 @@ class LoginPage extends React.Component {
} }
const application = this.getApplicationObj(); const application = this.getApplicationObj();
if (this.props.account.owner !== application.organization) { if (this.props.account.owner !== application?.organization) {
return null; return null;
} }
const params = new URLSearchParams(this.props.location.search);
const silentSignin = params.get("silentSignin");
if (silentSignin !== null) {
this.sendSilentSigninData("signing-in");
const values = {};
values["application"] = application.name;
this.onFinish(values);
}
if (application.enableAutoSignin) {
const values = {};
values["application"] = application.name;
this.onFinish(values);
}
return ( return (
<div> <div>
{/* {*/}
{/* JSON.stringify(silentSignin)*/}
{/* }*/}
<div style={{fontSize: 16, textAlign: "left"}}> <div style={{fontSize: 16, textAlign: "left"}}>
{i18next.t("login:Continue with")}&nbsp;: {i18next.t("login:Continue with")}&nbsp;:
</div> </div>
@ -656,7 +627,7 @@ class LoginPage extends React.Component {
<SelfLoginButton account={this.props.account} onClick={() => { <SelfLoginButton account={this.props.account} onClick={() => {
const values = {}; const values = {};
values["application"] = application.name; values["application"] = application.name;
this.onFinish(values); this.login(values);
}} /> }} />
<br /> <br />
<br /> <br />
@ -721,7 +692,7 @@ class LoginPage extends React.Component {
const accessToken = res.data; const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
} else { } else {
Setting.showMessage("success", i18next.t("login:Successfully logged in with webauthn credentials")); Setting.showMessage("success", i18next.t("login:Successfully logged in with WebAuthn credentials"));
Setting.goToLink("/"); Setting.goToLink("/");
} }
} else { } else {
@ -746,7 +717,7 @@ class LoginPage extends React.Component {
<Input.Password <Input.Password
prefix={<LockOutlined className="site-form-item-icon" />} prefix={<LockOutlined className="site-form-item-icon" />}
type="password" type="password"
placeholder={i18next.t("login:Password")} placeholder={i18next.t("general:Password")}
disabled={!application.enablePassword} disabled={!application.enablePassword}
/> />
</Form.Item> </Form.Item>
@ -776,10 +747,10 @@ class LoginPage extends React.Component {
renderMethodChoiceBox() { renderMethodChoiceBox() {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
const items = [ const items = [
{label: i18next.t("login:Password"), key: "password"}, {label: i18next.t("general:Password"), key: "password"},
]; ];
application.enableCodeSignin ? items.push({ application.enableCodeSignin ? items.push({
label: i18next.t("login:Verification Code"), label: i18next.t("login:Verification code"),
key: "verificationCode", key: "verificationCode",
}) : null; }) : null;
application.enableWebAuthn ? items.push({label: i18next.t("login:WebAuthn"), key: "webAuthn"}) : null; application.enableWebAuthn ? items.push({label: i18next.t("login:WebAuthn"), key: "webAuthn"}) : null;
@ -798,6 +769,9 @@ class LoginPage extends React.Component {
render() { render() {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
if (application === undefined) {
return null;
}
if (application === null) { if (application === null) {
return Util.renderMessageLarge(this, this.state.msg); return Util.renderMessageLarge(this, this.state.msg);
} }
@ -840,29 +814,13 @@ class LoginPage extends React.Component {
{ {
Setting.renderLogo(application) Setting.renderLogo(application)
} }
{/* {*/} <LanguageSelect languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} />
{/* this.state.clientId !== null ? "Redirect" : null*/}
{/* }*/}
<SelectLanguageBox languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} />
{ {
this.renderSignedInBox() this.renderSignedInBox()
} }
{ {
this.renderForm(application) this.renderForm(application)
} }
{
Setting.renderModal(this.state.isTermsOfUseVisible, () => {
this.form.current.setFieldsValue({agreement: true});
this.setState({
isTermsOfUseVisible: false,
});
}, () => {
this.form.current.setFieldsValue({agreement: false});
this.setState({
isTermsOfUseVisible: false,
});
}, this.state.termsOfUseContent)
}
</div> </div>
</div> </div>
</div> </div>

View File

@ -19,9 +19,9 @@ import * as UserBackend from "../backend/UserBackend";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
import AffiliationSelect from "../common/AffiliationSelect"; import AffiliationSelect from "../common/select/AffiliationSelect";
import OAuthWidget from "../common/OAuthWidget"; import OAuthWidget from "../common/OAuthWidget";
import SelectRegionBox from "../SelectRegionBox"; import RegionSelect from "../common/select/RegionSelect";
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
class PromptPage extends React.Component { class PromptPage extends React.Component {
@ -151,7 +151,7 @@ class PromptPage extends React.Component {
</span> </span>
</Col> </Col>
<Col > <Col >
<SelectRegionBox defaultValue={this.state.user.region} onChange={(value) => { <RegionSelect defaultValue={this.state.user.region} onChange={(value) => {
this.updateUserFieldWithoutSubmit("region", value); this.updateUserFieldWithoutSubmit("region", value);
}} /> }} />
</Col> </Col>

View File

@ -237,7 +237,7 @@ const authInfo = {
scope: "identity", scope: "identity",
endpoint: "https://www.patreon.com/oauth2/authorize", endpoint: "https://www.patreon.com/oauth2/authorize",
}, },
Paypal: { PayPal: {
scope: "openid%20profile%20email", scope: "openid%20profile%20email",
endpoint: "https://www.sandbox.paypal.com/connect", endpoint: "https://www.sandbox.paypal.com/connect",
}, },
@ -378,6 +378,12 @@ export function getAuthUrl(application, provider, method) {
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState); const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier")) const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
if (provider.type === "AzureAD") {
if (provider.domain !== "") {
endpoint = endpoint.replace("common", provider.domain);
}
}
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook" if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook"
|| provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD" || provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD"
|| provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet" || provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet"

View File

@ -44,69 +44,70 @@ import * as AuthBackend from "./AuthBackend";
import {getEvent} from "./Util"; import {getEvent} from "./Util";
import {Modal} from "antd"; import {Modal} from "antd";
function getSigninButton(type) { function getSigninButton(provider) {
const text = i18next.t("login:Sign in with {type}").replace("{type}", type); const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.type);
if (type === "GitHub") { if (provider.type === "GitHub") {
return <GithubLoginButton text={text} align={"center"} />; return <GithubLoginButton text={text} align={"center"} />;
} else if (type === "Google") { } else if (provider.type === "Google") {
return <GoogleLoginButton text={text} align={"center"} />; return <GoogleLoginButton text={text} align={"center"} />;
} else if (type === "QQ") { } else if (provider.type === "QQ") {
return <QqLoginButton text={text} align={"center"} />; return <QqLoginButton text={text} align={"center"} />;
} else if (type === "Facebook") { } else if (provider.type === "Facebook") {
return <FacebookLoginButton text={text} align={"center"} />; return <FacebookLoginButton text={text} align={"center"} />;
} else if (type === "Weibo") { } else if (provider.type === "Weibo") {
return <WeiboLoginButton text={text} align={"center"} />; return <WeiboLoginButton text={text} align={"center"} />;
} else if (type === "Gitee") { } else if (provider.type === "Gitee") {
return <GiteeLoginButton text={text} align={"center"} />; return <GiteeLoginButton text={text} align={"center"} />;
} else if (type === "WeChat") { } else if (provider.type === "WeChat") {
return <WechatLoginButton text={text} align={"center"} />; return <WechatLoginButton text={text} align={"center"} />;
} else if (type === "DingTalk") { } else if (provider.type === "DingTalk") {
return <DingTalkLoginButton text={text} align={"center"} />; return <DingTalkLoginButton text={text} align={"center"} />;
} else if (type === "LinkedIn") { } else if (provider.type === "LinkedIn") {
return <LinkedInLoginButton text={text} align={"center"} />; return <LinkedInLoginButton text={text} align={"center"} />;
} else if (type === "WeCom") { } else if (provider.type === "WeCom") {
return <WeComLoginButton text={text} align={"center"} />; return <WeComLoginButton text={text} align={"center"} />;
} else if (type === "Lark") { } else if (provider.type === "Lark") {
return <LarkLoginButton text={text} align={"center"} />; return <LarkLoginButton text={text} align={"center"} />;
} else if (type === "GitLab") { } else if (provider.type === "GitLab") {
return <GitLabLoginButton text={text} align={"center"} />; return <GitLabLoginButton text={text} align={"center"} />;
} else if (type === "Adfs") { } else if (provider.type === "Adfs") {
return <AdfsLoginButton text={text} align={"center"} />; return <AdfsLoginButton text={text} align={"center"} />;
} else if (type === "Casdoor") { } else if (provider.type === "Casdoor") {
return <CasdoorLoginButton text={text} align={"center"} />; return <CasdoorLoginButton text={text} align={"center"} />;
} else if (type === "Baidu") { } else if (provider.type === "Baidu") {
return <BaiduLoginButton text={text} align={"center"} />; return <BaiduLoginButton text={text} align={"center"} />;
} else if (type === "Alipay") { } else if (provider.type === "Alipay") {
return <AlipayLoginButton text={text} align={"center"} />; return <AlipayLoginButton text={text} align={"center"} />;
} else if (type === "Infoflow") { } else if (provider.type === "Infoflow") {
return <InfoflowLoginButton text={text} align={"center"} />; return <InfoflowLoginButton text={text} align={"center"} />;
} else if (type === "Apple") { } else if (provider.type === "Apple") {
return <AppleLoginButton text={text} align={"center"} />; return <AppleLoginButton text={text} align={"center"} />;
} else if (type === "AzureAD") { } else if (provider.type === "AzureAD") {
return <AzureADLoginButton text={text} align={"center"} />; return <AzureADLoginButton text={text} align={"center"} />;
} else if (type === "Slack") { } else if (provider.type === "Slack") {
return <SlackLoginButton text={text} align={"center"} />; return <SlackLoginButton text={text} align={"center"} />;
} else if (type === "Steam") { } else if (provider.type === "Steam") {
return <SteamLoginButton text={text} align={"center"} />; return <SteamLoginButton text={text} align={"center"} />;
} else if (type === "Bilibili") { } else if (provider.type === "Bilibili") {
return <BilibiliLoginButton text={text} align={"center"} />; return <BilibiliLoginButton text={text} align={"center"} />;
} else if (type === "Okta") { } else if (provider.type === "Okta") {
return <OktaLoginButton text={text} align={"center"} />; return <OktaLoginButton text={text} align={"center"} />;
} else if (type === "Douyin") { } else if (provider.type === "Douyin") {
return <DouyinLoginButton text={text} align={"center"} />; return <DouyinLoginButton text={text} align={"center"} />;
} else { } else {
return <LoginButton key={type} type={type} />; return <LoginButton key={provider.type} type={provider.type} logoUrl={getProviderLogoURL(provider)} />;
} }
} }
function getSamlUrl(provider, location) { function goToSamlUrl(provider, location) {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const clientId = params.get("client_id"); const clientId = params.get("client_id") ?? "";
const application = params.get("state"); const state = params.get("state");
const realRedirectUri = params.get("redirect_uri"); const realRedirectUri = params.get("redirect_uri");
const redirectUri = `${window.location.origin}/callback/saml`; const redirectUri = `${window.location.origin}/callback/saml`;
const providerName = provider.name; const providerName = provider.name;
const relayState = `${clientId}&${application}&${providerName}&${realRedirectUri}&${redirectUri}`;
const relayState = `${clientId}&${state}&${providerName}&${realRedirectUri}&${redirectUri}`;
AuthBackend.getSamlLogin(`${provider.owner}/${providerName}`, btoa(relayState)).then((res) => { AuthBackend.getSamlLogin(`${provider.owner}/${providerName}`, btoa(relayState)).then((res) => {
if (res.data2 === "POST") { if (res.data2 === "POST") {
document.write(res.data); document.write(res.data);
@ -148,12 +149,11 @@ export function renderProviderLogo(provider, application, width, margin, size, l
} }
} else if (provider.category === "SAML") { } else if (provider.category === "SAML") {
return ( return (
<a key={provider.displayName} onClick={() => getSamlUrl(provider, location)}> <a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)}>
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} /> <img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} />
</a> </a>
); );
} }
} else if (provider.type === "Custom") { } else if (provider.type === "Custom") {
// style definition // style definition
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName); const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName);
@ -172,7 +172,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
); );
} else if (provider.category === "SAML") { } else if (provider.category === "SAML") {
return ( return (
<a key={provider.displayName} onClick={() => getSamlUrl(provider, location)} style={customAStyle}> <a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)} style={customAStyle}>
<button style={customButtonStyle}> <button style={customButtonStyle}>
<img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} /> <img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} />
<span style={customSpanStyle}>{text}</span> <span style={customSpanStyle}>{text}</span>
@ -181,14 +181,27 @@ export function renderProviderLogo(provider, application, width, margin, size, l
); );
} }
} else { } else {
return ( // big button, for disable password signin
<div key={provider.displayName} style={{marginBottom: "10px"}}> if (provider.category === "SAML") {
<a href={Provider.getAuthUrl(application, provider, "signup")}> return (
{ <div key={provider.displayName} style={{marginBottom: "10px"}}>
getSigninButton(provider.type) <a onClick={() => goToSamlUrl(provider, location)}>
} {
</a> getSigninButton(provider)
</div> }
); </a>
</div>
);
} else {
return (
<div key={provider.displayName} style={{marginBottom: "10px"}}>
<a href={Provider.getAuthUrl(application, provider, "signup")}>
{
getSigninButton(provider)
}
</a>
</div>
);
}
} }
} }

View File

@ -49,18 +49,21 @@ class SamlCallback extends React.Component {
const params = new URLSearchParams(this.props.location.search); const params = new URLSearchParams(this.props.location.search);
const relayState = params.get("relayState"); const relayState = params.get("relayState");
const samlResponse = params.get("samlResponse"); const samlResponse = params.get("samlResponse");
const messages = atob(relayState).split("&"); const messages = atob(relayState).split("&");
const clientId = messages[0]; const clientId = messages[0] === "" ? "" : messages[0];
const applicationName = (messages[1] === "null" || messages[1] === "undefined") ? "app-built-in" : messages[1]; const application = messages[0] === "" ? "app-built-in" : "";
const state = messages[1];
const providerName = messages[2]; const providerName = messages[2];
const redirectUri = messages[3]; const redirectUri = messages[3];
const responseType = this.getResponseType(redirectUri); const responseType = this.getResponseType(redirectUri);
const body = { const body = {
type: responseType, type: responseType,
application: applicationName, clientId: clientId,
provider: providerName, provider: providerName,
state: applicationName, state: state,
application: application,
redirectUri: `${window.location.origin}/callback`, redirectUri: `${window.location.origin}/callback`,
method: "signup", method: "signup",
relayState: relayState, relayState: relayState,
@ -71,7 +74,7 @@ class SamlCallback extends React.Component {
if (clientId === null || clientId === "") { if (clientId === null || clientId === "") {
param = ""; param = "";
} else { } else {
param = `?clientId=${clientId}&responseType=${responseType}&redirectUri=${redirectUri}&scope=read&state=${applicationName}`; param = `?clientId=${clientId}&responseType=${responseType}&redirectUri=${redirectUri}&scope=read&state=${state}`;
} }
AuthBackend.loginWithSaml(body, param) AuthBackend.loginWithSaml(body, param)
@ -83,7 +86,7 @@ class SamlCallback extends React.Component {
Setting.goToLink("/"); Setting.goToLink("/");
} else if (responseType === "code") { } else if (responseType === "code") {
const code = res.data; const code = res.data;
Setting.goToLink(`${redirectUri}?code=${code}&state=${applicationName}`); Setting.goToLink(`${redirectUri}?code=${code}&state=${state}`);
} }
} else { } else {
this.setState({ this.setState({

View File

@ -40,7 +40,7 @@ class SelfLoginButton extends React.Component {
}; };
const SelfLoginButton = createButton(config); const SelfLoginButton = createButton(config);
return <SelfLoginButton text={this.getAccountShowName()} onClick={() => this.props.onClick()} align={"center"} />; return <SelfLoginButton text={this.getAccountShowName()} onClick={this.props.onClick} align={"center"} />;
} }
} }

View File

@ -21,12 +21,13 @@ import i18next from "i18next";
import * as Util from "./Util"; import * as Util from "./Util";
import {authConfig} from "./Auth"; import {authConfig} from "./Auth";
import * as ApplicationBackend from "../backend/ApplicationBackend"; import * as ApplicationBackend from "../backend/ApplicationBackend";
import * as AgreementModal from "../common/modal/AgreementModal";
import {SendCodeInput} from "../common/SendCodeInput"; import {SendCodeInput} from "../common/SendCodeInput";
import SelectRegionBox from "../SelectRegionBox"; import RegionSelect from "../common/select/RegionSelect";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../common/CustomGithubCorner";
import SelectLanguageBox from "../SelectLanguageBox"; import LanguageSelect from "../common/select/LanguageSelect";
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
import {CountryCodeSelect} from "../common/CountryCodeSelect"; import {CountryCodeSelect} from "../common/select/CountryCodeSelect";
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
@ -47,7 +48,7 @@ const formItemLayout = {
}, },
}; };
const tailFormItemLayout = { export const tailFormItemLayout = {
wrapperCol: { wrapperCol: {
xs: { xs: {
span: 24, span: 24,
@ -65,8 +66,7 @@ class SignupPage extends React.Component {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
applicationName: props.match.params?.applicationName ?? authConfig.appName, applicationName: props.match?.params?.applicationName ?? authConfig.appName,
application: null,
email: "", email: "",
phone: "", phone: "",
countryCode: "", countryCode: "",
@ -83,20 +83,17 @@ class SignupPage extends React.Component {
} }
componentDidMount() { componentDidMount() {
let applicationName = this.state.applicationName;
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null) { if (oAuthParams !== null) {
applicationName = oAuthParams.state;
this.setState({applicationName: oAuthParams.state});
const signinUrl = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize"); const signinUrl = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize");
sessionStorage.setItem("signinUrl", signinUrl); sessionStorage.setItem("signinUrl", signinUrl);
} }
if (this.getApplicationObj() === null) { if (this.getApplicationObj() === undefined) {
if (applicationName !== undefined) { if (this.state.applicationName !== null) {
this.getApplication(applicationName); this.getApplication(this.state.applicationName);
} else { } else {
Setting.showMessage("error", `Unknown application name: ${applicationName}`); Setting.showMessage("error", `Unknown application name: ${this.state.applicationName}`);
} }
} }
} }
@ -109,15 +106,6 @@ class SignupPage extends React.Component {
ApplicationBackend.getApplication("admin", applicationName) ApplicationBackend.getApplication("admin", applicationName)
.then((application) => { .then((application) => {
this.onUpdateApplication(application); this.onUpdateApplication(application);
this.setState({
application: application,
});
if (application !== null && application !== undefined) {
Setting.getTermsOfUseContent(application.termsOfUse, res => {
this.setState({termsOfUseContent: res});
});
}
}); });
} }
@ -134,7 +122,7 @@ class SignupPage extends React.Component {
} }
getApplicationObj() { getApplicationObj() {
return this.props.application ?? this.state.application; return this.props.application;
} }
onUpdateAccount(account) { onUpdateAccount(account) {
@ -173,7 +161,7 @@ class SignupPage extends React.Component {
this.onUpdateAccount(account); this.onUpdateAccount(account);
Setting.goToLinkSoft(this, this.getResultPath(application)); Setting.goToLinkSoft(this, this.getResultPath(application));
} else { } else {
Setting.showMessage("error", `Failed to sign in: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
} }
}); });
} else { } else {
@ -318,7 +306,7 @@ class SignupPage extends React.Component {
}, },
]} ]}
> >
<SelectRegionBox onChange={(value) => {this.setState({region: value});}} /> <RegionSelect onChange={(value) => {this.setState({region: value});}} />
</Form.Item> </Form.Item>
); );
} else if (signupItem.name === "Email") { } else if (signupItem.name === "Email") {
@ -484,32 +472,10 @@ class SignupPage extends React.Component {
</Form.Item> </Form.Item>
); );
} else if (signupItem.name === "Agreement") { } else if (signupItem.name === "Agreement") {
return ( return AgreementModal.renderAgreementFormItem(application, required, tailFormItemLayout, this);
Setting.renderAgreement(Setting.isAgreementRequired(application), () => {
this.setState({
isTermsOfUseVisible: true,
});
}, false, tailFormItemLayout, Setting.isDefaultTrue(application))
);
} }
} }
renderModal() {
return (
Setting.renderModal(this.state.isTermsOfUseVisible, () => {
this.form.current.setFieldsValue({agreement: true});
this.setState({
isTermsOfUseVisible: false,
});
}, () => {
this.form.current.setFieldsValue({agreement: false});
this.setState({
isTermsOfUseVisible: false,
});
}, this.state.termsOfUseContent)
);
}
renderForm(application) { renderForm(application) {
if (!application.enableSignUp) { if (!application.enableSignUp) {
return ( return (
@ -596,7 +562,7 @@ class SignupPage extends React.Component {
render() { render() {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
if (application === null) { if (application === undefined || application === null) {
return null; return null;
} }
@ -622,16 +588,13 @@ class SignupPage extends React.Component {
{ {
Setting.renderLogo(application) Setting.renderLogo(application)
} }
<SelectLanguageBox languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} /> <LanguageSelect languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} />
{ {
this.renderForm(application) this.renderForm(application)
} }
</div> </div>
</div> </div>
</div> </div>
{
this.renderModal()
}
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -43,27 +43,20 @@ export function renderMessage(msg) {
export function renderMessageLarge(ths, msg) { export function renderMessageLarge(ths, msg) {
if (msg !== null) { if (msg !== null) {
return ( return (
<div style={{display: "inline"}}> <Result
<Result style={{margin: "0px auto"}}
status="error" status="error"
title="There was a problem signing you in.." title="There was a problem signing you in.."
subTitle={msg} subTitle={msg}
extra={[ extra={[
<Button type="primary" key="back" onClick={() => { <Button type="primary" key="back" onClick={() => {
window.history.go(-2); window.history.go(-2);
}}> }}>
Back Back
</Button>, </Button>,
// <Button key="home" onClick={() => Setting.goToLinkSoft(ths, "/")}> ]}
// Home >
// </Button>, </Result>
// <Button type="primary" key="signup" onClick={() => Setting.goToLinkSoft(ths, "/signup")}>
// Sign Up
// </Button>,
]}
>
</Result>
</div>
); );
} else { } else {
return null; return null;
@ -71,7 +64,7 @@ export function renderMessageLarge(ths, msg) {
} }
function getRefinedValue(value) { function getRefinedValue(value) {
return (value === null) ? "" : value; return value ?? "";
} }
export function getCasParameters(params) { export function getCasParameters(params) {
@ -100,7 +93,7 @@ export function getOAuthGetParameters(params) {
const relayState = getRefinedValue(queries.get("RelayState")); const relayState = getRefinedValue(queries.get("RelayState"));
const noRedirect = getRefinedValue(queries.get("noRedirect")); const noRedirect = getRefinedValue(queries.get("noRedirect"));
if ((clientId === undefined || clientId === null || clientId === "") && (samlRequest === "" || samlRequest === undefined)) { if (clientId === "" && samlRequest === "") {
// login // login
return null; return null;
} else { } else {

View File

@ -28,7 +28,7 @@ const demoModeCallback = (res) => {
title: i18next.t("general:This is a read-only demo site!"), title: i18next.t("general:This is a read-only demo site!"),
icon: <ExclamationCircleFilled />, icon: <ExclamationCircleFilled />,
content: i18next.t("general:Go to writable demo site?"), content: i18next.t("general:Go to writable demo site?"),
okText: i18next.t("user:OK"), okText: i18next.t("general:OK"),
cancelText: i18next.t("general:Cancel"), cancelText: i18next.t("general:Cancel"),
onOk() { onOk() {
Setting.openLink(`https://demo.casdoor.com${location.pathname}${location.search}?username=built-in/admin&password=123`); Setting.openLink(`https://demo.casdoor.com${location.pathname}${location.search}?username=built-in/admin&password=123`);

View File

@ -129,7 +129,7 @@ export function sendCode(checkType, captchaToken, clientSecret, method, countryC
}, },
}).then(res => res.json()).then(res => { }).then(res => res.json()).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("user:Code Sent")); Setting.showMessage("success", i18next.t("user:Verification code sent"));
return true; return true;
} else { } else {
Setting.showMessage("error", i18next.t("user:" + res.msg)); Setting.showMessage("error", i18next.t("user:" + res.msg));

View File

@ -15,7 +15,7 @@
import {Button} from "antd"; import {Button} from "antd";
import React from "react"; import React from "react";
import i18next from "i18next"; import i18next from "i18next";
import {CaptchaModal} from "./CaptchaModal"; import {CaptchaModal} from "./modal/CaptchaModal";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
export const CaptchaPreview = (props) => { export const CaptchaPreview = (props) => {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import * as Conf from "./Conf"; import * as Conf from "../Conf";
import GithubCorner from "react-github-corner"; import GithubCorner from "react-github-corner";
class CustomGithubCorner extends React.Component { class CustomGithubCorner extends React.Component {

View File

@ -133,6 +133,11 @@ class OAuthWidget extends React.Component {
} }
} }
let linkButtonWidth = "110px";
if (Setting.getLanguage() === "id") {
linkButtonWidth = "160px";
}
return ( return (
<Row key={provider.name} style={{marginTop: "20px"}} > <Row key={provider.name} style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={this.props.labelSpan}> <Col style={{marginTop: "5px"}} span={this.props.labelSpan}>
@ -150,7 +155,7 @@ class OAuthWidget extends React.Component {
<span style={{width: this.props.labelSpan === 3 ? "300px" : "200px", display: (Setting.isMobile()) ? "inline" : "inline-block"}}> <span style={{width: this.props.labelSpan === 3 ? "300px" : "200px", display: (Setting.isMobile()) ? "inline" : "inline-block"}}>
{ {
linkedValue === "" ? ( linkedValue === "" ? (
"(empty)" `(${i18next.t("general:empty")})`
) : ( ) : (
profileUrl === "" ? name : ( profileUrl === "" ? name : (
<a target="_blank" rel="noreferrer" href={profileUrl}> <a target="_blank" rel="noreferrer" href={profileUrl}>
@ -165,10 +170,10 @@ class OAuthWidget extends React.Component {
{ {
linkedValue === "" ? ( linkedValue === "" ? (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}> <a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}>
<Button style={{marginLeft: "20px", width: "80px"}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button> <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
</a> </a>
) : ( ) : (
<Button disabled={!providerItem.canUnlink && !account.isGlobalAdmin} style={{marginLeft: "20px", width: "80px"}} onClick={() => this.unlinkUser(provider.type)}>{i18next.t("user:Unlink")}</Button> <Button disabled={!providerItem.canUnlink && !account.isGlobalAdmin} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type)}>{i18next.t("user:Unlink")}</Button>
) )
} }
</Col> </Col>

View File

@ -17,7 +17,7 @@ import React from "react";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import {SafetyOutlined} from "@ant-design/icons"; import {SafetyOutlined} from "@ant-design/icons";
import {CaptchaModal} from "./CaptchaModal"; import {CaptchaModal} from "./modal/CaptchaModal";
const {Search} = Input; const {Search} = Input;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import * as Setting from "./Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
export function sendTestEmail(provider, email) { export function sendTestEmail(provider, email) {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import * as Setting from "./Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
export function sendTestSms(provider, phone) { export function sendTestSms(provider, phone) {

View File

@ -0,0 +1,126 @@
// 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 {Checkbox, Form, Modal} from "antd";
import i18next from "i18next";
import React, {useEffect, useState} from "react";
export const AgreementModal = (props) => {
const {open, onOk, onCancel, application} = props;
const [doc, setDoc] = useState("");
useEffect(() => {
getTermsOfUseContent(application.termsOfUseUrl).then((data) => {
setDoc(data);
});
}, []);
return (
<Modal
title={i18next.t("signup:Terms of Use")}
open={open}
width={"55vw"}
closable={false}
okText={i18next.t("signup:Accept")}
cancelText={i18next.t("signup:Decline")}
onOk={onOk}
onCancel={onCancel}
>
<iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={doc} />
</Modal>
);
};
function getTermsOfUseContent(url) {
return fetch(url, {
method: "GET",
}).then(r => r.text());
}
export function isAgreementRequired(application) {
if (application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
if (!agreementItem || agreementItem.rule === "None" || !agreementItem.rule) {
return false;
}
if (agreementItem.required) {
return true;
}
}
return false;
}
function initDefaultValue(application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
return isAgreementRequired(application) && agreementItem.rule === "Signin (Default True)";
}
export function renderAgreementFormItem(application, required, layout, ths) {
return (<React.Fragment>
<Form.Item
name="agreement"
key="agreement"
valuePropName="checked"
rules={[
{
required: required,
},
() => ({
validator: (_, value) => {
if (!required) {
return Promise.resolve();
}
if (!value) {
return Promise.reject(i18next.t("signup:Please accept the agreement!"));
} else {
return Promise.resolve();
}
},
}),
]
}
{...layout}
initialValue={initDefaultValue(application)}
>
<Checkbox style={{float: "left"}}>
{i18next.t("signup:Accept")}&nbsp;
<a onClick={() => {
ths.setState({
isTermsOfUseVisible: true,
});
}}
>
{i18next.t("signup:Terms of Use")}
</a>
</Checkbox>
</Form.Item>
<AgreementModal application={application} layout={layout} open={ths.state.isTermsOfUseVisible}
onOk={() => {
ths.form.current.setFieldsValue({agreement: true});
ths.setState({
isTermsOfUseVisible: false,
});
}}
onCancel={() => {
ths.form.current.setFieldsValue({agreement: false});
ths.setState({
isTermsOfUseVisible: false,
});
}} />
</React.Fragment>
);
}

View File

@ -15,8 +15,8 @@
import {Button, Col, Input, Modal, Row} from "antd"; import {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import React, {useEffect} from "react"; import React, {useEffect} from "react";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../../backend/UserBackend";
import {CaptchaWidget} from "./CaptchaWidget"; import {CaptchaWidget} from "../CaptchaWidget";
import {SafetyOutlined} from "@ant-design/icons"; import {SafetyOutlined} from "@ant-design/icons";
export const CaptchaModal = (props) => { export const CaptchaModal = (props) => {
@ -143,8 +143,8 @@ export const CaptchaModal = (props) => {
} }
return [ return [
<Button key="cancel" onClick={handleCancel}>{i18next.t("user:Cancel")}</Button>, <Button key="cancel" onClick={handleCancel}>{i18next.t("general:Cancel")}</Button>,
<Button key="ok" disabled={isOkDisabled} type="primary" onClick={handleOk}>{i18next.t("user:OK")}</Button>, <Button key="ok" disabled={isOkDisabled} type="primary" onClick={handleOk}>{i18next.t("general:OK")}</Button>,
]; ];
}; };
@ -155,8 +155,8 @@ export const CaptchaModal = (props) => {
destroyOnClose={true} destroyOnClose={true}
title={i18next.t("general:Captcha")} title={i18next.t("general:Captcha")}
open={open} open={open}
okText={i18next.t("user:OK")} okText={i18next.t("general:OK")}
cancelText={i18next.t("user:Cancel")} cancelText={i18next.t("general:Cancel")}
width={350} width={350}
footer={renderFooter()} footer={renderFooter()}
onCancel={handleCancel} onCancel={handleCancel}

View File

@ -15,12 +15,12 @@
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import Cropper from "react-cropper"; import Cropper from "react-cropper";
import "cropperjs/dist/cropper.css"; import "cropperjs/dist/cropper.css";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
import {Button, Col, Modal, Row, Select} from "antd"; import {Button, Col, Modal, Row, Select} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as ResourceBackend from "./backend/ResourceBackend"; import * as ResourceBackend from "../../backend/ResourceBackend";
export const CropperDiv = (props) => { export const CropperDivModal = (props) => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const [image, setImage] = useState(""); const [image, setImage] = useState("");
@ -60,7 +60,7 @@ export const CropperDiv = (props) => {
// Setting.showMessage("success", "uploading..."); // Setting.showMessage("success", "uploading...");
const extension = image.substring(image.indexOf("/") + 1, image.indexOf(";base64")); const extension = image.substring(image.indexOf("/") + 1, image.indexOf(";base64"));
const fullFilePath = `avatar/${user.owner}/${user.name}.${extension}`; const fullFilePath = `avatar/${user.owner}/${user.name}.${extension}`;
ResourceBackend.uploadResource(user.owner, user.name, "avatar", "CropperDiv", fullFilePath, blob) ResourceBackend.uploadResource(user.owner, user.name, "avatar", "CropperDivModal", fullFilePath, blob)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
window.location.href = window.location.pathname; window.location.href = window.location.pathname;
@ -187,4 +187,4 @@ export const CropperDiv = (props) => {
); );
}; };
export default CropperDiv; export default CropperDivModal;

View File

@ -15,8 +15,8 @@
import {Button, Col, Input, Modal, Row} from "antd"; import {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import React from "react"; import React from "react";
import * as UserBackend from "./backend/UserBackend"; import * as UserBackend from "../../backend/UserBackend";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
export const PasswordModal = (props) => { export const PasswordModal = (props) => {
const [visible, setVisible] = React.useState(false); const [visible, setVisible] = React.useState(false);
@ -48,7 +48,7 @@ export const PasswordModal = (props) => {
UserBackend.setPassword(user.owner, user.name, oldPassword, newPassword).then((res) => { UserBackend.setPassword(user.owner, user.name, oldPassword, newPassword).then((res) => {
setConfirmLoading(false); setConfirmLoading(false);
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("user:Password Set")); Setting.showMessage("success", i18next.t("user:Password set successfully"));
setVisible(false); setVisible(false);
} else {Setting.showMessage("error", i18next.t(`user:${res.msg}`));} } else {Setting.showMessage("error", i18next.t(`user:${res.msg}`));}
}); });
@ -63,10 +63,10 @@ export const PasswordModal = (props) => {
</Button> </Button>
<Modal <Modal
maskClosable={false} maskClosable={false}
title={i18next.t("user:Password")} title={i18next.t("general:Password")}
open={visible} open={visible}
okText={i18next.t("user:Set Password")} okText={i18next.t("user:Set Password")}
cancelText={i18next.t("user:Cancel")} cancelText={i18next.t("general:Cancel")}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={handleCancel} onCancel={handleCancel}
onOk={handleOk} onOk={handleOk}

View File

@ -15,9 +15,9 @@
import {Button, Col, Input, Modal, Row} from "antd"; import {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
import * as UserBackend from "./backend/UserBackend"; import * as UserBackend from "../../backend/UserBackend";
import {SendCodeInput} from "./common/SendCodeInput"; import {SendCodeInput} from "../SendCodeInput";
import {MailOutlined, PhoneOutlined} from "@ant-design/icons"; import {MailOutlined, PhoneOutlined} from "@ant-design/icons";
export const ResetModal = (props) => { export const ResetModal = (props) => {
@ -45,13 +45,13 @@ export const ResetModal = (props) => {
return; return;
} }
if (code === "") { if (code === "") {
Setting.showMessage("error", i18next.t("code:Empty Code")); Setting.showMessage("error", i18next.t("code:Empty code"));
return; return;
} }
setConfirmLoading(true); setConfirmLoading(true);
UserBackend.resetEmailOrPhone(dest, destType, code).then(res => { UserBackend.resetEmailOrPhone(dest, destType, code).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("user:" + destType + " reset")); Setting.showMessage("success", i18next.t("user:Email/phone reset successfully"));
window.location.reload(); window.location.reload();
} else { } else {
Setting.showMessage("error", i18next.t("user:" + res.msg)); Setting.showMessage("error", i18next.t("user:" + res.msg));
@ -77,7 +77,7 @@ export const ResetModal = (props) => {
title={buttonText} title={buttonText}
open={visible} open={visible}
okText={buttonText} okText={buttonText}
cancelText={i18next.t("user:Cancel")} cancelText={i18next.t("general:Cancel")}
confirmLoading={confirmLoading} confirmLoading={confirmLoading}
onCancel={handleCancel} onCancel={handleCancel}
onOk={handleOk} onOk={handleOk}
@ -94,7 +94,7 @@ export const ResetModal = (props) => {
</Row> </Row>
<Row style={{width: "100%", marginBottom: "20px"}}> <Row style={{width: "100%", marginBottom: "20px"}}>
<SendCodeInput <SendCodeInput
textBefore={i18next.t("code:Code You Received")} textBefore={i18next.t("code:Code you received")}
onChange={setCode} onChange={setCode}
method={"reset"} method={"reset"}
onButtonClickArgs={[dest, destType, Setting.getApplicationName(application)]} onButtonClickArgs={[dest, destType, Setting.getApplicationName(application)]}

View File

@ -1,119 +1,119 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved. // Copyright 2021 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Cascader, Col, Input, Row, Select} from "antd"; import {Cascader, Col, Input, Row, Select} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../../backend/UserBackend";
import * as Setting from "../Setting"; import * as Setting from "../../Setting";
class AffiliationSelect extends React.Component { class AffiliationSelect extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
addressOptions: [], addressOptions: [],
affiliationOptions: [], affiliationOptions: [],
}; };
} }
UNSAFE_componentWillMount() { componentDidMount() {
this.getAddressOptions(this.props.application); this.getAddressOptions(this.props.application);
this.getAffiliationOptions(this.props.application, this.props.user); this.getAffiliationOptions(this.props.application, this.props.user);
} }
getAddressOptions(application) { getAddressOptions(application) {
if (application.affiliationUrl === "") { if (application.affiliationUrl === "") {
return; return;
} }
const addressUrl = application.affiliationUrl.split("|")[0]; const addressUrl = application.affiliationUrl.split("|")[0];
UserBackend.getAddressOptions(addressUrl) UserBackend.getAddressOptions(addressUrl)
.then((addressOptions) => { .then((addressOptions) => {
this.setState({ this.setState({
addressOptions: addressOptions, addressOptions: addressOptions,
}); });
}); });
} }
getAffiliationOptions(application, user) { getAffiliationOptions(application, user) {
if (application.affiliationUrl === "") { if (application.affiliationUrl === "") {
return; return;
} }
const affiliationUrl = application.affiliationUrl.split("|")[1]; const affiliationUrl = application.affiliationUrl.split("|")[1];
const code = user.address[user.address.length - 1]; const code = user.address[user.address.length - 1];
UserBackend.getAffiliationOptions(affiliationUrl, code) UserBackend.getAffiliationOptions(affiliationUrl, code)
.then((affiliationOptions) => { .then((affiliationOptions) => {
this.setState({ this.setState({
affiliationOptions: affiliationOptions, affiliationOptions: affiliationOptions,
}); });
}); });
} }
updateUserField(key, value) { updateUserField(key, value) {
this.props.onUpdateUserField(key, value); this.props.onUpdateUserField(key, value);
} }
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
{ {
this.props.application?.affiliationUrl === "" ? null : ( this.props.application?.affiliationUrl === "" ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={this.props.labelSpan}> <Col style={{marginTop: "5px"}} span={this.props.labelSpan}>
{Setting.getLabel(i18next.t("user:Address"), i18next.t("user:Address - Tooltip"))} : {Setting.getLabel(i18next.t("user:Address"), i18next.t("user:Address - Tooltip"))} :
</Col> </Col>
<Col span={24 - this.props.labelSpan} > <Col span={24 - this.props.labelSpan} >
<Cascader style={{width: "100%", maxWidth: "400px"}} value={this.props.user.address} options={this.state.addressOptions} onChange={value => { <Cascader style={{width: "100%", maxWidth: "400px"}} value={this.props.user.address} options={this.state.addressOptions} onChange={value => {
this.updateUserField("address", value); this.updateUserField("address", value);
this.updateUserField("affiliation", ""); this.updateUserField("affiliation", "");
this.updateUserField("score", 0); this.updateUserField("score", 0);
this.getAffiliationOptions(this.props.application, this.props.user); this.getAffiliationOptions(this.props.application, this.props.user);
}} placeholder={i18next.t("signup:Please input your address!")} /> }} placeholder={i18next.t("signup:Please input your address!")} />
</Col> </Col>
</Row> </Row>
) )
} }
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={this.props.labelSpan}> <Col style={{marginTop: "5px"}} span={this.props.labelSpan}>
{Setting.getLabel(i18next.t("user:Affiliation"), i18next.t("user:Affiliation - Tooltip"))} : {Setting.getLabel(i18next.t("user:Affiliation"), i18next.t("user:Affiliation - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
{ {
this.props.application?.affiliationUrl === "" ? ( this.props.application?.affiliationUrl === "" ? (
<Input value={this.props.user.affiliation} onChange={e => { <Input value={this.props.user.affiliation} onChange={e => {
this.updateUserField("affiliation", e.target.value); this.updateUserField("affiliation", e.target.value);
}} /> }} />
) : ( ) : (
<Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation} <Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation}
onChange={(value => { onChange={(value => {
const name = value; const name = value;
const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name); const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name);
const id = affiliationOption.id; const id = affiliationOption.id;
this.updateUserField("affiliation", name); this.updateUserField("affiliation", name);
this.updateUserField("score", id); this.updateUserField("score", id);
})} })}
options={[Setting.getOption("(empty)", "")].concat(this.state.affiliationOptions.map((affiliationOption) => Setting.getOption(affiliationOption.name, affiliationOption.name)) options={[Setting.getOption(`(${i18next.t("general:empty")})`, "")].concat(this.state.affiliationOptions.map((affiliationOption) => Setting.getOption(affiliationOption.name, affiliationOption.name))
)} /> )} />
) )
} }
</Col> </Col>
</Row> </Row>
</React.Fragment> </React.Fragment>
); );
} }
} }
export default AffiliationSelect; export default AffiliationSelect;

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import {Select} from "antd"; import {Select} from "antd";
import * as Setting from "../Setting"; import * as Setting from "../../Setting";
import React from "react"; import React from "react";
export const CountryCodeSelect = (props) => { export const CountryCodeSelect = (props) => {

View File

@ -13,9 +13,9 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
import {Dropdown} from "antd"; import {Dropdown} from "antd";
import "./App.less"; import "../../App.less";
import {GlobalOutlined} from "@ant-design/icons"; import {GlobalOutlined} from "@ant-design/icons";
function flagIcon(country, alt) { function flagIcon(country, alt) {
@ -24,7 +24,7 @@ function flagIcon(country, alt) {
); );
} }
class SelectLanguageBox extends React.Component { class LanguageSelect extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -63,4 +63,4 @@ class SelectLanguageBox extends React.Component {
} }
} }
export default SelectLanguageBox; export default LanguageSelect;

View File

@ -13,12 +13,12 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
import {Select} from "antd"; import {Select} from "antd";
const {Option} = Select; const {Option} = Select;
class SelectRegionBox extends React.Component { class RegionSelect extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
@ -59,4 +59,4 @@ class SelectRegionBox extends React.Component {
} }
} }
export default SelectRegionBox; export default RegionSelect;

View File

@ -13,9 +13,9 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "../../Setting";
import {Dropdown} from "antd"; import {Dropdown} from "antd";
import "./App.less"; import "../../App.less";
import i18next from "i18next"; import i18next from "i18next";
import {CheckOutlined} from "@ant-design/icons"; import {CheckOutlined} from "@ant-design/icons";
import {CompactTheme, DarkTheme, Light} from "antd-token-previewer/es/icons"; import {CompactTheme, DarkTheme, Light} from "antd-token-previewer/es/icons";
@ -34,7 +34,7 @@ function getIcon(themeKey) {
} }
} }
class SelectThemeBox extends React.Component { class ThemeSelect extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
} }
@ -91,4 +91,4 @@ class SelectThemeBox extends React.Component {
} }
} }
export default SelectThemeBox; export default ThemeSelect;

View File

@ -18,6 +18,7 @@ import zh from "./locales/zh/data.json";
import es from "./locales/es/data.json"; import es from "./locales/es/data.json";
import fr from "./locales/fr/data.json"; import fr from "./locales/fr/data.json";
import de from "./locales/de/data.json"; import de from "./locales/de/data.json";
import id from "./locales/id/data.json";
import ja from "./locales/ja/data.json"; import ja from "./locales/ja/data.json";
import ko from "./locales/ko/data.json"; import ko from "./locales/ko/data.json";
import ru from "./locales/ru/data.json"; import ru from "./locales/ru/data.json";
@ -31,6 +32,7 @@ const resources = {
es: es, es: es,
fr: fr, fr: fr,
de: de, de: de,
id: id,
ja: ja, ja: ja,
ko: ko, ko: ko,
ru: ru, ru: ru,
@ -66,6 +68,9 @@ function initLanguage() {
case "de": case "de":
language = "de"; language = "de";
break; break;
case "id":
language = "id";
break;
case "ja": case "ja":
language = "ja"; language = "ja";
break; break;

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,12 @@
"Sign Up": "Sign Up" "Sign Up": "Sign Up"
}, },
"adapter": { "adapter": {
"Duplicated policy rules": "Duplicated policy rules",
"Edit Adapter": "Edit Adapter", "Edit Adapter": "Edit Adapter",
"Failed to sync policies": "Failed to sync policies", "Failed to sync policies": "Failed to sync policies",
"New Adapter": "New Adapter", "New Adapter": "New Adapter",
"Policies": "Policies", "Policies": "Policies",
"Policies - Tooltip": "CURD to the policy rules", "Policies - Tooltip": "Casbin policy rules",
"Repeated policy rules": "Repeated policy rules",
"Sync": "Sync",
"Sync policies successfully": "Sync policies successfully" "Sync policies successfully": "Sync policies successfully"
}, },
"application": { "application": {
@ -19,103 +18,109 @@
"Auto signin": "Auto signin", "Auto signin": "Auto signin",
"Auto signin - Tooltip": "When a logged-in session exists in Casdoor, it is automatically used for application-side login", "Auto signin - Tooltip": "When a logged-in session exists in Casdoor, it is automatically used for application-side login",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Link to the background image of the login page", "Background URL - Tooltip": "URL of the background image used in the login page",
"Center": "Center", "Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
"Copy signup page URL": "Copy signup page URL", "Copy signup page URL": "Copy signup page URL",
"Edit Application": "Edit Application", "Edit Application": "Edit Application",
"Enable SAML compress": "Enable SAML compress", "Enable Email linking": "Enable Email linking",
"Enable SAML compress - Tooltip": "Whether to compress SAML response messages when Casdoor is used as SAML idp", "Enable Email linking - Tooltip": "When using 3rd-party providers to log in, if there is a user in the organization with the same Email, the 3rd-party login method will be automatically associated with that user",
"Enable SAML compression": "Enable SAML compression",
"Enable SAML compression - Tooltip": "Whether to compress SAML response messages when Casdoor is used as SAML idp",
"Enable WebAuthn signin": "Enable WebAuthn signin", "Enable WebAuthn signin": "Enable WebAuthn signin",
"Enable WebAuthn signin - Tooltip": "Whether to allow login with cell phone or email verification code", "Enable WebAuthn signin - Tooltip": "Whether to allow users to login with WebAuthn",
"Enable code signin": "Enable code signin", "Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Whether to allow login with cell phone or email verification code", "Enable code signin - Tooltip": "Whether to allow users to login with phone or Email verification code",
"Enable link accounts that with the same email": "Enable link accounts that with the same email", "Enable password": "Enable password",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email", "Enable password - Tooltip": "Whether to allow users to login with password",
"Enable side panel": "Enable side panel", "Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Whether Casdoor maintains a session after logging into Casdoor from the app", "Enable signin session - Tooltip": "Whether Casdoor maintains a session after logging into Casdoor from the application",
"Enable signup": "Enable signup", "Enable signup": "Enable signup",
"Enable signup - Tooltip": "Whether to allow users to register", "Enable signup - Tooltip": "Whether to allow users to register a new account",
"Failed to log in": "Failed to log in",
"Failed to sign in": "Failed to sign in", "Failed to sign in": "Failed to sign in",
"File uploaded successfully": "File uploaded successfully", "File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme", "Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "CSS styling of forms (e.g. adding borders and shadows)", "Form CSS - Tooltip": "CSS styling of the signup, signin and forget password forms (e.g. adding borders and shadows)",
"Form position": "Form position", "Form position": "Form position",
"Form position - Tooltip": "Location of forms for registration, login, forgotten password, etc.", "Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Select which Grant types are allowed in the OAuth protocol", "Grant types - Tooltip": "Select which grant types are allowed in the OAuth protocol",
"Incremental": "Incremental",
"Left": "Left", "Left": "Left",
"Logged in successfully": "Logged in successfully", "Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully", "Logged out successfully": "Logged out successfully",
"New Application": "New Application", "New Application": "New Application",
"No verification": "No verification",
"None": "None", "None": "None",
"Password ON": "Password ON", "Normal": "Normal",
"Password ON - Tooltip": "Whether to allow password login", "Only signup": "Only signup",
"Please input your application!": "Please input your application!", "Please input your application!": "Please input your application!",
"Please input your organization!": "Please input your organization!", "Please input your organization!": "Please input your organization!",
"Please select a HTML file": "Please select a HTML file", "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", "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",
"Random": "Random",
"Real name": "Real name",
"Redirect URL": "Redirect URL", "Redirect URL": "Redirect URL",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL)", "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL)",
"Redirect URLs": "Redirect URLs", "Redirect URLs": "Redirect URLs",
"Redirect URLs - Tooltip": "List of redirected addresses after successful login", "Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire", "Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time", "Refresh token expire - Tooltip": "Refresh token expiration time",
"Right": "Right", "Right": "Right",
"Rule": "Rule", "Rule": "Rule",
"SAML Reply URL": "SAML Reply URL",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "Metadata information of SAML protocol", "SAML metadata - Tooltip": "The metadata of SAML protocol",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"SAML reply URL": "SAML reply URL",
"Side panel HTML": "Side panel HTML", "Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit", "Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML", "Side panel HTML - Tooltip": "Customize the HTML code for the side panel of the login page",
"Sign Up Error": "Sign Up Error", "Sign Up Error": "Sign Up Error",
"Signin": "Signin",
"Signin (Default True)": "Signin (Default True)",
"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 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", "Signin session": "Signin session",
"Signup items": "Signup items", "Signup items": "Signup items",
"Signup items - Tooltip": "Items to be filled in when registering users", "Signup items - Tooltip": "Items for users to fill in when registering new accounts",
"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", "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", "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": "Token expire",
"Token expire - Tooltip": "Access token expiration time", "Token expire - Tooltip": "Access token expiration time",
"Token format": "Token format", "Token format": "Token format",
"Token format - Tooltip": "Select access token format", "Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page" "You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
}, },
"cert": { "cert": {
"Bit size": "Bit size", "Bit size": "Bit size",
"Bit size - Tooltip": "Secret key length", "Bit size - Tooltip": "Secret key length",
"Certificate": "Certificate", "Certificate": "Certificate",
"Certificate - Tooltip": "The public key, configured on your server, is used to decrypt the signature", "Certificate - Tooltip": "Public key certificate, used for decrypting the JWT signature of the Access Token. This certificate usually needs to be deployed on the Casdoor SDK side (i.e., the application) to parse the JWT",
"Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully", "Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
"Copy certificate": "Copy certificate", "Copy certificate": "Copy certificate",
"Copy private key": "Copy private key", "Copy private key": "Copy private key",
"Crypto algorithm": "Crypto algorithm", "Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Encryption algorithm of the certificate", "Crypto algorithm - Tooltip": "Encryption algorithm used by the certificate",
"Download certificate": "Download certificate", "Download certificate": "Download certificate",
"Download private key": "Download private key", "Download private key": "Download private key",
"Edit Cert": "Edit Cert", "Edit Cert": "Edit Cert",
"Expire in years": "Expire in years", "Expire in years": "Expire in years",
"Expire in years - Tooltip": "Expire in years", "Expire in years - Tooltip": "Validity period of the certificate, in years",
"New Cert": "New Cert", "New Cert": "New Cert",
"Private key": "Private key", "Private key": "Private key",
"Private key - Tooltip": "Private key for signature generation", "Private key - Tooltip": "Private key corresponding to the public key certificate",
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully", "Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
"Scope": "Scope", "Scope - Tooltip": "Usage scenarios of the certificate",
"Scope - Tooltip": "Scope of the certificate",
"Type": "Type",
"Type - Tooltip": "Type of certificate" "Type - Tooltip": "Type of certificate"
}, },
"code": { "code": {
"Code You Received": "Code You Received", "Code you received": "Code you received",
"Email code": "Email code", "Email code": "Email code",
"Empty Code": "Empty Code", "Empty code": "Empty code",
"Enter your code": "Enter your code", "Enter your code": "Enter your code",
"Phone code": "Phone code", "Phone code": "Phone code",
"Please input your phone verification code!": "Please input your phone verification code!", "Please input your phone verification code!": "Please input your phone verification code!",
@ -128,13 +133,11 @@
"Account": "Account", "Account": "Account",
"Change Password": "Change Password", "Change Password": "Change Password",
"Choose email or phone": "Choose email or phone", "Choose email or phone": "Choose email or phone",
"Confirm": "Confirm",
"Next Step": "Next Step", "Next Step": "Next Step",
"Password": "Password",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Retrieve password": "Retrieve password",
"Unknown forget type: ": "Unknown forget type: ", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
"general": { "general": {
@ -144,12 +147,12 @@
"Adapters": "Adapters", "Adapters": "Adapters",
"Add": "Add", "Add": "Add",
"Affiliation URL": "Affiliation URL", "Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "Affiliation URL", "Affiliation URL - Tooltip": "The homepage URL for the affiliation",
"Application": "Application", "Application": "Application",
"Applications": "Applications", "Applications": "Applications",
"Applications that require authentication": "Applications that require authentication", "Applications that require authentication": "Applications that require authentication",
"Avatar": "Avatar", "Avatar": "Avatar",
"Avatar - Tooltip": "Avatar to show to others", "Avatar - Tooltip": "Public avatar image for the user",
"Back Home": "Back Home", "Back Home": "Back Home",
"Cancel": "Cancel", "Cancel": "Cancel",
"Captcha": "Captcha", "Captcha": "Captcha",
@ -161,27 +164,27 @@
"Close": "Close", "Close": "Close",
"Created time": "Created time", "Created time": "Created time",
"Default application": "Default application", "Default application": "Default application",
"Default application - Tooltip": "Default application", "Default application - Tooltip": "Default application for users registered directly from the organization page",
"Default avatar": "Default avatar", "Default avatar": "Default avatar",
"Default avatar - Tooltip": "Default avatar", "Default avatar - Tooltip": "Default avatar used when newly registered users do not set an avatar image",
"Delete": "Delete", "Delete": "Delete",
"Description": "Description", "Description": "Description",
"Description - Tooltip": "Descriptive information related to this", "Description - Tooltip": "Detailed description information for reference, Casdoor itself will not use it",
"Display name": "Display name", "Display name": "Display name",
"Display name - Tooltip": "The name displayed, it can be repeated", "Display name - Tooltip": "A user-friendly, easily readable name displayed publicly in the UI",
"Down": "Down", "Down": "Down",
"Edit": "Edit", "Edit": "Edit",
"Email": "Email", "Email": "Email",
"Email - Tooltip": "Email address", "Email - Tooltip": "Valid email address",
"Failed to add": "Failed to add", "Failed to add": "Failed to add",
"Failed to connect to server": "Failed to connect to server", "Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete", "Failed to delete": "Failed to delete",
"Failed to save": "Failed to save", "Failed to save": "Failed to save",
"Favicon": "Favicon", "Favicon": "Favicon",
"Favicon - Tooltip": "Favicon icons for the website", "Favicon - Tooltip": "Favicon icon URL used in all Casdoor pages of the organization",
"First name": "First name", "First name": "First name",
"Forget URL": "Forget URL", "Forget URL": "Forget URL",
"Forget URL - Tooltip": "Forgot password URL", "Forget URL - Tooltip": "Custom URL for the \"Forget password\" page. If not set, the default Casdoor \"Forget password\" page will be used. When set, the \"Forget password\" link on the login page will redirect to this URL",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at", "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?", "Go to writable demo site?": "Go to writable demo site?",
"Home": "Home", "Home": "Home",
@ -198,52 +201,53 @@
"Logo": "Logo", "Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world", "Logo - Tooltip": "Icons that the application presents to the outside world",
"Master password": "Master password", "Master password": "Master password",
"Master password - Tooltip": "Can be used to log in to all users under the organization and facilitate administrators to log in as that user to solve technical problems", "Master password - Tooltip": "Can be used to log in to all users under this organization, making it convenient for administrators to log in as this user to solve technical issues",
"Menu": "Menu", "Menu": "Menu",
"Method": "Method", "Method": "Method",
"Model": "Model", "Model": "Model",
"Model - Tooltip": "Select one model belonging to the organization", "Model - Tooltip": "Casbin access control model",
"Models": "Models", "Models": "Models",
"Name": "Name", "Name": "Name",
"Name - Tooltip": "Unique, string-based ID", "Name - Tooltip": "Unique, string-based ID",
"OAuth providers": "OAuth providers", "OAuth providers": "OAuth providers",
"OK": "OK",
"Organization": "Organization", "Organization": "Organization",
"Organization - Tooltip": "Select one from the organizations", "Organization - Tooltip": "Similar to concepts such as tenants or user pools, each user and application belongs to an organization",
"Organizations": "Organizations", "Organizations": "Organizations",
"Password": "Password", "Password": "Password",
"Password - Tooltip": "Make sure the password is correct", "Password - Tooltip": "Make sure the password is correct",
"Password salt": "Password salt", "Password salt": "Password salt",
"Password salt - Tooltip": "Random parameters for password encryption", "Password salt - Tooltip": "Random parameter used for password encryption",
"Password type": "Password type", "Password type": "Password type",
"Password type - Tooltip": "Form of password storage in the database", "Password type - Tooltip": "Storage format of passwords in the database",
"Payments": "Payments", "Payments": "Payments",
"Permissions": "Permissions", "Permissions": "Permissions",
"Permissions - Tooltip": "Permissions owned", "Permissions - Tooltip": "Permissions owned by this user",
"Phone": "Phone", "Phone": "Phone",
"Phone - Tooltip": "Phone number", "Phone - Tooltip": "Phone number",
"Preview": "Preview", "Preview": "Preview",
"Preview - Tooltip": "Preview screen", "Preview - Tooltip": "Preview the configured effects",
"Products": "Products", "Products": "Products",
"Provider": "Provider", "Provider": "Provider",
"Provider - Tooltip": "Payment providers, such as paypel", "Provider - Tooltip": "Payment providers to be configured, including PayPal, Alipay, WeChat Pay, etc.",
"Providers": "Providers", "Providers": "Providers",
"Providers - Tooltip": "Configuration providers such as OAuth, SMS, storage", "Providers - Tooltip": "Providers to be configured, including 3rd-party login, object storage, verification code, etc.",
"Real name": "Real name", "Real name": "Real name",
"Records": "Records", "Records": "Records",
"Request URI": "Request URI", "Request URI": "Request URI",
"Resources": "Resources", "Resources": "Resources",
"Roles": "Roles", "Roles": "Roles",
"Roles - Tooltip": "Roles owned", "Roles - Tooltip": "Roles that the user belongs to",
"Save": "Save", "Save": "Save",
"Save & Exit": "Save & Exit", "Save & Exit": "Save & Exit",
"Session ID": "Session ID", "Session ID": "Session ID",
"Sessions": "Sessions", "Sessions": "Sessions",
"Signin URL": "Signin URL", "Signin URL": "Signin URL",
"Signin URL - Tooltip": "User's login address", "Signin URL - Tooltip": "Custom URL for the login page. If not set, the default Casdoor login page will be used. When set, the login links on various Casdoor pages will redirect to this URL",
"Signup URL": "Signup URL", "Signup URL": "Signup URL",
"Signup URL - Tooltip": "Registration address displayed to users", "Signup URL - Tooltip": "Custom URL for the registration page. If not set, the default Casdoor registration page will be used. When set, the registration links on various Casdoor pages will redirect to this URL",
"Signup application": "Signup application", "Signup application": "Signup application",
"Signup application - Tooltip": "Which application the user registered through when registering", "Signup application - Tooltip": "Which application the user registered through when they signed up",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.", "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, 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.", "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.",
@ -253,30 +257,30 @@
"Successfully deleted": "Successfully deleted", "Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved", "Successfully saved": "Successfully saved",
"Supported country codes": "Supported country codes", "Supported country codes": "Supported country codes",
"Supported country codes - Tooltip": "Cell phone number prefix, used to distinguish between countries or regions", "Supported country codes - Tooltip": "Country codes supported by the organization. These codes can be selected as a prefix when sending SMS verification codes",
"Sure to delete": "Sure to delete", "Sure to delete": "Sure to delete",
"Swagger": "Swagger", "Swagger": "Swagger",
"Sync": "Sync", "Sync": "Sync",
"Syncers": "Syncers", "Syncers": "Syncers",
"SysInfo": "SysInfo", "System Info": "System Info",
"This is a read-only demo site!": "This is a read-only demo site!", "This is a read-only demo site!": "This is a read-only demo site!",
"Timestamp": "Timestamp", "Timestamp": "Timestamp",
"Tokens": "Tokens", "Tokens": "Tokens",
"URL": "URL", "URL": "URL",
"URL - Tooltip": "URL Link", "URL - Tooltip": "URL link",
"Up": "Up", "Up": "Up",
"User": "User", "User": "User",
"User - Tooltip": "Make sure the username is spelled correctly", "User - Tooltip": "Make sure the username is correct",
"User containers": "User containers", "User containers": "User pools",
"User type": "User type", "User type": "User type",
"User type - Tooltip": "User roles with different privileges", "User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
"Users": "Users", "Users": "Users",
"Users under all organizations": "Users under all organizations", "Users under all organizations": "Users under all organizations",
"Webhooks": "Webhooks", "Webhooks": "Webhooks",
"empty": "empty",
"{total} in total": "{total} in total" "{total} in total": "{total} in total"
}, },
"ldap": { "ldap": {
"Address": "Address",
"Admin": "Admin", "Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator", "Admin - Tooltip": "CN or ID of the LDAP server administrator",
"Admin Password": "Admin Password", "Admin Password": "Admin Password",
@ -287,21 +291,17 @@
"Base DN - Tooltip": "Base DN during LDAP search", "Base DN - Tooltip": "Base DN during LDAP search",
"CN": "CN", "CN": "CN",
"Edit LDAP": "Edit LDAP", "Edit LDAP": "Edit LDAP",
"Email": "Email",
"Enable SSL": "Enable SSL", "Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip", "Enable SSL - Tooltip": "Whether to enable SSL",
"Group Id": "Group Id", "Group ID": "Group ID",
"ID": "ID",
"Last Sync": "Last Sync", "Last Sync": "Last Sync",
"Phone": "Phone",
"Server": "Server", "Server": "Server",
"Server Host": "Server Host", "Server host": "Server host",
"Server Host - Tooltip": "LDAP server address", "Server host - Tooltip": "LDAP server address",
"Server Name": "Server Name", "Server name": "Server name",
"Server Name - Tooltip": "LDAP server configuration display name", "Server name - Tooltip": "LDAP server configuration display name",
"Server Port": "Server Port", "Server port": "Server port",
"Server Port - Tooltip": "LDAP server port", "Server port - Tooltip": "LDAP server port",
"Sync": "Sync",
"The Auto Sync option will sync all users to specify organization": "The Auto Sync option will sync all users to specify organization", "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" "UidNumber / Uid": "UidNumber / Uid"
}, },
@ -314,8 +314,6 @@
"Logging out...": "Logging out...", "Logging out...": "Logging out...",
"No account?": "No account?", "No account?": "No account?",
"Or sign in with another account": "Or sign in with another account", "Or sign in with another account": "Or sign in with another account",
"Password": "Password",
"Password - Tooltip": "Make sure the password is correct",
"Please input your Email or Phone!": "Please input your Email or Phone!", "Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Please input your code!", "Please input your code!": "Please input your code!",
"Please input your password!": "Please input your password!", "Please input your password!": "Please input your password!",
@ -325,10 +323,10 @@
"Sign in with WebAuthn": "Sign in with WebAuthn", "Sign in with WebAuthn": "Sign in with WebAuthn",
"Sign in with {type}": "Sign in with {type}", "Sign in with {type}": "Sign in with {type}",
"Signing in...": "Signing in...", "Signing in...": "Signing in...",
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials", "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!", "The input is not valid Email or phone number!": "The input is not valid Email or phone number!",
"To access": "To access", "To access": "To access",
"Verification Code": "Verification Code", "Verification code": "Verification code",
"WebAuthn": "WebAuthn", "WebAuthn": "WebAuthn",
"sign up now": "sign up now", "sign up now": "sign up now",
"username, Email or phone": "username, Email or phone" "username, Email or phone": "username, Email or phone"
@ -336,93 +334,85 @@
"model": { "model": {
"Edit Model": "Edit Model", "Edit Model": "Edit Model",
"Model text": "Model text", "Model text": "Model text",
"Model text - Tooltip": "Casbin Access Control Model", "Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
"New Model": "New Model" "New Model": "New Model"
}, },
"organization": { "organization": {
"Account items": "Account items", "Account items": "Account items",
"Account items - Tooltip": "Items in the Personal settings page", "Account items - Tooltip": "Items in the Personal settings page",
"Default avatar": "Default avatar",
"Edit Organization": "Edit Organization", "Edit Organization": "Edit Organization",
"Favicon": "Favicon",
"Follow global theme": "Follow global theme", "Follow global theme": "Follow global theme",
"InitScore": "InitScore", "Init score": "Init score",
"Init score - Tooltip": "Initial score points awarded to users upon registration",
"Is profile public": "Is profile public", "Is profile public": "Is profile public",
"Is profile public - Tooltip": "When closed, only global administrators or users from the same organization can access the user home page", "Is profile public - Tooltip": "After being closed, only global administrators or users in the same organization can access the user's profile page",
"Modify rule": "Modify rule", "Modify rule": "Modify rule",
"New Organization": "New Organization", "New Organization": "New Organization",
"Soft deletion": "Soft deletion", "Soft deletion": "Soft deletion",
"Soft deletion - Tooltip": "When enabled, deletion of user information will not be completely cleared in the database, but will be marked as deleted", "Soft deletion - Tooltip": "When enabled, deleting users will not completely remove them from the database. Instead, they will be marked as deleted",
"Tags": "Tags", "Tags": "Tags",
"Tags - Tooltip": "Collection of user-selectable tags", "Tags - Tooltip": "Collection of tags available for users to choose from",
"The user's initScore - Tooltip": "User's initial points",
"View rule": "View rule", "View rule": "View rule",
"Visible": "Visible", "Visible": "Visible",
"Website URL": "Website URL", "Website URL": "Website URL",
"Website URL - Tooltip": "Website URL" "Website URL - Tooltip": "The homepage URL of the organization. This field is not used in Casdoor"
}, },
"payment": { "payment": {
"Confirm your invoice information": "Confirm your invoice information", "Confirm your invoice information": "Confirm your invoice information",
"Currency": "Currency", "Currency": "Currency",
"Currency - Tooltip": "Payment Currency", "Currency - Tooltip": "Like USD, CNY, etc.",
"Download Invoice": "Download Invoice", "Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment", "Edit Payment": "Edit Payment",
"Individual": "Individual", "Individual": "Individual",
"Invoice URL": "Invoice URL", "Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "URL for downloading the invoice", "Invoice URL - Tooltip": "URL for downloading the invoice",
"Invoice actions": "Invoice actions", "Invoice actions": "Invoice actions",
"Invoice actions - Tooltip": "Operation includes both invoicing and downloading invoices", "Invoice actions - Tooltip": "Operations include issuing invoices and downloading invoices",
"Invoice remark": "Invoice remark", "Invoice remark": "Invoice remark",
"Invoice remark - Tooltip": "Remarks no more than 50 words", "Invoice remark - Tooltip": "The remark should not exceed 50 characters",
"Invoice tax ID": "Invoice tax ID", "Invoice tax ID": "Invoice tax ID",
"Invoice tax ID - Tooltip": "When the invoicing type is unit, the unit taxpayer identification number must be entered; when the invoicing type is individual, it is not necessary to fill in", "Invoice tax ID - Tooltip": "When the invoice type is for an organization, the organization taxpayer identification number must be entered; when the invoice type is for an individual, it is not necessary to fill in this information",
"Invoice title": "Invoice title", "Invoice title": "Invoice title",
"Invoice title - Tooltip": "When the invoicing type is unit, the name of the unit can be entered in the invoice payable; when the invoicing type is individual, the system automatically fills in the name of the contributor", "Invoice title - Tooltip": "When the invoice type is for an organization, the invoice title can be entered as the organization name; when the invoice type is for an individual, the system will automatically fill in the payer's name",
"Invoice type": "Invoice type", "Invoice type": "Invoice type",
"Invoice type - Tooltip": "Invoicing type can be personal or unit", "Invoice type - Tooltip": "The invoice type can be for an individual or an organization",
"Issue Invoice": "Issue Invoice", "Issue Invoice": "Issue Invoice",
"Message": "Message", "Message": "Message",
"Message - Tooltip": "Payment processing result message", "Message - Tooltip": "Payment processing result message",
"New Payment": "New Payment", "New Payment": "New Payment",
"Organization": "Organization",
"Person Email": "Person Email", "Person Email": "Person Email",
"Person Email - Tooltip": "Person Email", "Person Email - Tooltip": "Email of the payer",
"Person ID card": "Person ID card", "Person ID card": "Person ID card",
"Person ID card - Tooltip": "ID number of the payer", "Person ID card - Tooltip": "ID card number of the payer",
"Person name": "Person name", "Person name": "Person name",
"Person name - Tooltip": "Name of the payer", "Person name - Tooltip": "Real name of the payer",
"Person phone": "Person phone", "Person phone": "Person phone",
"Person phone - Tooltip": "The cell phone number of the payer", "Person phone - Tooltip": "The phone number of the payer",
"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 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 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!", "Please pay the order first!": "Please pay the order first!",
"Price": "Price",
"Price - Tooltip": "Price of payment",
"Processing...": "Processing...", "Processing...": "Processing...",
"Product": "Product", "Product": "Product",
"Product - Tooltip": "Product Name", "Product - Tooltip": "Product Name",
"Result": "Result", "Result": "Result",
"Return to Website": "Return to Website", "Return to Website": "Return to Website",
"State": "State",
"State - Tooltip": "Payment Status",
"The payment has failed": "The payment has failed", "The payment has failed": "The payment has failed",
"The payment is still under processing": "The payment is still under processing", "The payment is still under processing": "The payment is still under processing",
"Type": "Type", "Type - Tooltip": "Payment method used when purchasing the product",
"Type - Tooltip": "Payment method when purchasing product",
"You have successfully completed the payment": "You have successfully completed the payment", "You have successfully completed the payment": "You have successfully completed the payment",
"please wait for a few seconds...": "please wait for a few seconds...", "please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is" "the current state is": "the current state is"
}, },
"permission": { "permission": {
"Actions": "Actions", "Actions": "Actions",
"Actions - Tooltip": "Authorized actions", "Actions - Tooltip": "Allowed actions",
"Admin": "Admin", "Admin": "Admin",
"Allow": "Allow", "Allow": "Allow",
"Approve time": "Approve time", "Approve time": "Approve time",
"Approve time - Tooltip": "The time when the authorization was approved", "Approve time - Tooltip": "The time of approval for this permission",
"Approved": "Approved", "Approved": "Approved",
"Approver": "Approver", "Approver": "Approver",
"Approver - Tooltip": "The person who approved the authorization", "Approver - Tooltip": "The person who approved the permission",
"Deny": "Deny", "Deny": "Deny",
"Edit Permission": "Edit Permission", "Edit Permission": "Edit Permission",
"Effect": "Effect", "Effect": "Effect",
@ -431,13 +421,10 @@
"Pending": "Pending", "Pending": "Pending",
"Read": "Read", "Read": "Read",
"Resource type": "Resource type", "Resource type": "Resource type",
"Resource type - Tooltip": "Type of authorized resources", "Resource type - Tooltip": "Type of resource",
"Resources": "Resources",
"Resources - Tooltip": "Authorized resources", "Resources - Tooltip": "Authorized resources",
"State": "State",
"State - Tooltip": "The current statue of the authorization",
"Submitter": "Submitter", "Submitter": "Submitter",
"Submitter - Tooltip": "The person applying for this authorization", "Submitter - Tooltip": "The person applying for this permission",
"TreeNode": "TreeNode", "TreeNode": "TreeNode",
"Write": "Write" "Write": "Write"
}, },
@ -446,35 +433,30 @@
"Buy": "Buy", "Buy": "Buy",
"Buy Product": "Buy Product", "Buy Product": "Buy Product",
"CNY": "CNY", "CNY": "CNY",
"Currency": "Currency",
"Currency - Tooltip": "Currency accepted for the product",
"Description": "Description",
"Description - Tooltip": "Product Description",
"Detail": "Detail", "Detail": "Detail",
"Detail - Tooltip": "Product Details", "Detail - Tooltip": "Detail of product",
"Edit Product": "Edit Product", "Edit Product": "Edit Product",
"I have completed the payment": "I have completed the payment", "I have completed the payment": "I have completed the payment",
"Image": "Image", "Image": "Image",
"Image - Tooltip": "Product Image", "Image - Tooltip": "Image of product",
"New Product": "New Product", "New Product": "New Product",
"Pay": "Pay", "Pay": "Pay",
"PayPal": "PayPal",
"Payment providers": "Payment providers", "Payment providers": "Payment providers",
"Payment providers - Tooltip": "Providers of payment services", "Payment providers - Tooltip": "Providers of payment services",
"Paypal": "Paypal",
"Placing order...": "Placing order...", "Placing order...": "Placing order...",
"Please provide your username in the remark": "Please provide your username in the remark", "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", "Please scan the QR code to pay": "Please scan the QR code to pay",
"Price": "Price", "Price": "Price",
"Price - Tooltip": "Price of the product", "Price - Tooltip": "Price of product",
"Quantity": "Quantity", "Quantity": "Quantity",
"Quantity - Tooltip": "Quantity of the product", "Quantity - Tooltip": "Quantity of product",
"Return URL": "Return URL", "Return URL": "Return URL",
"Return URL - Tooltip": "The link that jumps after payment is complete", "Return URL - Tooltip": "URL to return to after successful purchase",
"SKU": "SKU", "SKU": "SKU",
"Sold": "Sold", "Sold": "Sold",
"Sold - Tooltip": "Number of this product sold", "Sold - Tooltip": "Quantity sold",
"Tag": "Tag", "Tag - Tooltip": "Tag of product",
"Tag - Tooltip": "Tag of the product",
"Test buy page..": "Test buy page..", "Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.", "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.", "This product is currently not in sale.": "This product is currently not in sale.",
@ -491,12 +473,12 @@
"App key": "App key", "App key": "App key",
"App key - Tooltip": "App key", "App key - Tooltip": "App key",
"App secret": "App secret", "App secret": "App secret",
"AppSecret - Tooltip": "AppSecret", "AppSecret - Tooltip": "App secret",
"Auth URL": "Auth URL", "Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL", "Auth URL - Tooltip": "Auth URL",
"Bucket": "Bucket", "Bucket": "Bucket",
"Bucket - Tooltip": "Name of Bucket", "Bucket - Tooltip": "Name of bucket",
"Can not parse Metadata": "Can not parse Metadata", "Can not parse metadata": "Can not parse metadata",
"Can signin": "Can signin", "Can signin": "Can signin",
"Can signup": "Can signup", "Can signup": "Can signup",
"Can unlink": "Can unlink", "Can unlink": "Can unlink",
@ -514,43 +496,45 @@
"Client secret 2 - Tooltip": "The second client secret key", "Client secret 2 - Tooltip": "The second client secret key",
"Copy": "Copy", "Copy": "Copy",
"Disable SSL": "Disable SSL", "Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Whether to disable SSL security protocol when communicating with STMP server", "Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
"Domain": "Domain", "Domain": "Domain",
"Domain - Tooltip": "Storage node custom domain name", "Domain - Tooltip": "Custom domain for object storage",
"Edit Provider": "Edit Provider", "Edit Provider": "Edit Provider",
"Email content": "Email content", "Email content": "Email content",
"Email content - Tooltip": "Content of the email", "Email content - Tooltip": "Content of the Email",
"Email sent successfully": "Email sent successfully", "Email sent successfully": "Email sent successfully",
"Email title": "Email title", "Email title": "Email title",
"Email title - Tooltip": "Title of the email", "Email title - Tooltip": "Title of the email",
"Enable QR code": "Enable QR code", "Enable QR code": "Enable QR code",
"Enable QR code - Tooltip": "Enable scan code to login", "Enable QR code - Tooltip": "Whether to allow scanning QR code to login",
"Endpoint": "Endpoint", "Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)", "Endpoint (Intranet)": "Endpoint (Intranet)",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Fill in the host address and make sure you can connect", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
"IdP certificate": "IdP certificate", "IdP certificate": "IdP certificate",
"Intelligent Validation": "Intelligent Validation",
"Internal": "Internal",
"Issuer URL": "Issuer URL", "Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL", "Issuer URL - Tooltip": "Issuer URL",
"Link copied to clipboard successfully": "Link copied to clipboard successfully", "Link copied to clipboard successfully": "Link copied to clipboard successfully",
"Metadata": "Metadata", "Metadata": "Metadata",
"Metadata - Tooltip": "Metadata", "Metadata - Tooltip": "SAML metadata",
"Method": "Method", "Method - Tooltip": "Login method, QR code or silent login",
"Method - Tooltip": "Login behavior, QR code or silent authorization login",
"Name": "Name",
"New Provider": "New Provider", "New Provider": "New Provider",
"Normal": "Normal",
"Parse": "Parse", "Parse": "Parse",
"Parse Metadata successfully": "Parse Metadata successfully", "Parse metadata successfully": "Parse metadata successfully",
"Path prefix": "Path prefix", "Path prefix": "Path prefix",
"Path prefix - Tooltip": "Bucket path prefix for object storage",
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in", "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": "Port",
"Port - Tooltip": "Make sure the port is open", "Port - Tooltip": "Make sure the port is open",
"Prompted": "Prompted", "Prompted": "Prompted",
"Provider URL": "Provider URL", "Provider URL": "Provider URL",
"Provider URL - Tooltip": "Provider URL", "Provider URL - Tooltip": "URL for configuring the service provider, this field is only used for reference and is not used in Casdoor",
"Region ID": "Region ID", "Region ID": "Region ID",
"Region ID - Tooltip": "Region ID - Tooltip", "Region ID - Tooltip": "Region ID for the service provider",
"Region endpoint for Internet": "Region endpoint for Internet", "Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet", "Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required", "Required": "Required",
@ -568,9 +552,9 @@
"Scope": "Scope", "Scope": "Scope",
"Scope - Tooltip": "Scope", "Scope - Tooltip": "Scope",
"Secret access key": "Secret access key", "Secret access key": "Secret access key",
"Secret access key - Tooltip": "Secret access key",
"Secret key": "Secret key", "Secret key": "Secret key",
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification", "Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"SecretAccessKey - Tooltip": "SecretAccessKey",
"Send Testing Email": "Send Testing Email", "Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS", "Send Testing SMS": "Send Testing SMS",
"Sign Name": "Sign Name", "Sign Name": "Sign Name",
@ -579,57 +563,50 @@
"Sign request - Tooltip": "Whether the request requires a signature", "Sign request - Tooltip": "Whether the request requires a signature",
"Signin HTML": "Signin HTML", "Signin HTML": "Signin HTML",
"Signin HTML - Edit": "Signin HTML - Edit", "Signin HTML - Edit": "Signin HTML - Edit",
"Signin HTML - Tooltip": "Custom HTML for replacing the default login page style", "Signin HTML - Tooltip": "Custom HTML for replacing the default signin page style",
"Signup HTML": "Signup HTML", "Signup HTML": "Signup HTML",
"Signup HTML - Edit": "Signup HTML - Edit", "Signup HTML - Edit": "Signup HTML - Edit",
"Signup HTML - Tooltip": "Custom HTML for replacing the default registration page style", "Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
"Silent": "Silent",
"Site key": "Site key", "Site key": "Site key",
"Site key - Tooltip": "For front-end embedded pages", "Site key - Tooltip": "Site key",
"Sliding Validation": "Sliding Validation",
"Sub type": "Sub type", "Sub type": "Sub type",
"Sub type - Tooltip": "Sub type", "Sub type - Tooltip": "Sub type",
"Template Code": "Template Code", "Template code": "Template code",
"Template Code - Tooltip": "The code of the template used", "Template code - Tooltip": "Template code",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use to be followed by users",
"Test Connection": "Test Connection",
"Test Email": "Test Email", "Test Email": "Test Email",
"Test Email - Tooltip": "Address to accept test emails", "Test Email - Tooltip": "Email address to receive test mails",
"The prefix path of the file - Tooltip": "The prefix path of the file", "Test SMTP Connection": "Test SMTP Connection",
"Third-party": "Third-party",
"Token URL": "Token URL", "Token URL": "Token URL",
"Token URL - Tooltip": "Token URL", "Token URL - Tooltip": "Token URL",
"Type": "Type", "Type": "Type",
"Type - Tooltip": "Select a type", "Type - Tooltip": "Select a type",
"UserInfo URL": "UserInfo URL", "UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL", "UserInfo URL - Tooltip": "UserInfo URL",
"Visible": "Visible", "admin (Shared)": "admin (Shared)"
"admin (share)": "admin (share)",
"alertType": "alertType"
}, },
"record": { "record": {
"Is Triggered": "Is Triggered" "Is triggered": "Is triggered"
}, },
"resource": { "resource": {
"Application": "Application",
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
"File name": "File name", "File name": "File name",
"File size": "File size", "File size": "File size",
"Format": "Format", "Format": "Format",
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
"Parent": "Parent", "Parent": "Parent",
"Tag": "Tag", "Upload a file...": "Upload a file..."
"Type": "Type",
"Upload a file...": "Upload a file...",
"User": "User"
}, },
"role": { "role": {
"Edit Role": "Edit Role", "Edit Role": "Edit Role",
"New Role": "New Role", "New Role": "New Role",
"Sub domains": "Sub domains", "Sub domains": "Sub domains",
"Sub domains - Tooltip": "Sub domains included in the current role", "Sub domains - Tooltip": "Domains included in the current role",
"Sub roles": "Sub roles", "Sub roles": "Sub roles",
"Sub roles - Tooltip": "Sub-roles contained in the current role", "Sub roles - Tooltip": "Roles included in the current role",
"Sub users": "Sub users", "Sub users": "Sub users",
"Sub users - Tooltip": "Select the sub-users under the role" "Sub users - Tooltip": "Users included in the current role"
}, },
"signup": { "signup": {
"Accept": "Accept", "Accept": "Accept",
@ -653,12 +630,13 @@
"Please select your country code!": "Please select your country code!", "Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!", "Please select your country/region!": "Please select your country/region!",
"Terms of Use": "Terms of Use", "Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "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 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 Email!": "The input is not valid Email!",
"The input is not valid Phone!": "The input is not valid Phone!", "The input is not valid Phone!": "The input is not valid Phone!",
"Username": "Username", "Username": "Username",
"Username - Tooltip": "Allowed characters include letters, numbers, underscores, and may not begin with a number", "Username - Tooltip": "Username - Tooltip",
"Your account has been created!": "Your account has been created!", "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!", "Your confirmed password is inconsistent with the password!": "Your confirmed password is inconsistent with the password!",
"sign in now": "sign in now" "sign in now": "sign in now"
@ -667,25 +645,25 @@
"Affiliation table": "Affiliation table", "Affiliation table": "Affiliation table",
"Affiliation table - Tooltip": "Database table name of the work unit", "Affiliation table - Tooltip": "Database table name of the work unit",
"Avatar base URL": "Avatar base URL", "Avatar base URL": "Avatar base URL",
"Avatar base URL - Tooltip": "Prefix of the avatar URL", "Avatar base URL - Tooltip": "URL prefix for the avatar images",
"Casdoor column": "Casdoor column", "Casdoor column": "Casdoor column",
"Column name": "Column name", "Column name": "Column name",
"Column type": "Column type", "Column type": "Column type",
"Database": "Database", "Database": "Database",
"Database - Tooltip": "The original database name", "Database - Tooltip": "The original database name",
"Database type": "Database type", "Database type": "Database type",
"Database type - Tooltip": "Database type, now support MySQL, PostgreSQL, SQL server, Oracle, SQLite 3", "Database type - Tooltip": "Database type, supporting all databases supported by XORM, such as MySQL, PostgreSQL, SQL Server, Oracle, SQLite, etc.",
"Edit Syncer": "Edit Syncer", "Edit Syncer": "Edit Syncer",
"Error text": "Error text", "Error text": "Error text",
"Error text - Tooltip": "Error when synchronizer connects to database", "Error text - Tooltip": "Error text",
"Is hashed": "Is hashed", "Is hashed": "Is hashed",
"New Syncer": "New Syncer", "New Syncer": "New Syncer",
"Sync interval": "Sync interval", "Sync interval": "Sync interval",
"Sync interval - Tooltip": "Unit in seconds", "Sync interval - Tooltip": "Unit in seconds",
"Table": "Table", "Table": "Table",
"Table - Tooltip": "Name of table", "Table - Tooltip": "Name of database table",
"Table columns": "Table columns", "Table columns": "Table columns",
"Table columns - Tooltip": "Columns of the table that participate in data synchronization, columns that do not participate in synchronization do not need to be added", "Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added",
"Table primary key": "Table primary key", "Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key, such as id" "Table primary key - Tooltip": "Table primary key, such as id"
}, },
@ -694,11 +672,11 @@
"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", "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", "CPU Usage": "CPU Usage",
"Community": "Community", "Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed", "Failed to get CPU usage": "Failed to get CPU usage",
"Get Memory Usage Failed": "Get Memory Usage Failed", "Failed to get memory usage": "Failed to get memory usage",
"Memory Usage": "Memory Usage", "Memory Usage": "Memory Usage",
"Official Website": "Official Website", "Official website": "Official website",
"Unknown Version": "Unknown Version", "Unknown version": "Unknown version",
"Version": "Version" "Version": "Version"
}, },
"theme": { "theme": {
@ -712,7 +690,7 @@
"Is compact": "Is compact", "Is compact": "Is compact",
"Primary color": "Primary color", "Primary color": "Primary color",
"Theme": "Theme", "Theme": "Theme",
"Theme - Tooltip": "Select a theme for your application" "Theme - Tooltip": "Style theme of the application"
}, },
"token": { "token": {
"Access token": "Access token", "Access token": "Access token",
@ -720,42 +698,39 @@
"Edit Token": "Edit Token", "Edit Token": "Edit Token",
"Expires in": "Expires in", "Expires in": "Expires in",
"New Token": "New Token", "New Token": "New Token",
"Scope": "Scope",
"Token type": "Token type" "Token type": "Token type"
}, },
"user": { "user": {
"\" + destType + \" reset": "\" + destType + \" reset",
"3rd-party logins": "3rd-party logins", "3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "Login using a third-party application", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Usual residence address", "Address - Tooltip": "Residential address",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Workplace, such as company name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
"Bio - Tooltip": "Self Introduction", "Bio - Tooltip": "Self introduction of the user",
"Cancel": "Cancel",
"Captcha Verify Failed": "Captcha Verify Failed", "Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success", "Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code Sent",
"Country code": "Country code", "Country code": "Country code",
"Country/Region": "Country/Region", "Country/Region": "Country/Region",
"Country/Region - Tooltip": "Select Country/Region", "Country/Region - Tooltip": "Country or region",
"Edit User": "Edit User", "Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty", "Email cannot be empty": "Email cannot be empty",
"Email/phone reset successfully": "Email/phone reset successfully",
"Empty input!": "Empty input!", "Empty input!": "Empty input!",
"Homepage": "Homepage", "Homepage": "Homepage",
"Homepage - Tooltip": "Link to personal homepage", "Homepage - Tooltip": "Homepage URL of the user",
"ID card": "ID card", "ID card": "ID card",
"Input your email": "Input your email", "Input your email": "Input your email",
"Input your phone number": "Input your phone number", "Input your phone number": "Input your phone number",
"Is admin": "Is admin", "Is admin": "Is admin",
"Is admin - Tooltip": "Is the application admin", "Is admin - Tooltip": "Is an administrator of the organization the user belongs to",
"Is deleted": "Is deleted", "Is deleted": "Is deleted",
"Is deleted - Tooltip": "Is the account deleted", "Is deleted - Tooltip": "Soft-deleted users only retain database records and cannot perform any operations",
"Is forbidden": "Is forbidden", "Is forbidden": "Is forbidden",
"Is forbidden - Tooltip": "Is the account disabled", "Is forbidden - Tooltip": "Forbidden users cannot log in any more",
"Is global admin": "Is global admin", "Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is the global application", "Is global admin - Tooltip": "Is an administrator of Casdoor",
"Keys": "Keys", "Keys": "Keys",
"Link": "Link", "Link": "Link",
"Location": "Location", "Location": "Location",
@ -766,14 +741,12 @@
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
"New phone": "New phone", "New phone": "New phone",
"OK": "OK",
"Old Password": "Old Password", "Old Password": "Old Password",
"Password": "Password", "Password set successfully": "Password set successfully",
"Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources", "Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties", "Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip", "Properties - Tooltip": "Properties of the user",
"Re-enter New": "Re-enter New", "Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",
"Reset Phone...": "Reset Phone...", "Reset Phone...": "Reset Phone...",
@ -782,14 +755,15 @@
"Set new profile picture": "Set new profile picture", "Set new profile picture": "Set new profile picture",
"Set password...": "Set password...", "Set password...": "Set password...",
"Tag": "Tag", "Tag": "Tag",
"Tag - Tooltip": "User's Tags", "Tag - Tooltip": "Tag of the user",
"Title": "Title", "Title": "Title",
"Title - Tooltip": "Position in the unit/company", "Title - Tooltip": "Position in the affiliation",
"Two passwords you typed do not match.": "Two passwords you typed do not match.", "Two passwords you typed do not match.": "Two passwords you typed do not match.",
"Unlink": "Unlink", "Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)", "Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Upload a photo", "Upload a photo": "Upload a photo",
"Values": "Values", "Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials", "WebAuthn credentials": "WebAuthn credentials",
"input password": "input password" "input password": "input password"
}, },
@ -800,15 +774,11 @@
"Events": "Events", "Events": "Events",
"Events - Tooltip": "Events", "Events - Tooltip": "Events",
"Headers": "Headers", "Headers": "Headers",
"Headers - Tooltip": "HTTP protocol headers (key-value pairs)", "Headers - Tooltip": "HTTP headers (key-value pairs)",
"Is user extended": "Is user extended", "Is user extended": "Is user extended",
"Is user extended - Tooltip": "Add extendedUser to JSON to extend the user field", "Is user extended - Tooltip": "Whether to include the user's extended fields in the JSON",
"Method": "Method", "Method - Tooltip": "HTTP method",
"Method - Tooltip": "Http method",
"Name": "Name",
"New Webhook": "New Webhook", "New Webhook": "New Webhook",
"URL": "URL",
"URL - Tooltip": "URL",
"Value": "Value" "Value": "Value"
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,784 @@
{
"account": {
"Logout": "Keluar",
"My Account": "Akun Saya",
"Sign Up": "Mendaftar"
},
"adapter": {
"Duplicated policy rules": "Aturan kebijakan yang diduplikasi",
"Edit Adapter": "Mengedit adapter",
"Failed to sync policies": "Gagal melakukan sinkronisasi kebijakan",
"New Adapter": "Adapter Baru",
"Policies": "Kebijakan",
"Policies - Tooltip": "Kebijakan aturan Casbin",
"Sync policies successfully": "Sinkronisasi kebijakan berhasil dilakukan"
},
"application": {
"Always": "Selalu",
"Auto signin": "Masuk otomatis",
"Auto signin - Tooltip": "Ketika sesi masuk yang terdaftar ada di Casdoor, secara otomatis digunakan untuk masuk ke sisi aplikasi",
"Background URL": "URL latar belakang",
"Background URL - Tooltip": "URL dari gambar latar belakang yang digunakan di halaman login",
"Center": "pusat",
"Copy SAML metadata URL": "Salin URL metadata SAML",
"Copy prompt page URL": "Salin URL halaman prompt",
"Copy signin page URL": "Salin URL halaman masuk",
"Copy signup page URL": "Salin URL halaman pendaftaran",
"Edit Application": "Mengedit aplikasi",
"Enable Email linking": "Aktifkan pengaitan email",
"Enable Email linking - Tooltip": "Ketika menggunakan penyedia layanan pihak ketiga untuk masuk, jika ada pengguna di organisasi dengan email yang sama, metode login pihak ketiga akan secara otomatis terhubung dengan pengguna tersebut",
"Enable SAML compression": "Aktifkan kompresi SAML",
"Enable SAML compression - Tooltip": "Apakah pesan respons SAML harus dikompres saat Casdoor digunakan sebagai SAML idp?",
"Enable WebAuthn signin": "Aktifkan masuk WebAuthn",
"Enable WebAuthn signin - Tooltip": "Apakah mengizinkan pengguna untuk masuk dengan WebAuthn",
"Enable code signin": "Aktifkan tanda tangan kode",
"Enable code signin - Tooltip": "Apakah mengizinkan pengguna untuk login dengan kode verifikasi telepon atau email",
"Enable password": "Aktifkan kata sandi",
"Enable password - Tooltip": "Apakah harus memperbolehkan pengguna untuk masuk dengan kata sandi",
"Enable side panel": "Aktifkan panel samping",
"Enable signin session - Tooltip": "Apakah Casdoor mempertahankan sesi setelah login ke Casdoor dari aplikasi",
"Enable signup": "Aktifkan pendaftaran",
"Enable signup - Tooltip": "Apakah akan mengizinkan pengguna untuk mendaftar akun baru",
"Failed to sign in": "Gagal masuk",
"File uploaded successfully": "Berkas telah diunggah dengan sukses",
"First, last": "First, last",
"Follow organization theme": "Ikuti tema organisasi",
"Form CSS": "Formulir CSS",
"Form CSS - Edit": "Formulir CSS - Edit",
"Form CSS - Tooltip": "Pengaturan CSS dari formulir pendaftaran, masuk, dan lupa kata sandi (misalnya menambahkan batas dan bayangan)",
"Form position": "Posisi formulir",
"Form position - Tooltip": "Tempat pendaftaran, masuk, dan lupa kata sandi",
"Grant types": "Jenis-jenis hibah",
"Grant types - Tooltip": "Pilih jenis hibah apa yang diperbolehkan dalam protokol OAuth",
"Incremental": "Incremental",
"Left": "Kiri",
"Logged in successfully": "Berhasil masuk",
"Logged out successfully": "Berhasil keluar dari sistem",
"New Application": "Aplikasi Baru",
"No verification": "No verification",
"None": "Tidak ada",
"Normal": "Normal",
"Only signup": "Only signup",
"Please input your application!": "Silakan masukkan aplikasi Anda!",
"Please input your organization!": "Silakan masukkan organisasi Anda!",
"Please select a HTML file": "Silahkan pilih file HTML",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Tautan halaman Prompt berhasil disalin ke papan klip, silakan tempelkan ke jendela penyamaran atau browser lainnya",
"Random": "Random",
"Real name": "Real name",
"Redirect URL": "Mengalihkan URL",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL pengalihan (Penyanggah Konsumen Layanan Ikatan POST URL)",
"Redirect URLs": "Mengarahkan URL",
"Redirect URLs - Tooltip": "Daftar URL redirect yang diizinkan, mendukung pencocokan ekspresi reguler; URL yang tidak ada dalam daftar akan gagal dialihkan",
"Refresh token expire": "Token segar kedaluwarsa",
"Refresh token expire - Tooltip": "Waktu kedaluwarsa token penyegaran",
"Right": "Benar",
"Rule": "Aturan",
"SAML metadata": "Metadata SAML",
"SAML metadata - Tooltip": "Metadata dari protokol SAML",
"SAML metadata URL copied to clipboard successfully": "URL metadata SAML berhasil disalin ke clipboard",
"SAML reply URL": "Alamat URL Balasan SAML",
"Side panel HTML": "Panel samping HTML",
"Side panel HTML - Edit": "Panel sisi HTML - Sunting",
"Side panel HTML - Tooltip": "Menyesuaikan kode HTML untuk panel samping halaman login",
"Sign Up Error": "Kesalahan Pendaftaran",
"Signin": "Signin",
"Signin (Default True)": "Signin (Default True)",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL halaman masuk berhasil disalin ke clipboard, silakan tempelkan di jendela penyamaran atau browser lainnya",
"Signin session": "Sesi masuk",
"Signup items": "Item pendaftaran",
"Signup items - Tooltip": "Item-item yang harus diisi pengguna saat mendaftar untuk akun baru",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Tautan halaman pendaftaran URL berhasil disalin ke papan klip, silakan tempelkan ke dalam jendela incognito atau browser lain",
"The application does not allow to sign up new account": "Aplikasi tidak memperbolehkan untuk mendaftar akun baru",
"Token expire": "Token kadaluarsa",
"Token expire - Tooltip": "Waktu kadaluwarsa token akses",
"Token format": "Format token",
"Token format - Tooltip": "Format dari token akses",
"You are unexpected to see this prompt page": "Anda tidak mengharapkan untuk melihat halaman prompt ini"
},
"cert": {
"Bit size": "Ukuran bit",
"Bit size - Tooltip": "Panjang kunci rahasia",
"Certificate": "Sertifikat",
"Certificate - Tooltip": "Sertifikat kunci publik, digunakan untuk mendekripsi tanda tangan JWT pada Access Token. Sertifikat ini biasanya perlu diimplementasikan pada sisi SDK Casdoor (yaitu aplikasi) untuk memecahkan JWT",
"Certificate copied to clipboard successfully": "Sertifikat berhasil disalin ke clipboard",
"Copy certificate": "Salin sertifikat",
"Copy private key": "Salin kunci pribadi",
"Crypto algorithm": "Algoritma kriptografi",
"Crypto algorithm - Tooltip": "Algoritma enkripsi yang digunakan oleh sertifikat",
"Download certificate": "Unduh sertifikat",
"Download private key": "Unduh kunci pribadi",
"Edit Cert": "Mengedit Sertifikat",
"Expire in years": "Kedaluwarsa dalam tahun-tahun",
"Expire in years - Tooltip": "Masa berlaku sertifikat, dalam tahun",
"New Cert": "Sertifikat Baru",
"Private key": "Kunci pribadi",
"Private key - Tooltip": "Kunci pribadi yang sesuai dengan sertifikat kunci publik",
"Private key copied to clipboard successfully": "Kunci pribadi berhasil disalin ke clipboard",
"Scope - Tooltip": "Skema penggunaan sertifikat:",
"Type - Tooltip": "Jenis sertifikat"
},
"code": {
"Code you received": "Kode yang kamu terima",
"Email code": "Kode email",
"Empty code": "Kode kosong",
"Enter your code": "Masukkan kode Anda",
"Phone code": "Kode telepon",
"Please input your phone verification code!": "Silakan masukkan kode verifikasi telepon Anda!",
"Please input your verification code!": "Harap masukkan kode verifikasi Anda!",
"Send Code": "Kirimkan Kode",
"Sending": "Mengirimkan",
"Submit and complete": "Kirim dan selesaikan"
},
"forget": {
"Account": "Akun",
"Change Password": "Ubah Kata Sandi",
"Choose email or phone": "Pilih email atau telepon",
"Next Step": "Langkah selanjutnya",
"Please input your username!": "Silakan masukkan nama pengguna Anda!",
"Reset": "Menyetel-ulang",
"Retrieve password": "Mengambil password",
"Unknown forget type": "Tipe yang tidak diketahui terlupakan",
"Verify": "Memverifikasi"
},
"general": {
"Action": "Aksi",
"Adapter": "Adapter",
"Adapter - Tooltip": "Nama tabel dari penyimpanan kebijakan",
"Adapters": "Adaptor",
"Add": "Tambahkan",
"Affiliation URL": "URL Afiliasi",
"Affiliation URL - Tooltip": "URL halaman depan untuk afiliasi",
"Application": "Aplikasi",
"Applications": "Aplikasi",
"Applications that require authentication": "Aplikasi yang memerlukan autentikasi",
"Avatar": "Avatar",
"Avatar - Tooltip": "Gambar avatar publik untuk pengguna",
"Back Home": "Kembali ke Rumah",
"Cancel": "Membatalkan",
"Captcha": "Captcha",
"Cert": "Sertifikat",
"Cert - Tooltip": "Sertifikat kunci publik yang perlu diverifikasi oleh SDK klien yang sesuai dengan aplikasi ini",
"Certs": "Sertifikat",
"Click to Upload": "Klik untuk Mengunggah",
"Client IP": "IP klien",
"Close": "Tutup",
"Created time": "Waktu dibuat",
"Default application": "Aplikasi default",
"Default application - Tooltip": "Aplikasi default untuk pengguna yang terdaftar langsung dari halaman organisasi",
"Default avatar": "Avatar default",
"Default avatar - Tooltip": "Avatar default yang digunakan ketika pengguna yang baru terdaftar tidak mengatur gambar avatar",
"Delete": "Hapus",
"Description": "Deskripsi",
"Description - Tooltip": "Informasi deskripsi terperinci untuk referensi, Casdoor itu sendiri tidak akan menggunakannya",
"Display name": "Nama tampilan",
"Display name - Tooltip": "Sebuah nama yang mudah digunakan dan mudah dibaca yang ditampilkan secara publik di UI",
"Down": "Turun",
"Edit": "Mengedit",
"Email": "Email",
"Email - Tooltip": "Alamat email yang valid",
"Failed to add": "Gagal menambahkan",
"Failed to connect to server": "Gagal terhubung ke server",
"Failed to delete": "Gagal menghapus",
"Failed to save": "Gagal menyimpan",
"Favicon": "Favicon",
"Favicon - Tooltip": "URL ikon Favicon yang digunakan di semua halaman Casdoor organisasi",
"First name": "Nama depan",
"Forget URL": "Lupakan URL",
"Forget URL - Tooltip": "URL kustom untuk halaman \"Lupa kata sandi\". Jika tidak diatur, halaman \"Lupa kata sandi\" default Casdoor akan digunakan. Ketika diatur, tautan \"Lupa kata sandi\" pada halaman masuk akan diarahkan ke URL ini",
"Found some texts still not translated? Please help us translate at": "Menemukan beberapa teks yang masih belum diterjemahkan? Tolong bantu kami menerjemahkan di",
"Go to writable demo site?": "Pergi ke situs demo yang dapat ditulis?",
"Home": "Rumah",
"Home - Tooltip": "Halaman utama aplikasi",
"ID": "ID",
"ID - Tooltip": "Karakter acak unik",
"Is enabled": "Diaktifkan",
"Is enabled - Tooltip": "Atur apakah itu dapat digunakan",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "Server LDAP",
"Languages": "Bahasa-bahasa",
"Languages - Tooltip": "Bahasa yang tersedia",
"Last name": "Nama belakang",
"Logo": "Logo",
"Logo - Tooltip": "Ikon-ikon yang disajikan aplikasi ke dunia luar",
"Master password": "Kata sandi utama",
"Master password - Tooltip": "Dapat digunakan untuk masuk ke semua pengguna di bawah organisasi ini, sehingga memudahkan administrator untuk masuk sebagai pengguna ini untuk menyelesaikan masalah teknis",
"Menu": "Daftar makanan",
"Method": "Metode",
"Model": "Model",
"Model - Tooltip": "Model kontrol akses Casbin",
"Models": "Model-model",
"Name": "Nama",
"Name - Tooltip": "ID unik berbasis string",
"OAuth providers": "Penyedia OAuth",
"OK": "Baik atau Oke",
"Organization": "Organisasi",
"Organization - Tooltip": "Sama seperti konsep seperti penyewa atau grup pengguna, setiap pengguna dan aplikasi termasuk ke dalam suatu organisasi",
"Organizations": "Organisasi",
"Password": "Kata sandi",
"Password - Tooltip": "Pastikan kata sandi yang benar",
"Password salt": "Garam sandi",
"Password salt - Tooltip": "Parameter acak yang digunakan untuk enkripsi kata sandi",
"Password type": "Jenis kata sandi",
"Password type - Tooltip": "Format penyimpanan kata sandi di database",
"Payments": "Pembayaran-pembayaran",
"Permissions": "Izin-izin",
"Permissions - Tooltip": "Izin dimiliki oleh pengguna ini",
"Phone": "Telepon",
"Phone - Tooltip": "Nomor telepon",
"Preview": "Tinjauan",
"Preview - Tooltip": "Mengawali pratinjau efek yang sudah dikonfigurasi",
"Products": "Produk",
"Provider": "Penyedia",
"Provider - Tooltip": "Penyedia pembayaran harus dikonfigurasi, termasuk PayPal, Alipay, WeChat Pay, dan sebagainya.",
"Providers": "Penyedia-penyedia",
"Providers - Tooltip": "Penyedia harus dikonfigurasi, termasuk login pihak ketiga, penyimpanan objek, kode verifikasi, dan lain-lain.",
"Real name": "Nama asli",
"Records": "Catatan",
"Request URI": "Permintaan URI",
"Resources": "Sumber daya",
"Roles": "Peran-peran",
"Roles - Tooltip": "Peran-peran yang diikuti oleh pengguna",
"Save": "Menyimpan",
"Save & Exit": "Simpan & Keluar",
"Session ID": "ID sesi",
"Sessions": "Sesi-sesi",
"Signin URL": "URL Masuk",
"Signin URL - Tooltip": "URL kustom untuk halaman masuk. Jika tidak diatur, halaman masuk Casdoor default akan digunakan. Ketika diatur, tautan masuk di berbagai halaman Casdoor akan diarahkan ke URL ini",
"Signup URL": "URL Pendaftaran",
"Signup URL - Tooltip": "URL kustom untuk halaman pendaftaran. Jika tidak diatur, halaman pendaftaran Casdoor default akan digunakan. Ketika diatur, tautan pendaftaran pada berbagai halaman Casdoor akan dialihkan ke URL ini",
"Signup application": "Pendaftaran aplikasi",
"Signup application - Tooltip": "Melalui aplikasi mana pengguna mendaftar saat mereka mendaftar",
"Sorry, the page you visited does not exist.": "Maaf, halaman yang Anda kunjungi tidak ada.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Maaf, pengguna yang Anda kunjungi tidak ada atau Anda tidak diizinkan untuk mengakses pengguna ini.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Maaf, Anda tidak memiliki izin untuk mengakses halaman ini atau status masuk tidak valid.",
"State": "Negara",
"State - Tooltip": "Negara",
"Successfully added": "Berhasil ditambahkan",
"Successfully deleted": "Berhasil dihapus",
"Successfully saved": "Berhasil disimpan",
"Supported country codes": "Kode negara yang didukung",
"Supported country codes - Tooltip": "Kode negara yang didukung oleh organisasi. Kode-kode ini dapat dipilih sebagai awalan saat mengirim kode verifikasi SMS",
"Sure to delete": "Pasti untuk menghapus",
"Swagger": "Swagger",
"Sync": "Sinkronisasi",
"Syncers": "Sinkronisasi",
"System Info": "Informasi Sistem",
"This is a read-only demo site!": "Ini adalah situs demo hanya untuk dibaca saja!",
"Timestamp": "Waktu penanda waktu",
"Tokens": "Token-token",
"URL": "URL",
"URL - Tooltip": "Tautan URL",
"Up": "Ke atas",
"User": "Pengguna",
"User - Tooltip": "Pastikan username-nya benar",
"User containers": "User pools",
"User type": "Jenis pengguna",
"User type - Tooltip": "Tag yang dimiliki oleh pengguna, defaultnya adalah \"normal-user\"",
"Users": "Pengguna-pengguna",
"Users under all organizations": "Pengguna di bawah semua organisasi",
"Webhooks": "Webhooks",
"empty": "kosong",
"{total} in total": "{total} secara keseluruhan"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN atau ID dari administrator server LDAP",
"Admin Password": "Kata sandi administrator",
"Admin Password - Tooltip": "Kata sandi administrator server LDAP",
"Auto Sync": "Auto Sinkronisasi",
"Auto Sync - Tooltip": "Konfigurasi auto-sync dimatikan pada 0",
"Base DN": "DN dasar",
"Base DN - Tooltip": "Base DN selama pencarian LDAP",
"CN": "CN",
"Edit LDAP": "Mengedit LDAP",
"Enable SSL": "Aktifkan SSL",
"Enable SSL - Tooltip": "Apakah untuk mengaktifkan SSL?",
"Group ID": "ID grup",
"Last Sync": "Terakhir Sinkronisasi",
"Server": "Server",
"Server host": "Hewan Server",
"Server host - Tooltip": "Alamat server LDAP",
"Server name": "Nama server",
"Server name - Tooltip": "Konfigurasi nama tampilan server LDAP",
"Server port": "Port server",
"Server port - Tooltip": "Port server LDAP",
"The Auto Sync option will sync all users to specify organization": "Opsi Auto Sync akan menyinkronkan semua pengguna ke organisasi tertentu",
"UidNumber / Uid": "NomorUID / UID"
},
"login": {
"Auto sign in": "Masuk otomatis",
"Continue with": "Lanjutkan dengan",
"Email or phone": "Email atau telepon",
"Forgot password?": "Lupa kata sandi?",
"Loading": "Memuat",
"Logging out...": "Keluar...",
"No account?": "Tidak memiliki akun?",
"Or sign in with another account": "Atau masuk dengan akun lain",
"Please input your Email or Phone!": "Silahkan masukkan email atau nomor telepon Anda!",
"Please input your code!": "Silakan masukkan kode Anda!",
"Please input your password!": "Masukkan kata sandi Anda!",
"Please input your password, at least 6 characters!": "Silakan masukkan kata sandi Anda, minimal 6 karakter!",
"Redirecting, please wait.": "Mengalihkan, harap tunggu.",
"Sign In": "Masuk",
"Sign in with WebAuthn": "Masuk dengan WebAuthn",
"Sign in with {type}": "Masuk dengan {jenis}",
"Signing in...": "Masuk...",
"Successfully logged in with WebAuthn credentials": "Berhasil masuk dengan kredensial WebAuthn",
"The input is not valid Email or phone number!": "Input yang Anda masukkan tidak valid, tidak sesuai dengan Email atau nomor telepon!",
"To access": "Untuk mengakses",
"Verification code": "Kode verifikasi",
"WebAuthn": "WebAuthn",
"sign up now": "Daftar sekarang",
"username, Email or phone": "nama pengguna, Email atau nomor telepon"
},
"model": {
"Edit Model": "Mengedit Model",
"Model text": "Teks Model",
"Model text - Tooltip": "Model kontrol akses Casbin, termasuk model bawaan seperti ACL, RBAC, ABAC, RESTful, dll. Anda juga dapat membuat model kustom. Untuk informasi lebih lanjut, silakan kunjungi situs web Casbin",
"New Model": "Model baru"
},
"organization": {
"Account items": "Item akun",
"Account items - Tooltip": "Item pada halaman pengaturan personal",
"Edit Organization": "Edit Organisasi",
"Follow global theme": "Ikuti tema global",
"Init score": "Skor awal",
"Init score - Tooltip": "Poin skor awal diberikan kepada pengguna saat pendaftaran",
"Is profile public": "Apakah profilnya publik?",
"Is profile public - Tooltip": "Setelah ditutup, hanya administrator global atau pengguna di organisasi yang sama yang dapat mengakses halaman profil pengguna",
"Modify rule": "Mengubah aturan",
"New Organization": "Organisasi baru",
"Soft deletion": "Penghapusan lunak",
"Soft deletion - Tooltip": "Ketika diaktifkan, menghapus pengguna tidak akan sepenuhnya menghapus mereka dari database. Sebaliknya, mereka akan ditandai sebagai dihapus",
"Tags": "Tag-tag",
"Tags - Tooltip": "Kumpulan tag yang tersedia bagi pengguna untuk dipilih",
"View rule": "Aturan tampilan",
"Visible": "Terlihat",
"Website URL": "URL situs web",
"Website URL - Tooltip": "URL halaman utama organisasi. Bidang ini tidak digunakan di Casdoor"
},
"payment": {
"Confirm your invoice information": "Konfirmasikan informasi tagihan Anda",
"Currency": "Mata uang",
"Currency - Tooltip": "Seperti USD, CNY, dll.",
"Download Invoice": "Unduh Faktur",
"Edit Payment": "Edit Pembayaran",
"Individual": "Individu",
"Invoice URL": "URL Faktur",
"Invoice URL - Tooltip": "URL untuk mengunduh faktur",
"Invoice actions": "Tindakan faktur",
"Invoice actions - Tooltip": "Operasi mencakup penerbitan faktur dan unduhan faktur",
"Invoice remark": "Catatan Faktur",
"Invoice remark - Tooltip": "Komentar tidak boleh melebihi 50 karakter",
"Invoice tax ID": "Tagihan ID Pajak",
"Invoice tax ID - Tooltip": "Ketika jenis faktur untuk organisasi, nomor identifikasi pajak organisasi harus diisi; ketika jenis faktur untuk individu, tidak perlu mengisi informasi ini",
"Invoice title": "Judul faktur",
"Invoice title - Tooltip": "Ketika tipe faktur berupa organisasi, judul faktur dapat diisi dengan nama organisasi; ketika tipe faktur berupa individu, sistem akan secara otomatis mengisi nama pembayar",
"Invoice type": "Jenis faktur",
"Invoice type - Tooltip": "Jenis faktur dapat untuk individu atau organisasi",
"Issue Invoice": "Masalah Faktur",
"Message": "Pesan",
"Message - Tooltip": "Pesan hasil pemrosesan pembayaran",
"New Payment": "Pembayaran Baru",
"Person Email": "Email orang",
"Person Email - Tooltip": "Email dari pembayar",
"Person ID card": "Kartu identitas personal",
"Person ID card - Tooltip": "Nomor kartu identitas pembayar",
"Person name": "Nama orang",
"Person name - Tooltip": "Nama asli dari pembayar",
"Person phone": "Telepon pribadi",
"Person phone - Tooltip": "Nomor telepon pembayar",
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Tolong periksa dengan seksama informasi faktur anda. Setelah faktur dikeluarkan, tidak dapat ditarik atau diubah.",
"Please click the below button to return to the original website": "Silakan klik tombol di bawah ini untuk kembali ke situs web asli",
"Please pay the order first!": "Silakan bayar pesanan terlebih dahulu!",
"Processing...": "Pemrosesan...",
"Product": "Produk",
"Product - Tooltip": "Nama Produk",
"Result": "Hasil",
"Return to Website": "Kembali ke Situs Web",
"The payment has failed": "Pembayaran gagal",
"The payment is still under processing": "Pembayaran masih dalam proses",
"Type - Tooltip": "Metode pembayaran yang digunakan saat membeli produk",
"You have successfully completed the payment": "Anda telah berhasil menyelesaikan pembayaran",
"please wait for a few seconds...": "Mohon tunggu beberapa detik...",
"the current state is": "keadaan saat ini adalah"
},
"permission": {
"Actions": "Tindakan",
"Actions - Tooltip": "Aksi yang diizinkan",
"Admin": "Admin",
"Allow": "Mengizinkan",
"Approve time": "Menyetujui waktu",
"Approve time - Tooltip": "Waktu persetujuan untuk izin ini",
"Approved": "Disetujui",
"Approver": "Pengesah",
"Approver - Tooltip": "Orang yang menyetujui izin",
"Deny": "Menyangkal",
"Edit Permission": "Izin Edit",
"Effect": "Efek",
"Effect - Tooltip": "Mengizinkan atau menolak",
"New Permission": "Izin baru",
"Pending": "Tertunda",
"Read": "Membaca",
"Resource type": "Jenis sumber daya",
"Resource type - Tooltip": "Jenis sumber daya",
"Resources - Tooltip": "Sumber daya yang sah",
"Submitter": "Pengirim",
"Submitter - Tooltip": "Orang yang mengajukan izin ini",
"TreeNode": "PohonNode",
"Write": "Menulis"
},
"product": {
"Alipay": "Alipay",
"Buy": "Beli",
"Buy Product": "Beli Produk",
"CNY": "CNY",
"Detail": "Rincian",
"Detail - Tooltip": "Detail produk",
"Edit Product": "Edit Produk",
"I have completed the payment": "Saya telah menyelesaikan pembayaran",
"Image": "Gambar",
"Image - Tooltip": "Gambar produk",
"New Product": "Produk Baru",
"Pay": "Bayar",
"PayPal": "Paypal",
"Payment providers": "Penyedia pembayaran",
"Payment providers - Tooltip": "Penyedia layanan pembayaran",
"Placing order...": "Menempatkan pesanan...",
"Please provide your username in the remark": "Tolong berikan nama pengguna Anda dalam komentar",
"Please scan the QR code to pay": "Silakan pemindaian kode QR untuk pembayaran",
"Price": "Harga",
"Price - Tooltip": "Harga produk",
"Quantity": "Kuantitas",
"Quantity - Tooltip": "Kuantitas produk",
"Return URL": "URL Kembali",
"Return URL - Tooltip": "URL untuk kembali setelah pembelian sukses",
"SKU": "SKU",
"Sold": "Terjual",
"Sold - Tooltip": "Jumlah terjual",
"Tag - Tooltip": "Tag produk",
"Test buy page..": "Halaman pembelian uji coba.",
"There is no payment channel for this product.": "Tidak ada saluran pembayaran untuk produk ini.",
"This product is currently not in sale.": "Produk ini saat ini tidak dijual.",
"USD": "USD",
"WeChat Pay": "WeChat Pay"
},
"provider": {
"Access key": "Kunci akses",
"Access key - Tooltip": "Kunci akses",
"Agent ID": "ID agen",
"Agent ID - Tooltip": "ID Agen",
"App ID": "ID Aplikasi",
"App ID - Tooltip": "ID Aplikasi",
"App key": "Kunci aplikasi",
"App key - Tooltip": "Kunci aplikasi",
"App secret": "Rahasia aplikasi",
"AppSecret - Tooltip": "Rahasia aplikasi",
"Auth URL": "URL Otorisasi",
"Auth URL - Tooltip": "URL terautentikasi",
"Bucket": "Ember",
"Bucket - Tooltip": "Nama ember",
"Can not parse metadata": "Tidak dapat mengurai metadata",
"Can signin": "Bisa masuk",
"Can signup": "Bisa mendaftar",
"Can unlink": "Bisa melepaskan tautan",
"Category": "Kategori",
"Category - Tooltip": "Pilih kategori",
"Channel No.": "Saluran nomor.",
"Channel No. - Tooltip": "Saluran No.",
"Client ID": "ID klien",
"Client ID - Tooltip": "ID klien",
"Client ID 2": "ID klien 2",
"Client ID 2 - Tooltip": "ID Pelanggan kedua",
"Client secret": "Rahasia klien",
"Client secret - Tooltip": "Rahasia klien",
"Client secret 2": "Rahasia klien 2",
"Client secret 2 - Tooltip": "Kunci rahasia klien kedua",
"Copy": "Salin",
"Disable SSL": "Menonaktifkan SSL",
"Disable SSL - Tooltip": "Apakah perlu menonaktifkan protokol SSL saat berkomunikasi dengan server STMP?",
"Domain": "Domain",
"Domain - Tooltip": "Domain kustom untuk penyimpanan objek",
"Edit Provider": "Ubah Penyedia Layanan",
"Email content": "Konten Email",
"Email content - Tooltip": "Isi Email",
"Email sent successfully": "Email berhasil terkirim",
"Email title": "Judul Email",
"Email title - Tooltip": "Judul email",
"Enable QR code": "Aktifkan kode QR",
"Enable QR code - Tooltip": "Apakah diizinkan untuk memindai kode QR untuk masuk?",
"Endpoint": "Titik akhir",
"Endpoint (Intranet)": "Titik Akhir (Intranet)",
"Host": "Tuan rumah",
"Host - Tooltip": "Nama tuan rumah",
"IdP": "IdP",
"IdP certificate": "Sertifikat IdP",
"Intelligent Validation": "Intelligent Validation",
"Internal": "Internal",
"Issuer URL": "URL penerbit",
"Issuer URL - Tooltip": "URL Penerbit",
"Link copied to clipboard successfully": "Tautan berhasil disalin ke papan klip",
"Metadata": "Metadata: data yang menjelaskan atau memberikan informasi tentang data atau informasi digital lainnya, seperti informasi mengenai sumber data, format, waktu pembuatan, penulis, dan informasi lainnya yang dapat membantu dalam pengelolaan dan pemrosesan data",
"Metadata - Tooltip": "Metadata SAML",
"Method - Tooltip": "Metode login, kode QR atau login tanpa suara",
"New Provider": "Penyedia Baru",
"Normal": "Normal",
"Parse": "Parse: Memecah atau mengurai data atau teks menjadi bagian-bagian yang lebih kecil dan lebih mudah dipahami atau dimanipulasi",
"Parse metadata successfully": "Berhasil mem-parse metadata",
"Path prefix": "Awalan jalur",
"Path prefix - Tooltip": "Awalan path ember untuk penyimpanan objek dalam bucket",
"Please use WeChat and scan the QR code to sign in": "Silakan gunakan WeChat dan pindai kode QR untuk masuk",
"Port": "Pelabuhan",
"Port - Tooltip": "Pastikan port terbuka",
"Prompted": "Mendorong",
"Provider URL": "URL penyedia",
"Provider URL - Tooltip": "URL untuk melakukan konfigurasi service provider, kolom ini hanya digunakan sebagai referensi dan tidak digunakan dalam Casdoor",
"Region ID": "Daerah ID",
"Region ID - Tooltip": "Daerah ID untuk penyedia layanan",
"Region endpoint for Internet": "Titik akhir wilayah untuk Internet",
"Region endpoint for Intranet": "Titik akhir wilayah untuk Intranet",
"Required": "Dibutuhkan",
"SAML 2.0 Endpoint (HTTP)": "Titik akhir SAML 2.0 (HTTP)",
"SMS Test": "Pengujian SMS",
"SMS Test - Tooltip": "Nomor telepon untuk mengirim SMS uji",
"SMS account": "akun SMS",
"SMS account - Tooltip": "Akun SMS",
"SMS sent successfully": "SMS berhasil terkirim",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "Identitas Entitas SP",
"Scene": "Scena",
"Scene - Tooltip": "Latar belakang",
"Scope": "Lingkup",
"Scope - Tooltip": "Lingkup",
"Secret access key": "Kunci akses rahasia",
"Secret access key - Tooltip": "Kunci akses rahasia",
"Secret key": "Kunci rahasia",
"Secret key - Tooltip": "Digunakan oleh server untuk memanggil API penyedia kode verifikasi untuk melakukan verifikasi",
"Send Testing Email": "Kirim Email Uji Coba",
"Send Testing SMS": "Kirim SMS Uji Coba",
"Sign Name": "Tanda Tangan",
"Sign Name - Tooltip": "Nama tanda tangan yang akan digunakan",
"Sign request": "Permintaan tanda tangan",
"Sign request - Tooltip": "Apakah permintaan ini memerlukan tanda tangan?",
"Signin HTML": "Login HTML",
"Signin HTML - Edit": "Masuk HTML - Edit",
"Signin HTML - Tooltip": "HTML kustom untuk mengganti gaya halaman sign-in default",
"Signup HTML": "Pendaftaran HTML",
"Signup HTML - Edit": "Pendaftaran HTML - Sunting",
"Signup HTML - Tooltip": "HTML khusus untuk mengganti gaya halaman pendaftaran bawaan",
"Silent": "Silent",
"Site key": "Kunci situs",
"Site key - Tooltip": "Kunci situs atau kunci halaman web",
"Sliding Validation": "Sliding Validation",
"Sub type": "Sub jenis",
"Sub type - Tooltip": "Sub jenis",
"Template code": "Kode template",
"Template code - Tooltip": "Kode template",
"Test Email": "Email Uji Coba",
"Test Email - Tooltip": "Alamat email untuk menerima email percobaan",
"Test SMTP Connection": "Tes Koneksi SMTP",
"Third-party": "Third-party",
"Token URL": "Token URL: Tautan Token",
"Token URL - Tooltip": "Token URL: URL Token",
"Type": "Jenis",
"Type - Tooltip": "Pilih tipe",
"UserInfo URL": "URL UserInfo",
"UserInfo URL - Tooltip": "URL Informasi Pengguna",
"admin (Shared)": "Admin (Berbagi)"
},
"record": {
"Is triggered": "Ditimbulkan"
},
"resource": {
"Copy Link": "Salin Tautan",
"File name": "Nama file",
"File size": "Ukuran file",
"Format": "Format",
"Parent": "Orangtua",
"Upload a file...": "Unggah sebuah file..."
},
"role": {
"Edit Role": "Mengedit Peran",
"New Role": "Peran Baru",
"Sub domains": "Sub domain-sub domain",
"Sub domains - Tooltip": "Domain yang termasuk dalam peran saat ini",
"Sub roles": "Peran tambahan",
"Sub roles - Tooltip": "Terjemahkan ke bahasa Indonesia: Peran yang termasuk dalam peran saat ini",
"Sub users": "Pengguna sub",
"Sub users - Tooltip": "Pengguna yang termasuk dalam peran saat ini"
},
"signup": {
"Accept": "Menerima",
"Agreement": "Kesepakatan",
"Confirm": "Konfirmasi",
"Decline": "Menurun",
"Have account?": "Punya akun?",
"Please accept the agreement!": "Tolong terima perjanjian ini!",
"Please click the below button to sign in": "Silakan klik tombol di bawah ini untuk masuk",
"Please confirm your password!": "Tolong konfirmasi kata sandi Anda!",
"Please input the correct ID card number!": "Mohon masukkan nomor kartu identitas yang benar!",
"Please input your Email!": "Silahkan masukkan Email kamu!",
"Please input your ID card number!": "Mohon masukkan nomor kartu identitas Anda!",
"Please input your address!": "Silakan masukkan alamat Anda!",
"Please input your affiliation!": "Silakan masukkan afiliasi Anda!",
"Please input your display name!": "Silakan masukkan nama tampilan Anda!",
"Please input your first name!": "Silahkan masukkan nama depan Anda!",
"Please input your last name!": "Silahkan masukkan nama belakang Anda!",
"Please input your phone number!": "Silakan masukkan nomor telepon Anda!",
"Please input your real name!": "Silakan masukkan nama asli Anda!",
"Please select your country code!": "Tolong pilih kode negara Anda!",
"Please select your country/region!": "Silakan pilih negara/region Anda!",
"Terms of Use": "Syarat Penggunaan",
"Terms of Use - Tooltip": "Syarat penggunaan yang harus dibaca dan disetujui oleh pengguna selama proses registrasi",
"The input is not invoice Tax ID!": "Input ini bukan Tax ID faktur!",
"The input is not invoice title!": "Masukan bukan judul faktur!",
"The input is not valid Email!": "Input yang dimasukkan bukan sesuai dengan format Email yang valid!",
"The input is not valid Phone!": "Masukan ponsel tidak valid!",
"Username": "Nama pengguna",
"Username - Tooltip": "Username - Tooltip",
"Your account has been created!": "Akun Anda telah dibuat!",
"Your confirmed password is inconsistent with the password!": "Kata sandi yang dikonfirmasi tidak konsisten dengan kata sandi!",
"sign in now": "Masuk sekarang"
},
"syncer": {
"Affiliation table": "Tabel afiliasi",
"Affiliation table - Tooltip": "Nama tabel database dari unit kerja",
"Avatar base URL": "Avatar base URL: Alamat URL dasar Avatar",
"Avatar base URL - Tooltip": "Awalan URL untuk gambar avatar",
"Casdoor column": "Kolom Casdoor",
"Column name": "Nama kolom",
"Column type": "Tipe kolom",
"Database": "Database",
"Database - Tooltip": "Nama basis data asli",
"Database type": "Tipe Basis Data",
"Database type - Tooltip": "Jenis database, mendukung semua database yang didukung oleh XORM, seperti MySQL, PostgreSQL, SQL Server, Oracle, SQLite, dan lain-lain.",
"Edit Syncer": "Pengedit Sinkronisasi",
"Error text": "Teks kesalahan",
"Error text - Tooltip": "Teks kesalahan",
"Is hashed": "Apakah di-hash?",
"New Syncer": "Sinkronisasi Baru",
"Sync interval": "Interval sinkronisasi",
"Sync interval - Tooltip": "Satuan dalam detik",
"Table": "Tabel",
"Table - Tooltip": "Nama tabel database",
"Table columns": "Kolom tabel",
"Table columns - Tooltip": "Kolom pada tabel yang terlibat dalam sinkronisasi data. Kolom yang tidak terlibat dalam sinkronisasi tidak perlu ditambahkan",
"Table primary key": "Kunci utama tabel",
"Table primary key - Tooltip": "Kunci primer tabel, seperti id"
},
"system": {
"About Casdoor": "Tentang Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "Platform Identitas dan Akses Manajemen (IAM) / Single-Sign-On (SSO) dengan antarmuka web yang mendukung OAuth 2.0, OIDC, SAML, dan CAS",
"CPU Usage": "Penggunaan CPU",
"Community": "Komunitas",
"Failed to get CPU usage": "Gagal mendapatkan penggunaan CPU",
"Failed to get memory usage": "Gagal mendapatkan penggunaan memori",
"Memory Usage": "Penggunaan Memori",
"Official website": "Situs web resmi",
"Unknown version": "Versi tidak diketahui",
"Version": "Versi"
},
"theme": {
"Blossom": "Bunga mekar",
"Border radius": "Radius batas",
"Compact": "Kompak",
"Customize theme": "Menyesuaikan tema",
"Dark": "Gelap",
"Default": "Default",
"Document": "Dokumen",
"Is compact": "Apakah kompak",
"Primary color": "Warna primer",
"Theme": "Tema",
"Theme - Tooltip": "Tema gaya dari aplikasi"
},
"token": {
"Access token": "Token akses",
"Authorization code": "Kode otorisasi",
"Edit Token": "Mengedit Token",
"Expires in": "Berakhir pada",
"New Token": "Token baru",
"Token type": "Jenis token"
},
"user": {
"3rd-party logins": "Masuk pihak ketiga",
"3rd-party logins - Tooltip": "Masuk sosial yang terhubung oleh pengguna",
"Address": "Alamat",
"Address - Tooltip": "Alamat tempat tinggal",
"Affiliation": "Afiliasi",
"Affiliation - Tooltip": "Pemberi Kerja, seperti nama perusahaan atau nama organisasi",
"Bio": "Bio: Biografi",
"Bio - Tooltip": "Pengenalan diri dari pengguna",
"Captcha Verify Failed": "Gagal memverifikasi Captcha",
"Captcha Verify Success": "Captcha Verifikasi Berhasil",
"Country code": "Kode negara",
"Country/Region": "Negara/daerah",
"Country/Region - Tooltip": "Negara atau wilayah",
"Edit User": "Edit Pengguna",
"Email cannot be empty": "Email tidak boleh kosong",
"Email/phone reset successfully": "Email/telepon berhasil diatur ulang",
"Empty input!": "Masukan kosong!",
"Homepage": "Homepage",
"Homepage - Tooltip": "URL halaman depan pengguna",
"ID card": "Kartu identitas",
"Input your email": "Masukkan alamat email Anda",
"Input your phone number": "Masukkan nomor telepon Anda",
"Is admin": "Apakah admin?",
"Is admin - Tooltip": "Apakah seorang administrator dari organisasi yang pengguna menjadi bagian dari?",
"Is deleted": "Terhapus",
"Is deleted - Tooltip": "Pengguna yang dihapus secara lembut hanya mempertahankan catatan basis data dan tidak dapat melakukan operasi apa pun",
"Is forbidden": "Dilarang",
"Is forbidden - Tooltip": "User yang dilarang tidak dapat masuk lagi",
"Is global admin": "Apakah global admin",
"Is global admin - Tooltip": "Adalah seorang administrator Casdoor",
"Keys": "Kunci",
"Link": "Tautan",
"Location": "Lokasi",
"Location - Tooltip": "Kota tempat tinggal",
"Managed accounts": "Akun yang dikelola",
"Modify password...": "Mengubah kata sandi...",
"New Email": "Email baru",
"New Password": "Kata Sandi Baru",
"New User": "Pengguna Baru",
"New phone": "Telepon baru",
"Old Password": "Kata sandi lama",
"Password set successfully": "Kata sandi berhasil diatur",
"Phone cannot be empty": "Telepon tidak boleh kosong",
"Please select avatar from resources": "Silakan pilih avatar dari sumber daya",
"Properties": "Properti",
"Properties - Tooltip": "Properti dari pengguna",
"Re-enter New": "Masukkan kembali baru",
"Reset Email...": "Atur Ulang Email...",
"Reset Phone...": "Atur Ulang Telepon...",
"Select a photo...": "Pilih foto...",
"Set Password": "Atur Kata Sandi",
"Set new profile picture": "Mengatur gambar profil baru",
"Set password...": "Tetapkan kata sandi...",
"Tag": "tanda",
"Tag - Tooltip": "Tag pengguna",
"Title": "Judul",
"Title - Tooltip": "Posisi dalam afiliasi",
"Two passwords you typed do not match.": "Dua password yang Anda ketikkan tidak cocok.",
"Unlink": "Membatalkan Tautan",
"Upload (.xlsx)": "Unggah (.xlsx)",
"Upload a photo": "Unggah foto",
"Values": "Nilai-nilai",
"Verification code sent": "Kode verifikasi telah dikirim",
"WebAuthn credentials": "Kredensial WebAuthn",
"input password": "masukkan kata sandi"
},
"webhook": {
"Content type": "Jenis konten",
"Content type - Tooltip": "Tipe konten",
"Edit Webhook": "Mengedit Webhook",
"Events": "Acara-acara",
"Events - Tooltip": "Acara-acara",
"Headers": "Headers",
"Headers - Tooltip": "Header HTTP (pasangan kunci-nilai)",
"Is user extended": "Apakah pengguna diperpanjang?",
"Is user extended - Tooltip": "Apakah akan menyertakan bidang-bidang tambahan pengguna dalam JSON?",
"Method - Tooltip": "Metode HTTP",
"New Webhook": "Webhook Baru",
"Value": "Nilai"
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More