forked from casdoor/casdoor
Compare commits
4 Commits
copilot/ad
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5d3738e8f | ||
|
|
7e99bafe19 | ||
|
|
10f6719dfc | ||
|
|
fedb37c7f6 |
@@ -59,7 +59,6 @@ p, *, *, GET, /api/get-qrcode, *, *
|
||||
p, *, *, GET, /api/get-webhook-event, *, *
|
||||
p, *, *, GET, /api/get-captcha-status, *, *
|
||||
p, *, *, *, /api/login/oauth, *, *
|
||||
p, *, *, POST, /api/oauth/register, *, *
|
||||
p, *, *, GET, /api/get-application, *, *
|
||||
p, *, *, GET, /api/get-organization-applications, *, *
|
||||
p, *, *, GET, /api/get-user, *, *
|
||||
|
||||
@@ -30,8 +30,6 @@ ldapsServerPort = 636
|
||||
radiusServerPort = 1812
|
||||
radiusDefaultOrganization = "built-in"
|
||||
radiusSecret = "secret"
|
||||
proxyHttpPort =
|
||||
proxyHttpsPort =
|
||||
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
||||
logConfig = {"adapter":"file", "filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
|
||||
initDataNewOnly = false
|
||||
|
||||
@@ -323,7 +323,7 @@ func (c *ApiController) Signup() {
|
||||
|
||||
// If OAuth parameters are present, generate OAuth code and return it
|
||||
if clientId != "" && responseType == ResponseTypeCode {
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "password", responseType, redirectUri, scope, state, nonce, codeChallenge, "", c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "password", responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
@@ -688,51 +688,6 @@ func (c *ApiController) GetCaptcha() {
|
||||
applicationId := c.Ctx.Input.Query("applicationId")
|
||||
isCurrentProvider := c.Ctx.Input.Query("isCurrentProvider")
|
||||
|
||||
// When isCurrentProvider == "true", the frontend passes a provider ID instead of an application ID.
|
||||
// In that case, skip application lookup and rule evaluation, and just return the provider config.
|
||||
shouldSkipCaptcha := false
|
||||
|
||||
if isCurrentProvider != "true" {
|
||||
application, err := object.GetApplication(applicationId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), applicationId))
|
||||
return
|
||||
}
|
||||
|
||||
// Check the CAPTCHA rule to determine if CAPTCHA should be shown
|
||||
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
|
||||
|
||||
// For Internet-Only rule, we can determine on the backend if CAPTCHA should be shown
|
||||
// For other rules (Dynamic, Always), we need to return the CAPTCHA config
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider == nil || providerItem.Provider.Category != "Captcha" {
|
||||
continue
|
||||
}
|
||||
|
||||
// For "None" rule, skip CAPTCHA
|
||||
if providerItem.Rule == "None" || providerItem.Rule == "" {
|
||||
shouldSkipCaptcha = true
|
||||
} else if providerItem.Rule == "Internet-Only" {
|
||||
// For Internet-Only rule, check if the client is from intranet
|
||||
if !util.IsInternetIp(clientIp) {
|
||||
// Client is from intranet, skip CAPTCHA
|
||||
shouldSkipCaptcha = true
|
||||
}
|
||||
}
|
||||
|
||||
break // Only check the first CAPTCHA provider
|
||||
}
|
||||
|
||||
if shouldSkipCaptcha {
|
||||
c.ResponseOk(Captcha{Type: "none"})
|
||||
return
|
||||
}
|
||||
}
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
||||
@@ -161,13 +161,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
nonce := c.Ctx.Input.Query("nonce")
|
||||
challengeMethod := c.Ctx.Input.Query("code_challenge_method")
|
||||
codeChallenge := c.Ctx.Input.Query("code_challenge")
|
||||
resource := c.Ctx.Input.Query("resource")
|
||||
|
||||
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
|
||||
c.ResponseError(c.T("auth:Challenge method should be S256"))
|
||||
return
|
||||
}
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, form.SigninMethod, responseType, redirectUri, scope, state, nonce, codeChallenge, resource, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, form.SigninMethod, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// DynamicClientRegister
|
||||
// @Title DynamicClientRegister
|
||||
// @Tag OAuth API
|
||||
// @Description Register a new OAuth 2.0 client dynamically (RFC 7591)
|
||||
// @Param organization query string false "The organization name (defaults to built-in)"
|
||||
// @Param body body object.DynamicClientRegistrationRequest true "Client registration request"
|
||||
// @Success 201 {object} object.DynamicClientRegistrationResponse
|
||||
// @Failure 400 {object} object.DcrError
|
||||
// @router /api/oauth/register [post]
|
||||
func (c *ApiController) DynamicClientRegister() {
|
||||
var req object.DynamicClientRegistrationRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
|
||||
if err != nil {
|
||||
c.Ctx.Output.Status = http.StatusBadRequest
|
||||
c.Data["json"] = object.DcrError{
|
||||
Error: "invalid_client_metadata",
|
||||
ErrorDescription: "invalid request body: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// Get organization from query parameter or default to built-in
|
||||
organization := c.Ctx.Input.Query("organization")
|
||||
if organization == "" {
|
||||
organization = "built-in"
|
||||
}
|
||||
|
||||
// Register the client
|
||||
response, dcrErr, err := object.RegisterDynamicClient(&req, organization)
|
||||
if err != nil {
|
||||
c.Ctx.Output.Status = http.StatusInternalServerError
|
||||
c.Data["json"] = object.DcrError{
|
||||
Error: "server_error",
|
||||
ErrorDescription: err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
if dcrErr != nil {
|
||||
c.Ctx.Output.Status = http.StatusBadRequest
|
||||
c.Data["json"] = dcrErr
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// Return 201 Created
|
||||
c.Ctx.Output.Status = http.StatusCreated
|
||||
c.Data["json"] = response
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -137,29 +137,3 @@ func (c *RootController) GetWebFingerByApplication() {
|
||||
c.Ctx.Output.ContentType("application/jrd+json")
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOAuthServerMetadata
|
||||
// @Title GetOAuthServerMetadata
|
||||
// @Tag OAuth API
|
||||
// @Description Get OAuth 2.0 Authorization Server Metadata (RFC 8414)
|
||||
// @Success 200 {object} object.OidcDiscovery
|
||||
// @router /.well-known/oauth-authorization-server [get]
|
||||
func (c *RootController) GetOAuthServerMetadata() {
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOidcDiscovery(host, "")
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOAuthServerMetadataByApplication
|
||||
// @Title GetOAuthServerMetadataByApplication
|
||||
// @Tag OAuth API
|
||||
// @Description Get OAuth 2.0 Authorization Server Metadata for specific application (RFC 8414)
|
||||
// @Param application path string true "application name"
|
||||
// @Success 200 {object} object.OidcDiscovery
|
||||
// @router /.well-known/:application/oauth-authorization-server [get]
|
||||
func (c *RootController) GetOAuthServerMetadataByApplication() {
|
||||
application := c.Ctx.Input.Param(":application")
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOidcDiscovery(host, application)
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -176,7 +176,6 @@ func (c *ApiController) GetOAuthToken() {
|
||||
subjectToken := c.Ctx.Input.Query("subject_token")
|
||||
subjectTokenType := c.Ctx.Input.Query("subject_token_type")
|
||||
audience := c.Ctx.Input.Query("audience")
|
||||
resource := c.Ctx.Input.Query("resource")
|
||||
|
||||
if clientId == "" && clientSecret == "" {
|
||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||
@@ -232,9 +231,6 @@ func (c *ApiController) GetOAuthToken() {
|
||||
if audience == "" {
|
||||
audience = tokenRequest.Audience
|
||||
}
|
||||
if resource == "" {
|
||||
resource = tokenRequest.Resource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +275,7 @@ func (c *ApiController) GetOAuthToken() {
|
||||
}
|
||||
|
||||
host := c.Ctx.Request.Host
|
||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, nonce, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage(), subjectToken, subjectTokenType, audience, resource)
|
||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, nonce, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage(), subjectToken, subjectTokenType, audience)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -30,5 +30,4 @@ type TokenRequest struct {
|
||||
SubjectToken string `json:"subject_token"`
|
||||
SubjectTokenType string `json:"subject_token_type"`
|
||||
Audience string `json:"audience"`
|
||||
Resource string `json:"resource"` // RFC 8707 Resource Indicator
|
||||
}
|
||||
|
||||
@@ -151,14 +151,39 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(vform.ApplicationId)
|
||||
provider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), vform.ApplicationId))
|
||||
if provider != nil {
|
||||
if vform.CaptchaType != provider.Type {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Type != "Default" {
|
||||
vform.ClientSecret = provider.ClientSecret
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, provider.ClientId, vform.ClientSecret, provider.ClientId2); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(vform.ApplicationId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -189,7 +214,6 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
// Try to resolve user for CAPTCHA rule checking
|
||||
// checkUser != "", means method is ForgetVerification
|
||||
if vform.CheckUser != "" {
|
||||
owner := application.Organization
|
||||
@@ -207,86 +231,18 @@ func (c *ApiController) SendVerificationCode() {
|
||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||
return
|
||||
}
|
||||
} else if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
|
||||
// mfaUserSession != "", means method is MfaAuthVerification
|
||||
}
|
||||
|
||||
// mfaUserSession != "", means method is MfaAuthVerification
|
||||
if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
|
||||
user, err = object.GetUser(mfaUserSession)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if vform.Method == ResetVerification {
|
||||
// For reset verification, get the current logged-in user
|
||||
user = c.getCurrentUser()
|
||||
} else if vform.Method == LoginVerification {
|
||||
// For login verification, try to find user by email/phone for CAPTCHA check
|
||||
// This is a preliminary lookup; the actual validation happens later in the switch statement
|
||||
if vform.Type == object.VerifyTypeEmail && util.IsEmailValid(vform.Dest) {
|
||||
user, err = object.GetUserByEmail(organization.Name, vform.Dest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if vform.Type == object.VerifyTypePhone {
|
||||
// Prefer resolving the user directly by phone, consistent with the later login switch,
|
||||
// so that Dynamic CAPTCHA is not skipped due to missing/invalid country code.
|
||||
user, err = object.GetUserByPhone(organization.Name, vform.Dest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine username for CAPTCHA check
|
||||
username := ""
|
||||
if user != nil {
|
||||
username = user.Name
|
||||
} else if vform.CheckUser != "" {
|
||||
username = vform.CheckUser
|
||||
}
|
||||
|
||||
// Check if CAPTCHA should be enabled based on the rule (Dynamic/Always/Internet-Only)
|
||||
enableCaptcha, err := object.CheckToEnableCaptcha(application, organization.Name, username, clientIp)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Only verify CAPTCHA if it should be enabled
|
||||
if enableCaptcha {
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider != nil {
|
||||
if vform.CaptchaType != captchaProvider.Type {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider.Type != "Default" {
|
||||
vform.ClientSecret = captchaProvider.ClientSecret
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaService := captcha.GetCaptchaProvider(vform.CaptchaType); captchaService == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaService.VerifyCaptcha(vform.CaptchaToken, captchaProvider.ClientId, vform.ClientSecret, captchaProvider.ClientId2); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendResp := errors.New("invalid dest type")
|
||||
var provider *object.Provider
|
||||
|
||||
switch vform.Type {
|
||||
case object.VerifyTypeEmail:
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// GetOauthProtectedResourceMetadata
|
||||
// @Title GetOauthProtectedResourceMetadata
|
||||
// @Tag OAuth 2.0 API
|
||||
// @Description Get OAuth 2.0 Protected Resource Metadata (RFC 9728)
|
||||
// @Success 200 {object} object.OauthProtectedResourceMetadata
|
||||
// @router /.well-known/oauth-protected-resource [get]
|
||||
func (c *RootController) GetOauthProtectedResourceMetadata() {
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOauthProtectedResourceMetadata(host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOauthProtectedResourceMetadataByApplication
|
||||
// @Title GetOauthProtectedResourceMetadataByApplication
|
||||
// @Tag OAuth 2.0 API
|
||||
// @Description Get OAuth 2.0 Protected Resource Metadata for specific application (RFC 9728)
|
||||
// @Param application path string true "application name"
|
||||
// @Success 200 {object} object.OauthProtectedResourceMetadata
|
||||
// @router /.well-known/:application/oauth-protected-resource [get]
|
||||
func (c *RootController) GetOauthProtectedResourceMetadataByApplication() {
|
||||
application := c.Ctx.Input.Param(":application")
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOauthProtectedResourceMetadataByApplication(host, application)
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -18,7 +18,7 @@ type EmailProvider interface {
|
||||
Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error
|
||||
}
|
||||
|
||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, sslMode string, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string, enableProxy bool) EmailProvider {
|
||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string, enableProxy bool) EmailProvider {
|
||||
if typ == "Azure ACS" {
|
||||
return NewAzureACSEmailProvider(clientSecret, host)
|
||||
} else if typ == "Custom HTTP Email" {
|
||||
@@ -26,6 +26,6 @@ func GetEmailProvider(typ string, clientId string, clientSecret string, host str
|
||||
} else if typ == "SendGrid" {
|
||||
return NewSendgridEmailProvider(clientSecret, host, endpoint)
|
||||
} else {
|
||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, sslMode, enableProxy)
|
||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl, enableProxy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,20 +25,13 @@ type SmtpEmailProvider struct {
|
||||
Dialer *gomail.Dialer
|
||||
}
|
||||
|
||||
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, sslMode string, enableProxy bool) *SmtpEmailProvider {
|
||||
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, disableSsl bool, enableProxy bool) *SmtpEmailProvider {
|
||||
dialer := gomail.NewDialer(host, port, userName, password)
|
||||
if typ == "SUBMAIL" {
|
||||
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
// Handle SSL mode: "Auto" (or empty) means don't override gomail's default behavior
|
||||
// "Enable" means force SSL on, "Disable" means force SSL off
|
||||
if sslMode == "Enable" {
|
||||
dialer.SSL = true
|
||||
} else if sslMode == "Disable" {
|
||||
dialer.SSL = false
|
||||
}
|
||||
// If sslMode is "Auto" or empty, don't set dialer.SSL - let gomail decide based on port
|
||||
dialer.SSL = !disableSsl
|
||||
|
||||
if enableProxy {
|
||||
socks5Proxy := conf.GetConfigString("socks5Proxy")
|
||||
|
||||
7
go.mod
7
go.mod
@@ -17,7 +17,6 @@ require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible
|
||||
github.com/aliyun/credentials-go v1.3.10
|
||||
github.com/aws/aws-sdk-go v1.45.5
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0
|
||||
github.com/beego/beego/v2 v2.3.8
|
||||
github.com/beevik/etree v1.1.0
|
||||
@@ -30,6 +29,7 @@ require (
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||
github.com/casvisor/casvisor-go-sdk v1.4.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5
|
||||
@@ -49,7 +49,6 @@ require (
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/microsoft/go-mssqldb v1.9.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nyaruka/phonenumbers v1.2.2
|
||||
github.com/polarsource/polar-go v0.12.0
|
||||
@@ -115,6 +114,7 @@ require (
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||
github.com/apistd/uni-go-sdk v0.0.2 // indirect
|
||||
github.com/atc0005/go-teams-notify/v2 v2.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.45.5 // indirect
|
||||
github.com/aws/smithy-go v1.24.0 // indirect
|
||||
github.com/baidubce/bce-sdk-go v0.9.156 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -161,8 +161,7 @@ require (
|
||||
github.com/go-webauthn/x v0.1.9 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
|
||||
24
go.sum
24
go.sum
@@ -627,16 +627,6 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
@@ -652,8 +642,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@@ -929,6 +917,7 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPc
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE=
|
||||
github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY=
|
||||
@@ -1101,11 +1090,8 @@ github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -1364,8 +1350,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
@@ -1430,8 +1414,6 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4
|
||||
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microsoft/go-mssqldb v1.9.0 h1:5Vq+u2f4LDujJNeZn62Z4kBDEC9MjLv0ukRzOuEuvdA=
|
||||
github.com/microsoft/go-mssqldb v1.9.0/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
@@ -1514,8 +1496,6 @@ github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d h1:1DyyRrgYeNj
|
||||
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d/go.mod h1:ElJiub4lRy6UZDb+0JHDkGEdr6aOli+ykhyej7VCLoI=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20161029093637-248dadf4e906/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "Die Anmeldemethode: Anmeldung per E-Mail ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with face is not enabled for the application": "Die Anmeldemethode: Anmeldung per Gesicht ist für die Anwendung nicht aktiviert",
|
||||
"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 order: %s does not exist": "Die Bestellung: %s existiert nicht",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "Die Organisation: %s existiert nicht",
|
||||
"The organization: %s has disabled users to signin": "Die Organisation: %s hat die Anmeldung von Benutzern deaktiviert",
|
||||
"The plan: %s does not exist": "Der Plan: %s existiert nicht",
|
||||
@@ -48,7 +48,7 @@
|
||||
"CIDR for IP: %s should not be empty": "CIDR für IP: %s darf nicht leer sein",
|
||||
"Default code does not match the code's matching rules": "Standardcode entspricht nicht den Übereinstimmungsregeln des Codes",
|
||||
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
|
||||
"DisplayName is not valid real name": "Der Anzeigename ist kein gültiger echter Name",
|
||||
"DisplayName is not valid real name": "DisplayName ist kein gültiger Vorname",
|
||||
"Email already exists": "E-Mail existiert bereits",
|
||||
"Email cannot be empty": "E-Mail darf nicht leer sein",
|
||||
"Email is invalid": "E-Mail ist ungültig",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Gesichtsdaten stimmen nicht überein",
|
||||
"Failed to parse client IP: %s": "Fehler beim Parsen der Client-IP: %s",
|
||||
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Gastbenutzer müssen ihr Konto aktualisieren, indem sie einen Benutzernamen und ein Passwort festlegen, bevor sie sich direkt anmelden können",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Einladungscode darf nicht leer sein",
|
||||
"Invitation code exhausted": "Einladungscode aufgebraucht",
|
||||
"Invitation code is invalid": "Einladungscode ist ungültig",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Gruppen importieren fehlgeschlagen",
|
||||
"Failed to import users": "Fehler beim Importieren von Benutzern",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Unzureichendes Guthaben: neues Guthaben %v wäre unter dem Kreditlimit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Unzureichendes Guthaben: neues Organisationsguthaben %v wäre unter dem Kreditlimit %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Fehlender Parameter",
|
||||
"Only admin user can specify user": "Nur Administrator kann Benutzer angeben",
|
||||
"Please login first": "Bitte zuerst einloggen",
|
||||
"The LDAP: %s does not exist": "Das LDAP: %s existiert nicht",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "Die Organisation: %s sollte mindestens eine Anwendung haben",
|
||||
"The syncer: %s does not exist": "Der Synchronizer: %s existiert nicht",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
|
||||
"The user: %s is not found": "Der Benutzer: %s wurde nicht gefunden",
|
||||
"User is required for User category transaction": "Benutzer ist für Benutzer-Kategorie-Transaktionen erforderlich",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "Falsche Benutzer-ID",
|
||||
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
|
||||
"this operation is not allowed in demo mode": "Dieser Vorgang ist im Demo-Modus nicht erlaubt",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "Die Berechtigung: \"%s\" existiert nicht"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Produktliste darf nicht leer sein"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "ID-Verifizierungsanbieter konnte nicht initialisiert werden",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Ungültige Anwendungs-ID",
|
||||
"No ID Verification provider configured": "Kein ID-Verifizierungsanbieter konfiguriert",
|
||||
"Provider is not an ID Verification provider": "Anbieter ist kein ID-Verifizierungsanbieter",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "Der Anbieter %s existiert nicht"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Ungültige Telefonempfänger: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "Sitzungs-ID %s ist die aktuelle Sitzung und kann nicht gelöscht werden"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Der Objektschlüssel %s ist nicht erlaubt",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Fehler"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket nicht gefunden"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Anzeigename darf nicht leer sein",
|
||||
"ID card information and real name are required": "Personalausweisinformationen und vollständiger Name sind erforderlich",
|
||||
"Identity verification failed": "Identitätsprüfung fehlgeschlagen",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA-E-Mail ist aktiviert, aber E-Mail ist leer",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-Telefon ist aktiviert, aber Telefonnummer ist leer",
|
||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
|
||||
"No application found for user": "Keine Anwendung für Benutzer gefunden",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Das neue Passwort muss sich von Ihrem aktuellen Passwort unterscheiden",
|
||||
"User is already verified": "Benutzer ist bereits verifiziert",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "Eigentümer und Name des Benutzers dürfen nicht leer sein"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Ungültiger Captcha-Anbieter.",
|
||||
"Phone number is invalid in your region %s": "Die Telefonnummer ist in Ihrer Region %s ungültig",
|
||||
"The forgot password feature is disabled": "Die Funktion \"Passwort vergessen\" ist deaktiviert",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Der Verifizierungscode wurde bereits verwendet!",
|
||||
"The verification code has not been sent yet!": "Der Verifizierungscode wurde noch nicht gesendet!",
|
||||
"Turing test failed.": "Turing-Test fehlgeschlagen.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "El método de inicio de sesión: inicio de sesión con correo electrónico no está habilitado para la aplicación",
|
||||
"The login method: login with face is not enabled for the application": "El método de inicio de sesión: inicio de sesión con reconocimiento facial no está habilitado para la aplicación",
|
||||
"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 order: %s does not exist": "El pedido: %s no existe",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "La organización: %s no existe",
|
||||
"The organization: %s has disabled users to signin": "La organización: %s ha desactivado el inicio de sesión de usuarios",
|
||||
"The plan: %s does not exist": "El plan: %s no existe",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "La etiqueta del usuario: %s no está incluida en las etiquetas de la aplicación",
|
||||
"UserCode Expired": "Código de usuario expirado",
|
||||
"UserCode Invalid": "Código de usuario inválido",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "El usuario de pago %s no tiene una suscripción activa o pendiente y la aplicación %s no tiene precios predeterminados",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "El usuario de pago %s no tiene una suscripción activa o pendiente y la aplicación: %s no tiene precio predeterminado",
|
||||
"the application for user %s is not found": "no se encontró la aplicación para el usuario %s",
|
||||
"the organization: %s is not found": "no se encontró la organización: %s"
|
||||
},
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"check": {
|
||||
"%s does not meet the CIDR format requirements: %s": "%s no cumple con los requisitos del formato CIDR: %s",
|
||||
"Affiliation cannot be blank": "La afiliación no puede estar vacía",
|
||||
"Affiliation cannot be blank": "Afiliación no puede estar en blanco",
|
||||
"CIDR for IP: %s should not be empty": "El CIDR para la IP: %s no debe estar vacío",
|
||||
"Default code does not match the code's matching rules": "El código predeterminado no cumple con las reglas de validación del código",
|
||||
"Default code does not match the code's matching rules": "El código predeterminado no coincide con las reglas de coincidencia de códigos",
|
||||
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
|
||||
"DisplayName is not valid real name": "El nombre de pantalla no es un nombre real válido",
|
||||
"Email already exists": "El correo electrónico ya existe",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Los datos faciales no coinciden",
|
||||
"Failed to parse client IP: %s": "Error al analizar la IP del cliente: %s",
|
||||
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Los usuarios invitados deben actualizar su cuenta configurando un nombre de usuario y una contraseña antes de poder iniciar sesión directamente",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "El código de invitación no puede estar vacío",
|
||||
"Invitation code exhausted": "Código de invitación agotado",
|
||||
"Invitation code is invalid": "Código de invitación inválido",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Error al importar grupos",
|
||||
"Failed to import users": "Error al importar usuarios",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Saldo insuficiente: el nuevo saldo %v estaría por debajo del límite de crédito %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Saldo insuficiente: el nuevo saldo de la organización %v estaría por debajo del límite de crédito %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Parámetro faltante",
|
||||
"Only admin user can specify user": "Solo el usuario administrador puede especificar usuario",
|
||||
"Please login first": "Por favor, inicia sesión primero",
|
||||
"The LDAP: %s does not exist": "El LDAP: %s no existe",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "La organización: %s debe tener al menos una aplicación",
|
||||
"The syncer: %s does not exist": "El sincronizador: %s no existe",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "El usuario: %s no existe",
|
||||
"The user: %s is not found": "El usuario: %s no encontrado",
|
||||
"User is required for User category transaction": "El usuario es obligatorio para la transacción de la categoría Usuario",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "ID de usuario incorrecto",
|
||||
"don't support captchaProvider: ": "No apoyo a captchaProvider",
|
||||
"this operation is not allowed in demo mode": "esta operación no está permitida en modo de demostración",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "El permiso: \"%s\" no existe"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "La lista de productos no puede estar vacía"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Error al inicializar el proveedor de verificación de ID",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Identificación de aplicación no válida",
|
||||
"No ID Verification provider configured": "No hay proveedor de verificación de ID configurado",
|
||||
"Provider is not an ID Verification provider": "El proveedor no es un proveedor de verificación de ID",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "El proveedor: %s no existe"
|
||||
},
|
||||
"resource": {
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Error"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket no encontrado"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
|
||||
"ID card information and real name are required": "Se requiere información de la tarjeta de identificación y el nombre real",
|
||||
"Identity verification failed": "Falló la verificación de identidad",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "El correo electrónico MFA está habilitado pero el correo está vacío",
|
||||
"MFA phone is enabled but phone number is empty": "El teléfono MFA está habilitado pero el número de teléfono está vacío",
|
||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
|
||||
"No application found for user": "No se encontró aplicación para el usuario",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "La nueva contraseña debe ser diferente de su contraseña actual",
|
||||
"User is already verified": "El usuario ya está verificado",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "el propietario y el nombre del usuario no deben estar vacíos"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Proveedor de captcha no válido.",
|
||||
"Phone number is invalid in your region %s": "El número de teléfono es inválido en tu región %s",
|
||||
"The forgot password feature is disabled": "La función de contraseña olvidada está deshabilitada",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "¡El código de verificación ya ha sido utilizado!",
|
||||
"The verification code has not been sent yet!": "¡El código de verificación aún no ha sido enviado!",
|
||||
"Turing test failed.": "El test de Turing falló.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "La méthode de connexion : connexion par e-mail n'est pas activée pour l'application",
|
||||
"The login method: login with face is not enabled for the application": "La méthode de connexion : connexion par visage n'est pas activée pour l'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 order: %s does not exist": "La commande : %s n'existe pas",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "L'organisation : %s n'existe pas",
|
||||
"The organization: %s has disabled users to signin": "L'organisation: %s a désactivé la connexion des utilisateurs",
|
||||
"The plan: %s does not exist": "Le plan : %s n'existe pas",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "Le tag de l'utilisateur : %s n'est pas répertorié dans les tags de l'application",
|
||||
"UserCode Expired": "Code utilisateur expiré",
|
||||
"UserCode Invalid": "Code utilisateur invalide",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "L'utilisateur payant %s n'a pas d'abonnement actif ou en attente et l'application %s n'a pas de tarification par défaut",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "L'utilisateur payant %s n'a pas d'abonnement actif ou en attente et l'application : %s n'a pas de tarification par défaut",
|
||||
"the application for user %s is not found": "L'application pour l'utilisateur %s est introuvable",
|
||||
"the organization: %s is not found": "L'organisation : %s est introuvable"
|
||||
},
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"check": {
|
||||
"%s does not meet the CIDR format requirements: %s": "%s ne respecte pas les exigences du format CIDR : %s",
|
||||
"Affiliation cannot be blank": "L'affiliation ne peut pas être vide",
|
||||
"Affiliation cannot be blank": "Affiliation ne peut pas être vide",
|
||||
"CIDR for IP: %s should not be empty": "Le CIDR pour l'IP : %s ne doit pas être vide",
|
||||
"Default code does not match the code's matching rules": "Le code par défaut ne respecte pas les règles de validation du code",
|
||||
"Default code does not match the code's matching rules": "Le code par défaut ne correspond pas aux règles de correspondance du code",
|
||||
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
|
||||
"DisplayName is not valid real name": "DisplayName n'est pas un nom réel valide",
|
||||
"Email already exists": "E-mail déjà existant",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Données faciales incorrectes",
|
||||
"Failed to parse client IP: %s": "Échec de l'analyse de l'IP client : %s",
|
||||
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Les utilisateurs invités doivent mettre à niveau leur compte en définissant un nom d'utilisateur et un mot de passe avant de pouvoir se connecter directement",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Le code d'invitation ne peut pas être vide",
|
||||
"Invitation code exhausted": "Code d'invitation épuisé",
|
||||
"Invitation code is invalid": "Code d'invitation invalide",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Échec de l'importation des groupes",
|
||||
"Failed to import users": "Échec de l'importation des utilisateurs",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Solde insuffisant : le nouveau solde %v serait inférieur à la limite de crédit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Solde insuffisant : le nouveau solde de l'organisation %v serait inférieur à la limite de crédit %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Paramètre manquant",
|
||||
"Only admin user can specify user": "Seul un administrateur peut désigner un utilisateur",
|
||||
"Please login first": "Veuillez d'abord vous connecter",
|
||||
"The LDAP: %s does not exist": "Le LDAP : %s n'existe pas",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "L'organisation : %s doit avoir au moins une application",
|
||||
"The syncer: %s does not exist": "Le synchroniseur : %s n'existe pas",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
|
||||
"The user: %s is not found": "L'utilisateur : %s est introuvable",
|
||||
"User is required for User category transaction": "L'utilisateur est requis pour la transaction de catégorie Utilisateur",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "ID utilisateur incorrect",
|
||||
"don't support captchaProvider: ": "ne prend pas en charge captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "cette opération n'est pas autorisée en mode démo",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "La permission : \"%s\" n'existe pas"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "La liste des produits ne peut pas être vide"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Échec de l'initialisation du fournisseur de vérification d'identité",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Identifiant d'application invalide",
|
||||
"No ID Verification provider configured": "Aucun fournisseur de vérification d'identité configuré",
|
||||
"Provider is not an ID Verification provider": "Le fournisseur n'est pas un fournisseur de vérification d'identité",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "Le fournisseur : %s n'existe pas"
|
||||
},
|
||||
"resource": {
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Erreur"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket introuvable"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
|
||||
"ID card information and real name are required": "Les informations de la carte d'identité et le nom réel sont requis",
|
||||
"Identity verification failed": "Échec de la vérification d'identité",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "L'authentification MFA par e-mail est activée mais l'e-mail est vide",
|
||||
"MFA phone is enabled but phone number is empty": "L'authentification MFA par téléphone est activée mais le numéro de téléphone est vide",
|
||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
|
||||
"No application found for user": "Aucune application trouvée pour l'utilisateur",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Le nouveau mot de passe doit être différent de votre mot de passe actuel",
|
||||
"User is already verified": "L'utilisateur est déjà vérifié",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "le propriétaire et le nom de l'utilisateur ne doivent pas être vides"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Fournisseur de captcha invalide.",
|
||||
"Phone number is invalid in your region %s": "Le numéro de téléphone n'est pas valide dans votre région %s",
|
||||
"The forgot password feature is disabled": "La fonction de mot de passe oublié est désactivée",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Le code de vérification a déjà été utilisé !",
|
||||
"The verification code has not been sent yet!": "Le code de vérification n'a pas encore été envoyé !",
|
||||
"Turing test failed.": "Le test de Turing a échoué.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "このアプリケーションではメールログインは有効になっていません",
|
||||
"The login method: login with face is not enabled for the application": "このアプリケーションでは顔認証ログインは有効になっていません",
|
||||
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
||||
"The order: %s does not exist": "注文:%s は存在しません",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "組織「%s」は存在しません",
|
||||
"The organization: %s has disabled users to signin": "組織: %s はユーザーのサインインを無効にしました",
|
||||
"The plan: %s does not exist": "プラン: %sは存在しません",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "顔認証データが一致しません",
|
||||
"Failed to parse client IP: %s": "クライアント IP「%s」の解析に失敗しました",
|
||||
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "ゲストユーザーは直接サインインする前に、ユーザー名とパスワードを設定してアカウントをアップグレードする必要があります",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "招待コードは空にできません",
|
||||
"Invitation code exhausted": "招待コードの使用回数が上限に達しました",
|
||||
"Invitation code is invalid": "招待コードが無効です",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "グループのインポートに失敗しました",
|
||||
"Failed to import users": "ユーザーのインポートに失敗しました",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "残高不足:新しい残高 %v がクレジット制限 %v を下回ります",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "残高不足:新しい組織残高 %v がクレジット制限 %v を下回ります",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "不足しているパラメーター",
|
||||
"Only admin user can specify user": "管理者ユーザーのみがユーザーを指定できます",
|
||||
"Please login first": "最初にログインしてください",
|
||||
"The LDAP: %s does not exist": "LDAP:%s は存在しません",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "組織「%s」は少なくとも1つのアプリケーションを持っている必要があります",
|
||||
"The syncer: %s does not exist": "同期装置:%s は存在しません",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "そのユーザー:%sは存在しません",
|
||||
"The user: %s is not found": "ユーザー:%s が見つかりません",
|
||||
"User is required for User category transaction": "ユーザーカテゴリトランザクションにはユーザーが必要です",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "無効なユーザーIDです",
|
||||
"don't support captchaProvider: ": "captchaProviderをサポートしないでください",
|
||||
"this operation is not allowed in demo mode": "この操作はデモモードでは許可されていません",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "権限「%s」は存在しません"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "商品リストは空にできません"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "ID認証プロバイダーの初期化に失敗しました",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "アプリケーションIDが無効です",
|
||||
"No ID Verification provider configured": "ID認証プロバイダーが設定されていません",
|
||||
"Provider is not an ID Verification provider": "プロバイダーはID認証プロバイダーではありません",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "プロバイダー%sは存在しません"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "電話受信者が無効です:%s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "セッションID %s は現在のセッションであり、削除できません"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "オブジェクトキー %s は許可されていません",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "エラー"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "チケットが見つかりません"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "grant_type:%sはこのアプリケーションでサポートされていません",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "表示名は空にできません",
|
||||
"ID card information and real name are required": "身分証明書の情報と実名が必要です",
|
||||
"Identity verification failed": "身元確認に失敗しました",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA メールが有効になっていますが、メールアドレスが空です",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 電話番号が有効になっていますが、電話番号が空です",
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
||||
"No application found for user": "ユーザーのアプリケーションが見つかりません",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "新しいパスワードは現在のパスワードと異なる必要があります",
|
||||
"User is already verified": "ユーザーは既に認証済みです",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "ユーザーのオーナーと名前は空にできません"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "無効なCAPTCHAプロバイダー。",
|
||||
"Phone number is invalid in your region %s": "電話番号はあなたの地域で無効です %s",
|
||||
"The forgot password feature is disabled": "パスワードを忘れた機能は無効になっています",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "この検証コードは既に使用されています!",
|
||||
"The verification code has not been sent yet!": "検証コードはまだ送信されていません!",
|
||||
"Turing test failed.": "チューリングテストは失敗しました。",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "Metoda logowania: logowanie przez email nie jest włączona dla aplikacji",
|
||||
"The login method: login with face is not enabled for the application": "Metoda logowania: logowanie przez twarz nie jest włączona dla aplikacji",
|
||||
"The login method: login with password is not enabled for the application": "Metoda logowania: logowanie przez hasło nie jest włączone dla aplikacji",
|
||||
"The order: %s does not exist": "Zamówienie: %s nie istnieje",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "Organizacja: %s nie istnieje",
|
||||
"The organization: %s has disabled users to signin": "Organizacja: %s wyłączyła logowanie użytkowników",
|
||||
"The plan: %s does not exist": "Plan: %s nie istnieje",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Niezgodność danych twarzy",
|
||||
"Failed to parse client IP: %s": "Nie udało się przeanalizować IP klienta: %s",
|
||||
"FirstName cannot be blank": "Imię nie może być puste",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Użytkownicy-goście muszą uaktualnić swoje konto, ustawiając nazwę użytkownika i hasło, zanim będą mogli się zalogować bezpośrednio",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Kod zaproszenia nie może być pusty",
|
||||
"Invitation code exhausted": "Kod zaproszenia został wykorzystany",
|
||||
"Invitation code is invalid": "Kod zaproszenia jest nieprawidłowy",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Nie udało się zaimportować grup",
|
||||
"Failed to import users": "Nie udało się zaimportować użytkowników",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Niewystarczające saldo: nowe saldo %v byłoby poniżej limitu kredytowego %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Niewystarczające saldo: nowe saldo organizacji %v byłoby poniżej limitu kredytowego %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Brakujący parametr",
|
||||
"Only admin user can specify user": "Tylko administrator może wskazać użytkownika",
|
||||
"Please login first": "Najpierw się zaloguj",
|
||||
"The LDAP: %s does not exist": "LDAP: %s nie istnieje",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "Organizacja: %s powinna mieć co najmniej jedną aplikację",
|
||||
"The syncer: %s does not exist": "Synchronizer: %s nie istnieje",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "Użytkownik: %s nie istnieje",
|
||||
"The user: %s is not found": "Użytkownik: %s nie został znaleziony",
|
||||
"User is required for User category transaction": "Użytkownik jest wymagany do transakcji kategorii użytkownika",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "Nieprawidłowy userId",
|
||||
"don't support captchaProvider: ": "nie obsługuje captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "ta operacja nie jest dozwolona w trybie demo",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "Uprawnienie: \"%s\" nie istnieje"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Lista produktów nie może być pusta"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Nie udało się zainicjować dostawcy weryfikacji ID",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Nieprawidłowe id aplikacji",
|
||||
"No ID Verification provider configured": "Brak skonfigurowanego dostawcy weryfikacji ID",
|
||||
"Provider is not an ID Verification provider": "Dostawca nie jest dostawcą weryfikacji ID",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "dostawca: %s nie istnieje"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Nieprawidłowi odbiorcy telefonu: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "identyfikator sesji %s jest bieżącą sesją i nie może być usunięty"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Klucz obiektu: %s jest niedozwolony",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Błąd"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Nie znaleziono biletu"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s nie jest obsługiwany w tej aplikacji",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Nazwa wyświetlana nie może być pusta",
|
||||
"ID card information and real name are required": "Wymagane są informacje z dowodu osobistego i prawdziwe nazwisko",
|
||||
"Identity verification failed": "Weryfikacja tożsamości nie powiodła się",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA email jest włączone, ale email jest pusty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon jest włączony, ale numer telefonu jest pusty",
|
||||
"New password cannot contain blank space.": "Nowe hasło nie może zawierać spacji.",
|
||||
"No application found for user": "Nie znaleziono aplikacji dla użytkownika",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Nowe hasło musi różnić się od obecnego hasła",
|
||||
"User is already verified": "Użytkownik jest już zweryfikowany",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "właściciel i nazwa użytkownika nie powinny być puste"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Nieprawidłowy dostawca captcha.",
|
||||
"Phone number is invalid in your region %s": "Numer telefonu jest nieprawidłowy w twoim regionie %s",
|
||||
"The forgot password feature is disabled": "Funkcja \"Zapomniałem hasła\" jest wyłączona",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Kod weryfikacyjny został już wykorzystany!",
|
||||
"The verification code has not been sent yet!": "Kod weryfikacyjny nie został jeszcze wysłany!",
|
||||
"Turing test failed.": "Test Turinga nie powiódł się.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "O método de login com e-mail não está habilitado para o aplicativo",
|
||||
"The login method: login with face is not enabled for the application": "O método de login com reconhecimento facial não está habilitado para o aplicativo",
|
||||
"The login method: login with password is not enabled for the application": "O método de login com senha não está habilitado para o aplicativo",
|
||||
"The order: %s does not exist": "O pedido: %s não existe",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "A organização: %s não existe",
|
||||
"The organization: %s has disabled users to signin": "A organização: %s desativou o login de usuários",
|
||||
"The plan: %s does not exist": "O plano: %s não existe",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Dados faciais não correspondem",
|
||||
"Failed to parse client IP: %s": "Falha ao analisar o IP do cliente: %s",
|
||||
"FirstName cannot be blank": "O primeiro nome não pode estar em branco",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Usuários convidados devem atualizar suas contas definindo um nome de usuário e senha antes de poderem entrar diretamente",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "O código de convite não pode estar em branco",
|
||||
"Invitation code exhausted": "O código de convite foi esgotado",
|
||||
"Invitation code is invalid": "Código de convite inválido",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Falha ao importar grupos",
|
||||
"Failed to import users": "Falha ao importar usuários",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Saldo insuficiente: o novo saldo %v estaria abaixo do limite de crédito %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Saldo insuficiente: o novo saldo da organização %v estaria abaixo do limite de crédito %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Parâmetro ausente",
|
||||
"Only admin user can specify user": "Apenas um administrador pode especificar um usuário",
|
||||
"Please login first": "Por favor, faça login primeiro",
|
||||
"The LDAP: %s does not exist": "O LDAP: %s não existe",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "A organização: %s deve ter pelo menos um aplicativo",
|
||||
"The syncer: %s does not exist": "O sincronizador: %s não existe",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "O usuário: %s não existe",
|
||||
"The user: %s is not found": "O usuário: %s não foi encontrado",
|
||||
"User is required for User category transaction": "Usuário é obrigatório para transação de categoria de usuário",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "ID de usuário incorreto",
|
||||
"don't support captchaProvider: ": "captchaProvider não suportado: ",
|
||||
"this operation is not allowed in demo mode": "esta operação não é permitida no modo de demonstração",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "A permissão: \"%s\" não existe"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "A lista de produtos não pode estar vazia"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Falha ao inicializar provedor de verificação de ID",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "ID de aplicativo inválido",
|
||||
"No ID Verification provider configured": "Nenhum provedor de verificação de ID configurado",
|
||||
"Provider is not an ID Verification provider": "Provedor não é um provedor de verificação de ID",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "O provedor: %s não existe"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Destinatários de telefone inválidos: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "ID da sessão %s é a sessão atual e não pode ser excluída"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "A chave de objeto: %s não é permitida",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Erro"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket não encontrado"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s não é suportado neste aplicativo",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "O nome de exibição não pode estar vazio",
|
||||
"ID card information and real name are required": "Informações do documento de identidade e nome verdadeiro são obrigatórios",
|
||||
"Identity verification failed": "Falha na verificação de identidade",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA por e-mail está habilitado, mas o e-mail está vazio",
|
||||
"MFA phone is enabled but phone number is empty": "MFA por telefone está habilitado, mas o número de telefone está vazio",
|
||||
"New password cannot contain blank space.": "A nova senha não pode conter espaços em branco.",
|
||||
"No application found for user": "Nenhum aplicativo encontrado para o usuário",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "A nova senha deve ser diferente da senha atual",
|
||||
"User is already verified": "Usuário já está verificado",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "O proprietário e o nome do usuário não devem estar vazios"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Provedor de captcha inválido.",
|
||||
"Phone number is invalid in your region %s": "Número de telefone inválido na sua região %s",
|
||||
"The forgot password feature is disabled": "A funcionalidade de esqueci a senha está desabilitada",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "O código de verificação já foi utilizado!",
|
||||
"The verification code has not been sent yet!": "O código de verificação ainda não foi enviado!",
|
||||
"Turing test failed.": "O teste de Turing falhou.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "Uygulama için e-posta ile giriş yöntemi etkin değil",
|
||||
"The login method: login with face is not enabled for the application": "Uygulama için yüz ile giriş yöntemi etkin değil",
|
||||
"The login method: login with password is not enabled for the application": "Şifre ile giriş yöntemi bu uygulama için etkin değil",
|
||||
"The order: %s does not exist": "Sipariş: %s mevcut değil",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "Organizasyon: %s mevcut değil",
|
||||
"The organization: %s has disabled users to signin": "Organizasyon: %s kullanıcıların oturum açmasını devre dışı bıraktı",
|
||||
"The plan: %s does not exist": "Plan: %s mevcut değil",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Yüz verisi uyuşmazlığı",
|
||||
"Failed to parse client IP: %s": "İstemci IP'si ayrıştırılamadı: %s",
|
||||
"FirstName cannot be blank": "Ad boş olamaz",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Misafir kullanıcılar doğrudan giriş yapabilmek için kullanıcı adı ve şifre belirleyerek hesaplarını yükseltmelidir",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Davet kodu boş olamaz",
|
||||
"Invitation code exhausted": "Davet kodu kullanım dışı",
|
||||
"Invitation code is invalid": "Davet kodu geçersiz",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Gruplar içe aktarılamadı",
|
||||
"Failed to import users": "Kullanıcılar içe aktarılamadı",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Yetersiz bakiye: yeni bakiye %v kredi limitinin altında olacak %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Yetersiz bakiye: yeni organizasyon bakiyesi %v kredi limitinin altında olacak %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Eksik parametre",
|
||||
"Only admin user can specify user": "Yalnızca yönetici kullanıcı kullanıcı belirleyebilir",
|
||||
"Please login first": "Lütfen önce giriş yapın",
|
||||
"The LDAP: %s does not exist": "LDAP: %s mevcut değil",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "Organizasyon: %s en az bir uygulamaya sahip olmalı",
|
||||
"The syncer: %s does not exist": "Senkronizasyon: %s mevcut değil",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "Kullanıcı: %s bulunamadı",
|
||||
"The user: %s is not found": "Kullanıcı: %s bulunamadı",
|
||||
"User is required for User category transaction": "Kullanıcı kategorisi işlemi için kullanıcı gerekli",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "Yanlış kullanıcı kimliği",
|
||||
"don't support captchaProvider: ": "captchaProvider desteklenmiyor: ",
|
||||
"this operation is not allowed in demo mode": "bu işlem demo modunda izin verilmiyor",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "İzin: \"%s\" mevcut değil"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Ürün listesi boş olamaz"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Kimlik Doğrulama sağlayıcısı başlatılamadı",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Geçersiz uygulama id",
|
||||
"No ID Verification provider configured": "Kimlik Doğrulama sağlayıcısı yapılandırılmamış",
|
||||
"Provider is not an ID Verification provider": "Sağlayıcı bir Kimlik Doğrulama sağlayıcısı değil",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "provider: %s bulunamadı"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Geçersiz telefon alıcıları: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "oturum kimliği %s geçerli oturumdur ve silinemez"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s izin verilmiyor",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Hata"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Bilet bulunamadı"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s bu uygulamada desteklenmiyor",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Görünen ad boş olamaz",
|
||||
"ID card information and real name are required": "Kimlik kartı bilgileri ve gerçek adı gereklidir",
|
||||
"Identity verification failed": "Kimlik doğrulama başarısız",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA e-postası etkin ancak e-posta boş",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefonu etkin ancak telefon numarası boş",
|
||||
"New password cannot contain blank space.": "Yeni şifre boşluk içeremez.",
|
||||
"No application found for user": "Kullanıcı için uygulama bulunamadı",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Yeni şifre mevcut şifrenizden farklı olmalıdır",
|
||||
"User is already verified": "Kullanıcı zaten doğrulanmış",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "kullanıcının sahibi ve adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Geçersiz captcha sağlayıcı.",
|
||||
"Phone number is invalid in your region %s": "Telefon numaranız bölgenizde geçersiz %s",
|
||||
"The forgot password feature is disabled": "Şifremi unuttum özelliği devre dışı",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Doğrulama kodu zaten kullanılmış!",
|
||||
"The verification code has not been sent yet!": "Doğrulama kodu henüz gönderilmedi!",
|
||||
"Turing test failed.": "Turing testi başarısız.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "Метод входу через email не увімкнено для цього додатка",
|
||||
"The login method: login with face is not enabled for the application": "Метод входу через обличчя не увімкнено для цього додатка",
|
||||
"The login method: login with password is not enabled for the application": "Метод входу через пароль не увімкнено для цього додатка",
|
||||
"The order: %s does not exist": "Замовлення: %s не існує",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "Організація: %s не існує",
|
||||
"The organization: %s has disabled users to signin": "Організація: %s вимкнула вхід користувачів",
|
||||
"The plan: %s does not exist": "План: %s не існує",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Невідповідність даних обличчя",
|
||||
"Failed to parse client IP: %s": "Не вдалося розібрати IP клієнта: %s",
|
||||
"FirstName cannot be blank": "Ім’я не може бути порожнім",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Гостьові користувачі повинні оновити свій обліковий запис, встановивши ім'я користувача та пароль, перш ніж вони зможуть увійти безпосередньо",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Код запрошення не може бути порожнім",
|
||||
"Invitation code exhausted": "Код запрошення вичерпано",
|
||||
"Invitation code is invalid": "Код запрошення недійсний",
|
||||
@@ -83,8 +83,8 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Користувачу заборонено вхід, зверніться до адміністратора",
|
||||
"The user: %s doesn't exist in LDAP server": "Користувач: %s не існує на сервері LDAP",
|
||||
"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 value \"%s\" for account field \"%s\" doesn't match the account item regex": "Значення \"%s\" для поля облікового запису \"%s\" не відповідає регулярному виразу облікового запису",
|
||||
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Значення \"%s\" для поля реєстрації \"%s\" не відповідає регулярному виразу поля реєстрації додатка \"%s\"",
|
||||
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Värdet \"%s\" för kontofältet \"%s\" matchar inte kontots regex",
|
||||
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Värdet \"%s\" för registreringsfältet \"%s\" matchar inte registreringsfältets regex för applikationen \"%s\"",
|
||||
"Username already exists": "Ім’я користувача вже існує",
|
||||
"Username cannot be an email address": "Ім’я користувача не може бути email-адресою",
|
||||
"Username cannot contain white spaces": "Ім’я користувача не може містити пробіли",
|
||||
@@ -94,7 +94,7 @@
|
||||
"Username supports email format. Also 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. Also pay attention to the email format.": "Ім’я користувача підтримує формат email. Також може містити лише буквено-цифрові символи, підкреслення або дефіси, не може мати послідовні дефіси або підкреслення та не може починатися або закінчуватися дефісом або підкресленням. Зверніть увагу на формат email.",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Ви ввели неправильний пароль або код забагато разів, зачекайте %d хвилин і спробуйте знову",
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Ваша IP-адреса: %s заблокована відповідно до конфігурації: ",
|
||||
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Ваш пароль застарів. Будь ласка, скиньте пароль, натиснувши \"Забув пароль\"",
|
||||
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Ditt lösenord har gått ut. Återställ det genom att klicka på \"Glömt lösenord\"",
|
||||
"Your region is not allow to signup by phone": "У вашому регіоні реєстрація за телефоном недоступна",
|
||||
"password or code is incorrect": "пароль або код неправильний",
|
||||
"password or code is incorrect, you have %s remaining chances": "пароль або код неправильний, у вас залишилось %s спроб",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Не вдалося імпортувати групи",
|
||||
"Failed to import users": "Не вдалося імпортувати користувачів",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Недостатній баланс: новий баланс %v буде нижче кредитного ліміту %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Недостатній баланс: новий баланс організації %v буде нижче кредитного ліміту %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Відсутній параметр",
|
||||
"Only admin user can specify user": "Лише адміністратор може вказати користувача",
|
||||
"Please login first": "Спочатку увійдіть",
|
||||
"The LDAP: %s does not exist": "LDAP: %s не існує",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "Організація: %s має мати щонайменше один додаток",
|
||||
"The syncer: %s does not exist": "Синхронізатор: %s не існує",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "Користувач: %s не існує",
|
||||
"The user: %s is not found": "Користувач: %s не знайдено",
|
||||
"User is required for User category transaction": "Користувач обов'язковий для транзакції категорії користувача",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "Неправильний userId",
|
||||
"don't support captchaProvider: ": "не підтримується captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "ця операція недоступна в демо-режимі",
|
||||
@@ -143,16 +143,16 @@
|
||||
"adding a new user to the 'built-in' organization is currently disabled. Please note: all users in the 'built-in' organization are global administrators in Casdoor. Refer to the docs: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. If you still wish to create a user for the 'built-in' organization, go to the organization's settings page and enable the 'Has privilege consent' option.": "Додавання нового користувача до організації «built-in» (вбудованої) на даний момент вимкнено. Зауважте: усі користувачі в організації «built-in» є глобальними адміністраторами в Casdoor. Дивіться документацію: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Якщо ви все ще хочете створити користувача для організації «built-in», перейдіть на сторінку налаштувань організації та увімкніть опцію «Має згоду на привілеї»."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "Дозвіл: \"%s\" не існує"
|
||||
"The permission: \"%s\" doesn't exist": "Behörigheten: \"%s\" finns inte"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Список товарів не може бути порожнім"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Не вдалося ініціалізувати провайдера верифікації ID",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Недійсний id додатка",
|
||||
"No ID Verification provider configured": "Провайдер верифікації ID не налаштований",
|
||||
"Provider is not an ID Verification provider": "Провайдер не є провайдером верифікації ID",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "провайдер: %s не існує"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Недійсні отримувачі телефону: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "ідентифікатор сесії %s є поточною сесією і не може бути видалений"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s не дозволено",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Помилка"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Квиток не знайдено"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s не підтримується в цьому додатку",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Відображуване ім’я не може бути порожнім",
|
||||
"ID card information and real name are required": "Інформація про посвідчення особи та справжнє ім'я обов'язкові",
|
||||
"Identity verification failed": "Верифікація особи не вдалася",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA email увімкнено, але email порожній",
|
||||
"MFA phone is enabled but phone number is empty": "MFA телефон увімкнено, але номер телефону порожній",
|
||||
"New password cannot contain blank space.": "Новий пароль не може містити пробіли.",
|
||||
"No application found for user": "Не знайдено додаток для користувача",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Новий пароль повинен відрізнятися від поточного пароля",
|
||||
"User is already verified": "Користувач уже верифікований",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "власник ім’я користувача не повинні бути порожніми"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Недійсний провайдер captcha.",
|
||||
"Phone number is invalid in your region %s": "Номер телефону недійсний у вашому регіоні %s",
|
||||
"The forgot password feature is disabled": "Функція відновлення пароля вимкнена",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Код підтвердження вже використано!",
|
||||
"The verification code has not been sent yet!": "Код підтвердження ще не надіслано!",
|
||||
"Turing test failed.": "Тест Тюрінга не пройдено.",
|
||||
@@ -219,8 +219,8 @@
|
||||
"Unknown type": "Невідомий тип",
|
||||
"Wrong verification code!": "Неправильний код підтвердження!",
|
||||
"You should verify your code in %d min!": "Ви маєте підтвердити код за %d хв!",
|
||||
"please add a SMS provider to the \"Providers\" list for the application: %s": "будь ласка, додайте SMS-провайдера до списку \"Провайдери\" для додатка: %s",
|
||||
"please add an Email provider to the \"Providers\" list for the application: %s": "будь ласка, додайте Email-провайдера до списку \"Провайдери\" для додатка: %s",
|
||||
"please add a SMS provider to the \"Providers\" list for the application: %s": "lägg till en SMS-leverantör i listan \"Leverantörer\" för applikationen: %s",
|
||||
"please add an Email provider to the \"Providers\" list for the application: %s": "lägg till en e-postleverantör i listan \"Leverantörer\" för applikationen: %s",
|
||||
"the user does not exist, please sign up first": "користувача не існує, спочатку зареєструйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "Phương thức đăng nhập bằng email chưa được bật cho ứng dụng",
|
||||
"The login method: login with face is not enabled for the application": "Phương thức đăng nhập bằng khuôn mặt chưa được bật cho ứng dụng",
|
||||
"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 order: %s does not exist": "Đơn hàng: %s không tồn tại",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "Tổ chức: %s không tồn tại",
|
||||
"The organization: %s has disabled users to signin": "Tổ chức: %s đã vô hiệu hóa đăng nhập của người dùng",
|
||||
"The plan: %s does not exist": "Kế hoạch: %s không tồn tại",
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "Dữ liệu khuôn mặt không khớp",
|
||||
"Failed to parse client IP: %s": "Không thể phân tích IP khách: %s",
|
||||
"FirstName cannot be blank": "Tên không được để trống",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Người dùng khách phải nâng cấp tài khoản bằng cách đặt tên người dùng và mật khẩu trước khi có thể đăng nhập trực tiếp",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Mã mời không được để trống",
|
||||
"Invitation code exhausted": "Mã mời đã hết",
|
||||
"Invitation code is invalid": "Mã mời không hợp lệ",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Không thể nhập nhóm",
|
||||
"Failed to import users": "Không thể nhập người dùng",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Số dư không đủ: số dư mới %v sẽ thấp hơn giới hạn tín dụng %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Số dư không đủ: số dư tổ chức mới %v sẽ thấp hơn giới hạn tín dụng %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Thiếu tham số",
|
||||
"Only admin user can specify user": "Chỉ người dùng quản trị mới có thể chỉ định người dùng",
|
||||
"Please login first": "Vui lòng đăng nhập trước",
|
||||
"The LDAP: %s does not exist": "LDAP: %s không tồn tại",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "Tổ chức: %s cần có ít nhất một ứng dụng",
|
||||
"The syncer: %s does not exist": "Bộ đồng bộ: %s không tồn tại",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "Người dùng: %s không tồn tại",
|
||||
"The user: %s is not found": "Người dùng: %s không được tìm thấy",
|
||||
"User is required for User category transaction": "Người dùng được yêu cầu cho giao dịch danh mục Người dùng",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "ID người dùng sai",
|
||||
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "thao tác này không được phép trong chế độ demo",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "Quyền: \"%s\" không tồn tại"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Danh sách sản phẩm không thể trống"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Không thể khởi tạo nhà cung cấp Xác minh ID",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Sai ID ứng dụng",
|
||||
"No ID Verification provider configured": "Không có nhà cung cấp Xác minh ID được cấu hình",
|
||||
"Provider is not an ID Verification provider": "Nhà cung cấp không phải là nhà cung cấp Xác minh ID",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "Người nhận điện thoại không hợp lệ: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "id phiên %s là phiên hiện tại và không thể bị xóa"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Khóa đối tượng: %s không được phép",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "Lỗi"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Không tìm thấy vé"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"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",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Tên hiển thị không thể trống",
|
||||
"ID card information and real name are required": "Thông tin chứng minh nhân dân và tên thật là bắt buộc",
|
||||
"Identity verification failed": "Xác minh danh tính thất bại",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA email đã bật nhưng email trống",
|
||||
"MFA phone is enabled but phone number is empty": "MFA điện thoại đã bật nhưng số điện thoại trống",
|
||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
|
||||
"No application found for user": "Không tìm thấy ứng dụng cho người dùng",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "Mật khẩu mới phải khác với mật khẩu hiện tại của bạn",
|
||||
"User is already verified": "Người dùng đã được xác minh",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "chủ sở hữu và tên người dùng không được để trống"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +210,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Nhà cung cấp captcha không hợp lệ.",
|
||||
"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",
|
||||
"The forgot password feature is disabled": "Tính năng quên mật khẩu đã bị tắt",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "Mã xác thực đã được sử dụng!",
|
||||
"The verification code has not been sent yet!": "Mã xác thực chưa được gửi!",
|
||||
"Turing test failed.": "Kiểm định Turing thất bại.",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
|
||||
"The login method: login with face is not enabled for the application": "该应用禁止采用人脸登录",
|
||||
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||
"The order: %s does not exist": "订单: %s 不存在",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "组织: %s 不存在",
|
||||
"The organization: %s has disabled users to signin": "组织: %s 禁止用户登录",
|
||||
"The plan: %s does not exist": "计划: %s不存在",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
|
||||
"UserCode Expired": "用户代码已过期",
|
||||
"UserCode Invalid": "用户代码无效",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "付费用户 %s 没有激活或待处理的订阅,且应用 %s 没有默认定价",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s 没有激活或正在等待订阅并且应用: %s 没有默认值",
|
||||
"the application for user %s is not found": "未找到用户 %s 的应用程序",
|
||||
"the organization: %s is not found": "组织: %s 不存在"
|
||||
},
|
||||
@@ -57,7 +57,7 @@
|
||||
"Face data mismatch": "人脸不匹配",
|
||||
"Failed to parse client IP: %s": "无法解析客户端 IP 地址: %s",
|
||||
"FirstName cannot be blank": "名不可以为空",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "访客用户必须通过设置用户名和密码来升级账户,然后才能直接登录",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "邀请码不能为空",
|
||||
"Invitation code exhausted": "邀请码使用次数已耗尽",
|
||||
"Invitation code is invalid": "邀请码无效",
|
||||
@@ -106,17 +106,17 @@
|
||||
"general": {
|
||||
"Failed to import groups": "导入群组失败",
|
||||
"Failed to import users": "导入用户失败",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "余额不足:新余额 %v 将低于信用限额 %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "余额不足:新组织余额 %v 将低于信用限额 %v",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "缺少参数",
|
||||
"Only admin user can specify user": "仅管理员用户可以指定用户",
|
||||
"Please login first": "请先登录",
|
||||
"The LDAP: %s does not exist": "LDAP: %s 不存在",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "组织: %s 应该拥有至少一个应用",
|
||||
"The syncer: %s does not exist": "同步器: %s 不存在",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "用户: %s不存在",
|
||||
"The user: %s is not found": "用户: %s 未找到",
|
||||
"User is required for User category transaction": "用户类别交易需要用户",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "错误的 userId",
|
||||
"don't support captchaProvider: ": "不支持验证码提供商: ",
|
||||
"this operation is not allowed in demo mode": "demo模式下不允许该操作",
|
||||
@@ -146,13 +146,13 @@
|
||||
"The permission: \"%s\" doesn't exist": "权限: \"%s\" 不存在"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "产品列表不能为空"
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "初始化身份验证提供商失败",
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "无效的应用ID",
|
||||
"No ID Verification provider configured": "未配置身份验证提供商",
|
||||
"Provider is not an ID Verification provider": "提供商不是身份验证提供商",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "提供商: %s不存在"
|
||||
},
|
||||
"resource": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"Invalid phone receivers: %s": "无效的手机短信收信人: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "会话ID %s 是当前会话,无法删除"
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s被禁止",
|
||||
@@ -181,7 +181,7 @@
|
||||
"Error": "错误"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "工单未找到"
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
|
||||
@@ -192,14 +192,14 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "显示名称不可为空",
|
||||
"ID card information and real name are required": "需要身份证信息和真实姓名",
|
||||
"Identity verification failed": "身份验证失败",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA 电子邮件已启用,但电子邮件为空",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 电话已启用,但电话号码为空",
|
||||
"New password cannot contain blank space.": "新密码不可以包含空格",
|
||||
"No application found for user": "未找到用户的应用程序",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "新密码必须与您当前的密码不同",
|
||||
"User is already verified": "用户已验证",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "用户的组织和名称不能为空"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -86,18 +86,6 @@
|
||||
{"name": "Homepage", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Bio", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Tag", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
|
||||
{"name": "Language", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Gender", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Birthday", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Education", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Balance", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Balance credit", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Balance currency", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Cart", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Transactions", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Score", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Karma", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Ranking", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Signup application", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
|
||||
{"name": "Register type", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
|
||||
{"name": "Register source", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
|
||||
|
||||
2
main.go
2
main.go
@@ -72,7 +72,6 @@ func main() {
|
||||
object.InitFromFile()
|
||||
object.InitCasvisorConfig()
|
||||
object.InitCleanupTokens()
|
||||
object.InitApplicationMap()
|
||||
|
||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||
util.SafeGoroutine(func() { controllers.InitCLIDownloader() })
|
||||
@@ -126,7 +125,6 @@ func main() {
|
||||
go ldap.StartLdapServer()
|
||||
go radius.StartRadiusServer()
|
||||
go object.ClearThroughputPerSecond()
|
||||
go proxy.StartProxyServer()
|
||||
|
||||
web.Run(fmt.Sprintf(":%v", port))
|
||||
}
|
||||
|
||||
56
mcp/auth.go
56
mcp/auth.go
@@ -15,7 +15,6 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -121,58 +120,3 @@ func (c *McpController) GetAcceptLanguage() string {
|
||||
}
|
||||
return language
|
||||
}
|
||||
|
||||
// GetTokenFromRequest extracts the Bearer token from the Authorization header
|
||||
func (c *McpController) GetTokenFromRequest() string {
|
||||
authHeader := c.Ctx.Request.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extract Bearer token
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
// GetClaimsFromToken parses and validates the JWT token and returns the claims
|
||||
// Returns nil if no token is present or if token is invalid
|
||||
func (c *McpController) GetClaimsFromToken() *object.Claims {
|
||||
tokenString := c.GetTokenFromRequest()
|
||||
if tokenString == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to find the application for this token
|
||||
// For MCP, we'll try to parse using the first available application's certificate
|
||||
// In a production scenario, you might want to use a specific MCP application
|
||||
token, err := object.GetTokenByAccessToken(tokenString)
|
||||
if err != nil || token == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(token.Application)
|
||||
if err != nil || application == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
claims, err := object.ParseJwtTokenByApplication(tokenString, application)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return claims
|
||||
}
|
||||
|
||||
// GetScopesFromClaims extracts the scopes from JWT claims and returns them as a slice
|
||||
func GetScopesFromClaims(claims *object.Claims) []string {
|
||||
if claims == nil || claims.Scope == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Scopes are space-separated in OAuth 2.0
|
||||
return strings.Split(claims.Scope, " ")
|
||||
}
|
||||
|
||||
211
mcp/base.go
211
mcp/base.go
@@ -268,160 +268,7 @@ func (c *McpController) handlePing(req McpRequest) {
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsList(req McpRequest) {
|
||||
allTools := c.getAllTools()
|
||||
|
||||
// Get JWT claims from the request
|
||||
claims := c.GetClaimsFromToken()
|
||||
|
||||
// If no token is present, check session authentication
|
||||
if claims == nil {
|
||||
username := c.GetSessionUsername()
|
||||
// If user is authenticated via session, return all tools (backward compatibility)
|
||||
if username != "" {
|
||||
result := McpListToolsResult{
|
||||
Tools: allTools,
|
||||
}
|
||||
c.McpResponseOk(req.ID, result)
|
||||
return
|
||||
}
|
||||
|
||||
// Unauthenticated request - return all tools for discovery
|
||||
// This allows clients to see what tools are available before authenticating
|
||||
result := McpListToolsResult{
|
||||
Tools: allTools,
|
||||
}
|
||||
c.McpResponseOk(req.ID, result)
|
||||
return
|
||||
}
|
||||
|
||||
// Token-based authentication - filter tools by scopes
|
||||
grantedScopes := GetScopesFromClaims(claims)
|
||||
allowedTools := GetToolsForScopes(grantedScopes, BuiltinScopes)
|
||||
|
||||
// Filter tools based on allowed scopes
|
||||
var filteredTools []McpTool
|
||||
for _, tool := range allTools {
|
||||
if allowedTools[tool.Name] {
|
||||
filteredTools = append(filteredTools, tool)
|
||||
}
|
||||
}
|
||||
|
||||
result := McpListToolsResult{
|
||||
Tools: filteredTools,
|
||||
}
|
||||
|
||||
c.McpResponseOk(req.ID, result)
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsCall(req McpRequest) {
|
||||
var params McpCallToolParams
|
||||
err := json.Unmarshal(req.Params, ¶ms)
|
||||
if err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Check scope-tool permission
|
||||
if !c.checkToolPermission(req.ID, params.Name) {
|
||||
return // Error already sent by checkToolPermission
|
||||
}
|
||||
|
||||
// Route to the appropriate tool handler
|
||||
switch params.Name {
|
||||
case "get_applications":
|
||||
var args GetApplicationsArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleGetApplicationsTool(req.ID, args)
|
||||
case "get_application":
|
||||
var args GetApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleGetApplicationTool(req.ID, args)
|
||||
case "add_application":
|
||||
var args AddApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleAddApplicationTool(req.ID, args)
|
||||
case "update_application":
|
||||
var args UpdateApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleUpdateApplicationTool(req.ID, args)
|
||||
case "delete_application":
|
||||
var args DeleteApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleDeleteApplicationTool(req.ID, args)
|
||||
default:
|
||||
c.McpResponseError(req.ID, -32602, "Invalid tool name", fmt.Sprintf("Tool '%s' not found", params.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// checkToolPermission validates that the current token has the required scope for the tool
|
||||
// Returns false and sends an error response if permission is denied
|
||||
func (c *McpController) checkToolPermission(id interface{}, toolName string) bool {
|
||||
// Get JWT claims from the request
|
||||
claims := c.GetClaimsFromToken()
|
||||
|
||||
// If no token is present, check if the user is authenticated via session
|
||||
if claims == nil {
|
||||
username := c.GetSessionUsername()
|
||||
// If user is authenticated via session (e.g., session cookie), allow access
|
||||
// This maintains backward compatibility with existing session-based auth
|
||||
if username != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// No authentication present - deny access
|
||||
c.sendInsufficientScopeError(id, toolName, []string{})
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract scopes from claims
|
||||
grantedScopes := GetScopesFromClaims(claims)
|
||||
|
||||
// Get allowed tools for the granted scopes
|
||||
allowedTools := GetToolsForScopes(grantedScopes, BuiltinScopes)
|
||||
|
||||
// Check if the requested tool is allowed
|
||||
if !allowedTools[toolName] {
|
||||
c.sendInsufficientScopeError(id, toolName, grantedScopes)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// sendInsufficientScopeError sends an error response for insufficient scope
|
||||
func (c *McpController) sendInsufficientScopeError(id interface{}, toolName string, grantedScopes []string) {
|
||||
// Find required scope for this tool
|
||||
requiredScope := GetRequiredScopeForTool(toolName, BuiltinScopes)
|
||||
|
||||
errorData := map[string]interface{}{
|
||||
"tool": toolName,
|
||||
"granted_scopes": grantedScopes,
|
||||
}
|
||||
if requiredScope != "" {
|
||||
errorData["required_scope"] = requiredScope
|
||||
}
|
||||
|
||||
c.McpResponseError(id, -32001, "insufficient_scope", errorData)
|
||||
}
|
||||
|
||||
// getAllTools returns all available MCP tools
|
||||
func (c *McpController) getAllTools() []McpTool {
|
||||
return []McpTool{
|
||||
tools := []McpTool{
|
||||
{
|
||||
Name: "get_applications",
|
||||
Description: "Get all applications for a specific owner",
|
||||
@@ -497,4 +344,60 @@ func (c *McpController) getAllTools() []McpTool {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := McpListToolsResult{
|
||||
Tools: tools,
|
||||
}
|
||||
|
||||
c.McpResponseOk(req.ID, result)
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsCall(req McpRequest) {
|
||||
var params McpCallToolParams
|
||||
err := json.Unmarshal(req.Params, ¶ms)
|
||||
if err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Route to the appropriate tool handler
|
||||
switch params.Name {
|
||||
case "get_applications":
|
||||
var args GetApplicationsArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleGetApplicationsTool(req.ID, args)
|
||||
case "get_application":
|
||||
var args GetApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleGetApplicationTool(req.ID, args)
|
||||
case "add_application":
|
||||
var args AddApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleAddApplicationTool(req.ID, args)
|
||||
case "update_application":
|
||||
var args UpdateApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleUpdateApplicationTool(req.ID, args)
|
||||
case "delete_application":
|
||||
var args DeleteApplicationArgs
|
||||
if err := json.Unmarshal(params.Arguments, &args); err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
c.handleDeleteApplicationTool(req.ID, args)
|
||||
default:
|
||||
c.McpResponseError(req.ID, -32602, "Invalid tool name", fmt.Sprintf("Tool '%s' not found", params.Name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// BuiltinScopes defines the default scope-to-tool mappings for Casdoor's MCP server
|
||||
var BuiltinScopes = []*object.ScopeItem{
|
||||
{
|
||||
Name: "application:read",
|
||||
DisplayName: "Read Applications",
|
||||
Description: "View application list and details",
|
||||
Tools: []string{"get_applications", "get_application"},
|
||||
},
|
||||
{
|
||||
Name: "application:write",
|
||||
DisplayName: "Manage Applications",
|
||||
Description: "Create, update, and delete applications",
|
||||
Tools: []string{"add_application", "update_application", "delete_application"},
|
||||
},
|
||||
{
|
||||
Name: "user:read",
|
||||
DisplayName: "Read Users",
|
||||
Description: "View user list and details",
|
||||
Tools: []string{"get_users", "get_user"},
|
||||
},
|
||||
{
|
||||
Name: "user:write",
|
||||
DisplayName: "Manage Users",
|
||||
Description: "Create, update, and delete users",
|
||||
Tools: []string{"add_user", "update_user", "delete_user"},
|
||||
},
|
||||
{
|
||||
Name: "organization:read",
|
||||
DisplayName: "Read Organizations",
|
||||
Description: "View organization list and details",
|
||||
Tools: []string{"get_organizations", "get_organization"},
|
||||
},
|
||||
{
|
||||
Name: "organization:write",
|
||||
DisplayName: "Manage Organizations",
|
||||
Description: "Create, update, and delete organizations",
|
||||
Tools: []string{"add_organization", "update_organization", "delete_organization"},
|
||||
},
|
||||
{
|
||||
Name: "permission:read",
|
||||
DisplayName: "Read Permissions",
|
||||
Description: "View permission list and details",
|
||||
Tools: []string{"get_permissions", "get_permission"},
|
||||
},
|
||||
{
|
||||
Name: "permission:write",
|
||||
DisplayName: "Manage Permissions",
|
||||
Description: "Create, update, and delete permissions",
|
||||
Tools: []string{"add_permission", "update_permission", "delete_permission"},
|
||||
},
|
||||
{
|
||||
Name: "role:read",
|
||||
DisplayName: "Read Roles",
|
||||
Description: "View role list and details",
|
||||
Tools: []string{"get_roles", "get_role"},
|
||||
},
|
||||
{
|
||||
Name: "role:write",
|
||||
DisplayName: "Manage Roles",
|
||||
Description: "Create, update, and delete roles",
|
||||
Tools: []string{"add_role", "update_role", "delete_role"},
|
||||
},
|
||||
{
|
||||
Name: "provider:read",
|
||||
DisplayName: "Read Providers",
|
||||
Description: "View provider list and details",
|
||||
Tools: []string{"get_providers", "get_provider"},
|
||||
},
|
||||
{
|
||||
Name: "provider:write",
|
||||
DisplayName: "Manage Providers",
|
||||
Description: "Create, update, and delete providers",
|
||||
Tools: []string{"add_provider", "update_provider", "delete_provider"},
|
||||
},
|
||||
{
|
||||
Name: "token:read",
|
||||
DisplayName: "Read Tokens",
|
||||
Description: "View token list and details",
|
||||
Tools: []string{"get_tokens", "get_token"},
|
||||
},
|
||||
{
|
||||
Name: "token:write",
|
||||
DisplayName: "Manage Tokens",
|
||||
Description: "Delete tokens",
|
||||
Tools: []string{"delete_token"},
|
||||
},
|
||||
}
|
||||
|
||||
// ConvenienceScopes defines alias scopes that expand to multiple resource scopes
|
||||
var ConvenienceScopes = map[string][]string{
|
||||
"read": {"application:read", "user:read", "organization:read", "permission:read", "role:read", "provider:read", "token:read"},
|
||||
"write": {"application:write", "user:write", "organization:write", "permission:write", "role:write", "provider:write", "token:write"},
|
||||
"admin": {"application:read", "application:write", "user:read", "user:write", "organization:read", "organization:write", "permission:read", "permission:write", "role:read", "role:write", "provider:read", "provider:write", "token:read", "token:write"},
|
||||
}
|
||||
|
||||
// GetToolsForScopes returns a map of tools allowed by the given scopes
|
||||
// The grantedScopes are the scopes present in the token
|
||||
// The registry contains the scope-to-tool mappings (either BuiltinScopes or Application.Scopes)
|
||||
func GetToolsForScopes(grantedScopes []string, registry []*object.ScopeItem) map[string]bool {
|
||||
allowed := make(map[string]bool)
|
||||
|
||||
// Expand convenience scopes first
|
||||
expandedScopes := make([]string, 0)
|
||||
for _, scopeName := range grantedScopes {
|
||||
if expansion, isConvenience := ConvenienceScopes[scopeName]; isConvenience {
|
||||
expandedScopes = append(expandedScopes, expansion...)
|
||||
} else {
|
||||
expandedScopes = append(expandedScopes, scopeName)
|
||||
}
|
||||
}
|
||||
|
||||
// Map scopes to tools
|
||||
for _, scopeName := range expandedScopes {
|
||||
for _, item := range registry {
|
||||
if item.Name == scopeName {
|
||||
for _, tool := range item.Tools {
|
||||
allowed[tool] = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowed
|
||||
}
|
||||
|
||||
// GetRequiredScopeForTool returns the first scope that provides access to the given tool
|
||||
// Returns an empty string if no scope is found for the tool
|
||||
func GetRequiredScopeForTool(toolName string, registry []*object.ScopeItem) string {
|
||||
for _, scopeItem := range registry {
|
||||
for _, tool := range scopeItem.Tools {
|
||||
if tool == toolName {
|
||||
return scopeItem.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -61,17 +61,9 @@ type SamlItem struct {
|
||||
}
|
||||
|
||||
type JwtItem struct {
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type ScopeItem struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
Tools []string `json:"tools"` // MCP tools allowed by this scope
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
@@ -80,9 +72,6 @@ type Application struct {
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Category string `xorm:"varchar(20)" json:"category"`
|
||||
Type string `xorm:"varchar(20)" json:"type"`
|
||||
Scopes []*ScopeItem `xorm:"mediumtext" json:"scopes"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Favicon string `xorm:"varchar(200)" json:"favicon"`
|
||||
@@ -154,13 +143,6 @@ type Application struct {
|
||||
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
|
||||
CodeResendTimeout int `json:"codeResendTimeout"`
|
||||
|
||||
// Reverse proxy fields
|
||||
Domain string `xorm:"varchar(100)" json:"domain"`
|
||||
OtherDomains []string `xorm:"varchar(1000)" json:"otherDomains"`
|
||||
UpstreamHost string `xorm:"varchar(100)" json:"upstreamHost"`
|
||||
SslMode string `xorm:"varchar(100)" json:"sslMode"`
|
||||
SslCert string `xorm:"varchar(100)" json:"sslCert"`
|
||||
}
|
||||
|
||||
func GetApplicationCount(owner, field, value string) (int64, error) {
|
||||
@@ -173,16 +155,6 @@ func GetOrganizationApplicationCount(owner, organization, field, value string) (
|
||||
return session.Where("organization = ? or is_shared = ? ", organization, true).Count(&Application{})
|
||||
}
|
||||
|
||||
func GetGlobalApplications() ([]*Application, error) {
|
||||
applications := []*Application{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&applications)
|
||||
if err != nil {
|
||||
return applications, err
|
||||
}
|
||||
|
||||
return applications, nil
|
||||
}
|
||||
|
||||
func GetApplications(owner string) ([]*Application, error) {
|
||||
applications := []*Application{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner})
|
||||
@@ -697,21 +669,6 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func checkMultipleCaptchaProviders(application *Application, lang string) error {
|
||||
var captchaProviders []string
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider != nil && providerItem.Provider.Category == "Captcha" {
|
||||
captchaProviders = append(captchaProviders, providerItem.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(captchaProviders) > 1 {
|
||||
return fmt.Errorf(i18n.Translate(lang, "general:Multiple captcha providers are not allowed in the same application: %s"), strings.Join(captchaProviders, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application, isGlobalAdmin bool, lang string) (bool, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
@@ -750,11 +707,6 @@ func UpdateApplication(id string, application *Application, isGlobalAdmin bool,
|
||||
return false, fmt.Errorf("only applications belonging to built-in organization can be shared")
|
||||
}
|
||||
|
||||
err = checkMultipleCaptchaProviders(application, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, providerItem := range application.Providers {
|
||||
providerItem.Provider = nil
|
||||
}
|
||||
@@ -768,12 +720,6 @@ func UpdateApplication(id string, application *Application, isGlobalAdmin bool,
|
||||
return false, err
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
if err := RefreshApplicationCache(); err != nil {
|
||||
fmt.Printf("Failed to refresh application cache after update: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
@@ -825,12 +771,6 @@ func AddApplication(application *Application) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
if err := RefreshApplicationCache(); err != nil {
|
||||
fmt.Printf("Failed to refresh application cache after add: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
@@ -840,12 +780,6 @@ func deleteApplication(application *Application) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
if err := RefreshApplicationCache(); err != nil {
|
||||
fmt.Printf("Failed to refresh application cache after delete: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
var (
|
||||
applicationMap = make(map[string]*Application)
|
||||
applicationMapMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func InitApplicationMap() error {
|
||||
// Set up the application lookup function for the proxy package
|
||||
proxy.SetApplicationLookup(func(domain string) *proxy.Application {
|
||||
app := GetApplicationByDomain(domain)
|
||||
if app == nil {
|
||||
return nil
|
||||
}
|
||||
return &proxy.Application{
|
||||
Owner: app.Owner,
|
||||
Name: app.Name,
|
||||
UpstreamHost: app.UpstreamHost,
|
||||
}
|
||||
})
|
||||
|
||||
return refreshApplicationMap()
|
||||
}
|
||||
|
||||
func refreshApplicationMap() error {
|
||||
applications, err := GetGlobalApplications()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get global applications: %w", err)
|
||||
}
|
||||
|
||||
newApplicationMap := make(map[string]*Application)
|
||||
for _, app := range applications {
|
||||
if app.Domain != "" {
|
||||
newApplicationMap[strings.ToLower(app.Domain)] = app
|
||||
}
|
||||
for _, domain := range app.OtherDomains {
|
||||
if domain != "" {
|
||||
newApplicationMap[strings.ToLower(domain)] = app
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applicationMapMutex.Lock()
|
||||
applicationMap = newApplicationMap
|
||||
applicationMapMutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetApplicationByDomain(domain string) *Application {
|
||||
applicationMapMutex.RLock()
|
||||
defer applicationMapMutex.RUnlock()
|
||||
|
||||
domain = strings.ToLower(domain)
|
||||
if app, ok := applicationMap[domain]; ok {
|
||||
return app
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RefreshApplicationCache() error {
|
||||
return refreshApplicationMap()
|
||||
}
|
||||
@@ -20,8 +20,7 @@ import "github.com/casdoor/casdoor/email"
|
||||
|
||||
// TestSmtpServer Test the SMTP server
|
||||
func TestSmtpServer(provider *Provider) error {
|
||||
sslMode := getSslMode(provider)
|
||||
smtpEmailProvider := email.NewSmtpEmailProvider(provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.Type, sslMode, provider.EnableProxy)
|
||||
smtpEmailProvider := email.NewSmtpEmailProvider(provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.Type, provider.DisableSsl, provider.EnableProxy)
|
||||
sender, err := smtpEmailProvider.Dialer.Dial()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -32,8 +31,7 @@ func TestSmtpServer(provider *Provider) error {
|
||||
}
|
||||
|
||||
func SendEmail(provider *Provider, title string, content string, dest []string, sender string) error {
|
||||
sslMode := getSslMode(provider)
|
||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, sslMode, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl, provider.EnableProxy)
|
||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl, provider.EnableProxy)
|
||||
|
||||
fromAddress := provider.ClientId2
|
||||
if fromAddress == "" {
|
||||
@@ -47,19 +45,3 @@ func SendEmail(provider *Provider, title string, content string, dest []string,
|
||||
|
||||
return emailProvider.Send(fromAddress, fromName, dest, title, content)
|
||||
}
|
||||
|
||||
// getSslMode returns the SSL mode for the provider, with backward compatibility for DisableSsl
|
||||
func getSslMode(provider *Provider) string {
|
||||
// If SslMode is set, use it
|
||||
if provider.SslMode != "" {
|
||||
return provider.SslMode
|
||||
}
|
||||
|
||||
// Backward compatibility: convert DisableSsl to SslMode
|
||||
if provider.DisableSsl {
|
||||
return "Disable"
|
||||
}
|
||||
|
||||
// Default to "Auto" for new configurations or when DisableSsl is false
|
||||
return "Auto"
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -50,12 +49,7 @@ func GetDashboard(owner string) (*map[string][]int64, error) {
|
||||
dashboard[tableName+"Counts"] = make([]int64, 31)
|
||||
tableFullName := tableNamePrefix + tableName
|
||||
go func(ch chan error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ch <- fmt.Errorf("panic in dashboard goroutine: %v", r)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
defer wg.Done()
|
||||
dashboardDateItems := []DashboardDateItem{}
|
||||
var countResult int64
|
||||
|
||||
|
||||
@@ -72,18 +72,6 @@ func getBuiltInAccountItems() []*AccountItem {
|
||||
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Language", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Gender", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Birthday", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Education", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Balance", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Balance credit", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Balance currency", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Cart", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Transactions", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Score", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Karma", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Ranking", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Register type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Register source", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
@@ -132,7 +120,6 @@ func initBuiltInOrganization() bool {
|
||||
IsProfilePublic: false,
|
||||
UseEmailAsUsername: false,
|
||||
EnableTour: true,
|
||||
DcrPolicy: "open",
|
||||
}
|
||||
_, err = AddOrganization(organization)
|
||||
if err != nil {
|
||||
@@ -198,9 +185,6 @@ func initBuiltInApplication() {
|
||||
Name: "app-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Casdoor",
|
||||
Category: "Default",
|
||||
Type: "All",
|
||||
Scopes: []*ScopeItem{},
|
||||
Logo: fmt.Sprintf("%s/img/casdoor-logo_1185x256.png", conf.GetConfigString("staticBaseUrl")),
|
||||
HomepageUrl: "https://casdoor.org",
|
||||
Organization: "built-in",
|
||||
|
||||
@@ -37,9 +37,8 @@ type Ldap struct {
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
CustomAttributes map[string]string `json:"customAttributes"`
|
||||
|
||||
AutoSync int `json:"autoSync"`
|
||||
LastSync string `xorm:"varchar(100)" json:"lastSync"`
|
||||
EnableGroups bool `xorm:"bool" json:"enableGroups"`
|
||||
AutoSync int `json:"autoSync"`
|
||||
LastSync string `xorm:"varchar(100)" json:"lastSync"`
|
||||
}
|
||||
|
||||
func AddLdap(ldap *Ldap) (bool, error) {
|
||||
@@ -153,7 +152,7 @@ func UpdateLdap(ldap *Ldap) (bool, error) {
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
|
||||
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group", "password_type", "allow_self_signed_cert", "custom_attributes", "enable_groups").Update(ldap)
|
||||
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group", "password_type", "allow_self_signed_cert", "custom_attributes").Update(ldap)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -91,28 +91,13 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
||||
return err
|
||||
}
|
||||
|
||||
// fetch all users and groups
|
||||
// fetch all users
|
||||
conn, err := ldap.GetLdapConn()
|
||||
if err != nil {
|
||||
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||
continue
|
||||
}
|
||||
|
||||
// Sync groups first if enabled (so they exist before assigning users)
|
||||
if ldap.EnableGroups {
|
||||
groups, err := conn.GetLdapGroups(ldap)
|
||||
if err != nil {
|
||||
logs.Warning(fmt.Sprintf("autoSync failed to fetch groups for %s, error %s", ldap.Id, err))
|
||||
} else {
|
||||
newGroups, updatedGroups, err := SyncLdapGroups(ldap.Owner, groups, ldap.Id)
|
||||
if err != nil {
|
||||
logs.Warning(fmt.Sprintf("autoSync failed to sync groups for %s, error %s", ldap.Id, err))
|
||||
} else {
|
||||
logs.Info(fmt.Sprintf("ldap group sync success for %s, %d new groups, %d updated groups", ldap.Id, newGroups, updatedGroups))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
users, err := conn.GetLdapUsers(ldap)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
|
||||
@@ -87,19 +87,10 @@ type LdapUser struct {
|
||||
|
||||
GroupId string `json:"groupId"`
|
||||
Address string `json:"address"`
|
||||
MemberOf []string `json:"memberOf"`
|
||||
MemberOf string `json:"memberOf"`
|
||||
Attributes map[string]string `json:"attributes"`
|
||||
}
|
||||
|
||||
type LdapGroup struct {
|
||||
Dn string `json:"dn"`
|
||||
Cn string `json:"cn"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Member []string `json:"member"`
|
||||
ParentDn string `json:"parentDn"`
|
||||
}
|
||||
|
||||
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
||||
var conn *goldap.Conn
|
||||
tlsConfig := tls.Config{
|
||||
@@ -188,7 +179,7 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
SearchAttributes := []string{
|
||||
"uidNumber", "cn", "sn", "gidNumber", "entryUUID", "displayName", "mail", "email",
|
||||
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
|
||||
"c", "co", "memberOf",
|
||||
"c", "co",
|
||||
}
|
||||
if l.IsAD {
|
||||
SearchAttributes = append(SearchAttributes, "sAMAccountName")
|
||||
@@ -256,7 +247,7 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
case "co":
|
||||
user.CountryName = attribute.Values[0]
|
||||
case "memberOf":
|
||||
user.MemberOf = attribute.Values
|
||||
user.MemberOf = attribute.Values[0]
|
||||
default:
|
||||
if propName, ok := ldapServer.CustomAttributes[attribute.Name]; ok {
|
||||
if user.Attributes == nil {
|
||||
@@ -272,135 +263,42 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
return ldapUsers, nil
|
||||
}
|
||||
|
||||
// GetLdapGroups fetches LDAP groups and organizational units
|
||||
func (l *LdapConn) GetLdapGroups(ldapServer *Ldap) ([]LdapGroup, error) {
|
||||
var allGroups []LdapGroup
|
||||
|
||||
// Search for LDAP groups (groupOfNames, groupOfUniqueNames, posixGroup)
|
||||
groupFilters := []string{
|
||||
"(objectClass=groupOfNames)",
|
||||
"(objectClass=groupOfUniqueNames)",
|
||||
"(objectClass=posixGroup)",
|
||||
}
|
||||
|
||||
// Add Active Directory group filter
|
||||
if l.IsAD {
|
||||
groupFilters = append(groupFilters, "(objectClass=group)")
|
||||
}
|
||||
|
||||
// Build combined filter
|
||||
var filterBuilder strings.Builder
|
||||
filterBuilder.WriteString("(|")
|
||||
for _, filter := range groupFilters {
|
||||
filterBuilder.WriteString(filter)
|
||||
}
|
||||
filterBuilder.WriteString(")")
|
||||
|
||||
SearchAttributes := []string{"cn", "name", "description", "member", "uniqueMember", "memberUid"}
|
||||
searchReq := goldap.NewSearchRequest(ldapServer.BaseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
filterBuilder.String(), SearchAttributes, nil)
|
||||
|
||||
searchResult, err := l.Conn.SearchWithPaging(searchReq, 100)
|
||||
if err != nil {
|
||||
// Groups might not exist, which is okay
|
||||
return allGroups, nil
|
||||
}
|
||||
|
||||
for _, entry := range searchResult.Entries {
|
||||
group := LdapGroup{
|
||||
Dn: entry.DN,
|
||||
}
|
||||
|
||||
for _, attribute := range entry.Attributes {
|
||||
switch attribute.Name {
|
||||
case "cn":
|
||||
group.Cn = attribute.Values[0]
|
||||
case "name":
|
||||
group.Name = attribute.Values[0]
|
||||
case "description":
|
||||
if len(attribute.Values) > 0 {
|
||||
group.Description = attribute.Values[0]
|
||||
}
|
||||
case "member", "uniqueMember", "memberUid":
|
||||
group.Member = append(group.Member, attribute.Values...)
|
||||
}
|
||||
}
|
||||
|
||||
// Use cn as name if name is not set
|
||||
if group.Name == "" {
|
||||
group.Name = group.Cn
|
||||
}
|
||||
|
||||
// Parse parent DN from the entry DN
|
||||
group.ParentDn = getParentDn(entry.DN)
|
||||
|
||||
allGroups = append(allGroups, group)
|
||||
}
|
||||
|
||||
// Also fetch organizational units as groups
|
||||
ouFilter := "(objectClass=organizationalUnit)"
|
||||
ouSearchReq := goldap.NewSearchRequest(ldapServer.BaseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
ouFilter, []string{"ou", "description"}, nil)
|
||||
|
||||
ouSearchResult, err := l.Conn.SearchWithPaging(ouSearchReq, 100)
|
||||
if err == nil {
|
||||
for _, entry := range ouSearchResult.Entries {
|
||||
ou := LdapGroup{
|
||||
Dn: entry.DN,
|
||||
}
|
||||
|
||||
for _, attribute := range entry.Attributes {
|
||||
switch attribute.Name {
|
||||
case "ou":
|
||||
ou.Name = attribute.Values[0]
|
||||
ou.Cn = attribute.Values[0]
|
||||
case "description":
|
||||
if len(attribute.Values) > 0 {
|
||||
ou.Description = attribute.Values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parent DN from the entry DN
|
||||
ou.ParentDn = getParentDn(entry.DN)
|
||||
|
||||
allGroups = append(allGroups, ou)
|
||||
}
|
||||
}
|
||||
|
||||
return allGroups, nil
|
||||
}
|
||||
|
||||
// getParentDn extracts the parent DN from a full DN
|
||||
func getParentDn(dn string) string {
|
||||
// Split DN by comma
|
||||
parts := strings.Split(dn, ",")
|
||||
if len(parts) <= 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Remove the first component (the current node) and rejoin
|
||||
return strings.Join(parts[1:], ",")
|
||||
}
|
||||
|
||||
// parseDnToGroupName converts a DN to a group name
|
||||
func parseDnToGroupName(dn string) string {
|
||||
// Extract the CN or OU from the DN
|
||||
parts := strings.Split(dn, ",")
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
firstPart := parts[0]
|
||||
// Extract value after = sign
|
||||
if idx := strings.Index(firstPart, "="); idx != -1 {
|
||||
return firstPart[idx+1:]
|
||||
}
|
||||
|
||||
return firstPart
|
||||
}
|
||||
// FIXME: The Base DN does not necessarily contain the Group
|
||||
//
|
||||
// func (l *ldapConn) GetLdapGroups(baseDn string) (map[string]ldapGroup, error) {
|
||||
// SearchFilter := "(objectClass=posixGroup)"
|
||||
// SearchAttributes := []string{"cn", "gidNumber"}
|
||||
// groupMap := make(map[string]ldapGroup)
|
||||
//
|
||||
// searchReq := goldap.NewSearchRequest(baseDn,
|
||||
// goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
// SearchFilter, SearchAttributes, nil)
|
||||
// searchResult, err := l.Conn.Search(searchReq)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// if len(searchResult.Entries) == 0 {
|
||||
// return nil, errors.New("no result")
|
||||
// }
|
||||
//
|
||||
// for _, entry := range searchResult.Entries {
|
||||
// var ldapGroupItem ldapGroup
|
||||
// for _, attribute := range entry.Attributes {
|
||||
// switch attribute.Name {
|
||||
// case "gidNumber":
|
||||
// ldapGroupItem.GidNumber = attribute.Values[0]
|
||||
// break
|
||||
// case "cn":
|
||||
// ldapGroupItem.Cn = attribute.Values[0]
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// groupMap[ldapGroupItem.GidNumber] = ldapGroupItem
|
||||
// }
|
||||
//
|
||||
// return groupMap, nil
|
||||
// }
|
||||
|
||||
func AutoAdjustLdapUser(users []LdapUser) []LdapUser {
|
||||
res := make([]LdapUser, len(users))
|
||||
@@ -417,7 +315,6 @@ func AutoAdjustLdapUser(users []LdapUser) []LdapUser {
|
||||
Address: util.ReturnAnyNotEmpty(user.Address, user.PostalAddress, user.RegisteredAddress),
|
||||
Country: util.ReturnAnyNotEmpty(user.Country, user.CountryName),
|
||||
CountryName: user.CountryName,
|
||||
MemberOf: user.MemberOf,
|
||||
Attributes: user.Attributes,
|
||||
}
|
||||
}
|
||||
@@ -501,22 +398,8 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
||||
}
|
||||
formatUserPhone(newUser)
|
||||
|
||||
// Assign user to groups based on memberOf attribute
|
||||
userGroups := []string{}
|
||||
if ldap.DefaultGroup != "" {
|
||||
userGroups = append(userGroups, ldap.DefaultGroup)
|
||||
}
|
||||
|
||||
// Extract group names from memberOf DNs
|
||||
for _, memberDn := range syncUser.MemberOf {
|
||||
groupName := dnToGroupName(owner, memberDn)
|
||||
if groupName != "" {
|
||||
userGroups = append(userGroups, groupName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(userGroups) > 0 {
|
||||
newUser.Groups = userGroups
|
||||
newUser.Groups = []string{ldap.DefaultGroup}
|
||||
}
|
||||
|
||||
affected, err := AddUser(newUser, "en")
|
||||
@@ -537,179 +420,6 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
||||
return existUsers, failedUsers, err
|
||||
}
|
||||
|
||||
// SyncLdapGroups syncs LDAP groups/OUs to Casdoor groups with hierarchy
|
||||
func SyncLdapGroups(owner string, ldapGroups []LdapGroup, ldapId string) (newGroups int, updatedGroups int, err error) {
|
||||
if len(ldapGroups) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
// Create a map of DN to group for quick lookup
|
||||
dnToGroup := make(map[string]*LdapGroup)
|
||||
for i := range ldapGroups {
|
||||
dnToGroup[ldapGroups[i].Dn] = &ldapGroups[i]
|
||||
}
|
||||
|
||||
// Get existing groups for this organization
|
||||
existingGroups, err := GetGroups(owner)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
existingGroupMap := make(map[string]*Group)
|
||||
for _, group := range existingGroups {
|
||||
existingGroupMap[group.Name] = group
|
||||
}
|
||||
|
||||
ldap, err := GetLdap(ldapId)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// Process groups in hierarchical order (parents before children)
|
||||
processedGroups := make(map[string]bool)
|
||||
var processGroup func(ldapGroup *LdapGroup) error
|
||||
|
||||
processGroup = func(ldapGroup *LdapGroup) error {
|
||||
if processedGroups[ldapGroup.Dn] {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate group name from DN
|
||||
groupName := dnToGroupName(owner, ldapGroup.Dn)
|
||||
if groupName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Determine parent
|
||||
var parentId string
|
||||
var isTopGroup bool
|
||||
|
||||
if ldapGroup.ParentDn == "" || ldapGroup.ParentDn == ldap.BaseDn {
|
||||
isTopGroup = true
|
||||
parentId = ""
|
||||
} else {
|
||||
// Process parent first
|
||||
if parentGroup, exists := dnToGroup[ldapGroup.ParentDn]; exists {
|
||||
err := processGroup(parentGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentId = dnToGroupName(owner, ldapGroup.ParentDn)
|
||||
} else {
|
||||
isTopGroup = true
|
||||
}
|
||||
}
|
||||
|
||||
// Check if group already exists
|
||||
if existingGroup, exists := existingGroupMap[groupName]; exists {
|
||||
// Update existing group
|
||||
existingGroup.DisplayName = ldapGroup.Name
|
||||
existingGroup.ParentId = parentId
|
||||
existingGroup.IsTopGroup = isTopGroup
|
||||
existingGroup.Type = "ldap-synced"
|
||||
existingGroup.UpdatedTime = util.GetCurrentTime()
|
||||
|
||||
_, err := UpdateGroup(existingGroup.GetId(), existingGroup)
|
||||
if err == nil {
|
||||
updatedGroups++
|
||||
}
|
||||
} else {
|
||||
// Create new group
|
||||
newGroup := &Group{
|
||||
Owner: owner,
|
||||
Name: groupName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
UpdatedTime: util.GetCurrentTime(),
|
||||
DisplayName: ldapGroup.Name,
|
||||
ParentId: parentId,
|
||||
IsTopGroup: isTopGroup,
|
||||
Type: "ldap-synced",
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
_, err := AddGroup(newGroup)
|
||||
if err == nil {
|
||||
newGroups++
|
||||
existingGroupMap[groupName] = newGroup
|
||||
}
|
||||
}
|
||||
|
||||
processedGroups[ldapGroup.Dn] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process all groups
|
||||
for i := range ldapGroups {
|
||||
err := processGroup(&ldapGroups[i])
|
||||
if err != nil {
|
||||
// Log error but continue processing other groups
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return newGroups, updatedGroups, nil
|
||||
}
|
||||
|
||||
// dnToGroupName converts an LDAP DN to a Casdoor group name
|
||||
func dnToGroupName(owner, dn string) string {
|
||||
if dn == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Parse DN to extract meaningful components
|
||||
parts := strings.Split(dn, ",")
|
||||
|
||||
// Build a hierarchical name from DN components (excluding DC parts)
|
||||
var nameComponents []string
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
lowerPart := strings.ToLower(part)
|
||||
|
||||
// Skip DC (domain component) parts
|
||||
if strings.HasPrefix(lowerPart, "dc=") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract value after = sign
|
||||
if idx := strings.Index(part, "="); idx != -1 {
|
||||
value := part[idx+1:]
|
||||
nameComponents = append(nameComponents, value)
|
||||
}
|
||||
}
|
||||
|
||||
if len(nameComponents) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Reverse to get top-down hierarchy
|
||||
for i, j := 0, len(nameComponents)-1; i < j; i, j = i+1, j-1 {
|
||||
nameComponents[i], nameComponents[j] = nameComponents[j], nameComponents[i]
|
||||
}
|
||||
|
||||
// Join with underscore to create a unique group name
|
||||
groupName := strings.Join(nameComponents, "_")
|
||||
|
||||
// Sanitize group name - replace invalid characters with underscores
|
||||
// Keep only alphanumeric characters, underscores, and hyphens
|
||||
var sanitized strings.Builder
|
||||
for _, r := range groupName {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
|
||||
sanitized.WriteRune(r)
|
||||
} else {
|
||||
sanitized.WriteRune('_')
|
||||
}
|
||||
}
|
||||
groupName = sanitized.String()
|
||||
|
||||
// Remove consecutive underscores and trim
|
||||
for strings.Contains(groupName, "__") {
|
||||
groupName = strings.ReplaceAll(groupName, "__", "_")
|
||||
}
|
||||
groupName = strings.Trim(groupName, "_")
|
||||
|
||||
return groupName
|
||||
}
|
||||
|
||||
func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
||||
var existUuids []string
|
||||
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// DynamicClientRegistrationRequest represents an RFC 7591 client registration request
|
||||
type DynamicClientRegistrationRequest struct {
|
||||
ClientName string `json:"client_name,omitempty"`
|
||||
RedirectUris []string `json:"redirect_uris,omitempty"`
|
||||
GrantTypes []string `json:"grant_types,omitempty"`
|
||||
ResponseTypes []string `json:"response_types,omitempty"`
|
||||
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
|
||||
ApplicationType string `json:"application_type,omitempty"`
|
||||
Contacts []string `json:"contacts,omitempty"`
|
||||
LogoUri string `json:"logo_uri,omitempty"`
|
||||
ClientUri string `json:"client_uri,omitempty"`
|
||||
PolicyUri string `json:"policy_uri,omitempty"`
|
||||
TosUri string `json:"tos_uri,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
}
|
||||
|
||||
// DynamicClientRegistrationResponse represents an RFC 7591 client registration response
|
||||
type DynamicClientRegistrationResponse struct {
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret,omitempty"`
|
||||
ClientIdIssuedAt int64 `json:"client_id_issued_at,omitempty"`
|
||||
ClientSecretExpiresAt int64 `json:"client_secret_expires_at,omitempty"`
|
||||
ClientName string `json:"client_name,omitempty"`
|
||||
RedirectUris []string `json:"redirect_uris,omitempty"`
|
||||
GrantTypes []string `json:"grant_types,omitempty"`
|
||||
ResponseTypes []string `json:"response_types,omitempty"`
|
||||
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
|
||||
ApplicationType string `json:"application_type,omitempty"`
|
||||
Contacts []string `json:"contacts,omitempty"`
|
||||
LogoUri string `json:"logo_uri,omitempty"`
|
||||
ClientUri string `json:"client_uri,omitempty"`
|
||||
PolicyUri string `json:"policy_uri,omitempty"`
|
||||
TosUri string `json:"tos_uri,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
RegistrationClientUri string `json:"registration_client_uri,omitempty"`
|
||||
RegistrationAccessToken string `json:"registration_access_token,omitempty"`
|
||||
}
|
||||
|
||||
// DcrError represents an RFC 7591 error response
|
||||
type DcrError struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
// RegisterDynamicClient creates a new application based on DCR request
|
||||
func RegisterDynamicClient(req *DynamicClientRegistrationRequest, organization string) (*DynamicClientRegistrationResponse, *DcrError, error) {
|
||||
// Validate organization exists and has DCR enabled
|
||||
org, err := GetOrganization(util.GetId("admin", organization))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if org == nil {
|
||||
return nil, &DcrError{
|
||||
Error: "invalid_client_metadata",
|
||||
ErrorDescription: "organization not found",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check if DCR is enabled for this organization
|
||||
if org.DcrPolicy == "" || org.DcrPolicy == "disabled" {
|
||||
return nil, &DcrError{
|
||||
Error: "invalid_client_metadata",
|
||||
ErrorDescription: "dynamic client registration is disabled for this organization",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if len(req.RedirectUris) == 0 {
|
||||
return nil, &DcrError{
|
||||
Error: "invalid_redirect_uri",
|
||||
ErrorDescription: "redirect_uris is required and must contain at least one URI",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if req.ClientName == "" {
|
||||
clientIdPrefix := util.GenerateClientId()
|
||||
if len(clientIdPrefix) > 8 {
|
||||
clientIdPrefix = clientIdPrefix[:8]
|
||||
}
|
||||
req.ClientName = fmt.Sprintf("DCR Client %s", clientIdPrefix)
|
||||
}
|
||||
if len(req.GrantTypes) == 0 {
|
||||
req.GrantTypes = []string{"authorization_code"}
|
||||
}
|
||||
if len(req.ResponseTypes) == 0 {
|
||||
req.ResponseTypes = []string{"code"}
|
||||
}
|
||||
if req.TokenEndpointAuthMethod == "" {
|
||||
req.TokenEndpointAuthMethod = "client_secret_basic"
|
||||
}
|
||||
if req.ApplicationType == "" {
|
||||
req.ApplicationType = "web"
|
||||
}
|
||||
|
||||
// Generate unique application name
|
||||
randomName := util.GetRandomName()
|
||||
appName := fmt.Sprintf("dcr_%s", randomName)
|
||||
|
||||
// Create Application object
|
||||
// Note: DCR applications are created under "admin" owner by default
|
||||
// This can be made configurable in future versions
|
||||
clientId := util.GenerateClientId()
|
||||
clientSecret := util.GenerateClientSecret()
|
||||
createdTime := util.GetCurrentTime()
|
||||
|
||||
application := &Application{
|
||||
Owner: "admin",
|
||||
Name: appName,
|
||||
Organization: organization,
|
||||
CreatedTime: createdTime,
|
||||
DisplayName: req.ClientName,
|
||||
Category: "Agent",
|
||||
Type: "MCP",
|
||||
Scopes: []*ScopeItem{},
|
||||
Logo: req.LogoUri,
|
||||
HomepageUrl: req.ClientUri,
|
||||
ClientId: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectUris: req.RedirectUris,
|
||||
GrantTypes: req.GrantTypes,
|
||||
EnablePassword: false,
|
||||
EnableSignUp: false,
|
||||
DisableSignin: false,
|
||||
EnableSigninSession: false,
|
||||
EnableCodeSignin: true,
|
||||
EnableAutoSignin: false,
|
||||
TokenFormat: "JWT",
|
||||
ExpireInHours: 168,
|
||||
RefreshExpireInHours: 168,
|
||||
CookieExpireInHours: 720,
|
||||
FormOffset: 2,
|
||||
Tags: []string{"dcr"},
|
||||
TermsOfUse: req.TosUri,
|
||||
}
|
||||
|
||||
// Add the application
|
||||
affected, err := AddApplication(application)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !affected {
|
||||
return nil, &DcrError{
|
||||
Error: "invalid_client_metadata",
|
||||
ErrorDescription: "failed to create client application",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Build response
|
||||
response := &DynamicClientRegistrationResponse{
|
||||
ClientId: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
ClientIdIssuedAt: time.Now().Unix(),
|
||||
ClientSecretExpiresAt: 0, // Never expires
|
||||
ClientName: req.ClientName,
|
||||
RedirectUris: req.RedirectUris,
|
||||
GrantTypes: req.GrantTypes,
|
||||
ResponseTypes: req.ResponseTypes,
|
||||
TokenEndpointAuthMethod: req.TokenEndpointAuthMethod,
|
||||
ApplicationType: req.ApplicationType,
|
||||
Contacts: req.Contacts,
|
||||
LogoUri: req.LogoUri,
|
||||
ClientUri: req.ClientUri,
|
||||
PolicyUri: req.PolicyUri,
|
||||
TosUri: req.TosUri,
|
||||
Scope: req.Scope,
|
||||
}
|
||||
|
||||
return response, nil, nil
|
||||
}
|
||||
@@ -32,7 +32,6 @@ type OidcDiscovery struct {
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
UserinfoEndpoint string `json:"userinfo_endpoint"`
|
||||
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
|
||||
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
|
||||
JwksUri string `json:"jwks_uri"`
|
||||
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported"`
|
||||
@@ -41,7 +40,6 @@ type OidcDiscovery struct {
|
||||
SubjectTypesSupported []string `json:"subject_types_supported"`
|
||||
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
||||
ScopesSupported []string `json:"scopes_supported"`
|
||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
|
||||
ClaimsSupported []string `json:"claims_supported"`
|
||||
RequestParameterSupported bool `json:"request_parameter_supported"`
|
||||
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
|
||||
@@ -125,23 +123,6 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
|
||||
jwksUri = fmt.Sprintf("%s/.well-known/jwks", originBackend)
|
||||
}
|
||||
|
||||
// Default OIDC scopes
|
||||
scopes := []string{"openid", "email", "profile", "address", "phone", "offline_access"}
|
||||
|
||||
// Merge application-specific custom scopes if application is provided
|
||||
if applicationName != "" {
|
||||
applicationId := util.GetId("admin", applicationName)
|
||||
application, err := GetApplication(applicationId)
|
||||
if err == nil && application != nil && len(application.Scopes) > 0 {
|
||||
for _, scope := range application.Scopes {
|
||||
// Add custom scope names to the scopes list
|
||||
if scope.Name != "" {
|
||||
scopes = append(scopes, scope.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Examples:
|
||||
// https://login.okta.com/.well-known/openid-configuration
|
||||
// https://auth0.auth0.com/.well-known/openid-configuration
|
||||
@@ -153,7 +134,6 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
|
||||
TokenEndpoint: fmt.Sprintf("%s/api/login/oauth/access_token", originBackend),
|
||||
UserinfoEndpoint: fmt.Sprintf("%s/api/userinfo", originBackend),
|
||||
DeviceAuthorizationEndpoint: fmt.Sprintf("%s/api/device-auth", originBackend),
|
||||
RegistrationEndpoint: fmt.Sprintf("%s/api/oauth/register", originBackend),
|
||||
JwksUri: jwksUri,
|
||||
IntrospectionEndpoint: fmt.Sprintf("%s/api/login/oauth/introspect", originBackend),
|
||||
ResponseTypesSupported: []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none"},
|
||||
@@ -161,8 +141,7 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
|
||||
GrantTypesSupported: []string{"authorization_code", "implicit", "password", "client_credentials", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code", "urn:ietf:params:oauth:grant-type:token-exchange"},
|
||||
SubjectTypesSupported: []string{"public"},
|
||||
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"},
|
||||
ScopesSupported: scopes,
|
||||
CodeChallengeMethodsSupported: []string{"S256"},
|
||||
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
|
||||
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
|
||||
RequestParameterSupported: true,
|
||||
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
|
||||
@@ -49,7 +49,6 @@ type Order struct {
|
||||
type ProductInfo struct {
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
CreatedTime string `json:"createdTime,omitempty"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Image string `json:"image,omitempty"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
|
||||
@@ -276,7 +276,7 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
|
||||
OutOrderId: payResp.OrderId,
|
||||
}
|
||||
|
||||
if provider.Type == "Balance" {
|
||||
if provider.Type == "Dummy" || provider.Type == "Balance" {
|
||||
payment.State = pp.PaymentStatePaid
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
|
||||
}
|
||||
|
||||
order.Payment = payment.Name
|
||||
if provider.Type == "Balance" {
|
||||
if provider.Type == "Dummy" || provider.Type == "Balance" {
|
||||
order.State = "Paid"
|
||||
order.Message = "Payment successful"
|
||||
order.UpdateTime = util.GetCurrentTime()
|
||||
@@ -364,7 +364,7 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
|
||||
}
|
||||
|
||||
// Update product stock after order state is persisted (for instant payment methods)
|
||||
if provider.Type == "Balance" {
|
||||
if provider.Type == "Dummy" || provider.Type == "Balance" {
|
||||
err = UpdateProductStock(orderProductInfos)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -92,8 +92,6 @@ type Organization struct {
|
||||
AccountMenu string `xorm:"varchar(20)" json:"accountMenu"`
|
||||
AccountItems []*AccountItem `xorm:"mediumtext" json:"accountItems"`
|
||||
|
||||
DcrPolicy string `xorm:"varchar(100)" json:"dcrPolicy"`
|
||||
|
||||
OrgBalance float64 `json:"orgBalance"`
|
||||
UserBalance float64 `json:"userBalance"`
|
||||
BalanceCredit float64 `json:"balanceCredit"`
|
||||
|
||||
@@ -29,9 +29,9 @@ import (
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
_ "github.com/lib/pq" // db = postgres
|
||||
_ "github.com/microsoft/go-mssqldb" // db = mssql
|
||||
_ "github.com/denisenkom/go-mssqldb" // db = mssql
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
_ "github.com/lib/pq" // db = postgres
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/core"
|
||||
"github.com/xorm-io/xorm/names"
|
||||
|
||||
@@ -303,7 +303,7 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
|
||||
order.Message = "Payment successful"
|
||||
order.UpdateTime = util.GetCurrentTime()
|
||||
} else if payment.State == pp.PaymentStateError {
|
||||
order.State = "Failed"
|
||||
order.State = "PaymentFailed"
|
||||
order.Message = payment.Message
|
||||
order.UpdateTime = util.GetCurrentTime()
|
||||
} else if payment.State == pp.PaymentStateCanceled {
|
||||
|
||||
@@ -53,8 +53,7 @@ type Provider struct {
|
||||
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
DisableSsl bool `json:"disableSsl"` // Deprecated: Use SslMode instead. If the provider type is WeChat, DisableSsl means EnableQRCode, if type is Google, it means sync phone number
|
||||
SslMode string `xorm:"varchar(100)" json:"sslMode"` // "Auto" (empty means Auto), "Enable", "Disable"
|
||||
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode, if type is Google, it means sync phone number
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Content string `xorm:"varchar(2000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
||||
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// AwsIamSyncerProvider implements SyncerProvider for AWS IAM API-based syncers
|
||||
type AwsIamSyncerProvider struct {
|
||||
Syncer *Syncer
|
||||
iamClient *iam.IAM
|
||||
}
|
||||
|
||||
// InitAdapter initializes the AWS IAM syncer
|
||||
func (p *AwsIamSyncerProvider) InitAdapter() error {
|
||||
// syncer.Host should be the AWS region (e.g., "us-east-1")
|
||||
// syncer.User should be the AWS Access Key ID
|
||||
// syncer.Password should be the AWS Secret Access Key
|
||||
|
||||
region := p.Syncer.Host
|
||||
if region == "" {
|
||||
return fmt.Errorf("AWS region (host field) is required for AWS IAM syncer")
|
||||
}
|
||||
|
||||
accessKeyId := p.Syncer.User
|
||||
if accessKeyId == "" {
|
||||
return fmt.Errorf("AWS Access Key ID (user field) is required for AWS IAM syncer")
|
||||
}
|
||||
|
||||
secretAccessKey := p.Syncer.Password
|
||||
if secretAccessKey == "" {
|
||||
return fmt.Errorf("AWS Secret Access Key (password field) is required for AWS IAM syncer")
|
||||
}
|
||||
|
||||
// Create AWS session
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
Region: aws.String(region),
|
||||
Credentials: credentials.NewStaticCredentials(accessKeyId, secretAccessKey, ""),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create AWS session: %w", err)
|
||||
}
|
||||
|
||||
// Create IAM client
|
||||
p.iamClient = iam.New(sess)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOriginalUsers retrieves all users from AWS IAM API
|
||||
func (p *AwsIamSyncerProvider) GetOriginalUsers() ([]*OriginalUser, error) {
|
||||
if p.iamClient == nil {
|
||||
if err := p.InitAdapter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p.getAwsIamUsers()
|
||||
}
|
||||
|
||||
// AddUser adds a new user to AWS IAM (not supported for read-only API)
|
||||
func (p *AwsIamSyncerProvider) AddUser(user *OriginalUser) (bool, error) {
|
||||
// AWS IAM syncer is typically read-only
|
||||
return false, fmt.Errorf("adding users to AWS IAM is not supported")
|
||||
}
|
||||
|
||||
// UpdateUser updates an existing user in AWS IAM (not supported for read-only API)
|
||||
func (p *AwsIamSyncerProvider) UpdateUser(user *OriginalUser) (bool, error) {
|
||||
// AWS IAM syncer is typically read-only
|
||||
return false, fmt.Errorf("updating users in AWS IAM is not supported")
|
||||
}
|
||||
|
||||
// TestConnection tests the AWS IAM API connection
|
||||
func (p *AwsIamSyncerProvider) TestConnection() error {
|
||||
if p.iamClient == nil {
|
||||
if err := p.InitAdapter(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Try to list users with a limit of 1 to test the connection
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
input := &iam.ListUsersInput{
|
||||
MaxItems: aws.Int64(1),
|
||||
}
|
||||
|
||||
_, err := p.iamClient.ListUsersWithContext(ctx, input)
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes any open connections
|
||||
func (p *AwsIamSyncerProvider) Close() error {
|
||||
// AWS IAM client doesn't require explicit cleanup
|
||||
p.iamClient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAwsIamUsers gets all users from AWS IAM API
|
||||
func (p *AwsIamSyncerProvider) getAwsIamUsers() ([]*OriginalUser, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
allUsers := []*iam.User{}
|
||||
var marker *string
|
||||
|
||||
// Paginate through all users
|
||||
for {
|
||||
input := &iam.ListUsersInput{
|
||||
Marker: marker,
|
||||
}
|
||||
|
||||
result, err := p.iamClient.ListUsersWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list IAM users: %w", err)
|
||||
}
|
||||
|
||||
allUsers = append(allUsers, result.Users...)
|
||||
|
||||
if result.IsTruncated == nil || !*result.IsTruncated {
|
||||
break
|
||||
}
|
||||
|
||||
marker = result.Marker
|
||||
}
|
||||
|
||||
// Convert AWS IAM users to Casdoor OriginalUser
|
||||
originalUsers := []*OriginalUser{}
|
||||
for _, iamUser := range allUsers {
|
||||
originalUser, err := p.awsIamUserToOriginalUser(iamUser)
|
||||
if err != nil {
|
||||
// Log error but continue processing other users
|
||||
userName := "unknown"
|
||||
if iamUser.UserName != nil {
|
||||
userName = *iamUser.UserName
|
||||
}
|
||||
fmt.Printf("Warning: Failed to convert IAM user %s: %v\n", userName, err)
|
||||
continue
|
||||
}
|
||||
originalUsers = append(originalUsers, originalUser)
|
||||
}
|
||||
|
||||
return originalUsers, nil
|
||||
}
|
||||
|
||||
// awsIamUserToOriginalUser converts AWS IAM user to Casdoor OriginalUser
|
||||
func (p *AwsIamSyncerProvider) awsIamUserToOriginalUser(iamUser *iam.User) (*OriginalUser, error) {
|
||||
if iamUser == nil {
|
||||
return nil, fmt.Errorf("IAM user is nil")
|
||||
}
|
||||
|
||||
user := &OriginalUser{
|
||||
Address: []string{},
|
||||
Properties: map[string]string{},
|
||||
Groups: []string{},
|
||||
}
|
||||
|
||||
// Set ID from UserId (unique identifier)
|
||||
if iamUser.UserId != nil {
|
||||
user.Id = *iamUser.UserId
|
||||
}
|
||||
|
||||
// Set Name from UserName
|
||||
if iamUser.UserName != nil {
|
||||
user.Name = *iamUser.UserName
|
||||
}
|
||||
|
||||
// Set DisplayName (use UserName if not available separately)
|
||||
if iamUser.UserName != nil {
|
||||
user.DisplayName = *iamUser.UserName
|
||||
}
|
||||
|
||||
// Set CreatedTime from CreateDate
|
||||
if iamUser.CreateDate != nil {
|
||||
user.CreatedTime = iamUser.CreateDate.Format(time.RFC3339)
|
||||
} else {
|
||||
user.CreatedTime = util.GetCurrentTime()
|
||||
}
|
||||
|
||||
// Get user tags which might contain additional information
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
tagsInput := &iam.ListUserTagsInput{
|
||||
UserName: iamUser.UserName,
|
||||
}
|
||||
|
||||
tagsResult, err := p.iamClient.ListUserTagsWithContext(ctx, tagsInput)
|
||||
if err == nil && tagsResult != nil {
|
||||
// Process tags to extract additional user information
|
||||
for _, tag := range tagsResult.Tags {
|
||||
if tag.Key != nil && tag.Value != nil {
|
||||
key := *tag.Key
|
||||
value := *tag.Value
|
||||
|
||||
switch key {
|
||||
case "Email", "email":
|
||||
user.Email = value
|
||||
case "Phone", "phone":
|
||||
user.Phone = value
|
||||
case "DisplayName", "displayName":
|
||||
user.DisplayName = value
|
||||
case "FirstName", "firstName":
|
||||
user.FirstName = value
|
||||
case "LastName", "lastName":
|
||||
user.LastName = value
|
||||
case "Title", "title":
|
||||
user.Title = value
|
||||
case "Department", "department":
|
||||
user.Affiliation = value
|
||||
default:
|
||||
// Store other tags in Properties
|
||||
user.Properties[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AWS IAM users are active by default unless specified in tags
|
||||
// Check if there's a "Status" or "Active" tag
|
||||
if status, ok := user.Properties["Status"]; ok {
|
||||
if status == "Inactive" || status == "Disabled" {
|
||||
user.IsForbidden = true
|
||||
}
|
||||
}
|
||||
if active, ok := user.Properties["Active"]; ok {
|
||||
if active == "false" || active == "False" || active == "0" {
|
||||
user.IsForbidden = true
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetOriginalGroups retrieves all groups from AWS IAM
|
||||
func (p *AwsIamSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
|
||||
if p.iamClient == nil {
|
||||
if err := p.InitAdapter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
||||
allGroups := []*iam.Group{}
|
||||
var marker *string
|
||||
|
||||
// Paginate through all groups
|
||||
for {
|
||||
input := &iam.ListGroupsInput{
|
||||
Marker: marker,
|
||||
}
|
||||
|
||||
result, err := p.iamClient.ListGroupsWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list IAM groups: %w", err)
|
||||
}
|
||||
|
||||
allGroups = append(allGroups, result.Groups...)
|
||||
|
||||
if result.IsTruncated == nil || !*result.IsTruncated {
|
||||
break
|
||||
}
|
||||
|
||||
marker = result.Marker
|
||||
}
|
||||
|
||||
// Convert AWS IAM groups to Casdoor OriginalGroup
|
||||
originalGroups := []*OriginalGroup{}
|
||||
for _, iamGroup := range allGroups {
|
||||
if iamGroup.GroupId != nil && iamGroup.GroupName != nil {
|
||||
group := &OriginalGroup{
|
||||
Id: *iamGroup.GroupId,
|
||||
Name: *iamGroup.GroupName,
|
||||
}
|
||||
|
||||
if iamGroup.GroupName != nil {
|
||||
group.DisplayName = *iamGroup.GroupName
|
||||
}
|
||||
|
||||
originalGroups = append(originalGroups, group)
|
||||
}
|
||||
}
|
||||
|
||||
return originalGroups, nil
|
||||
}
|
||||
|
||||
// GetOriginalUserGroups retrieves the group IDs that a user belongs to
|
||||
func (p *AwsIamSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
|
||||
if p.iamClient == nil {
|
||||
if err := p.InitAdapter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Note: AWS IAM API requires UserName to query groups, but this interface provides UserId.
|
||||
// This is a known limitation. To properly implement this, we would need to:
|
||||
// 1. Maintain a mapping cache from UserId to UserName, or
|
||||
// 2. Modify the interface to accept both UserId and UserName
|
||||
// For now, returning empty groups to maintain interface compatibility.
|
||||
// TODO: Implement user group synchronization by maintaining a UserId->UserName mapping
|
||||
|
||||
return []string{}, nil
|
||||
}
|
||||
@@ -175,18 +175,14 @@ func (p *DingtalkSyncerProvider) getDingtalkAccessToken() (string, error) {
|
||||
return tokenResp.AccessToken, nil
|
||||
}
|
||||
|
||||
// getDingtalkDepartments gets all department IDs from DingTalk API recursively
|
||||
// getDingtalkDepartments gets all department IDs from DingTalk API
|
||||
func (p *DingtalkSyncerProvider) getDingtalkDepartments(accessToken string) ([]int64, error) {
|
||||
return p.getDingtalkDepartmentsRecursive(accessToken, 1)
|
||||
}
|
||||
|
||||
// getDingtalkDepartmentsRecursive recursively fetches all departments starting from parentDeptId
|
||||
func (p *DingtalkSyncerProvider) getDingtalkDepartmentsRecursive(accessToken string, parentDeptId int64) ([]int64, error) {
|
||||
apiUrl := fmt.Sprintf("https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=%s",
|
||||
url.QueryEscape(accessToken))
|
||||
|
||||
// Get root department (dept_id=1)
|
||||
postData := map[string]interface{}{
|
||||
"dept_id": parentDeptId,
|
||||
"dept_id": 1,
|
||||
}
|
||||
|
||||
data, err := p.postJSON(apiUrl, postData)
|
||||
@@ -205,16 +201,9 @@ func (p *DingtalkSyncerProvider) getDingtalkDepartmentsRecursive(accessToken str
|
||||
deptResp.Errcode, deptResp.Errmsg)
|
||||
}
|
||||
|
||||
// Start with the parent department itself
|
||||
deptIds := []int64{parentDeptId}
|
||||
|
||||
// Recursively fetch all child departments
|
||||
deptIds := []int64{1} // Include root department
|
||||
for _, dept := range deptResp.Result {
|
||||
childDeptIds, err := p.getDingtalkDepartmentsRecursive(accessToken, dept.DeptId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deptIds = append(deptIds, childDeptIds...)
|
||||
deptIds = append(deptIds, dept.DeptId)
|
||||
}
|
||||
|
||||
return deptIds, nil
|
||||
@@ -426,7 +415,6 @@ func (p *DingtalkSyncerProvider) dingtalkUserToOriginalUser(dingtalkUser *Dingta
|
||||
Address: []string{},
|
||||
Properties: map[string]string{},
|
||||
Groups: []string{},
|
||||
DingTalk: dingtalkUser.UserId, // Link DingTalk provider account
|
||||
}
|
||||
|
||||
// Add department IDs to Groups field
|
||||
|
||||
@@ -72,8 +72,6 @@ func GetSyncerProvider(syncer *Syncer) SyncerProvider {
|
||||
return &OktaSyncerProvider{Syncer: syncer}
|
||||
case "SCIM":
|
||||
return &SCIMSyncerProvider{Syncer: syncer}
|
||||
case "AWS IAM":
|
||||
return &AwsIamSyncerProvider{Syncer: syncer}
|
||||
case "Keycloak":
|
||||
return &KeycloakSyncerProvider{
|
||||
DatabaseSyncerProvider: DatabaseSyncerProvider{Syncer: syncer},
|
||||
|
||||
@@ -376,7 +376,6 @@ func (p *LarkSyncerProvider) larkUserToOriginalUser(larkUser *LarkUser) *Origina
|
||||
Address: []string{},
|
||||
Properties: map[string]string{},
|
||||
Groups: []string{},
|
||||
Lark: larkUser.UserId, // Link Lark provider account
|
||||
}
|
||||
|
||||
// Set avatar if available
|
||||
|
||||
@@ -70,18 +70,6 @@ func (syncer *Syncer) updateUserForOriginalFields(user *User, key string) (bool,
|
||||
|
||||
columns := syncer.getCasdoorColumns()
|
||||
columns = append(columns, "affiliation", "hash", "pre_hash")
|
||||
|
||||
// Add provider-specific field for API-based syncers to enable login binding
|
||||
// This allows synced users to login via their provider accounts
|
||||
switch syncer.Type {
|
||||
case "WeCom":
|
||||
columns = append(columns, "wecom")
|
||||
case "DingTalk":
|
||||
columns = append(columns, "dingtalk")
|
||||
case "Lark":
|
||||
columns = append(columns, "lark")
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Where(key+" = ? and owner = ?", syncer.getUserValue(&oldUser, key), oldUser.Owner).Cols(columns...).Update(user)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
@@ -275,7 +275,6 @@ func (p *WecomSyncerProvider) wecomUserToOriginalUser(wecomUser *WecomUser) *Ori
|
||||
Address: []string{},
|
||||
Properties: map[string]string{},
|
||||
Groups: []string{},
|
||||
Wecom: wecomUser.UserId, // Link WeCom provider account
|
||||
}
|
||||
|
||||
// Set gender
|
||||
|
||||
@@ -43,7 +43,6 @@ type Token struct {
|
||||
CodeChallenge string `xorm:"varchar(100)" json:"codeChallenge"`
|
||||
CodeIsUsed bool `json:"codeIsUsed"`
|
||||
CodeExpireIn int64 `json:"codeExpireIn"`
|
||||
Resource string `xorm:"varchar(255)" json:"resource"` // RFC 8707 Resource Indicator
|
||||
}
|
||||
|
||||
func GetTokenCount(owner, organization, field, value string) (int64, error) {
|
||||
|
||||
@@ -338,50 +338,6 @@ func getClaimsWithoutThirdIdp(claims Claims) ClaimsWithoutThirdIdp {
|
||||
return res
|
||||
}
|
||||
|
||||
// getUserFieldValue gets the value of a user field by name, handling special cases like Roles and Permissions
|
||||
func getUserFieldValue(user *User, fieldName string) (interface{}, bool) {
|
||||
if user == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Handle special fields that need conversion
|
||||
switch fieldName {
|
||||
case "Roles":
|
||||
return getUserRoleNames(user), true
|
||||
case "Permissions":
|
||||
return getUserPermissionNames(user), true
|
||||
case "permissionNames":
|
||||
permissionNames := []string{}
|
||||
for _, val := range user.Permissions {
|
||||
permissionNames = append(permissionNames, val.Name)
|
||||
}
|
||||
return permissionNames, true
|
||||
}
|
||||
|
||||
// Handle Properties fields (e.g., Properties.my_field)
|
||||
if strings.HasPrefix(fieldName, "Properties.") {
|
||||
parts := strings.Split(fieldName, ".")
|
||||
if len(parts) == 2 {
|
||||
propName := parts[1]
|
||||
if user.Properties != nil {
|
||||
if value, exists := user.Properties[propName]; exists {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Use reflection to get the field value
|
||||
userValue := reflect.ValueOf(user).Elem()
|
||||
userField := userValue.FieldByName(fieldName)
|
||||
if userField.IsValid() {
|
||||
return userField.Interface(), true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func getClaimsCustom(claims Claims, tokenField []string, tokenAttributes []*JwtItem) jwt.MapClaims {
|
||||
res := make(jwt.MapClaims)
|
||||
|
||||
@@ -458,30 +414,16 @@ func getClaimsCustom(claims Claims, tokenField []string, tokenAttributes []*JwtI
|
||||
}
|
||||
|
||||
for _, item := range tokenAttributes {
|
||||
var value interface{}
|
||||
|
||||
// If Category is "Existing Field", get the actual field value from the user
|
||||
if item.Category == "Existing Field" {
|
||||
fieldValue, found := getUserFieldValue(claims.User, item.Value)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
value = fieldValue
|
||||
} else {
|
||||
// Default behavior: use replaceAttributeValue for "Static Value" or empty category
|
||||
valueList := replaceAttributeValue(claims.User, item.Value)
|
||||
if len(valueList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Type == "String" {
|
||||
value = valueList[0]
|
||||
} else {
|
||||
value = valueList
|
||||
}
|
||||
valueList := replaceAttributeValue(claims.User, item.Value)
|
||||
if len(valueList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
res[item.Name] = value
|
||||
if item.Type == "String" {
|
||||
res[item.Name] = valueList[0]
|
||||
} else {
|
||||
res[item.Name] = valueList
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -509,7 +451,7 @@ func refineUser(user *User) *User {
|
||||
return user
|
||||
}
|
||||
|
||||
func generateJwtToken(application *Application, user *User, provider string, signinMethod string, nonce string, scope string, resource string, host string) (string, string, string, error) {
|
||||
func generateJwtToken(application *Application, user *User, provider string, signinMethod string, nonce string, scope string, host string) (string, string, string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(time.Duration(application.ExpireInHours * float64(time.Hour)))
|
||||
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours * float64(time.Hour)))
|
||||
@@ -553,10 +495,7 @@ func generateJwtToken(application *Application, user *User, provider string, sig
|
||||
},
|
||||
}
|
||||
|
||||
// RFC 8707: Use resource as audience when provided
|
||||
if resource != "" {
|
||||
claims.Audience = []string{resource}
|
||||
} else if application.IsShared {
|
||||
if application.IsShared {
|
||||
claims.Audience = []string{application.ClientId + "-org-" + user.Owner}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -93,26 +92,6 @@ type DeviceAuthResponse struct {
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
||||
// validateResourceURI validates that the resource parameter is a valid absolute URI
|
||||
// according to RFC 8707 Section 2
|
||||
func validateResourceURI(resource string) error {
|
||||
if resource == "" {
|
||||
return nil // empty resource is allowed (backward compatibility)
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(resource)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resource must be a valid URI")
|
||||
}
|
||||
|
||||
// RFC 8707: The resource parameter must be an absolute URI
|
||||
if !parsedURL.IsAbs() {
|
||||
return fmt.Errorf("resource must be an absolute URI")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
||||
token, err := GetTokenByAccessToken(accessToken)
|
||||
if err != nil {
|
||||
@@ -159,7 +138,7 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
|
||||
return "", application, nil
|
||||
}
|
||||
|
||||
func GetOAuthCode(userId string, clientId string, provider string, signinMethod string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, resource string, host string, lang string) (*Code, error) {
|
||||
func GetOAuthCode(userId string, clientId string, provider string, signinMethod string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -190,19 +169,11 @@ func GetOAuthCode(userId string, clientId string, provider string, signinMethod
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validate resource parameter (RFC 8707)
|
||||
if err := validateResourceURI(resource); err != nil {
|
||||
return &Code{
|
||||
Message: err.Error(),
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, provider, signinMethod, nonce, scope, resource, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, provider, signinMethod, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -227,7 +198,6 @@ func GetOAuthCode(userId string, clientId string, provider string, signinMethod
|
||||
CodeChallenge: challenge,
|
||||
CodeIsUsed: false,
|
||||
CodeExpireIn: time.Now().Add(time.Minute * 5).Unix(),
|
||||
Resource: resource,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
@@ -240,7 +210,7 @@ func GetOAuthCode(userId string, clientId string, provider string, signinMethod
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, nonce string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string, subjectToken string, subjectTokenType string, audience string, resource string) (interface{}, error) {
|
||||
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, nonce string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string, subjectToken string, subjectTokenType string, audience string) (interface{}, error) {
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -266,7 +236,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
||||
var tokenError *TokenError
|
||||
switch grantType {
|
||||
case "authorization_code": // Authorization Code Grant
|
||||
token, tokenError, err = GetAuthorizationCodeToken(application, clientSecret, code, verifier, resource)
|
||||
token, tokenError, err = GetAuthorizationCodeToken(application, clientSecret, code, verifier)
|
||||
case "password": // Resource Owner Password Credentials Grant
|
||||
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
|
||||
case "client_credentials": // Client Credentials Grant
|
||||
@@ -421,7 +391,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, "", host)
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -575,7 +545,7 @@ func createGuestUserToken(application *Application, clientSecret string, verifie
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, guestUser, "", "", "", "", "", "")
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, guestUser, "", "", "", "", "")
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -625,7 +595,7 @@ func generateGuestUsername() string {
|
||||
|
||||
// GetAuthorizationCodeToken
|
||||
// Authorization code flow
|
||||
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string, resource string) (*Token, *TokenError, error) {
|
||||
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError, error) {
|
||||
if code == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidRequest,
|
||||
@@ -693,14 +663,6 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RFC 8707: Validate resource parameter matches the one in the authorization request
|
||||
if resource != token.Resource {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("resource parameter does not match authorization request, expected: [%s], got: [%s]", token.Resource, resource),
|
||||
}, nil
|
||||
}
|
||||
|
||||
nowUnix := time.Now().Unix()
|
||||
if nowUnix > token.CodeExpireIn {
|
||||
// code must be used within 5 minutes
|
||||
@@ -757,7 +719,7 @@ func GetPasswordToken(application *Application, username string, password string
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -803,7 +765,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
||||
Type: "application",
|
||||
}
|
||||
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", "", "", scope, "", host)
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -867,7 +829,7 @@ func GetTokenByUser(application *Application, user *User, scope string, nonce st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", nonce, scope, "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -974,7 +936,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", "", "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", "", host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -1148,7 +1110,7 @@ func GetTokenExchangeToken(application *Application, clientSecret string, subjec
|
||||
}
|
||||
|
||||
// Generate new JWT token
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
|
||||
@@ -689,15 +689,6 @@ func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error)
|
||||
if user.OriginalRefreshToken != "" {
|
||||
user.OriginalRefreshToken = "***"
|
||||
}
|
||||
// Mask per-provider OAuth tokens in Properties
|
||||
if user.Properties != nil {
|
||||
for key := range user.Properties {
|
||||
// More specific pattern matching to avoid masking unrelated properties
|
||||
if strings.HasPrefix(key, "oauth_") && (strings.HasSuffix(key, "_accessToken") || strings.HasSuffix(key, "_refreshToken")) {
|
||||
user.Properties[key] = "***"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if user.ManagedAccounts != nil {
|
||||
|
||||
@@ -184,32 +184,9 @@ func getUserExtraProperty(user *User, providerType, key string) (string, error)
|
||||
return extra[key], nil
|
||||
}
|
||||
|
||||
// getOAuthTokenPropertyKey returns the property key for storing OAuth tokens
|
||||
func getOAuthTokenPropertyKey(providerType string, tokenType string) string {
|
||||
return fmt.Sprintf("oauth_%s_%s", providerType, tokenType)
|
||||
}
|
||||
|
||||
// GetUserOAuthAccessToken retrieves the OAuth access token for a specific provider
|
||||
func GetUserOAuthAccessToken(user *User, providerType string) string {
|
||||
return getUserProperty(user, getOAuthTokenPropertyKey(providerType, "accessToken"))
|
||||
}
|
||||
|
||||
// GetUserOAuthRefreshToken retrieves the OAuth refresh token for a specific provider
|
||||
func GetUserOAuthRefreshToken(user *User, providerType string) string {
|
||||
return getUserProperty(user, getOAuthTokenPropertyKey(providerType, "refreshToken"))
|
||||
}
|
||||
|
||||
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo, token *oauth2.Token, userMapping ...map[string]string) (bool, error) {
|
||||
// Store the original OAuth provider token if available
|
||||
if token != nil && token.AccessToken != "" {
|
||||
// Store tokens per provider in Properties map
|
||||
setUserProperty(user, getOAuthTokenPropertyKey(providerType, "accessToken"), token.AccessToken)
|
||||
|
||||
if token.RefreshToken != "" {
|
||||
setUserProperty(user, getOAuthTokenPropertyKey(providerType, "refreshToken"), token.RefreshToken)
|
||||
}
|
||||
|
||||
// Also update the legacy fields for backward compatibility
|
||||
user.OriginalToken = token.AccessToken
|
||||
user.OriginalRefreshToken = token.RefreshToken
|
||||
}
|
||||
@@ -405,7 +382,7 @@ func ClearUserOAuthProperties(user *User, providerType string) (bool, error) {
|
||||
|
||||
func userVisible(isAdmin bool, item *AccountItem) bool {
|
||||
if item == nil {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
if item.ViewRule == "Admin" && !isAdmin {
|
||||
@@ -564,11 +541,10 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Language != newUser.Language {
|
||||
item := GetAccountItemByName("Language", organization)
|
||||
if oldUser.SignupApplication != newUser.SignupApplication {
|
||||
item := GetAccountItemByName("Signup application", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Language = oldUser.Language
|
||||
newUser.SignupApplication = oldUser.SignupApplication
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
@@ -601,83 +577,6 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Balance != newUser.Balance {
|
||||
item := GetAccountItemByName("Balance", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Balance = oldUser.Balance
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.BalanceCredit != newUser.BalanceCredit {
|
||||
item := GetAccountItemByName("Balance credit", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.BalanceCredit = oldUser.BalanceCredit
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.BalanceCurrency != newUser.BalanceCurrency {
|
||||
item := GetAccountItemByName("Balance currency", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.BalanceCurrency = oldUser.BalanceCurrency
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
oldUserCartJson, _ := json.Marshal(oldUser.Cart)
|
||||
if newUser.Cart == nil {
|
||||
newUser.Cart = []ProductInfo{}
|
||||
}
|
||||
newUserCartJson, _ := json.Marshal(newUser.Cart)
|
||||
if string(oldUserCartJson) != string(newUserCartJson) {
|
||||
item := GetAccountItemByName("Cart", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Cart = oldUser.Cart
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Score != newUser.Score {
|
||||
item := GetAccountItemByName("Score", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Score = oldUser.Score
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Karma != newUser.Karma {
|
||||
item := GetAccountItemByName("Karma", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Karma = oldUser.Karma
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Ranking != newUser.Ranking {
|
||||
item := GetAccountItemByName("Ranking", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Ranking = oldUser.Ranking
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.SignupApplication != newUser.SignupApplication {
|
||||
item := GetAccountItemByName("Signup application", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.SignupApplication = oldUser.SignupApplication
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.IdCard != newUser.IdCard {
|
||||
item := GetAccountItemByName("ID card", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
@@ -806,6 +705,51 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Balance != newUser.Balance {
|
||||
item := GetAccountItemByName("Balance", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Balance = oldUser.Balance
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Score != newUser.Score {
|
||||
item := GetAccountItemByName("Score", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Score = oldUser.Score
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Karma != newUser.Karma {
|
||||
item := GetAccountItemByName("Karma", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Karma = oldUser.Karma
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Language != newUser.Language {
|
||||
item := GetAccountItemByName("Language", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Language = oldUser.Language
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Ranking != newUser.Ranking {
|
||||
item := GetAccountItemByName("Ranking", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
newUser.Ranking = oldUser.Ranking
|
||||
} else {
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser.Currency != newUser.Currency {
|
||||
item := GetAccountItemByName("Currency", organization)
|
||||
if !userVisible(isAdmin, item) {
|
||||
@@ -825,11 +769,6 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
|
||||
}
|
||||
|
||||
for _, accountItem := range itemsChanged {
|
||||
// Skip nil items - these occur when a field doesn't have a corresponding
|
||||
// account item configuration, meaning no validation rules apply
|
||||
if accountItem == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if pass, err := CheckAccountItemModifyRule(accountItem, isAdmin, lang); !pass {
|
||||
return pass, err
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
mssql "github.com/microsoft/go-mssqldb"
|
||||
mssql "github.com/denisenkom/go-mssqldb"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// OauthProtectedResourceMetadata represents RFC 9728 OAuth 2.0 Protected Resource Metadata
|
||||
type OauthProtectedResourceMetadata struct {
|
||||
Resource string `json:"resource"`
|
||||
AuthorizationServers []string `json:"authorization_servers"`
|
||||
BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
|
||||
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||||
ResourceSigningAlg []string `json:"resource_signing_alg_values_supported,omitempty"`
|
||||
ResourceDocumentation string `json:"resource_documentation,omitempty"`
|
||||
}
|
||||
|
||||
// GetOauthProtectedResourceMetadata returns RFC 9728 Protected Resource Metadata for global discovery
|
||||
func GetOauthProtectedResourceMetadata(host string) OauthProtectedResourceMetadata {
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
return OauthProtectedResourceMetadata{
|
||||
Resource: originBackend,
|
||||
AuthorizationServers: []string{originBackend},
|
||||
BearerMethodsSupported: []string{"header"},
|
||||
ScopesSupported: []string{"openid", "profile", "email", "read", "write"},
|
||||
ResourceSigningAlg: []string{"RS256"},
|
||||
}
|
||||
}
|
||||
|
||||
// GetOauthProtectedResourceMetadataByApplication returns RFC 9728 Protected Resource Metadata for application-specific discovery
|
||||
func GetOauthProtectedResourceMetadataByApplication(host string, applicationName string) OauthProtectedResourceMetadata {
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
// For application-specific discovery, the resource identifier includes the application name
|
||||
resourceIdentifier := fmt.Sprintf("%s/.well-known/%s", originBackend, applicationName)
|
||||
authServer := fmt.Sprintf("%s/.well-known/%s", originBackend, applicationName)
|
||||
|
||||
return OauthProtectedResourceMetadata{
|
||||
Resource: resourceIdentifier,
|
||||
AuthorizationServers: []string{authServer},
|
||||
BearerMethodsSupported: []string{"header"},
|
||||
ScopesSupported: []string{"openid", "profile", "email", "read", "write"},
|
||||
ResourceSigningAlg: []string{"RS256"},
|
||||
}
|
||||
}
|
||||
41
pp/dummy.go
41
pp/dummy.go
@@ -14,59 +14,22 @@
|
||||
|
||||
package pp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DummyPaymentProvider struct{}
|
||||
|
||||
type DummyOrderInfo struct {
|
||||
Price float64 `json:"price"`
|
||||
Currency string `json:"currency"`
|
||||
ProductDisplayName string `json:"productDisplayName"`
|
||||
}
|
||||
|
||||
func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
||||
pp := &DummyPaymentProvider{}
|
||||
return pp, nil
|
||||
}
|
||||
|
||||
func (pp *DummyPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||
// Encode payment information in OrderId for later retrieval in Notify.
|
||||
// Note: This is a test/mock provider and the OrderId is only used internally for testing.
|
||||
// Real payment providers would receive this information from their external payment gateway.
|
||||
orderInfo := DummyOrderInfo{
|
||||
Price: r.Price,
|
||||
Currency: r.Currency,
|
||||
ProductDisplayName: "",
|
||||
}
|
||||
orderInfoBytes, err := json.Marshal(orderInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode order info: %w", err)
|
||||
}
|
||||
|
||||
return &PayResp{
|
||||
PayUrl: r.ReturnUrl,
|
||||
OrderId: string(orderInfoBytes),
|
||||
PayUrl: r.ReturnUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||
// Decode payment information from OrderId
|
||||
var orderInfo DummyOrderInfo
|
||||
if orderId != "" {
|
||||
err := json.Unmarshal([]byte(orderId), &orderInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode order info: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &NotifyResult{
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
Price: orderInfo.Price,
|
||||
Currency: orderInfo.Currency,
|
||||
ProductDisplayName: orderInfo.ProductDisplayName,
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
)
|
||||
|
||||
// Application represents a simplified application structure for reverse proxy
|
||||
type Application struct {
|
||||
Owner string
|
||||
Name string
|
||||
UpstreamHost string
|
||||
}
|
||||
|
||||
// ApplicationLookupFunc is a function type for looking up applications by domain
|
||||
type ApplicationLookupFunc func(domain string) *Application
|
||||
|
||||
var applicationLookup ApplicationLookupFunc
|
||||
|
||||
// SetApplicationLookup sets the function to use for looking up applications by domain
|
||||
func SetApplicationLookup(lookupFunc ApplicationLookupFunc) {
|
||||
applicationLookup = lookupFunc
|
||||
}
|
||||
|
||||
// getDomainWithoutPort removes the port from a domain string
|
||||
func getDomainWithoutPort(domain string) string {
|
||||
if !strings.Contains(domain, ":") {
|
||||
return domain
|
||||
}
|
||||
|
||||
tokens := strings.SplitN(domain, ":", 2)
|
||||
if len(tokens) > 1 {
|
||||
return tokens[0]
|
||||
}
|
||||
return domain
|
||||
}
|
||||
|
||||
// forwardHandler creates and configures a reverse proxy for the given target URL
|
||||
func forwardHandler(targetUrl string, writer http.ResponseWriter, request *http.Request) {
|
||||
target, err := url.Parse(targetUrl)
|
||||
if err != nil {
|
||||
logs.Error("Failed to parse target URL %s: %v", targetUrl, err)
|
||||
http.Error(writer, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
|
||||
// Configure the Director to set proper headers
|
||||
proxy.Director = func(r *http.Request) {
|
||||
r.URL.Scheme = target.Scheme
|
||||
r.URL.Host = target.Host
|
||||
r.Host = target.Host
|
||||
|
||||
// Set X-Real-IP and X-Forwarded-For headers
|
||||
if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
|
||||
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||
r.Header.Set("X-Forwarded-For", fmt.Sprintf("%s, %s", xff, clientIP))
|
||||
} else {
|
||||
r.Header.Set("X-Forwarded-For", clientIP)
|
||||
}
|
||||
r.Header.Set("X-Real-IP", clientIP)
|
||||
}
|
||||
|
||||
// Set X-Forwarded-Proto header
|
||||
if r.TLS != nil {
|
||||
r.Header.Set("X-Forwarded-Proto", "https")
|
||||
} else {
|
||||
r.Header.Set("X-Forwarded-Proto", "http")
|
||||
}
|
||||
|
||||
// Set X-Forwarded-Host header
|
||||
r.Header.Set("X-Forwarded-Host", request.Host)
|
||||
}
|
||||
|
||||
// Handle ModifyResponse for security enhancements
|
||||
proxy.ModifyResponse = func(resp *http.Response) error {
|
||||
// Add Secure flag to all Set-Cookie headers in HTTPS responses
|
||||
if request.TLS != nil {
|
||||
// Add HSTS header for HTTPS responses if not already set by backend
|
||||
if resp.Header.Get("Strict-Transport-Security") == "" {
|
||||
resp.Header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
||||
}
|
||||
|
||||
cookies := resp.Header["Set-Cookie"]
|
||||
if len(cookies) > 0 {
|
||||
// Clear existing Set-Cookie headers
|
||||
resp.Header.Del("Set-Cookie")
|
||||
// Add them back with Secure flag if not already present
|
||||
for _, cookie := range cookies {
|
||||
// Check if Secure attribute is already present (case-insensitive)
|
||||
cookieLower := strings.ToLower(cookie)
|
||||
hasSecure := strings.Contains(cookieLower, ";secure;") ||
|
||||
strings.Contains(cookieLower, "; secure;") ||
|
||||
strings.HasSuffix(cookieLower, ";secure") ||
|
||||
strings.HasSuffix(cookieLower, "; secure")
|
||||
if !hasSecure {
|
||||
cookie = cookie + "; Secure"
|
||||
}
|
||||
resp.Header.Add("Set-Cookie", cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
proxy.ServeHTTP(writer, request)
|
||||
}
|
||||
|
||||
// HandleReverseProxy handles incoming requests and forwards them to the appropriate upstream
|
||||
func HandleReverseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
domain := getDomainWithoutPort(r.Host)
|
||||
|
||||
if applicationLookup == nil {
|
||||
logs.Error("Application lookup function not set")
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Lookup the application by domain
|
||||
app := applicationLookup(domain)
|
||||
if app == nil {
|
||||
logs.Info("No application found for domain: %s", domain)
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the application has an upstream host configured
|
||||
if app.UpstreamHost == "" {
|
||||
logs.Warn("Application %s/%s has no upstream host configured", app.Owner, app.Name)
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Build the target URL - just use the upstream host, the actual path/query will be set by the proxy Director
|
||||
targetUrl := app.UpstreamHost
|
||||
if !strings.HasPrefix(targetUrl, "http://") && !strings.HasPrefix(targetUrl, "https://") {
|
||||
targetUrl = "http://" + targetUrl
|
||||
}
|
||||
|
||||
logs.Debug("Forwarding request from %s%s to %s", r.Host, r.RequestURI, targetUrl)
|
||||
forwardHandler(targetUrl, w, r)
|
||||
}
|
||||
|
||||
// StartProxyServer starts the HTTP and HTTPS proxy servers based on configuration
|
||||
func StartProxyServer() {
|
||||
proxyHttpPort := conf.GetConfigString("proxyHttpPort")
|
||||
proxyHttpsPort := conf.GetConfigString("proxyHttpsPort")
|
||||
|
||||
if proxyHttpPort == "" && proxyHttpsPort == "" {
|
||||
logs.Info("Reverse proxy not enabled (proxyHttpPort and proxyHttpsPort are empty)")
|
||||
return
|
||||
}
|
||||
|
||||
serverMux := http.NewServeMux()
|
||||
serverMux.HandleFunc("/", HandleReverseProxy)
|
||||
|
||||
// Start HTTP proxy if configured
|
||||
if proxyHttpPort != "" {
|
||||
go func() {
|
||||
addr := fmt.Sprintf(":%s", proxyHttpPort)
|
||||
logs.Info("Starting reverse proxy HTTP server on %s", addr)
|
||||
err := http.ListenAndServe(addr, serverMux)
|
||||
if err != nil {
|
||||
logs.Error("Failed to start HTTP proxy server: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Start HTTPS proxy if configured
|
||||
if proxyHttpsPort != "" {
|
||||
go func() {
|
||||
addr := fmt.Sprintf(":%s", proxyHttpsPort)
|
||||
|
||||
// For now, HTTPS will need certificate configuration
|
||||
// This can be enhanced later to use Application's SslCert field
|
||||
logs.Info("HTTPS proxy server on %s requires certificate configuration - not implemented yet", addr)
|
||||
|
||||
// When implemented, use code like:
|
||||
// server := &http.Server{
|
||||
// Handler: serverMux,
|
||||
// Addr: addr,
|
||||
// TLSConfig: &tls.Config{
|
||||
// MinVersion: tls.VersionTLS12,
|
||||
// PreferServerCipherSuites: true,
|
||||
// CipherSuites: []uint16{
|
||||
// tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
// tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
// tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
// tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
// tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
// tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
// },
|
||||
// CurvePreferences: []tls.CurveID{
|
||||
// tls.X25519,
|
||||
// tls.CurveP256,
|
||||
// tls.CurveP384,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// err := server.ListenAndServeTLS("", "")
|
||||
// if err != nil {
|
||||
// logs.Error("Failed to start HTTPS proxy server: %v", err)
|
||||
// }
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestReverseProxyIntegration tests the reverse proxy with a real backend server
|
||||
func TestReverseProxyIntegration(t *testing.T) {
|
||||
// Create a test backend server that echoes the request path
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Verify headers
|
||||
headers := []string{
|
||||
"X-Forwarded-For",
|
||||
"X-Forwarded-Proto",
|
||||
"X-Real-IP",
|
||||
"X-Forwarded-Host",
|
||||
}
|
||||
|
||||
for _, header := range headers {
|
||||
if r.Header.Get(header) == "" {
|
||||
t.Errorf("Expected header %s to be set", header)
|
||||
}
|
||||
}
|
||||
|
||||
// Echo the path and query
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("Path: " + r.URL.Path + "\n"))
|
||||
w.Write([]byte("Query: " + r.URL.RawQuery + "\n"))
|
||||
w.Write([]byte("Host: " + r.Host + "\n"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
// Set up the application lookup
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
if domain == "myapp.example.com" {
|
||||
return &Application{
|
||||
Owner: "test-owner",
|
||||
Name: "my-app",
|
||||
UpstreamHost: backend.URL,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Test various request paths
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
query string
|
||||
expected string
|
||||
}{
|
||||
{"Simple path", "/", "", "Path: /\n"},
|
||||
{"Path with segments", "/api/v1/users", "", "Path: /api/v1/users\n"},
|
||||
{"Path with query", "/search", "q=test&limit=10", "Query: q=test&limit=10\n"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
url := "http://myapp.example.com" + tt.path
|
||||
if tt.query != "" {
|
||||
url += "?" + tt.query
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("GET", url, nil)
|
||||
req.Host = "myapp.example.com"
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(w.Body)
|
||||
bodyStr := string(body)
|
||||
|
||||
if !strings.Contains(bodyStr, tt.expected) {
|
||||
t.Errorf("Expected response to contain %q, got %q", tt.expected, bodyStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestReverseProxyWebSocket tests that WebSocket upgrade headers are preserved
|
||||
func TestReverseProxyWebSocket(t *testing.T) {
|
||||
// Note: WebSocket upgrade through httptest.ResponseRecorder has limitations
|
||||
// This test verifies that WebSocket headers are passed through, but
|
||||
// full WebSocket functionality would need integration testing with real servers
|
||||
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Verify WebSocket headers are present
|
||||
if r.Header.Get("Upgrade") == "websocket" &&
|
||||
r.Header.Get("Connection") != "" &&
|
||||
r.Header.Get("Sec-WebSocket-Version") != "" &&
|
||||
r.Header.Get("Sec-WebSocket-Key") != "" {
|
||||
// Headers are present - this is what we're testing
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("WebSocket headers received"))
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Missing WebSocket headers"))
|
||||
}
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
if domain == "ws.example.com" {
|
||||
return &Application{
|
||||
Owner: "test-owner",
|
||||
Name: "ws-app",
|
||||
UpstreamHost: backend.URL,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "http://ws.example.com/ws", nil)
|
||||
req.Host = "ws.example.com"
|
||||
req.Header.Set("Upgrade", "websocket")
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Sec-WebSocket-Version", "13")
|
||||
req.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==")
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
body, _ := io.ReadAll(w.Body)
|
||||
bodyStr := string(body)
|
||||
|
||||
// We expect the headers to be passed through to the backend
|
||||
if !strings.Contains(bodyStr, "WebSocket headers received") {
|
||||
t.Errorf("WebSocket headers were not properly forwarded. Got: %s", bodyStr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReverseProxyUpstreamHostVariations tests different UpstreamHost formats
|
||||
func TestReverseProxyUpstreamHostVariations(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("OK"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
// Parse backend URL to get host
|
||||
backendURL, err := url.Parse(backend.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse backend URL: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
upstreamHost string
|
||||
shouldWork bool
|
||||
}{
|
||||
{"Full URL", backend.URL, true},
|
||||
{"Host only", backendURL.Host, true},
|
||||
{"Empty", "", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
if domain == "test.example.com" {
|
||||
return &Application{
|
||||
Owner: "test-owner",
|
||||
Name: "test-app",
|
||||
UpstreamHost: tt.upstreamHost,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "http://test.example.com/", nil)
|
||||
req.Host = "test.example.com"
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
if tt.shouldWork {
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
} else {
|
||||
if w.Code == http.StatusOK {
|
||||
t.Errorf("Expected failure, but got status 200")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetDomainWithoutPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"example.com", "example.com"},
|
||||
{"example.com:8080", "example.com"},
|
||||
{"localhost:3000", "localhost"},
|
||||
{"subdomain.example.com:443", "subdomain.example.com"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := getDomainWithoutPort(test.input)
|
||||
if result != test.expected {
|
||||
t.Errorf("getDomainWithoutPort(%s) = %s; want %s", test.input, result, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleReverseProxy(t *testing.T) {
|
||||
// Create a test backend server
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Check that headers are set correctly
|
||||
if r.Header.Get("X-Forwarded-For") == "" {
|
||||
t.Error("X-Forwarded-For header not set")
|
||||
}
|
||||
if r.Header.Get("X-Forwarded-Proto") == "" {
|
||||
t.Error("X-Forwarded-Proto header not set")
|
||||
}
|
||||
if r.Header.Get("X-Real-IP") == "" {
|
||||
t.Error("X-Real-IP header not set")
|
||||
}
|
||||
if r.Header.Get("X-Forwarded-Host") == "" {
|
||||
t.Error("X-Forwarded-Host header not set")
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Backend response")
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
// Set up a mock application lookup function
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
if domain == "test.example.com" {
|
||||
return &Application{
|
||||
Owner: "test-owner",
|
||||
Name: "test-app",
|
||||
UpstreamHost: backend.URL,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Test successful proxy
|
||||
req := httptest.NewRequest("GET", "http://test.example.com/path", nil)
|
||||
req.Host = "test.example.com"
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
// Test domain not found
|
||||
req = httptest.NewRequest("GET", "http://unknown.example.com/path", nil)
|
||||
req.Host = "unknown.example.com"
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("Expected status 404 for unknown domain, got %d", w.Code)
|
||||
}
|
||||
|
||||
// Test application without upstream host
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
if domain == "no-upstream.example.com" {
|
||||
return &Application{
|
||||
Owner: "test-owner",
|
||||
Name: "test-app-no-upstream",
|
||||
UpstreamHost: "",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
req = httptest.NewRequest("GET", "http://no-upstream.example.com/path", nil)
|
||||
req.Host = "no-upstream.example.com"
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
HandleReverseProxy(w, req)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("Expected status 404 for app without upstream, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationLookup(t *testing.T) {
|
||||
// Test setting and using the application lookup function
|
||||
called := false
|
||||
SetApplicationLookup(func(domain string) *Application {
|
||||
called = true
|
||||
return &Application{
|
||||
Owner: "test",
|
||||
Name: "app",
|
||||
UpstreamHost: "http://localhost:8080",
|
||||
}
|
||||
})
|
||||
|
||||
if applicationLookup == nil {
|
||||
t.Error("applicationLookup should not be nil after SetApplicationLookup")
|
||||
}
|
||||
|
||||
app := applicationLookup("test.com")
|
||||
if !called {
|
||||
t.Error("applicationLookup function was not called")
|
||||
}
|
||||
if app == nil {
|
||||
t.Error("applicationLookup should return non-nil application")
|
||||
}
|
||||
if app.Owner != "test" {
|
||||
t.Errorf("Expected owner 'test', got '%s'", app.Owner)
|
||||
}
|
||||
}
|
||||
@@ -94,17 +94,6 @@ func denyMcpRequest(ctx *context.Context) {
|
||||
Data: T(ctx, "auth:Unauthorized operation"),
|
||||
})
|
||||
|
||||
// Add WWW-Authenticate header per MCP Authorization spec (RFC 9728)
|
||||
// Use the same logic as getOriginFromHost to determine the scheme
|
||||
host := ctx.Request.Host
|
||||
scheme := "https"
|
||||
if !strings.Contains(host, ".") {
|
||||
// localhost:8000 or computer-name:80
|
||||
scheme = "http"
|
||||
}
|
||||
resourceMetadataUrl := fmt.Sprintf("%s://%s/.well-known/oauth-protected-resource", scheme, host)
|
||||
ctx.Output.Header("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"casdoor\", resource_metadata=\"%s\"", resourceMetadataUrl))
|
||||
|
||||
ctx.Output.SetStatus(http.StatusUnauthorized)
|
||||
_ = ctx.Output.JSON(resp, true, false)
|
||||
}
|
||||
|
||||
@@ -298,7 +298,6 @@ func InitAPI() {
|
||||
web.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
|
||||
web.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
|
||||
web.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
|
||||
web.Router("/api/oauth/register", &controllers.ApiController{}, "POST:DynamicClientRegister")
|
||||
|
||||
web.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
|
||||
web.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
|
||||
@@ -321,14 +320,10 @@ func InitAPI() {
|
||||
|
||||
web.Router("/.well-known/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscovery")
|
||||
web.Router("/.well-known/:application/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscoveryByApplication")
|
||||
web.Router("/.well-known/oauth-authorization-server", &controllers.RootController{}, "GET:GetOAuthServerMetadata")
|
||||
web.Router("/.well-known/:application/oauth-authorization-server", &controllers.RootController{}, "GET:GetOAuthServerMetadataByApplication")
|
||||
web.Router("/.well-known/jwks", &controllers.RootController{}, "*:GetJwks")
|
||||
web.Router("/.well-known/:application/jwks", &controllers.RootController{}, "*:GetJwksByApplication")
|
||||
web.Router("/.well-known/webfinger", &controllers.RootController{}, "GET:GetWebFinger")
|
||||
web.Router("/.well-known/:application/webfinger", &controllers.RootController{}, "GET:GetWebFingerByApplication")
|
||||
web.Router("/.well-known/oauth-protected-resource", &controllers.RootController{}, "GET:GetOauthProtectedResourceMetadata")
|
||||
web.Router("/.well-known/:application/oauth-protected-resource", &controllers.RootController{}, "GET:GetOauthProtectedResourceMetadataByApplication")
|
||||
|
||||
web.Router("/cas/:organization/:application/serviceValidate", &controllers.RootController{}, "GET:CasServiceValidate")
|
||||
web.Router("/cas/:organization/:application/proxyValidate", &controllers.RootController{}, "GET:CasProxyValidate")
|
||||
|
||||
@@ -89,7 +89,7 @@ func fastAutoSignin(ctx *context.Context) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "autoSignin", responseType, redirectUri, scope, state, nonce, codeChallenge, "", ctx.Request.Host, getAcceptLanguage(ctx))
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "autoSignin", responseType, redirectUri, scope, state, nonce, codeChallenge, ctx.Request.Host, getAcceptLanguage(ctx))
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if code.Message != "" {
|
||||
|
||||
@@ -158,7 +158,7 @@ class AdapterEditPage extends React.Component {
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} disabled={Setting.builtInObject(this.state.adapter)} style={{width: "100%"}} value={this.state.adapter.type} onChange={(value => {
|
||||
|
||||
@@ -48,7 +48,6 @@ import ProviderTable from "./table/ProviderTable";
|
||||
import SigninMethodTable from "./table/SigninMethodTable";
|
||||
import SignupTable from "./table/SignupTable";
|
||||
import SamlAttributeTable from "./table/SamlAttributeTable";
|
||||
import ScopeTable from "./table/ScopeTable";
|
||||
import PromptPage from "./auth/PromptPage";
|
||||
import copy from "copy-to-clipboard";
|
||||
import ThemeEditor from "./common/theme/ThemeEditor";
|
||||
@@ -59,7 +58,6 @@ import * as GroupBackend from "./backend/GroupBackend";
|
||||
import TokenAttributeTable from "./table/TokenAttributeTable";
|
||||
import {Content, Header} from "antd/es/layout/layout";
|
||||
import Sider from "antd/es/layout/Sider";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -151,6 +149,7 @@ class ApplicationEditPage extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getApplication();
|
||||
this.getOrganizations();
|
||||
this.getGroups();
|
||||
}
|
||||
|
||||
getApplication() {
|
||||
@@ -202,6 +201,17 @@ class ApplicationEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getGroups() {
|
||||
GroupBackend.getGroups(this.state.owner)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
groups: res.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCerts(application) {
|
||||
let owner = application.organization;
|
||||
if (application.isShared) {
|
||||
@@ -308,61 +318,6 @@ class ApplicationEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("general:Category"), i18next.t("general:Category - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Select
|
||||
virtual={false}
|
||||
style={{width: "100%"}}
|
||||
value={this.state.application.category}
|
||||
onChange={(value) => {
|
||||
this.updateApplicationField("category", value);
|
||||
if (value === "Agent") {
|
||||
this.updateApplicationField("type", "MCP");
|
||||
} else {
|
||||
this.updateApplicationField("type", "All");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Option value="Default">Default</Option>
|
||||
<Option value="Agent">Agent</Option>
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Select
|
||||
virtual={false}
|
||||
style={{width: "100%"}}
|
||||
value={this.state.application.type}
|
||||
onChange={(value) => {
|
||||
this.updateApplicationField("type", value);
|
||||
}}
|
||||
>
|
||||
{
|
||||
(this.state.application.category === "Agent") ? (
|
||||
<>
|
||||
<Option value="MCP">MCP</Option>
|
||||
<Option value="A2A">A2A</Option>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Option value="All">All</Option>
|
||||
<Option value="OIDC">OIDC</Option>
|
||||
<Option value="OAuth">OAuth</Option>
|
||||
<Option value="SAML">SAML</Option>
|
||||
<Option value="CAS">CAS</Option>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("general:Is shared"), i18next.t("general:Is shared - Tooltip"))} :
|
||||
@@ -572,22 +527,6 @@ class ApplicationEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
(this.state.application.category === "Agent") ? (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("general:Scopes"), i18next.t("general:Scopes - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<ScopeTable
|
||||
title={i18next.t("general:Scopes")}
|
||||
table={this.state.application.scopes}
|
||||
onUpdateTable={(value) => {this.updateApplicationField("scopes", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
) : null
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("application:Token format"), i18next.t("application:Token format - Tooltip"))} :
|
||||
@@ -672,30 +611,24 @@ class ApplicationEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("ldap:Default group"), i18next.t("ldap:Default group - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21}>
|
||||
<PaginateSelect
|
||||
virtual
|
||||
style={{width: "100%"}}
|
||||
allowClear
|
||||
placeholder={i18next.t("general:Default")}
|
||||
value={this.state.application.defaultGroup || undefined}
|
||||
fetchPage={GroupBackend.getGroups}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.owner, false, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={this.state.owner}
|
||||
optionMapper={(group) => Setting.getOption(
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.application.defaultGroup ?? []} onChange={(value => {
|
||||
this.updateApplicationField("defaultGroup", value);
|
||||
})}
|
||||
>
|
||||
<Option key={""} value={""}>
|
||||
<Space>
|
||||
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||
{group.displayName}
|
||||
</Space>,
|
||||
`${group.owner}/${group.name}`
|
||||
)}
|
||||
filterOption={false}
|
||||
onChange={(value) => {
|
||||
this.updateApplicationField("defaultGroup", value || "");
|
||||
}}
|
||||
/>
|
||||
{i18next.t("general:Default")}
|
||||
</Space>
|
||||
</Option>
|
||||
{
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
|
||||
<Space>
|
||||
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||
{group.displayName}
|
||||
</Space>
|
||||
</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
@@ -1373,68 +1306,6 @@ class ApplicationEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{this.state.activeMenuKey === "reverse-proxy" && (
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Input value={this.state.application.domain} placeholder="e.g., blog.example.com" onChange={e => {
|
||||
this.updateApplicationField("domain", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("application:Other domains"), i18next.t("application:Other domains - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<UrlTable
|
||||
title={i18next.t("application:Other domains")}
|
||||
table={this.state.application.otherDomains}
|
||||
onUpdateTable={(value) => {this.updateApplicationField("otherDomains", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("application:Upstream host"), i18next.t("application:Upstream host - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Input value={this.state.application.upstreamHost} placeholder="e.g., localhost:8080 or 192.168.1.100:3000" onChange={e => {
|
||||
this.updateApplicationField("upstreamHost", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("provider:SSL mode"), i18next.t("provider:SSL mode - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.application.sslMode} onChange={(value => {this.updateApplicationField("sslMode", value);})}>
|
||||
<Option value="">{i18next.t("general:None")}</Option>
|
||||
<Option value="HTTP">HTTP</Option>
|
||||
<Option value="HTTPS and HTTP">HTTPS and HTTP</Option>
|
||||
<Option value="HTTPS Only">HTTPS Only</Option>
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
|
||||
{Setting.getLabel(i18next.t("application:SSL cert"), i18next.t("application:SSL cert - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.application.sslCert} onChange={(value => {this.updateApplicationField("sslCert", value);})}>
|
||||
<Option value="">{i18next.t("general:None")}</Option>
|
||||
{
|
||||
this.state.certs.map((cert, index) => <Option key={index} value={cert.name}>{cert.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
)}</>;
|
||||
}
|
||||
|
||||
@@ -1447,12 +1318,11 @@ class ApplicationEditPage extends React.Component {
|
||||
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitApplicationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteApplication()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
} style={{margin: (Setting.isMobile()) ? "5px" : {}, height: "calc(100vh - 145px - 48px)", overflow: "hidden"}}
|
||||
styles={{body: {height: "100%"}}} type="inner">
|
||||
<Layout style={{background: "inherit", height: "100%", overflow: "auto"}}>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
<Layout style={{background: "inherit"}}>
|
||||
{
|
||||
this.state.menuMode === "horizontal" || !this.state.menuMode ? (
|
||||
<Header style={{background: "inherit", padding: "0px", position: "sticky", top: 0}}>
|
||||
<Header style={{background: "inherit", padding: "0px"}}>
|
||||
<div className="demo-logo" />
|
||||
<Tabs
|
||||
onChange={(key) => {
|
||||
@@ -1467,13 +1337,12 @@ class ApplicationEditPage extends React.Component {
|
||||
{label: i18next.t("application:Providers"), key: "providers"},
|
||||
{label: i18next.t("application:UI Customization"), key: "ui-customization"},
|
||||
{label: i18next.t("application:Security"), key: "security"},
|
||||
{label: i18next.t("application:Reverse Proxy"), key: "reverse-proxy"},
|
||||
]}
|
||||
/>
|
||||
</Header>
|
||||
) : null
|
||||
}
|
||||
<Layout style={{background: "inherit", overflow: "auto"}}>
|
||||
<Layout style={{background: "inherit", maxHeight: "calc(70vh - 70px)", overflow: "auto"}}>
|
||||
{
|
||||
this.state.menuMode === "vertical" ? (
|
||||
<Sider width={200} style={{background: "inherit", position: "sticky", top: 0}}>
|
||||
@@ -1491,7 +1360,6 @@ class ApplicationEditPage extends React.Component {
|
||||
<Menu.Item key="providers">{i18next.t("application:Providers")}</Menu.Item>
|
||||
<Menu.Item key="ui-customization">{i18next.t("application:UI Customization")}</Menu.Item>
|
||||
<Menu.Item key="security">{i18next.t("application:Security")}</Menu.Item>
|
||||
<Menu.Item key="reverse-proxy">{i18next.t("application:Reverse Proxy")}</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>) : null
|
||||
}
|
||||
|
||||
@@ -38,9 +38,6 @@ class ApplicationListPage extends BaseListPage {
|
||||
organization: organizationName,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Application - ${randomName}`,
|
||||
category: "Default",
|
||||
type: "All",
|
||||
scopes: [],
|
||||
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
|
||||
enablePassword: true,
|
||||
enableSignUp: true,
|
||||
@@ -182,36 +179,6 @@ class ApplicationListPage extends BaseListPage {
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("displayName"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("category"),
|
||||
render: (text, record, index) => {
|
||||
if (!text) {
|
||||
text = "Default";
|
||||
}
|
||||
|
||||
if (text === "Agent") {
|
||||
return Setting.getTag("success", text);
|
||||
} else {
|
||||
return Setting.getTag("default", text);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Type"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
width: "100px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("type"),
|
||||
render: (text, record, index) => {
|
||||
return text;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Logo",
|
||||
dataIndex: "logo",
|
||||
|
||||
@@ -75,11 +75,6 @@ class CartListPage extends BaseListPage {
|
||||
|
||||
const owner = this.state.user?.owner || this.props.account.owner;
|
||||
const carts = this.state.data || [];
|
||||
const invalidCarts = carts.filter(item => item.isInvalid);
|
||||
if (invalidCarts.length > 0) {
|
||||
Setting.showMessage("error", i18next.t("product:Cart contains invalid products, please delete them before placing an order"));
|
||||
return;
|
||||
}
|
||||
if (carts.length === 0) {
|
||||
Setting.showMessage("error", i18next.t("product:Product list cannot be empty"));
|
||||
return;
|
||||
@@ -122,11 +117,7 @@ class CartListPage extends BaseListPage {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = user.cart.findIndex(item =>
|
||||
item.name === record.name &&
|
||||
(record.isRecharge ? item.price === record.price : true) &&
|
||||
(item.pricingName || "") === (record.pricingName || "") &&
|
||||
(item.planName || "") === (record.planName || ""));
|
||||
const index = user.cart.findIndex(item => item.name === record.name && item.price === record.price && (item.pricingName || "") === (record.pricingName || "") && (item.planName || "") === (record.planName || ""));
|
||||
if (index === -1) {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to delete"));
|
||||
return;
|
||||
@@ -153,7 +144,7 @@ class CartListPage extends BaseListPage {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemKey = `${record.name}-${record.price !== null ? record.price : "null"}-${record.pricingName || ""}-${record.planName || ""}`;
|
||||
const itemKey = `${record.name}-${record.price}-${record.pricingName || ""}-${record.planName || ""}`;
|
||||
if (this.updatingCartItemsRef?.[itemKey]) {
|
||||
return;
|
||||
}
|
||||
@@ -161,71 +152,64 @@ class CartListPage extends BaseListPage {
|
||||
this.updatingCartItemsRef[itemKey] = true;
|
||||
|
||||
const user = Setting.deepCopy(this.state.user);
|
||||
const index = user.cart.findIndex(item =>
|
||||
item.name === record.name &&
|
||||
(record.isRecharge ? item.price === record.price : true) &&
|
||||
(item.pricingName || "") === (record.pricingName || "") &&
|
||||
(item.planName || "") === (record.planName || ""));
|
||||
const index = user.cart.findIndex(item => item.name === record.name && item.price === record.price && (item.pricingName || "") === (record.pricingName || "") && (item.planName || "") === (record.planName || ""));
|
||||
if (index === -1) {
|
||||
delete this.updatingCartItemsRef[itemKey];
|
||||
return;
|
||||
}
|
||||
|
||||
user.cart[index].quantity = newQuantity;
|
||||
const newData = [...this.state.data];
|
||||
const dataIndex = newData.findIndex(item =>
|
||||
item.name === record.name &&
|
||||
(record.price !== null ? item.price === record.price : true) &&
|
||||
(item.pricingName || "") === (record.pricingName || "") &&
|
||||
(item.planName || "") === (record.planName || ""));
|
||||
if (dataIndex !== -1) {
|
||||
newData[dataIndex].quantity = newQuantity;
|
||||
this.setState({data: newData});
|
||||
}
|
||||
if (index !== -1) {
|
||||
user.cart[index].quantity = newQuantity;
|
||||
|
||||
this.setState(prevState => ({
|
||||
updatingCartItems: {
|
||||
...(prevState.updatingCartItems || {}),
|
||||
[itemKey]: true,
|
||||
},
|
||||
}));
|
||||
const newData = [...this.state.data];
|
||||
const dataIndex = newData.findIndex(item => item.name === record.name && item.price === record.price && (item.pricingName || "") === (record.pricingName || "") && (item.planName || "") === (record.planName || ""));
|
||||
if (dataIndex !== -1) {
|
||||
newData[dataIndex].quantity = newQuantity;
|
||||
this.setState({data: newData});
|
||||
}
|
||||
|
||||
UserBackend.updateUser(user.owner, user.name, user)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({user: user});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
this.setState(prevState => ({
|
||||
updatingCartItems: {
|
||||
...(prevState.updatingCartItems || {}),
|
||||
[itemKey]: true,
|
||||
},
|
||||
}));
|
||||
|
||||
UserBackend.updateUser(user.owner, user.name, user)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({user: user});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
this.fetch();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
this.fetch();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
this.fetch();
|
||||
})
|
||||
.finally(() => {
|
||||
delete this.updatingCartItemsRef[itemKey];
|
||||
this.setState(prevState => {
|
||||
const updatingCartItems = {...(prevState.updatingCartItems || {})};
|
||||
delete updatingCartItems[itemKey];
|
||||
return {updatingCartItems};
|
||||
})
|
||||
.finally(() => {
|
||||
delete this.updatingCartItemsRef[itemKey];
|
||||
this.setState(prevState => {
|
||||
const updatingCartItems = {...(prevState.updatingCartItems || {})};
|
||||
delete updatingCartItems[itemKey];
|
||||
return {updatingCartItems};
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderTable(carts) {
|
||||
const isEmpty = carts === undefined || carts === null || carts.length === 0;
|
||||
const hasInvalidItems = carts && carts.some(item => item.isInvalid);
|
||||
const owner = this.state.user?.owner || this.props.account.owner;
|
||||
|
||||
let total = 0;
|
||||
let currency = "";
|
||||
if (carts && carts.length > 0) {
|
||||
const validCarts = carts.filter(item => !item.isInvalid);
|
||||
validCarts.forEach(item => {
|
||||
carts.forEach(item => {
|
||||
total += item.price * item.quantity;
|
||||
});
|
||||
currency = validCarts.length > 0 ? validCarts[0].currency : (carts[0].currency || "USD");
|
||||
currency = carts[0].currency;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
@@ -238,9 +222,6 @@ class CartListPage extends BaseListPage {
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("name"),
|
||||
render: (text, record, index) => {
|
||||
if (record.isInvalid) {
|
||||
return <span style={{color: "red"}}>{text}</span>;
|
||||
}
|
||||
return (
|
||||
<Link to={`/products/${owner}/${text}`}>
|
||||
{text}
|
||||
@@ -254,12 +235,6 @@ class CartListPage extends BaseListPage {
|
||||
key: "displayName",
|
||||
width: "170px",
|
||||
sorter: true,
|
||||
render: (text, record) => {
|
||||
if (record.isInvalid) {
|
||||
return <span style={{color: "red"}}>{i18next.t("product:Invalid product")}</span>;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("product:Image"),
|
||||
@@ -293,9 +268,6 @@ class CartListPage extends BaseListPage {
|
||||
sorter: true,
|
||||
render: (text, record) => {
|
||||
if (!text) {return null;}
|
||||
if (record.isInvalid) {
|
||||
return <span style={{color: "red"}}>{text}</span>;
|
||||
}
|
||||
return (
|
||||
<Link to={`/pricings/${owner}/${text}`}>
|
||||
{text}
|
||||
@@ -311,9 +283,6 @@ class CartListPage extends BaseListPage {
|
||||
sorter: true,
|
||||
render: (text, record) => {
|
||||
if (!text) {return null;}
|
||||
if (record.isInvalid) {
|
||||
return <span style={{color: "red"}}>{text}</span>;
|
||||
}
|
||||
return (
|
||||
<Link to={`/plans/${owner}/${text}`}>
|
||||
{text}
|
||||
@@ -328,7 +297,7 @@ class CartListPage extends BaseListPage {
|
||||
width: "100px",
|
||||
sorter: true,
|
||||
render: (text, record) => {
|
||||
const itemKey = `${record.name}-${record.price !== null ? record.price : "null"}-${record.pricingName || ""}-${record.planName || ""}`;
|
||||
const itemKey = `${record.name}-${record.price}-${record.pricingName || ""}-${record.planName || ""}`;
|
||||
const isUpdating = this.state.updatingCartItems?.[itemKey] === true;
|
||||
return (
|
||||
<QuantityStepper
|
||||
@@ -337,7 +306,7 @@ class CartListPage extends BaseListPage {
|
||||
onIncrease={() => this.updateCartItemQuantity(record, text + 1)}
|
||||
onDecrease={() => this.updateCartItemQuantity(record, text - 1)}
|
||||
onChange={null}
|
||||
disabled={isUpdating || record.isInvalid}
|
||||
disabled={isUpdating}
|
||||
/>
|
||||
);
|
||||
},
|
||||
@@ -351,11 +320,7 @@ class CartListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div style={{display: "flex", flexWrap: "wrap", gap: "8px"}}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.props.history.push(`/products/${owner}/${record.name}/buy`)}
|
||||
disabled={record.isInvalid}
|
||||
>
|
||||
<Button type="primary" onClick={() => this.props.history.push(`/products/${owner}/${record.name}/buy`)}>
|
||||
{i18next.t("general:Detail")}
|
||||
</Button>
|
||||
<PopconfirmModal
|
||||
@@ -393,7 +358,7 @@ class CartListPage extends BaseListPage {
|
||||
onConfirm={() => this.clearCart()}
|
||||
disabled={isEmpty}
|
||||
/>
|
||||
<Button type="primary" size="small" onClick={() => this.placeOrder()} disabled={isEmpty || hasInvalidItems || this.state.isPlacingOrder} loading={this.state.isPlacingOrder}>{i18next.t("general:Place Order")}</Button>
|
||||
<Button type="primary" size="small" onClick={() => this.placeOrder()} disabled={isEmpty || this.state.isPlacingOrder} loading={this.state.isPlacingOrder}>{i18next.t("general:Place Order")}</Button>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
@@ -414,7 +379,7 @@ class CartListPage extends BaseListPage {
|
||||
size="large"
|
||||
style={{height: "50px", fontSize: "20px", padding: "0 40px", borderRadius: "5px"}}
|
||||
onClick={() => this.placeOrder()}
|
||||
disabled={hasInvalidItems || this.state.isPlacingOrder}
|
||||
disabled={this.state.isPlacingOrder}
|
||||
loading={this.state.isPlacingOrder}
|
||||
>
|
||||
{i18next.t("general:Place Order")}
|
||||
@@ -439,33 +404,17 @@ class CartListPage extends BaseListPage {
|
||||
ProductBackend.getProduct(organizationName, item.name)
|
||||
.then(pRes => {
|
||||
if (pRes.status === "ok" && pRes.data) {
|
||||
const isCurrencyChanged = item.currency && pRes.data.currency && item.currency !== pRes.data.currency;
|
||||
if (isCurrencyChanged) {
|
||||
Setting.showMessage("warning", i18next.t("product:Product not found or invalid") + `: ${item.name}`);
|
||||
}
|
||||
return {
|
||||
...pRes.data,
|
||||
createdTime: item.createdTime,
|
||||
pricingName: item.pricingName,
|
||||
planName: item.planName,
|
||||
quantity: item.quantity,
|
||||
price: pRes.data.isRecharge ? item.price : pRes.data.price,
|
||||
isInvalid: isCurrencyChanged,
|
||||
};
|
||||
}
|
||||
Setting.showMessage("warning", i18next.t("product:Product not found or invalid") + `: ${item.name}`);
|
||||
return {
|
||||
...item,
|
||||
isInvalid: true,
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
Setting.showMessage("warning", i18next.t("product:Product not found or invalid") + `: ${item.name}`);
|
||||
return {
|
||||
...item,
|
||||
isInvalid: true,
|
||||
};
|
||||
return item;
|
||||
})
|
||||
.catch(() => item)
|
||||
);
|
||||
|
||||
const fullCartData = await Promise.all(productPromises);
|
||||
@@ -483,10 +432,6 @@ class CartListPage extends BaseListPage {
|
||||
const comparison = aValue > bValue ? 1 : -1;
|
||||
return params.sortOrder === "ascend" ? comparison : -comparison;
|
||||
});
|
||||
} else {
|
||||
sortedData.sort((a, b) => {
|
||||
return b.createdTime - a.createdTime;
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -500,11 +445,6 @@ class CartListPage extends BaseListPage {
|
||||
searchText: params.searchText,
|
||||
searchedColumn: params.searchedColumn,
|
||||
});
|
||||
|
||||
const invalidProducts = sortedData.filter(item => item.isInvalid);
|
||||
invalidProducts.forEach(item => {
|
||||
Setting.showMessage("error", i18next.t("product:Product not found or invalid") + `: ${item.name}`);
|
||||
});
|
||||
} else {
|
||||
this.setState({loading: false});
|
||||
Setting.showMessage("error", res.msg);
|
||||
|
||||
@@ -133,7 +133,7 @@ class CertEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("cert:Scope - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.scope} onChange={(value => {
|
||||
@@ -149,7 +149,7 @@ class CertEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.type} onChange={(value => {
|
||||
|
||||
@@ -93,7 +93,7 @@ class FormEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={Setting.isMobile() ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<Select
|
||||
|
||||
@@ -148,7 +148,7 @@ class GroupEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select style={{width: "100%"}}
|
||||
|
||||
@@ -188,7 +188,7 @@ function ManagementPage(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown key="/rightDropDown" menu={{items, onClick}} placement="bottomRight" >
|
||||
<Dropdown key="/rightDropDown" menu={{items, onClick}} >
|
||||
<div className="rightDropDown">
|
||||
{
|
||||
renderAvatar()
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select} from "antd";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
import * as OrderBackend from "./backend/OrderBackend";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
@@ -42,6 +41,7 @@ class OrderEditPage extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getOrder();
|
||||
this.getProducts();
|
||||
this.getUsers();
|
||||
this.getPayments();
|
||||
}
|
||||
|
||||
@@ -72,6 +72,19 @@ class OrderEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getUsers() {
|
||||
UserBackend.getUsers(this.state.organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to get users: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getPayments() {
|
||||
PaymentBackend.getPayments(this.state.organizationName)
|
||||
.then((res) => {
|
||||
@@ -171,24 +184,13 @@ class OrderEditPage extends React.Component {
|
||||
{i18next.t("general:User")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
style={{width: "100%"}}
|
||||
value={this.state.order.user}
|
||||
disabled={isViewMode}
|
||||
allowClear
|
||||
fetchPage={UserBackend.getUsers}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.organizationName, page, pageSize, field, searchText];
|
||||
}}
|
||||
reloadKey={this.state.organizationName}
|
||||
optionMapper={(user) => Setting.getOption(user.name, user.name)}
|
||||
filterOption={false}
|
||||
onChange={(value) => {
|
||||
this.updateOrderField("user", value || "");
|
||||
}}
|
||||
/>
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.order.user} disabled={isViewMode} onChange={(value) => {
|
||||
this.updateOrderField("user", value);
|
||||
}}>
|
||||
{
|
||||
this.state.users?.map((user, index) => <Option key={index} value={user.name}>{user.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
|
||||
@@ -236,13 +236,6 @@ class OrderListPage extends BaseListPage {
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("state"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Tooltip title={record.message || ""}>
|
||||
<span>{text}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
@@ -255,7 +248,7 @@ class OrderListPage extends BaseListPage {
|
||||
return (
|
||||
<div style={{display: "flex", flexWrap: "wrap", gap: "8px"}}>
|
||||
<Button onClick={() => this.props.history.push(`/orders/${record.owner}/${record.name}/pay`)}>
|
||||
{(record.state === "Created" || record.state === "Failed") ? i18next.t("order:Pay") : i18next.t("general:Detail")}
|
||||
{record.state === "Created" ? i18next.t("order:Pay") : i18next.t("general:Detail")}
|
||||
</Button>
|
||||
<Button danger onClick={() => this.cancelOrder(record)} disabled={record.state !== "Created" || !isAdmin}>
|
||||
{i18next.t("general:Cancel")}
|
||||
|
||||
@@ -272,7 +272,7 @@ class OrderPayPage extends React.Component {
|
||||
const updateTimeMap = {
|
||||
Paid: i18next.t("order:Payment time"),
|
||||
Canceled: i18next.t("order:Cancel time"),
|
||||
Failed: i18next.t("order:Payment failed time"),
|
||||
PaymentFailed: i18next.t("order:Payment failed time"),
|
||||
Timeout: i18next.t("order:Timeout time"),
|
||||
};
|
||||
const updateTimeLabel = updateTimeMap[state] || i18next.t("general:Updated time");
|
||||
|
||||
@@ -232,7 +232,7 @@ class PaymentEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.payment.type} onChange={e => {
|
||||
|
||||
@@ -122,7 +122,7 @@ class PaymentResultPage extends React.Component {
|
||||
payment: payment,
|
||||
});
|
||||
if (payment.state === "Created") {
|
||||
if (["PayPal", "Stripe", "AirWallex", "Alipay", "WeChat Pay", "Balance", "Dummy"].includes(payment.type)) {
|
||||
if (["PayPal", "Stripe", "AirWallex", "Alipay", "WeChat Pay", "Balance"].includes(payment.type)) {
|
||||
this.setState({
|
||||
timeout: setTimeout(async() => {
|
||||
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
import * as PermissionBackend from "./backend/PermissionBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
@@ -69,6 +68,9 @@ class PermissionEditPage extends React.Component {
|
||||
permission: permission,
|
||||
});
|
||||
|
||||
this.getUsers(permission.owner);
|
||||
this.getGroups(permission.owner);
|
||||
this.getRoles(permission.owner);
|
||||
this.getModels(permission.owner);
|
||||
this.getResources(permission.owner);
|
||||
this.getModel(permission.model);
|
||||
@@ -84,6 +86,48 @@ class PermissionEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getGroups(organizationName) {
|
||||
GroupBackend.getGroups(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
groups: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
roles: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getModels(organizationName) {
|
||||
ModelBackend.getModels(organizationName)
|
||||
.then((res) => {
|
||||
@@ -167,6 +211,9 @@ class PermissionEditPage extends React.Component {
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.permission.owner} onChange={(owner => {
|
||||
this.updatePermissionField("owner", owner);
|
||||
this.getUsers(owner);
|
||||
this.getGroups(owner);
|
||||
this.getRoles(owner);
|
||||
this.getModels(owner);
|
||||
this.getResources(owner);
|
||||
})}
|
||||
@@ -221,35 +268,12 @@ class PermissionEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.permission.users}
|
||||
allowClear
|
||||
fetchPage={async(...args) => {
|
||||
const res = await UserBackend.getUsers(...args);
|
||||
if (res.status !== "ok") {
|
||||
return res;
|
||||
}
|
||||
const data = res.data.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`));
|
||||
if (args?.[1] === 1 && Array.isArray(res?.data)) {
|
||||
res.data = [
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...data,
|
||||
];
|
||||
} else {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.permission.owner, page, pageSize, field, searchText];
|
||||
}}
|
||||
reloadKey={this.state.permission?.owner}
|
||||
filterOption={false}
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.users}
|
||||
onChange={(value => {this.updatePermissionField("users", value);})}
|
||||
options={[
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`)),
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -258,35 +282,12 @@ class PermissionEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub groups"), i18next.t("role:Sub groups - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.permission.groups}
|
||||
allowClear
|
||||
fetchPage={async(...args) => {
|
||||
const res = await GroupBackend.getGroups(...args);
|
||||
if (res.status !== "ok") {
|
||||
return res;
|
||||
}
|
||||
const data = res.data.map((group) => Setting.getOption(`${group.owner}/${group.name}`, `${group.owner}/${group.name}`));
|
||||
if (args?.[2] === 1 && Array.isArray(res?.data)) {
|
||||
res.data = [
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...data,
|
||||
];
|
||||
} else {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.permission.owner, false, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={this.state.permission?.owner}
|
||||
filterOption={false}
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.groups}
|
||||
onChange={(value => {this.updatePermissionField("groups", value);})}
|
||||
options={[
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...this.state.groups.map((group) => Setting.getOption(`${group.owner}/${group.name}`, `${group.owner}/${group.name}`)),
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -295,37 +296,12 @@ class PermissionEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.permission.roles}
|
||||
disabled={!this.hasRoleDefinition(this.state.model)}
|
||||
allowClear
|
||||
fetchPage={async(...args) => {
|
||||
const res = await RoleBackend.getRoles(...args);
|
||||
if (res.status !== "ok") {
|
||||
return res;
|
||||
}
|
||||
const data = res.data.map((role) => Setting.getOption(`${role.owner}/${role.name}`, `${role.owner}/${role.name}`));
|
||||
if (args?.[1] === 1 && Array.isArray(res?.data)) {
|
||||
// res.data = [{owner: i18next.t("general:All"), name: "*"}, ...res.data];
|
||||
res.data = [
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...data,
|
||||
];
|
||||
} else {
|
||||
res.data = data;
|
||||
}
|
||||
return res;
|
||||
}}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.permission.owner, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={this.state.permission?.owner}
|
||||
filterOption={false}
|
||||
<Select disabled={!this.hasRoleDefinition(this.state.model)} placeholder={this.hasRoleDefinition(this.state.model) ? "" : "This field is disabled because the model is empty or it doesn't support RBAC (in another word, doesn't contain [role_definition])"} virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.roles}
|
||||
onChange={(value => {this.updatePermissionField("roles", value);})}
|
||||
options={[
|
||||
Setting.getOption(i18next.t("general:All"), "*"),
|
||||
...this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission) => Setting.getOption(`${permission.owner}/${permission.name}`, `${permission.owner}/${permission.name}`)),
|
||||
]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as RoleBackend from "./backend/RoleBackend";
|
||||
import * as PlanBackend from "./backend/PlanBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
@@ -57,10 +57,40 @@ class PlanEditPage extends React.Component {
|
||||
plan: res.data,
|
||||
});
|
||||
|
||||
this.getUsers(this.state.organizationName);
|
||||
this.getRoles(this.state.organizationName);
|
||||
this.getPaymentProviders(this.state.organizationName);
|
||||
});
|
||||
}
|
||||
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
roles: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPaymentProviders(organizationName) {
|
||||
ProviderBackend.getProviders(organizationName)
|
||||
.then((res) => {
|
||||
@@ -121,6 +151,8 @@ class PlanEditPage extends React.Component {
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.plan.owner} disabled={isViewMode} onChange={(owner => {
|
||||
this.updatePlanField("owner", owner);
|
||||
this.getUsers(owner);
|
||||
this.getRoles(owner);
|
||||
this.getPaymentProviders(owner);
|
||||
})}
|
||||
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
|
||||
@@ -152,22 +184,9 @@ class PlanEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Role"), i18next.t("general:Role - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
style={{width: "100%"}}
|
||||
value={this.state.plan.role}
|
||||
disabled={isViewMode}
|
||||
allowClear
|
||||
fetchPage={RoleBackend.getRoles}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.plan.owner, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={this.state.plan.owner}
|
||||
optionMapper={(role) => Setting.getOption(role.name, role.name)}
|
||||
filterOption={false}
|
||||
onChange={(value => {this.updatePlanField("role", value || "");})}
|
||||
/>
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.plan.role} disabled={isViewMode} onChange={(value => {this.updatePlanField("role", value);})}
|
||||
options={this.state.roles.map((role) => Setting.getOption(role.name, role.name))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
import React from "react";
|
||||
import {Button, Descriptions, Divider, InputNumber, Radio, Space, Spin, Typography} from "antd";
|
||||
import moment from "moment";
|
||||
import i18next from "i18next";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import * as PlanBackend from "./backend/PlanBackend";
|
||||
@@ -194,13 +193,7 @@ class ProductBuyPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const cartPrice = product.isRecharge ? actualPrice : null;
|
||||
const existingItemIndex = cart.findIndex(item =>
|
||||
item.name === product.name &&
|
||||
(product.isRecharge ? item.price === actualPrice : true) &&
|
||||
(item.pricingName || "") === pricingName &&
|
||||
(item.planName || "") === planName
|
||||
);
|
||||
const existingItemIndex = cart.findIndex(item => item.name === product.name && item.price === actualPrice && (item.pricingName || "") === pricingName && (item.planName || "") === planName);
|
||||
const quantityToAdd = this.state.buyQuantity;
|
||||
|
||||
if (existingItemIndex !== -1) {
|
||||
@@ -208,8 +201,7 @@ class ProductBuyPage extends React.Component {
|
||||
} else {
|
||||
const newProductInfo = {
|
||||
name: product.name,
|
||||
createdTime: moment().format(),
|
||||
price: cartPrice,
|
||||
price: actualPrice,
|
||||
currency: product.currency,
|
||||
pricingName: pricingName,
|
||||
planName: planName,
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Row, Tag, Typography} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
@@ -23,6 +22,8 @@ import {FloatingCartButton, QuantityStepper} from "./common/product/CartControls
|
||||
|
||||
const {Text, Title} = Typography;
|
||||
|
||||
const MAX_DISPLAYED_RECHARGE_OPTIONS = 3;
|
||||
|
||||
class ProductStorePage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -127,13 +128,7 @@ class ProductStorePage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (product.isRecharge) {
|
||||
Setting.showMessage("error", i18next.t("product:Recharge products need to go to the product detail page to set custom amount"));
|
||||
this.setState(prevState => ({addingToCartProducts: prevState.addingToCartProducts.filter(name => name !== product.name)}));
|
||||
return;
|
||||
}
|
||||
|
||||
const existingItemIndex = cart.findIndex(item => item.name === product.name);
|
||||
const existingItemIndex = cart.findIndex(item => item.name === product.name && item.price === product.price);
|
||||
const quantityToAdd = this.state.productQuantities[product.name] || 1;
|
||||
|
||||
if (existingItemIndex !== -1) {
|
||||
@@ -141,7 +136,7 @@ class ProductStorePage extends React.Component {
|
||||
} else {
|
||||
const newCartProductInfo = {
|
||||
name: product.name,
|
||||
createdTime: moment().format(),
|
||||
price: product.price,
|
||||
currency: product.currency,
|
||||
pricingName: "",
|
||||
planName: "",
|
||||
@@ -280,15 +275,17 @@ class ProductStorePage extends React.Component {
|
||||
<Text type="secondary" style={{fontSize: "13px", display: "block", marginBottom: 4}}>
|
||||
{i18next.t("product:Recharge options")}:
|
||||
</Text>
|
||||
<div style={{display: "flex", flexWrap: "wrap", gap: "4px", alignItems: "center"}}>
|
||||
{product.rechargeOptions.map((amount, index) => (
|
||||
<Tag key={amount} color="blue" style={{fontSize: "14px", fontWeight: 600, margin: 0}}>
|
||||
<div style={{display: "flex", flexWrap: "wrap", gap: "4px"}}>
|
||||
{product.rechargeOptions.slice(0, MAX_DISPLAYED_RECHARGE_OPTIONS).map((amount, index) => (
|
||||
<Tag key={index} color="blue" style={{fontSize: "14px", fontWeight: 600, margin: 0}}>
|
||||
{Setting.getCurrencySymbol(product.currency)}{amount}
|
||||
</Tag>
|
||||
))}
|
||||
<Text type="secondary" style={{fontSize: "13px", marginLeft: 8}}>
|
||||
{Setting.getCurrencyWithFlag(product.currency)}
|
||||
</Text>
|
||||
{product.rechargeOptions.length > MAX_DISPLAYED_RECHARGE_OPTIONS && (
|
||||
<Tag color="blue" style={{fontSize: "14px", fontWeight: 600, margin: 0}}>
|
||||
+{product.rechargeOptions.length - MAX_DISPLAYED_RECHARGE_OPTIONS}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -297,23 +294,13 @@ class ProductStorePage extends React.Component {
|
||||
<Text strong style={{fontSize: "16px", color: "#1890ff"}}>
|
||||
{i18next.t("product:Custom amount available")}
|
||||
</Text>
|
||||
{(!product.rechargeOptions || product.rechargeOptions.length === 0) && (
|
||||
<Text type="secondary" style={{fontSize: "13px", marginLeft: 8}}>
|
||||
{Setting.getCurrencyWithFlag(product.currency)}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{(!product.rechargeOptions || product.rechargeOptions.length === 0) && product.disableCustomRecharge === true && (
|
||||
<div style={{marginBottom: 8}}>
|
||||
<Text type="secondary" style={{fontSize: "13px", display: "block", marginBottom: 4}}>
|
||||
{i18next.t("product:No recharge options available")}
|
||||
</Text>
|
||||
<Text type="secondary" style={{fontSize: "13px"}}>
|
||||
{Setting.getCurrencyWithFlag(product.currency)}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Text type="secondary" style={{fontSize: "13px"}}>
|
||||
{Setting.getCurrencyWithFlag(product.currency)}
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -687,7 +687,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Category"), i18next.t("general:Category - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.category} onChange={(value => {
|
||||
@@ -698,7 +698,7 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("type", "Default");
|
||||
this.updateProviderField("host", "smtp.example.com");
|
||||
this.updateProviderField("port", 465);
|
||||
this.updateProviderField("sslMode", "Auto");
|
||||
this.updateProviderField("disableSsl", false);
|
||||
this.updateProviderField("title", "Casdoor Verification Code");
|
||||
this.updateProviderField("content", Setting.getDefaultHtmlEmailContent());
|
||||
this.updateProviderField("metadata", Setting.getDefaultInvitationHtmlEmailContent());
|
||||
@@ -751,7 +751,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} showSearch value={this.state.provider.type} onChange={(value => {
|
||||
@@ -823,19 +823,6 @@ class ProviderEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.scopes} onChange={value => {
|
||||
this.updateProviderField("scopes", value);
|
||||
}}>
|
||||
<Option key="snsapi_userinfo" value="snsapi_userinfo">snsapi_userinfo</Option>
|
||||
<Option key="snsapi_privateinfo" value="snsapi_privateinfo">snsapi_privateinfo</Option>
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Use id as name"), i18next.t("provider:Use id as name - Tooltip"))} :
|
||||
@@ -893,7 +880,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))}
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("cert:Scope - Tooltip"))}
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.scopes} onChange={e => {
|
||||
@@ -1294,16 +1281,12 @@ class ProviderEditPage extends React.Component {
|
||||
{["Azure ACS", "SendGrid"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:SSL mode"), i18next.t("provider:SSL mode - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("provider:Disable SSL"), i18next.t("provider:Disable SSL - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "200px"}} value={this.state.provider.sslMode || "Auto"} onChange={value => {
|
||||
this.updateProviderField("sslMode", value);
|
||||
}}>
|
||||
<Option value="Auto">{i18next.t("general:Auto")}</Option>
|
||||
<Option value="Enable">{i18next.t("general:Enable")}</Option>
|
||||
<Option value="Disable">{i18next.t("general:Disable")}</Option>
|
||||
</Select>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.provider.disableSsl} onChange={checked => {
|
||||
this.updateProviderField("disableSsl", checked);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
@@ -139,7 +139,7 @@ class ProviderListPage extends BaseListPage {
|
||||
...this.getColumnSearchProps("displayName"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Category"),
|
||||
title: i18next.t("provider:Category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
filterMultiple: false,
|
||||
|
||||
@@ -20,7 +20,6 @@ import * as GroupBackend from "./backend/GroupBackend";
|
||||
import * as RoleBackend from "./backend/RoleBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
|
||||
class RoleEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -31,6 +30,9 @@ class RoleEditPage extends React.Component {
|
||||
roleName: decodeURIComponent(props.match.params.roleName),
|
||||
role: null,
|
||||
organizations: [],
|
||||
users: [],
|
||||
groups: [],
|
||||
roles: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
@@ -55,6 +57,10 @@ class RoleEditPage extends React.Component {
|
||||
this.setState({
|
||||
role: res.data,
|
||||
});
|
||||
|
||||
this.getUsers(this.state.organizationName);
|
||||
this.getGroups(this.state.organizationName);
|
||||
this.getRoles(this.state.organizationName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,6 +73,48 @@ class RoleEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getGroups(organizationName) {
|
||||
GroupBackend.getGroups(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
groups: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
roles: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parseRoleField(key, value) {
|
||||
if ([""].includes(key)) {
|
||||
value = Setting.myParseInt(value);
|
||||
@@ -139,20 +187,9 @@ class RoleEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.role.users}
|
||||
fetchPage={UserBackend.getUsers}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.role.owner, page, pageSize, field, searchText];
|
||||
}}
|
||||
reloadKey={this.state.role.owner}
|
||||
optionMapper={(user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`)}
|
||||
filterOption={false}
|
||||
<Select virtual={true} mode="multiple" style={{width: "100%"}} value={this.state.role.users}
|
||||
onChange={(value => {this.updateRoleField("users", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -161,19 +198,9 @@ class RoleEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub groups"), i18next.t("role:Sub groups - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.role.groups}
|
||||
fetchPage={GroupBackend.getGroups}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.role.owner, false, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={this.state.role.owner}
|
||||
optionMapper={(group) => Setting.getOption(`${group.owner}/${group.name}`, `${group.owner}/${group.name}`)}
|
||||
filterOption={false}
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.role.groups}
|
||||
onChange={(value => {this.updateRoleField("groups", value);})}
|
||||
options={this.state.groups.map((group) => Setting.getOption(`${group.owner}/${group.name}`, `${group.owner}/${group.name}`))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -182,25 +209,9 @@ class RoleEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
mode="multiple"
|
||||
style={{width: "100%"}}
|
||||
value={this.state.role.roles}
|
||||
fetchPage={RoleBackend.getRoles}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.role.owner, page, pageSize, field, searchText, "", ""];
|
||||
}}
|
||||
reloadKey={`${this.state.role.owner}/${this.state.role.name}`}
|
||||
optionMapper={(role) => {
|
||||
if (role.owner === this.state.role.owner && role.name === this.state.role.name) {
|
||||
return null;
|
||||
}
|
||||
return Setting.getOption(`${role.owner}/${role.name}`, `${role.owner}/${role.name}`);
|
||||
}}
|
||||
filterOption={false}
|
||||
onChange={(value => {this.updateRoleField("roles", value);})}
|
||||
/>
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.role.roles} onChange={(value => {this.updateRoleField("roles", value);})}
|
||||
options={this.state.roles.filter(role => (role.owner !== this.state.role.owner || role.name !== this.state.role.name)).map((role) => Setting.getOption(`${role.owner}/${role.name}`, `${role.owner}/${role.name}`))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
|
||||
@@ -34,9 +34,6 @@ export const ServerUrl = "";
|
||||
|
||||
export const StaticBaseUrl = "https://cdn.casbin.org";
|
||||
|
||||
export const MAX_PAGE_SIZE = 25;
|
||||
export const SEARCH_DEBOUNCE_MS = 300;
|
||||
|
||||
export const Countries = [
|
||||
{label: "English", key: "en", country: "US", alt: "English"},
|
||||
{label: "Español", key: "es", country: "ES", alt: "Español"},
|
||||
@@ -289,11 +286,11 @@ export const OtherProviderInfo = {
|
||||
url: "https://fastspring.com/",
|
||||
},
|
||||
"Lemon Squeezy": {
|
||||
logo: `${StaticBaseUrl}/img/payment_lemonsqueezy.jpg`,
|
||||
logo: `${StaticBaseUrl}/img/payment_lemonsqueezy.png`,
|
||||
url: "https://www.lemonsqueezy.com/",
|
||||
},
|
||||
"Adyen": {
|
||||
logo: `${StaticBaseUrl}/img/payment_adyen.svg`,
|
||||
logo: `${StaticBaseUrl}/img/payment_adyen.png`,
|
||||
url: "https://www.adyen.com/",
|
||||
},
|
||||
},
|
||||
@@ -517,7 +514,6 @@ export const GetTranslatedUserItems = () => {
|
||||
{name: "Balance", label: i18next.t("user:Balance")},
|
||||
{name: "Balance currency", label: i18next.t("organization:Balance currency")},
|
||||
{name: "Balance credit", label: i18next.t("organization:Balance credit")},
|
||||
{name: "Cart", label: i18next.t("general:Cart")},
|
||||
{name: "Transactions", label: i18next.t("general:Transactions")},
|
||||
{name: "Score", label: i18next.t("user:Score")},
|
||||
{name: "Karma", label: i18next.t("user:Karma")},
|
||||
@@ -2267,7 +2263,7 @@ export function getFormTypeItems(formType) {
|
||||
{name: "owner", label: "general:Organization", visible: true, width: "150"},
|
||||
{name: "createdTime", label: "general:Created time", visible: true, width: "180"},
|
||||
{name: "displayName", label: "general:Display name", visible: true, width: "150"},
|
||||
{name: "category", label: "general:Category", visible: true, width: "110"},
|
||||
{name: "category", label: "provider:Category", visible: true, width: "110"},
|
||||
{name: "type", label: "general:Type", visible: true, width: "110"},
|
||||
{name: "clientId", label: "provider:Client ID", visible: true, width: "100"},
|
||||
{name: "providerUrl", label: "provider:Provider URL", visible: true, width: "150"},
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import {Button, Card, Col, DatePicker, Input, Row, Select} from "antd";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as PricingBackend from "./backend/PricingBackend";
|
||||
import * as PlanBackend from "./backend/PlanBackend";
|
||||
@@ -64,6 +63,7 @@ class SubscriptionEditPage extends React.Component {
|
||||
subscription: res.data,
|
||||
});
|
||||
|
||||
this.getUsers(this.state.organizationName);
|
||||
this.getPricings(this.state.organizationName);
|
||||
this.getPlans(this.state.organizationName);
|
||||
});
|
||||
@@ -87,6 +87,20 @@ class SubscriptionEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getOrganizations() {
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
.then((res) => {
|
||||
@@ -133,6 +147,7 @@ class SubscriptionEditPage extends React.Component {
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.subscription.owner} disabled={isViewMode} onChange={(owner => {
|
||||
this.updateSubscriptionField("owner", owner);
|
||||
this.getUsers(owner);
|
||||
this.getPlans(owner);
|
||||
})}
|
||||
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
|
||||
@@ -202,21 +217,10 @@ class SubscriptionEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PaginateSelect
|
||||
virtual
|
||||
style={{width: "100%"}}
|
||||
value={this.state.subscription.user}
|
||||
<Select style={{width: "100%"}} value={this.state.subscription.user}
|
||||
disabled={isViewMode}
|
||||
allowClear
|
||||
fetchPage={UserBackend.getUsers}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.subscription.owner, page, pageSize, field, searchText];
|
||||
}}
|
||||
reloadKey={this.state.subscription?.owner}
|
||||
optionMapper={(user) => Setting.getOption(user.name, user.name)}
|
||||
filterOption={false}
|
||||
onChange={(value => {this.updateSubscriptionField("user", value || "");})}
|
||||
onChange={(value => {this.updateSubscriptionField("user", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(user.name, user.name))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -710,79 +710,6 @@ class SyncerEditPage extends React.Component {
|
||||
"values": [],
|
||||
},
|
||||
];
|
||||
case "AWS IAM":
|
||||
return [
|
||||
{
|
||||
"name": "UserId",
|
||||
"type": "string",
|
||||
"casdoorName": "Id",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "UserName",
|
||||
"type": "string",
|
||||
"casdoorName": "Name",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "UserName",
|
||||
"type": "string",
|
||||
"casdoorName": "DisplayName",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.Email",
|
||||
"type": "string",
|
||||
"casdoorName": "Email",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.Phone",
|
||||
"type": "string",
|
||||
"casdoorName": "Phone",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.FirstName",
|
||||
"type": "string",
|
||||
"casdoorName": "FirstName",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.LastName",
|
||||
"type": "string",
|
||||
"casdoorName": "LastName",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.Title",
|
||||
"type": "string",
|
||||
"casdoorName": "Title",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "Tags.Department",
|
||||
"type": "string",
|
||||
"casdoorName": "Affiliation",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
{
|
||||
"name": "CreateDate",
|
||||
"type": "string",
|
||||
"casdoorName": "CreatedTime",
|
||||
"isHashed": true,
|
||||
"values": [],
|
||||
},
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
@@ -826,7 +753,7 @@ class SyncerEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.syncer.type} onChange={(value => {
|
||||
@@ -839,14 +766,14 @@ class SyncerEditPage extends React.Component {
|
||||
});
|
||||
})}>
|
||||
{
|
||||
["Database", "Keycloak", "WeCom", "Azure AD", "Active Directory", "Google Workspace", "DingTalk", "Lark", "Okta", "SCIM", "AWS IAM"]
|
||||
["Database", "Keycloak", "WeCom", "Azure AD", "Active Directory", "Google Workspace", "DingTalk", "Lark", "Okta", "SCIM"]
|
||||
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Active Directory" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" || this.state.syncer.type === "AWS IAM" ? null : (
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Active Directory" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("syncer:Database type"), i18next.t("syncer:Database type - Tooltip"))} :
|
||||
@@ -878,7 +805,7 @@ class SyncerEditPage extends React.Component {
|
||||
this.state.syncer.databaseType !== "postgres" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:SSL mode"), i18next.t("provider:SSL mode - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("syncer:SSL mode"), i18next.t("syncer:SSL mode - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.syncer.sslMode} onChange={(value => {this.updateSyncerField("sslMode", value);})}>
|
||||
@@ -901,7 +828,7 @@ class SyncerEditPage extends React.Component {
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(this.state.syncer.type === "Azure AD" ? i18next.t("provider:Tenant ID") : this.state.syncer.type === "Google Workspace" ? i18next.t("syncer:Admin Email") : this.state.syncer.type === "Active Directory" ? i18next.t("ldap:Server") : this.state.syncer.type === "SCIM" ? i18next.t("syncer:SCIM Server URL") : this.state.syncer.type === "AWS IAM" ? i18next.t("syncer:AWS Region") : i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
||||
{Setting.getLabel(this.state.syncer.type === "Azure AD" ? i18next.t("provider:Tenant ID") : this.state.syncer.type === "Google Workspace" ? i18next.t("syncer:Admin Email") : this.state.syncer.type === "Active Directory" ? i18next.t("ldap:Server") : this.state.syncer.type === "SCIM" ? i18next.t("syncer:SCIM Server URL") : i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input prefix={<LinkOutlined />} value={this.state.syncer.host} onChange={e => {
|
||||
@@ -912,7 +839,7 @@ class SyncerEditPage extends React.Component {
|
||||
)
|
||||
}
|
||||
{
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" || this.state.syncer.type === "AWS IAM" ? null : (
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(this.state.syncer.type === "Active Directory" ? i18next.t("provider:LDAP port") : i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
||||
@@ -936,8 +863,7 @@ class SyncerEditPage extends React.Component {
|
||||
this.state.syncer.type === "Azure AD" ? i18next.t("provider:Client ID") :
|
||||
this.state.syncer.type === "Active Directory" ? i18next.t("syncer:Bind DN") :
|
||||
this.state.syncer.type === "SCIM" ? i18next.t("syncer:Username (optional)") :
|
||||
this.state.syncer.type === "AWS IAM" ? i18next.t("syncer:AWS Access Key ID") :
|
||||
i18next.t("general:User"),
|
||||
i18next.t("general:User"),
|
||||
i18next.t("general:User - Tooltip")
|
||||
)} :
|
||||
</Col>
|
||||
@@ -958,8 +884,7 @@ class SyncerEditPage extends React.Component {
|
||||
this.state.syncer.type === "Azure AD" ? i18next.t("provider:Client secret") :
|
||||
this.state.syncer.type === "Google Workspace" ? i18next.t("syncer:Service account key") :
|
||||
this.state.syncer.type === "SCIM" ? i18next.t("syncer:API Token / Password") :
|
||||
this.state.syncer.type === "AWS IAM" ? i18next.t("syncer:AWS Secret Access Key") :
|
||||
i18next.t("general:Password"),
|
||||
i18next.t("general:Password"),
|
||||
i18next.t("general:Password - Tooltip")
|
||||
)} :
|
||||
</Col>
|
||||
@@ -978,7 +903,7 @@ class SyncerEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" || this.state.syncer.type === "AWS IAM" ? null : (
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(this.state.syncer.type === "Active Directory" ? i18next.t("ldap:Base DN") : i18next.t("syncer:Database"), i18next.t("syncer:Database - Tooltip"))} :
|
||||
@@ -1074,7 +999,7 @@ class SyncerEditPage extends React.Component {
|
||||
) : null
|
||||
}
|
||||
{
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" || this.state.syncer.type === "AWS IAM" ? null : (
|
||||
this.state.syncer.type === "WeCom" || this.state.syncer.type === "Azure AD" || this.state.syncer.type === "Google Workspace" || this.state.syncer.type === "DingTalk" || this.state.syncer.type === "Lark" || this.state.syncer.type === "Okta" || this.state.syncer.type === "SCIM" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("syncer:Table"), i18next.t("syncer:Table - Tooltip"))} :
|
||||
|
||||
@@ -158,7 +158,7 @@ class TokenEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))}
|
||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("cert:Scope - Tooltip"))}
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.token.scope} onChange={e => {
|
||||
|
||||
@@ -19,7 +19,6 @@ import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
|
||||
import PaginateSelect from "./common/PaginateSelect";
|
||||
import i18next from "i18next";
|
||||
|
||||
const {Option} = Select;
|
||||
@@ -44,6 +43,7 @@ class TransactionEditPage extends React.Component {
|
||||
if (this.state.mode === "recharge") {
|
||||
this.getOrganizations();
|
||||
this.getApplications(this.state.organizationName);
|
||||
this.getUsers(this.state.organizationName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,19 @@ class TransactionEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
const targetOrganizationName = organizationName || this.state.organizationName;
|
||||
UserBackend.getUsers(targetOrganizationName)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
users: res.data || [],
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
submitTransactionEdit(exitAfterSave) {
|
||||
if (this.state.transaction === null) {
|
||||
return;
|
||||
@@ -192,6 +205,7 @@ class TransactionEditPage extends React.Component {
|
||||
this.updateTransactionField("owner", value);
|
||||
this.updateTransactionField("application", "");
|
||||
this.getApplications(value);
|
||||
this.getUsers(value);
|
||||
}}>
|
||||
{
|
||||
this.state.organizations.map((org, index) => <Option key={index} value={org.name}>{org.name}</Option>)
|
||||
@@ -261,7 +275,7 @@ class TransactionEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Category"), i18next.t("general:Category - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.transaction.category} />
|
||||
@@ -269,7 +283,7 @@ class TransactionEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("cert:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.transaction.type} onChange={e => {
|
||||
@@ -326,24 +340,17 @@ class TransactionEditPage extends React.Component {
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{isRechargeMode ? (
|
||||
<PaginateSelect
|
||||
virtual
|
||||
style={{width: "100%"}}
|
||||
<Select virtual={false} style={{width: "100%"}}
|
||||
value={this.state.transaction.user}
|
||||
disabled={this.state.transaction.tag === "Organization"}
|
||||
allowClear
|
||||
fetchPage={UserBackend.getUsers}
|
||||
buildFetchArgs={({page, pageSize, searchText}) => {
|
||||
const field = searchText ? "name" : "";
|
||||
return [this.state.transaction?.organization || this.state.organizationName, page, pageSize, field, searchText];
|
||||
}}
|
||||
reloadKey={this.state.transaction?.organization || this.state.organizationName}
|
||||
optionMapper={(user) => Setting.getOption(user.name, user.name)}
|
||||
filterOption={false}
|
||||
onChange={(value) => {
|
||||
this.updateTransactionField("user", value || "");
|
||||
}}
|
||||
/>
|
||||
}}>
|
||||
{
|
||||
this.state.users.map((user, index) => <Option key={index} value={user.name}>{user.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
) : (
|
||||
<Input disabled={true} value={this.state.transaction.user} onChange={e => {
|
||||
}} />
|
||||
|
||||
@@ -48,7 +48,6 @@ import FaceIdTable from "./table/FaceIdTable";
|
||||
import MfaAccountTable from "./table/MfaAccountTable";
|
||||
import MfaTable from "./table/MfaTable";
|
||||
import TransactionTable from "./table/TransactionTable";
|
||||
import CartTable from "./table/CartTable";
|
||||
import * as TransactionBackend from "./backend/TransactionBackend";
|
||||
import {Content, Header} from "antd/es/layout/layout";
|
||||
import Sider from "antd/es/layout/Sider";
|
||||
@@ -862,17 +861,6 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Cart") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Cart"), i18next.t("general:Cart"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<CartTable cart={this.state.user.cart} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Transactions") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
|
||||
@@ -439,10 +439,6 @@ export function getAuthUrl(application, provider, method, code) {
|
||||
const redirectOrigin = application.forcedRedirectOrigin ? application.forcedRedirectOrigin : window.location.origin;
|
||||
let redirectUri = `${redirectOrigin}/callback`;
|
||||
let scope = authInfo[type].scope;
|
||||
// Allow provider.scopes to override default scope if specified
|
||||
if (provider.scopes && provider.scopes.trim() !== "") {
|
||||
scope = provider.scopes;
|
||||
}
|
||||
const isShortState = (provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger")) || (provider.type === "Twitter");
|
||||
let applicationName = application.name;
|
||||
if (application?.isShared) {
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Select, Spin} from "antd";
|
||||
import * as Setting from "../Setting";
|
||||
|
||||
const SCROLL_BOTTOM_OFFSET = 20;
|
||||
|
||||
const defaultOptionMapper = (item) => {
|
||||
if (item === null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof item === "string") {
|
||||
return Setting.getOption(item, item);
|
||||
}
|
||||
const value = item.value ?? item.name ?? item.id ?? item.key;
|
||||
const label = item.label ?? item.displayName ?? value;
|
||||
if (value === undefined) {
|
||||
return null;
|
||||
}
|
||||
return Setting.getOption(label, value);
|
||||
};
|
||||
|
||||
function PaginateSelect(props) {
|
||||
const {
|
||||
fetchPage,
|
||||
buildFetchArgs,
|
||||
optionMapper = defaultOptionMapper,
|
||||
pageSize = Setting.MAX_PAGE_SIZE,
|
||||
debounceMs = Setting.SEARCH_DEBOUNCE_MS,
|
||||
onError,
|
||||
onSearch: onSearchProp,
|
||||
onPopupScroll: onPopupScrollProp,
|
||||
showSearch = true,
|
||||
filterOption = false,
|
||||
notFoundContent,
|
||||
loading: selectLoading,
|
||||
dropdownMatchSelectWidth = false,
|
||||
virtual = false,
|
||||
reloadKey,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const [options, setOptions] = React.useState([]);
|
||||
const [hasMore, setHasMore] = React.useState(true);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const debounceRef = React.useRef(null);
|
||||
const latestSearchRef = React.useRef("");
|
||||
const loadingRef = React.useRef(false);
|
||||
const requestIdRef = React.useRef(0);
|
||||
const pageRef = React.useRef(0);
|
||||
const fetchPageRef = React.useRef(fetchPage);
|
||||
const buildFetchArgsRef = React.useRef(buildFetchArgs);
|
||||
const optionMapperRef = React.useRef(optionMapper ?? defaultOptionMapper);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchPageRef.current = fetchPage;
|
||||
}, [fetchPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
buildFetchArgsRef.current = buildFetchArgs;
|
||||
}, [buildFetchArgs]);
|
||||
|
||||
React.useEffect(() => {
|
||||
optionMapperRef.current = optionMapper ?? defaultOptionMapper;
|
||||
}, [optionMapper]);
|
||||
|
||||
const handleError = React.useCallback((error) => {
|
||||
if (onError) {
|
||||
onError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Setting?.showMessage) {
|
||||
Setting.showMessage("error", error?.message ?? String(error));
|
||||
}
|
||||
}, [onError]);
|
||||
|
||||
const extractItems = React.useCallback((response) => {
|
||||
if (Array.isArray(response)) {
|
||||
return response;
|
||||
}
|
||||
if (Array.isArray(response?.items)) {
|
||||
return response.items;
|
||||
}
|
||||
if (Array.isArray(response?.data)) {
|
||||
return response.data;
|
||||
}
|
||||
if (Array.isArray(response?.list)) {
|
||||
return response.list;
|
||||
}
|
||||
return [];
|
||||
}, []);
|
||||
|
||||
const mergeOptions = React.useCallback((prev, next, reset) => {
|
||||
if (reset) {
|
||||
return next;
|
||||
}
|
||||
|
||||
const merged = [...prev];
|
||||
const indexByValue = new Map();
|
||||
merged.forEach((opt, idx) => {
|
||||
if (opt?.value !== undefined) {
|
||||
indexByValue.set(opt.value, idx);
|
||||
}
|
||||
});
|
||||
|
||||
next.forEach((opt) => {
|
||||
if (!opt) {
|
||||
return;
|
||||
}
|
||||
const optionValue = opt.value;
|
||||
if (optionValue === undefined) {
|
||||
merged.push(opt);
|
||||
return;
|
||||
}
|
||||
if (indexByValue.has(optionValue)) {
|
||||
merged[indexByValue.get(optionValue)] = opt;
|
||||
return;
|
||||
}
|
||||
indexByValue.set(optionValue, merged.length);
|
||||
merged.push(opt);
|
||||
});
|
||||
|
||||
return merged;
|
||||
}, []);
|
||||
|
||||
const loadPage = React.useCallback(async({pageToLoad = 1, reset = false, search = latestSearchRef.current} = {}) => {
|
||||
const fetcher = fetchPageRef.current;
|
||||
if (typeof fetcher !== "function") {
|
||||
return;
|
||||
}
|
||||
if (loadingRef.current && !reset) {
|
||||
return;
|
||||
}
|
||||
if (reset) {
|
||||
loadingRef.current = false;
|
||||
}
|
||||
|
||||
const currentRequestId = requestIdRef.current + 1;
|
||||
requestIdRef.current = currentRequestId;
|
||||
|
||||
loadingRef.current = true;
|
||||
setLoading(true);
|
||||
|
||||
const defaultArgsObject = {
|
||||
page: pageToLoad,
|
||||
pageSize,
|
||||
search,
|
||||
searchText: search,
|
||||
query: search,
|
||||
};
|
||||
|
||||
try {
|
||||
const argsBuilder = buildFetchArgsRef.current;
|
||||
const builtArgs = argsBuilder ? argsBuilder({
|
||||
page: pageToLoad,
|
||||
pageSize,
|
||||
searchText: search,
|
||||
}) : defaultArgsObject;
|
||||
|
||||
const payload = Array.isArray(builtArgs) ?
|
||||
await fetcher(...builtArgs) :
|
||||
await fetcher(builtArgs ?? defaultArgsObject);
|
||||
|
||||
if (currentRequestId !== requestIdRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload?.status && payload.status !== "ok") {
|
||||
handleError(payload?.msg ?? payload?.error ?? "Request failed");
|
||||
setHasMore(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const items = extractItems(payload);
|
||||
const mapper = optionMapperRef.current ?? defaultOptionMapper;
|
||||
const mappedOptions = items.map(mapper).filter(Boolean);
|
||||
setOptions((prev) => mergeOptions(prev, mappedOptions, reset));
|
||||
pageRef.current = pageToLoad;
|
||||
|
||||
const hasMoreFromPayload = typeof payload?.hasMore === "boolean" ? payload.hasMore : null;
|
||||
const hasMoreFromTotal = typeof payload?.total === "number" ? (pageToLoad * pageSize < payload.total) : null;
|
||||
const fallbackHasMore = mappedOptions.length === pageSize;
|
||||
setHasMore(hasMoreFromPayload ?? hasMoreFromTotal ?? fallbackHasMore);
|
||||
} catch (error) {
|
||||
if (currentRequestId === requestIdRef.current) {
|
||||
handleError(error);
|
||||
}
|
||||
} finally {
|
||||
if (currentRequestId === requestIdRef.current) {
|
||||
loadingRef.current = false;
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [pageSize, extractItems, mergeOptions, handleError]);
|
||||
|
||||
const resetAndLoad = React.useCallback((search = "") => {
|
||||
latestSearchRef.current = search;
|
||||
setOptions([]);
|
||||
setHasMore(true);
|
||||
pageRef.current = 0;
|
||||
loadPage({pageToLoad: 1, reset: true, search});
|
||||
}, [loadPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
resetAndLoad("");
|
||||
return () => {
|
||||
if (debounceRef.current) {
|
||||
clearTimeout(debounceRef.current);
|
||||
}
|
||||
};
|
||||
}, [resetAndLoad, reloadKey]);
|
||||
|
||||
const handleSearch = React.useCallback((value) => {
|
||||
onSearchProp?.(value);
|
||||
if (debounceRef.current) {
|
||||
clearTimeout(debounceRef.current);
|
||||
}
|
||||
|
||||
const triggerSearch = () => resetAndLoad(value || "");
|
||||
|
||||
if (!debounceMs) {
|
||||
triggerSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
debounceRef.current = setTimeout(triggerSearch, debounceMs);
|
||||
}, [debounceMs, onSearchProp, resetAndLoad]);
|
||||
|
||||
const handlePopupScroll = React.useCallback((event) => {
|
||||
onPopupScrollProp?.(event);
|
||||
const target = event?.target;
|
||||
if (!target || loadingRef.current || !hasMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reachedBottom = target.scrollTop + target.offsetHeight >= target.scrollHeight - SCROLL_BOTTOM_OFFSET;
|
||||
if (reachedBottom) {
|
||||
const nextPage = pageRef.current + 1;
|
||||
loadPage({pageToLoad: nextPage});
|
||||
}
|
||||
}, [hasMore, loadPage, onPopupScrollProp]);
|
||||
|
||||
const mergedLoading = selectLoading ?? loading;
|
||||
const mergedNotFound = mergedLoading ? <Spin size="small" /> : notFoundContent;
|
||||
|
||||
return (
|
||||
<Select
|
||||
{...restProps}
|
||||
virtual={virtual}
|
||||
showSearch={showSearch}
|
||||
filterOption={filterOption}
|
||||
options={options}
|
||||
loading={mergedLoading}
|
||||
notFoundContent={mergedNotFound}
|
||||
onSearch={showSearch ? handleSearch : undefined}
|
||||
onPopupScroll={handlePopupScroll}
|
||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default PaginateSelect;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"Exit impersonation": "Impersonation beenden",
|
||||
"Exit impersonation": "Exit impersonation",
|
||||
"Logout": "Abmeldung",
|
||||
"My Account": "Mein Konto",
|
||||
"Sign Up": "Anmelden"
|
||||
@@ -22,22 +22,22 @@
|
||||
"Add Face ID with Image": "Face ID mit Bild hinzufügen",
|
||||
"Always": "Immer",
|
||||
"Array": "Array",
|
||||
"Authentication": "Authentifizierung",
|
||||
"Authentication": "Authentication",
|
||||
"Auto signin": "Automatische Anmeldung",
|
||||
"Auto signin - Tooltip": "Wenn eine angemeldete Session in Casdoor vorhanden ist, wird diese automatisch für die Anmeldung auf Anwendungsebene verwendet",
|
||||
"Background URL": "Background-URL",
|
||||
"Background URL - Tooltip": "URL des Hintergrundbildes, das auf der Anmeldeseite angezeigt wird",
|
||||
"Background URL Mobile": "Hintergrund-URL mobil",
|
||||
"Background URL Mobile - Tooltip": "URL des Hintergrundbildes für mobile Geräte",
|
||||
"Basic": "Basis",
|
||||
"Basic": "Basic",
|
||||
"Big icon": "Großes Symbol",
|
||||
"Binding providers": "Bindungsanbieter",
|
||||
"CSS style": "CSS-Stil",
|
||||
"Center": "Zentrum",
|
||||
"Code resend timeout": "Code-Neusendungs-Timeout",
|
||||
"Code resend timeout - Tooltip": "Zeitraum (in Sekunden), den Benutzer warten müssen, bevor sie einen weiteren Verifizierungscode anfordern können. Auf 0 setzen, um die globale Standardeinstellung (60 Sekunden) zu verwenden",
|
||||
"Cookie expire": "Cookie-Ablauf",
|
||||
"Cookie expire - Tooltip": "Cookie-Ablauf - Hinweis",
|
||||
"Cookie expire": "Cookie expire",
|
||||
"Cookie expire - Tooltip": "Cookie expire - Tooltip",
|
||||
"Copy SAML metadata URL": "SAML-Metadaten-URL kopieren",
|
||||
"Copy prompt page URL": "URL der Prompt-Seite kopieren",
|
||||
"Copy signin page URL": "Kopieren Sie die URL der Anmeldeseite",
|
||||
@@ -48,8 +48,8 @@
|
||||
"Custom CSS Mobile": "Benutzerdefiniertes CSS mobil",
|
||||
"Custom CSS Mobile - Edit": "Benutzerdefiniertes CSS mobil – Bearbeiten",
|
||||
"Custom CSS Mobile - Tooltip": "CSS-Styling für mobile Geräte",
|
||||
"Disable SAML attributes": "SAML-Attribute deaktivieren",
|
||||
"Disable SAML attributes - Tooltip": "SAML-Attribute deaktivieren - Hinweis",
|
||||
"Disable SAML attributes": "Disable SAML attributes",
|
||||
"Disable SAML attributes - Tooltip": "Disable SAML attributes - Tooltip",
|
||||
"Disable signin": "Anmeldung deaktivieren",
|
||||
"Disable signin - Tooltip": "Anmeldung für Benutzer deaktivieren",
|
||||
"Dynamic": "Dynamisch",
|
||||
@@ -60,17 +60,16 @@
|
||||
"Enable SAML C14N10 - Tooltip": "C14N10 anstelle von C14N11 in SAML verwenden",
|
||||
"Enable SAML POST binding": "SAML POST-Binding aktivieren",
|
||||
"Enable SAML POST binding - Tooltip": "Das HTTP-POST-Binding verwendet HTML-Formular-Eingabefelder, um SAML-Nachrichten zu senden. Aktivieren Sie diese Option, wenn Ihr SP dies verwendet.",
|
||||
"Enable SAML assertion signature": "SAML-Assertion-Signatur aktivieren",
|
||||
"Enable SAML assertion signature - Tooltip": "SAML-Assertion-Signatur aktivieren - Hinweis",
|
||||
"Enable SAML assertion signature": "Enable SAML assertion signature",
|
||||
"Enable SAML assertion signature - Tooltip": "Enable SAML assertion signature - Tooltip",
|
||||
"Enable SAML compression": "Aktivieren Sie SAML-Komprimierung",
|
||||
"Enable SAML compression - Tooltip": "Ob SAML-Antwortnachrichten komprimiert werden sollen, wenn Casdoor als SAML-IdP verwendet wird",
|
||||
"Enable exclusive signin": "Exklusive Anmeldung aktivieren",
|
||||
"Enable exclusive signin - Tooltip": "Wenn die exklusive Anmeldung aktiviert ist, kann der Benutzer nicht mehrere aktive Sitzungen haben",
|
||||
"Enable exclusive signin": "Enable exclusive signin",
|
||||
"Enable exclusive signin - Tooltip": "When exclusive signin enabled, user cannot have multiple active session",
|
||||
"Enable side panel": "Sidepanel aktivieren",
|
||||
"Enable signin session - Tooltip": "Ob Casdoor eine Sitzung aufrechterhält, nachdem man sich von der Anwendung aus bei Casdoor angemeldet hat",
|
||||
"Enable signup": "Registrierung aktivieren",
|
||||
"Enable signup - Tooltip": "Ob Benutzern erlaubt werden soll, ein neues Konto zu registrieren",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "Sperrzeit bei fehlgeschlagenem Login",
|
||||
"Failed signin frozen time - Tooltip": "Zeit, für die das Konto nach fehlgeschlagenen Anmeldeversuchen gesperrt wird",
|
||||
"Failed signin limit": "Limit für fehlgeschlagene Logins",
|
||||
@@ -102,8 +101,8 @@
|
||||
"Logged out successfully": "Erfolgreich ausgeloggt",
|
||||
"MFA remember time": "MFA-Merkezeit",
|
||||
"MFA remember time - Tooltip": "Konfiguriert die Dauer, für die ein Konto nach einer erfolgreichen MFA-Anmeldung als vertrauenswürdig gespeichert wird",
|
||||
"Menu mode": "Menümodus",
|
||||
"Menu mode - Tooltip": "Menümodus - Hinweis",
|
||||
"Menu mode": "Menu mode",
|
||||
"Menu mode - Tooltip": "Menu mode - Tooltip",
|
||||
"Multiple Choices": "Mehrfachauswahl",
|
||||
"New Application": "Neue Anwendung",
|
||||
"No verification": "Keine Verifizierung",
|
||||
@@ -112,13 +111,13 @@
|
||||
"Order": "Reihenfolge",
|
||||
"Order - Tooltip": "Je kleiner der Wert, desto höher rangiert er auf der Apps-Seite",
|
||||
"Org choice mode": "Organisationsauswahlmodus",
|
||||
"Org choice mode - Tooltip": "Organisationsauswahlmodus - Hinweis",
|
||||
"Org choice mode - Tooltip": "Organisationsauswahlmodus",
|
||||
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "Bitte aktivieren Sie zuerst \"Anmeldesitzung\", bevor Sie \"Automatische Anmeldung\" aktivieren.",
|
||||
"Please input your application!": "Bitte geben Sie Ihre Anwendung ein!",
|
||||
"Please input your organization!": "Bitte geben Sie Ihre Organisation ein!",
|
||||
"Please select a HTML file": "Bitte wählen Sie eine HTML-Datei aus",
|
||||
"Pop up": "Pop-up",
|
||||
"Providers": "Anbieter",
|
||||
"Providers": "Providers",
|
||||
"Random": "Zufällig",
|
||||
"Real name": "Echter Name",
|
||||
"Redirect URL": "Weiterleitungs-URL",
|
||||
@@ -133,26 +132,25 @@
|
||||
"SAML hash algorithm": "SAML-Hash-Algorithmus",
|
||||
"SAML hash algorithm - Tooltip": "Hash-Algorithmus für SAML-Signatur",
|
||||
"SAML metadata": "SAML-Metadaten",
|
||||
"SAML metadata - Tooltip": "Die Metadaten des SAML-Protokolls - Hinweis",
|
||||
"SAML metadata - Tooltip": "Die Metadaten des SAML-Protokolls",
|
||||
"SAML reply URL": "SAML Reply-URL",
|
||||
"Security": "Sicherheit",
|
||||
"Security": "Security",
|
||||
"Select": "Auswählen",
|
||||
"Side panel HTML": "Sidepanel-HTML",
|
||||
"Side panel HTML - Edit": "Sidepanel HTML - Bearbeiten",
|
||||
"Side panel HTML - Tooltip": "Den HTML-Code für die Seitenleiste der Anmeldeseite anpassen - Hinweis",
|
||||
"Side panel HTML - Tooltip": "Passen Sie den HTML-Code für das Sidepanel der Login-Seite an",
|
||||
"Sign Up Error": "Registrierungsfehler",
|
||||
"Signin": "Anmelden",
|
||||
"Signin (Default True)": "Anmelden (Standard: Wahr)",
|
||||
"Signin items": "Anmeldeelemente",
|
||||
"Signin items - Tooltip": "Anmeldeelemente",
|
||||
"Signin methods": "Anmeldemethoden",
|
||||
"Signin methods - Tooltip": "Erlaubte Anmeldemethoden für Benutzer hinzufügen. Standardmäßig sind alle Methoden verfügbar - Hinweis",
|
||||
"Signin methods - Tooltip": "Hinzufügen der zulässigen Anmeldemethoden für Benutzer, standardmäßig sind alle Methoden verfügbar",
|
||||
"Signin session": "Anmeldesession",
|
||||
"Signup items": "Registrierungs Items",
|
||||
"Signup items - Tooltip": "Elemente für Benutzer, die beim Registrieren neuer Konten ausgefüllt werden müssen - Hinweis",
|
||||
"Signup items - Tooltip": "Items, die Benutzer ausfüllen müssen, wenn sie neue Konten registrieren",
|
||||
"Single Choice": "Einfachauswahl",
|
||||
"Small icon": "Kleines Symbol",
|
||||
"Static Value": "Static Value",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "Nur Benutzer mit einem Tag, das in den Anwendungstags aufgeführt ist, können sich anmelden",
|
||||
"The application does not allow to sign up new account": "Die Anwendung erlaubt es nicht, ein neues Konto zu registrieren",
|
||||
@@ -164,10 +162,10 @@
|
||||
"Token format - Tooltip": "Das Format des Access-Tokens",
|
||||
"Token signing method": "Token-Signaturmethode",
|
||||
"Token signing method - Tooltip": "Signaturmethode des JWT-Tokens muss mit dem Zertifikat übereinstimmen",
|
||||
"UI Customization": "UI-Anpassung",
|
||||
"UI Customization": "UI Customization",
|
||||
"Use Email as NameID": "E-Mail als NameID verwenden",
|
||||
"Use Email as NameID - Tooltip": "E-Mail als NameID verwenden",
|
||||
"Vertical": "Vertikal",
|
||||
"Vertical": "Vertical",
|
||||
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
|
||||
},
|
||||
"cert": {
|
||||
@@ -186,7 +184,9 @@
|
||||
"Expire in years - Tooltip": "Gültigkeitsdauer des Zertifikats in Jahren",
|
||||
"New Cert": "Neues Zertifikat",
|
||||
"Private key": "Private-Key",
|
||||
"Private key - Tooltip": "Privater Schlüssel, der zum öffentlichen Schlüsselzertifikat gehört"
|
||||
"Private key - Tooltip": "Privater Schlüssel, der zum öffentlichen Schlüsselzertifikat gehört",
|
||||
"Scope - Tooltip": "Nutzungsszenarien des Zertifikats",
|
||||
"Type - Tooltip": "Art des Zertifikats"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Der Code, den Sie erhalten haben",
|
||||
@@ -250,7 +250,7 @@
|
||||
"Width": "Breite"
|
||||
},
|
||||
"general": {
|
||||
"A normal user can only modify the permission submitted by itself": "Ein normaler Benutzer kann nur die von ihm selbst eingereichte Berechtigung ändern",
|
||||
"A normal user can only modify the permission submitted by itself": "A normal user can only modify the permission submitted by itself",
|
||||
"AI Assistant": "KI-Assistent",
|
||||
"API key": "API-Schlüssel",
|
||||
"API key - Tooltip": "API-Schlüssel für den Zugriff auf den Dienst",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "Anwendungen, die eine Authentifizierung erfordern",
|
||||
"Apps": "Anwendungen",
|
||||
"Authorization": "Autorisierung",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "Profilbild",
|
||||
"Avatar - Tooltip": "Öffentliches Avatarbild für den Benutzer",
|
||||
"Back": "Zurück",
|
||||
@@ -283,13 +282,11 @@
|
||||
"Business & Payments": "Geschäft & Zahlungen",
|
||||
"Cancel": "Abbrechen",
|
||||
"Captcha": "Captcha",
|
||||
"Cart": "Warenkorb",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cart": "Cart",
|
||||
"Cert": "Zertifikat",
|
||||
"Cert - Tooltip": "Das Public-Key-Zertifikat, das vom Client-SDK, das mit dieser Anwendung korrespondiert, verifiziert werden muss",
|
||||
"Certs": "Zertifikate",
|
||||
"Clear": "Leeren",
|
||||
"Clear": "Clear",
|
||||
"Click to Upload": "Klicken Sie zum Hochladen",
|
||||
"Client IP": "Client-IP",
|
||||
"Close": "Schließen",
|
||||
@@ -310,12 +307,12 @@
|
||||
"Delete": "Löschen",
|
||||
"Description": "Beschreibung",
|
||||
"Description - Tooltip": "Detaillierte Beschreibungsinformationen zur Referenz, Casdoor selbst wird es nicht verwenden",
|
||||
"Detail": "Details",
|
||||
"Detail": "详情",
|
||||
"Disable": "Deaktivieren",
|
||||
"Display name": "Anzeigename",
|
||||
"Display name - Tooltip": "Ein benutzerfreundlicher, leicht lesbarer Name, der öffentlich in der Benutzeroberfläche angezeigt wird",
|
||||
"Down": "Nach unten",
|
||||
"Download template": "Vorlage herunterladen",
|
||||
"Download template": "Download template",
|
||||
"Edit": "Bearbeiten",
|
||||
"Email": "E-Mail",
|
||||
"Email - Tooltip": "Gültige E-Mail-Adresse",
|
||||
@@ -330,21 +327,21 @@
|
||||
"Enabled successfully": "Erfolgreich aktiviert",
|
||||
"Enforcers": "Enforcer",
|
||||
"Failed to add": "Fehler beim hinzufügen",
|
||||
"Failed to cancel": "Abbrechen fehlgeschlagen",
|
||||
"Failed to cancel": "Failed to cancel",
|
||||
"Failed to connect to server": "Die Verbindung zum Server konnte nicht hergestellt werden",
|
||||
"Failed to copy": "Kopieren fehlgeschlagen",
|
||||
"Failed to delete": "Konnte nicht gelöscht werden",
|
||||
"Failed to enable": "Aktivierung fehlgeschlagen",
|
||||
"Failed to get": "Abruf fehlgeschlagen",
|
||||
"Failed to load": "Laden fehlgeschlagen",
|
||||
"Failed to log out": "Abmelden fehlgeschlagen",
|
||||
"Failed to load": "Failed to load",
|
||||
"Failed to log out": "Failed to log out",
|
||||
"Failed to remove": "Entfernen fehlgeschlagen",
|
||||
"Failed to save": "Konnte nicht gespeichert werden",
|
||||
"Failed to send": "Senden fehlgeschlagen",
|
||||
"Failed to send": "Failed to send",
|
||||
"Failed to sync": "Synchronisation fehlgeschlagen",
|
||||
"Failed to unlink": "Verknüpfung aufheben fehlgeschlagen",
|
||||
"Failed to update": "Aktualisieren fehlgeschlagen",
|
||||
"Failed to upload": "Hochladen fehlgeschlagen",
|
||||
"Failed to unlink": "Failed to unlink",
|
||||
"Failed to update": "Failed to update",
|
||||
"Failed to upload": "Failed to upload",
|
||||
"Failed to verify": "Verifizierung fehlgeschlagen",
|
||||
"False": "Falsch",
|
||||
"Favicon": "Favicon",
|
||||
@@ -357,7 +354,7 @@
|
||||
"Forget URL - Tooltip": "Benutzerdefinierte URL für die \"Passwort vergessen\" Seite. Wenn nicht festgelegt, wird die standardmäßige Casdoor \"Passwort vergessen\" Seite verwendet. Wenn sie festgelegt ist, wird der \"Passwort vergessen\" Link auf der Login-Seite zu dieser URL umgeleitet",
|
||||
"Forms": "Formulare",
|
||||
"Found some texts still not translated? Please help us translate at": "Haben Sie noch Texte gefunden, die nicht übersetzt wurden? Bitte helfen Sie uns beim Übersetzen",
|
||||
"Generate": "Generieren",
|
||||
"Generate": "Generate",
|
||||
"Go to enable": "Zum Aktivieren gehen",
|
||||
"Go to writable demo site?": "Gehe zur beschreibbaren Demo-Website?",
|
||||
"Groups": "Gruppen",
|
||||
@@ -370,7 +367,7 @@
|
||||
"IP whitelist": "IP-Whitelist",
|
||||
"IP whitelist - Tooltip": "IP-Whitelist",
|
||||
"Identity": "Identität",
|
||||
"Impersonation": "Identitätswechsel",
|
||||
"Impersonation": "Impersonation",
|
||||
"Invitations": "Einladungen",
|
||||
"Is enabled": "Ist aktiviert",
|
||||
"Is enabled - Tooltip": "Festlegen, ob es verwendet werden kann",
|
||||
@@ -403,21 +400,21 @@
|
||||
"Name": "Name",
|
||||
"Name - Tooltip": "Eindeutige, auf Strings basierende ID",
|
||||
"Name format": "Namensformat",
|
||||
"No products available": "Keine Produkte verfügbar",
|
||||
"No sheets found in file": "Keine Tabellenblätter in der Datei gefunden",
|
||||
"No verification method": "Keine Verifizierungsmethode",
|
||||
"No products available": "No products available",
|
||||
"No sheets found in file": "No sheets found in file",
|
||||
"No verification method": "No verification method",
|
||||
"Non-LDAP": "Nicht-LDAP",
|
||||
"None": "Keine",
|
||||
"OAuth providers": "OAuth-Provider",
|
||||
"OFF": "AUS",
|
||||
"OK": "OK",
|
||||
"ON": "EIN",
|
||||
"Only 1 MFA method can be required": "Es kann nur 1 MFA-Methode erforderlich sein",
|
||||
"Or": "Oder",
|
||||
"Orders": "Bestellungen",
|
||||
"Only 1 MFA method can be required": "Only 1 MFA method can be required",
|
||||
"Or": "Or",
|
||||
"Orders": "Orders",
|
||||
"Organization": "Organisation",
|
||||
"Organization - Tooltip": "Ähnlich wie bei Konzepten wie Mietern oder Benutzerpools gehört jeder Benutzer und jede Anwendung einer Organisation an",
|
||||
"Organization is null": "Organisation ist leer",
|
||||
"Organization is null": "Organization is null",
|
||||
"Organizations": "Organisationen",
|
||||
"Password": "Passwort",
|
||||
"Password - Tooltip": "Stellen Sie sicher, dass das Passwort korrekt ist",
|
||||
@@ -440,21 +437,21 @@
|
||||
"Phone - Tooltip": "Telefonnummer",
|
||||
"Phone only": "Nur Telefon",
|
||||
"Phone or Email": "Telefon oder E-Mail",
|
||||
"Place Order": "Bestellung aufgeben",
|
||||
"Place Order": "Place Order",
|
||||
"Plain": "Klartext",
|
||||
"Plan": "Plan",
|
||||
"Plan - Tooltip": "Abonnementplan",
|
||||
"Plans": "Pläne",
|
||||
"Plans - Tooltip": "Pläne",
|
||||
"Please complete the captcha correctly": "Bitte lösen Sie das Captcha korrekt",
|
||||
"Please complete the captcha correctly": "Please complete the captcha correctly",
|
||||
"Please input your search": "Bitte geben Sie Ihre Suche ein",
|
||||
"Please select at least 1 user first": "Bitte wählen Sie zuerst mindestens 1 Benutzer aus",
|
||||
"Please select at least 1 user first": "Please select at least 1 user first",
|
||||
"Preview": "Vorschau",
|
||||
"Preview - Tooltip": "Vorschau der konfigurierten Effekte",
|
||||
"Pricing": "Preisgestaltung",
|
||||
"Pricing - Tooltip": "Preisgestaltung",
|
||||
"Pricings": "Preise",
|
||||
"Product Store": "Produktshop",
|
||||
"Product Store": "Product Store",
|
||||
"Products": "Produkte",
|
||||
"Provider": "Anbieter",
|
||||
"Provider - Tooltip": "Zahlungsprovider, die konfiguriert werden müssen, inkl. PayPal, Alipay, WeChat Pay usw.",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "Der Authentifizierungstyp für SSH-Verbindungen",
|
||||
"Save": "Speichern",
|
||||
"Save & Exit": "Speichern und verlassen",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "Suchen",
|
||||
"Send": "Senden",
|
||||
"Session ID": "Session-ID",
|
||||
@@ -501,10 +496,10 @@
|
||||
"Status": "Status",
|
||||
"Subscriptions": "Abonnements",
|
||||
"Successfully added": "Erfolgreich hinzugefügt",
|
||||
"Successfully canceled": "Erfolgreich abgebrochen",
|
||||
"Successfully canceled": "Successfully canceled",
|
||||
"Successfully copied": "Erfolgreich kopiert",
|
||||
"Successfully deleted": "Erfolgreich gelöscht",
|
||||
"Successfully executed": "Erfolgreich ausgeführt",
|
||||
"Successfully executed": "Successfully executed",
|
||||
"Successfully removed": "Erfolgreich entfernt",
|
||||
"Successfully saved": "Erfolgreich gespeichert",
|
||||
"Successfully sent": "Erfolgreich gesendet",
|
||||
@@ -519,9 +514,9 @@
|
||||
"Syncers": "Syncer",
|
||||
"System Info": "Systeminformationen",
|
||||
"Tab": "Tab",
|
||||
"The actions cannot be empty": "Die Aktionen dürfen nicht leer sein",
|
||||
"The resources cannot be empty": "Die Ressourcen dürfen nicht leer sein",
|
||||
"The users and roles cannot be empty at the same time": "Benutzer und Rollen dürfen nicht gleichzeitig leer sein",
|
||||
"The actions cannot be empty": "The actions cannot be empty",
|
||||
"The resources cannot be empty": "The resources cannot be empty",
|
||||
"The users and roles cannot be empty at the same time": "The users and roles cannot be empty at the same time",
|
||||
"There was a problem signing you in..": "Es gab ein Problem beim Anmelden...",
|
||||
"This is a read-only demo site!": "Dies ist eine schreibgeschützte Demo-Seite!",
|
||||
"Tickets": "Tickets",
|
||||
@@ -535,18 +530,17 @@
|
||||
"Transactions": "Transaktionen",
|
||||
"True": "Wahr",
|
||||
"Type": "Typ",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "URL-Link",
|
||||
"Unknown application name": "Unbekannter Anwendungsname",
|
||||
"Unknown authentication type": "Unbekannter Authentifizierungstyp",
|
||||
"Unknown application name": "Unknown application name",
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "Oben",
|
||||
"Updated time": "Aktualisierungszeit",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "Nutzer",
|
||||
"User - Tooltip": "Stellen Sie sicher, dass der Benutzername korrekt ist",
|
||||
"User Management": "Benutzerverwaltung",
|
||||
"User already exists": "Benutzer existiert bereits",
|
||||
"User already exists": "User already exists",
|
||||
"User containers": "Nutzerpools",
|
||||
"User type": "Benutzertyp",
|
||||
"User type - Tooltip": "Tags, denen der Benutzer angehört, standardmäßig auf \"normaler Benutzer\" festgelegt",
|
||||
@@ -554,10 +548,10 @@
|
||||
"Users - Tooltip": "Benutzer",
|
||||
"Users under all organizations": "Benutzer unter allen Organisationen",
|
||||
"Verifications": "Verifizierungen",
|
||||
"View": "Anzeigen",
|
||||
"View": "View",
|
||||
"Webhooks": "Webhooks",
|
||||
"You can only select one physical group": "Sie können nur eine physische Gruppe auswählen",
|
||||
"You must select a picture first": "Sie müssen zuerst ein Bild auswählen",
|
||||
"You must select a picture first": "You must select a picture first",
|
||||
"empty": "leere",
|
||||
"remove": "entfernen",
|
||||
"{total} in total": "Insgesamt {total}"
|
||||
@@ -632,7 +626,7 @@
|
||||
"login": {
|
||||
"Auto sign in": "Automatische Anmeldung",
|
||||
"Back button": "Zurück-Button",
|
||||
"Click the button below to sign in with Telegram": "Klicken Sie auf die Schaltfläche unten, um sich mit Telegram anzumelden",
|
||||
"Click the button below to sign in with Telegram": "Click the button below to sign in with Telegram",
|
||||
"Continue with": "Weitermachen mit",
|
||||
"Email or phone": "E-Mail oder Telefon",
|
||||
"Face ID": "Face ID",
|
||||
@@ -655,12 +649,12 @@
|
||||
"Please input your Email!": "Bitte geben Sie Ihre E-Mail ein!",
|
||||
"Please input your LDAP username!": "Bitte geben Sie Ihren LDAP-Benutzernamen ein!",
|
||||
"Please input your Phone!": "Bitte geben Sie Ihre Telefonnummer ein!",
|
||||
"Please input your RADIUS password!": "Bitte geben Sie Ihr RADIUS-Passwort ein!",
|
||||
"Please input your RADIUS username!": "Bitte geben Sie Ihren RADIUS-Benutzernamen ein!",
|
||||
"Please input your RADIUS password!": "Please input your RADIUS password!",
|
||||
"Please input your RADIUS username!": "Please input your RADIUS username!",
|
||||
"Please input your code!": "Bitte geben Sie Ihren Code ein!",
|
||||
"Please input your organization name!": "Bitte geben Sie Ihren Organisationsnamen ein!",
|
||||
"Please input your password!": "Bitte geben Sie Ihr Passwort ein!",
|
||||
"Please input your push notification receiver!": "Bitte geben Sie den Empfänger für Push-Benachrichtigungen ein!",
|
||||
"Please input your push notification receiver!": "Please input your push notification receiver!",
|
||||
"Please load the webpage using HTTPS, otherwise the camera cannot be accessed": "Bitte laden Sie die Webseite über HTTPS, sonst kann auf die Kamera nicht zugegriffen werden.",
|
||||
"Please provide permission to access the camera": "Bitte erteilen Sie die Kamerazugriffsberechtigung.",
|
||||
"Please select an organization": "Bitte wählen Sie eine Organisation aus.",
|
||||
@@ -671,7 +665,7 @@
|
||||
"Select organization": "Organisation auswählen",
|
||||
"Sign In": "Anmelden",
|
||||
"Sign in with Face ID": "Mit Face ID anmelden",
|
||||
"Sign in with Telegram": "Mit Telegram anmelden",
|
||||
"Sign in with Telegram": "Sign in with Telegram",
|
||||
"Sign in with WebAuthn": "Melden Sie sich mit WebAuthn an",
|
||||
"Sign in with {type}": "Melden Sie sich mit {type} an",
|
||||
"Signin button": "Anmelde-Button",
|
||||
@@ -704,7 +698,7 @@
|
||||
"Please confirm the information below": "Bitte bestätigen Sie die folgenden Informationen",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Bitte speichern Sie diesen Wiederherstellungscode. Falls Ihr Gerät keinen Code liefern kann, können Sie MFA mit diesem Code zurücksetzen.",
|
||||
"Protect your account with Multi-factor authentication": "Schützen Sie Ihr Konto mit MFA",
|
||||
"Push notification receiver": "Empfänger für Push-Benachrichtigungen",
|
||||
"Push notification receiver": "Push notification receiver",
|
||||
"Recovery code": "Wiederherstellungscode",
|
||||
"Remember this account for {hour} hours": "Dieses Konto für {hour} Stunden merken",
|
||||
"Scan the QR code with your Authenticator App": "Scannen Sie den QR-Code mit Ihrer Authenticator-App",
|
||||
@@ -714,17 +708,17 @@
|
||||
"To ensure the security of your account, it is required to enable multi-factor authentication": "Zur Sicherheit Ihres Kontos ist MFA erforderlich.",
|
||||
"Use Authenticator App": "Authenticator-App verwenden",
|
||||
"Use Email": "E-Mail verwenden",
|
||||
"Use Push Notification": "Push-Benachrichtigung verwenden",
|
||||
"Use Radius": "RADIUS verwenden",
|
||||
"Use Push Notification": "Use Push Notification",
|
||||
"Use Radius": "Use Radius",
|
||||
"Use SMS": "SMS verwenden",
|
||||
"Use SMS verification code": "SMS-Verifizierungscode verwenden",
|
||||
"Use a recovery code": "Wiederherstellungscode verwenden",
|
||||
"Verify Code": "Code verifizieren",
|
||||
"Verify Password": "Passwort verifizieren",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "Sie haben MFA aktiviert. Klicken Sie auf „Code senden“, um fortzufahren.",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "Sie haben MFA aktiviert. Bitte geben Sie das RADIUS-Passwort ein.",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "You have enabled Multi-Factor Authentication, please enter the RADIUS password",
|
||||
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "Sie haben MFA aktiviert. Bitte geben Sie den TOTP-Code ein.",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "Sie haben MFA aktiviert. Bitte geben Sie den Verifizierungscode aus der Push-Benachrichtigung ein",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "You have enabled Multi-Factor Authentication, please enter the verification code from push notification",
|
||||
"Your email is": "Ihre E-Mail ist",
|
||||
"Your phone is": "Ihr Telefon ist",
|
||||
"preferred": "bevorzugt"
|
||||
@@ -738,29 +732,29 @@
|
||||
"New Model": "Neues Modell"
|
||||
},
|
||||
"order": {
|
||||
"Cancel time": "Stornierungszeit",
|
||||
"Edit Order": "Bestellung bearbeiten",
|
||||
"New Order": "Neue Bestellung",
|
||||
"Order not found": "Bestellung nicht gefunden",
|
||||
"Pay": "Bezahlen",
|
||||
"Payment failed time": "Zeitpunkt des Zahlungsfehlers",
|
||||
"Payment time": "Zahlungszeit",
|
||||
"Price": "Preis",
|
||||
"Return to Order List": "Zur Bestellliste zurückkehren",
|
||||
"Timeout time": "Zeitpunkt des Timeouts",
|
||||
"View Order": "Bestellung anzeigen"
|
||||
"Cancel time": "Cancel time",
|
||||
"Edit Order": "Edit Order",
|
||||
"New Order": "New Order",
|
||||
"Order not found": "Order not found",
|
||||
"Pay": "Pay",
|
||||
"Payment failed time": "Payment failed time",
|
||||
"Payment time": "Payment time",
|
||||
"Price": "Price",
|
||||
"Return to Order List": "Return to Order List",
|
||||
"Timeout time": "Timeout time",
|
||||
"View Order": "View Order"
|
||||
},
|
||||
"organization": {
|
||||
"Account items": "Konto Items",
|
||||
"Account items - Tooltip": "Elemente auf der persönlichen Einstellungsseite",
|
||||
"Account menu": "Kontomenü",
|
||||
"Account menu - Tooltip": "Kontomenü - Tooltip",
|
||||
"Admin navbar items": "Admin-Navigationsleisten-Elemente",
|
||||
"Admin navbar items - Tooltip": "Admin-Navigationsleisten-Elemente - Tooltip",
|
||||
"Balance credit": "Guthaben (Credits)",
|
||||
"Balance credit - Tooltip": "Guthaben (Credits) - Tooltip",
|
||||
"Balance currency": "Guthabenwährung",
|
||||
"Balance currency - Tooltip": "Guthabenwährung - Tooltip",
|
||||
"Account menu": "Account menu",
|
||||
"Account menu - Tooltip": "Account menu - Tooltip",
|
||||
"Admin navbar items": "Admin navbar items",
|
||||
"Admin navbar items - Tooltip": "Admin navbar items - Tooltip",
|
||||
"Balance credit": "Balance credit",
|
||||
"Balance credit - Tooltip": "Balance credit - Tooltip",
|
||||
"Balance currency": "Balance currency",
|
||||
"Balance currency - Tooltip": "Balance currency - Tooltip",
|
||||
"Edit Organization": "Organisation bearbeiten",
|
||||
"Follow global theme": "Folge dem globalen Theme",
|
||||
"Has privilege consent": "Privilegienzustimmung vorhanden",
|
||||
@@ -773,8 +767,8 @@
|
||||
"Modify rule": "Regel ändern",
|
||||
"New Organization": "Neue Organisation",
|
||||
"Optional": "Optional",
|
||||
"Org balance": "Organisationsguthaben",
|
||||
"Org balance - Tooltip": "Organisationsguthaben - Tooltip",
|
||||
"Org balance": "Org balance",
|
||||
"Org balance - Tooltip": "Org balance - Tooltip",
|
||||
"Password expire days": "Passwort läuft ab in Tagen",
|
||||
"Password expire days - Tooltip": "Anzahl der Tage vor dem Ablauf des Passworts",
|
||||
"Prompt": "Aufforderung",
|
||||
@@ -784,10 +778,10 @@
|
||||
"Tags": "Tags",
|
||||
"Use Email as username": "E-Mail als Benutzername verwenden",
|
||||
"Use Email as username - Tooltip": "E-Mail als Benutzername verwenden, wenn das Feld „Benutzername“ bei der Registrierung nicht sichtbar ist.",
|
||||
"User balance": "Benutzerguthaben",
|
||||
"User balance - Tooltip": "Benutzerguthaben - Tooltip",
|
||||
"User navbar items": "Benutzer-Navigationsleisten-Elemente",
|
||||
"User navbar items - Tooltip": "Benutzer-Navigationsleisten-Elemente - Tooltip",
|
||||
"User balance": "User balance",
|
||||
"User balance - Tooltip": "User balance - Tooltip",
|
||||
"User navbar items": "User navbar items",
|
||||
"User navbar items - Tooltip": "User navbar items - Tooltip",
|
||||
"User types": "Benutzertypen",
|
||||
"User types - Tooltip": "Verfügbare Benutzertypen im System",
|
||||
"View rule": "Ansichtsregel",
|
||||
@@ -832,15 +826,15 @@
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Bitte prüfen Sie sorgfältig Ihre Rechnungsinformationen. Sobald die Rechnung ausgestellt wurde, kann sie nicht zurückgenommen oder geändert werden.",
|
||||
"Please pay the order first!": "Bitte zahlen Sie zuerst die Bestellung!",
|
||||
"Processing...": "In Bearbeitung...",
|
||||
"Products - Tooltip": "Produkte - Tooltip",
|
||||
"Products - Tooltip": "Products - Tooltip",
|
||||
"Recharged successfully": "Erfolgreich aufgeladen",
|
||||
"Result": "Ergebnis",
|
||||
"The payment has been canceled": "Die Zahlung wurde storniert",
|
||||
"The payment has failed": "Die Zahlung ist fehlgeschlagen",
|
||||
"The payment has timed out": "Die Zahlung ist abgelaufen",
|
||||
"The payment has timed out": "The payment has timed out",
|
||||
"The payment is still under processing": "Die Zahlung wird immer noch bearbeitet",
|
||||
"View Payment": "Zahlung anzeigen",
|
||||
"You can view your order details or return to the order list": "Sie können Ihre Bestelldetails ansehen oder zur Bestellliste zurückkehren",
|
||||
"View Payment": "View Payment",
|
||||
"You can view your order details or return to the order list": "You can view your order details or return to the order list",
|
||||
"You have successfully completed the payment": "Sie haben die Zahlung erfolgreich abgeschlossen",
|
||||
"You have successfully recharged": "Sie haben erfolgreich aufgeladen",
|
||||
"Your current balance is": "Ihr aktuelles Guthaben beträgt",
|
||||
@@ -873,15 +867,13 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "Plan bearbeiten",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "Neuer Plan",
|
||||
"Period": "Zeitraum",
|
||||
"Period - Tooltip": "Zeitraum",
|
||||
"Plan name": "Planname",
|
||||
"Plan name": "Plan name",
|
||||
"Price - Tooltip": "Preis",
|
||||
"Related product": "Zugehöriges Produkt",
|
||||
"View Plan": "Plan anzeigen",
|
||||
"View Plan": "View Plan",
|
||||
"per month": "pro Monat",
|
||||
"per year": "pro Jahr"
|
||||
},
|
||||
@@ -891,60 +883,55 @@
|
||||
"Free": "Kostenlos",
|
||||
"Getting started": "Loslegen",
|
||||
"New Pricing": "Neue Preisgestaltung",
|
||||
"Pricing name": "Pricing-Name",
|
||||
"Pricing name": "Pricing name",
|
||||
"Trial duration": "Testphase Dauer",
|
||||
"Trial duration - Tooltip": "Dauer der Testphase",
|
||||
"View Pricing": "Preisgestaltung anzeigen",
|
||||
"View Pricing": "View Pricing",
|
||||
"days trial available!": "Tage Testphase verfügbar!",
|
||||
"paid-user do not have active subscription or pending subscription, please select a plan to buy": "Bezahlte Benutzer haben keine aktive oder ausstehende Abonnements, bitte wählen Sie einen Plan zum Kauf aus."
|
||||
},
|
||||
"product": {
|
||||
"Add to cart": "In den Warenkorb",
|
||||
"Add to cart": "Add to cart",
|
||||
"AirWallex": "AirWallex",
|
||||
"Alipay": "Alipay",
|
||||
"Amount": "Betrag",
|
||||
"Amount": "Amount",
|
||||
"Buy": "Kaufen",
|
||||
"Buy Product": "Produkt kaufen",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "Benutzerdefinierter Betrag verfügbar",
|
||||
"Custom price should be greater than zero": "Benutzerdefinierter Preis muss größer als null sein",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "Detail des Produkts",
|
||||
"Disable custom amount": "Benutzerdefinierten Betrag deaktivieren",
|
||||
"Disable custom amount - Tooltip": "Benutzerdefinierten Betrag deaktivieren - Tooltip",
|
||||
"Disable custom amount": "Disable custom amount",
|
||||
"Disable custom amount - Tooltip": "Disable custom amount - Tooltip",
|
||||
"Dummy": "Dummy",
|
||||
"Edit Product": "Produkt bearbeiten",
|
||||
"Enter preset amounts": "Vorgegebene Beträge eingeben",
|
||||
"Failed to create order": "Bestellung konnte nicht erstellt werden",
|
||||
"Enter preset amounts": "Enter preset amounts",
|
||||
"Failed to create order": "Failed to create order",
|
||||
"Image": "Bild",
|
||||
"Image - Tooltip": "Bild des Produkts",
|
||||
"Information": "Information",
|
||||
"Invalid product": "Invalid product",
|
||||
"Is recharge": "Ist Aufladung",
|
||||
"Is recharge - Tooltip": "Ob das Produkt zum Aufladen des Guthabens dient",
|
||||
"New Product": "Neues Produkt",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "Bestellung erfolgreich erstellt",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "PayPal",
|
||||
"Payment cancelled": "Zahlung storniert",
|
||||
"Payment failed": "Zahlung fehlgeschlagen",
|
||||
"Payment providers": "Zahlungsprovider",
|
||||
"Payment providers - Tooltip": "Provider von Zahlungsdiensten",
|
||||
"Placing order...": "Bestellung aufgeben...",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Bitte fügen Sie mindestens eine Aufladeoption hinzu, wenn der benutzerdefinierte Betrag deaktiviert ist",
|
||||
"Please select a currency": "Bitte wählen Sie eine Währung",
|
||||
"Please select at least one payment provider": "Bitte wählen Sie mindestens einen Zahlungsanbieter aus",
|
||||
"Processing payment...": "Zahlung wird verarbeitet...",
|
||||
"Product list cannot be empty": "Produktliste darf nicht leer sein",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Please add at least one recharge option when custom amount is disabled",
|
||||
"Please select a currency": "Please select a currency",
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Quantity": "Menge",
|
||||
"Quantity - Tooltip": "Menge des Produkts",
|
||||
"Recharge options": "Aufladeoptionen",
|
||||
"Recharge options - Tooltip": "Aufladeoptionen - Tooltip",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Recharge options - Tooltip",
|
||||
"Return URL": "Rückkeht-URL",
|
||||
"Return URL - Tooltip": "URL für die Rückkehr nach einem erfolgreichen Kauf",
|
||||
"SKU": "SKU",
|
||||
"Select amount": "Betrag auswählen",
|
||||
"Select amount": "Select amount",
|
||||
"Sold": "Verkauft",
|
||||
"Sold - Tooltip": "Menge verkauft",
|
||||
"Stripe": "Stripe",
|
||||
@@ -952,12 +939,12 @@
|
||||
"Success URL - Tooltip": "URL, zu der nach dem Kauf zurückgekehrt wird",
|
||||
"Tag - Tooltip": "Tag des Produkts",
|
||||
"Test buy page..": "Testkaufseite.",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "Die Währung des Produkts, das Sie hinzufügen, unterscheidet sich von der Währung der Artikel im Warenkorb",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "The currency of the product you are adding is different from the currency of the items in the cart",
|
||||
"There is no payment channel for this product.": "Es gibt keinen Zahlungskanal für dieses Produkt.",
|
||||
"This product is currently not in sale.": "Dieses Produkt steht derzeit nicht zum Verkauf.",
|
||||
"This product is currently not purchasable (No options available)": "Dieses Produkt ist derzeit nicht kaufbar (keine Optionen verfügbar)",
|
||||
"Total Price": "Gesamtpreis",
|
||||
"View Product": "Produkt anzeigen",
|
||||
"This product is currently not purchasable (No options available)": "This product is currently not purchasable (No options available)",
|
||||
"Total Price": "Total Price",
|
||||
"View Product": "View Product",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "Kann sich einloggen",
|
||||
"Can signup": "Kann sich registrieren",
|
||||
"Can unlink": "Entlinken möglich",
|
||||
"Category": "Kategorie",
|
||||
"Category - Tooltip": "Kennung zur Kategorisierung und Gruppierung von Elementen oder Inhalten, erleichtert Filterung und Verwaltung",
|
||||
"Channel No.": "Kanal Nr.",
|
||||
"Channel No. - Tooltip": "Eindeutige Nummer zur Identifizierung eines Kommunikations- oder Datenübertragungskanals, verwendet zur Unterscheidung verschiedener Übertragungswege",
|
||||
"Chat ID": "Chat-ID",
|
||||
@@ -999,8 +988,10 @@
|
||||
"Client secret 2 - Tooltip": "Backup-Client-Verifizierungsschlüssel, verwendet zur Verifizierung bei Ausfall des primären Schlüssels oder in speziellen Szenarien",
|
||||
"Content": "Inhalt",
|
||||
"Content - Tooltip": "Spezifische Informationen oder Daten in Nachrichten, Benachrichtigungen oder Dokumenten",
|
||||
"DB test": "DB-Test",
|
||||
"DB test - Tooltip": "DB-Test - Tooltip",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "SSL deaktivieren",
|
||||
"Disable SSL - Tooltip": "Ob die Deaktivierung des SSL-Protokolls bei der Kommunikation mit dem STMP-Server erfolgen soll",
|
||||
"Domain": "Domäne",
|
||||
"Domain - Tooltip": "Benutzerdefinierte Domain für Objektspeicher",
|
||||
"Edit Provider": "Provider bearbeiten",
|
||||
@@ -1010,10 +1001,10 @@
|
||||
"Email regex - Tooltip": "Nur E-Mails, die diesem regulären Ausdruck entsprechen, können sich registrieren oder anmelden",
|
||||
"Email title": "Email-Titel",
|
||||
"Email title - Tooltip": "Betreff der E-Mail",
|
||||
"Enable PKCE": "PKCE aktivieren",
|
||||
"Enable PKCE": "Enable PKCE",
|
||||
"Enable PKCE - Tooltip": "Enable PKCE - Tooltip",
|
||||
"Enable proxy": "Proxy aktivieren",
|
||||
"Enable proxy - Tooltip": "SOCKS5-Proxy beim Senden von E-Mails oder SMS aktivieren",
|
||||
"Enable proxy": "Enable proxy",
|
||||
"Enable proxy - Tooltip": "Enable socks5 Proxy when sending email or sms",
|
||||
"Endpoint": "Endpunkt",
|
||||
"Endpoint (Intranet)": "Endpunkt (Intranet)",
|
||||
"Endpoint - Tooltip": "URL des Dienstendpunkts",
|
||||
@@ -1040,13 +1031,13 @@
|
||||
"Key ID - Tooltip": "Eindeutige Kennung für den Schlüssel",
|
||||
"Key text": "Schlüsseltext",
|
||||
"Key text - Tooltip": "Inhalt des Schlüsseltexts",
|
||||
"LDAP port": "LDAP-Port",
|
||||
"LDAP port": "LDAP port",
|
||||
"Metadata": "Metadaten",
|
||||
"Metadata - Tooltip": "SAML-Metadaten",
|
||||
"Metadata url": "Metadaten-URL",
|
||||
"Metadata url - Tooltip": "SAML-Metadaten-URL",
|
||||
"Method - Tooltip": "Anmeldeverfahren, QR-Code oder Silent-Login",
|
||||
"Mobile": "Mobil",
|
||||
"Mobile": "Mobile",
|
||||
"New Provider": "Neuer Anbieter",
|
||||
"Parameter": "Parameter",
|
||||
"Parameter - Tooltip": "Konfigurationsparameter",
|
||||
@@ -1064,10 +1055,10 @@
|
||||
"Prompted": "ausgelöst",
|
||||
"Provider URL": "Anbieter-URL",
|
||||
"Provider URL - Tooltip": "URL zur Konfiguration des Dienstanbieters, dieses Feld dient nur als Referenz und wird in Casdoor nicht verwendet",
|
||||
"Provider test successful": "Provider-Test erfolgreich",
|
||||
"Provider test successful": "Provider test successful",
|
||||
"Public key": "Öffentlicher Schlüssel",
|
||||
"Public key - Tooltip": "Öffentlicher Schlüssel für Verschlüsselung",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret von RADIUS",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret of RADIUS",
|
||||
"Region": "Region",
|
||||
"Region - Tooltip": "Geografische Region des Dienstes",
|
||||
"Region ID": "Regions-ID",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "SP-ACS-URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL",
|
||||
"SP Entity ID": "SP-Entitäts-ID",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "Szene",
|
||||
"Scene - Tooltip": "Spezifisches Geschäftsszenario, in dem die Funktion oder Operation angewendet wird, verwendet zur Anpassung der logischen Verarbeitung für verschiedene Szenarien",
|
||||
"Scope": "Umfang",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Secret-Access-Key",
|
||||
"Secret access key - Tooltip": "Privater Schlüssel, der mit dem Zugriffsschlüssel gepaart ist, verwendet zum Signieren sensibler Operationen zur Verbesserung der Zugriffssicherheit",
|
||||
"Secret key": "Secret-Key",
|
||||
@@ -1123,8 +1111,8 @@
|
||||
"Sub type - Tooltip": "Weitere Unterkategorie unter dem Haupttyp, verwendet zur präziseren Unterscheidung von Objekten oder Funktionen",
|
||||
"Subject": "Betreff",
|
||||
"Subject - Tooltip": "E-Mail-Betreff",
|
||||
"Subtype": "Untertyp",
|
||||
"Subtype - Tooltip": "Untertyp - Tooltip",
|
||||
"Subtype": "Subtype",
|
||||
"Subtype - Tooltip": "Subtype - Tooltip",
|
||||
"Syncer test": "Synchronisierer-Test",
|
||||
"Syncer test - Tooltip": "Synchronisierer-Test",
|
||||
"Team ID": "Team-ID",
|
||||
@@ -1138,8 +1126,8 @@
|
||||
"Test SMTP Connection": "Testen Sie die SMTP-Verbindung",
|
||||
"Third-party": "Drittanbieter",
|
||||
"This field is required": "Dieses Feld ist erforderlich",
|
||||
"To address": "An-Adresse",
|
||||
"To address - Tooltip": "E-Mail-Adresse im Feld „An“",
|
||||
"To address": "To address",
|
||||
"To address - Tooltip": "Email address of \"To\"",
|
||||
"Token URL": "Token-URL",
|
||||
"Token URL - Tooltip": "Benutzerdefinierte OAuth Token-URL",
|
||||
"Use WeChat Media Platform in PC": "WeChat Media Platform am PC verwenden",
|
||||
@@ -1209,7 +1197,7 @@
|
||||
"Please input your last name!": "Bitte geben Sie Ihren Nachnamen ein!",
|
||||
"Please input your phone number!": "Bitte geben Sie Ihre Telefonnummer ein!",
|
||||
"Please input your real name!": "Bitte geben Sie Ihren richtigen Namen ein!",
|
||||
"Please input your {label}!": "Bitte geben Sie {label} ein!",
|
||||
"Please input your {label}!": "Please input your {label}!",
|
||||
"Please select your country code!": "Bitte wählen Sie Ihren Ländercode aus!",
|
||||
"Please select your country/region!": "Bitte wählen Sie Ihr Land/Ihre Region aus!",
|
||||
"Regex": "Regex",
|
||||
@@ -1222,7 +1210,7 @@
|
||||
"Text 4": "Text 4",
|
||||
"Text 5": "Text 5",
|
||||
"The input Email doesn't match the signup item regex!": "Die eingegebene E-Mail entspricht nicht dem Regex des Registrierungselements!",
|
||||
"The input doesn't match the signup item regex!": "Die Eingabe entspricht nicht dem Regex des Registrierungselements!",
|
||||
"The input doesn't match the signup item regex!": "The input doesn't match the signup item regex!",
|
||||
"The input is not invoice Tax ID!": "Die Eingabe ist keine Rechnungssteuer-ID!",
|
||||
"The input is not invoice title!": "Der Eingabewert ist nicht die Rechnungsbezeichnung!",
|
||||
"The input is not valid Phone!": "Die Eingabe ist kein gültiges Telefon!",
|
||||
@@ -1242,29 +1230,26 @@
|
||||
"New Subscription": "Neues Abonnement",
|
||||
"Start time": "Startzeit",
|
||||
"Start time - Tooltip": "Startzeit",
|
||||
"Subscription plan": "Abonnementplan",
|
||||
"Subscription pricing": "Abonnement-Preisgestaltung",
|
||||
"Subscription plan": "Subscription plan",
|
||||
"Subscription pricing": "Subscription pricing",
|
||||
"Suspended": "Ausgesetzt",
|
||||
"Upcoming": "Bevorstehend",
|
||||
"View Subscription": "Abonnement anzeigen"
|
||||
"View Subscription": "View Subscription"
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "API Token / Password",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "Admin-E-Mail",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "Zuordnungstabelle",
|
||||
"Affiliation table - Tooltip": "Datenbanktabellenname der Arbeitseinheit",
|
||||
"Avatar base URL": "Avatar-Basis-URL",
|
||||
"Avatar base URL - Tooltip": "URL-Präfix für die Avatar-Bilder",
|
||||
"Bind DN": "Bind-DN",
|
||||
"Bind DN": "Bind DN",
|
||||
"Casdoor column": "Casdoor-Spalte",
|
||||
"Column name": "Spaltenname",
|
||||
"Column type": "Spaltentyp",
|
||||
"Connect successfully": "Verbindung erfolgreich",
|
||||
"Corp ID": "Corp-ID",
|
||||
"Corp secret": "Corp-Secret",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp secret": "Corp secret",
|
||||
"Database": "Datenbank",
|
||||
"Database - Tooltip": "Der ursprüngliche Datenbankname",
|
||||
"Database type": "Datenbanktyp",
|
||||
@@ -1278,13 +1263,15 @@
|
||||
"Is read-only": "Nur lesbar",
|
||||
"Is read-only - Tooltip": "Nur lesbar",
|
||||
"New Syncer": "Neuer Syncer",
|
||||
"Paste your Google Workspace service account JSON key here": "Fügen Sie hier Ihren Google Workspace Service-Account-JSON-Schlüssel ein",
|
||||
"SCIM Server URL": "SCIM-Server-URL",
|
||||
"Paste your Google Workspace service account JSON key here": "Paste your Google Workspace service account JSON key here",
|
||||
"SCIM Server URL": "SCIM Server URL",
|
||||
"SSH host": "SSH-Host",
|
||||
"SSH password": "SSH-Passwort",
|
||||
"SSH port": "SSH-Port",
|
||||
"SSH user": "SSH-Benutzer",
|
||||
"Service account key": "Service-Account-Schlüssel",
|
||||
"SSL mode": "SSL-Modus",
|
||||
"SSL mode - Tooltip": "SSL-Modus",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "Synchronisierungsintervall",
|
||||
"Sync interval - Tooltip": "Einheit in Sekunden",
|
||||
"Table": "Tabelle",
|
||||
@@ -1292,8 +1279,8 @@
|
||||
"Table columns": "Tabellenspalten",
|
||||
"Table columns - Tooltip": "Tabellenspalten, die an der Datensynchronisation beteiligt sind. Spalten, die nicht an der Synchronisation beteiligt sind, müssen nicht hinzugefügt werden",
|
||||
"Test Connection": "Verbindung testen",
|
||||
"Test DB Connection": "DB-Verbindung testen",
|
||||
"Username (optional)": "Benutzername (optional)"
|
||||
"Test DB Connection": "Test DB Connection",
|
||||
"Username (optional)": "Username (optional)"
|
||||
},
|
||||
"system": {
|
||||
"API Latency": "API Latenz",
|
||||
@@ -1324,16 +1311,16 @@
|
||||
"Theme - Tooltip": "Stiltheme der Anwendung"
|
||||
},
|
||||
"ticket": {
|
||||
"Closed": "Geschlossen",
|
||||
"Edit Ticket": "Ticket bearbeiten",
|
||||
"In Progress": "In Bearbeitung",
|
||||
"Messages": "Nachrichten",
|
||||
"New Ticket": "Neues Ticket",
|
||||
"Open": "Offen",
|
||||
"Please enter a message": "Bitte geben Sie eine Nachricht ein",
|
||||
"Press Ctrl+Enter to send": "Drücken Sie Strg+Enter zum Senden",
|
||||
"Resolved": "Gelöst",
|
||||
"Type your message here...": "Geben Sie hier Ihre Nachricht ein..."
|
||||
"Closed": "Closed",
|
||||
"Edit Ticket": "Edit Ticket",
|
||||
"In Progress": "In Progress",
|
||||
"Messages": "Messages",
|
||||
"New Ticket": "New Ticket",
|
||||
"Open": "Open",
|
||||
"Please enter a message": "Please enter a message",
|
||||
"Press Ctrl+Enter to send": "Press Ctrl+Enter to send",
|
||||
"Resolved": "Resolved",
|
||||
"Type your message here...": "Type your message here..."
|
||||
},
|
||||
"token": {
|
||||
"Access token": "Access-Token",
|
||||
@@ -1355,7 +1342,7 @@
|
||||
"Amount - Tooltip": "Der Betrag der gehandelten Produkte",
|
||||
"Edit Transaction": "Transaktion bearbeiten",
|
||||
"New Transaction": "Neue Transaktion",
|
||||
"Recharge": "Aufladen"
|
||||
"Recharge": "Recharge"
|
||||
},
|
||||
"user": {
|
||||
"3rd-party logins": "Drittanbieter-Logins",
|
||||
@@ -1399,20 +1386,20 @@
|
||||
"ID card type": "Ausweistyp",
|
||||
"ID card type - Tooltip": "Art des Ausweises",
|
||||
"ID card with person": "Ausweis mit Person",
|
||||
"ID verification": "Identitätsprüfung",
|
||||
"ID verification - Tooltip": "Identitätsprüfung - Tooltip",
|
||||
"Identity verification successful": "Identitätsprüfung erfolgreich",
|
||||
"Identity verified": "Identität verifiziert",
|
||||
"ID verification": "ID verification",
|
||||
"ID verification - Tooltip": "ID verification - Tooltip",
|
||||
"Identity verification successful": "Identity verification successful",
|
||||
"Identity verified": "Identity verified",
|
||||
"Input your email": "Geben Sie Ihre E-Mail-Adresse ein",
|
||||
"Input your phone number": "Geben Sie Ihre Telefonnummer ein",
|
||||
"Is admin": "Ist Admin",
|
||||
"Is admin - Tooltip": "Ist ein Administrator der Organisation, zu der der Benutzer gehört",
|
||||
"Is deleted": "ist gelöscht",
|
||||
"Is deleted - Tooltip": "Gelöschte Benutzer behalten nur Datenbankdatensätze und können keine Operationen ausführen - Hinweis",
|
||||
"Is deleted - Tooltip": "\"Gelöschte Benutzer behalten nur Datenbankdatensätze und können keine Operationen ausführen.\"",
|
||||
"Is forbidden": "Ist verboten",
|
||||
"Is forbidden - Tooltip": "Verbotene Benutzer können sich nicht mehr einloggen",
|
||||
"Is online": "Ist online",
|
||||
"Is verified": "Ist verifiziert",
|
||||
"Is verified": "Is verified",
|
||||
"Karma": "Karma",
|
||||
"Karma - Tooltip": "Punkte zur Messung der Vertrauensstufe des Benutzers, beeinflussen Berechtigungen oder den Umfang der Dienstnutzung",
|
||||
"Keys": "Schlüssel",
|
||||
@@ -1437,16 +1424,16 @@
|
||||
"Other": "Andere",
|
||||
"Password set successfully": "Passwort erfolgreich festgelegt",
|
||||
"Phone cannot be empty": "Telefonnummer kann nicht leer sein",
|
||||
"Please enter your real name": "Bitte geben Sie Ihren echten Namen ein",
|
||||
"Please fill in ID card information first": "Bitte füllen Sie zuerst die Ausweisinformationen aus",
|
||||
"Please fill in your real name first": "Bitte geben Sie zuerst Ihren echten Namen ein",
|
||||
"Please enter your real name": "Please enter your real name",
|
||||
"Please fill in ID card information first": "Please fill in ID card information first",
|
||||
"Please fill in your real name first": "Please fill in your real name first",
|
||||
"Please select avatar from resources": "Bitte wählen Sie einen Avatar aus den Ressourcen aus",
|
||||
"Properties": "Eigenschaften",
|
||||
"Properties - Tooltip": "Eigenschaften des Benutzers",
|
||||
"Ranking": "Rang",
|
||||
"Ranking - Tooltip": "Position des Benutzers in der Rangfolge basierend auf Punkten, Aktivitätsniveau und anderen Metriken",
|
||||
"Re-enter New": "Neueingabe wiederholen",
|
||||
"Real name - Tooltip": "Echter Name - Tooltip",
|
||||
"Real name - Tooltip": "Real name - Tooltip",
|
||||
"Register source": "Registrierungsquelle",
|
||||
"Register source - Tooltip": "Die Quelle, von der aus der Benutzer registriert wurde",
|
||||
"Register type": "Registrierungstyp",
|
||||
@@ -1475,8 +1462,8 @@
|
||||
"User Profile": "Benutzerprofil",
|
||||
"Values": "Werte",
|
||||
"Verification code sent": "Bestätigungscode gesendet",
|
||||
"Verified": "Verifiziert",
|
||||
"Verify Identity": "Identität verifizieren",
|
||||
"Verified": "Verified",
|
||||
"Verify Identity": "Verify Identity",
|
||||
"WebAuthn credentials": "WebAuthn-Anmeldeinformationen",
|
||||
"Work": "Arbeit",
|
||||
"You have changed the username, please save your change first before modifying the password": "Sie haben den Benutzernamen geändert. Bitte speichern Sie zuerst, bevor Sie das Passwort ändern.",
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
"Enable signin session - Tooltip": "Whether Casdoor maintains a session after logging into Casdoor from the application",
|
||||
"Enable signup": "Enable signup",
|
||||
"Enable signup - Tooltip": "Whether to allow users to register a new account",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "Failed signin frozen time",
|
||||
"Failed signin frozen time - Tooltip": "Waiting time after exceeding the number of failed login attempts. Users can only log in again after the waiting time expires. Default value is 15 minutes. The set value must be a positive integer",
|
||||
"Failed signin limit": "Failed signin limit",
|
||||
@@ -152,7 +151,6 @@
|
||||
"Signup items - Tooltip": "Items for users to fill in when registering new accounts",
|
||||
"Single Choice": "Single Choice",
|
||||
"Small icon": "Small icon",
|
||||
"Static Value": "Static Value",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "Only users with the tag that is listed in the application tags can login",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
@@ -186,7 +184,9 @@
|
||||
"Expire in years - Tooltip": "Validity period of the certificate, in years",
|
||||
"New Cert": "New Cert",
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key corresponding to the public key certificate"
|
||||
"Private key - Tooltip": "Private key corresponding to the public key certificate",
|
||||
"Scope - Tooltip": "Usage scenarios of the certificate",
|
||||
"Type - Tooltip": "Type of certificate"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Code you received",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "Applications that require authentication",
|
||||
"Apps": "Apps",
|
||||
"Authorization": "Authorization",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "Avatar",
|
||||
"Avatar - Tooltip": "Public avatar image for the user",
|
||||
"Back": "Back",
|
||||
@@ -284,8 +283,6 @@
|
||||
"Cancel": "Cancel",
|
||||
"Captcha": "Captcha",
|
||||
"Cart": "Cart",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cert": "Cert",
|
||||
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
|
||||
"Certs": "Certs",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "The auth type of SSH connection",
|
||||
"Save": "Save",
|
||||
"Save & Exit": "Save & Exit",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "Search",
|
||||
"Send": "Send",
|
||||
"Session ID": "Session ID",
|
||||
@@ -535,7 +530,6 @@
|
||||
"Transactions": "Transactions",
|
||||
"True": "True",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "URL link",
|
||||
"Unknown application name": "Unknown application name",
|
||||
@@ -873,8 +867,6 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "Edit Plan",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "New Plan",
|
||||
"Period": "Period",
|
||||
"Period - Tooltip": "Period for the plan",
|
||||
@@ -905,7 +897,6 @@
|
||||
"Amount": "Amount",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
@@ -918,11 +909,9 @@
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image of product",
|
||||
"Information": "Information",
|
||||
"Invalid product": "Invalid product",
|
||||
"Is recharge": "Is recharge",
|
||||
"Is recharge - Tooltip": "Whether the current product is to recharge balance",
|
||||
"New Product": "New Product",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "PayPal",
|
||||
"Payment cancelled": "Payment cancelled",
|
||||
@@ -935,12 +924,10 @@
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Quantity": "Quantity",
|
||||
"Quantity - Tooltip": "Quantity of product",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Preset recharge amounts",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Return URL": "Return URL",
|
||||
"Return URL - Tooltip": "URL to return to after successful purchase",
|
||||
"SKU": "SKU",
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "Can signin",
|
||||
"Can signup": "Can signup",
|
||||
"Can unlink": "Can unlink",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Identifier for categorizing and grouping items or content, facilitating filtering and management",
|
||||
"Channel No.": "Channel No.",
|
||||
"Channel No. - Tooltip": "Unique number identifying a communication or data transmission channel, used to distinguish different transmission paths",
|
||||
"Chat ID": "Chat ID",
|
||||
@@ -1001,6 +990,8 @@
|
||||
"Content - Tooltip": "Specific information or data contained in messages, notifications, or documents",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "Disable SSL",
|
||||
"Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
|
||||
"Domain": "Domain",
|
||||
"Domain - Tooltip": "Custom domain for object storage",
|
||||
"Edit Provider": "Edit Provider",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL",
|
||||
"SP Entity ID": "SP Entity ID",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "Scene",
|
||||
"Scene - Tooltip": "Specific business scenario where the function or operation applies, used to adapt logic processing for different scenarios",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Secret access key",
|
||||
"Secret access key - Tooltip": "Private key paired with the access key, used for signing sensitive operations to enhance access security",
|
||||
"Secret key": "Secret key",
|
||||
@@ -1250,9 +1238,6 @@
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "API Token / Password",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "Affiliation table",
|
||||
"Affiliation table - Tooltip": "Database table name of the work unit",
|
||||
@@ -1284,6 +1269,8 @@
|
||||
"SSH password": "SSH password",
|
||||
"SSH port": "SSH port",
|
||||
"SSH user": "SSH user",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "The SSL mode used when connecting to the database",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "Sync interval",
|
||||
"Sync interval - Tooltip": "Unit in seconds",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"Exit impersonation": "Salir de suplantación",
|
||||
"Exit impersonation": "Exit impersonation",
|
||||
"Logout": "Cierre de sesión",
|
||||
"My Account": "Mi cuenta",
|
||||
"Sign Up": "Registrarse"
|
||||
@@ -22,22 +22,22 @@
|
||||
"Add Face ID with Image": "Agregar Face ID con imagen",
|
||||
"Always": "siempre",
|
||||
"Array": "Array",
|
||||
"Authentication": "Autenticación",
|
||||
"Authentication": "Authentication",
|
||||
"Auto signin": "Inicio de sesión automático",
|
||||
"Auto signin - Tooltip": "Cuando existe una sesión iniciada en Casdoor, se utiliza automáticamente para el inicio de sesión del lado de la aplicación",
|
||||
"Background URL": "URL de fondo",
|
||||
"Background URL - Tooltip": "URL de la imagen de fondo utilizada en la página de inicio de sesión",
|
||||
"Background URL Mobile": "URL de fondo para móvil",
|
||||
"Background URL Mobile - Tooltip": "URL de la imagen de fondo para dispositivos móviles",
|
||||
"Basic": "Básico",
|
||||
"Basic": "Basic",
|
||||
"Big icon": "Icono grande",
|
||||
"Binding providers": "Proveedores de vinculación",
|
||||
"CSS style": "Estilo CSS",
|
||||
"Center": "Centro",
|
||||
"Code resend timeout": "Tiempo de espera para reenviar código",
|
||||
"Code resend timeout - Tooltip": "Período de tiempo (en segundos) que los usuarios deben esperar antes de solicitar otro código de verificación. Establecer en 0 para usar el valor predeterminado global (60 segundos)",
|
||||
"Cookie expire": "Caducidad de cookie",
|
||||
"Cookie expire - Tooltip": "Tiempo de caducidad de la cookie - Sugerencia",
|
||||
"Cookie expire": "Cookie expire",
|
||||
"Cookie expire - Tooltip": "Cookie expire - Tooltip",
|
||||
"Copy SAML metadata URL": "Copia la URL de metadatos SAML",
|
||||
"Copy prompt page URL": "Copiar URL de la página del prompt",
|
||||
"Copy signin page URL": "Copiar la URL de la página de inicio de sesión",
|
||||
@@ -47,9 +47,9 @@
|
||||
"Custom CSS - Tooltip": "Estilo CSS de los formularios de registro, inicio de sesión y olvido de contraseña (por ejemplo, agregar bordes y sombras)",
|
||||
"Custom CSS Mobile": "CSS personalizado para móvil",
|
||||
"Custom CSS Mobile - Edit": "CSS personalizado para móvil - Editar",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personalizado para dispositivos móviles - Sugerencia",
|
||||
"Disable SAML attributes": "Desactivar atributos SAML",
|
||||
"Disable SAML attributes - Tooltip": "Desactivar atributos SAML - Sugerencia",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personalizado para dispositivos móviles",
|
||||
"Disable SAML attributes": "Disable SAML attributes",
|
||||
"Disable SAML attributes - Tooltip": "Disable SAML attributes - Tooltip",
|
||||
"Disable signin": "Desactivar inicio de sesión",
|
||||
"Disable signin - Tooltip": "Desactivar el inicio de sesión para los usuarios",
|
||||
"Dynamic": "Dinámico",
|
||||
@@ -60,17 +60,16 @@
|
||||
"Enable SAML C14N10 - Tooltip": "Usar C14N10 en lugar de C14N11 en SAML",
|
||||
"Enable SAML POST binding": "Habilitar enlace POST de SAML",
|
||||
"Enable SAML POST binding - Tooltip": "El enlace HTTP POST utiliza campos de entrada en un formulario HTML para enviar mensajes SAML. Habilítelo cuando su SP lo utilice.",
|
||||
"Enable SAML assertion signature": "Habilitar firma de aserción SAML",
|
||||
"Enable SAML assertion signature - Tooltip": "Habilitar firma de aserción SAML - Sugerencia",
|
||||
"Enable SAML assertion signature": "Enable SAML assertion signature",
|
||||
"Enable SAML assertion signature - Tooltip": "Enable SAML assertion signature - Tooltip",
|
||||
"Enable SAML compression": "Activar la compresión SAML",
|
||||
"Enable SAML compression - Tooltip": "Si comprimir o no los mensajes de respuesta SAML cuando se utiliza Casdoor como proveedor de identidad SAML",
|
||||
"Enable exclusive signin": "Habilitar inicio de sesión exclusivo",
|
||||
"Enable exclusive signin - Tooltip": "Cuando el inicio de sesión exclusivo está habilitado, el usuario no puede tener varias sesiones activas",
|
||||
"Enable exclusive signin": "Enable exclusive signin",
|
||||
"Enable exclusive signin - Tooltip": "When exclusive signin enabled, user cannot have multiple active session",
|
||||
"Enable side panel": "Habilitar panel lateral",
|
||||
"Enable signin session - Tooltip": "Si Casdoor mantiene una sesión después de iniciar sesión en Casdoor desde la aplicación",
|
||||
"Enable signup": "Habilitar registro",
|
||||
"Enable signup - Tooltip": "Ya sea permitir que los usuarios registren una nueva cuenta",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "Tiempo de congelación tras inicio fallido",
|
||||
"Failed signin frozen time - Tooltip": "Tiempo durante el cual la cuenta está congelada después de intentos fallidos de inicio de sesión",
|
||||
"Failed signin limit": "Límite de intentos fallidos de inicio",
|
||||
@@ -102,8 +101,8 @@
|
||||
"Logged out successfully": "Cerró sesión exitosamente",
|
||||
"MFA remember time": "Tiempo de recordatorio MFA",
|
||||
"MFA remember time - Tooltip": "Configura la duración durante la cual una cuenta se recuerda como confiable después de un inicio de sesión MFA exitoso",
|
||||
"Menu mode": "Modo de menú",
|
||||
"Menu mode - Tooltip": "Modo de menú - Sugerencia",
|
||||
"Menu mode": "Menu mode",
|
||||
"Menu mode - Tooltip": "Menu mode - Tooltip",
|
||||
"Multiple Choices": "Opciones múltiples",
|
||||
"New Application": "Nueva aplicación",
|
||||
"No verification": "Sin verificación",
|
||||
@@ -112,17 +111,17 @@
|
||||
"Order": "Orden",
|
||||
"Order - Tooltip": "Cuanto menor sea el valor, más alto se clasificará en la página de Aplicaciones",
|
||||
"Org choice mode": "Modo de selección de organización",
|
||||
"Org choice mode - Tooltip": "Método utilizado para seleccionar la organización para iniciar sesión - Sugerencia",
|
||||
"Org choice mode - Tooltip": "Modo de elección de organización",
|
||||
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "Por favor, habilita \"Sesión de inicio de sesión\" primero antes de habilitar \"Inicio de sesión automático\"",
|
||||
"Please input your application!": "¡Por favor, ingrese su solicitud!",
|
||||
"Please input your organization!": "¡Por favor, ingrese su organización!",
|
||||
"Please select a HTML file": "Por favor, seleccione un archivo HTML",
|
||||
"Pop up": "Ventana emergente",
|
||||
"Providers": "Proveedores",
|
||||
"Providers": "Providers",
|
||||
"Random": "Aleatorio",
|
||||
"Real name": "Nombre real",
|
||||
"Redirect URL": "Redireccionar URL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redireccionamiento (URL de enlace de publicación del servicio consumidor de afirmaciones) - Sugerencia",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redireccionamiento (URL de enlace de publicación del servicio consumidor de afirmaciones)",
|
||||
"Redirect URLs": "Redireccionar URLs",
|
||||
"Redirect URLs - Tooltip": "Lista de URL de redireccionamiento permitidos, con soporte para coincidencias de expresiones regulares; las URL que no estén en la lista no se redirigirán",
|
||||
"Refresh token expire": "Token de actualización expirado",
|
||||
@@ -133,28 +132,27 @@
|
||||
"SAML hash algorithm": "Algoritmo hash SAML",
|
||||
"SAML hash algorithm - Tooltip": "Algoritmo hash para la firma SAML",
|
||||
"SAML metadata": "Metadatos de SAML",
|
||||
"SAML metadata - Tooltip": "Los metadatos del protocolo SAML - Sugerencia",
|
||||
"SAML metadata - Tooltip": "Los metadatos del protocolo SAML",
|
||||
"SAML reply URL": "URL de respuesta SAML",
|
||||
"Security": "Seguridad",
|
||||
"Security": "Security",
|
||||
"Select": "Seleccionar",
|
||||
"Side panel HTML": "Panel lateral HTML",
|
||||
"Side panel HTML - Edit": "Panel lateral HTML - Editar",
|
||||
"Side panel HTML - Tooltip": "Personalizar el código HTML del panel lateral de la página de inicio de sesión - Sugerencia",
|
||||
"Side panel HTML - Tooltip": "Personaliza el código HTML del panel lateral de la página de inicio de sesión",
|
||||
"Sign Up Error": "Error de registro",
|
||||
"Signin": "Iniciar sesión",
|
||||
"Signin (Default True)": "Iniciar sesión (Verdadero por defecto)",
|
||||
"Signin items": "Elementos de inicio",
|
||||
"Signin items - Tooltip": "Elementos para que los usuarios completen al iniciar sesión - Sugerencia",
|
||||
"Signin items - Tooltip": "Elementos de inicio de sesión",
|
||||
"Signin methods": "Métodos de inicio",
|
||||
"Signin methods - Tooltip": "Métodos de inicio - Información adicional",
|
||||
"Signin session": "Sesión de inicio de sesión",
|
||||
"Signup items": "Artículos de registro",
|
||||
"Signup items - Tooltip": "Elementos para que los usuarios completen al registrar nuevas cuentas - Sugerencia",
|
||||
"Signup items - Tooltip": "Elementos para que los usuarios los completen al registrar nuevas cuentas",
|
||||
"Single Choice": "Opción única",
|
||||
"Small icon": "Icono pequeño",
|
||||
"Static Value": "Static Value",
|
||||
"String": "Cadena",
|
||||
"Tags - Tooltip": "Solo los usuarios con la etiqueta que esté listada en las etiquetas de la aplicación pueden iniciar sesión - Sugerencia",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "Solo los usuarios con la etiqueta que esté listada en las etiquetas de la aplicación pueden iniciar sesión",
|
||||
"The application does not allow to sign up new account": "La aplicación no permite registrarse una cuenta nueva",
|
||||
"Token expire": "Token expirado",
|
||||
"Token expire - Tooltip": "Tiempo de expiración del token de acceso",
|
||||
@@ -164,7 +162,7 @@
|
||||
"Token format - Tooltip": "El formato del token de acceso",
|
||||
"Token signing method": "Método de firma del token",
|
||||
"Token signing method - Tooltip": "Método de firma del token JWT, debe ser el mismo algoritmo que el certificado",
|
||||
"UI Customization": "Personalización de la interfaz",
|
||||
"UI Customization": "UI Customization",
|
||||
"Use Email as NameID": "Usar correo electrónico como NameID",
|
||||
"Use Email as NameID - Tooltip": "Usar correo electrónico como NameID - Información adicional",
|
||||
"Vertical": "Vertical",
|
||||
@@ -184,9 +182,11 @@
|
||||
"Edit Cert": "Editar Certificado",
|
||||
"Expire in years": "Vencer en años",
|
||||
"Expire in years - Tooltip": "Período de validez del certificado, en años",
|
||||
"New Cert": "Nuevo certificado",
|
||||
"New Cert": "ificado",
|
||||
"Private key": "Clave privada",
|
||||
"Private key - Tooltip": "Clave privada correspondiente al certificado de clave pública"
|
||||
"Private key - Tooltip": "Clave privada correspondiente al certificado de clave pública",
|
||||
"Scope - Tooltip": "Escenarios de uso del certificado",
|
||||
"Type - Tooltip": "Tipo de certificado"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Código que recibió",
|
||||
@@ -250,7 +250,7 @@
|
||||
"Width": "Ancho"
|
||||
},
|
||||
"general": {
|
||||
"A normal user can only modify the permission submitted by itself": "Un usuario normal solo puede modificar el permiso enviado por sí mismo",
|
||||
"A normal user can only modify the permission submitted by itself": "A normal user can only modify the permission submitted by itself",
|
||||
"AI Assistant": "Asistente de IA",
|
||||
"API key": "Clave API",
|
||||
"API key - Tooltip": "Clave API para acceder al servicio",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "Aplicaciones que requieren autenticación",
|
||||
"Apps": "Aplicaciones",
|
||||
"Authorization": "Autorización",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "Avatar",
|
||||
"Avatar - Tooltip": "Imagen de avatar pública para el usuario",
|
||||
"Back": "Atrás",
|
||||
@@ -283,13 +282,11 @@
|
||||
"Business & Payments": "Negocios y pagos",
|
||||
"Cancel": "Cancelar",
|
||||
"Captcha": "Captcha",
|
||||
"Cart": "Carrito",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cert": "Certificado",
|
||||
"Cart": "Cart",
|
||||
"Cert": "ificado",
|
||||
"Cert - Tooltip": "El certificado de clave pública que necesita ser verificado por el SDK del cliente correspondiente a esta aplicación",
|
||||
"Certs": "Certificaciones",
|
||||
"Clear": "Limpiar",
|
||||
"Clear": "Clear",
|
||||
"Click to Upload": "Haz clic para cargar",
|
||||
"Client IP": "IP del cliente",
|
||||
"Close": "Cerca",
|
||||
@@ -310,12 +307,12 @@
|
||||
"Delete": "Eliminar",
|
||||
"Description": "Descripción",
|
||||
"Description - Tooltip": "Información detallada de descripción para referencia, Casdoor en sí no la utilizará",
|
||||
"Detail": "Detalle",
|
||||
"Detail": "详情",
|
||||
"Disable": "Deshabilitar",
|
||||
"Display name": "Nombre de pantalla",
|
||||
"Display name - Tooltip": "Un nombre fácil de usar y leer que se muestra públicamente en la interfaz de usuario",
|
||||
"Down": "Abajo",
|
||||
"Download template": "Descargar plantilla",
|
||||
"Download template": "Download template",
|
||||
"Edit": "Editar",
|
||||
"Email": "Correo electrónico",
|
||||
"Email - Tooltip": "Dirección de correo electrónico válida",
|
||||
@@ -330,21 +327,21 @@
|
||||
"Enabled successfully": "Habilitado exitosamente",
|
||||
"Enforcers": "Aplicadores",
|
||||
"Failed to add": "No se pudo agregar",
|
||||
"Failed to cancel": "Error al cancelar",
|
||||
"Failed to cancel": "Failed to cancel",
|
||||
"Failed to connect to server": "No se pudo conectar al servidor",
|
||||
"Failed to copy": "Error al copiar",
|
||||
"Failed to delete": "No se pudo eliminar",
|
||||
"Failed to enable": "Error al habilitar",
|
||||
"Failed to get": "Error al obtener",
|
||||
"Failed to load": "Error al cargar",
|
||||
"Failed to log out": "Error al cerrar sesión",
|
||||
"Failed to load": "Failed to load",
|
||||
"Failed to log out": "Failed to log out",
|
||||
"Failed to remove": "Error al eliminar",
|
||||
"Failed to save": "No se pudo guardar",
|
||||
"Failed to send": "Error al enviar",
|
||||
"Failed to send": "Failed to send",
|
||||
"Failed to sync": "Error al sincronizar",
|
||||
"Failed to unlink": "Error al desvincular",
|
||||
"Failed to update": "Error al actualizar",
|
||||
"Failed to upload": "Error al subir",
|
||||
"Failed to unlink": "Failed to unlink",
|
||||
"Failed to update": "Failed to update",
|
||||
"Failed to upload": "Failed to upload",
|
||||
"Failed to verify": "Error al verificar",
|
||||
"False": "Falso",
|
||||
"Favicon": "Favicon",
|
||||
@@ -357,7 +354,7 @@
|
||||
"Forget URL - Tooltip": "URL personalizada para la página \"Olvidé mi contraseña\". Si no se establece, se utilizará la página \"Olvidé mi contraseña\" predeterminada de Casdoor. Cuando se establezca, el enlace \"Olvidé mi contraseña\" en la página de inicio de sesión redireccionará a esta URL",
|
||||
"Forms": "Formularios",
|
||||
"Found some texts still not translated? Please help us translate at": "¿Encontraste algunos textos que aún no están traducidos? Por favor, ayúdanos a traducirlos en",
|
||||
"Generate": "Generar",
|
||||
"Generate": "Generate",
|
||||
"Go to enable": "Ir a habilitar",
|
||||
"Go to writable demo site?": "¿Ir al sitio demo editable?",
|
||||
"Groups": "Grupos",
|
||||
@@ -370,7 +367,7 @@
|
||||
"IP whitelist": "Lista blanca de IP",
|
||||
"IP whitelist - Tooltip": "Lista blanca de IP",
|
||||
"Identity": "Identidad",
|
||||
"Impersonation": "Suplantación",
|
||||
"Impersonation": "Impersonation",
|
||||
"Invitations": "Invitaciones",
|
||||
"Is enabled": "Está habilitado",
|
||||
"Is enabled - Tooltip": "Establecer si se puede usar",
|
||||
@@ -403,21 +400,21 @@
|
||||
"Name": "Nombre",
|
||||
"Name - Tooltip": "ID único basado en cadenas",
|
||||
"Name format": "Formato del nombre",
|
||||
"No products available": "No hay productos disponibles",
|
||||
"No sheets found in file": "No se encontraron hojas en el archivo",
|
||||
"No verification method": "No hay método de verificación",
|
||||
"No products available": "No products available",
|
||||
"No sheets found in file": "No sheets found in file",
|
||||
"No verification method": "No verification method",
|
||||
"Non-LDAP": "No LDAP",
|
||||
"None": "Ninguno",
|
||||
"OAuth providers": "Proveedores de OAuth",
|
||||
"OFF": "DESACTIVADO",
|
||||
"OK": "Aceptar",
|
||||
"ON": "ACTIVADO",
|
||||
"Only 1 MFA method can be required": "Solo se puede requerir 1 método MFA",
|
||||
"Or": "O",
|
||||
"Orders": "Pedidos",
|
||||
"Only 1 MFA method can be required": "Only 1 MFA method can be required",
|
||||
"Or": "Or",
|
||||
"Orders": "Orders",
|
||||
"Organization": "Organización",
|
||||
"Organization - Tooltip": "Similar a conceptos como inquilinos o grupos de usuarios, cada usuario y aplicación pertenece a una organización",
|
||||
"Organization is null": "La organización es nula",
|
||||
"Organization is null": "Organization is null",
|
||||
"Organizations": "Organizaciones",
|
||||
"Password": "Contraseña",
|
||||
"Password - Tooltip": "Asegúrate de que la contraseña sea correcta",
|
||||
@@ -440,21 +437,21 @@
|
||||
"Phone - Tooltip": "Número de teléfono",
|
||||
"Phone only": "Solo teléfono",
|
||||
"Phone or Email": "Teléfono o correo electrónico",
|
||||
"Place Order": "Realizar pedido",
|
||||
"Place Order": "Place Order",
|
||||
"Plain": "Plano",
|
||||
"Plan": "Plan",
|
||||
"Plan - Tooltip": "Plan de suscripción",
|
||||
"Plans": "Planes",
|
||||
"Plans - Tooltip": "Planes - Información adicional",
|
||||
"Please complete the captcha correctly": "Por favor, completa el captcha correctamente",
|
||||
"Please complete the captcha correctly": "Please complete the captcha correctly",
|
||||
"Please input your search": "Por favor ingrese su búsqueda",
|
||||
"Please select at least 1 user first": "Por favor, selecciona al menos 1 usuario primero",
|
||||
"Please select at least 1 user first": "Please select at least 1 user first",
|
||||
"Preview": "Avance",
|
||||
"Preview - Tooltip": "Vista previa de los efectos configurados",
|
||||
"Pricing": "Precios",
|
||||
"Pricing - Tooltip": "Precios - Información adicional",
|
||||
"Pricings": "Precios",
|
||||
"Product Store": "Tienda de productos",
|
||||
"Product Store": "Product Store",
|
||||
"Products": "Productos",
|
||||
"Provider": "Proveedor",
|
||||
"Provider - Tooltip": "Proveedores de pago a configurar, incluyendo PayPal, Alipay, WeChat Pay, etc.",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "El tipo de autenticación de conexión SSH",
|
||||
"Save": "Guardar",
|
||||
"Save & Exit": "Guardar y salir",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "Buscar",
|
||||
"Send": "Enviar",
|
||||
"Session ID": "ID de sesión",
|
||||
@@ -498,13 +493,13 @@
|
||||
"Sorry, you do not have permission to access this page or logged in status invalid.": "Lo siento, no tiene permiso para acceder a esta página o su estado de inicio de sesión es inválido.",
|
||||
"State": "Estado",
|
||||
"State - Tooltip": "Estado",
|
||||
"Status": "Estado",
|
||||
"Status": "Status",
|
||||
"Subscriptions": "Suscripciones",
|
||||
"Successfully added": "Éxito al agregar",
|
||||
"Successfully canceled": "Cancelado con éxito",
|
||||
"Successfully canceled": "Successfully canceled",
|
||||
"Successfully copied": "Copiado exitosamente",
|
||||
"Successfully deleted": "Éxito en la eliminación",
|
||||
"Successfully executed": "Ejecutado con éxito",
|
||||
"Successfully executed": "Successfully executed",
|
||||
"Successfully removed": "Eliminado exitosamente",
|
||||
"Successfully saved": "Guardado exitosamente",
|
||||
"Successfully sent": "Enviado exitosamente",
|
||||
@@ -519,9 +514,9 @@
|
||||
"Syncers": "Sincronizadores",
|
||||
"System Info": "Información del Sistema",
|
||||
"Tab": "Pestaña",
|
||||
"The actions cannot be empty": "Las acciones no pueden estar vacías",
|
||||
"The resources cannot be empty": "Los recursos no pueden estar vacíos",
|
||||
"The users and roles cannot be empty at the same time": "Los usuarios y los roles no pueden estar vacíos al mismo tiempo",
|
||||
"The actions cannot be empty": "The actions cannot be empty",
|
||||
"The resources cannot be empty": "The resources cannot be empty",
|
||||
"The users and roles cannot be empty at the same time": "The users and roles cannot be empty at the same time",
|
||||
"There was a problem signing you in..": "Hubo un problema al iniciar sesión...",
|
||||
"This is a read-only demo site!": "¡Este es un sitio de demostración solo de lectura!",
|
||||
"Tickets": "Tickets",
|
||||
@@ -535,18 +530,17 @@
|
||||
"Transactions": "Transacciones",
|
||||
"True": "Verdadero",
|
||||
"Type": "Tipo",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "Enlace de URL",
|
||||
"Unknown application name": "Nombre de aplicación desconocido",
|
||||
"Unknown authentication type": "Tipo de autenticación desconocido",
|
||||
"Unknown application name": "Unknown application name",
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "Arriba",
|
||||
"Updated time": "Tiempo actualizado",
|
||||
"Upload (.xlsx)": "Subir (.xlsx)",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "Usuario",
|
||||
"User - Tooltip": "Asegúrate de que el nombre de usuario sea correcto",
|
||||
"User Management": "Gestión de usuarios",
|
||||
"User already exists": "El usuario ya existe",
|
||||
"User already exists": "User already exists",
|
||||
"User containers": "Piscinas de usuarios",
|
||||
"User type": "Tipo de usuario",
|
||||
"User type - Tooltip": "Etiquetas a las que el usuario pertenece, con una configuración predeterminada en \"usuario-normal\"",
|
||||
@@ -554,10 +548,10 @@
|
||||
"Users - Tooltip": "Usuarios - Información adicional",
|
||||
"Users under all organizations": "Usuarios bajo todas las organizaciones",
|
||||
"Verifications": "Verificaciones",
|
||||
"View": "Ver",
|
||||
"View": "View",
|
||||
"Webhooks": "Webhooks",
|
||||
"You can only select one physical group": "Solo puedes seleccionar un grupo físico",
|
||||
"You must select a picture first": "Primero debes seleccionar una imagen",
|
||||
"You must select a picture first": "You must select a picture first",
|
||||
"empty": "vacío",
|
||||
"remove": "eliminar",
|
||||
"{total} in total": "{total} en total"
|
||||
@@ -632,7 +626,7 @@
|
||||
"login": {
|
||||
"Auto sign in": "Inicio de sesión automático",
|
||||
"Back button": "Botón atrás",
|
||||
"Click the button below to sign in with Telegram": "Haz clic en el botón de abajo para iniciar sesión con Telegram",
|
||||
"Click the button below to sign in with Telegram": "Click the button below to sign in with Telegram",
|
||||
"Continue with": "Continúe con",
|
||||
"Email or phone": "Correo electrónico o teléfono",
|
||||
"Face ID": "Face ID",
|
||||
@@ -655,12 +649,12 @@
|
||||
"Please input your Email!": "¡Por favor ingresa tu correo electrónico!",
|
||||
"Please input your LDAP username!": "¡Por favor ingresa tu usuario LDAP!",
|
||||
"Please input your Phone!": "¡Por favor ingresa tu teléfono!",
|
||||
"Please input your RADIUS password!": "¡Por favor, introduce tu contraseña RADIUS!",
|
||||
"Please input your RADIUS username!": "¡Por favor, introduce tu usuario RADIUS!",
|
||||
"Please input your RADIUS password!": "Please input your RADIUS password!",
|
||||
"Please input your RADIUS username!": "Please input your RADIUS username!",
|
||||
"Please input your code!": "¡Por favor ingrese su código!",
|
||||
"Please input your organization name!": "¡Por favor ingresa el nombre de tu organización!",
|
||||
"Please input your password!": "¡Ingrese su contraseña, por favor!",
|
||||
"Please input your push notification receiver!": "¡Por favor, introduce el destinatario de notificaciones push!",
|
||||
"Please input your push notification receiver!": "Please input your push notification receiver!",
|
||||
"Please load the webpage using HTTPS, otherwise the camera cannot be accessed": "Por favor carga la página usando HTTPS, de lo contrario no se puede acceder a la cámara",
|
||||
"Please provide permission to access the camera": "Por favor otorga permiso para acceder a la cámara",
|
||||
"Please select an organization": "Por favor selecciona una organización",
|
||||
@@ -671,7 +665,7 @@
|
||||
"Select organization": "Seleccionar organización",
|
||||
"Sign In": "Iniciar sesión",
|
||||
"Sign in with Face ID": "Iniciar sesión con Face ID",
|
||||
"Sign in with Telegram": "Iniciar sesión con Telegram",
|
||||
"Sign in with Telegram": "Sign in with Telegram",
|
||||
"Sign in with WebAuthn": "Iniciar sesión con WebAuthn",
|
||||
"Sign in with {type}": "Inicia sesión con {tipo}",
|
||||
"Signin button": "Botón de inicio",
|
||||
@@ -704,7 +698,7 @@
|
||||
"Please confirm the information below": "Por favor confirma la siguiente información",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Por favor guarda este código de recuperación. Si tu dispositivo no puede proporcionar un código de autenticación, puedes restablecer la autenticación MFA con este código",
|
||||
"Protect your account with Multi-factor authentication": "Protege tu cuenta con autenticación multifactor",
|
||||
"Push notification receiver": "Destinatario de notificaciones push",
|
||||
"Push notification receiver": "Push notification receiver",
|
||||
"Recovery code": "Código de recuperación",
|
||||
"Remember this account for {hour} hours": "Recordar esta cuenta por {hour} horas",
|
||||
"Scan the QR code with your Authenticator App": "Escanea el código QR con tu aplicación de autenticación",
|
||||
@@ -714,17 +708,17 @@
|
||||
"To ensure the security of your account, it is required to enable multi-factor authentication": "Para asegurar la seguridad de tu cuenta, es obligatorio habilitar la autenticación multifactor",
|
||||
"Use Authenticator App": "Usar aplicación de autenticación",
|
||||
"Use Email": "Usar correo electrónico",
|
||||
"Use Push Notification": "Usar notificación push",
|
||||
"Use Radius": "Usar RADIUS",
|
||||
"Use Push Notification": "Use Push Notification",
|
||||
"Use Radius": "Use Radius",
|
||||
"Use SMS": "Usar SMS",
|
||||
"Use SMS verification code": "Usar código de verificación SMS",
|
||||
"Use a recovery code": "Usar un código de recuperación",
|
||||
"Verify Code": "Verificar código",
|
||||
"Verify Password": "Verificar contraseña",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "Has habilitado autenticación multifactor, por favor haz clic en 'Enviar código' para continuar",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "Has habilitado la autenticación multifactor. Por favor, introduce la contraseña RADIUS",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "You have enabled Multi-Factor Authentication, please enter the RADIUS password",
|
||||
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "Has habilitado autenticación multifactor, por favor ingresa el código TOTP",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "Has habilitado la autenticación multifactor. Por favor, introduce el código de verificación de la notificación push",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "You have enabled Multi-Factor Authentication, please enter the verification code from push notification",
|
||||
"Your email is": "Tu correo electrónico es",
|
||||
"Your phone is": "Tu teléfono es",
|
||||
"preferred": "preferido"
|
||||
@@ -738,29 +732,29 @@
|
||||
"New Model": "Nuevo modelo"
|
||||
},
|
||||
"order": {
|
||||
"Cancel time": "Hora de cancelación",
|
||||
"Edit Order": "Editar pedido",
|
||||
"New Order": "Nuevo pedido",
|
||||
"Order not found": "Pedido no encontrado",
|
||||
"Pay": "Pagar",
|
||||
"Payment failed time": "Hora del fallo de pago",
|
||||
"Payment time": "Hora de pago",
|
||||
"Price": "Precio",
|
||||
"Return to Order List": "Volver a la lista de pedidos",
|
||||
"Timeout time": "Hora de expiración",
|
||||
"View Order": "Ver pedido"
|
||||
"Cancel time": "Cancel time",
|
||||
"Edit Order": "Edit Order",
|
||||
"New Order": "New Order",
|
||||
"Order not found": "Order not found",
|
||||
"Pay": "Pay",
|
||||
"Payment failed time": "Payment failed time",
|
||||
"Payment time": "Payment time",
|
||||
"Price": "Price",
|
||||
"Return to Order List": "Return to Order List",
|
||||
"Timeout time": "Timeout time",
|
||||
"View Order": "View Order"
|
||||
},
|
||||
"organization": {
|
||||
"Account items": "Elementos de la cuenta",
|
||||
"Account items - Tooltip": "Elementos en la página de configuración personal",
|
||||
"Account menu": "Menú de cuenta",
|
||||
"Account menu - Tooltip": "Menú de cuenta - Tooltip",
|
||||
"Admin navbar items": "Elementos de la barra de navegación del administrador",
|
||||
"Admin navbar items - Tooltip": "Elementos de la barra de navegación del administrador - Tooltip",
|
||||
"Balance credit": "Saldo (créditos)",
|
||||
"Balance credit - Tooltip": "Saldo (créditos) - Tooltip",
|
||||
"Balance currency": "Moneda del saldo",
|
||||
"Balance currency - Tooltip": "Moneda del saldo - Tooltip",
|
||||
"Account menu": "Account menu",
|
||||
"Account menu - Tooltip": "Account menu - Tooltip",
|
||||
"Admin navbar items": "Admin navbar items",
|
||||
"Admin navbar items - Tooltip": "Admin navbar items - Tooltip",
|
||||
"Balance credit": "Balance credit",
|
||||
"Balance credit - Tooltip": "Balance credit - Tooltip",
|
||||
"Balance currency": "Balance currency",
|
||||
"Balance currency - Tooltip": "Balance currency - Tooltip",
|
||||
"Edit Organization": "Editar organización",
|
||||
"Follow global theme": "Seguir el tema global",
|
||||
"Has privilege consent": "Tiene consentimiento de privilegio",
|
||||
@@ -773,8 +767,8 @@
|
||||
"Modify rule": "Modificar regla",
|
||||
"New Organization": "Nueva organización",
|
||||
"Optional": "Opcional",
|
||||
"Org balance": "Saldo de la organización",
|
||||
"Org balance - Tooltip": "Saldo de la organización - Tooltip",
|
||||
"Org balance": "Org balance",
|
||||
"Org balance - Tooltip": "Org balance - Tooltip",
|
||||
"Password expire days": "Días de expiración de contraseña",
|
||||
"Password expire days - Tooltip": "Días de expiración de contraseña - Información adicional",
|
||||
"Prompt": "Sugerencia",
|
||||
@@ -784,14 +778,14 @@
|
||||
"Tags": "Etiquetas",
|
||||
"Use Email as username": "Usar correo electrónico como nombre de usuario",
|
||||
"Use Email as username - Tooltip": "Usar correo electrónico como nombre de usuario si el campo de nombre de usuario no es visible al registrarse",
|
||||
"User balance": "Saldo del usuario",
|
||||
"User balance - Tooltip": "Saldo del usuario - Tooltip",
|
||||
"User navbar items": "Elementos de la barra de navegación del usuario",
|
||||
"User navbar items - Tooltip": "Elementos de la barra de navegación del usuario - Tooltip",
|
||||
"User balance": "User balance",
|
||||
"User balance - Tooltip": "User balance - Tooltip",
|
||||
"User navbar items": "User navbar items",
|
||||
"User navbar items - Tooltip": "User navbar items - Tooltip",
|
||||
"User types": "Tipos de usuario",
|
||||
"User types - Tooltip": "Tipos de usuario - Información adicional",
|
||||
"View rule": "Regla de visualización",
|
||||
"Visible": "Visible",
|
||||
"Visible": "Visible - Visible",
|
||||
"Website URL": "URL del sitio web",
|
||||
"Website URL - Tooltip": "La URL de la página de inicio de la organización. Este campo no se usa en Casdoor",
|
||||
"Widget items": "Elementos del widget",
|
||||
@@ -832,15 +826,15 @@
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Por favor, revise detenidamente la información de su factura. Una vez emitida la factura, no se puede retirar ni modificar.",
|
||||
"Please pay the order first!": "Por favor, pague el pedido primero",
|
||||
"Processing...": "Procesando...",
|
||||
"Products - Tooltip": "Productos - Tooltip",
|
||||
"Products - Tooltip": "Products - Tooltip",
|
||||
"Recharged successfully": "Recarga exitosa",
|
||||
"Result": "Resultado",
|
||||
"The payment has been canceled": "El pago ha sido cancelado",
|
||||
"The payment has failed": "El pago ha fallado",
|
||||
"The payment has timed out": "El pago ha expirado",
|
||||
"The payment has timed out": "The payment has timed out",
|
||||
"The payment is still under processing": "El pago aún está en proceso",
|
||||
"View Payment": "Ver pago",
|
||||
"You can view your order details or return to the order list": "Puedes ver los detalles del pedido o volver a la lista de pedidos",
|
||||
"View Payment": "View Payment",
|
||||
"You can view your order details or return to the order list": "You can view your order details or return to the order list",
|
||||
"You have successfully completed the payment": "Has completado el pago exitosamente",
|
||||
"You have successfully recharged": "Has recargado exitosamente",
|
||||
"Your current balance is": "Tu saldo actual es",
|
||||
@@ -873,15 +867,13 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "Editar plan",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "Nuevo plan",
|
||||
"Period": "Período",
|
||||
"Period - Tooltip": "Período",
|
||||
"Plan name": "Nombre del plan",
|
||||
"Plan name": "Plan name",
|
||||
"Price - Tooltip": "Precio",
|
||||
"Related product": "Producto relacionado",
|
||||
"View Plan": "Ver plan",
|
||||
"View Plan": "View Plan",
|
||||
"per month": "por mes",
|
||||
"per year": "por año"
|
||||
},
|
||||
@@ -891,60 +883,55 @@
|
||||
"Free": "Gratis",
|
||||
"Getting started": "Empezar",
|
||||
"New Pricing": "Nuevo precio",
|
||||
"Pricing name": "Nombre de precios",
|
||||
"Pricing name": "Pricing name",
|
||||
"Trial duration": "Duración del período de prueba",
|
||||
"Trial duration - Tooltip": "Duración del período de prueba",
|
||||
"View Pricing": "Ver precios",
|
||||
"View Pricing": "View Pricing",
|
||||
"days trial available!": "días de prueba disponibles",
|
||||
"paid-user do not have active subscription or pending subscription, please select a plan to buy": "El usuario de pago no tiene una suscripción activa o pendiente, por favor selecciona un plan para comprar"
|
||||
},
|
||||
"product": {
|
||||
"Add to cart": "Añadir al carrito",
|
||||
"Add to cart": "Add to cart",
|
||||
"AirWallex": "AirWallex",
|
||||
"Alipay": "Alipay",
|
||||
"Amount": "Importe",
|
||||
"Amount": "Amount",
|
||||
"Buy": "Comprar",
|
||||
"Buy Product": "Comprar producto",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "Importe personalizado disponible",
|
||||
"Custom price should be greater than zero": "El precio personalizado debe ser mayor que cero",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "Detalle del producto",
|
||||
"Disable custom amount": "Desactivar importe personalizado",
|
||||
"Disable custom amount - Tooltip": "Desactivar importe personalizado - Tooltip",
|
||||
"Disable custom amount": "Disable custom amount",
|
||||
"Disable custom amount - Tooltip": "Disable custom amount - Tooltip",
|
||||
"Dummy": "Ficticio",
|
||||
"Edit Product": "Editar Producto",
|
||||
"Enter preset amounts": "Introducir importes predefinidos",
|
||||
"Failed to create order": "Error al crear el pedido",
|
||||
"Enter preset amounts": "Enter preset amounts",
|
||||
"Failed to create order": "Failed to create order",
|
||||
"Image": "Imagen",
|
||||
"Image - Tooltip": "Imagen del producto",
|
||||
"Information": "Información",
|
||||
"Invalid product": "Invalid product",
|
||||
"Information": "Information",
|
||||
"Is recharge": "Es recarga",
|
||||
"Is recharge - Tooltip": "Indica si el producto actual es para recargar saldo",
|
||||
"New Product": "Nuevo producto",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "Pedido creado con éxito",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "PayPal",
|
||||
"Payment cancelled": "Pago cancelado",
|
||||
"Payment failed": "Pago fallido",
|
||||
"Payment providers": "Proveedores de pago",
|
||||
"Payment providers - Tooltip": "Proveedores de servicios de pago",
|
||||
"Placing order...": "Haciendo un pedido...",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Por favor, añade al menos una opción de recarga cuando el importe personalizado esté desactivado",
|
||||
"Please select a currency": "Por favor, selecciona una moneda",
|
||||
"Please select at least one payment provider": "Por favor, selecciona al menos un proveedor de pago",
|
||||
"Processing payment...": "Procesando el pago...",
|
||||
"Product list cannot be empty": "La lista de productos no puede estar vacía",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Please add at least one recharge option when custom amount is disabled",
|
||||
"Please select a currency": "Please select a currency",
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Quantity": "Cantidad",
|
||||
"Quantity - Tooltip": "Cantidad de producto",
|
||||
"Recharge options": "Opciones de recarga",
|
||||
"Recharge options - Tooltip": "Opciones de recarga - Tooltip",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Recharge options - Tooltip",
|
||||
"Return URL": "URL de retorno",
|
||||
"Return URL - Tooltip": "URL para regresar después de una compra exitosa",
|
||||
"SKU": "SKU",
|
||||
"Select amount": "Seleccionar importe",
|
||||
"Select amount": "Select amount",
|
||||
"Sold": "Vendido",
|
||||
"Sold - Tooltip": "Cantidad vendida",
|
||||
"Stripe": "Stripe",
|
||||
@@ -952,12 +939,12 @@
|
||||
"Success URL - Tooltip": "URL para regresar después de la compra",
|
||||
"Tag - Tooltip": "Etiqueta de producto",
|
||||
"Test buy page..": "Página de compra de prueba.",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "La moneda del producto que estás añadiendo es diferente de la moneda de los artículos del carrito",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "The currency of the product you are adding is different from the currency of the items in the cart",
|
||||
"There is no payment channel for this product.": "No hay canal de pago para este producto.",
|
||||
"This product is currently not in sale.": "Este producto actualmente no está a la venta.",
|
||||
"This product is currently not purchasable (No options available)": "Este producto no se puede comprar actualmente (no hay opciones disponibles)",
|
||||
"Total Price": "Precio total",
|
||||
"View Product": "Ver producto",
|
||||
"This product is currently not purchasable (No options available)": "This product is currently not purchasable (No options available)",
|
||||
"Total Price": "Total Price",
|
||||
"View Product": "View Product",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "¿Puedes iniciar sesión?",
|
||||
"Can signup": "Puede registrarse",
|
||||
"Can unlink": "Desvincular",
|
||||
"Category": "Categoría",
|
||||
"Category - Tooltip": "Identificador para categorizar y agrupar elementos o contenido, facilitando el filtrado y la gestión",
|
||||
"Channel No.": "Canal No.",
|
||||
"Channel No. - Tooltip": "Número único que identifica un canal de comunicación o transmisión de datos, utilizado para distinguir diferentes rutas de transmisión",
|
||||
"Chat ID": "ID de chat",
|
||||
@@ -999,8 +988,10 @@
|
||||
"Client secret 2 - Tooltip": "La segunda clave secreta del cliente",
|
||||
"Content": "Contenido",
|
||||
"Content - Tooltip": "Contenido - Información adicional",
|
||||
"DB test": "Prueba de BD",
|
||||
"DB test - Tooltip": "Prueba de BD - Tooltip",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "Desactivar SSL",
|
||||
"Disable SSL - Tooltip": "¿Hay que desactivar el protocolo SSL al comunicarse con el servidor STMP?",
|
||||
"Domain": "Dominio",
|
||||
"Domain - Tooltip": "Dominio personalizado para almacenamiento de objetos",
|
||||
"Edit Provider": "Editar proveedor",
|
||||
@@ -1010,10 +1001,10 @@
|
||||
"Email regex - Tooltip": "Expresión regular de correo electrónico - Información adicional",
|
||||
"Email title": "Título del correo electrónico",
|
||||
"Email title - Tooltip": "Título del correo electrónico",
|
||||
"Enable PKCE": "Habilitar PKCE",
|
||||
"Enable PKCE - Tooltip": "Habilitar PKCE - Tooltip",
|
||||
"Enable proxy": "Habilitar proxy",
|
||||
"Enable proxy - Tooltip": "Habilitar proxy SOCKS5 al enviar correos o SMS",
|
||||
"Enable PKCE": "Enable PKCE",
|
||||
"Enable PKCE - Tooltip": "Enable PKCE - Tooltip",
|
||||
"Enable proxy": "Enable proxy",
|
||||
"Enable proxy - Tooltip": "Enable socks5 Proxy when sending email or sms",
|
||||
"Endpoint": "Punto final",
|
||||
"Endpoint (Intranet)": "Punto final (intranet)",
|
||||
"Endpoint - Tooltip": "Punto de acceso - Información adicional",
|
||||
@@ -1040,13 +1031,13 @@
|
||||
"Key ID - Tooltip": "ID de clave",
|
||||
"Key text": "Texto de clave",
|
||||
"Key text - Tooltip": "Texto de clave",
|
||||
"LDAP port": "Puerto LDAP",
|
||||
"LDAP port": "LDAP port",
|
||||
"Metadata": "Metadatos",
|
||||
"Metadata - Tooltip": "Metadatos SAML",
|
||||
"Metadata url": "URL de metadatos",
|
||||
"Metadata url - Tooltip": "URL de metadatos - Información adicional",
|
||||
"Method - Tooltip": "Método de inicio de sesión, código QR o inicio de sesión silencioso",
|
||||
"Mobile": "Móvil",
|
||||
"Mobile": "Mobile",
|
||||
"New Provider": "Nuevo proveedor",
|
||||
"Parameter": "Parámetro",
|
||||
"Parameter - Tooltip": "Parámetro - Información adicional",
|
||||
@@ -1064,10 +1055,10 @@
|
||||
"Prompted": "Estimulado",
|
||||
"Provider URL": "URL del proveedor",
|
||||
"Provider URL - Tooltip": "Dirección URL para configurar el proveedor de servicios, este campo sólo se utiliza como referencia y no se utiliza en Casdoor",
|
||||
"Provider test successful": "Prueba del proveedor exitosa",
|
||||
"Provider test successful": "Provider test successful",
|
||||
"Public key": "Clave pública",
|
||||
"Public key - Tooltip": "Clave pública - Información adicional",
|
||||
"RADIUS Shared Secret - Tooltip": "Secreto compartido de RADIUS",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret of RADIUS",
|
||||
"Region": "Región",
|
||||
"Region - Tooltip": "Región - Información adicional",
|
||||
"Region ID": "ID de región",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "URL de ACS de SP",
|
||||
"SP ACS URL - Tooltip": "URL del ACS de SP",
|
||||
"SP Entity ID": "ID de entidad SP",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "Escena",
|
||||
"Scene - Tooltip": "Escena",
|
||||
"Scope": "Alcance",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Clave de acceso secreta",
|
||||
"Secret access key - Tooltip": "Clave de acceso secreta",
|
||||
"Secret key": "Clave secreta",
|
||||
@@ -1123,8 +1111,8 @@
|
||||
"Sub type - Tooltip": "Subtipo",
|
||||
"Subject": "Asunto",
|
||||
"Subject - Tooltip": "Asunto del correo electrónico",
|
||||
"Subtype": "Subtipo",
|
||||
"Subtype - Tooltip": "Subtipo - Tooltip",
|
||||
"Subtype": "Subtype",
|
||||
"Subtype - Tooltip": "Subtype - Tooltip",
|
||||
"Syncer test": "Prueba de sincronizador",
|
||||
"Syncer test - Tooltip": "Prueba de sincronizador",
|
||||
"Team ID": "ID del equipo",
|
||||
@@ -1138,8 +1126,8 @@
|
||||
"Test SMTP Connection": "Prueba de conexión SMTP",
|
||||
"Third-party": "Terceros",
|
||||
"This field is required": "Este campo es obligatorio",
|
||||
"To address": "Dirección del destinatario",
|
||||
"To address - Tooltip": "Dirección de correo del campo \"Para\"",
|
||||
"To address": "To address",
|
||||
"To address - Tooltip": "Email address of \"To\"",
|
||||
"Token URL": "URL del token",
|
||||
"Token URL - Tooltip": "URL de token",
|
||||
"Use WeChat Media Platform in PC": "Usar plataforma de medios WeChat en PC",
|
||||
@@ -1172,7 +1160,7 @@
|
||||
"File name": "Nombre del archivo",
|
||||
"File size": "Tamaño de archivo",
|
||||
"Format": "Formato",
|
||||
"Parent": "Padre",
|
||||
"Parent": "Padre o madre (depending on the gender of the parent)",
|
||||
"Upload a file...": "Subir un archivo..."
|
||||
},
|
||||
"role": {
|
||||
@@ -1209,7 +1197,7 @@
|
||||
"Please input your last name!": "¡Por favor ingrese su apellido!",
|
||||
"Please input your phone number!": "¡Por favor, ingrese su número de teléfono!",
|
||||
"Please input your real name!": "¡Por favor, ingresa tu nombre real!",
|
||||
"Please input your {label}!": "¡Por favor, introduce tu {label}!",
|
||||
"Please input your {label}!": "Please input your {label}!",
|
||||
"Please select your country code!": "¡Por favor seleccione su código de país!",
|
||||
"Please select your country/region!": "¡Por favor seleccione su país/región!",
|
||||
"Regex": "Expresión regular",
|
||||
@@ -1222,7 +1210,7 @@
|
||||
"Text 4": "Texto 4",
|
||||
"Text 5": "Texto 5",
|
||||
"The input Email doesn't match the signup item regex!": "¡El correo electrónico ingresado no coincide con la expresión regular del elemento de registro!",
|
||||
"The input doesn't match the signup item regex!": "¡La entrada no coincide con la expresión regular del elemento de registro!",
|
||||
"The input doesn't match the signup item regex!": "The input doesn't match the signup item regex!",
|
||||
"The input is not invoice Tax ID!": "¡La entrada no es el ID fiscal de la factura!",
|
||||
"The input is not invoice title!": "¡El entrada no es el título de la factura!",
|
||||
"The input is not valid Phone!": "¡La entrada no es un número de teléfono válido!",
|
||||
@@ -1242,29 +1230,26 @@
|
||||
"New Subscription": "Nueva suscripción",
|
||||
"Start time": "Hora de inicio",
|
||||
"Start time - Tooltip": "Hora de inicio",
|
||||
"Subscription plan": "Plan de suscripción",
|
||||
"Subscription pricing": "Precios de suscripción",
|
||||
"Subscription plan": "Subscription plan",
|
||||
"Subscription pricing": "Subscription pricing",
|
||||
"Suspended": "Suspendido",
|
||||
"Upcoming": "Próximo",
|
||||
"View Subscription": "Ver suscripción"
|
||||
"View Subscription": "View Subscription"
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "Token API / Contraseña",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "Correo del administrador",
|
||||
"API Token / Password": "API Token / Password",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "Tabla de afiliación",
|
||||
"Affiliation table - Tooltip": "Nombre de la tabla de base de datos de la unidad de trabajo",
|
||||
"Avatar base URL": "URL de la base de Avatar",
|
||||
"Avatar base URL - Tooltip": "Prefijo de URL para las imágenes de avatar",
|
||||
"Bind DN": "DN de enlace",
|
||||
"Bind DN": "Bind DN",
|
||||
"Casdoor column": "Columna de Casdoor",
|
||||
"Column name": "Nombre de la columna",
|
||||
"Column type": "Tipo de columna",
|
||||
"Connect successfully": "Conexión exitosa",
|
||||
"Corp ID": "ID corporativo",
|
||||
"Corp secret": "Secreto corporativo",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp secret": "Corp secret",
|
||||
"Database": "Base de datos",
|
||||
"Database - Tooltip": "El nombre original de la base de datos",
|
||||
"Database type": "Tipo de base de datos",
|
||||
@@ -1278,13 +1263,15 @@
|
||||
"Is read-only": "Es solo lectura",
|
||||
"Is read-only - Tooltip": "Es solo lectura - Información adicional",
|
||||
"New Syncer": "Nuevo Syncer",
|
||||
"Paste your Google Workspace service account JSON key here": "Pega aquí la clave JSON de la cuenta de servicio de Google Workspace",
|
||||
"SCIM Server URL": "URL del servidor SCIM",
|
||||
"Paste your Google Workspace service account JSON key here": "Paste your Google Workspace service account JSON key here",
|
||||
"SCIM Server URL": "SCIM Server URL",
|
||||
"SSH host": "Host SSH",
|
||||
"SSH password": "Contraseña SSH",
|
||||
"SSH port": "Puerto SSH",
|
||||
"SSH user": "Usuario SSH",
|
||||
"Service account key": "Clave de la cuenta de servicio",
|
||||
"SSL mode": "Modo SSL",
|
||||
"SSL mode - Tooltip": "Modo SSL",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "Intervalo de sincronización",
|
||||
"Sync interval - Tooltip": "Unidad en segundos",
|
||||
"Table": "Mesa",
|
||||
@@ -1292,8 +1279,8 @@
|
||||
"Table columns": "Columnas de tabla",
|
||||
"Table columns - Tooltip": "Columnas de la tabla involucradas en la sincronización de datos. Las columnas que no participan en la sincronización no necesitan ser agregadas",
|
||||
"Test Connection": "Probar conexión",
|
||||
"Test DB Connection": "Probar conexión de BD",
|
||||
"Username (optional)": "Nombre de usuario (opcional)"
|
||||
"Test DB Connection": "Test DB Connection",
|
||||
"Username (optional)": "Username (optional)"
|
||||
},
|
||||
"system": {
|
||||
"API Latency": "Retraso API",
|
||||
@@ -1324,16 +1311,16 @@
|
||||
"Theme - Tooltip": "Tema de estilo de la aplicación"
|
||||
},
|
||||
"ticket": {
|
||||
"Closed": "Cerrado",
|
||||
"Edit Ticket": "Editar ticket",
|
||||
"In Progress": "En progreso",
|
||||
"Messages": "Mensajes",
|
||||
"New Ticket": "Nuevo ticket",
|
||||
"Open": "Abierto",
|
||||
"Please enter a message": "Por favor, introduce un mensaje",
|
||||
"Press Ctrl+Enter to send": "Pulsa Ctrl+Enter para enviar",
|
||||
"Resolved": "Resuelto",
|
||||
"Type your message here...": "Escribe tu mensaje aquí..."
|
||||
"Closed": "Closed",
|
||||
"Edit Ticket": "Edit Ticket",
|
||||
"In Progress": "In Progress",
|
||||
"Messages": "Messages",
|
||||
"New Ticket": "New Ticket",
|
||||
"Open": "Open",
|
||||
"Please enter a message": "Please enter a message",
|
||||
"Press Ctrl+Enter to send": "Press Ctrl+Enter to send",
|
||||
"Resolved": "Resolved",
|
||||
"Type your message here...": "Type your message here..."
|
||||
},
|
||||
"token": {
|
||||
"Access token": "Token de acceso",
|
||||
@@ -1355,7 +1342,7 @@
|
||||
"Amount - Tooltip": "Cantidad de productos intercambiados",
|
||||
"Edit Transaction": "Editar transacción",
|
||||
"New Transaction": "Nueva transacción",
|
||||
"Recharge": "Recargar"
|
||||
"Recharge": "Recharge"
|
||||
},
|
||||
"user": {
|
||||
"3rd-party logins": "Inicio de sesión de terceros",
|
||||
@@ -1376,7 +1363,7 @@
|
||||
"Captcha Verify Success": "Verificación de Captcha Exitosa",
|
||||
"City": "Ciudad",
|
||||
"Country code": "Código de país",
|
||||
"Country code - Tooltip": "Código de país - Tooltip",
|
||||
"Country code - Tooltip": "Country code - Tooltip",
|
||||
"Country/Region": "País/Región",
|
||||
"Country/Region - Tooltip": "País o región",
|
||||
"Edit User": "Editar usuario",
|
||||
@@ -1385,7 +1372,7 @@
|
||||
"Email cannot be empty": "El correo electrónico no puede estar vacío",
|
||||
"Email/phone reset successfully": "Restablecimiento de correo electrónico/teléfono exitoso",
|
||||
"Empty input!": "¡Entrada vacía!",
|
||||
"Face IDs": "IDs de Face",
|
||||
"Face IDs": "Face IDs",
|
||||
"Gender": "Género",
|
||||
"Gender - Tooltip": "Género - Información adicional",
|
||||
"Homepage": "Página de inicio del usuario",
|
||||
@@ -1399,10 +1386,10 @@
|
||||
"ID card type": "Tipo de carnet",
|
||||
"ID card type - Tooltip": "Tipo de carnet - Información adicional",
|
||||
"ID card with person": "Carnet con persona",
|
||||
"ID verification": "Verificación de identidad",
|
||||
"ID verification - Tooltip": "Verificación de identidad - Tooltip",
|
||||
"Identity verification successful": "Verificación de identidad exitosa",
|
||||
"Identity verified": "Identidad verificada",
|
||||
"ID verification": "ID verification",
|
||||
"ID verification - Tooltip": "ID verification - Tooltip",
|
||||
"Identity verification successful": "Identity verification successful",
|
||||
"Identity verified": "Identity verified",
|
||||
"Input your email": "Introduce tu correo electrónico",
|
||||
"Input your phone number": "Ingrese su número de teléfono",
|
||||
"Is admin": "Es el administrador",
|
||||
@@ -1412,7 +1399,7 @@
|
||||
"Is forbidden": "está prohibido",
|
||||
"Is forbidden - Tooltip": "Los usuarios bloqueados ya no pueden iniciar sesión",
|
||||
"Is online": "Está en línea",
|
||||
"Is verified": "Está verificado",
|
||||
"Is verified": "Is verified",
|
||||
"Karma": "Karma",
|
||||
"Karma - Tooltip": "Karma - Información adicional",
|
||||
"Keys": "Claves",
|
||||
@@ -1437,16 +1424,16 @@
|
||||
"Other": "Otro",
|
||||
"Password set successfully": "Contraseña establecida exitosamente",
|
||||
"Phone cannot be empty": "El teléfono no puede estar vacío",
|
||||
"Please enter your real name": "Por favor, introduce tu nombre real",
|
||||
"Please fill in ID card information first": "Por favor, completa primero la información del documento de identidad",
|
||||
"Please fill in your real name first": "Por favor, completa primero tu nombre real",
|
||||
"Please enter your real name": "Please enter your real name",
|
||||
"Please fill in ID card information first": "Please fill in ID card information first",
|
||||
"Please fill in your real name first": "Please fill in your real name first",
|
||||
"Please select avatar from resources": "Por favor, selecciona un avatar de los recursos disponibles",
|
||||
"Properties": "Propiedades",
|
||||
"Properties - Tooltip": "Propiedades del usuario",
|
||||
"Ranking": "Clasificación",
|
||||
"Ranking - Tooltip": "Clasificación - Información adicional",
|
||||
"Re-enter New": "Volver a ingresar Nueva",
|
||||
"Real name - Tooltip": "Nombre real - Tooltip",
|
||||
"Real name - Tooltip": "Real name - Tooltip",
|
||||
"Register source": "Fuente de registro",
|
||||
"Register source - Tooltip": "La fuente desde la cual se registró el usuario",
|
||||
"Register type": "Tipo de registro",
|
||||
@@ -1475,8 +1462,8 @@
|
||||
"User Profile": "Perfil de usuario",
|
||||
"Values": "Valores",
|
||||
"Verification code sent": "Código de verificación enviado",
|
||||
"Verified": "Verificado",
|
||||
"Verify Identity": "Verificar identidad",
|
||||
"Verified": "Verified",
|
||||
"Verify Identity": "Verify Identity",
|
||||
"WebAuthn credentials": "Credenciales de WebAuthn",
|
||||
"Work": "Trabajo",
|
||||
"You have changed the username, please save your change first before modifying the password": "Has cambiado el nombre de usuario, por favor guarda el cambio antes de modificar la contraseña",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"Exit impersonation": "Quitter l'usurpation",
|
||||
"Exit impersonation": "Exit impersonation",
|
||||
"Logout": "Déconnexion",
|
||||
"My Account": "Mon Compte",
|
||||
"Sign Up": "S'inscrire"
|
||||
@@ -22,22 +22,22 @@
|
||||
"Add Face ID with Image": "Ajouter Face ID avec image",
|
||||
"Always": "Toujours",
|
||||
"Array": "Array",
|
||||
"Authentication": "Authentification",
|
||||
"Authentication": "Authentication",
|
||||
"Auto signin": "Connexion automatique",
|
||||
"Auto signin - Tooltip": "Lorsqu'une session connectée existe dans Casdoor, elle est automatiquement utilisée pour la connexion côté application",
|
||||
"Background URL": "URL de fond",
|
||||
"Background URL - Tooltip": "L'URL de l'image d'arrière-plan utilisée sur la page de connexion",
|
||||
"Background URL Mobile": "URL de fond mobile",
|
||||
"Background URL Mobile - Tooltip": "URL de l'image d'arrière-plan pour les appareils mobiles",
|
||||
"Basic": "Basique",
|
||||
"Basic": "Basic",
|
||||
"Big icon": "Grande icône",
|
||||
"Binding providers": "Fournisseurs de liaison",
|
||||
"CSS style": "Style CSS",
|
||||
"Center": "Centré",
|
||||
"Code resend timeout": "Délai de renvoi du code",
|
||||
"Code resend timeout - Tooltip": "Période (en secondes) pendant laquelle les utilisateurs doivent attendre avant de demander un autre code de vérification. Définir à 0 pour utiliser la valeur par défaut globale (60 secondes)",
|
||||
"Cookie expire": "Expiration du cookie",
|
||||
"Cookie expire - Tooltip": "Durée de validité du cookie - Info-bulle",
|
||||
"Cookie expire": "Cookie expire",
|
||||
"Cookie expire - Tooltip": "Cookie expire - Tooltip",
|
||||
"Copy SAML metadata URL": "Copiez l'URL de métadonnées SAML",
|
||||
"Copy prompt page URL": "Copier l'URL de la page de l'invite",
|
||||
"Copy signin page URL": "Copier l'URL de la page de connexion",
|
||||
@@ -47,9 +47,9 @@
|
||||
"Custom CSS - Tooltip": "Mise en forme CSS des formulaires d'inscription, de connexion et de récupération de mot de passe (par exemple, en ajoutant des bordures et des ombres)",
|
||||
"Custom CSS Mobile": "CSS personnalisé mobile",
|
||||
"Custom CSS Mobile - Edit": "CSS personnalisé mobile - Modifier",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personnalisé pour les appareils mobiles - Info-bulle",
|
||||
"Disable SAML attributes": "Désactiver les attributs SAML",
|
||||
"Disable SAML attributes - Tooltip": "Désactiver les attributs SAML - Info-bulle",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personnalisé pour les appareils mobiles",
|
||||
"Disable SAML attributes": "Disable SAML attributes",
|
||||
"Disable SAML attributes - Tooltip": "Disable SAML attributes - Tooltip",
|
||||
"Disable signin": "Désactiver la connexion",
|
||||
"Disable signin - Tooltip": "Désactiver la connexion pour les utilisateurs",
|
||||
"Dynamic": "Dynamique",
|
||||
@@ -60,17 +60,16 @@
|
||||
"Enable SAML C14N10 - Tooltip": "Utiliser C14N10 au lieu de C14N11 dans SAML",
|
||||
"Enable SAML POST binding": "Activer la liaison POST SAML",
|
||||
"Enable SAML POST binding - Tooltip": "La liaison POST HTTP utilise des champs de formulaire HTML pour envoyer des messages SAML, activez-la si votre SP l'utilise",
|
||||
"Enable SAML assertion signature": "Activer la signature d'assertion SAML",
|
||||
"Enable SAML assertion signature - Tooltip": "Activer la signature d'assertion SAML - Info-bulle",
|
||||
"Enable SAML assertion signature": "Enable SAML assertion signature",
|
||||
"Enable SAML assertion signature - Tooltip": "Enable SAML assertion signature - Tooltip",
|
||||
"Enable SAML compression": "Activer la compression SAML",
|
||||
"Enable SAML compression - Tooltip": "Compresser ou non les messages de réponse SAML lorsque Casdoor est utilisé en tant que fournisseur d'identité SAML",
|
||||
"Enable exclusive signin": "Activer la connexion exclusive",
|
||||
"Enable exclusive signin - Tooltip": "Lorsque la connexion exclusive est activée, l'utilisateur ne peut pas avoir plusieurs sessions actives",
|
||||
"Enable exclusive signin": "Enable exclusive signin",
|
||||
"Enable exclusive signin - Tooltip": "When exclusive signin enabled, user cannot have multiple active session",
|
||||
"Enable side panel": "Activer le panneau latéral",
|
||||
"Enable signin session - Tooltip": "Conserver une session après la connexion à Casdoor à partir de l'application",
|
||||
"Enable signup": "Activer l'inscription",
|
||||
"Enable signup - Tooltip": "Autoriser la création de nouveaux comptes",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "Temps de blocage après échec de connexion",
|
||||
"Failed signin frozen time - Tooltip": "Durée pendant laquelle le compte est gelé après des tentatives de connexion échouées",
|
||||
"Failed signin limit": "Limite d'échecs de connexion",
|
||||
@@ -102,8 +101,8 @@
|
||||
"Logged out successfully": "Déconnexion réussie",
|
||||
"MFA remember time": "Durée de mémorisation MFA",
|
||||
"MFA remember time - Tooltip": "Configure la durée pendant laquelle un compte est mémorisé comme fiable après une connexion MFA réussie",
|
||||
"Menu mode": "Mode du menu",
|
||||
"Menu mode - Tooltip": "Mode du menu - Info-bulle",
|
||||
"Menu mode": "Menu mode",
|
||||
"Menu mode - Tooltip": "Menu mode - Tooltip",
|
||||
"Multiple Choices": "Choix multiples",
|
||||
"New Application": "Nouvelle application",
|
||||
"No verification": "Aucune vérification",
|
||||
@@ -112,17 +111,17 @@
|
||||
"Order": "Ordre",
|
||||
"Order - Tooltip": "Plus la valeur est petite, plus elle est classée haut dans la page Applications",
|
||||
"Org choice mode": "Mode de choix d'organisation",
|
||||
"Org choice mode - Tooltip": "Méthode utilisée pour sélectionner l'organisation pour se connecter - Info-bulle",
|
||||
"Org choice mode - Tooltip": "Mode de choix d'organisation",
|
||||
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "Veuillez activer \"Session de connexion\" avant d'activer \"Connexion automatique\"",
|
||||
"Please input your application!": "Veuillez saisir votre application !",
|
||||
"Please input your organization!": "Veuillez saisir votre organisation !",
|
||||
"Please select a HTML file": "Veuillez sélectionner un fichier HTML",
|
||||
"Pop up": "Fenêtre contextuelle",
|
||||
"Providers": "Fournisseurs",
|
||||
"Providers": "Providers",
|
||||
"Random": "Aléatoire",
|
||||
"Real name": "Nom réel",
|
||||
"Redirect URL": "URL de redirection",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redirection (Assertion Consumer Service POST Binding URL) - Info-bulle",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redirection (Assertion Consumer Service POST Binding URL)",
|
||||
"Redirect URLs": "URLs de redirection",
|
||||
"Redirect URLs - Tooltip": "Liste des URL de redirection autorisées, les expressions régulières sont supportées ; les URL n'étant pas dans la liste ne seront pas redirigées",
|
||||
"Refresh token expire": "Expiration du jeton de rafraîchissement",
|
||||
@@ -131,30 +130,29 @@
|
||||
"Right": "Droit",
|
||||
"Rule": "Règle",
|
||||
"SAML hash algorithm": "Algorithme de hachage SAML",
|
||||
"SAML hash algorithm - Tooltip": "Algorithme de hachage pour la signature SAML - Info-bulle",
|
||||
"SAML hash algorithm - Tooltip": "Algorithme de hachage pour la signature SAML",
|
||||
"SAML metadata": "Métadonnées SAML",
|
||||
"SAML metadata - Tooltip": "Métadonnées du protocole SAML - Info-bulle",
|
||||
"SAML metadata - Tooltip": "Les métadonnées du protocole SAML",
|
||||
"SAML reply URL": "URL de réponse SAML",
|
||||
"Security": "Sécurité",
|
||||
"Security": "Security",
|
||||
"Select": "Sélectionner",
|
||||
"Side panel HTML": "HTML du panneau latéral",
|
||||
"Side panel HTML - Edit": "HTML du panneau latéral - Modifier",
|
||||
"Side panel HTML - Tooltip": "Personnaliser le code HTML du panneau latéral de la page de connexion - Info-bulle",
|
||||
"Side panel HTML - Tooltip": "Personnalisez le code HTML du panneau latéral de la page de connexion",
|
||||
"Sign Up Error": "Erreur d'inscription",
|
||||
"Signin": "Se connecter",
|
||||
"Signin (Default True)": "Connexion (Vrai par défaut)",
|
||||
"Signin items": "Éléments de connexion",
|
||||
"Signin items - Tooltip": "Éléments de connexion - Info-bulle",
|
||||
"Signin items - Tooltip": "Éléments de connexion",
|
||||
"Signin methods": "Méthodes de connexion",
|
||||
"Signin methods - Tooltip": "Méthodes de connexion - Infobulle",
|
||||
"Signin session": "Session de connexion",
|
||||
"Signup items": "Champs d'inscription",
|
||||
"Signup items - Tooltip": "Éléments à remplir par les utilisateurs lors de la création de nouveaux comptes - Info-bulle",
|
||||
"Signup items - Tooltip": "Champs à remplir lors de l'enregistrement de nouveaux comptes",
|
||||
"Single Choice": "Choix unique",
|
||||
"Small icon": "Petite icône",
|
||||
"Static Value": "Static Value",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "Seuls les utilisateurs avec le tag listé dans les tags de l'application peuvent se connecter - Info-bulle",
|
||||
"Tags - Tooltip": "Seuls les utilisateurs avec le tag listé dans les tags de l'application peuvent se connecter",
|
||||
"The application does not allow to sign up new account": "L'application ne permet pas de créer un nouveau compte",
|
||||
"Token expire": "Expiration du jeton",
|
||||
"Token expire - Tooltip": "Durée avant expiration du jeton d'accès",
|
||||
@@ -164,7 +162,7 @@
|
||||
"Token format - Tooltip": "Le format du jeton d'accès",
|
||||
"Token signing method": "Méthode de signature du jeton",
|
||||
"Token signing method - Tooltip": "Méthode de signature du jeton JWT, doit être le même algorithme que le certificat",
|
||||
"UI Customization": "Personnalisation de l'interface",
|
||||
"UI Customization": "UI Customization",
|
||||
"Use Email as NameID": "Utiliser l'e-mail comme NameID",
|
||||
"Use Email as NameID - Tooltip": "Utiliser l'e-mail comme NameID - Infobulle",
|
||||
"Vertical": "Vertical",
|
||||
@@ -186,7 +184,9 @@
|
||||
"Expire in years - Tooltip": "Période de validité du certificat, en années",
|
||||
"New Cert": "Nouveau Certificat",
|
||||
"Private key": "Clé privée",
|
||||
"Private key - Tooltip": "Clé privée correspondant au certificat de la clé publique"
|
||||
"Private key - Tooltip": "Clé privée correspondant au certificat de la clé publique",
|
||||
"Scope - Tooltip": "Scénarios d'utilisation du certificat",
|
||||
"Type - Tooltip": "Type de certificat"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Le code que vous avez reçu",
|
||||
@@ -250,7 +250,7 @@
|
||||
"Width": "Largeur"
|
||||
},
|
||||
"general": {
|
||||
"A normal user can only modify the permission submitted by itself": "Un utilisateur normal ne peut modifier que l'autorisation qu'il a lui-même soumise",
|
||||
"A normal user can only modify the permission submitted by itself": "A normal user can only modify the permission submitted by itself",
|
||||
"AI Assistant": "Assistant IA",
|
||||
"API key": "Clé API",
|
||||
"API key - Tooltip": "Clé API pour accéder au service",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "Applications qui nécessitent une authentification",
|
||||
"Apps": "Applications",
|
||||
"Authorization": "Autorisation",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "Avatar",
|
||||
"Avatar - Tooltip": "Image d'avatar publique pour le compte",
|
||||
"Back": "Retour",
|
||||
@@ -283,13 +282,11 @@
|
||||
"Business & Payments": "Entreprise et paiements",
|
||||
"Cancel": "Annuler",
|
||||
"Captcha": "Captcha",
|
||||
"Cart": "Panier",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cart": "Cart",
|
||||
"Cert": "Certificat",
|
||||
"Cert - Tooltip": "La clé publique du certificat qui doit être vérifiée par le kit de développement client correspondant à cette application",
|
||||
"Certs": "Certificats",
|
||||
"Clear": "Effacer",
|
||||
"Clear": "Clear",
|
||||
"Click to Upload": "Cliquer pour télécharger",
|
||||
"Client IP": "IP client",
|
||||
"Close": "Fermer",
|
||||
@@ -310,12 +307,12 @@
|
||||
"Delete": "Supprimer",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description détaillée pour référence, Casdoor ne l'utilisera pas en soi",
|
||||
"Detail": "Détail",
|
||||
"Detail": "详情",
|
||||
"Disable": "Désactiver",
|
||||
"Display name": "Nom d'affichage",
|
||||
"Display name - Tooltip": "Un nom convivial et facilement lisible affiché publiquement dans l'interface utilisateur",
|
||||
"Down": "Descendre",
|
||||
"Download template": "Télécharger le modèle",
|
||||
"Download template": "Download template",
|
||||
"Edit": "Modifier",
|
||||
"Email": "E-mail",
|
||||
"Email - Tooltip": "Adresse e-mail valide",
|
||||
@@ -330,21 +327,21 @@
|
||||
"Enabled successfully": "Activé avec succès",
|
||||
"Enforcers": "Agents",
|
||||
"Failed to add": "Échec d'ajout",
|
||||
"Failed to cancel": "Échec de l'annulation",
|
||||
"Failed to cancel": "Failed to cancel",
|
||||
"Failed to connect to server": "Échec de la connexion au serveur",
|
||||
"Failed to copy": "Échec de la copie",
|
||||
"Failed to delete": "Échec de la suppression",
|
||||
"Failed to enable": "Échec de l'activation",
|
||||
"Failed to get": "Échec de la récupération",
|
||||
"Failed to load": "Échec du chargement",
|
||||
"Failed to log out": "Échec de la déconnexion",
|
||||
"Failed to load": "Failed to load",
|
||||
"Failed to log out": "Failed to log out",
|
||||
"Failed to remove": "Échec de la suppression",
|
||||
"Failed to save": "Échec de sauvegarde",
|
||||
"Failed to send": "Échec de l'envoi",
|
||||
"Failed to send": "Failed to send",
|
||||
"Failed to sync": "Échec de la synchronisation",
|
||||
"Failed to unlink": "Échec de la dissociation",
|
||||
"Failed to update": "Échec de la mise à jour",
|
||||
"Failed to upload": "Échec du téléchargement",
|
||||
"Failed to unlink": "Failed to unlink",
|
||||
"Failed to update": "Failed to update",
|
||||
"Failed to upload": "Failed to upload",
|
||||
"Failed to verify": "Échec de la vérification",
|
||||
"False": "Faux",
|
||||
"Favicon": "Favicon",
|
||||
@@ -357,7 +354,7 @@
|
||||
"Forget URL - Tooltip": "URL personnalisée pour la page \"Mot de passe oublié\". Si elle n'est pas définie, la page par défaut \"Mot de passe oublié\" de Casdoor sera utilisée. Lorsqu'elle est définie, le lien \"Mot de passe oublié\" sur la page de connexion sera redirigé vers cette URL",
|
||||
"Forms": "Formulaires",
|
||||
"Found some texts still not translated? Please help us translate at": "Trouvé des textes encore non traduits ? Veuillez nous aider à les traduire sur",
|
||||
"Generate": "Générer",
|
||||
"Generate": "Generate",
|
||||
"Go to enable": "Aller activer",
|
||||
"Go to writable demo site?": "Allez sur le site de démonstration modifiable ?",
|
||||
"Groups": "Groupes",
|
||||
@@ -370,7 +367,7 @@
|
||||
"IP whitelist": "Liste blanche IP",
|
||||
"IP whitelist - Tooltip": "Liste blanche IP",
|
||||
"Identity": "Identité",
|
||||
"Impersonation": "Usurpation",
|
||||
"Impersonation": "Impersonation",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Est activé",
|
||||
"Is enabled - Tooltip": "Définir s'il peut être utilisé",
|
||||
@@ -403,21 +400,21 @@
|
||||
"Name": "Nom",
|
||||
"Name - Tooltip": "Identifiant unique à base de chaîne",
|
||||
"Name format": "Format du nom",
|
||||
"No products available": "Aucun produit disponible",
|
||||
"No sheets found in file": "Aucune feuille trouvée dans le fichier",
|
||||
"No verification method": "Aucune méthode de vérification",
|
||||
"No products available": "No products available",
|
||||
"No sheets found in file": "No sheets found in file",
|
||||
"No verification method": "No verification method",
|
||||
"Non-LDAP": "Non-LDAP",
|
||||
"None": "Aucun",
|
||||
"OAuth providers": "Fournisseurs OAuth",
|
||||
"OFF": "DÉSACTIVÉ",
|
||||
"OK": "OK",
|
||||
"ON": "ACTIVÉ",
|
||||
"Only 1 MFA method can be required": "Une seule méthode MFA peut être requise",
|
||||
"Or": "Ou",
|
||||
"Orders": "Commandes",
|
||||
"Only 1 MFA method can be required": "Only 1 MFA method can be required",
|
||||
"Or": "Or",
|
||||
"Orders": "Orders",
|
||||
"Organization": "Organisation",
|
||||
"Organization - Tooltip": "Similaire à des concepts tels que les locataires (tenants) ou les groupes de compte, chaque compte et application appartient à une organisation",
|
||||
"Organization is null": "L'organisation est nulle",
|
||||
"Organization is null": "Organization is null",
|
||||
"Organizations": "Organisations",
|
||||
"Password": "Mot de passe",
|
||||
"Password - Tooltip": "Assurez-vous que le mot de passe soit correct",
|
||||
@@ -440,21 +437,21 @@
|
||||
"Phone - Tooltip": "Numéro de téléphone",
|
||||
"Phone only": "Téléphone uniquement",
|
||||
"Phone or Email": "Téléphone ou e-mail",
|
||||
"Place Order": "Passer la commande",
|
||||
"Place Order": "Place Order",
|
||||
"Plain": "Simple",
|
||||
"Plan": "Plan",
|
||||
"Plan - Tooltip": "Plan d'abonnement",
|
||||
"Plans": "Offres",
|
||||
"Plans - Tooltip": "Plans - Infobulle",
|
||||
"Please complete the captcha correctly": "Veuillez compléter correctement le captcha",
|
||||
"Please complete the captcha correctly": "Please complete the captcha correctly",
|
||||
"Please input your search": "Veuillez saisir votre recherche",
|
||||
"Please select at least 1 user first": "Veuillez d'abord sélectionner au moins 1 utilisateur",
|
||||
"Please select at least 1 user first": "Please select at least 1 user first",
|
||||
"Preview": "Aperçu",
|
||||
"Preview - Tooltip": "Prévisualisation des effets configurés",
|
||||
"Pricing": "Tarification",
|
||||
"Pricing - Tooltip": "Tarification - Infobulle",
|
||||
"Pricings": "Tarifs",
|
||||
"Product Store": "Boutique de produits",
|
||||
"Product Store": "Product Store",
|
||||
"Products": "Produits",
|
||||
"Provider": "Fournisseur",
|
||||
"Provider - Tooltip": "Les fournisseurs de paiement à configurer, tels que PayPal, Alipay, WeChat Pay, etc.",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "Type d'authentification de connexion SSH",
|
||||
"Save": "Enregistrer",
|
||||
"Save & Exit": "Enregistrer et quitter",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "Rechercher",
|
||||
"Send": "Envoyer",
|
||||
"Session ID": "Identifiant de session",
|
||||
@@ -498,13 +493,13 @@
|
||||
"Sorry, you do not have permission to access this page or logged in status invalid.": "Désolé, vous n'avez pas la permission d'accéder à cette page ou votre statut de connexion est invalide.",
|
||||
"State": "État",
|
||||
"State - Tooltip": "État",
|
||||
"Status": "Statut",
|
||||
"Status": "Status",
|
||||
"Subscriptions": "Abonnements",
|
||||
"Successfully added": "Ajouté avec succès",
|
||||
"Successfully canceled": "Annulé avec succès",
|
||||
"Successfully canceled": "Successfully canceled",
|
||||
"Successfully copied": "Copié avec succès",
|
||||
"Successfully deleted": "Supprimé avec succès",
|
||||
"Successfully executed": "Exécuté avec succès",
|
||||
"Successfully executed": "Successfully executed",
|
||||
"Successfully removed": "Supprimé avec succès",
|
||||
"Successfully saved": "Enregistré avec succès",
|
||||
"Successfully sent": "Envoyé avec succès",
|
||||
@@ -519,9 +514,9 @@
|
||||
"Syncers": "Synchroniseurs",
|
||||
"System Info": "Informations système",
|
||||
"Tab": "Onglet",
|
||||
"The actions cannot be empty": "Les actions ne peuvent pas être vides",
|
||||
"The resources cannot be empty": "Les ressources ne peuvent pas être vides",
|
||||
"The users and roles cannot be empty at the same time": "Les utilisateurs et les rôles ne peuvent pas être vides en même temps",
|
||||
"The actions cannot be empty": "The actions cannot be empty",
|
||||
"The resources cannot be empty": "The resources cannot be empty",
|
||||
"The users and roles cannot be empty at the same time": "The users and roles cannot be empty at the same time",
|
||||
"There was a problem signing you in..": "Un problème est survenu lors de votre connexion..",
|
||||
"This is a read-only demo site!": "Ceci est un site de démonstration en lecture seule !",
|
||||
"Tickets": "Tickets",
|
||||
@@ -535,18 +530,17 @@
|
||||
"Transactions": "Transactions",
|
||||
"True": "Vrai",
|
||||
"Type": "Type",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "Lien de l'URL",
|
||||
"Unknown application name": "Nom d'application inconnu",
|
||||
"Unknown authentication type": "Type d'authentification inconnu",
|
||||
"Unknown application name": "Unknown application name",
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "Monter",
|
||||
"Updated time": "Heure de mise à jour",
|
||||
"Upload (.xlsx)": "Télécharger (.xlsx)",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "Compte",
|
||||
"User - Tooltip": "Assurez-vous que l'identifiant est correct",
|
||||
"User Management": "Gestion des utilisateurs",
|
||||
"User already exists": "L'utilisateur existe déjà",
|
||||
"User already exists": "User already exists",
|
||||
"User containers": "Groupe de comptes",
|
||||
"User type": "Type de compte",
|
||||
"User type - Tooltip": "Étiquettes associées au compte, avec une valeur par défaut \"normal-user\"",
|
||||
@@ -554,10 +548,10 @@
|
||||
"Users - Tooltip": "Utilisateurs - Infobulle",
|
||||
"Users under all organizations": "Comptes sous toutes les organisations",
|
||||
"Verifications": "Vérifications",
|
||||
"View": "Voir",
|
||||
"View": "View",
|
||||
"Webhooks": "Webhooks",
|
||||
"You can only select one physical group": "Vous ne pouvez sélectionner qu'un seul groupe physique",
|
||||
"You must select a picture first": "Vous devez d'abord sélectionner une image",
|
||||
"You must select a picture first": "You must select a picture first",
|
||||
"empty": "vide",
|
||||
"remove": "supprimer",
|
||||
"{total} in total": "{total} au total"
|
||||
@@ -632,7 +626,7 @@
|
||||
"login": {
|
||||
"Auto sign in": "Connexion automatique",
|
||||
"Back button": "Bouton retour",
|
||||
"Click the button below to sign in with Telegram": "Cliquez sur le bouton ci-dessous pour vous connecter avec Telegram",
|
||||
"Click the button below to sign in with Telegram": "Click the button below to sign in with Telegram",
|
||||
"Continue with": "Continuer avec",
|
||||
"Email or phone": "Email ou téléphone",
|
||||
"Face ID": "Face ID",
|
||||
@@ -655,12 +649,12 @@
|
||||
"Please input your Email!": "Veuillez entrer votre e-mail !",
|
||||
"Please input your LDAP username!": "Veuillez entrer votre nom d'utilisateur LDAP !",
|
||||
"Please input your Phone!": "Veuillez entrer votre téléphone !",
|
||||
"Please input your RADIUS password!": "Veuillez saisir votre mot de passe RADIUS !",
|
||||
"Please input your RADIUS username!": "Veuillez saisir votre identifiant RADIUS !",
|
||||
"Please input your RADIUS password!": "Please input your RADIUS password!",
|
||||
"Please input your RADIUS username!": "Please input your RADIUS username!",
|
||||
"Please input your code!": "Veuillez saisir votre code !",
|
||||
"Please input your organization name!": "Veuillez entrer le nom de votre organisation !",
|
||||
"Please input your password!": "Veuillez saisir votre mot de passe !",
|
||||
"Please input your push notification receiver!": "Veuillez saisir le destinataire de la notification push !",
|
||||
"Please input your push notification receiver!": "Please input your push notification receiver!",
|
||||
"Please load the webpage using HTTPS, otherwise the camera cannot be accessed": "Veuillez charger la page web en HTTPS, sinon l'appareil photo ne peut pas être accessible",
|
||||
"Please provide permission to access the camera": "Veuillez autoriser l'accès à l'appareil photo",
|
||||
"Please select an organization": "Veuillez sélectionner une organisation",
|
||||
@@ -671,7 +665,7 @@
|
||||
"Select organization": "Sélectionner l'organisation",
|
||||
"Sign In": "Se connecter",
|
||||
"Sign in with Face ID": "Se connecter avec Face ID",
|
||||
"Sign in with Telegram": "Se connecter avec Telegram",
|
||||
"Sign in with Telegram": "Sign in with Telegram",
|
||||
"Sign in with WebAuthn": "Connectez-vous avec WebAuthn",
|
||||
"Sign in with {type}": "Connectez-vous avec {type}",
|
||||
"Signin button": "Bouton de connexion",
|
||||
@@ -704,7 +698,7 @@
|
||||
"Please confirm the information below": "Veuillez confirmer les informations ci-dessous",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Veuillez enregistrer ce code de récupération. Si votre appareil ne peut pas fournir de code d'authentification, vous pouvez réinitialiser l'authentification MFA avec ce code",
|
||||
"Protect your account with Multi-factor authentication": "Protégez votre compte avec l'authentification multi-facteur",
|
||||
"Push notification receiver": "Destinataire de la notification push",
|
||||
"Push notification receiver": "Push notification receiver",
|
||||
"Recovery code": "Code de récupération",
|
||||
"Remember this account for {hour} hours": "Se souvenir de ce compte pendant {hour} heures",
|
||||
"Scan the QR code with your Authenticator App": "Scannez le code QR avec votre application d'authentification",
|
||||
@@ -714,17 +708,17 @@
|
||||
"To ensure the security of your account, it is required to enable multi-factor authentication": "Pour assurer la sécurité de votre compte, l'authentification multi-facteur est requise",
|
||||
"Use Authenticator App": "Utiliser l'application d'authentification",
|
||||
"Use Email": "Utiliser l'e-mail",
|
||||
"Use Push Notification": "Utiliser la notification push",
|
||||
"Use Radius": "Utiliser RADIUS",
|
||||
"Use Push Notification": "Use Push Notification",
|
||||
"Use Radius": "Use Radius",
|
||||
"Use SMS": "Utiliser SMS",
|
||||
"Use SMS verification code": "Utiliser le code de vérification SMS",
|
||||
"Use a recovery code": "Utiliser un code de récupération",
|
||||
"Verify Code": "Vérifier le code",
|
||||
"Verify Password": "Vérifier le mot de passe",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "Vous avez activé l'authentification multi-facteur, veuillez cliquer sur 'Envoyer le code' pour continuer",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "Vous avez activé l'authentification multi-facteur. Veuillez saisir le mot de passe RADIUS",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "You have enabled Multi-Factor Authentication, please enter the RADIUS password",
|
||||
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "Vous avez activé l'authentification multi-facteur, veuillez entrer le code TOTP",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "Vous avez activé l'authentification multi-facteur. Veuillez saisir le code de vérification de la notification push",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "You have enabled Multi-Factor Authentication, please enter the verification code from push notification",
|
||||
"Your email is": "Votre e-mail est",
|
||||
"Your phone is": "Votre téléphone est",
|
||||
"preferred": "préféré"
|
||||
@@ -738,29 +732,29 @@
|
||||
"New Model": "Nouveau modèle"
|
||||
},
|
||||
"order": {
|
||||
"Cancel time": "Heure d'annulation",
|
||||
"Edit Order": "Modifier la commande",
|
||||
"New Order": "Nouvelle commande",
|
||||
"Order not found": "Commande introuvable",
|
||||
"Pay": "Payer",
|
||||
"Payment failed time": "Heure de l'échec de paiement",
|
||||
"Payment time": "Heure du paiement",
|
||||
"Price": "Prix",
|
||||
"Return to Order List": "Retourner à la liste des commandes",
|
||||
"Timeout time": "Heure d'expiration",
|
||||
"View Order": "Voir la commande"
|
||||
"Cancel time": "Cancel time",
|
||||
"Edit Order": "Edit Order",
|
||||
"New Order": "New Order",
|
||||
"Order not found": "Order not found",
|
||||
"Pay": "Pay",
|
||||
"Payment failed time": "Payment failed time",
|
||||
"Payment time": "Payment time",
|
||||
"Price": "Price",
|
||||
"Return to Order List": "Return to Order List",
|
||||
"Timeout time": "Timeout time",
|
||||
"View Order": "View Order"
|
||||
},
|
||||
"organization": {
|
||||
"Account items": "Champs du compte",
|
||||
"Account items - Tooltip": "Champs de la page des paramètres personnels",
|
||||
"Account menu": "Menu du compte",
|
||||
"Account menu - Tooltip": "Menu du compte - Infobulle",
|
||||
"Admin navbar items": "Éléments de la barre de navigation admin",
|
||||
"Admin navbar items - Tooltip": "Éléments de la barre de navigation admin - Infobulle",
|
||||
"Balance credit": "Solde (crédits)",
|
||||
"Balance credit - Tooltip": "Solde (crédits) - Infobulle",
|
||||
"Balance currency": "Devise du solde",
|
||||
"Balance currency - Tooltip": "Devise du solde - Infobulle",
|
||||
"Account menu": "Account menu",
|
||||
"Account menu - Tooltip": "Account menu - Tooltip",
|
||||
"Admin navbar items": "Admin navbar items",
|
||||
"Admin navbar items - Tooltip": "Admin navbar items - Tooltip",
|
||||
"Balance credit": "Balance credit",
|
||||
"Balance credit - Tooltip": "Balance credit - Tooltip",
|
||||
"Balance currency": "Balance currency",
|
||||
"Balance currency - Tooltip": "Balance currency - Tooltip",
|
||||
"Edit Organization": "Modifier l'organisation",
|
||||
"Follow global theme": "Suivre le thème global",
|
||||
"Has privilege consent": "A le consentement des privilèges",
|
||||
@@ -773,8 +767,8 @@
|
||||
"Modify rule": "Règle de modification",
|
||||
"New Organization": "Nouvelle organisation",
|
||||
"Optional": "Optionnel",
|
||||
"Org balance": "Solde de l'organisation",
|
||||
"Org balance - Tooltip": "Solde de l'organisation - Infobulle",
|
||||
"Org balance": "Org balance",
|
||||
"Org balance - Tooltip": "Org balance - Tooltip",
|
||||
"Password expire days": "Jours d'expiration du mot de passe",
|
||||
"Password expire days - Tooltip": "Jours d'expiration du mot de passe - Infobulle",
|
||||
"Prompt": "Invite",
|
||||
@@ -782,15 +776,15 @@
|
||||
"Soft deletion": "Suppression douce",
|
||||
"Soft deletion - Tooltip": "Lorsque c'est activée, la suppression de compte ne les retirera pas complètement de la base de données. Au lieu de cela, ils seront marqués comme supprimés",
|
||||
"Tags": "Étiquettes",
|
||||
"Use Email as username": "Utiliser l'e-mail comme identifiant",
|
||||
"Use Email as username - Tooltip": "Utiliser l'e-mail comme identifiant si le champ identifiant est masqué lors de l'inscription",
|
||||
"User balance": "Solde de l'utilisateur",
|
||||
"User balance - Tooltip": "Solde de l'utilisateur - Infobulle",
|
||||
"User navbar items": "Éléments de la barre de navigation utilisateur",
|
||||
"User navbar items - Tooltip": "Éléments de la barre de navigation utilisateur - Infobulle",
|
||||
"User types": "Types de compte",
|
||||
"User types - Tooltip": "Types d'utilisateur - Infobulle",
|
||||
"View rule": "Règle d'affichage",
|
||||
"Use Email as username": "Utiliser l'e-mail comme nom d'utilisateur",
|
||||
"Use Email as username - Tooltip": "Utiliser l'e-mail comme nom d'utilisateur si le champ nom d'utilisateur n'est pas visible à l'inscription",
|
||||
"User balance": "User balance",
|
||||
"User balance - Tooltip": "User balance - Tooltip",
|
||||
"User navbar items": "User navbar items",
|
||||
"User navbar items - Tooltip": "User navbar items - Tooltip",
|
||||
"User types": "Types d'utilisateurs",
|
||||
"User types - Tooltip": "Types d'utilisateurs - Infobulle",
|
||||
"View rule": "Règle de visibilité",
|
||||
"Visible": "Visible",
|
||||
"Website URL": "URL du site web",
|
||||
"Website URL - Tooltip": "URL du site web l'organisation. Ce champ n'est pas utilisé dans Casdoor",
|
||||
@@ -832,15 +826,15 @@
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Veuillez vérifier attentivement les informations de votre facture. Une fois émise, la facture ne peut pas être retirée ou modifiée.",
|
||||
"Please pay the order first!": "Veuillez d'abord payer la commande !",
|
||||
"Processing...": "Traitement...",
|
||||
"Products - Tooltip": "Produits - Infobulle",
|
||||
"Products - Tooltip": "Products - Tooltip",
|
||||
"Recharged successfully": "Rechargé avec succès",
|
||||
"Result": "Résultat",
|
||||
"The payment has been canceled": "Le paiement a été annulé",
|
||||
"The payment has failed": "Le paiement a échoué",
|
||||
"The payment has timed out": "Le paiement a expiré",
|
||||
"The payment has timed out": "The payment has timed out",
|
||||
"The payment is still under processing": "Le paiement est encore en cours de traitement",
|
||||
"View Payment": "Voir le paiement",
|
||||
"You can view your order details or return to the order list": "Vous pouvez consulter les détails de la commande ou retourner à la liste des commandes",
|
||||
"View Payment": "View Payment",
|
||||
"You can view your order details or return to the order list": "You can view your order details or return to the order list",
|
||||
"You have successfully completed the payment": "Vous avez effectué le paiement avec succès",
|
||||
"You have successfully recharged": "Vous avez rechargé avec succès",
|
||||
"Your current balance is": "Votre solde actuel est",
|
||||
@@ -849,7 +843,7 @@
|
||||
},
|
||||
"permission": {
|
||||
"Actions": "Actions",
|
||||
"Actions - Tooltip": "Actions autorisées - Infobulle",
|
||||
"Actions - Tooltip": "Actions autorisées",
|
||||
"Allow": "Permettre",
|
||||
"Approve time": "Date d'approbation",
|
||||
"Approve time - Tooltip": "La date d'approbation pour cette permission",
|
||||
@@ -865,7 +859,7 @@
|
||||
"Read": "Lire",
|
||||
"Resource type": "Type de ressource",
|
||||
"Resource type - Tooltip": "Type de ressource",
|
||||
"Resources - Tooltip": "Ressources autorisées - Infobulle",
|
||||
"Resources - Tooltip": "Ressources autorisées",
|
||||
"Submitter": "Soumetteur",
|
||||
"Submitter - Tooltip": "La personne demandant cette permission",
|
||||
"TreeNode": "Nœud d'arbre",
|
||||
@@ -873,15 +867,13 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "Modifier le plan",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "Nouveau plan",
|
||||
"Period": "Période",
|
||||
"Period - Tooltip": "Période",
|
||||
"Plan name": "Nom du plan",
|
||||
"Plan name": "Plan name",
|
||||
"Price - Tooltip": "Prix",
|
||||
"Related product": "Produit associé",
|
||||
"View Plan": "Voir le plan",
|
||||
"View Plan": "View Plan",
|
||||
"per month": "par mois",
|
||||
"per year": "par an"
|
||||
},
|
||||
@@ -891,60 +883,55 @@
|
||||
"Free": "Gratuit",
|
||||
"Getting started": "Commencer",
|
||||
"New Pricing": "Nouvelle tarification",
|
||||
"Pricing name": "Nom de la tarification",
|
||||
"Pricing name": "Pricing name",
|
||||
"Trial duration": "Durée de l'essai",
|
||||
"Trial duration - Tooltip": "Durée de la période d'essai",
|
||||
"View Pricing": "Voir la tarification",
|
||||
"View Pricing": "View Pricing",
|
||||
"days trial available!": "jours d'essai disponibles !",
|
||||
"paid-user do not have active subscription or pending subscription, please select a plan to buy": "L'utilisateur payant n'a pas d'abonnement actif ou en attente, veuillez sélectionner un plan à acheter"
|
||||
},
|
||||
"product": {
|
||||
"Add to cart": "Ajouter au panier",
|
||||
"Add to cart": "Add to cart",
|
||||
"AirWallex": "AirWallex",
|
||||
"Alipay": "Alipay",
|
||||
"Amount": "Montant",
|
||||
"Amount": "Amount",
|
||||
"Buy": "Acheter",
|
||||
"Buy Product": "Acheter un produit",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "Montant personnalisé disponible",
|
||||
"Custom price should be greater than zero": "Le prix personnalisé doit être supérieur à zéro",
|
||||
"Detail - Tooltip": "Détail du produit - Infobulle",
|
||||
"Disable custom amount": "Désactiver le montant personnalisé",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "Détail du produit",
|
||||
"Disable custom amount": "Disable custom amount",
|
||||
"Disable custom amount - Tooltip": "Disable custom amount - Tooltip",
|
||||
"Dummy": "Factice",
|
||||
"Edit Product": "Modifier le produit",
|
||||
"Enter preset amounts": "Saisir des montants prédéfinis",
|
||||
"Failed to create order": "Échec de la création de la commande",
|
||||
"Enter preset amounts": "Enter preset amounts",
|
||||
"Failed to create order": "Failed to create order",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image du produit",
|
||||
"Information": "Informations",
|
||||
"Invalid product": "Invalid product",
|
||||
"Information": "Information",
|
||||
"Is recharge": "Est un rechargement",
|
||||
"Is recharge - Tooltip": "Indique si le produit actuel permet de recharger le solde",
|
||||
"New Product": "Nouveau produit",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "Commande créée avec succès",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "PayPal",
|
||||
"Payment cancelled": "Paiement annulé",
|
||||
"Payment failed": "Paiement échoué",
|
||||
"Payment providers": "Fournisseurs de paiement",
|
||||
"Payment providers - Tooltip": "Fournisseurs de services de paiement",
|
||||
"Placing order...": "Passer une commande...",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Veuillez ajouter au moins une option de recharge lorsque le montant personnalisé est désactivé",
|
||||
"Please select a currency": "Veuillez sélectionner une devise",
|
||||
"Please select at least one payment provider": "Veuillez sélectionner au moins un fournisseur de paiement",
|
||||
"Processing payment...": "Traitement du paiement...",
|
||||
"Product list cannot be empty": "La liste des produits ne peut pas être vide",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Please add at least one recharge option when custom amount is disabled",
|
||||
"Please select a currency": "Please select a currency",
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Quantity": "Quantité",
|
||||
"Quantity - Tooltip": "Quantité du produit",
|
||||
"Recharge options": "Options de recharge",
|
||||
"Quantity - Tooltip": "Quantité de produit",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Recharge options - Tooltip",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Return URL": "URL de retour",
|
||||
"Return URL - Tooltip": "URL de retour après l'achat réussi",
|
||||
"SKU": "SKU",
|
||||
"Select amount": "Sélectionner un montant",
|
||||
"Select amount": "Select amount",
|
||||
"Sold": "Vendu",
|
||||
"Sold - Tooltip": "Quantité vendue",
|
||||
"Stripe": "Stripe",
|
||||
@@ -952,12 +939,12 @@
|
||||
"Success URL - Tooltip": "URL de retour après achat",
|
||||
"Tag - Tooltip": "Étiquette de produit",
|
||||
"Test buy page..": "Page d'achat de test.",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "La devise du produit que vous ajoutez est différente de celle des articles du panier",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "The currency of the product you are adding is different from the currency of the items in the cart",
|
||||
"There is no payment channel for this product.": "Il n'y a aucun canal de paiement pour ce produit.",
|
||||
"This product is currently not in sale.": "Ce produit n'est actuellement pas en vente.",
|
||||
"This product is currently not purchasable (No options available)": "Ce produit n'est actuellement pas achetable (aucune option disponible)",
|
||||
"Total Price": "Prix total",
|
||||
"View Product": "Voir le produit",
|
||||
"This product is currently not purchasable (No options available)": "This product is currently not purchasable (No options available)",
|
||||
"Total Price": "Total Price",
|
||||
"View Product": "View Product",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "Pouvez-vous vous connecter?",
|
||||
"Can signup": "Peut s'inscrire",
|
||||
"Can unlink": "Peut annuler le lien",
|
||||
"Category": "Catégorie",
|
||||
"Category - Tooltip": "Sélectionnez une catégorie",
|
||||
"Channel No.": "chaîne n°",
|
||||
"Channel No. - Tooltip": "Canal N°",
|
||||
"Chat ID": "ID de chat",
|
||||
@@ -999,8 +988,10 @@
|
||||
"Client secret 2 - Tooltip": "La deuxième clé secrète du client",
|
||||
"Content": "Contenu",
|
||||
"Content - Tooltip": "Contenu - Infobulle",
|
||||
"DB test": "Test BD",
|
||||
"DB test - Tooltip": "Test BD - Infobulle",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "Désactiver SSL",
|
||||
"Disable SSL - Tooltip": "Désactiver le protocole SSL lors de la communication avec le serveur STMP",
|
||||
"Domain": "Domaine",
|
||||
"Domain - Tooltip": "Domaine personnalisé pour le stockage d'objets",
|
||||
"Edit Provider": "Modifier le fournisseur",
|
||||
@@ -1010,10 +1001,10 @@
|
||||
"Email regex - Tooltip": "Regex e-mail - Infobulle",
|
||||
"Email title": "Titre de l'email",
|
||||
"Email title - Tooltip": "Titre de l'email",
|
||||
"Enable PKCE": "Activer PKCE",
|
||||
"Enable PKCE": "Enable PKCE",
|
||||
"Enable PKCE - Tooltip": "Enable PKCE - Tooltip",
|
||||
"Enable proxy": "Activer le proxy",
|
||||
"Enable proxy - Tooltip": "Activer un proxy SOCKS5 lors de l'envoi d'e-mails ou de SMS",
|
||||
"Enable proxy": "Enable proxy",
|
||||
"Enable proxy - Tooltip": "Enable socks5 Proxy when sending email or sms",
|
||||
"Endpoint": "Point de terminaison",
|
||||
"Endpoint (Intranet)": "Endpoint (intranet)",
|
||||
"Endpoint - Tooltip": "Point de terminaison - Infobulle",
|
||||
@@ -1040,7 +1031,7 @@
|
||||
"Key ID - Tooltip": "ID de clé",
|
||||
"Key text": "Texte de clé",
|
||||
"Key text - Tooltip": "Texte de clé",
|
||||
"LDAP port": "Port LDAP",
|
||||
"LDAP port": "LDAP port",
|
||||
"Metadata": "Métadonnées",
|
||||
"Metadata - Tooltip": "Métadonnées SAML",
|
||||
"Metadata url": "URL des métadonnées",
|
||||
@@ -1064,10 +1055,10 @@
|
||||
"Prompted": "Incité",
|
||||
"Provider URL": "URL du fournisseur",
|
||||
"Provider URL - Tooltip": "URL pour configurer le fournisseur de services, ce champ est uniquement utilisé à titre de référence et n'est pas utilisé dans Casdoor",
|
||||
"Provider test successful": "Test du fournisseur réussi",
|
||||
"Provider test successful": "Provider test successful",
|
||||
"Public key": "Clé publique",
|
||||
"Public key - Tooltip": "Clé publique - Infobulle",
|
||||
"RADIUS Shared Secret - Tooltip": "Secret partagé RADIUS",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret of RADIUS",
|
||||
"Region": "Région",
|
||||
"Region - Tooltip": "Région - Infobulle",
|
||||
"Region ID": "Identifiant de région",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "URL du SP ACS",
|
||||
"SP ACS URL - Tooltip": "URL de l'ACS du fournisseur de service",
|
||||
"SP Entity ID": "Identifiant d'entité SP",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "Scène",
|
||||
"Scene - Tooltip": "Scène",
|
||||
"Scope": "Portée",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Clé d'accès secrète",
|
||||
"Secret access key - Tooltip": "Clé d'accès secrète",
|
||||
"Secret key": "Clé secrète",
|
||||
@@ -1123,8 +1111,8 @@
|
||||
"Sub type - Tooltip": "Sous-type",
|
||||
"Subject": "Sujet",
|
||||
"Subject - Tooltip": "Sujet de l'e-mail",
|
||||
"Subtype": "Sous-type",
|
||||
"Subtype - Tooltip": "Sous-type - Infobulle",
|
||||
"Subtype": "Subtype",
|
||||
"Subtype - Tooltip": "Subtype - Tooltip",
|
||||
"Syncer test": "Test du synchroniseur",
|
||||
"Syncer test - Tooltip": "Test du synchroniseur",
|
||||
"Team ID": "ID de l'équipe",
|
||||
@@ -1138,8 +1126,8 @@
|
||||
"Test SMTP Connection": "Test de connexion SMTP",
|
||||
"Third-party": "Tierce partie",
|
||||
"This field is required": "Ce champ est requis",
|
||||
"To address": "Adresse du destinataire",
|
||||
"To address - Tooltip": "Adresse e-mail du champ « À »",
|
||||
"To address": "To address",
|
||||
"To address - Tooltip": "Email address of \"To\"",
|
||||
"Token URL": "URL de jeton",
|
||||
"Token URL - Tooltip": "URL de jeton",
|
||||
"Use WeChat Media Platform in PC": "Utiliser la plateforme WeChat Media sur PC",
|
||||
@@ -1209,7 +1197,7 @@
|
||||
"Please input your last name!": "Veuillez saisir votre nom de famille !",
|
||||
"Please input your phone number!": "Veuillez saisir votre numéro de téléphone !",
|
||||
"Please input your real name!": "Veuillez saisir votre nom complet !",
|
||||
"Please input your {label}!": "Veuillez saisir votre {label} !",
|
||||
"Please input your {label}!": "Please input your {label}!",
|
||||
"Please select your country code!": "Sélectionnez votre code de pays, s'il vous plaît !",
|
||||
"Please select your country/region!": "Veuillez sélectionner votre pays/région !",
|
||||
"Regex": "Expression régulière",
|
||||
@@ -1222,7 +1210,7 @@
|
||||
"Text 4": "Texte 4",
|
||||
"Text 5": "Texte 5",
|
||||
"The input Email doesn't match the signup item regex!": "L'e-mail saisi ne correspond pas au regex de l'élément d'inscription !",
|
||||
"The input doesn't match the signup item regex!": "L'entrée ne correspond pas au regex de l'élément d'inscription !",
|
||||
"The input doesn't match the signup item regex!": "The input doesn't match the signup item regex!",
|
||||
"The input is not invoice Tax ID!": "L'entrée n'est pas l'identifiant fiscal de la facture !",
|
||||
"The input is not invoice title!": "L'entrée n'est pas un nom ou une dénomination sociale !",
|
||||
"The input is not valid Phone!": "L'entrée n'est pas un numéro de téléphone valide !",
|
||||
@@ -1242,18 +1230,15 @@
|
||||
"New Subscription": "Nouvel abonnement",
|
||||
"Start time": "Heure de début",
|
||||
"Start time - Tooltip": "Heure de début",
|
||||
"Subscription plan": "Plan d'abonnement",
|
||||
"Subscription pricing": "Tarification d'abonnement",
|
||||
"Subscription plan": "Subscription plan",
|
||||
"Subscription pricing": "Subscription pricing",
|
||||
"Suspended": "Suspendu",
|
||||
"Upcoming": "À venir",
|
||||
"View Subscription": "Voir l'abonnement"
|
||||
"View Subscription": "View Subscription"
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "Jeton API / Mot de passe",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "E-mail admin",
|
||||
"API Token / Password": "API Token / Password",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "Table d'affiliation",
|
||||
"Affiliation table - Tooltip": "Nom de la table de la base de données de l'unité de travail",
|
||||
"Avatar base URL": "URL de base de l'avatar",
|
||||
@@ -1263,8 +1248,8 @@
|
||||
"Column name": "Nom de la colonne",
|
||||
"Column type": "Type de colonne",
|
||||
"Connect successfully": "Connexion réussie",
|
||||
"Corp ID": "ID entreprise",
|
||||
"Corp secret": "Secret entreprise",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp secret": "Corp secret",
|
||||
"Database": "Base de données",
|
||||
"Database - Tooltip": "Le nom original de la base de données",
|
||||
"Database type": "Type de base de données",
|
||||
@@ -1278,13 +1263,15 @@
|
||||
"Is read-only": "Est en lecture seule",
|
||||
"Is read-only - Tooltip": "Est en lecture seule - Infobulle",
|
||||
"New Syncer": "Nouveau synchroniseur",
|
||||
"Paste your Google Workspace service account JSON key here": "Collez ici la clé JSON du compte de service Google Workspace",
|
||||
"SCIM Server URL": "URL du serveur SCIM",
|
||||
"Paste your Google Workspace service account JSON key here": "Paste your Google Workspace service account JSON key here",
|
||||
"SCIM Server URL": "SCIM Server URL",
|
||||
"SSH host": "Hôte SSH",
|
||||
"SSH password": "Mot de passe SSH",
|
||||
"SSH port": "Port SSH",
|
||||
"SSH user": "Utilisateur SSH",
|
||||
"Service account key": "Clé du compte de service",
|
||||
"SSL mode": "Mode SSL",
|
||||
"SSL mode - Tooltip": "Mode SSL",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "Intervalle de synchronisation",
|
||||
"Sync interval - Tooltip": "Unité en secondes",
|
||||
"Table": "Tableau",
|
||||
@@ -1292,8 +1279,8 @@
|
||||
"Table columns": "Colonnes de table",
|
||||
"Table columns - Tooltip": "Colonnes du tableau impliquées dans la synchronisation des données. Les colonnes qui ne participent pas à la synchronisation n'ont pas besoin d'être ajoutées",
|
||||
"Test Connection": "Tester la connexion",
|
||||
"Test DB Connection": "Tester la connexion BD",
|
||||
"Username (optional)": "Nom d'utilisateur (optionnel)"
|
||||
"Test DB Connection": "Test DB Connection",
|
||||
"Username (optional)": "Username (optional)"
|
||||
},
|
||||
"system": {
|
||||
"API Latency": "Retard API",
|
||||
@@ -1324,16 +1311,16 @@
|
||||
"Theme - Tooltip": "Thème de style de l'application"
|
||||
},
|
||||
"ticket": {
|
||||
"Closed": "Fermé",
|
||||
"Edit Ticket": "Modifier le ticket",
|
||||
"In Progress": "En cours",
|
||||
"Closed": "Closed",
|
||||
"Edit Ticket": "Edit Ticket",
|
||||
"In Progress": "In Progress",
|
||||
"Messages": "Messages",
|
||||
"New Ticket": "Nouveau ticket",
|
||||
"Open": "Ouvert",
|
||||
"Please enter a message": "Veuillez saisir un message",
|
||||
"Press Ctrl+Enter to send": "Appuyez sur Ctrl+Entrée pour envoyer",
|
||||
"Resolved": "Résolu",
|
||||
"Type your message here...": "Saisissez votre message ici..."
|
||||
"New Ticket": "New Ticket",
|
||||
"Open": "Open",
|
||||
"Please enter a message": "Please enter a message",
|
||||
"Press Ctrl+Enter to send": "Press Ctrl+Enter to send",
|
||||
"Resolved": "Resolved",
|
||||
"Type your message here...": "Type your message here..."
|
||||
},
|
||||
"token": {
|
||||
"Access token": "Token d'accès",
|
||||
@@ -1355,7 +1342,7 @@
|
||||
"Amount - Tooltip": "Le montant des produits échangés",
|
||||
"Edit Transaction": "Modifier la transaction",
|
||||
"New Transaction": "Nouvelle transaction",
|
||||
"Recharge": "Recharger"
|
||||
"Recharge": "Recharge"
|
||||
},
|
||||
"user": {
|
||||
"3rd-party logins": "Services de connexions tiers",
|
||||
@@ -1385,7 +1372,7 @@
|
||||
"Email cannot be empty": "Le champ e-mail doit être rempli",
|
||||
"Email/phone reset successfully": "E-mail ou téléphone réinitialisé avec succès",
|
||||
"Empty input!": "Champ vide !",
|
||||
"Face IDs": "Identifiants Face",
|
||||
"Face IDs": "Face IDs",
|
||||
"Gender": "Genre",
|
||||
"Gender - Tooltip": "Genre - Infobulle",
|
||||
"Homepage": "Site web",
|
||||
@@ -1399,10 +1386,10 @@
|
||||
"ID card type": "Type de carte d'identité",
|
||||
"ID card type - Tooltip": "Type de carte d'identité - Infobulle",
|
||||
"ID card with person": "Carte d'identité avec la personne",
|
||||
"ID verification": "Vérification d'identité",
|
||||
"ID verification - Tooltip": "Vérification d'identité - Infobulle",
|
||||
"Identity verification successful": "Vérification d'identité réussie",
|
||||
"Identity verified": "Identité vérifiée",
|
||||
"ID verification": "ID verification",
|
||||
"ID verification - Tooltip": "ID verification - Tooltip",
|
||||
"Identity verification successful": "Identity verification successful",
|
||||
"Identity verified": "Identity verified",
|
||||
"Input your email": "Saisissez votre adresse e-mail",
|
||||
"Input your phone number": "Saisissez votre numéro de téléphone",
|
||||
"Is admin": "Est administrateur ou administratrice",
|
||||
@@ -1412,7 +1399,7 @@
|
||||
"Is forbidden": "Est interdit",
|
||||
"Is forbidden - Tooltip": "Les comptes interdits ne peuvent plus se connecter",
|
||||
"Is online": "En ligne",
|
||||
"Is verified": "Est vérifié",
|
||||
"Is verified": "Is verified",
|
||||
"Karma": "Karma",
|
||||
"Karma - Tooltip": "Karma - Infobulle",
|
||||
"Keys": "Clés",
|
||||
@@ -1437,16 +1424,16 @@
|
||||
"Other": "Autre",
|
||||
"Password set successfully": "Mot de passe changé avec succès",
|
||||
"Phone cannot be empty": "Le numéro de téléphone ne peut pas être vide",
|
||||
"Please enter your real name": "Veuillez saisir votre nom complet",
|
||||
"Please fill in ID card information first": "Veuillez d'abord remplir les informations de la pièce d'identité",
|
||||
"Please fill in your real name first": "Veuillez d'abord saisir votre nom complet",
|
||||
"Please enter your real name": "Please enter your real name",
|
||||
"Please fill in ID card information first": "Please fill in ID card information first",
|
||||
"Please fill in your real name first": "Please fill in your real name first",
|
||||
"Please select avatar from resources": "Sélectionner un avatar à partir des ressources",
|
||||
"Properties": "Propriétés",
|
||||
"Properties - Tooltip": "Propriétés du compte",
|
||||
"Ranking": "Classement",
|
||||
"Ranking - Tooltip": "Classement - Infobulle",
|
||||
"Re-enter New": "Confirmer le mot de passe",
|
||||
"Real name - Tooltip": "Nom complet - Infobulle",
|
||||
"Real name - Tooltip": "Real name - Tooltip",
|
||||
"Register source": "Source d'enregistrement",
|
||||
"Register source - Tooltip": "La source à partir de laquelle l'utilisateur a été enregistré",
|
||||
"Register type": "Type d'enregistrement",
|
||||
@@ -1475,8 +1462,8 @@
|
||||
"User Profile": "Profil utilisateur",
|
||||
"Values": "Valeurs",
|
||||
"Verification code sent": "Code de vérification envoyé",
|
||||
"Verified": "Vérifié",
|
||||
"Verify Identity": "Vérifier l'identité",
|
||||
"Verified": "Verified",
|
||||
"Verify Identity": "Verify Identity",
|
||||
"WebAuthn credentials": "Identifiants WebAuthn",
|
||||
"Work": "Travail",
|
||||
"You have changed the username, please save your change first before modifying the password": "Vous avez changé le nom d'utilisateur, veuillez d'abord enregistrer votre modification avant de modifier le mot de passe",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"Exit impersonation": "なりすましを終了",
|
||||
"Exit impersonation": "Exit impersonation",
|
||||
"Logout": "ログアウト",
|
||||
"My Account": "マイアカウント",
|
||||
"Sign Up": "新規登録"
|
||||
@@ -21,23 +21,23 @@
|
||||
"Add Face ID": "顔IDを追加",
|
||||
"Add Face ID with Image": "画像で顔IDを追加",
|
||||
"Always": "常に",
|
||||
"Array": "配列",
|
||||
"Authentication": "認証",
|
||||
"Array": "Array",
|
||||
"Authentication": "Authentication",
|
||||
"Auto signin": "自動サインイン",
|
||||
"Auto signin - Tooltip": "Casdoorにログインセッションが存在する場合、アプリケーション側のログインに自動的に使用されます",
|
||||
"Background URL": "背景URL",
|
||||
"Background URL - Tooltip": "ログインページで使用される背景画像のURL",
|
||||
"Background URL Mobile": "モバイル背景URL",
|
||||
"Background URL Mobile - Tooltip": "モバイルデバイス用の背景画像URL",
|
||||
"Basic": "基本",
|
||||
"Basic": "Basic",
|
||||
"Big icon": "大きいアイコン",
|
||||
"Binding providers": "バインディングプロバイダー",
|
||||
"CSS style": "CSSスタイル",
|
||||
"Center": "センター",
|
||||
"Code resend timeout": "コード再送信タイムアウト",
|
||||
"Code resend timeout - Tooltip": "ユーザーが別の確認コードを要求する前に待機する必要がある期間(秒単位)。グローバルデフォルト(60秒)を使用するには0に設定します",
|
||||
"Cookie expire": "Cookieの有効期限",
|
||||
"Cookie expire - Tooltip": "Cookieの有効期限 - ヒント",
|
||||
"Cookie expire": "Cookie expire",
|
||||
"Cookie expire - Tooltip": "Cookie expire - Tooltip",
|
||||
"Copy SAML metadata URL": "SAMLメタデータのURLをコピーしてください",
|
||||
"Copy prompt page URL": "プロンプトページのURLをコピーしてください",
|
||||
"Copy signin page URL": "サインインページのURLをコピーしてください",
|
||||
@@ -47,9 +47,9 @@
|
||||
"Custom CSS - Tooltip": "サインアップ、サインイン、パスワード忘れのフォームのCSSスタイリング(例:境界線や影の追加)",
|
||||
"Custom CSS Mobile": "カスタムCSS(モバイル)",
|
||||
"Custom CSS Mobile - Edit": "カスタムCSS(モバイル)- 編集",
|
||||
"Custom CSS Mobile - Tooltip": "モバイルデバイス用のカスタムCSS - ヒント",
|
||||
"Disable SAML attributes": "SAML属性を無効にする",
|
||||
"Disable SAML attributes - Tooltip": "SAML属性を無効にする - ヒント",
|
||||
"Custom CSS Mobile - Tooltip": "モバイルデバイス用のカスタムCSS",
|
||||
"Disable SAML attributes": "Disable SAML attributes",
|
||||
"Disable SAML attributes - Tooltip": "Disable SAML attributes - Tooltip",
|
||||
"Disable signin": "サインインを無効化",
|
||||
"Disable signin - Tooltip": "ユーザーのサインインを無効化する",
|
||||
"Dynamic": "動的",
|
||||
@@ -60,17 +60,16 @@
|
||||
"Enable SAML C14N10 - Tooltip": "SAMLでC14N11の代わりにC14N10を使用する",
|
||||
"Enable SAML POST binding": "SAML POSTバインディングを有効にする",
|
||||
"Enable SAML POST binding - Tooltip": "HTTP POSTバインディングはHTMLフォームの入力フィールドを使用してSAMLメッセージを送信します。SPがこれを使用する場合は有効にしてください。",
|
||||
"Enable SAML assertion signature": "SAMLアサーション署名を有効にする",
|
||||
"Enable SAML assertion signature - Tooltip": "SAMLアサーション署名を有効にする - ヒント",
|
||||
"Enable SAML assertion signature": "Enable SAML assertion signature",
|
||||
"Enable SAML assertion signature - Tooltip": "Enable SAML assertion signature - Tooltip",
|
||||
"Enable SAML compression": "SAMLの圧縮を有効にする",
|
||||
"Enable SAML compression - Tooltip": "CasdoorをSAML IdPとして使用する場合、SAMLレスポンスメッセージを圧縮するかどうか。圧縮する: 圧縮するかどうか。圧縮しない: 圧縮しないかどうか",
|
||||
"Enable exclusive signin": "排他的サインインを有効にする",
|
||||
"Enable exclusive signin - Tooltip": "排他的サインインが有効な場合、ユーザーは複数のアクティブセッションを持てません",
|
||||
"Enable exclusive signin": "Enable exclusive signin",
|
||||
"Enable exclusive signin - Tooltip": "When exclusive signin enabled, user cannot have multiple active session",
|
||||
"Enable side panel": "サイドパネルを有効にする",
|
||||
"Enable signin session - Tooltip": "アプリケーションから Casdoor にログイン後、Casdoor がセッションを維持しているかどうか",
|
||||
"Enable signup": "サインアップを有効にする",
|
||||
"Enable signup - Tooltip": "新しいアカウントの登録をユーザーに許可するかどうか",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "サインイン失敗時の凍結時間",
|
||||
"Failed signin frozen time - Tooltip": "サインイン失敗後にアカウントが凍結される時間",
|
||||
"Failed signin limit": "サインイン失敗回数制限",
|
||||
@@ -90,7 +89,7 @@
|
||||
"Header HTML": "ヘッダーHTML",
|
||||
"Header HTML - Edit": "ヘッダーHTML - 編集",
|
||||
"Header HTML - Tooltip": "アプリケーションエントリーページのheadタグをカスタマイズします",
|
||||
"Horizontal": "水平",
|
||||
"Horizontal": "Horizontal",
|
||||
"Incremental": "増分",
|
||||
"Inline": "インライン",
|
||||
"Input": "入力",
|
||||
@@ -102,8 +101,8 @@
|
||||
"Logged out successfully": "正常にログアウトしました",
|
||||
"MFA remember time": "MFA記憶時間",
|
||||
"MFA remember time - Tooltip": "MFAログインに成功した後、アカウントが信頼できるものとして記憶される期間を設定します",
|
||||
"Menu mode": "メニューモード",
|
||||
"Menu mode - Tooltip": "メニューモード - ヒント",
|
||||
"Menu mode": "Menu mode",
|
||||
"Menu mode - Tooltip": "Menu mode - Tooltip",
|
||||
"Multiple Choices": "複数選択",
|
||||
"New Application": "新しいアプリケーション",
|
||||
"No verification": "検証なし",
|
||||
@@ -112,17 +111,17 @@
|
||||
"Order": "順序",
|
||||
"Order - Tooltip": "値が小さいほど、アプリページで上位にランク付けされます",
|
||||
"Org choice mode": "組織選択モード",
|
||||
"Org choice mode - Tooltip": "ログインする組織を選択する方法 - ヒント",
|
||||
"Org choice mode - Tooltip": "組織選択モード",
|
||||
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "\"自動サインイン\"を有効にする前に、まず\"サインインセッション\"を有効にしてください",
|
||||
"Please input your application!": "あなたの申請を入力してください!",
|
||||
"Please input your organization!": "あなたの組織を入力してください!",
|
||||
"Please select a HTML file": "HTMLファイルを選択してください",
|
||||
"Pop up": "ポップアップ",
|
||||
"Providers": "プロバイダー",
|
||||
"Providers": "Providers",
|
||||
"Random": "ランダム",
|
||||
"Real name": "本名",
|
||||
"Redirect URL": "リダイレクトURL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "リダイレクトURL(アサーションコンシューマーサービスPOSTバインディングURL) - ヒント",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "リダイレクトURL(アサーションコンシューマサービスPOSTバインディングURL)",
|
||||
"Redirect URLs": "リダイレクトURL",
|
||||
"Redirect URLs - Tooltip": "許可されたリダイレクトURLリストは、正規表現マッチングをサポートしています。リストに含まれていないURLはリダイレクトできません",
|
||||
"Refresh token expire": "リフレッシュトークンの有効期限が切れました",
|
||||
@@ -133,18 +132,18 @@
|
||||
"SAML hash algorithm": "SAMLハッシュアルゴリズム",
|
||||
"SAML hash algorithm - Tooltip": "SAML署名のハッシュアルゴリズム",
|
||||
"SAML metadata": "SAMLメタデータ",
|
||||
"SAML metadata - Tooltip": "SAMLプロトコルのメタデータ - ヒント",
|
||||
"SAML metadata - Tooltip": "SAMLプロトコルのメタデータ",
|
||||
"SAML reply URL": "SAMLリプライURL",
|
||||
"Security": "セキュリティ",
|
||||
"Security": "Security",
|
||||
"Select": "選択",
|
||||
"Side panel HTML": "サイドパネルのHTML",
|
||||
"Side panel HTML - Edit": "サイドパネルのHTML - 編集",
|
||||
"Side panel HTML - Tooltip": "ログインページのサイドパネルのHTMLコードをカスタマイズします - ヒント",
|
||||
"Side panel HTML - Tooltip": "ログインページのサイドパネルに対するHTMLコードをカスタマイズしてください",
|
||||
"Sign Up Error": "サインアップエラー",
|
||||
"Signin": "サインイン",
|
||||
"Signin (Default True)": "サインイン(デフォルトは有効)",
|
||||
"Signin items": "サインイン項目",
|
||||
"Signin items - Tooltip": "ユーザーがサインインするときに記入するアイテム - ヒント",
|
||||
"Signin items - Tooltip": "サインイン項目",
|
||||
"Signin methods": "サインイン方法",
|
||||
"Signin methods - Tooltip": "サインイン方法 - ツールチップ",
|
||||
"Signin session": "サインインセッション",
|
||||
@@ -152,8 +151,7 @@
|
||||
"Signup items - Tooltip": "新しいアカウントを登録する際にユーザーが入力するアイテム",
|
||||
"Single Choice": "単一選択",
|
||||
"Small icon": "小さいアイコン",
|
||||
"Static Value": "Static Value",
|
||||
"String": "文字列",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "アプリケーションタグに含まれるタグを持つユーザーのみログイン可能です",
|
||||
"The application does not allow to sign up new account": "アプリケーションでは新しいアカウントの登録ができません",
|
||||
"Token expire": "トークンの有効期限が切れました",
|
||||
@@ -164,10 +162,10 @@
|
||||
"Token format - Tooltip": "アクセストークンのフォーマット",
|
||||
"Token signing method": "トークン署名方法",
|
||||
"Token signing method - Tooltip": "JWTトークンの署名方法。証明書と同じアルゴリズムである必要があります。",
|
||||
"UI Customization": "UIのカスタマイズ",
|
||||
"UI Customization": "UI Customization",
|
||||
"Use Email as NameID": "メールアドレスをNameIDとして使用",
|
||||
"Use Email as NameID - Tooltip": "メールアドレスをNameIDとして使用 - ツールチップ",
|
||||
"Vertical": "垂直",
|
||||
"Vertical": "Vertical",
|
||||
"You are unexpected to see this prompt page": "このプロンプトページを見ることは予期せぬことである"
|
||||
},
|
||||
"cert": {
|
||||
@@ -186,7 +184,9 @@
|
||||
"Expire in years - Tooltip": "証明書の有効期間、年数で",
|
||||
"New Cert": "新しい証明書",
|
||||
"Private key": "プライベートキー",
|
||||
"Private key - Tooltip": "公開鍵証明書に対応する秘密鍵"
|
||||
"Private key - Tooltip": "公開鍵証明書に対応する秘密鍵",
|
||||
"Scope - Tooltip": "証明書の使用シナリオ",
|
||||
"Type - Tooltip": "証明書の種類"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "受け取ったコード",
|
||||
@@ -250,7 +250,7 @@
|
||||
"Width": "幅"
|
||||
},
|
||||
"general": {
|
||||
"A normal user can only modify the permission submitted by itself": "通常ユーザーは自分が提出した権限のみ変更できます",
|
||||
"A normal user can only modify the permission submitted by itself": "A normal user can only modify the permission submitted by itself",
|
||||
"AI Assistant": "AIアシスタント",
|
||||
"API key": "APIキー",
|
||||
"API key - Tooltip": "サービスにアクセスするためのAPIキー",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "認証が必要なアプリケーション",
|
||||
"Apps": "アプリ",
|
||||
"Authorization": "認可",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "アバター",
|
||||
"Avatar - Tooltip": "ユーザーのパブリックアバター画像",
|
||||
"Back": "戻る",
|
||||
@@ -283,13 +282,11 @@
|
||||
"Business & Payments": "ビジネスと支払い",
|
||||
"Cancel": "キャンセルします",
|
||||
"Captcha": "キャプチャ",
|
||||
"Cart": "カート",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cart": "Cart",
|
||||
"Cert": "証明書",
|
||||
"Cert - Tooltip": "このアプリケーションに対応するクライアントSDKによって検証する必要がある公開鍵証明書",
|
||||
"Certs": "証明書",
|
||||
"Clear": "クリア",
|
||||
"Clear": "Clear",
|
||||
"Click to Upload": "アップロードするにはクリックしてください",
|
||||
"Client IP": "クライアントIP",
|
||||
"Close": "閉じる",
|
||||
@@ -310,12 +307,12 @@
|
||||
"Delete": "削除",
|
||||
"Description": "説明",
|
||||
"Description - Tooltip": "参照用の詳細な説明情報です。Casdoor自体はそれを使用しません",
|
||||
"Detail": "詳細",
|
||||
"Detail": "详情",
|
||||
"Disable": "無効",
|
||||
"Display name": "表示名",
|
||||
"Display name - Tooltip": "UIで公開されている使いやすく読みやすい名前",
|
||||
"Down": "ダウン",
|
||||
"Download template": "テンプレートをダウンロード",
|
||||
"Download template": "Download template",
|
||||
"Edit": "編集",
|
||||
"Email": "電子メール",
|
||||
"Email - Tooltip": "有効な電子メールアドレス",
|
||||
@@ -330,21 +327,21 @@
|
||||
"Enabled successfully": "正常に有効化されました",
|
||||
"Enforcers": "エンフォーサー",
|
||||
"Failed to add": "追加できませんでした",
|
||||
"Failed to cancel": "キャンセルに失敗しました",
|
||||
"Failed to cancel": "Failed to cancel",
|
||||
"Failed to connect to server": "サーバーに接続できませんでした",
|
||||
"Failed to copy": "コピーに失敗しました",
|
||||
"Failed to delete": "削除に失敗しました",
|
||||
"Failed to enable": "有効化に失敗しました",
|
||||
"Failed to get": "取得に失敗しました",
|
||||
"Failed to load": "読み込みに失敗しました",
|
||||
"Failed to log out": "ログアウトに失敗しました",
|
||||
"Failed to load": "Failed to load",
|
||||
"Failed to log out": "Failed to log out",
|
||||
"Failed to remove": "削除に失敗しました",
|
||||
"Failed to save": "保存に失敗しました",
|
||||
"Failed to send": "送信に失敗しました",
|
||||
"Failed to send": "Failed to send",
|
||||
"Failed to sync": "同期に失敗しました",
|
||||
"Failed to unlink": "リンク解除に失敗しました",
|
||||
"Failed to update": "更新に失敗しました",
|
||||
"Failed to upload": "アップロードに失敗しました",
|
||||
"Failed to unlink": "Failed to unlink",
|
||||
"Failed to update": "Failed to update",
|
||||
"Failed to upload": "Failed to upload",
|
||||
"Failed to verify": "検証に失敗しました",
|
||||
"False": "偽",
|
||||
"Favicon": "ファビコン",
|
||||
@@ -357,7 +354,7 @@
|
||||
"Forget URL - Tooltip": "「パスワードをお忘れの場合」ページのカスタムURL。未設定の場合、デフォルトのCasdoor「パスワードをお忘れの場合」ページが使用されます。設定された場合、ログインページの「パスワードをお忘れの場合」リンクはこのURLにリダイレクトされます",
|
||||
"Forms": "フォーム",
|
||||
"Found some texts still not translated? Please help us translate at": "まだ翻訳されていない文章が見つかりましたか?是非とも翻訳のお手伝いをお願いします",
|
||||
"Generate": "生成",
|
||||
"Generate": "Generate",
|
||||
"Go to enable": "有効にする",
|
||||
"Go to writable demo site?": "書き込み可能なデモサイトに移動しますか?",
|
||||
"Groups": "グループ",
|
||||
@@ -370,7 +367,7 @@
|
||||
"IP whitelist": "IPホワイトリスト",
|
||||
"IP whitelist - Tooltip": "IPホワイトリスト",
|
||||
"Identity": "ID",
|
||||
"Impersonation": "なりすまし",
|
||||
"Impersonation": "Impersonation",
|
||||
"Invitations": "招待",
|
||||
"Is enabled": "可能になっています",
|
||||
"Is enabled - Tooltip": "使用可能かどうかを設定してください",
|
||||
@@ -403,21 +400,21 @@
|
||||
"Name": "名前",
|
||||
"Name - Tooltip": "ユニークで文字列ベースのID",
|
||||
"Name format": "名前フォーマット",
|
||||
"No products available": "利用可能な商品がありません",
|
||||
"No sheets found in file": "ファイル内にシートが見つかりません",
|
||||
"No verification method": "検証方法がありません",
|
||||
"No products available": "No products available",
|
||||
"No sheets found in file": "No sheets found in file",
|
||||
"No verification method": "No verification method",
|
||||
"Non-LDAP": "非LDAP",
|
||||
"None": "なし",
|
||||
"OAuth providers": "OAuthプロバイダー",
|
||||
"OFF": "オフ",
|
||||
"OK": "OK",
|
||||
"ON": "オン",
|
||||
"Only 1 MFA method can be required": "必須にできるMFA方法は1つだけです",
|
||||
"Or": "または",
|
||||
"Orders": "注文",
|
||||
"Only 1 MFA method can be required": "Only 1 MFA method can be required",
|
||||
"Or": "Or",
|
||||
"Orders": "Orders",
|
||||
"Organization": "組織",
|
||||
"Organization - Tooltip": "テナントまたはユーザープールのような概念に似て、各ユーザーとアプリケーションは組織に属しています",
|
||||
"Organization is null": "組織が空です",
|
||||
"Organization is null": "Organization is null",
|
||||
"Organizations": "組織",
|
||||
"Password": "パスワード",
|
||||
"Password - Tooltip": "パスワードが正しいことを確認してください",
|
||||
@@ -440,21 +437,21 @@
|
||||
"Phone - Tooltip": "電話番号",
|
||||
"Phone only": "電話のみ",
|
||||
"Phone or Email": "電話またはメール",
|
||||
"Place Order": "注文する",
|
||||
"Place Order": "Place Order",
|
||||
"Plain": "プレーン",
|
||||
"Plan": "プラン",
|
||||
"Plan - Tooltip": "サブスクリプションプラン",
|
||||
"Plans": "プラン",
|
||||
"Plans - Tooltip": "プラン - ツールチップ",
|
||||
"Please complete the captcha correctly": "キャプチャを正しく入力してください",
|
||||
"Please complete the captcha correctly": "Please complete the captcha correctly",
|
||||
"Please input your search": "検索内容を入力してください",
|
||||
"Please select at least 1 user first": "まず少なくとも1人のユーザーを選択してください",
|
||||
"Please select at least 1 user first": "Please select at least 1 user first",
|
||||
"Preview": "プレビュー",
|
||||
"Preview - Tooltip": "構成されたエフェクトをプレビューする",
|
||||
"Pricing": "価格設定",
|
||||
"Pricing - Tooltip": "価格設定 - ツールチップ",
|
||||
"Pricings": "価格設定",
|
||||
"Product Store": "商品ストア",
|
||||
"Product Store": "Product Store",
|
||||
"Products": "製品",
|
||||
"Provider": "プロバイダー",
|
||||
"Provider - Tooltip": "支払いプロバイダーを設定する必要があります。これには、PayPal、Alipay、WeChat Payなどが含まれます。",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "SSH接続の認証タイプ",
|
||||
"Save": "保存",
|
||||
"Save & Exit": "保存して終了",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "検索",
|
||||
"Send": "送信",
|
||||
"Session ID": "セッションID",
|
||||
@@ -498,13 +493,13 @@
|
||||
"Sorry, you do not have permission to access this page or logged in status invalid.": "申し訳ありませんが、このページにアクセスする権限がありません、またはログイン状態が無効です。",
|
||||
"State": "州",
|
||||
"State - Tooltip": "状態",
|
||||
"Status": "状態",
|
||||
"Status": "Status",
|
||||
"Subscriptions": "サブスクリプション",
|
||||
"Successfully added": "正常に追加されました",
|
||||
"Successfully canceled": "正常にキャンセルされました",
|
||||
"Successfully canceled": "Successfully canceled",
|
||||
"Successfully copied": "正常にコピーされました",
|
||||
"Successfully deleted": "正常に削除されました",
|
||||
"Successfully executed": "正常に実行されました",
|
||||
"Successfully executed": "Successfully executed",
|
||||
"Successfully removed": "正常に削除されました",
|
||||
"Successfully saved": "成功的に保存されました",
|
||||
"Successfully sent": "正常に送信されました",
|
||||
@@ -519,12 +514,12 @@
|
||||
"Syncers": "同期ツール",
|
||||
"System Info": "システム情報",
|
||||
"Tab": "タブ",
|
||||
"The actions cannot be empty": "アクションを空にできません",
|
||||
"The resources cannot be empty": "リソースを空にできません",
|
||||
"The users and roles cannot be empty at the same time": "ユーザーとロールを同時に空にすることはできません",
|
||||
"The actions cannot be empty": "The actions cannot be empty",
|
||||
"The resources cannot be empty": "The resources cannot be empty",
|
||||
"The users and roles cannot be empty at the same time": "The users and roles cannot be empty at the same time",
|
||||
"There was a problem signing you in..": "サインインに問題が発生しました...",
|
||||
"This is a read-only demo site!": "これは読み取り専用のデモサイトです!",
|
||||
"Tickets": "チケット",
|
||||
"Tickets": "Tickets",
|
||||
"Timestamp": "タイムスタンプ",
|
||||
"Title": "タイトル",
|
||||
"Title - Tooltip": "タイトル",
|
||||
@@ -535,18 +530,17 @@
|
||||
"Transactions": "取引",
|
||||
"True": "真",
|
||||
"Type": "タイプ",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "URLリンク",
|
||||
"Unknown application name": "不明なアプリケーション名",
|
||||
"Unknown authentication type": "不明な認証タイプ",
|
||||
"Unknown application name": "Unknown application name",
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "アップ",
|
||||
"Updated time": "更新日時",
|
||||
"Upload (.xlsx)": "アップロード (.xlsx)",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "ユーザー",
|
||||
"User - Tooltip": "ユーザー名が正しいことを確認してください",
|
||||
"User Management": "ユーザー管理",
|
||||
"User already exists": "ユーザーは既に存在します",
|
||||
"User already exists": "User already exists",
|
||||
"User containers": "ユーザープール",
|
||||
"User type": "ユーザータイプ",
|
||||
"User type - Tooltip": "ユーザーが属するタグは、デフォルトでは「通常ユーザー」となります",
|
||||
@@ -554,10 +548,10 @@
|
||||
"Users - Tooltip": "ユーザー - ツールチップ",
|
||||
"Users under all organizations": "すべての組織のユーザー",
|
||||
"Verifications": "検証",
|
||||
"View": "表示",
|
||||
"View": "View",
|
||||
"Webhooks": "Webhook",
|
||||
"You can only select one physical group": "物理グループは1つしか選択できません",
|
||||
"You must select a picture first": "まず画像を選択してください",
|
||||
"You must select a picture first": "You must select a picture first",
|
||||
"empty": "空",
|
||||
"remove": "削除",
|
||||
"{total} in total": "総計{total}"
|
||||
@@ -632,7 +626,7 @@
|
||||
"login": {
|
||||
"Auto sign in": "自動サインイン",
|
||||
"Back button": "戻るボタン",
|
||||
"Click the button below to sign in with Telegram": "下のボタンをクリックしてTelegramでサインインしてください",
|
||||
"Click the button below to sign in with Telegram": "Click the button below to sign in with Telegram",
|
||||
"Continue with": "続ける",
|
||||
"Email or phone": "メールまたは電話",
|
||||
"Face ID": "Face ID",
|
||||
@@ -655,12 +649,12 @@
|
||||
"Please input your Email!": "メールアドレスを入力してください!",
|
||||
"Please input your LDAP username!": "LDAPユーザー名を入力してください!",
|
||||
"Please input your Phone!": "電話番号を入力してください!",
|
||||
"Please input your RADIUS password!": "RADIUSパスワードを入力してください!",
|
||||
"Please input your RADIUS username!": "RADIUSユーザー名を入力してください!",
|
||||
"Please input your RADIUS password!": "Please input your RADIUS password!",
|
||||
"Please input your RADIUS username!": "Please input your RADIUS username!",
|
||||
"Please input your code!": "あなたのコードを入力してください!",
|
||||
"Please input your organization name!": "組織名を入力してください!",
|
||||
"Please input your password!": "パスワードを入力してください!",
|
||||
"Please input your push notification receiver!": "プッシュ通知の受信者を入力してください!",
|
||||
"Please input your push notification receiver!": "Please input your push notification receiver!",
|
||||
"Please load the webpage using HTTPS, otherwise the camera cannot be accessed": "HTTPSでページを読み込んでください。そうでない場合、カメラにアクセスできません",
|
||||
"Please provide permission to access the camera": "カメラへのアクセス許可を与えてください",
|
||||
"Please select an organization": "組織を選択してください",
|
||||
@@ -671,7 +665,7 @@
|
||||
"Select organization": "組織を選択",
|
||||
"Sign In": "サインイン",
|
||||
"Sign in with Face ID": "顔IDでサインイン",
|
||||
"Sign in with Telegram": "Telegramでサインイン",
|
||||
"Sign in with Telegram": "Sign in with Telegram",
|
||||
"Sign in with WebAuthn": "WebAuthnでサインインしてください",
|
||||
"Sign in with {type}": "{type}でサインインしてください",
|
||||
"Signin button": "サインインボタン",
|
||||
@@ -704,7 +698,7 @@
|
||||
"Please confirm the information below": "以下の情報を確認してください",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "このリカバリーコードを保存してください。デバイスが認証コードを提供できない場合、このリカバリーコードでMFA認証をリセットできます",
|
||||
"Protect your account with Multi-factor authentication": "多要素認証でアカウントを保護する",
|
||||
"Push notification receiver": "プッシュ通知受信者",
|
||||
"Push notification receiver": "Push notification receiver",
|
||||
"Recovery code": "リカバリーコード",
|
||||
"Remember this account for {hour} hours": "{hour}時間このアカウントを記憶する",
|
||||
"Scan the QR code with your Authenticator App": "認証アプリでQRコードをスキャンしてください",
|
||||
@@ -714,17 +708,17 @@
|
||||
"To ensure the security of your account, it is required to enable multi-factor authentication": "アカウントのセキュリティを確保するため、多要素認証の有効化が必要です",
|
||||
"Use Authenticator App": "認証アプリを使用",
|
||||
"Use Email": "メールを使用",
|
||||
"Use Push Notification": "プッシュ通知を使用",
|
||||
"Use Radius": "RADIUSを使用",
|
||||
"Use Push Notification": "Use Push Notification",
|
||||
"Use Radius": "Use Radius",
|
||||
"Use SMS": "SMSを使用",
|
||||
"Use SMS verification code": "SMS検証コードを使用",
|
||||
"Use a recovery code": "リカバリーコードを使用",
|
||||
"Verify Code": "検証コード",
|
||||
"Verify Password": "パスワードを検証",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "多要素認証が有効になっています。「コードを送信」をクリックして続行してください",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "多要素認証が有効になっています。RADIUSパスワードを入力してください",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "You have enabled Multi-Factor Authentication, please enter the RADIUS password",
|
||||
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "多要素認証が有効になっています。TOTPコードを入力してください",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "多要素認証が有効になっています。プッシュ通知の確認コードを入力してください",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "You have enabled Multi-Factor Authentication, please enter the verification code from push notification",
|
||||
"Your email is": "あなたのメールは",
|
||||
"Your phone is": "あなたの電話は",
|
||||
"preferred": "優先"
|
||||
@@ -738,29 +732,29 @@
|
||||
"New Model": "新しいモデル"
|
||||
},
|
||||
"order": {
|
||||
"Cancel time": "キャンセル時刻",
|
||||
"Edit Order": "注文を編集",
|
||||
"New Order": "新しい注文",
|
||||
"Order not found": "注文が見つかりません",
|
||||
"Pay": "支払う",
|
||||
"Payment failed time": "支払い失敗時刻",
|
||||
"Payment time": "支払い時刻",
|
||||
"Price": "価格",
|
||||
"Return to Order List": "注文一覧に戻る",
|
||||
"Timeout time": "タイムアウト時刻",
|
||||
"View Order": "注文を表示"
|
||||
"Cancel time": "Cancel time",
|
||||
"Edit Order": "Edit Order",
|
||||
"New Order": "New Order",
|
||||
"Order not found": "Order not found",
|
||||
"Pay": "Pay",
|
||||
"Payment failed time": "Payment failed time",
|
||||
"Payment time": "Payment time",
|
||||
"Price": "Price",
|
||||
"Return to Order List": "Return to Order List",
|
||||
"Timeout time": "Timeout time",
|
||||
"View Order": "View Order"
|
||||
},
|
||||
"organization": {
|
||||
"Account items": "アカウントアイテム",
|
||||
"Account items - Tooltip": "個人設定ページのアイテム",
|
||||
"Account menu": "アカウントメニュー",
|
||||
"Account menu - Tooltip": "アカウントメニュー - ツールチップ",
|
||||
"Admin navbar items": "管理者ナビバー項目",
|
||||
"Admin navbar items - Tooltip": "管理者ナビバー項目 - ツールチップ",
|
||||
"Balance credit": "残高クレジット",
|
||||
"Balance credit - Tooltip": "残高クレジット - ツールチップ",
|
||||
"Balance currency": "残高通貨",
|
||||
"Balance currency - Tooltip": "残高通貨 - ツールチップ",
|
||||
"Account menu": "Account menu",
|
||||
"Account menu - Tooltip": "Account menu - Tooltip",
|
||||
"Admin navbar items": "Admin navbar items",
|
||||
"Admin navbar items - Tooltip": "Admin navbar items - Tooltip",
|
||||
"Balance credit": "Balance credit",
|
||||
"Balance credit - Tooltip": "Balance credit - Tooltip",
|
||||
"Balance currency": "Balance currency",
|
||||
"Balance currency - Tooltip": "Balance currency - Tooltip",
|
||||
"Edit Organization": "組織の編集",
|
||||
"Follow global theme": "グローバルテーマに従ってください",
|
||||
"Has privilege consent": "権限同意あり",
|
||||
@@ -773,8 +767,8 @@
|
||||
"Modify rule": "ルールを変更する",
|
||||
"New Organization": "新しい組織",
|
||||
"Optional": "オプション",
|
||||
"Org balance": "組織残高",
|
||||
"Org balance - Tooltip": "組織残高 - ツールチップ",
|
||||
"Org balance": "Org balance",
|
||||
"Org balance - Tooltip": "Org balance - Tooltip",
|
||||
"Password expire days": "パスワード有効期限(日)",
|
||||
"Password expire days - Tooltip": "パスワード有効期限(日) - ツールチップ",
|
||||
"Prompt": "プロンプト",
|
||||
@@ -784,10 +778,10 @@
|
||||
"Tags": "タグ",
|
||||
"Use Email as username": "メールアドレスをユーザー名として使用",
|
||||
"Use Email as username - Tooltip": "ユーザー名フィールドがサインアップ時に表示されない場合、メールアドレスをユーザー名として使用します",
|
||||
"User balance": "ユーザー残高",
|
||||
"User balance - Tooltip": "ユーザー残高 - ツールチップ",
|
||||
"User navbar items": "ユーザーナビバー項目",
|
||||
"User navbar items - Tooltip": "ユーザーナビバー項目 - ツールチップ",
|
||||
"User balance": "User balance",
|
||||
"User balance - Tooltip": "User balance - Tooltip",
|
||||
"User navbar items": "User navbar items",
|
||||
"User navbar items - Tooltip": "User navbar items - Tooltip",
|
||||
"User types": "ユーザータイプ",
|
||||
"User types - Tooltip": "ユーザータイプ - ツールチップ",
|
||||
"View rule": "ビュールール",
|
||||
@@ -832,15 +826,15 @@
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "請求書情報を慎重に確認してください。一度請求書が発行されると、取り下げまたは変更することはできません。",
|
||||
"Please pay the order first!": "最初に注文をお支払いください!",
|
||||
"Processing...": "処理中... ",
|
||||
"Products - Tooltip": "商品 - ツールチップ",
|
||||
"Products - Tooltip": "Products - Tooltip",
|
||||
"Recharged successfully": "正常にチャージされました",
|
||||
"Result": "結果",
|
||||
"The payment has been canceled": "支払いがキャンセルされました",
|
||||
"The payment has failed": "支払いに失敗しました",
|
||||
"The payment has timed out": "支払いがタイムアウトしました",
|
||||
"The payment has timed out": "The payment has timed out",
|
||||
"The payment is still under processing": "支払いはまだ処理中です",
|
||||
"View Payment": "支払いを表示",
|
||||
"You can view your order details or return to the order list": "注文詳細を表示するか、注文一覧に戻ることができます",
|
||||
"View Payment": "View Payment",
|
||||
"You can view your order details or return to the order list": "You can view your order details or return to the order list",
|
||||
"You have successfully completed the payment": "あなたは支払いを正常に完了しました",
|
||||
"You have successfully recharged": "正常にチャージされました",
|
||||
"Your current balance is": "現在の残高は",
|
||||
@@ -873,15 +867,13 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "プランを編集",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "新しいプラン",
|
||||
"Period": "期間",
|
||||
"Period - Tooltip": "期間",
|
||||
"Plan name": "プラン名",
|
||||
"Plan name": "Plan name",
|
||||
"Price - Tooltip": "価格",
|
||||
"Related product": "関連製品",
|
||||
"View Plan": "プランを表示",
|
||||
"View Plan": "View Plan",
|
||||
"per month": "月毎",
|
||||
"per year": "年あたり"
|
||||
},
|
||||
@@ -891,60 +883,55 @@
|
||||
"Free": "無料",
|
||||
"Getting started": "はじめる",
|
||||
"New Pricing": "新しい価格設定",
|
||||
"Pricing name": "価格設定名",
|
||||
"Pricing name": "Pricing name",
|
||||
"Trial duration": "トライアル期間の長さ",
|
||||
"Trial duration - Tooltip": "トライアル期間の長さ",
|
||||
"View Pricing": "価格設定を表示",
|
||||
"View Pricing": "View Pricing",
|
||||
"days trial available!": "日間のトライアルが利用可能です!",
|
||||
"paid-user do not have active subscription or pending subscription, please select a plan to buy": "有料ユーザーにアクティブまたは保留中のサブスクリプションがありません。購入するプランを選択してください"
|
||||
},
|
||||
"product": {
|
||||
"Add to cart": "カートに追加",
|
||||
"Add to cart": "Add to cart",
|
||||
"AirWallex": "エアウォレックス",
|
||||
"Alipay": "アリペイ",
|
||||
"Amount": "金額",
|
||||
"Amount": "Amount",
|
||||
"Buy": "購入",
|
||||
"Buy Product": "製品を購入する",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "任意金額を利用可能",
|
||||
"Custom price should be greater than zero": "カスタム価格は0より大きくする必要があります",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "製品の詳細",
|
||||
"Disable custom amount": "任意金額を無効にする",
|
||||
"Disable custom amount - Tooltip": "任意金額を無効にする - ツールチップ",
|
||||
"Disable custom amount": "Disable custom amount",
|
||||
"Disable custom amount - Tooltip": "Disable custom amount - Tooltip",
|
||||
"Dummy": "ダミー",
|
||||
"Edit Product": "製品を編集",
|
||||
"Enter preset amounts": "プリセット金額を入力",
|
||||
"Failed to create order": "注文の作成に失敗しました",
|
||||
"Enter preset amounts": "Enter preset amounts",
|
||||
"Failed to create order": "Failed to create order",
|
||||
"Image": "画像",
|
||||
"Image - Tooltip": "製品のイメージ",
|
||||
"Information": "情報",
|
||||
"Invalid product": "Invalid product",
|
||||
"Information": "Information",
|
||||
"Is recharge": "チャージ用か",
|
||||
"Is recharge - Tooltip": "現在の製品が残高をチャージするためかどうか",
|
||||
"New Product": "新製品",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "注文が正常に作成されました",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "ペイパル",
|
||||
"Payment cancelled": "支払いキャンセル",
|
||||
"Payment failed": "支払い失敗",
|
||||
"Payment providers": "支払いプロバイダー",
|
||||
"Payment providers - Tooltip": "支払いサービスの提供者",
|
||||
"Placing order...": "注文をする...",
|
||||
"Please add at least one recharge option when custom amount is disabled": "任意金額が無効な場合は、少なくとも1つのチャージオプションを追加してください",
|
||||
"Please select a currency": "通貨を選択してください",
|
||||
"Please select at least one payment provider": "少なくとも1つの支払いプロバイダーを選択してください",
|
||||
"Processing payment...": "支払い処理中...",
|
||||
"Product list cannot be empty": "商品リストを空にできません",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Please add at least one recharge option when custom amount is disabled",
|
||||
"Please select a currency": "Please select a currency",
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Quantity": "量",
|
||||
"Quantity - Tooltip": "製品の量",
|
||||
"Recharge options": "チャージオプション",
|
||||
"Recharge options - Tooltip": "チャージオプション - ツールチップ",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Recharge options - Tooltip",
|
||||
"Return URL": "戻りURL",
|
||||
"Return URL - Tooltip": "成功した購入後に戻るURL",
|
||||
"SKU": "SKU",
|
||||
"Select amount": "金額を選択",
|
||||
"Select amount": "Select amount",
|
||||
"Sold": "売れました",
|
||||
"Sold - Tooltip": "販売数量",
|
||||
"Stripe": "ストライプ",
|
||||
@@ -952,12 +939,12 @@
|
||||
"Success URL - Tooltip": "購入後に戻るURL",
|
||||
"Tag - Tooltip": "製品のタグ",
|
||||
"Test buy page..": "テスト購入ページ。",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "追加しようとしている商品の通貨がカート内の商品の通貨と異なります",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "The currency of the product you are adding is different from the currency of the items in the cart",
|
||||
"There is no payment channel for this product.": "この製品には支払いチャネルがありません。",
|
||||
"This product is currently not in sale.": "この製品は現在販売されていません。",
|
||||
"This product is currently not purchasable (No options available)": "この商品は現在購入できません(利用可能なオプションがありません)",
|
||||
"Total Price": "合計金額",
|
||||
"View Product": "商品を表示",
|
||||
"This product is currently not purchasable (No options available)": "This product is currently not purchasable (No options available)",
|
||||
"Total Price": "Total Price",
|
||||
"View Product": "View Product",
|
||||
"WeChat Pay": "微信支付"
|
||||
},
|
||||
"provider": {
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "サインインできますか?",
|
||||
"Can signup": "サインアップできますか?",
|
||||
"Can unlink": "アンリンクすることができます",
|
||||
"Category": "カテゴリー",
|
||||
"Category - Tooltip": "カテゴリーを選択してください",
|
||||
"Channel No.": "チャンネル番号",
|
||||
"Channel No. - Tooltip": "チャンネル番号",
|
||||
"Chat ID": "チャットID",
|
||||
@@ -999,8 +988,10 @@
|
||||
"Client secret 2 - Tooltip": "第二クライアント秘密鍵",
|
||||
"Content": "コンテンツ",
|
||||
"Content - Tooltip": "コンテンツ - ツールチップ",
|
||||
"DB test": "DBテスト",
|
||||
"DB test - Tooltip": "DBテスト - ツールチップ",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "SSLを無効にする",
|
||||
"Disable SSL - Tooltip": "SMTPサーバーと通信する場合にSSLプロトコルを無効にするかどうか",
|
||||
"Domain": "ドメイン",
|
||||
"Domain - Tooltip": "オブジェクトストレージのカスタムドメイン",
|
||||
"Edit Provider": "編集プロバイダー",
|
||||
@@ -1010,10 +1001,10 @@
|
||||
"Email regex - Tooltip": "メール正規表現 - ツールチップ",
|
||||
"Email title": "電子メールのタイトル",
|
||||
"Email title - Tooltip": "メールのタイトル",
|
||||
"Enable PKCE": "PKCEを有効にする",
|
||||
"Enable PKCE - Tooltip": "PKCEを有効にする - ツールチップ",
|
||||
"Enable proxy": "プロキシを有効にする",
|
||||
"Enable proxy - Tooltip": "メールまたはSMS送信時にsocks5プロキシを有効にする",
|
||||
"Enable PKCE": "Enable PKCE",
|
||||
"Enable PKCE - Tooltip": "Enable PKCE - Tooltip",
|
||||
"Enable proxy": "Enable proxy",
|
||||
"Enable proxy - Tooltip": "Enable socks5 Proxy when sending email or sms",
|
||||
"Endpoint": "エンドポイント",
|
||||
"Endpoint (Intranet)": "エンドポイント(イントラネット)",
|
||||
"Endpoint - Tooltip": "エンドポイント - ツールチップ",
|
||||
@@ -1040,13 +1031,13 @@
|
||||
"Key ID - Tooltip": "キーID",
|
||||
"Key text": "キーテキスト",
|
||||
"Key text - Tooltip": "キーテキスト",
|
||||
"LDAP port": "LDAPポート",
|
||||
"LDAP port": "LDAP port",
|
||||
"Metadata": "メタデータ",
|
||||
"Metadata - Tooltip": "SAMLのメタデータ",
|
||||
"Metadata url": "メタデータURL",
|
||||
"Metadata url - Tooltip": "メタデータURL - ツールチップ",
|
||||
"Method - Tooltip": "ログイン方法、QRコードまたはサイレントログイン",
|
||||
"Mobile": "モバイル",
|
||||
"Mobile": "Mobile",
|
||||
"New Provider": "新しい提供者",
|
||||
"Parameter": "パラメータ",
|
||||
"Parameter - Tooltip": "パラメータ - ツールチップ",
|
||||
@@ -1064,10 +1055,10 @@
|
||||
"Prompted": "促された",
|
||||
"Provider URL": "プロバイダーURL",
|
||||
"Provider URL - Tooltip": "サービスプロバイダーの設定用URL。このフィールドは参照用にのみ使用され、Casdoorでは使用されません",
|
||||
"Provider test successful": "プロバイダーのテストに成功しました",
|
||||
"Provider test successful": "Provider test successful",
|
||||
"Public key": "公開鍵",
|
||||
"Public key - Tooltip": "公開鍵 - ツールチップ",
|
||||
"RADIUS Shared Secret - Tooltip": "RADIUSの共有シークレット",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret of RADIUS",
|
||||
"Region": "リージョン",
|
||||
"Region - Tooltip": "リージョン - ツールチップ",
|
||||
"Region ID": "地域ID",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "SP ACS URL",
|
||||
"SP ACS URL - Tooltip": "SP ACS URL - ツールチップ",
|
||||
"SP Entity ID": "SPエンティティID",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "シーン",
|
||||
"Scene - Tooltip": "シーン",
|
||||
"Scope": "範囲",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "秘密のアクセスキー",
|
||||
"Secret access key - Tooltip": "秘密のアクセスキー",
|
||||
"Secret key": "秘密鍵",
|
||||
@@ -1123,8 +1111,8 @@
|
||||
"Sub type - Tooltip": "サブタイプ",
|
||||
"Subject": "件名",
|
||||
"Subject - Tooltip": "メールの件名",
|
||||
"Subtype": "サブタイプ",
|
||||
"Subtype - Tooltip": "サブタイプ - ツールチップ",
|
||||
"Subtype": "Subtype",
|
||||
"Subtype - Tooltip": "Subtype - Tooltip",
|
||||
"Syncer test": "同期テスト",
|
||||
"Syncer test - Tooltip": "同期テスト",
|
||||
"Team ID": "チームID",
|
||||
@@ -1138,8 +1126,8 @@
|
||||
"Test SMTP Connection": "SMTP接続をテストする",
|
||||
"Third-party": "サードパーティ",
|
||||
"This field is required": "このフィールドは必須です",
|
||||
"To address": "宛先アドレス",
|
||||
"To address - Tooltip": "「宛先」のメールアドレス",
|
||||
"To address": "To address",
|
||||
"To address - Tooltip": "Email address of \"To\"",
|
||||
"Token URL": "トークンのURL",
|
||||
"Token URL - Tooltip": "トークンURL",
|
||||
"Use WeChat Media Platform in PC": "PCでWeChat Media Platformを使用",
|
||||
@@ -1158,7 +1146,7 @@
|
||||
"UserInfo URL - Tooltip": "ユーザー情報URL",
|
||||
"Wallets": "ウォレット",
|
||||
"Wallets - Tooltip": "ウォレット - ツールチップ",
|
||||
"Web": "ウェブ",
|
||||
"Web": "Web",
|
||||
"admin (Shared)": "管理者(共有)"
|
||||
},
|
||||
"record": {
|
||||
@@ -1209,7 +1197,7 @@
|
||||
"Please input your last name!": "あなたの姓を入力してください!",
|
||||
"Please input your phone number!": "あなたの電話番号を入力してください!",
|
||||
"Please input your real name!": "正しい名前を入力してください!",
|
||||
"Please input your {label}!": "{label}を入力してください!",
|
||||
"Please input your {label}!": "Please input your {label}!",
|
||||
"Please select your country code!": "あなたの国コードを選択してください!",
|
||||
"Please select your country/region!": "あなたの国/地域を選択してください!",
|
||||
"Regex": "正規表現",
|
||||
@@ -1222,7 +1210,7 @@
|
||||
"Text 4": "テキスト4",
|
||||
"Text 5": "テキスト5",
|
||||
"The input Email doesn't match the signup item regex!": "入力されたメールアドレスがサインアップ項目の正規表現と一致しません!",
|
||||
"The input doesn't match the signup item regex!": "入力内容がサインアップ項目の正規表現と一致しません!",
|
||||
"The input doesn't match the signup item regex!": "The input doesn't match the signup item regex!",
|
||||
"The input is not invoice Tax ID!": "入力されたものは請求書の税番号ではありません!",
|
||||
"The input is not invoice title!": "インプットは請求書タイトルではありません!",
|
||||
"The input is not valid Phone!": "この入力は有効な電話番号ではありません!",
|
||||
@@ -1242,29 +1230,26 @@
|
||||
"New Subscription": "新しいサブスクリプション",
|
||||
"Start time": "開始時刻",
|
||||
"Start time - Tooltip": "開始時刻",
|
||||
"Subscription plan": "サブスクリプションプラン",
|
||||
"Subscription pricing": "サブスクリプション価格設定",
|
||||
"Subscription plan": "Subscription plan",
|
||||
"Subscription pricing": "Subscription pricing",
|
||||
"Suspended": "一時停止",
|
||||
"Upcoming": "近日",
|
||||
"View Subscription": "サブスクリプションを表示"
|
||||
"View Subscription": "View Subscription"
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "APIトークン / パスワード",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "管理者メール",
|
||||
"API Token / Password": "API Token / Password",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "所属テーブル",
|
||||
"Affiliation table - Tooltip": "作業単位のデータベーステーブル名",
|
||||
"Avatar base URL": "アバターベースURL",
|
||||
"Avatar base URL - Tooltip": "アバター画像のURLプレフィックス",
|
||||
"Bind DN": "バインドDN",
|
||||
"Bind DN": "Bind DN",
|
||||
"Casdoor column": "カスドアカラム",
|
||||
"Column name": "列名",
|
||||
"Column type": "コラムタイプ",
|
||||
"Connect successfully": "接続成功",
|
||||
"Corp ID": "企業ID",
|
||||
"Corp secret": "企業シークレット",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp secret": "Corp secret",
|
||||
"Database": "データベース",
|
||||
"Database - Tooltip": "元のデータベース名",
|
||||
"Database type": "データベースのタイプ",
|
||||
@@ -1278,13 +1263,15 @@
|
||||
"Is read-only": "読み取り専用か",
|
||||
"Is read-only - Tooltip": "読み取り専用か - ツールチップ",
|
||||
"New Syncer": "新しいシンクロナイザー",
|
||||
"Paste your Google Workspace service account JSON key here": "ここにGoogle WorkspaceサービスアカウントのJSONキーを貼り付けてください",
|
||||
"SCIM Server URL": "SCIMサーバーURL",
|
||||
"Paste your Google Workspace service account JSON key here": "Paste your Google Workspace service account JSON key here",
|
||||
"SCIM Server URL": "SCIM Server URL",
|
||||
"SSH host": "SSHホスト",
|
||||
"SSH password": "SSHパスワード",
|
||||
"SSH port": "SSHポート",
|
||||
"SSH user": "SSHユーザー",
|
||||
"Service account key": "サービスアカウントキー",
|
||||
"SSL mode": "SSLモード",
|
||||
"SSL mode - Tooltip": "SSLモード",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "同期の間隔",
|
||||
"Sync interval - Tooltip": "単位は秒です",
|
||||
"Table": "テーブル",
|
||||
@@ -1292,8 +1279,8 @@
|
||||
"Table columns": "テーブルの列",
|
||||
"Table columns - Tooltip": "データ同期に関与するテーブル列。同期に関与しない列は追加する必要がありません",
|
||||
"Test Connection": "接続をテスト",
|
||||
"Test DB Connection": "DB接続をテスト",
|
||||
"Username (optional)": "ユーザー名(任意)"
|
||||
"Test DB Connection": "Test DB Connection",
|
||||
"Username (optional)": "Username (optional)"
|
||||
},
|
||||
"system": {
|
||||
"API Latency": "API遅延",
|
||||
@@ -1324,16 +1311,16 @@
|
||||
"Theme - Tooltip": "アプリケーションのスタイルテーマ"
|
||||
},
|
||||
"ticket": {
|
||||
"Closed": "クローズ",
|
||||
"Edit Ticket": "チケットを編集",
|
||||
"In Progress": "進行中",
|
||||
"Messages": "メッセージ",
|
||||
"New Ticket": "新しいチケット",
|
||||
"Open": "オープン",
|
||||
"Please enter a message": "メッセージを入力してください",
|
||||
"Press Ctrl+Enter to send": "Ctrl+Enterで送信",
|
||||
"Resolved": "解決済み",
|
||||
"Type your message here...": "ここにメッセージを入力..."
|
||||
"Closed": "Closed",
|
||||
"Edit Ticket": "Edit Ticket",
|
||||
"In Progress": "In Progress",
|
||||
"Messages": "Messages",
|
||||
"New Ticket": "New Ticket",
|
||||
"Open": "Open",
|
||||
"Please enter a message": "Please enter a message",
|
||||
"Press Ctrl+Enter to send": "Press Ctrl+Enter to send",
|
||||
"Resolved": "Resolved",
|
||||
"Type your message here...": "Type your message here..."
|
||||
},
|
||||
"token": {
|
||||
"Access token": "アクセストークン",
|
||||
@@ -1355,7 +1342,7 @@
|
||||
"Amount - Tooltip": "取引製品の金額",
|
||||
"Edit Transaction": "取引を編集",
|
||||
"New Transaction": "新しい取引",
|
||||
"Recharge": "チャージ"
|
||||
"Recharge": "Recharge"
|
||||
},
|
||||
"user": {
|
||||
"3rd-party logins": "サードパーティログイン",
|
||||
@@ -1376,7 +1363,7 @@
|
||||
"Captcha Verify Success": "キャプチャを確認しました。成功しました",
|
||||
"City": "都市",
|
||||
"Country code": "国番号",
|
||||
"Country code - Tooltip": "国コード - ツールチップ",
|
||||
"Country code - Tooltip": "Country code - Tooltip",
|
||||
"Country/Region": "国/地域",
|
||||
"Country/Region - Tooltip": "国または地域",
|
||||
"Edit User": "ユーザーの編集",
|
||||
@@ -1399,10 +1386,10 @@
|
||||
"ID card type": "身分証タイプ",
|
||||
"ID card type - Tooltip": "身分証タイプ - ツールチップ",
|
||||
"ID card with person": "本人と身分証",
|
||||
"ID verification": "本人確認",
|
||||
"ID verification - Tooltip": "本人確認 - ツールチップ",
|
||||
"Identity verification successful": "本人確認に成功しました",
|
||||
"Identity verified": "本人確認済み",
|
||||
"ID verification": "ID verification",
|
||||
"ID verification - Tooltip": "ID verification - Tooltip",
|
||||
"Identity verification successful": "Identity verification successful",
|
||||
"Identity verified": "Identity verified",
|
||||
"Input your email": "あなたのメールアドレスを入力してください",
|
||||
"Input your phone number": "電話番号を入力してください",
|
||||
"Is admin": "管理者ですか?",
|
||||
@@ -1412,7 +1399,7 @@
|
||||
"Is forbidden": "禁止されています",
|
||||
"Is forbidden - Tooltip": "禁止されたユーザーはこれ以上ログインできません",
|
||||
"Is online": "オンラインか",
|
||||
"Is verified": "確認済み",
|
||||
"Is verified": "Is verified",
|
||||
"Karma": "カルマ",
|
||||
"Karma - Tooltip": "カルマ - ツールチップ",
|
||||
"Keys": "鍵",
|
||||
@@ -1437,19 +1424,19 @@
|
||||
"Other": "その他",
|
||||
"Password set successfully": "パスワードの設定に成功しました",
|
||||
"Phone cannot be empty": "電話は空白にできません",
|
||||
"Please enter your real name": "実名を入力してください",
|
||||
"Please fill in ID card information first": "先に身分証情報を入力してください",
|
||||
"Please fill in your real name first": "先に実名を入力してください",
|
||||
"Please enter your real name": "Please enter your real name",
|
||||
"Please fill in ID card information first": "Please fill in ID card information first",
|
||||
"Please fill in your real name first": "Please fill in your real name first",
|
||||
"Please select avatar from resources": "リソースからアバターを選択してください",
|
||||
"Properties": "特性",
|
||||
"Properties - Tooltip": "ユーザーのプロパティー",
|
||||
"Ranking": "ランキング",
|
||||
"Ranking - Tooltip": "ランキング - ツールチップ",
|
||||
"Re-enter New": "新しく入り直す",
|
||||
"Real name - Tooltip": "実名 - ツールチップ",
|
||||
"Real name - Tooltip": "Real name - Tooltip",
|
||||
"Register source": "登録元",
|
||||
"Register source - Tooltip": "ユーザーが登録されたソース",
|
||||
"Register type": "登録方法",
|
||||
"Register type": "登録タイプ",
|
||||
"Register type - Tooltip": "ユーザーを登録するために使用される方法",
|
||||
"Reset Email...": "リセットメール...",
|
||||
"Reset Phone...": "リセットします...",
|
||||
@@ -1475,8 +1462,8 @@
|
||||
"User Profile": "ユーザープロフィール",
|
||||
"Values": "価値観",
|
||||
"Verification code sent": "確認コードを送信しました",
|
||||
"Verified": "確認済み",
|
||||
"Verify Identity": "本人確認",
|
||||
"Verified": "Verified",
|
||||
"Verify Identity": "Verify Identity",
|
||||
"WebAuthn credentials": "WebAuthnの資格情報",
|
||||
"Work": "職場",
|
||||
"You have changed the username, please save your change first before modifying the password": "ユーザー名を変更しました。パスワードを変更する前に、変更を保存してください",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"account": {
|
||||
"Exit impersonation": "Sair da impersonação",
|
||||
"Exit impersonation": "Exit impersonation",
|
||||
"Logout": "Sair",
|
||||
"My Account": "Minha Conta",
|
||||
"Sign Up": "Cadastrar-se"
|
||||
@@ -22,22 +22,22 @@
|
||||
"Add Face ID with Image": "Adicionar Face ID com imagem",
|
||||
"Always": "Sempre",
|
||||
"Array": "Array",
|
||||
"Authentication": "Autenticação",
|
||||
"Authentication": "Authentication",
|
||||
"Auto signin": "Login automático",
|
||||
"Auto signin - Tooltip": "Quando uma sessão logada existe no Casdoor, ela é automaticamente usada para o login no lado da aplicação",
|
||||
"Background URL": "URL de Fundo",
|
||||
"Background URL - Tooltip": "URL da imagem de fundo usada na página de login",
|
||||
"Background URL Mobile": "URL de fundo para dispositivos móveis",
|
||||
"Background URL Mobile - Tooltip": "URL da imagem de fundo para dispositivos móveis",
|
||||
"Basic": "Básico",
|
||||
"Basic": "Basic",
|
||||
"Big icon": "Ícone grande",
|
||||
"Binding providers": "Provedores de vinculação",
|
||||
"CSS style": "Estilo CSS",
|
||||
"Center": "Centro",
|
||||
"Code resend timeout": "Tempo limite de reenvio de código",
|
||||
"Code resend timeout - Tooltip": "Período de tempo (em segundos) que os usuários devem esperar antes de solicitar outro código de verificação. Defina como 0 para usar o padrão global (60 segundos)",
|
||||
"Cookie expire": "Expiração do cookie",
|
||||
"Cookie expire - Tooltip": "Tempo de expiração do cookie - Dica",
|
||||
"Cookie expire": "Cookie expire",
|
||||
"Cookie expire - Tooltip": "Cookie expire - Tooltip",
|
||||
"Copy SAML metadata URL": "Copiar URL de metadados SAML",
|
||||
"Copy prompt page URL": "Copiar URL da página de prompt",
|
||||
"Copy signin page URL": "Copiar URL da página de login",
|
||||
@@ -47,9 +47,9 @@
|
||||
"Custom CSS - Tooltip": "Estilização CSS dos formulários de registro, login e recuperação de senha (por exemplo, adicionando bordas e sombras)",
|
||||
"Custom CSS Mobile": "CSS personalizado para dispositivos móveis",
|
||||
"Custom CSS Mobile - Edit": "Editar CSS personalizado para dispositivos móveis",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personalizado para dispositivos móveis - Dica",
|
||||
"Disable SAML attributes": "Desativar atributos SAML",
|
||||
"Disable SAML attributes - Tooltip": "Se deve desativar atributos SAML - Dica",
|
||||
"Custom CSS Mobile - Tooltip": "CSS personalizado para dispositivos móveis",
|
||||
"Disable SAML attributes": "Disable SAML attributes",
|
||||
"Disable SAML attributes - Tooltip": "Disable SAML attributes - Tooltip",
|
||||
"Disable signin": "Desativar login",
|
||||
"Disable signin - Tooltip": "Desativar login para usuários",
|
||||
"Dynamic": "Dinâmico",
|
||||
@@ -60,17 +60,16 @@
|
||||
"Enable SAML C14N10 - Tooltip": "Use C14N10 em vez de C14N11 no SAML",
|
||||
"Enable SAML POST binding": "Ativar vinculação SAML POST",
|
||||
"Enable SAML POST binding - Tooltip": "A vinculação por POST HTTP usa campos de formulário HTML para enviar mensagens SAML. Ative quando seu SP a utilizar.",
|
||||
"Enable SAML assertion signature": "Ativar assinatura da asserção SAML",
|
||||
"Enable SAML assertion signature - Tooltip": "Se deve assinar a asserção SAML - Dica",
|
||||
"Enable SAML assertion signature": "Enable SAML assertion signature",
|
||||
"Enable SAML assertion signature - Tooltip": "Enable SAML assertion signature - Tooltip",
|
||||
"Enable SAML compression": "Ativar compressão SAML",
|
||||
"Enable SAML compression - Tooltip": "Se deve comprimir as mensagens de resposta SAML quando o Casdoor é usado como provedor de identidade SAML",
|
||||
"Enable exclusive signin": "Ativar login exclusivo",
|
||||
"Enable exclusive signin - Tooltip": "Quando o login exclusivo está ativado, o usuário não pode ter várias sessões ativas",
|
||||
"Enable exclusive signin": "Enable exclusive signin",
|
||||
"Enable exclusive signin - Tooltip": "When exclusive signin enabled, user cannot have multiple active session",
|
||||
"Enable side panel": "Ativar painel lateral",
|
||||
"Enable signin session - Tooltip": "Se o Casdoor mantém uma sessão depois de fazer login no Casdoor a partir da aplicação",
|
||||
"Enable signup": "Ativar registro",
|
||||
"Enable signup - Tooltip": "Se permite que os usuários registrem uma nova conta",
|
||||
"Existing Field": "Existing Field",
|
||||
"Failed signin frozen time": "Tempo de bloqueio após falha de login",
|
||||
"Failed signin frozen time - Tooltip": "Tempo em que a conta fica congelada após tentativas de login falhadas",
|
||||
"Failed signin limit": "Limite de tentativas de login falhadas",
|
||||
@@ -102,8 +101,8 @@
|
||||
"Logged out successfully": "Logout realizado com sucesso",
|
||||
"MFA remember time": "Tempo de lembrar MFA",
|
||||
"MFA remember time - Tooltip": "Configura a duração pela qual uma conta é lembrada como confiável após um login MFA bem-sucedido",
|
||||
"Menu mode": "Modo do menu",
|
||||
"Menu mode - Tooltip": "Modo do menu - Dica",
|
||||
"Menu mode": "Menu mode",
|
||||
"Menu mode - Tooltip": "Menu mode - Tooltip",
|
||||
"Multiple Choices": "Múltipla escolha",
|
||||
"New Application": "Nova Aplicação",
|
||||
"No verification": "Sem verificação",
|
||||
@@ -112,17 +111,17 @@
|
||||
"Order": "Ordem",
|
||||
"Order - Tooltip": "Quanto menor o valor, maior a classificação na página de Aplicativos",
|
||||
"Org choice mode": "Modo de escolha da organização",
|
||||
"Org choice mode - Tooltip": "Método usado para selecionar a organização para fazer login - Dica",
|
||||
"Org choice mode - Tooltip": "Modo de escolha de organização",
|
||||
"Please enable \"Signin session\" first before enabling \"Auto signin\"": "Por favor, habilite a \"Sessão de login\" primeiro antes de habilitar o \"Login automático\"",
|
||||
"Please input your application!": "Por favor, insira o nome da sua aplicação!",
|
||||
"Please input your organization!": "Por favor, insira o nome da sua organização!",
|
||||
"Please select a HTML file": "Por favor, selecione um arquivo HTML",
|
||||
"Pop up": "Abrir em pop-up",
|
||||
"Providers": "Provedores",
|
||||
"Providers": "Providers",
|
||||
"Random": "Aleatório",
|
||||
"Real name": "Nome real",
|
||||
"Redirect URL": "URL de redirecionamento",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redirecionamento (URL de ligação de postagem de serviço do consumidor de afirmação) - Dica",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "URL de redirecionamento (URL de ligação de postagem de serviço do consumidor de afirmação)",
|
||||
"Redirect URLs": "URLs de redirecionamento",
|
||||
"Redirect URLs - Tooltip": "Lista de URLs de redirecionamento permitidos, com suporte à correspondência por expressões regulares; URLs que não estão na lista falharão ao redirecionar",
|
||||
"Refresh token expire": "Expiração do token de atualização",
|
||||
@@ -133,28 +132,27 @@
|
||||
"SAML hash algorithm": "Algoritmo de hash SAML",
|
||||
"SAML hash algorithm - Tooltip": "Algoritmo de hash para assinatura SAML",
|
||||
"SAML metadata": "Metadados do SAML",
|
||||
"SAML metadata - Tooltip": "Os metadados do protocolo SAML - Dica",
|
||||
"SAML metadata - Tooltip": "Os metadados do protocolo SAML",
|
||||
"SAML reply URL": "URL de resposta do SAML",
|
||||
"Security": "Segurança",
|
||||
"Security": "Security",
|
||||
"Select": "Selecionar",
|
||||
"Side panel HTML": "HTML do painel lateral",
|
||||
"Side panel HTML - Edit": "Editar HTML do painel lateral",
|
||||
"Side panel HTML - Tooltip": "Personalize o código HTML do painel lateral da página de login - Dica",
|
||||
"Side panel HTML - Tooltip": "Personalize o código HTML para o painel lateral da página de login",
|
||||
"Sign Up Error": "Erro ao Registrar",
|
||||
"Signin": "Entrar",
|
||||
"Signin (Default True)": "Login (Padrão: Verdadeiro)",
|
||||
"Signin items": "Itens de login",
|
||||
"Signin items - Tooltip": "Itens de login - Dica",
|
||||
"Signin items - Tooltip": "Itens de login",
|
||||
"Signin methods": "Métodos de login",
|
||||
"Signin methods - Tooltip": "Dica: métodos de login - Dica",
|
||||
"Signin methods - Tooltip": "Dica: métodos de login",
|
||||
"Signin session": "Sessão de login",
|
||||
"Signup items": "Itens de registro",
|
||||
"Signup items - Tooltip": "Itens para os usuários preencherem ao fazer login - Dica",
|
||||
"Signup items - Tooltip": "Itens para os usuários preencherem ao registrar novas contas",
|
||||
"Single Choice": "Escolha única",
|
||||
"Small icon": "Ícone pequeno",
|
||||
"Static Value": "Static Value",
|
||||
"String": "String",
|
||||
"Tags - Tooltip": "Apenas usuários com a tag listada nas tags da aplicação podem fazer login - Dica",
|
||||
"Tags - Tooltip": "Apenas usuários com a tag listada nas tags da aplicação podem fazer login",
|
||||
"The application does not allow to sign up new account": "A aplicação não permite o registro de novas contas",
|
||||
"Token expire": "Expiração do Token",
|
||||
"Token expire - Tooltip": "Tempo de expiração do token de acesso",
|
||||
@@ -164,7 +162,7 @@
|
||||
"Token format - Tooltip": "O formato do token de acesso",
|
||||
"Token signing method": "Método de assinatura do token",
|
||||
"Token signing method - Tooltip": "Método de assinatura do token JWT. Deve ser o mesmo algoritmo do certificado.",
|
||||
"UI Customization": "Personalização da interface",
|
||||
"UI Customization": "UI Customization",
|
||||
"Use Email as NameID": "Usar e-mail como NameID",
|
||||
"Use Email as NameID - Tooltip": "Dica: usar e-mail como NameID",
|
||||
"Vertical": "Vertical",
|
||||
@@ -186,7 +184,9 @@
|
||||
"Expire in years - Tooltip": "Período de validade do certificado, em anos",
|
||||
"New Cert": "Novo Certificado",
|
||||
"Private key": "Chave privada",
|
||||
"Private key - Tooltip": "Chave privada correspondente ao certificado de chave pública"
|
||||
"Private key - Tooltip": "Chave privada correspondente ao certificado de chave pública",
|
||||
"Scope - Tooltip": "Cenários de uso do certificado",
|
||||
"Type - Tooltip": "Tipo de certificado"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Código que você recebeu",
|
||||
@@ -250,7 +250,7 @@
|
||||
"Width": "Largura"
|
||||
},
|
||||
"general": {
|
||||
"A normal user can only modify the permission submitted by itself": "Um usuário comum só pode modificar a permissão enviada por ele mesmo",
|
||||
"A normal user can only modify the permission submitted by itself": "A normal user can only modify the permission submitted by itself",
|
||||
"AI Assistant": "Assistente de IA",
|
||||
"API key": "Chave da API",
|
||||
"API key - Tooltip": "Chave da API para acessar o serviço",
|
||||
@@ -275,7 +275,6 @@
|
||||
"Applications that require authentication": "Aplicações que requerem autenticação",
|
||||
"Apps": "Aplicativos",
|
||||
"Authorization": "Autorização",
|
||||
"Auto": "Auto",
|
||||
"Avatar": "Avatar",
|
||||
"Avatar - Tooltip": "Imagem de avatar pública do usuário",
|
||||
"Back": "Voltar",
|
||||
@@ -283,13 +282,11 @@
|
||||
"Business & Payments": "Negócios e Pagamentos",
|
||||
"Cancel": "Cancelar",
|
||||
"Captcha": "Captcha",
|
||||
"Cart": "Carrinho",
|
||||
"Category": "Category",
|
||||
"Category - Tooltip": "Category - Tooltip",
|
||||
"Cart": "Cart",
|
||||
"Cert": "Certificado",
|
||||
"Cert - Tooltip": "O certificado da chave pública que precisa ser verificado pelo SDK do cliente correspondente a esta aplicação",
|
||||
"Certs": "Certificados",
|
||||
"Clear": "Limpar",
|
||||
"Clear": "Clear",
|
||||
"Click to Upload": "Clique para Enviar",
|
||||
"Client IP": "IP do cliente",
|
||||
"Close": "Fechar",
|
||||
@@ -310,12 +307,12 @@
|
||||
"Delete": "Excluir",
|
||||
"Description": "Descrição",
|
||||
"Description - Tooltip": "Informações de descrição detalhadas para referência, o Casdoor em si não irá utilizá-las",
|
||||
"Detail": "Detalhes",
|
||||
"Detail": "详情",
|
||||
"Disable": "Desabilitar",
|
||||
"Display name": "Nome de exibição",
|
||||
"Display name - Tooltip": "Um nome amigável e facilmente legível exibido publicamente na interface do usuário",
|
||||
"Down": "Descer",
|
||||
"Download template": "Baixar modelo",
|
||||
"Download template": "Download template",
|
||||
"Edit": "Editar",
|
||||
"Email": "E-mail",
|
||||
"Email - Tooltip": "Endereço de e-mail válido",
|
||||
@@ -330,21 +327,21 @@
|
||||
"Enabled successfully": "Ativado com sucesso",
|
||||
"Enforcers": "Aplicadores",
|
||||
"Failed to add": "Falha ao adicionar",
|
||||
"Failed to cancel": "Falha ao cancelar",
|
||||
"Failed to cancel": "Failed to cancel",
|
||||
"Failed to connect to server": "Falha ao conectar ao servidor",
|
||||
"Failed to copy": "Falha ao copiar",
|
||||
"Failed to delete": "Falha ao excluir",
|
||||
"Failed to enable": "Falha ao ativar",
|
||||
"Failed to get": "Falha ao obter",
|
||||
"Failed to load": "Falha ao carregar",
|
||||
"Failed to log out": "Falha ao sair",
|
||||
"Failed to load": "Failed to load",
|
||||
"Failed to log out": "Failed to log out",
|
||||
"Failed to remove": "Falha ao remover",
|
||||
"Failed to save": "Falha ao salvar",
|
||||
"Failed to send": "Falha ao enviar",
|
||||
"Failed to send": "Failed to send",
|
||||
"Failed to sync": "Falha ao sincronizar",
|
||||
"Failed to unlink": "Falha ao desvincular",
|
||||
"Failed to update": "Falha ao atualizar",
|
||||
"Failed to upload": "Falha ao enviar",
|
||||
"Failed to unlink": "Failed to unlink",
|
||||
"Failed to update": "Failed to update",
|
||||
"Failed to upload": "Failed to upload",
|
||||
"Failed to verify": "Falha ao verificar",
|
||||
"False": "Falso",
|
||||
"Favicon": "Ícone do site",
|
||||
@@ -357,7 +354,7 @@
|
||||
"Forget URL - Tooltip": "URL personalizada para a página de \"Esqueci a senha\". Se não definido, será usada a página padrão de \"Esqueci a senha\" do Casdoor. Quando definido, o link de \"Esqueci a senha\" na página de login será redirecionado para esta URL",
|
||||
"Forms": "Formulários",
|
||||
"Found some texts still not translated? Please help us translate at": "Encontrou algum texto ainda não traduzido? Ajude-nos a traduzir em",
|
||||
"Generate": "Gerar",
|
||||
"Generate": "Generate",
|
||||
"Go to enable": "Ir para ativar",
|
||||
"Go to writable demo site?": "Acessar o site de demonstração gravável?",
|
||||
"Groups": "Grupos",
|
||||
@@ -370,7 +367,7 @@
|
||||
"IP whitelist": "Lista de IPs permitidos",
|
||||
"IP whitelist - Tooltip": "Lista branca de IP",
|
||||
"Identity": "Identidade",
|
||||
"Impersonation": "Impersonação",
|
||||
"Impersonation": "Impersonation",
|
||||
"Invitations": "Convites",
|
||||
"Is enabled": "Está habilitado",
|
||||
"Is enabled - Tooltip": "Define se está habilitado",
|
||||
@@ -384,7 +381,7 @@
|
||||
"Last name - Tooltip": "O sobrenome do usuário",
|
||||
"Later": "Depois",
|
||||
"Logging & Auditing": "Registro e Auditoria",
|
||||
"Login page": "Página de login",
|
||||
"Login page": "Login page",
|
||||
"Logo": "Logotipo",
|
||||
"Logo - Tooltip": "Ícones que o aplicativo apresenta para o mundo externo",
|
||||
"Logo dark": "Logo escuro",
|
||||
@@ -403,21 +400,21 @@
|
||||
"Name": "Nome",
|
||||
"Name - Tooltip": "ID único em formato de string",
|
||||
"Name format": "Formato do nome",
|
||||
"No products available": "Nenhum produto disponível",
|
||||
"No sheets found in file": "Nenhuma planilha encontrada no arquivo",
|
||||
"No verification method": "Nenhum método de verificação",
|
||||
"No products available": "No products available",
|
||||
"No sheets found in file": "No sheets found in file",
|
||||
"No verification method": "No verification method",
|
||||
"Non-LDAP": "Não-LDAP",
|
||||
"None": "Nenhum",
|
||||
"OAuth providers": "Provedores OAuth",
|
||||
"OFF": "DESLIGADO",
|
||||
"OK": "OK",
|
||||
"ON": "LIGADO",
|
||||
"Only 1 MFA method can be required": "Apenas 1 método de MFA pode ser exigido",
|
||||
"Or": "Ou",
|
||||
"Orders": "Pedidos",
|
||||
"Only 1 MFA method can be required": "Only 1 MFA method can be required",
|
||||
"Or": "Or",
|
||||
"Orders": "Orders",
|
||||
"Organization": "Organização",
|
||||
"Organization - Tooltip": "Semelhante a conceitos como inquilinos ou grupos de usuários, cada usuário e aplicativo pertence a uma organização",
|
||||
"Organization is null": "A organização está vazia",
|
||||
"Organization is null": "Organization is null",
|
||||
"Organizations": "Organizações",
|
||||
"Password": "Senha",
|
||||
"Password - Tooltip": "Certifique-se de que a senha está correta",
|
||||
@@ -440,21 +437,21 @@
|
||||
"Phone - Tooltip": "Número de telefone",
|
||||
"Phone only": "Apenas telefone",
|
||||
"Phone or Email": "Telefone ou e-mail",
|
||||
"Place Order": "Fazer pedido",
|
||||
"Place Order": "Place Order",
|
||||
"Plain": "Simples",
|
||||
"Plan": "Plano",
|
||||
"Plan - Tooltip": "Plano de assinatura",
|
||||
"Plans": "Planos",
|
||||
"Plans": "Kế hoạch",
|
||||
"Plans - Tooltip": "Dica: planos",
|
||||
"Please complete the captcha correctly": "Por favor, conclua o captcha corretamente",
|
||||
"Please complete the captcha correctly": "Please complete the captcha correctly",
|
||||
"Please input your search": "Por favor, insira sua busca",
|
||||
"Please select at least 1 user first": "Por favor, selecione pelo menos 1 usuário primeiro",
|
||||
"Please select at least 1 user first": "Please select at least 1 user first",
|
||||
"Preview": "Visualizar",
|
||||
"Preview - Tooltip": "Visualizar os efeitos configurados",
|
||||
"Pricing": "Preços",
|
||||
"Pricing - Tooltip": "Dica: preços",
|
||||
"Pricings": "Tabelas de preços",
|
||||
"Product Store": "Loja de produtos",
|
||||
"Pricings": "Bảng giá",
|
||||
"Product Store": "Product Store",
|
||||
"Products": "Produtos",
|
||||
"Provider": "Provedor",
|
||||
"Provider - Tooltip": "Provedores de pagamento a serem configurados, incluindo PayPal, Alipay, WeChat Pay, etc.",
|
||||
@@ -479,8 +476,6 @@
|
||||
"SSH type - Tooltip": "Tipo de autenticação para conexão SSH",
|
||||
"Save": "Salvar",
|
||||
"Save & Exit": "Salvar e Sair",
|
||||
"Scopes": "Scopes",
|
||||
"Scopes - Tooltip": "Scopes - Tooltip",
|
||||
"Search": "Buscar",
|
||||
"Send": "Enviar",
|
||||
"Session ID": "ID da sessão",
|
||||
@@ -499,12 +494,12 @@
|
||||
"State": "Estado",
|
||||
"State - Tooltip": "Estado",
|
||||
"Status": "Status",
|
||||
"Subscriptions": "Assinaturas",
|
||||
"Subscriptions": "Đăng ký",
|
||||
"Successfully added": "Adicionado com sucesso",
|
||||
"Successfully canceled": "Cancelado com sucesso",
|
||||
"Successfully canceled": "Successfully canceled",
|
||||
"Successfully copied": "Copiado com sucesso",
|
||||
"Successfully deleted": "Excluído com sucesso",
|
||||
"Successfully executed": "Executado com sucesso",
|
||||
"Successfully executed": "Successfully executed",
|
||||
"Successfully removed": "Removido com sucesso",
|
||||
"Successfully saved": "Salvo com sucesso",
|
||||
"Successfully sent": "Enviado com sucesso",
|
||||
@@ -519,12 +514,12 @@
|
||||
"Syncers": "Sincronizadores",
|
||||
"System Info": "Informações do Sistema",
|
||||
"Tab": "Aba",
|
||||
"The actions cannot be empty": "As ações não podem estar vazias",
|
||||
"The resources cannot be empty": "Os recursos não podem estar vazios",
|
||||
"The users and roles cannot be empty at the same time": "Os usuários e as funções não podem estar vazios ao mesmo tempo",
|
||||
"The actions cannot be empty": "The actions cannot be empty",
|
||||
"The resources cannot be empty": "The resources cannot be empty",
|
||||
"The users and roles cannot be empty at the same time": "The users and roles cannot be empty at the same time",
|
||||
"There was a problem signing you in..": "Ocorreu um problema ao fazer login...",
|
||||
"This is a read-only demo site!": "Este é um site de demonstração apenas para leitura!",
|
||||
"Tickets": "Chamados",
|
||||
"Tickets": "Tickets",
|
||||
"Timestamp": "Carimbo de data/hora",
|
||||
"Title": "Título",
|
||||
"Title - Tooltip": "Título",
|
||||
@@ -535,18 +530,17 @@
|
||||
"Transactions": "Transações",
|
||||
"True": "Verdadeiro",
|
||||
"Type": "Tipo",
|
||||
"Type - Tooltip": "Type - Tooltip",
|
||||
"URL": "URL",
|
||||
"URL - Tooltip": "Link da URL",
|
||||
"Unknown application name": "Nome de aplicação desconhecido",
|
||||
"Unknown authentication type": "Tipo de autenticação desconhecido",
|
||||
"Unknown application name": "Unknown application name",
|
||||
"Unknown authentication type": "Unknown authentication type",
|
||||
"Up": "Acima",
|
||||
"Updated time": "Hora da atualização",
|
||||
"Upload (.xlsx)": "Enviar (.xlsx)",
|
||||
"Upload (.xlsx)": "Upload (.xlsx)",
|
||||
"User": "Usuário",
|
||||
"User - Tooltip": "Certifique-se de que o nome de usuário esteja correto",
|
||||
"User Management": "Gerenciamento de usuários",
|
||||
"User already exists": "O usuário já existe",
|
||||
"User already exists": "User already exists",
|
||||
"User containers": "Pools de Usuários",
|
||||
"User type": "Tipo de Usuário",
|
||||
"User type - Tooltip": "Tags às quais o usuário pertence, com valor padrão de \"usuário-normal\"",
|
||||
@@ -554,10 +548,10 @@
|
||||
"Users - Tooltip": "Dica: usuários",
|
||||
"Users under all organizations": "Usuários em todas as organizações",
|
||||
"Verifications": "Verificações",
|
||||
"View": "Ver",
|
||||
"View": "View",
|
||||
"Webhooks": "Webhooks",
|
||||
"You can only select one physical group": "Você só pode selecionar um grupo físico",
|
||||
"You must select a picture first": "Você deve selecionar uma imagem primeiro",
|
||||
"You must select a picture first": "You must select a picture first",
|
||||
"empty": "vazio",
|
||||
"remove": "remover",
|
||||
"{total} in total": "{total} no total"
|
||||
@@ -704,7 +698,7 @@
|
||||
"Please confirm the information below": "Por favor, confirme as informações abaixo",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Por favor, salve este código de recuperação. Se seu dispositivo não puder fornecer um código de autenticação, você poderá redefinir a autenticação MFA com este código",
|
||||
"Protect your account with Multi-factor authentication": "Proteja sua conta com autenticação multifator",
|
||||
"Push notification receiver": "Destinatário de notificação push",
|
||||
"Push notification receiver": "Push notification receiver",
|
||||
"Recovery code": "Código de recuperação",
|
||||
"Remember this account for {hour} hours": "Lembrar desta conta por {hour} horas",
|
||||
"Scan the QR code with your Authenticator App": "Escaneie o código QR com seu aplicativo autenticador",
|
||||
@@ -714,17 +708,17 @@
|
||||
"To ensure the security of your account, it is required to enable multi-factor authentication": "Para garantir a segurança da sua conta, é obrigatório ativar a autenticação multifator",
|
||||
"Use Authenticator App": "Usar aplicativo autenticador",
|
||||
"Use Email": "Usar e-mail",
|
||||
"Use Push Notification": "Usar notificação push",
|
||||
"Use Radius": "Usar RADIUS",
|
||||
"Use Push Notification": "Use Push Notification",
|
||||
"Use Radius": "Use Radius",
|
||||
"Use SMS": "Usar SMS",
|
||||
"Use SMS verification code": "Usar código de verificação por SMS",
|
||||
"Use a recovery code": "Usar código de recuperação",
|
||||
"Verify Code": "Verificar código",
|
||||
"Verify Password": "Verificar senha",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "Você ativou a autenticação multifator. Por favor, clique em 'Enviar código' para continuar",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "Você ativou a autenticação multifator. Por favor, insira a senha do RADIUS",
|
||||
"You have enabled Multi-Factor Authentication, please enter the RADIUS password": "You have enabled Multi-Factor Authentication, please enter the RADIUS password",
|
||||
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "Você ativou a autenticação multifator. Por favor, insira o código TOTP",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "Você ativou a autenticação multifator. Por favor, insira o código de verificação da notificação push",
|
||||
"You have enabled Multi-Factor Authentication, please enter the verification code from push notification": "You have enabled Multi-Factor Authentication, please enter the verification code from push notification",
|
||||
"Your email is": "Seu e-mail é",
|
||||
"Your phone is": "Seu telefone é",
|
||||
"preferred": "preferido"
|
||||
@@ -738,29 +732,29 @@
|
||||
"New Model": "Novo Modelo"
|
||||
},
|
||||
"order": {
|
||||
"Cancel time": "Hora do cancelamento",
|
||||
"Edit Order": "Editar Pedido",
|
||||
"New Order": "Novo Pedido",
|
||||
"Order not found": "Pedido não encontrado",
|
||||
"Pay": "Pagar",
|
||||
"Payment failed time": "Hora da falha no pagamento",
|
||||
"Payment time": "Hora do pagamento",
|
||||
"Price": "Preço",
|
||||
"Return to Order List": "Voltar para a lista de pedidos",
|
||||
"Timeout time": "Tempo limite",
|
||||
"View Order": "Ver Pedido"
|
||||
"Cancel time": "Cancel time",
|
||||
"Edit Order": "Edit Order",
|
||||
"New Order": "New Order",
|
||||
"Order not found": "Order not found",
|
||||
"Pay": "Pay",
|
||||
"Payment failed time": "Payment failed time",
|
||||
"Payment time": "Payment time",
|
||||
"Price": "Price",
|
||||
"Return to Order List": "Return to Order List",
|
||||
"Timeout time": "Timeout time",
|
||||
"View Order": "View Order"
|
||||
},
|
||||
"organization": {
|
||||
"Account items": "Itens da Conta",
|
||||
"Account items - Tooltip": "Itens na página de Configurações Pessoais",
|
||||
"Account menu": "Menu da conta",
|
||||
"Account menu - Tooltip": "Dica: menu da conta",
|
||||
"Admin navbar items": "Itens da barra de navegação do administrador",
|
||||
"Admin navbar items - Tooltip": "Dica: itens da barra de navegação do administrador",
|
||||
"Balance credit": "Saldo de créditos",
|
||||
"Balance credit - Tooltip": "Dica: saldo de créditos",
|
||||
"Balance currency": "Moeda do saldo",
|
||||
"Balance currency - Tooltip": "Dica: moeda do saldo",
|
||||
"Account menu": "Account menu",
|
||||
"Account menu - Tooltip": "Account menu - Tooltip",
|
||||
"Admin navbar items": "Admin navbar items",
|
||||
"Admin navbar items - Tooltip": "Admin navbar items - Tooltip",
|
||||
"Balance credit": "Balance credit",
|
||||
"Balance credit - Tooltip": "Balance credit - Tooltip",
|
||||
"Balance currency": "Balance currency",
|
||||
"Balance currency - Tooltip": "Balance currency - Tooltip",
|
||||
"Edit Organization": "Editar Organização",
|
||||
"Follow global theme": "Seguir tema global",
|
||||
"Has privilege consent": "Tem consentimento de privilégio",
|
||||
@@ -773,8 +767,8 @@
|
||||
"Modify rule": "Modificar regra",
|
||||
"New Organization": "Nova Organização",
|
||||
"Optional": "Opcional",
|
||||
"Org balance": "Saldo da organização",
|
||||
"Org balance - Tooltip": "Dica: saldo da organização",
|
||||
"Org balance": "Org balance",
|
||||
"Org balance - Tooltip": "Org balance - Tooltip",
|
||||
"Password expire days": "Dias para expiração da senha",
|
||||
"Password expire days - Tooltip": "Dica: dias para expiração da senha",
|
||||
"Prompt": "Prompt",
|
||||
@@ -784,10 +778,10 @@
|
||||
"Tags": "Etiquetas",
|
||||
"Use Email as username": "Usar e-mail como nome de usuário",
|
||||
"Use Email as username - Tooltip": "Usar e-mail como nome de usuário se o campo de nome de usuário não estiver visível no cadastro",
|
||||
"User balance": "Saldo do usuário",
|
||||
"User balance - Tooltip": "Dica: saldo do usuário",
|
||||
"User navbar items": "Itens da barra de navegação do usuário",
|
||||
"User navbar items - Tooltip": "Dica: itens da barra de navegação do usuário",
|
||||
"User balance": "User balance",
|
||||
"User balance - Tooltip": "User balance - Tooltip",
|
||||
"User navbar items": "User navbar items",
|
||||
"User navbar items - Tooltip": "User navbar items - Tooltip",
|
||||
"User types": "Tipos de usuário",
|
||||
"User types - Tooltip": "Dica: tipos de usuário",
|
||||
"View rule": "Ver regra",
|
||||
@@ -832,15 +826,15 @@
|
||||
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Por favor, verifique cuidadosamente as informações da sua fatura. Uma vez emitida, a fatura não pode ser cancelada ou modificada.",
|
||||
"Please pay the order first!": "Por favor, faça o pagamento do pedido primeiro!",
|
||||
"Processing...": "Processando...",
|
||||
"Products - Tooltip": "Dica: produtos",
|
||||
"Products - Tooltip": "Products - Tooltip",
|
||||
"Recharged successfully": "Recarregado com sucesso",
|
||||
"Result": "Resultado",
|
||||
"The payment has been canceled": "O pagamento foi cancelado",
|
||||
"The payment has failed": "O pagamento falhou",
|
||||
"The payment has timed out": "O pagamento expirou",
|
||||
"The payment has timed out": "The payment has timed out",
|
||||
"The payment is still under processing": "O pagamento ainda está sendo processado",
|
||||
"View Payment": "Ver Pagamento",
|
||||
"You can view your order details or return to the order list": "Você pode ver os detalhes do seu pedido ou voltar para a lista de pedidos",
|
||||
"View Payment": "View Payment",
|
||||
"You can view your order details or return to the order list": "You can view your order details or return to the order list",
|
||||
"You have successfully completed the payment": "Você concluiu o pagamento com sucesso",
|
||||
"You have successfully recharged": "Você recarregou com sucesso",
|
||||
"Your current balance is": "Seu saldo atual é",
|
||||
@@ -873,78 +867,71 @@
|
||||
},
|
||||
"plan": {
|
||||
"Edit Plan": "Editar Plano",
|
||||
"Is exclusive": "Is exclusive",
|
||||
"Is exclusive - Tooltip": "Is exclusive - Tooltip",
|
||||
"New Plan": "Novo Plano",
|
||||
"Period": "Período",
|
||||
"Period - Tooltip": "Período",
|
||||
"Plan name": "Nome do plano",
|
||||
"Plan name": "Plan name",
|
||||
"Price - Tooltip": "Preço",
|
||||
"Related product": "Produto relacionado",
|
||||
"View Plan": "Ver Plano",
|
||||
"per month": "por mês",
|
||||
"View Plan": "View Plan",
|
||||
"per month": "mỗi tháng",
|
||||
"per year": "por ano"
|
||||
},
|
||||
"pricing": {
|
||||
"Copy pricing page URL": "Copiar URL da página de preços",
|
||||
"Copy pricing page URL": "Sao chép URL trang bảng giá",
|
||||
"Edit Pricing": "Editar Preço",
|
||||
"Free": "Gratuito",
|
||||
"Getting started": "Começar",
|
||||
"Free": "Miễn phí",
|
||||
"Getting started": "Bắt đầu",
|
||||
"New Pricing": "Novo Preço",
|
||||
"Pricing name": "Nome do preço",
|
||||
"Trial duration": "Duração do teste",
|
||||
"Trial duration - Tooltip": "Dica: duração do teste",
|
||||
"View Pricing": "Ver Preço",
|
||||
"days trial available!": "dias de teste disponíveis!",
|
||||
"Pricing name": "Pricing name",
|
||||
"Trial duration": "Thời gian thử nghiệm",
|
||||
"Trial duration - Tooltip": "Thời gian thử nghiệm",
|
||||
"View Pricing": "View Pricing",
|
||||
"days trial available!": "ngày dùng thử có sẵn!",
|
||||
"paid-user do not have active subscription or pending subscription, please select a plan to buy": "usuário pago não tem assinatura ativa ou pendente, por favor selecione um plano para comprar"
|
||||
},
|
||||
"product": {
|
||||
"Add to cart": "Adicionar ao carrinho",
|
||||
"Add to cart": "Add to cart",
|
||||
"AirWallex": "AirWallex",
|
||||
"Alipay": "Alipay",
|
||||
"Amount": "Valor",
|
||||
"Amount": "Amount",
|
||||
"Buy": "Comprar",
|
||||
"Buy Product": "Comprar Produto",
|
||||
"Cart contains invalid products, please delete them before placing an order": "Cart contains invalid products, please delete them before placing an order",
|
||||
"Custom amount available": "Valor personalizado disponível",
|
||||
"Custom price should be greater than zero": "O preço personalizado deve ser maior que zero",
|
||||
"Custom amount available": "Custom amount available",
|
||||
"Custom price should be greater than zero": "Custom price should be greater than zero",
|
||||
"Detail - Tooltip": "Detalhes do produto",
|
||||
"Disable custom amount": "Desativar valor personalizado",
|
||||
"Disable custom amount - Tooltip": "Dica: desativar valor personalizado",
|
||||
"Dummy": "Fictício",
|
||||
"Disable custom amount": "Disable custom amount",
|
||||
"Disable custom amount - Tooltip": "Disable custom amount - Tooltip",
|
||||
"Dummy": "Dummy",
|
||||
"Edit Product": "Editar Produto",
|
||||
"Enter preset amounts": "Inserir valores predefinidos",
|
||||
"Failed to create order": "Falha ao criar pedido",
|
||||
"Enter preset amounts": "Enter preset amounts",
|
||||
"Failed to create order": "Failed to create order",
|
||||
"Image": "Imagem",
|
||||
"Image - Tooltip": "Imagem do produto",
|
||||
"Information": "Informações",
|
||||
"Invalid product": "Invalid product",
|
||||
"Information": "Information",
|
||||
"Is recharge": "É recarga",
|
||||
"Is recharge - Tooltip": "Se o produto atual é para recarregar saldo",
|
||||
"New Product": "Novo Produto",
|
||||
"No recharge options available": "No recharge options available",
|
||||
"Order created successfully": "Pedido criado com sucesso",
|
||||
"Order created successfully": "Order created successfully",
|
||||
"PayPal": "PayPal",
|
||||
"Payment cancelled": "Pagamento cancelado",
|
||||
"Payment failed": "Pagamento falhou",
|
||||
"Payment providers": "Provedores de Pagamento",
|
||||
"Payment providers - Tooltip": "Fornecedores de serviços de pagamento",
|
||||
"Placing order...": "Processando pedido...",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Por favor, adicione pelo menos uma opção de recarga quando o valor personalizado estiver desativado",
|
||||
"Please select a currency": "Por favor, selecione uma moeda",
|
||||
"Please select at least one payment provider": "Por favor, selecione pelo menos um provedor de pagamento",
|
||||
"Processing payment...": "Processando pagamento...",
|
||||
"Product list cannot be empty": "A lista de produtos não pode estar vazia",
|
||||
"Product not found or invalid": "Product not found or invalid",
|
||||
"Please add at least one recharge option when custom amount is disabled": "Please add at least one recharge option when custom amount is disabled",
|
||||
"Please select a currency": "Please select a currency",
|
||||
"Please select at least one payment provider": "Please select at least one payment provider",
|
||||
"Processing payment...": "Processing payment...",
|
||||
"Product list cannot be empty": "Product list cannot be empty",
|
||||
"Quantity": "Quantidade",
|
||||
"Quantity - Tooltip": "Quantidade do produto",
|
||||
"Recharge options": "Opções de recarga",
|
||||
"Recharge options - Tooltip": "Dica: opções de recarga",
|
||||
"Recharge products need to go to the product detail page to set custom amount": "Recharge products need to go to the product detail page to set custom amount",
|
||||
"Recharge options": "Recharge options",
|
||||
"Recharge options - Tooltip": "Recharge options - Tooltip",
|
||||
"Return URL": "URL de Retorno",
|
||||
"Return URL - Tooltip": "URL para retornar após a compra bem-sucedida",
|
||||
"SKU": "SKU",
|
||||
"Select amount": "Selecionar valor",
|
||||
"Select amount": "Select amount",
|
||||
"Sold": "Vendido",
|
||||
"Sold - Tooltip": "Quantidade vendida",
|
||||
"Stripe": "Stripe",
|
||||
@@ -952,12 +939,12 @@
|
||||
"Success URL - Tooltip": "URL para retornar após compra",
|
||||
"Tag - Tooltip": "Tag do produto",
|
||||
"Test buy page..": "Página de teste de compra...",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "A moeda do produto que você está adicionando é diferente da moeda dos itens no carrinho",
|
||||
"The currency of the product you are adding is different from the currency of the items in the cart": "The currency of the product you are adding is different from the currency of the items in the cart",
|
||||
"There is no payment channel for this product.": "Não há canal de pagamento disponível para este produto.",
|
||||
"This product is currently not in sale.": "Este produto não está disponível para venda no momento.",
|
||||
"This product is currently not purchasable (No options available)": "Este produto não pode ser comprado no momento (nenhuma opção disponível)",
|
||||
"Total Price": "Preço total",
|
||||
"View Product": "Ver Produto",
|
||||
"This product is currently not purchasable (No options available)": "This product is currently not purchasable (No options available)",
|
||||
"Total Price": "Total Price",
|
||||
"View Product": "View Product",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@@ -985,6 +972,8 @@
|
||||
"Can signin": "Pode fazer login",
|
||||
"Can signup": "Pode se inscrever",
|
||||
"Can unlink": "Pode desvincular",
|
||||
"Category": "Categoria",
|
||||
"Category - Tooltip": "Selecione uma categoria",
|
||||
"Channel No.": "Número do canal",
|
||||
"Channel No. - Tooltip": "Número do canal",
|
||||
"Chat ID": "ID do chat",
|
||||
@@ -999,8 +988,10 @@
|
||||
"Client secret 2 - Tooltip": "A segunda chave secreta do cliente",
|
||||
"Content": "Conteúdo",
|
||||
"Content - Tooltip": "Dica: conteúdo",
|
||||
"DB test": "Teste do banco de dados",
|
||||
"DB test - Tooltip": "Dica: teste do banco de dados",
|
||||
"DB test": "DB test",
|
||||
"DB test - Tooltip": "DB test - Tooltip",
|
||||
"Disable SSL": "Desabilitar SSL",
|
||||
"Disable SSL - Tooltip": "Se deve desabilitar o protocolo SSL ao comunicar com o servidor SMTP",
|
||||
"Domain": "Domínio",
|
||||
"Domain - Tooltip": "Domínio personalizado para armazenamento de objetos",
|
||||
"Edit Provider": "Editar Provedor",
|
||||
@@ -1010,10 +1001,10 @@
|
||||
"Email regex - Tooltip": "Dica: regex de e-mail",
|
||||
"Email title": "Título do e-mail",
|
||||
"Email title - Tooltip": "Título do e-mail",
|
||||
"Enable PKCE": "Ativar PKCE",
|
||||
"Enable PKCE - Tooltip": "Dica: ativar PKCE",
|
||||
"Enable proxy": "Ativar proxy",
|
||||
"Enable proxy - Tooltip": "Ativar proxy socks5 ao enviar e-mail ou SMS",
|
||||
"Enable PKCE": "Enable PKCE",
|
||||
"Enable PKCE - Tooltip": "Enable PKCE - Tooltip",
|
||||
"Enable proxy": "Enable proxy",
|
||||
"Enable proxy - Tooltip": "Enable socks5 Proxy when sending email or sms",
|
||||
"Endpoint": "Endpoint",
|
||||
"Endpoint (Intranet)": "Endpoint (Intranet)",
|
||||
"Endpoint - Tooltip": "Dica: endpoint",
|
||||
@@ -1040,13 +1031,13 @@
|
||||
"Key ID - Tooltip": "ID da chave",
|
||||
"Key text": "Texto da chave",
|
||||
"Key text - Tooltip": "Texto da chave",
|
||||
"LDAP port": "Porta LDAP",
|
||||
"LDAP port": "LDAP port",
|
||||
"Metadata": "Metadados",
|
||||
"Metadata - Tooltip": "Metadados SAML",
|
||||
"Metadata url": "URL de metadados",
|
||||
"Metadata url - Tooltip": "Dica: URL de metadados",
|
||||
"Method - Tooltip": "Método de login, código QR ou login silencioso",
|
||||
"Mobile": "Dispositivo móvel",
|
||||
"Mobile": "Mobile",
|
||||
"New Provider": "Novo Provedor",
|
||||
"Parameter": "Parâmetro",
|
||||
"Parameter - Tooltip": "Dica: parâmetro",
|
||||
@@ -1064,10 +1055,10 @@
|
||||
"Prompted": "Solicitado",
|
||||
"Provider URL": "URL do Provedor",
|
||||
"Provider URL - Tooltip": "URL para configurar o provedor de serviço, este campo é apenas usado para referência e não é usado no Casdoor",
|
||||
"Provider test successful": "Teste do provedor bem-sucedido",
|
||||
"Provider test successful": "Provider test successful",
|
||||
"Public key": "Chave pública",
|
||||
"Public key - Tooltip": "Dica: chave pública",
|
||||
"RADIUS Shared Secret - Tooltip": "Segredo compartilhado do RADIUS",
|
||||
"RADIUS Shared Secret - Tooltip": "Shared Secret of RADIUS",
|
||||
"Region": "Região",
|
||||
"Region - Tooltip": "Dica: região",
|
||||
"Region ID": "ID da Região",
|
||||
@@ -1083,12 +1074,9 @@
|
||||
"SP ACS URL": "URL SP ACS",
|
||||
"SP ACS URL - Tooltip": "URL SP ACS",
|
||||
"SP Entity ID": "ID da Entidade SP",
|
||||
"SSL mode": "SSL mode",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Scene": "Cenário",
|
||||
"Scene - Tooltip": "Cenário",
|
||||
"Scope": "Escopo",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Secret access key": "Chave de acesso secreta",
|
||||
"Secret access key - Tooltip": "Chave de acesso secreta",
|
||||
"Secret key": "Chave secreta",
|
||||
@@ -1103,7 +1091,7 @@
|
||||
"Service ID identifier": "Identificador do ID do serviço",
|
||||
"Service ID identifier - Tooltip": "Identificador do ID do serviço",
|
||||
"Service account JSON": "JSON da conta de serviço",
|
||||
"Service account JSON - Tooltip": "Dica: JSON da conta de serviço",
|
||||
"Service account JSON - Tooltip": "Service account JSON - Tooltip",
|
||||
"Sign Name": "Nome do Sinal",
|
||||
"Sign Name - Tooltip": "Nome da assinatura a ser usada",
|
||||
"Sign request": "Solicitação de assinatura",
|
||||
@@ -1123,12 +1111,12 @@
|
||||
"Sub type - Tooltip": "Subtipo",
|
||||
"Subject": "Assunto",
|
||||
"Subject - Tooltip": "Assunto do e-mail",
|
||||
"Subtype": "Subtipo",
|
||||
"Subtype - Tooltip": "Dica: subtipo",
|
||||
"Subtype": "Subtype",
|
||||
"Subtype - Tooltip": "Subtype - Tooltip",
|
||||
"Syncer test": "Teste de sincronizador",
|
||||
"Syncer test - Tooltip": "Testar se a conexão com o sincronizador está funcionando",
|
||||
"Team ID": "ID da equipe",
|
||||
"Team ID - Tooltip": "Dica: ID da equipe",
|
||||
"Team ID - Tooltip": "Team ID - Tooltip",
|
||||
"Template code": "Código do modelo",
|
||||
"Template code - Tooltip": "Código do modelo",
|
||||
"Tenant ID": "ID Tenant",
|
||||
@@ -1138,8 +1126,8 @@
|
||||
"Test SMTP Connection": "Testar Conexão SMTP",
|
||||
"Third-party": "Terceiro",
|
||||
"This field is required": "Este campo é obrigatório",
|
||||
"To address": "Endereço do destinatário",
|
||||
"To address - Tooltip": "Endereço de e-mail do campo \"Para\"",
|
||||
"To address": "To address",
|
||||
"To address - Tooltip": "Email address of \"To\"",
|
||||
"Token URL": "URL do Token",
|
||||
"Token URL - Tooltip": "URL do Token",
|
||||
"Use WeChat Media Platform in PC": "Usar plataforma de mídia WeChat no PC",
|
||||
@@ -1151,7 +1139,7 @@
|
||||
"Use id as name": "Usar ID como nome",
|
||||
"Use id as name - Tooltip": "Usar ID como nome do usuário",
|
||||
"User flow": "Fluxo do usuário",
|
||||
"User flow - Tooltip": "Fluxo do usuário - Dica",
|
||||
"User flow - Tooltip": "User flow - Tooltip",
|
||||
"User mapping": "Mapeamento de usuário",
|
||||
"User mapping - Tooltip": "Dica: mapeamento de usuário",
|
||||
"UserInfo URL": "URL do UserInfo",
|
||||
@@ -1181,7 +1169,7 @@
|
||||
"Sub domains": "Subdomínios",
|
||||
"Sub domains - Tooltip": "Domínios incluídos na função atual",
|
||||
"Sub groups": "Subgrupos",
|
||||
"Sub groups - Tooltip": "Subgrupos - Dica",
|
||||
"Sub groups - Tooltip": "Sub groups - Tooltip",
|
||||
"Sub roles": "Subfunções",
|
||||
"Sub roles - Tooltip": "Funções incluídas na função atual",
|
||||
"Sub users": "Subusuários",
|
||||
@@ -1236,35 +1224,32 @@
|
||||
"Active": "Ativa",
|
||||
"Edit Subscription": "Editar Assinatura",
|
||||
"End time": "Hora de término",
|
||||
"End time - Tooltip": "Dica: hora de término",
|
||||
"End time - Tooltip": "End time - Tooltip",
|
||||
"Error": "Erro",
|
||||
"Expired": "Expirada",
|
||||
"New Subscription": "Nova Assinatura",
|
||||
"Start time": "Hora de início",
|
||||
"Start time - Tooltip": "Dica: hora de início",
|
||||
"Subscription plan": "Plano de assinatura",
|
||||
"Subscription pricing": "Preço da assinatura",
|
||||
"Start time - Tooltip": "Start time - Tooltip",
|
||||
"Subscription plan": "Subscription plan",
|
||||
"Subscription pricing": "Subscription pricing",
|
||||
"Suspended": "Suspensa",
|
||||
"Upcoming": "Próxima",
|
||||
"View Subscription": "Ver Assinatura"
|
||||
"View Subscription": "View Subscription"
|
||||
},
|
||||
"syncer": {
|
||||
"API Token / Password": "Token de API / Senha",
|
||||
"AWS Access Key ID": "AWS Access Key ID",
|
||||
"AWS Region": "AWS Region",
|
||||
"AWS Secret Access Key": "AWS Secret Access Key",
|
||||
"Admin Email": "E-mail do administrador",
|
||||
"API Token / Password": "API Token / Password",
|
||||
"Admin Email": "Admin Email",
|
||||
"Affiliation table": "Tabela de Afiliação",
|
||||
"Affiliation table - Tooltip": "Nome da tabela no banco de dados da unidade de trabalho",
|
||||
"Avatar base URL": "URL base do Avatar",
|
||||
"Avatar base URL - Tooltip": "Prefixo URL para as imagens de avatar",
|
||||
"Bind DN": "DN de vinculação",
|
||||
"Bind DN": "Bind DN",
|
||||
"Casdoor column": "Coluna Casdoor",
|
||||
"Column name": "Nome da coluna",
|
||||
"Column type": "Tipo de coluna",
|
||||
"Connect successfully": "Conectado com sucesso",
|
||||
"Corp ID": "ID da corporação",
|
||||
"Corp secret": "Segredo da corporação",
|
||||
"Corp ID": "Corp ID",
|
||||
"Corp secret": "Corp secret",
|
||||
"Database": "Banco de dados",
|
||||
"Database - Tooltip": "Nome original do banco de dados",
|
||||
"Database type": "Tipo de banco de dados",
|
||||
@@ -1278,13 +1263,15 @@
|
||||
"Is read-only": "É somente leitura",
|
||||
"Is read-only - Tooltip": "Dica: é somente leitura",
|
||||
"New Syncer": "Novo Syncer",
|
||||
"Paste your Google Workspace service account JSON key here": "Cole aqui a chave JSON da conta de serviço do Google Workspace",
|
||||
"Paste your Google Workspace service account JSON key here": "Paste your Google Workspace service account JSON key here",
|
||||
"SCIM Server URL": "SCIM Server URL",
|
||||
"SSH host": "Host SSH",
|
||||
"SSH password": "Senha SSH",
|
||||
"SSH port": "Porta SSH",
|
||||
"SSH user": "Usuário SSH",
|
||||
"Service account key": "Chave da conta de serviço",
|
||||
"SSL mode": "Modo SSL",
|
||||
"SSL mode - Tooltip": "SSL mode - Tooltip",
|
||||
"Service account key": "Service account key",
|
||||
"Sync interval": "Intervalo de sincronização",
|
||||
"Sync interval - Tooltip": "Unidade em segundos",
|
||||
"Table": "Tabela",
|
||||
@@ -1292,8 +1279,8 @@
|
||||
"Table columns": "Colunas da tabela",
|
||||
"Table columns - Tooltip": "Colunas da tabela envolvidas na sincronização de dados. Colunas que não participam da sincronização não precisam ser adicionadas",
|
||||
"Test Connection": "Testar conexão",
|
||||
"Test DB Connection": "Testar conexão com o banco de dados",
|
||||
"Username (optional)": "Nome de usuário (opcional)"
|
||||
"Test DB Connection": "Test DB Connection",
|
||||
"Username (optional)": "Username (optional)"
|
||||
},
|
||||
"system": {
|
||||
"API Latency": "Latência da API",
|
||||
@@ -1312,7 +1299,7 @@
|
||||
"Version": "Versão"
|
||||
},
|
||||
"theme": {
|
||||
"Blossom": "Florescer",
|
||||
"Blossom": "Blossom",
|
||||
"Border radius": "Raio da borda",
|
||||
"Compact": "Compacto",
|
||||
"Customize theme": "Personalizar tema",
|
||||
@@ -1324,16 +1311,16 @@
|
||||
"Theme - Tooltip": "Tema de estilo do aplicativo"
|
||||
},
|
||||
"ticket": {
|
||||
"Closed": "Fechado",
|
||||
"Edit Ticket": "Editar chamado",
|
||||
"In Progress": "Em andamento",
|
||||
"Messages": "Mensagens",
|
||||
"New Ticket": "Novo chamado",
|
||||
"Open": "Aberto",
|
||||
"Please enter a message": "Por favor, digite uma mensagem",
|
||||
"Press Ctrl+Enter to send": "Pressione Ctrl+Enter para enviar",
|
||||
"Resolved": "Resolvido",
|
||||
"Type your message here...": "Digite sua mensagem aqui..."
|
||||
"Closed": "Closed",
|
||||
"Edit Ticket": "Edit Ticket",
|
||||
"In Progress": "In Progress",
|
||||
"Messages": "Messages",
|
||||
"New Ticket": "New Ticket",
|
||||
"Open": "Open",
|
||||
"Please enter a message": "Please enter a message",
|
||||
"Press Ctrl+Enter to send": "Press Ctrl+Enter to send",
|
||||
"Resolved": "Resolved",
|
||||
"Type your message here...": "Type your message here..."
|
||||
},
|
||||
"token": {
|
||||
"Access token": "Token de acesso",
|
||||
@@ -1355,7 +1342,7 @@
|
||||
"Amount - Tooltip": "Dica: valor do produto negociado",
|
||||
"Edit Transaction": "Editar Transação",
|
||||
"New Transaction": "Nova Transação",
|
||||
"Recharge": "Recarga"
|
||||
"Recharge": "Recharge"
|
||||
},
|
||||
"user": {
|
||||
"3rd-party logins": "Logins de terceiros",
|
||||
@@ -1376,7 +1363,7 @@
|
||||
"Captcha Verify Success": "Verificação de captcha bem-sucedida",
|
||||
"City": "Cidade",
|
||||
"Country code": "Código do país",
|
||||
"Country code - Tooltip": "Dica: código do país",
|
||||
"Country code - Tooltip": "Country code - Tooltip",
|
||||
"Country/Region": "País/Região",
|
||||
"Country/Region - Tooltip": "País ou região",
|
||||
"Edit User": "Editar Usuário",
|
||||
@@ -1385,7 +1372,7 @@
|
||||
"Email cannot be empty": "O e-mail não pode ficar em branco",
|
||||
"Email/phone reset successfully": "Redefinição de e-mail/telefone com sucesso",
|
||||
"Empty input!": "Entrada vazia!",
|
||||
"Face IDs": "IDs faciais",
|
||||
"Face IDs": "Face IDs",
|
||||
"Gender": "Gênero",
|
||||
"Gender - Tooltip": "Dica: gênero",
|
||||
"Homepage": "Página inicial",
|
||||
@@ -1399,10 +1386,10 @@
|
||||
"ID card type": "Tipo de cartão de identidade",
|
||||
"ID card type - Tooltip": "Dica: tipo de cartão de identidade",
|
||||
"ID card with person": "Cartão de identidade com pessoa",
|
||||
"ID verification": "Verificação de ID",
|
||||
"ID verification - Tooltip": "Dica: verificação de ID",
|
||||
"Identity verification successful": "Verificação de identidade bem-sucedida",
|
||||
"Identity verified": "Identidade verificada",
|
||||
"ID verification": "ID verification",
|
||||
"ID verification - Tooltip": "ID verification - Tooltip",
|
||||
"Identity verification successful": "Identity verification successful",
|
||||
"Identity verified": "Identity verified",
|
||||
"Input your email": "Digite seu e-mail",
|
||||
"Input your phone number": "Digite seu número de telefone",
|
||||
"Is admin": "É administrador",
|
||||
@@ -1412,7 +1399,7 @@
|
||||
"Is forbidden": "Está proibido",
|
||||
"Is forbidden - Tooltip": "Usuários proibidos não podem fazer login novamente",
|
||||
"Is online": "Está online",
|
||||
"Is verified": "Está verificado",
|
||||
"Is verified": "Is verified",
|
||||
"Karma": "Karma",
|
||||
"Karma - Tooltip": "Dica: karma",
|
||||
"Keys": "Chaves",
|
||||
@@ -1437,19 +1424,19 @@
|
||||
"Other": "Outro",
|
||||
"Password set successfully": "Senha definida com sucesso",
|
||||
"Phone cannot be empty": "O telefone não pode ficar vazio",
|
||||
"Please enter your real name": "Por favor, insira seu nome real",
|
||||
"Please fill in ID card information first": "Por favor, preencha primeiro as informações do documento de identidade",
|
||||
"Please fill in your real name first": "Por favor, preencha primeiro seu nome real",
|
||||
"Please enter your real name": "Please enter your real name",
|
||||
"Please fill in ID card information first": "Please fill in ID card information first",
|
||||
"Please fill in your real name first": "Please fill in your real name first",
|
||||
"Please select avatar from resources": "Selecione um avatar dos recursos",
|
||||
"Properties": "Propriedades",
|
||||
"Properties - Tooltip": "Propriedades do usuário",
|
||||
"Ranking": "Classificação",
|
||||
"Ranking - Tooltip": "Dica: classificação",
|
||||
"Re-enter New": "Digite Novamente",
|
||||
"Real name - Tooltip": "Dica: nome real",
|
||||
"Register source": "Origem do registro",
|
||||
"Real name - Tooltip": "Real name - Tooltip",
|
||||
"Register source": "Register source",
|
||||
"Register source - Tooltip": "A fonte da qual o usuário foi registrado",
|
||||
"Register type": "Tipo de registro",
|
||||
"Register type": "Register type",
|
||||
"Register type - Tooltip": "O método usado para registrar o usuário",
|
||||
"Reset Email...": "Redefinir E-mail...",
|
||||
"Reset Phone...": "Redefinir Telefone...",
|
||||
@@ -1475,8 +1462,8 @@
|
||||
"User Profile": "Perfil do usuário",
|
||||
"Values": "Valores",
|
||||
"Verification code sent": "Código de verificação enviado",
|
||||
"Verified": "Verificado",
|
||||
"Verify Identity": "Verificar identidade",
|
||||
"Verified": "Verified",
|
||||
"Verify Identity": "Verify Identity",
|
||||
"WebAuthn credentials": "Credenciais WebAuthn",
|
||||
"Work": "Trabalho",
|
||||
"You have changed the username, please save your change first before modifying the password": "Você alterou o nome de usuário. Salve a alteração antes de modificar a senha",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user