Compare commits

...

47 Commits

Author SHA1 Message Date
Yang Luo
df8e9fceea feat: disable /forget API when "Forgot Password?" signin item is hidden (#4977) 2026-02-04 23:04:00 +08:00
Yang Luo
d674f0c33d feat: update Swagger docs 2026-02-03 21:34:38 +08:00
buzaslan129
1e1b5273d9 feat: expose get-all-* Casbin endpoints in Swagger (#4952) 2026-02-03 21:32:50 +08:00
IsAurora6
cf5e88915c feat: The order.products display is divided into two parts. Remove the "startTime" and "endTime", and add an updateTime field. (#4968) 2026-02-03 21:12:27 +08:00
Yang Luo
c8973e6c9e feat: add Cloud PNVS SMS provider (#4964) 2026-02-03 02:00:08 +08:00
Yang Luo
87ea451561 feat: support group sync in Google Workspace syncer (#4962) 2026-02-03 01:58:28 +08:00
Yang Luo
8f32779b42 feat: fix invitation code group assignment for OAuth provider signup (#4961) 2026-02-03 01:23:36 +08:00
Yang Luo
aba471b4e8 feat: install lsof in ALLINONE Docker image (#4958) 2026-02-02 23:51:49 +08:00
DacongDA
72b70c3b03 feat: use sqlite DB instead of mariadb for all-in-one Docker image (#4949) 2026-02-02 00:13:14 +08:00
DacongDA
a1c56894c7 feat: add tabs to user edit page (#4945) 2026-02-01 14:01:28 +08:00
Yang Luo
a9ae9394c7 feat: add Linux machine login via LDAP with POSIX attributes (#4944) 2026-01-31 22:37:29 +08:00
Yang Luo
5f0fa5f23e feat: fix properties field xlsx import issue in user list page (#4943) 2026-01-31 01:49:36 +08:00
Yang Luo
f99aa047a9 feat: add Org.AccountItems.Tab field to have tabs in user edit page (#4892) 2026-01-30 21:56:35 +08:00
Yang Luo
1d22b7ebd0 feat: prevent duplicate webhook events from redundant payment notifications (#4936) 2026-01-30 21:56:09 +08:00
IsAurora6
d147053329 feat: Optimize the display of the products column on the order and payment, adjust the color of the “Add to Cart” button. (#4933) 2026-01-30 14:03:15 +08:00
IsAurora6
0f8cd92be4 feat: resolve returnUrl redirection failure of UserEditPage (#4931) 2026-01-29 09:37:47 +08:00
DacongDA
7ea6f1296d feat: fix i18n/generate.go bug in handling "\" (#4930) 2026-01-28 23:35:23 +08:00
Yang Luo
db8c649f5e feat: include payment status in notify-payment webhook payload (#4929) 2026-01-28 19:59:10 +08:00
DacongDA
a06d003589 feat: make codeChallenge dynamic for custom OAuth provider (#4924) 2026-01-28 17:56:28 +08:00
Jacob
33298e44d4 feat(ldap-sync): support syncing phone country code and formatting mobile number (#4919) 2026-01-28 14:09:52 +08:00
IsAurora6
f4d86f8d92 feat: fix incorrect clearing of the returnUrl path parameter in redirects (#4920) 2026-01-28 10:51:44 +08:00
Yang Luo
af4337a1ae feat: add multi-address support to user edit page (#4916) 2026-01-27 21:46:41 +08:00
IsAurora6
81e650df65 feat: Optimize the display of the order price column and improve parameter passing in the OrderPay view mode. (#4912) 2026-01-27 12:17:15 +08:00
Yang Luo
fcea1e4c07 feat: add SCIM 2.0 syncer (#4909) 2026-01-27 01:47:50 +08:00
Yang Luo
639a8a47b1 feat: add Okta syncer (#4908) 2026-01-27 01:19:39 +08:00
Yang Luo
43f61d4426 feat: add Lark syncer (#4897) 2026-01-27 01:00:19 +08:00
IsAurora6
e90cdb8a74 feat: add default payment providers on startup, improve checkProduct() logic (#4895) 2026-01-27 00:23:09 +08:00
DacongDA
bfe8955250 feat: remove bottom save button and extra scrollbar in application edit page (#4890) 2026-01-25 11:03:52 +08:00
DacongDA
36b9c4602a feat: add tab menu for application edit page (#4889) 2026-01-24 18:05:17 +08:00
IsAurora6
18117833e1 feat: Optimize button logic of product buy/store page,non-Created orders display "Detail" Button, and add "clear cart" Button (#4887) 2026-01-24 12:17:44 +08:00
Yang Luo
78dde97b64 feat: add PKCE support for Custom OAuth providers (#4880) 2026-01-23 21:29:57 +08:00
Yang Luo
3a06c66057 feat: fix Azure AD syncer OAuth2 token request - send parameters in body (#4878) 2026-01-22 23:01:38 +08:00
Yang Luo
aa59901400 feat: change Application.TermsOfUse length to 200 chars 2026-01-21 17:24:06 +08:00
IsAurora6
8e03b2d97c feat: Enable subscription-based products to be added to the cart and purchased, and optimize the cart page. (#4868) 2026-01-21 17:18:11 +08:00
MarshallHuang
d1da9499e8 fix: update OIDC discovery to use consistent authorization endpoint (#4872) 2026-01-21 16:54:51 +08:00
Yang Luo
2e7673c015 feat: use unionid in DingTalk syncer for consistency with OAuth provider (#4870) 2026-01-21 00:07:31 +08:00
DacongDA
2d1ace427e feat: support GetVersionInfo() API in released binary (#4860) 2026-01-20 18:05:11 +08:00
IsAurora6
039c12afa3 feat: add the shopping cart page (#4855) 2026-01-19 12:12:15 +08:00
slavb18
4236160fa7 feat: add User.OriginalRefreshToken field (#4721) 2026-01-19 12:08:18 +08:00
Yang Luo
071b5ddec0 feat: fix error for "/.well-known/:application/openid-configuration" API (#4866) 2026-01-19 03:06:33 +08:00
Gucheng Wang
f46b92d225 feat: reduce i18n languages (#4862) 2026-01-18 18:28:13 +08:00
Yang Luo
cc7eb4664c feat: support comma-separated user tags in application tag validation (#4856) 2026-01-17 01:26:12 +08:00
Yang Luo
1567723e2b feat: fix null issue for GrantTypes and RedirectUris in application 2026-01-17 01:18:57 +08:00
IsAurora6
074253f45e feat: Optimize PlaceOrder-related methods and pages to support the creation of multi-item orders. (#4847) 2026-01-16 16:52:02 +08:00
Yang Luo
23c86e9018 feat: add application.EnableSamlAssertionSignature to allow disabling SAML assertion signatures (#4850) 2026-01-16 14:30:48 +08:00
DacongDA
f088827a50 feat: redirect user to last login org's login page while cookie expired (#4844) 2026-01-15 18:17:12 +08:00
IsAurora6
663815fefe feat: The frontend supports payment logic for multi-item orders. (#4843) 2026-01-15 18:16:28 +08:00
138 changed files with 6570 additions and 26064 deletions

View File

@@ -14,6 +14,7 @@ before:
- go mod tidy
# you may remove this if you don't need go generate
#- go generate ./...
- go test -v -run TestGetVersionInfo ./util/system_test.go ./util/system.go ./util/variable.go
builds:
- env:

View File

@@ -19,8 +19,8 @@ RUN go mod download
# Copy source files
COPY . .
RUN go test -v -run TestGetVersionInfo ./util/system_test.go ./util/system.go ./util/variable.go
RUN ./build.sh
RUN go test -v -run TestGetVersionInfo ./util/system_test.go ./util/system.go > version_info.txt
FROM alpine:latest AS STANDARD
LABEL MAINTAINER="https://casdoor.org/"
@@ -46,35 +46,25 @@ WORKDIR /
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
ENTRYPOINT ["/server"]
FROM debian:latest AS db
RUN apt update \
&& apt install -y \
mariadb-server \
mariadb-client \
&& rm -rf /var/lib/apt/lists/*
FROM db AS ALLINONE
FROM debian:latest AS ALLINONE
LABEL MAINTAINER="https://casdoor.org/"
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN apt update
RUN apt install -y ca-certificates && update-ca-certificates
RUN apt install -y ca-certificates lsof && update-ca-certificates
WORKDIR /
COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
COPY --from=FRONT /web/build ./web/build
ENTRYPOINT ["/bin/bash"]

View File

@@ -100,7 +100,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
// check user's tag
if !user.IsGlobalAdmin() && !user.IsAdmin && len(application.Tags) > 0 {
// only users with the tag that is listed in the application tags can login
if !util.InSlice(application.Tags, user.Tag) {
// supports comma-separated tags in user.Tag (e.g., "default-policy,project-admin")
if !util.HasTagInSlice(application.Tags, user.Tag) {
c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag))
return
}
@@ -738,6 +739,7 @@ func (c *ApiController) Login() {
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
// OAuth
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
idpInfo.CodeVerifier = authForm.CodeVerifier
var idProvider idp.IdProvider
idProvider, err = idp.GetIdProvider(idpInfo, authForm.RedirectUri)
if err != nil {
@@ -865,10 +867,16 @@ func (c *ApiController) Login() {
return
}
if application.IsSignupItemRequired("Invitation code") {
c.ResponseError(c.T("check:Invitation code cannot be blank"))
// Check and validate invitation code
invitation, msg := object.CheckInvitationCode(application, organization, &authForm, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
}
invitationName := ""
if invitation != nil {
invitationName = invitation.Name
}
// Handle UseEmailAsUsername for OAuth and Web3
if organization.UseEmailAsUsername && userInfo.Email != "" {
@@ -935,11 +943,16 @@ func (c *ApiController) Login() {
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
Invitation: invitationName,
InvitationCode: authForm.InvitationCode,
RegisterType: "Application Signup",
RegisterSource: fmt.Sprintf("%s/%s", application.Organization, application.Name),
}
if providerItem.SignupGroup != "" {
// Set group from invitation code if available, otherwise use provider's signup group
if invitation != nil && invitation.SignupGroup != "" {
user.Groups = []string{invitation.SignupGroup}
} else if providerItem.SignupGroup != "" {
user.Groups = []string{providerItem.SignupGroup}
}
@@ -954,6 +967,16 @@ func (c *ApiController) Login() {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
// Increment invitation usage count
if invitation != nil {
invitation.UsedCount += 1
_, err = object.UpdateInvitation(invitation.GetId(), invitation, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
}
}
// sync info from 3rd-party if possible

View File

@@ -303,6 +303,13 @@ func (c *ApiController) BatchEnforce() {
c.ResponseOk(res, keyRes)
}
// GetAllObjects
// @Title GetAllObjects
// @Tag Enforcer API
// @Description Get all objects for a user (Casbin API)
// @Param userId query string false "user id like built-in/admin"
// @Success 200 {object} controllers.Response The Response object
// @router /get-all-objects [get]
func (c *ApiController) GetAllObjects() {
userId := c.Ctx.Input.Query("userId")
if userId == "" {
@@ -322,6 +329,13 @@ func (c *ApiController) GetAllObjects() {
c.ResponseOk(objects)
}
// GetAllActions
// @Title GetAllActions
// @Tag Enforcer API
// @Description Get all actions for a user (Casbin API)
// @Param userId query string false "user id like built-in/admin"
// @Success 200 {object} controllers.Response The Response object
// @router /get-all-actions [get]
func (c *ApiController) GetAllActions() {
userId := c.Ctx.Input.Query("userId")
if userId == "" {
@@ -341,6 +355,13 @@ func (c *ApiController) GetAllActions() {
c.ResponseOk(actions)
}
// GetAllRoles
// @Title GetAllRoles
// @Tag Enforcer API
// @Description Get all roles for a user (Casbin API)
// @Param userId query string false "user id like built-in/admin"
// @Success 200 {object} controllers.Response The Response object
// @router /get-all-roles [get]
func (c *ApiController) GetAllRoles() {
userId := c.Ctx.Input.Query("userId")
if userId == "" {

View File

@@ -15,8 +15,8 @@
package controllers
import (
"encoding/json"
"fmt"
"strconv"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@@ -34,30 +34,21 @@ import (
// @Success 200 {object} object.Order The Response object
// @router /place-order [post]
func (c *ApiController) PlaceOrder() {
productId := c.Ctx.Input.Query("productId")
pricingName := c.Ctx.Input.Query("pricingName")
planName := c.Ctx.Input.Query("planName")
customPriceStr := c.Ctx.Input.Query("customPrice")
owner := c.Ctx.Input.Query("owner")
paidUserName := c.Ctx.Input.Query("userName")
if productId == "" {
c.ResponseError(c.T("general:ProductId is required"))
var req struct {
ProductInfos []object.ProductInfo `json:"productInfos"`
}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
if err != nil {
c.ResponseError(err.Error())
return
}
var customPrice float64
if customPriceStr != "" {
var err error
customPrice, err = strconv.ParseFloat(customPriceStr, 64)
if err != nil {
c.ResponseError(fmt.Sprintf(c.T("general:Invalid customPrice: %s"), customPriceStr))
return
}
}
owner, _, err := util.GetOwnerAndNameFromIdWithError(productId)
if err != nil {
c.ResponseError(err.Error())
productInfos := req.ProductInfos
if len(productInfos) == 0 {
c.ResponseError(c.T("product:Product list cannot be empty"))
return
}
@@ -89,7 +80,7 @@ func (c *ApiController) PlaceOrder() {
return
}
order, err := object.PlaceOrder(productId, user, pricingName, planName, customPrice)
order, err := object.PlaceOrder(owner, productInfos, user)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -15,7 +15,10 @@
package controllers
import (
"errors"
"github.com/casdoor/casdoor/util"
"github.com/go-git/go-git/v5"
)
// GetSystemInfo
@@ -46,10 +49,10 @@ func (c *ApiController) GetSystemInfo() {
// @Success 200 {object} util.VersionInfo The Response object
// @router /get-version-info [get]
func (c *ApiController) GetVersionInfo() {
errInfo := ""
versionInfo, err := util.GetVersionInfo()
if err != nil {
errInfo = "Git error: " + err.Error()
if err != nil && !errors.Is(err, git.ErrRepositoryNotExists) {
c.ResponseError(err.Error())
return
}
if versionInfo.Version != "" {
@@ -57,14 +60,7 @@ func (c *ApiController) GetVersionInfo() {
return
}
versionInfo, err = util.GetVersionInfoFromFile()
if err != nil {
errInfo = errInfo + ", File error: " + err.Error()
c.ResponseError(errInfo)
return
}
c.ResponseOk(versionInfo)
c.ResponseOk(util.GetBuiltInVersionInfo())
}
// Health

View File

@@ -187,6 +187,22 @@ func (c *ApiController) SendVerificationCode() {
return
}
// Check if "Forgot password?" signin item is visible when using forget verification
if vform.Method == ForgetVerification {
isForgotPasswordEnabled := false
for _, item := range application.SigninItems {
if item.Name == "Forgot password?" {
isForgotPasswordEnabled = item.Visible
break
}
}
// Block access if the signin item is not found or is explicitly hidden
if !isForgotPasswordEnabled {
c.ResponseError(c.T("verification:The forgot password feature is disabled"))
return
}
}
organization, err := object.GetOrganization(util.GetId(application.Owner, application.Organization))
if err != nil {
c.ResponseError(c.T(err.Error()))

View File

@@ -1,8 +1,10 @@
#!/bin/bash
if [ "${MYSQL_ROOT_PASSWORD}" = "" ] ;then MYSQL_ROOT_PASSWORD=123456 ;fi
service mariadb start
if [ -z "${driverName:-}" ]; then
export driverName=sqlite
fi
if [ -z "${dataSourceName:-}" ]; then
export dataSourceName="file:casdoor.db?cache=shared"
fi
mysqladmin -u root password ${MYSQL_ROOT_PASSWORD}
exec /server --createDatabase=true
exec /server

View File

@@ -46,6 +46,7 @@ type AuthForm struct {
State string `json:"state"`
RedirectUri string `json:"redirectUri"`
Method string `json:"method"`
CodeVerifier string `json:"codeVerifier"`
EmailCode string `json:"emailCode"`
PhoneCode string `json:"phoneCode"`

2
go.mod
View File

@@ -14,6 +14,7 @@ require (
github.com/alibabacloud-go/openapi-util v0.1.0
github.com/alibabacloud-go/tea v1.3.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
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-v2/service/s3 v1.95.0
@@ -111,7 +112,6 @@ require (
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
github.com/alibabacloud-go/tea-utils v1.3.6 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545 // 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

8
go.sum
View File

@@ -766,8 +766,8 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545 h1:0LfzeUr4quwrrrTHn1kfLA0FBdsChCMs8eK2EzOwXVQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
@@ -1294,7 +1294,6 @@ github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aW
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -1308,7 +1307,6 @@ github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -2615,7 +2613,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -2637,6 +2634,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -19,6 +19,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/casdoor/casdoor/util"
@@ -47,7 +48,11 @@ func getAllI18nStringsFrontend(fileContent string) []string {
}
for _, match := range matches {
res = append(res, match[1])
target, err := strconv.Unquote("\"" + match[1] + "\"")
if err != nil {
target = match[1]
}
res = append(res, target)
}
return res
}
@@ -61,7 +66,12 @@ func getAllI18nStringsBackend(fileContent string, isObjectPackage bool) []string
}
for _, match := range matches {
match := strings.SplitN(match[1], ",", 2)
res = append(res, match[1][2:])
target, err := strconv.Unquote("\"" + match[1][2:] + "\"")
if err != nil {
target = match[1][2:]
}
res = append(res, target)
}
} else {
matches := reI18nBackendController.FindAllStringSubmatch(fileContent, -1)
@@ -69,7 +79,11 @@ func getAllI18nStringsBackend(fileContent string, isObjectPackage bool) []string
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
target, err := strconv.Unquote("\"" + match[1][1:] + "\"")
if err != nil {
target = match[1][1:]
}
res = append(res, target)
}
}

View File

@@ -22,60 +22,30 @@ func TestGenerateI18nFrontend(t *testing.T) {
data := parseAllWords("frontend")
applyToOtherLanguage("frontend", "en", data)
applyToOtherLanguage("frontend", "zh", data)
applyToOtherLanguage("frontend", "es", data)
applyToOtherLanguage("frontend", "fr", data)
applyToOtherLanguage("frontend", "de", data)
applyToOtherLanguage("frontend", "id", data)
applyToOtherLanguage("frontend", "ja", data)
applyToOtherLanguage("frontend", "ko", data)
applyToOtherLanguage("frontend", "ru", data)
applyToOtherLanguage("frontend", "zh", data)
applyToOtherLanguage("frontend", "vi", data)
applyToOtherLanguage("frontend", "pt", data)
applyToOtherLanguage("frontend", "it", data)
applyToOtherLanguage("frontend", "ms", data)
applyToOtherLanguage("frontend", "tr", data)
applyToOtherLanguage("frontend", "ar", data)
applyToOtherLanguage("frontend", "he", data)
applyToOtherLanguage("frontend", "nl", data)
applyToOtherLanguage("frontend", "pl", data)
applyToOtherLanguage("frontend", "fi", data)
applyToOtherLanguage("frontend", "sv", data)
applyToOtherLanguage("frontend", "uk", data)
applyToOtherLanguage("frontend", "kk", data)
applyToOtherLanguage("frontend", "fa", data)
applyToOtherLanguage("frontend", "cs", data)
applyToOtherLanguage("frontend", "sk", data)
applyToOtherLanguage("frontend", "az", data)
}
func TestGenerateI18nBackend(t *testing.T) {
data := parseAllWords("backend")
applyToOtherLanguage("backend", "en", data)
applyToOtherLanguage("backend", "zh", data)
applyToOtherLanguage("backend", "es", data)
applyToOtherLanguage("backend", "fr", data)
applyToOtherLanguage("backend", "de", data)
applyToOtherLanguage("backend", "id", data)
applyToOtherLanguage("backend", "ja", data)
applyToOtherLanguage("backend", "ko", data)
applyToOtherLanguage("backend", "ru", data)
applyToOtherLanguage("backend", "zh", data)
applyToOtherLanguage("backend", "vi", data)
applyToOtherLanguage("backend", "pt", data)
applyToOtherLanguage("backend", "it", data)
applyToOtherLanguage("backend", "ms", data)
applyToOtherLanguage("backend", "tr", data)
applyToOtherLanguage("backend", "ar", data)
applyToOtherLanguage("backend", "he", data)
applyToOtherLanguage("backend", "nl", data)
applyToOtherLanguage("backend", "pl", data)
applyToOtherLanguage("backend", "fi", data)
applyToOtherLanguage("backend", "sv", data)
applyToOtherLanguage("backend", "uk", data)
applyToOtherLanguage("backend", "kk", data)
applyToOtherLanguage("backend", "fa", data)
applyToOtherLanguage("backend", "cs", data)
applyToOtherLanguage("backend", "sk", data)
applyToOtherLanguage("backend", "az", data)
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "فشل إضافة المستخدم",
"Get init score failed, error: %w": "فشل الحصول على النتيجة الأولية، الخطأ: %w",
"Please sign out first": "يرجى تسجيل الخروج أولاً",
"The application does not allow to sign up new account": "التطبيق لا يسمح بالتسجيل بحساب جديد"
},
"auth": {
"Challenge method should be S256": "يجب أن تكون طريقة التحدي S256",
"DeviceCode Invalid": "رمز الجهاز غير صالح",
"Failed to create user, user information is invalid: %s": "فشل إنشاء المستخدم، معلومات المستخدم غير صالحة: %s",
"Failed to login in: %s": "فشل تسجيل الدخول: %s",
"Invalid token": "الرمز غير صالح",
"State expected: %s, but got: %s": "كان من المتوقع الحالة: %s، لكن حصلنا على: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد عبر %s، يرجى استخدام طريقة أخرى للتسجيل",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد، يرجى الاتصال بدعم تكنولوجيا المعلومات",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) مرتبط بالفعل بحساب آخر: %s (%s)",
"The application: %s does not exist": "التطبيق: %s غير موجود",
"The application: %s has disabled users to signin": "التطبيق: %s قد عطّل تسجيل دخول المستخدمين",
"The group: %s does not exist": "المجموعة: %s غير موجودة",
"The login method: login with LDAP is not enabled for the application": "طريقة تسجيل الدخول: تسجيل الدخول باستخدام LDAP غير مفعّلة لهذا التطبيق",
"The login method: login with SMS is not enabled for the application": "طريقة تسجيل الدخول: تسجيل الدخول باستخدام الرسائل النصية غير مفعّلة لهذا التطبيق",
"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 organization: %s does not exist": "المنظمة: %s غير موجودة",
"The organization: %s has disabled users to signin": "المنظمة: %s قد عطّلت تسجيل دخول المستخدمين",
"The plan: %s does not exist": "الخطة: %s غير موجودة",
"The pricing: %s does not exist": "التسعير: %s غير موجود",
"The pricing: %s does not have plan: %s": "التسعير: %s لا يحتوي على الخطة: %s",
"The provider: %s does not exist": "المزود: %s غير موجود",
"The provider: %s is not enabled for the application": "المزود: %s غير مفعّل لهذا التطبيق",
"Unauthorized operation": "عملية غير مصرح بها",
"Unknown authentication type (not password or provider), form = %s": "نوع مصادقة غير معروف (ليس كلمة مرور أو مزود)، النموذج = %s",
"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 ليس لديه تسعير افتراضي",
"the application for user %s is not found": "لم يتم العثور على التطبيق الخاص بالمستخدم %s",
"the organization: %s is not found": "لم يتم العثور على المنظمة: %s"
},
"cas": {
"Service %s and %s do not match": "الخدمة %s و %s غير متطابقتين"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s لا تلبي متطلبات تنسيق CIDR: %s",
"Affiliation cannot be blank": "الانتماء لا يمكن أن يكون فارغاً",
"CIDR for IP: %s should not be empty": "CIDR لعنوان IP: %s لا يجب أن يكون فارغاً",
"Default code does not match the code's matching rules": "الرمز الافتراضي لا يتطابق مع قواعد المطابقة",
"DisplayName cannot be blank": "اسم العرض لا يمكن أن يكون فارغاً",
"DisplayName is not valid real name": "اسم العرض ليس اسمًا حقيقيًا صالحًا",
"Email already exists": "البريد الإلكتروني موجود بالفعل",
"Email cannot be empty": "البريد الإلكتروني لا يمكن أن يكون فارغاً",
"Email is invalid": "البريد الإلكتروني غير صالح",
"Empty username.": "اسم المستخدم فارغ.",
"Face data does not exist, cannot log in": "بيانات الوجه غير موجودة، لا يمكن تسجيل الدخول",
"Face data mismatch": "عدم تطابق بيانات الوجه",
"Failed to parse client IP: %s": "فشل تحليل IP العميل: %s",
"FirstName cannot be blank": "الاسم الأول لا يمكن أن يكون فارغاً",
"Invitation code cannot be blank": "رمز الدعوة لا يمكن أن يكون فارغاً",
"Invitation code exhausted": "رمز الدعوة استُنفِد",
"Invitation code is invalid": "رمز الدعوة غير صالح",
"Invitation code suspended": "رمز الدعوة موقوف",
"LDAP user name or password incorrect": "اسم مستخدم LDAP أو كلمة المرور غير صحيحة",
"LastName cannot be blank": "الاسم الأخير لا يمكن أن يكون فارغاً",
"Multiple accounts with same uid, please check your ldap server": "حسابات متعددة بنفس uid، يرجى التحقق من خادم ldap الخاص بك",
"Organization does not exist": "المنظمة غير موجودة",
"Password cannot be empty": "كلمة المرور لا يمكن أن تكون فارغة",
"Phone already exists": "الهاتف موجود بالفعل",
"Phone cannot be empty": "الهاتف لا يمكن أن يكون فارغاً",
"Phone number is invalid": "رقم الهاتف غير صالح",
"Please register using the email corresponding to the invitation code": "يرجى التسجيل باستخدام البريد الإلكتروني المطابق لرمز الدعوة",
"Please register using the phone corresponding to the invitation code": "يرجى التسجيل باستخدام الهاتف المطابق لرمز الدعوة",
"Please register using the username corresponding to the invitation code": "يرجى التسجيل باستخدام اسم المستخدم المطابق لرمز الدعوة",
"Session outdated, please login again": "الجلسة منتهية الصلاحية، يرجى تسجيل الدخول مرة أخرى",
"The invitation code has already been used": "رمز الدعوة تم استخدامه بالفعل",
"The password must contain at least one special character": "يجب أن تحتوي كلمة المرور على حرف خاص واحد على الأقل",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل وحرف صغير ورقم",
"The password must have at least 6 characters": "يجب أن تحتوي كلمة المرور على 6 أحرف على الأقل",
"The password must have at least 8 characters": "يجب أن تحتوي كلمة المرور على 8 أحرف على الأقل",
"The password must not contain any repeated characters": "يجب ألا تحتوي كلمة المرور على أي أحرف متكررة",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "تم حذف المستخدم ولا يمكن استخدامه لتسجيل الدخول، يرجى الاتصال بالمسؤول",
"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": "Hesap alanı \\\"%s\\\" için \\\"%s\\\" değeri, hesap öğesi regex'iyle eşleşmiyor",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Kayıt alanı \\\"%s\\\" için \\\"%s\\\" değeri, \\\"%s\\\" uygulamasının kayıt öğesi regex'iyle eşleşmiyor",
"Username already exists": "اسم المستخدم موجود بالفعل",
"Username cannot be an email address": "اسم المستخدم لا يمكن أن يكون عنوان بريد إلكتروني",
"Username cannot contain white spaces": "اسم المستخدم لا يمكن أن يحتوي على مسافات",
"Username cannot start with a digit": "اسم المستخدم لا يمكن أن يبدأ برقم",
"Username is too long (maximum is 255 characters).": "اسم المستخدم طويل جداً (الحد الأقصى 255 حرفاً).",
"Username must have at least 2 characters": "اسم المستخدم يجب أن يحتوي على حرفين على الأقل",
"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.": "اسم المستخدم يدعم تنسيق البريد الإلكتروني. كما أن اسم المستخدم يمكن أن يحتوي فقط على أحرف وأرقام، شرطات سفلية أو علوية، لا يمكن أن تحتوي على شرطات متتالية، ولا يمكن أن يبدأ أو ينتهي بشرطة. انتبه أيضًا لتنسيق البريد الإلكتروني.",
"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\\\"": "Şifrenizin süresi doldu. Lütfen \\\"Şifremi unuttum\\\"a tıklayarak şifrenizi sıfırlayın",
"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 فرصة متبقية",
"unsupported password type: %s": "نوع كلمة المرور غير مدعوم: %s"
},
"enforcer": {
"the adapter: %s is not found": "المحول: %s غير موجود"
},
"general": {
"Failed to import groups": "فشل استيراد المجموعات",
"Failed to import users": "فشل استيراد المستخدمين",
"Missing parameter": "المعلمة مفقودة",
"Only admin user can specify user": "فقط المسؤول يمكنه تحديد المستخدم",
"Please login first": "يرجى تسجيل الدخول أولاً",
"The organization: %s should have one application at least": "المنظمة: %s يجب أن تحتوي على تطبيق واحد على الأقل",
"The user: %s doesn't exist": "المستخدم: %s غير موجود",
"Wrong userId": "معرف المستخدم غير صحيح",
"don't support captchaProvider: ": "لا يدعم captchaProvider: ",
"this operation is not allowed in demo mode": "هذه العملية غير مسموح بها في وضع العرض التوضيحي",
"this operation requires administrator to perform": "هذه العملية تتطلب مسؤولاً لتنفيذها"
},
"invitation": {
"Invitation %s does not exist": "الدعوة %s غير موجودة"
},
"ldap": {
"Ldap server exist": "خادم LDAP موجود"
},
"link": {
"Please link first": "يرجى الربط أولاً",
"This application has no providers": "هذا التطبيق لا يحتوي على مزودين",
"This application has no providers of type": "هذا التطبيق لا يحتوي على مزودين من النوع",
"This provider can't be unlinked": "لا يمكن فصل هذا المزود",
"You are not the global admin, you can't unlink other users": "أنت لست المسؤول العام، لا يمكنك فصل مستخدمين آخرين",
"You can't unlink yourself, you are not a member of any application": "لا يمكنك فصل نفسك، أنت لست عضواً في أي تطبيق"
},
"organization": {
"Only admin can modify the %s.": "فقط المسؤول يمكنه تعديل %s.",
"The %s is immutable.": "%s غير قابل للتعديل.",
"Unknown modify rule %s.": "قاعدة تعديل غير معروفة %s.",
"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.": "إضافة مستخدم جديد إلى المنظمة \"المدمجة\" غير متوفر حاليًا. يرجى ملاحظة: جميع المستخدمين في المنظمة \"المدمجة\" هم مسؤولون عالميون في Casdoor. يرجى الرجوع إلى الوثائق: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. إذا كنت لا تزال ترغب في إنشاء مستخدم للمنظمة \"المدمجة\"، اไป إلى صفحة إعدادات المنظمة وقم بتمكين خيار \"لديه موافقة صلاحية\"."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "İzin: \\\"%s\\\" mevcut değil"
},
"provider": {
"Invalid application id": "معرف التطبيق غير صالح",
"the provider: %s does not exist": "المزود: %s غير موجود"
},
"resource": {
"User is nil for tag: avatar": "المستخدم nil للوسم: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "اسم المستخدم أو fullFilePath فارغ: username = %s، fullFilePath = %s"
},
"saml": {
"Application %s not found": "التطبيق %s غير موجود"
},
"saml_sp": {
"provider %s's category is not SAML": "فئة المزود %s ليست SAML"
},
"service": {
"Empty parameters for emailForm: %v": "معلمات فارغة لـ emailForm: %v",
"Invalid Email receivers: %s": "مستقبلو البريد الإلكتروني غير صالحين: %s",
"Invalid phone receivers: %s": "مستقلو الهاتف غير صالحين: %s"
},
"storage": {
"The objectKey: %s is not allowed": "مفتاح الكائن: %s غير مسموح به",
"The provider type: %s is not supported": "نوع المزود: %s غير مدعوم"
},
"subscription": {
"Error": "خطأ"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s غير مدعوم في هذا التطبيق",
"Invalid application or wrong clientSecret": "تطبيق غير صالح أو clientSecret خاطئ",
"Invalid client_id": "client_id غير صالح",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s غير موجود في قائمة Redirect URI المسموح بها",
"Token not found, invalid accessToken": "الرمز غير موجود، accessToken غير صالح"
},
"user": {
"Display name cannot be empty": "اسم العرض لا يمكن أن يكون فارغاً",
"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.": "كلمة المرور الجديدة لا يمكن أن تحتوي على مسافات.",
"The new password must be different from your current password": "يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور الحالية",
"the user's owner and name should not be empty": "مالك المستخدم واسمه لا يجب أن يكونا فارغين"
},
"util": {
"No application is found for userId: %s": "لم يتم العثور على تطبيق لـ userId: %s",
"No provider for category: %s is found for application: %s": "لم يتم العثور على مزود للفئة: %s للتطبيق: %s",
"The provider: %s is not found": "المزود: %s غير موجود"
},
"verification": {
"Invalid captcha provider.": "مزود captcha غير صالح.",
"Phone number is invalid in your region %s": "رقم الهاتف غير صالح في منطقتك %s",
"The verification code has already been used!": "رمز التحقق تم استخدامه بالفعل!",
"The verification code has not been sent yet!": "رمز التحقق لم يُرسل بعد!",
"Turing test failed.": "فشل اختبار تورينغ.",
"Unable to get the email modify rule.": "غير قادر على الحصول على قاعدة تعديل البريد الإلكتروني.",
"Unable to get the phone modify rule.": "غير قادر على الحصول على قاعدة تعديل الهاتف.",
"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": "lütfen uygulama için \\\"Sağlayıcılar\\\" listesine bir SMS sağlayıcı ekleyin: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "lütfen uygulama için \\\"Sağlayıcılar\\\" listesine bir E-posta sağlayıcı ekleyin: %s",
"the user does not exist, please sign up first": "المستخدم غير موجود، يرجى التسجيل أولاً"
},
"webauthn": {
"Found no credentials for this user": "لم يتم العثور على بيانات اعتماد لهذا المستخدم",
"Please call WebAuthnSigninBegin first": "يرجى استدعاء WebAuthnSigninBegin أولاً"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "İstifadəçi əlavə etmə uğursuz oldu",
"Get init score failed, error: %w": "Başlanğıc xal alınması uğursuz oldu, xəta: %w",
"Please sign out first": "Xahiş edirik əvvəlcə çıxış edin",
"The application does not allow to sign up new account": "Tətbiq yeni hesab qeydiyyatına icazə vermir"
},
"auth": {
"Challenge method should be S256": "Çağırış metodu S256 olmalıdır",
"DeviceCode Invalid": "Cihaz Kodu Etibarsızdır",
"Failed to create user, user information is invalid: %s": "İstifadəçi yaratma uğursuz oldu, istifadəçi məlumatları etibarsızdır: %s",
"Failed to login in: %s": "Giriş uğursuz oldu: %s",
"Invalid token": "Etibarsız token",
"State expected: %s, but got: %s": "Gözlənilən vəziyyət: %s, lakin alınan: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) mövcud deyil və %s vasitəsilə yeni hesab olaraq qeydiyyatdan keçməyə icazə verilmir, xahiş edirik qeydiyyat üçün başqa üsul istifadə edin",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) mövcud deyil və yeni hesab olaraq qeydiyyatdan keçməyə icazə verilmir, xahiş edirik IT dəstəyinizlə əlaqə saxlayın",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) artıq başqa hesabla əlaqələndirilmişdir: %s (%s)",
"The application: %s does not exist": "Tətbiq: %s mövcud deyil",
"The application: %s has disabled users to signin": "Tətbiq: %s istifadəçilərin girişini söndürmüşdür",
"The group: %s does not exist": "Qrup: %s mövcud deyil",
"The login method: login with LDAP is not enabled for the application": "Giriş metodu: LDAP ilə giriş bu tətbiq üçün aktiv deyil",
"The login method: login with SMS is not enabled for the application": "Giriş metodu: SMS ilə giriş bu tətbiq üçün aktiv deyil",
"The login method: login with email is not enabled for the application": "Giriş metodu: email ilə giriş bu tətbiq üçün aktiv deyil",
"The login method: login with face is not enabled for the application": "Giriş metodu: üz ilə giriş bu tətbiq üçün aktiv deyil",
"The login method: login with password is not enabled for the application": "Giriş metodu: şifrə ilə giriş bu tətbiq üçün aktiv deyil",
"The organization: %s does not exist": "Təşkilat: %s mövcud deyil",
"The organization: %s has disabled users to signin": "Təşkilat: %s istifadəçilərin girişini söndürmüşdür",
"The plan: %s does not exist": "Plan: %s mövcud deyil",
"The pricing: %s does not exist": "Qiymətləndirmə: %s mövcud deyil",
"The pricing: %s does not have plan: %s": "Qiymətləndirmə: %s planı yoxdur: %s",
"The provider: %s does not exist": "Provayder: %s mövcud deyil",
"The provider: %s is not enabled for the application": "Provayder: %s bu tətbiq üçün aktiv deyil",
"Unauthorized operation": "İcazəsiz əməliyyat",
"Unknown authentication type (not password or provider), form = %s": "Naməlum təsdiq növü (şifrə və ya provayder deyil), forma = %s",
"User's tag: %s is not listed in the application's tags": "İstifadəçinin teqi: %s tətbiqin teqləri siyahısında yoxdur",
"UserCode Expired": "İstifadəçi Kodunun Vaxtı Keçib",
"UserCode Invalid": "İstifadəçi Kodu Etibarsızdır",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "ödənişli istifadəçi %s aktiv və ya gözləyən abunəyə malik deyil və tətbiq: %s defolt qiymətləndirməyə malik deyil",
"the application for user %s is not found": "istifadəçi %s üçün tətbiq tapılmadı",
"the organization: %s is not found": "təşkilat: %s tapılmadı"
},
"cas": {
"Service %s and %s do not match": "Xidmət %s və %s uyğun gəlmir"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s CIDR format tələblərinə cavab vermir: %s",
"Affiliation cannot be blank": "Mənsub sahəsi boş ola bilməz",
"CIDR for IP: %s should not be empty": "IP üçün CIDR: %s boş olmamalıdır",
"Default code does not match the code's matching rules": "Defolt kod kodun uyğunluq qaydalara uyğun gəlmir",
"DisplayName cannot be blank": "Göstərilən Ad boş ola bilməz",
"DisplayName is not valid real name": "Göstərilən Ad etibarlı həqiqi ad deyil",
"Email already exists": "Email artıq mövcuddur",
"Email cannot be empty": "Email boş ola bilməz",
"Email is invalid": "Email etibarsızdır",
"Empty username.": "Boş istifadəçi adı.",
"Face data does not exist, cannot log in": "Üz məlumatları mövcud deyil, giriş edilə bilməz",
"Face data mismatch": "Üz məlumatları uyğun gəlmir",
"Failed to parse client IP: %s": "Müştəri IP-ni təhlil etmək uğursuz oldu: %s",
"FirstName cannot be blank": "Ad boş ola bilməz",
"Invitation code cannot be blank": "Dəvət kodu boş ola bilməz",
"Invitation code exhausted": "Dəvət kodu tükənib",
"Invitation code is invalid": "Dəvət kodu etibarsızdır",
"Invitation code suspended": "Dəvət kodu dayandırılıb",
"LDAP user name or password incorrect": "LDAP istifadəçi adı və ya şifrə yanlışdır",
"LastName cannot be blank": "Soyad boş ola bilməz",
"Multiple accounts with same uid, please check your ldap server": "Eyni uid ilə çoxlu hesablar, xahiş edirik ldap serverinizi yoxlayın",
"Organization does not exist": "Təşkilat mövcud deyil",
"Password cannot be empty": "Şifrə boş ola bilməz",
"Phone already exists": "Telefon artıq mövcuddur",
"Phone cannot be empty": "Telefon boş ola bilməz",
"Phone number is invalid": "Telefon nömrəsi etibarsızdır",
"Please register using the email corresponding to the invitation code": "Xahiş edirik dəvət koduna uyğun email istifadə edərək qeydiyyatdan keçin",
"Please register using the phone corresponding to the invitation code": "Xahiş edirik dəvət koduna uyğun telefon istifadə edərək qeydiyyatdan keçin",
"Please register using the username corresponding to the invitation code": "Xahiş edirik dəvət koduna uyğun istifadəçi adı istifadə edərək qeydiyyatdan keçin",
"Session outdated, please login again": "Sessiyanın vaxtı keçib, xahiş edirik yenidən daxil olun",
"The invitation code has already been used": "Dəvət kodu artıq istifadə edilib",
"The password must contain at least one special character": "Parol ən azı bir xüsusi simvol ehtiva etməlidir",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Parol ən azı bir böyük hərf, bir kiçik hərf və bir rəqəm ehtiva etməlidir",
"The password must have at least 6 characters": "Parol ən azı 6 simvoldan ibarət olmalıdır",
"The password must have at least 8 characters": "Parol ən azı 8 simvoldan ibarət olmalıdır",
"The password must not contain any repeated characters": "Parol təkrarlanan simvollar ehtiva etməməlidir",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "İstifadəçi silinib və daxil olmaq üçün istifadə edilə bilməz, zəhmət olmasa administratorla əlaqə saxlayın",
"The user is forbidden to sign in, please contact the administrator": "İstifadəçinin girişi qadağandır, xahiş edirik administratorla əlaqə saxlayın",
"The user: %s doesn't exist in LDAP server": "İstifadəçi: %s LDAP serverində mövcud deyil",
"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.": "İstifadəçi adı yalnız hərf-rəqəm simvolları, alt xətt və ya defis ehtiva edə bilər, ardıcıl defis və ya alt xətt ola bilməz və defis və ya alt xəttlə başlaya və ya bitə bilməz.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hesab sahəsi \\\"%s\\\" üçün dəyər \\\"%s\\\" hesab elementi regex-inə uyğun gəlmir",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Qeydiyyat sahəsi \\\"%s\\\" üçün dəyər \\\"%s\\\" tətbiq \\\"%s\\\"in qeydiyyat elementi regex-inə uyğun gəlmir",
"Username already exists": "İstifadəçi adı artıq mövcuddur",
"Username cannot be an email address": "İstifadəçi adı email ünvanı ola bilməz",
"Username cannot contain white spaces": "İstifadəçi adı boşluqlar ehtiva edə bilməz",
"Username cannot start with a digit": "İstifadəçi adı rəqəmlə başlaya bilməz",
"Username is too long (maximum is 255 characters).": "İstifadəçi adı çox uzundur (maksimum 255 simvoldur).",
"Username must have at least 2 characters": "İstifadəçi adı ən azı 2 simvola malik olmalıdır",
"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.": "İstifadəçi adı email formatını dəstəkləyir. Həmçinin istifadəçi adı yalnız hərf-rəqəm simvolları, alt xətt və ya defis ehtiva edə bilər, ardıcıl defis və ya alt xətt ola bilməz və defis və ya alt xəttlə başlaya və ya bitə bilməz. Həmçinin email formatına diqqət edin.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Yanlış şifrə və ya kod dəfələrlə daxil etmisiniz, xahiş edirik %d dəqiqə gözləyin və yenidən cəhd edin",
"Your IP address: %s has been banned according to the configuration of: ": "IP ünvanınız: %s konfiqurasiyaya görə qadağan edilib: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Şifrənizin vaxtı keçib. Xahiş edirik \\\"Şifrəni unutdum\\\" düyməsinə basaraq şifrənizi sıfırlayın",
"Your region is not allow to signup by phone": "Regionunuzda telefonla qeydiyyata icazə verilmir",
"password or code is incorrect": "şifrə və ya kod yanlışdır",
"password or code is incorrect, you have %s remaining chances": "şifrə və ya kod yanlışdır, %s şansınız qalıb",
"unsupported password type: %s": "dəstəklənməyən şifrə növü: %s"
},
"enforcer": {
"the adapter: %s is not found": "adapter: %s tapılmadı"
},
"general": {
"Failed to import groups": "Qrupları idxal etmək uğursuz oldu",
"Failed to import users": "İstifadəçiləri idxal etmək uğursuz oldu",
"Missing parameter": "Parametr çatışmır",
"Only admin user can specify user": "Yalnız admin istifadəçi başqa istifadəçini təyin edə bilər",
"Please login first": "Xahiş edirik əvvəlcə daxil olun",
"The organization: %s should have one application at least": "Təşkilat: %s ən azı bir tətbiqə malik olmalıdır",
"The user: %s doesn't exist": "İstifadəçi: %s mövcud deyil",
"Wrong userId": "Yanlış istifadəçi ID-si",
"don't support captchaProvider: ": "captcha provayderini dəstəkləmir: ",
"this operation is not allowed in demo mode": "bu əməliyyat demo rejimində icazə verilmir",
"this operation requires administrator to perform": "bu əməliyyat administrator tərəfindən yerinə yetirilməsini tələb edir"
},
"invitation": {
"Invitation %s does not exist": "Dəvət %s mövcud deyil"
},
"ldap": {
"Ldap server exist": "LDAP serveri mövcuddur"
},
"link": {
"Please link first": "Xahiş edirik əvvəlcə əlaqələndirin",
"This application has no providers": "Bu tətbiqin provayderləri yoxdur",
"This application has no providers of type": "Bu tətbiqin bu növdə provayderi yoxdur",
"This provider can't be unlinked": "Bu provayderin əlaqəsi kəsilə bilməz",
"You are not the global admin, you can't unlink other users": "Siz qlobal admin deyilsiniz, digər istifadəçilərin əlaqəsini kəsə bilməzsiniz",
"You can't unlink yourself, you are not a member of any application": "Öz əlaqənizi kəsə bilməzsiniz, heç bir tətbiqin üzvü deyilsiniz"
},
"organization": {
"Only admin can modify the %s.": "Yalnız admin %s-ni dəyişdirə bilər.",
"The %s is immutable.": "%s dəyişilməzdir.",
"Unknown modify rule %s.": "Naməlum dəyişdirmə qaydası %s.",
"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' təşkilatına yeni istifadəçi əlavə etmək hazırda söndürülüb. Qeyd edin: 'built-in' təşkilatındakı bütün istifadəçilər Casdoor-da qlobal administratorlardır. Sənədlərə baxın: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Əgər hələ də 'built-in' təşkilat üçün istifadəçi yaratmaq istəyirsinizsə, təşkilatın tənzimləmələr səhifəsinə gedib 'İmtiyaz razılığına malikdir' seçimini aktiv edin."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "İcazə: \\\"%s\\\" mövcud deyil"
},
"provider": {
"Invalid application id": "Etibarsız tətbiq id-si",
"the provider: %s does not exist": "provayder: %s mövcud deyil"
},
"resource": {
"User is nil for tag: avatar": "Avatar teqi üçün istifadəçi nil-dir",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "İstifadəçi adı və ya tam fayl yolu boşdur: istifadəçi adı = %s, tam fayl yolu = %s"
},
"saml": {
"Application %s not found": "Tətbiq %s tapılmadı"
},
"saml_sp": {
"provider %s's category is not SAML": "provayder %s-in kateqoriyası SAML deyil"
},
"service": {
"Empty parameters for emailForm: %v": "emailForm üçün boş parametrlər: %v",
"Invalid Email receivers: %s": "Etibarsız Email qəbuledicilər: %s",
"Invalid phone receivers: %s": "Etibarsız telefon qəbuledicilər: %s"
},
"storage": {
"The objectKey: %s is not allowed": "obyekt açarı: %s icazə verilmir",
"The provider type: %s is not supported": "provayder növü: %s dəstəklənmir"
},
"subscription": {
"Error": "Xəta"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s bu tətbiqdə dəstəklənmir",
"Invalid application or wrong clientSecret": "Etibarsız tətbiq və ya yanlış müştəri sirri",
"Invalid client_id": "Etibarsız client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Yönləndirmə URI: %s icazə verilən Yönləndirmə URI siyahısında mövcud deyil",
"Token not found, invalid accessToken": "Token tapılmadı, etibarsız accessToken"
},
"user": {
"Display name cannot be empty": "Göstərilən ad boş ola bilməz",
"MFA email is enabled but email is empty": "MFA email aktiv edilib, lakin email boşdur",
"MFA phone is enabled but phone number is empty": "MFA telefon aktiv edilib, lakin telefon nömrəsi boşdur",
"New password cannot contain blank space.": "Yeni şifrə boş yer ehtiva edə bilməz.",
"The new password must be different from your current password": "Yeni şifrə cari şifrənizdən fərqli olmalıdır",
"the user's owner and name should not be empty": "istifadəçinin sahibi və adı boş olmamalıdır"
},
"util": {
"No application is found for userId: %s": "İstifadəçi ID-si üçün heç bir tətbiq tapılmadı: %s",
"No provider for category: %s is found for application: %s": "Tətbiq üçün kateqoriya üçün heç bir provayder tapılmadı: %s: %s",
"The provider: %s is not found": "Provayder: %s tapılmadı"
},
"verification": {
"Invalid captcha provider.": "Etibarsız captcha provaydeři.",
"Phone number is invalid in your region %s": "Telefon nömrəsi sizin regionunuzda etibarsızdır %s",
"The verification code has already been used!": "Doğrulama kodu artıq istifadə edilib!",
"The verification code has not been sent yet!": "Doğrulama kodu hələ göndərilməyib!",
"Turing test failed.": "Türinq testi uğursuz oldu.",
"Unable to get the email modify rule.": "Email dəyişdirmə qaydasını əldə etmək mümkün olmadı.",
"Unable to get the phone modify rule.": "Telefon dəyişdirmə qaydasını əldə etmək mümkün olmadı.",
"Unknown type": "Naməlum növ",
"Wrong verification code!": "Yanlış doğrulama kodu!",
"You should verify your code in %d min!": "Kodunuzu %d dəqiqə içində doğrulamalısınız!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "xahiş edirik tətbiq üçün \\\"Provaydeerlər\\\" siyahısına SMS provaydeři əlavə edin: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "xahiş edirik tətbiq üçün \\\"Provaydeerlər\\\" siyahısına Email provaydeři əlavə edin: %s",
"the user does not exist, please sign up first": "istifadəçi mövcud deyil, xahiş edirik əvvəlcə qeydiyyatdan keçin"
},
"webauthn": {
"Found no credentials for this user": "Bu istifadəçi üçün heç bir etimadnamə tapılmadı",
"Please call WebAuthnSigninBegin first": "Xahiş edirik əvvəlcə WebAuthnSigninBegin çağırın"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Nepodařilo se přidat uživatele",
"Get init score failed, error: %w": "Nepodařilo se získat počáteční skóre, chyba: %w",
"Please sign out first": "Nejprve se prosím odhlaste",
"The application does not allow to sign up new account": "Aplikace neumožňuje registraci nového účtu"
},
"auth": {
"Challenge method should be S256": "Metoda výzvy by měla být S256",
"DeviceCode Invalid": "DeviceCode je neplatný",
"Failed to create user, user information is invalid: %s": "Nepodařilo se vytvořit uživatele, informace o uživateli jsou neplatné: %s",
"Failed to login in: %s": "Nepodařilo se přihlásit: %s",
"Invalid token": "Neplatný token",
"State expected: %s, but got: %s": "Očekávaný stav: %s, ale získán: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet přes %s, prosím použijte jiný způsob registrace",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet, prosím kontaktujte svou IT podporu",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) je již propojen s jiným účtem: %s (%s)",
"The application: %s does not exist": "Aplikace: %s neexistuje",
"The application: %s has disabled users to signin": "Aplikace: %s zakázala přihlašování uživatelů",
"The group: %s does not exist": "Skupina: %s neexistuje",
"The login method: login with LDAP is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí LDAP není pro aplikaci povolena",
"The login method: login with SMS is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí SMS není pro aplikaci povolena",
"The login method: login with email is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí e-mailu není pro aplikaci povolena",
"The login method: login with face is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí obličeje není pro aplikaci povolena",
"The login method: login with password is not enabled for the application": "Metoda přihlášení: přihlášení pomocí hesla není pro aplikaci povolena",
"The organization: %s does not exist": "Organizace: %s neexistuje",
"The organization: %s has disabled users to signin": "Organizace: %s zakázala přihlašování uživatelů",
"The plan: %s does not exist": "Plán: %s neexistuje",
"The pricing: %s does not exist": "Ceník: %s neexistuje",
"The pricing: %s does not have plan: %s": "Ceník: %s nemá plán: %s",
"The provider: %s does not exist": "Poskytovatel: %s neexistuje",
"The provider: %s is not enabled for the application": "Poskytovatel: %s není pro aplikaci povolen",
"Unauthorized operation": "Neoprávněná operace",
"Unknown authentication type (not password or provider), form = %s": "Neznámý typ autentizace (není heslo nebo poskytovatel), formulář = %s",
"User's tag: %s is not listed in the application's tags": "Uživatelův tag: %s není uveden v tagech aplikace",
"UserCode Expired": "UserCode vypršel",
"UserCode Invalid": "UserCode je neplatný",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Placený uživatel %s nemá aktivní ani čekající předplatné a aplikace: %s nemá výchozí ceny",
"the application for user %s is not found": "Aplikace pro uživatele %s nebyla nalezena",
"the organization: %s is not found": "Organizace: %s nebyla nalezena"
},
"cas": {
"Service %s and %s do not match": "Služba %s a %s se neshodují"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s nesplňuje požadavky formátu CIDR: %s",
"Affiliation cannot be blank": "Příslušnost nemůže být prázdná",
"CIDR for IP: %s should not be empty": "CIDR pro IP: %s by neměl být prázdný",
"Default code does not match the code's matching rules": "Výchozí kód neodpovídá pravidlům shody kódu",
"DisplayName cannot be blank": "Zobrazované jméno nemůže být prázdné",
"DisplayName is not valid real name": "Zobrazované jméno není platné skutečné jméno",
"Email already exists": "Email již existuje",
"Email cannot be empty": "Email nemůže být prázdný",
"Email is invalid": "Email je neplatný",
"Empty username.": "Prázdné uživatelské jméno.",
"Face data does not exist, cannot log in": "Data obličeje neexistují, nelze se přihlásit",
"Face data mismatch": "Neshoda dat obličeje",
"Failed to parse client IP: %s": "Nepodařilo se parsovat IP klienta: %s",
"FirstName cannot be blank": "Křestní jméno nemůže být prázdné",
"Invitation code cannot be blank": "Pozvánkový kód nemůže být prázdný",
"Invitation code exhausted": "Pozvánkový kód je vyčerpán",
"Invitation code is invalid": "Pozvánkový kód je neplatný",
"Invitation code suspended": "Pozvánkový kód je pozastaven",
"LDAP user name or password incorrect": "Uživatelské jméno nebo heslo LDAP je nesprávné",
"LastName cannot be blank": "Příjmení nemůže být prázdné",
"Multiple accounts with same uid, please check your ldap server": "Více účtů se stejným uid, prosím zkontrolujte svůj ldap server",
"Organization does not exist": "Organizace neexistuje",
"Password cannot be empty": "Heslo nemůže být prázdné",
"Phone already exists": "Telefon již existuje",
"Phone cannot be empty": "Telefon nemůže být prázdný",
"Phone number is invalid": "Telefonní číslo je neplatné",
"Please register using the email corresponding to the invitation code": "Prosím registrujte se pomocí e-mailu odpovídajícího pozvánkovému kódu",
"Please register using the phone corresponding to the invitation code": "Prosím registrujte se pomocí telefonu odpovídajícího pozvánkovému kódu",
"Please register using the username corresponding to the invitation code": "Prosím registrujte se pomocí uživatelského jména odpovídajícího pozvánkovému kódu",
"Session outdated, please login again": "Relace je zastaralá, prosím přihlaste se znovu",
"The invitation code has already been used": "Pozvánkový kód již byl použit",
"The password must contain at least one special character": "Heslo musí obsahovat alespoň jeden speciální znak",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Heslo musí obsahovat alespoň jedno velké písmeno, jedno malé písmeno a jednu číslici",
"The password must have at least 6 characters": "Heslo musí mít alespoň 6 znaků",
"The password must have at least 8 characters": "Heslo musí mít alespoň 8 znaků",
"The password must not contain any repeated characters": "Heslo nesmí obsahovat opakující se znaky",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Uživatel byl odstraněn a nelze jej použít k přihlášení, kontaktujte prosím správce",
"The user is forbidden to sign in, please contact the administrator": "Uživatel má zakázáno se přihlásit, prosím kontaktujte administrátora",
"The user: %s doesn't exist in LDAP server": "Uživatel: %s neexistuje na LDAP serveru",
"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.": "Uživatelské jméno může obsahovat pouze alfanumerické znaky, podtržítka nebo pomlčky, nemůže mít po sobě jdoucí pomlčky nebo podtržítka a nemůže začínat nebo končit pomlčkou nebo podtržítkem.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hodnota \\\"%s\\\" pro pole účtu \\\"%s\\\" neodpovídá regexu položky účtu",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Hodnota \\\"%s\\\" pro pole registrace \\\"%s\\\" neodpovídá regexu položky registrace aplikace \\\"%s\\\"",
"Username already exists": "Uživatelské jméno již existuje",
"Username cannot be an email address": "Uživatelské jméno nemůže být emailová adresa",
"Username cannot contain white spaces": "Uživatelské jméno nemůže obsahovat mezery",
"Username cannot start with a digit": "Uživatelské jméno nemůže začínat číslicí",
"Username is too long (maximum is 255 characters).": "Uživatelské jméno je příliš dlouhé (maximálně 255 znaků).",
"Username must have at least 2 characters": "Uživatelské jméno musí mít alespoň 2 znaky",
"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.": "Uživatelské jméno podporuje formát e-mailu. Také uživatelské jméno může obsahovat pouze alfanumerické znaky, podtržítka nebo pomlčky, nemůže mít souvislé pomlčky nebo podtržítka a nemůže začínat nebo končit pomlčkou nebo podtržítkem. Také dbejte na formát e-mailu.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali jste špatné heslo nebo kód příliš mnohokrát, prosím počkejte %d minut a zkuste to znovu",
"Your IP address: %s has been banned according to the configuration of: ": "Vaše IP adresa: %s byla zablokována podle konfigurace: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Vaše heslo vypršelo. Prosím resetujte si heslo kliknutím na \\\"Zapomněl jsem heslo\\\"",
"Your region is not allow to signup by phone": "Vaše oblast neumožňuje registraci pomocí telefonu",
"password or code is incorrect": "heslo nebo kód je nesprávný",
"password or code is incorrect, you have %s remaining chances": "heslo nebo kód je nesprávné, máte %s zbývajících pokusů",
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
},
"enforcer": {
"the adapter: %s is not found": "adaptér: %s nebyl nalezen"
},
"general": {
"Failed to import groups": "Nepodařilo se importovat skupiny",
"Failed to import users": "Nepodařilo se importovat uživatele",
"Missing parameter": "Chybějící parametr",
"Only admin user can specify user": "Pouze administrátor může určit uživatele",
"Please login first": "Prosím, přihlaste se nejprve",
"The organization: %s should have one application at least": "Organizace: %s by měla mít alespoň jednu aplikaci",
"The user: %s doesn't exist": "Uživatel: %s neexistuje",
"Wrong userId": "Nesprávné uživatelské ID",
"don't support captchaProvider: ": "nepodporuje captchaProvider: ",
"this operation is not allowed in demo mode": "tato operace není povolena v demo režimu",
"this operation requires administrator to perform": "tato operace vyžaduje administrátora k provedení"
},
"invitation": {
"Invitation %s does not exist": "Pozvánka %s neexistuje"
},
"ldap": {
"Ldap server exist": "Ldap server existuje"
},
"link": {
"Please link first": "Prosím, nejprve propojte",
"This application has no providers": "Tato aplikace nemá žádné poskytovatele",
"This application has no providers of type": "Tato aplikace nemá žádné poskytovatele typu",
"This provider can't be unlinked": "Tento poskytovatel nemůže být odpojen",
"You are not the global admin, you can't unlink other users": "Nejste globální administrátor, nemůžete odpojovat jiné uživatele",
"You can't unlink yourself, you are not a member of any application": "Nemůžete odpojit sami sebe, nejste členem žádné aplikace"
},
"organization": {
"Only admin can modify the %s.": "Pouze administrátor může upravit %s.",
"The %s is immutable.": "%s je neměnný.",
"Unknown modify rule %s.": "Neznámé pravidlo úpravy %s.",
"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.": "Přidání nového uživatele do 'vestavěné' organizace je momentálně zakázáno. Poznámka: všichni uživatelé v 'vestavěné' organizaci jsou globálními správci v Casdooru. Viz docs: https://casdoor.org/docs/basic/core-concepts#how -dělá-casdoor-spravovat-sám. Pokud stále chcete vytvořit uživatele pro 'vestavěnou' organizaci, přejděte na stránku nastavení organizace a aktivujte možnost 'Má souhlas s oprávněními'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Oprávnění: \\\"%s\\\" neexistuje"
},
"provider": {
"Invalid application id": "Neplatné ID aplikace",
"the provider: %s does not exist": "poskytovatel: %s neexistuje"
},
"resource": {
"User is nil for tag: avatar": "Uživatel je nil pro tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Uživatelské jméno nebo úplná cesta k souboru je prázdná: uživatelské jméno = %s, úplná cesta k souboru = %s"
},
"saml": {
"Application %s not found": "Aplikace %s nebyla nalezena"
},
"saml_sp": {
"provider %s's category is not SAML": "poskytovatel %s není kategorie SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Prázdné parametry pro emailForm: %v",
"Invalid Email receivers: %s": "Neplatní příjemci emailu: %s",
"Invalid phone receivers: %s": "Neplatní příjemci telefonu: %s"
},
"storage": {
"The objectKey: %s is not allowed": "objectKey: %s není povolen",
"The provider type: %s is not supported": "typ poskytovatele: %s není podporován"
},
"subscription": {
"Error": "Chyba"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s není v této aplikaci podporován",
"Invalid application or wrong clientSecret": "Neplatná aplikace nebo špatný clientSecret",
"Invalid client_id": "Neplatné client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Přesměrovací URI: %s neexistuje v seznamu povolených přesměrovacích URI",
"Token not found, invalid accessToken": "Token nenalezen, neplatný accessToken"
},
"user": {
"Display name cannot be empty": "Zobrazované jméno nemůže být prázdné",
"MFA email is enabled but email is empty": "MFA e-mail je povolen, ale e-mail je prázdný",
"MFA phone is enabled but phone number is empty": "MFA telefon je povolen, ale telefonní číslo je prázdné",
"New password cannot contain blank space.": "Nové heslo nemůže obsahovat prázdné místo.",
"The new password must be different from your current password": "Nové heslo musí být odlišné od vašeho současného hesla",
"the user's owner and name should not be empty": "vlastník a jméno uživatele by neměly být prázdné"
},
"util": {
"No application is found for userId: %s": "Pro userId: %s nebyla nalezena žádná aplikace",
"No provider for category: %s is found for application: %s": "Pro kategorii: %s nebyl nalezen žádný poskytovatel pro aplikaci: %s",
"The provider: %s is not found": "Poskytovatel: %s nebyl nalezen"
},
"verification": {
"Invalid captcha provider.": "Neplatný poskytovatel captcha.",
"Phone number is invalid in your region %s": "Telefonní číslo je ve vaší oblasti %s neplatné",
"The verification code has already been used!": "Ověřovací kód již byl použit!",
"The verification code has not been sent yet!": "Ověřovací kód ještě nebyl odeslán!",
"Turing test failed.": "Turingův test selhal.",
"Unable to get the email modify rule.": "Nelze získat pravidlo pro úpravu emailu.",
"Unable to get the phone modify rule.": "Nelze získat pravidlo pro úpravu telefonu.",
"Unknown type": "Neznámý typ",
"Wrong verification code!": "Špatný ověřovací kód!",
"You should verify your code in %d min!": "Měli byste ověřit svůj kód do %d minut!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "prosím přidejte SMS poskytovatele do seznamu \\\"Providers\\\" pro aplikaci: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "prosím přidejte e-mailového poskytovatele do seznamu \\\"Providers\\\" pro aplikaci: %s",
"the user does not exist, please sign up first": "uživatel neexistuje, prosím nejprve se zaregistrujte"
},
"webauthn": {
"Found no credentials for this user": "Pro tohoto uživatele nebyly nalezeny žádné přihlašovací údaje",
"Please call WebAuthnSigninBegin first": "Prosím, nejprve zavolejte WebAuthnSigninBegin"
}
}

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "Dem Benutzer ist der Zugang verboten, bitte kontaktieren Sie den Administrator",
"The user: %s doesn't exist in LDAP server": "Der Benutzer: %s existiert nicht im LDAP-Server",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Der Wert \\\"%s\\\" für das Kontenfeld \\\"%s\\\" stimmt nicht mit dem Kontenelement-Regex überein",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Der Wert \\\"%s\\\" für das Registrierungsfeld \\\"%s\\\" stimmt nicht mit dem Registrierungselement-Regex der Anwendung \\\"%s\\\" überein",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Der Wert \"%s\" für das Kontenfeld \"%s\" stimmt nicht mit dem Kontenelement-Regex überein",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Der Wert \"%s\" für das Registrierungsfeld \"%s\" stimmt nicht mit dem Registrierungselement-Regex der Anwendung \"%s\" überein",
"Username already exists": "Benutzername existiert bereits",
"Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
"Username cannot contain white spaces": "Benutzername darf keine Leerzeichen enthalten",
@@ -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.": "Benutzername unterstützt E-Mail-Format. Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden. Achten Sie auch auf das E-Mail-Format.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Sie haben zu oft das falsche Passwort oder den falschen Code eingegeben. Bitte warten Sie %d Minuten und versuchen Sie es erneut",
"Your IP address: %s has been banned according to the configuration of: ": "Ihre IP-Adresse: %s wurde laut Konfiguration gesperrt von: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Ihr Passwort ist abgelaufen. Bitte setzen Sie Ihr Passwort zurück, indem Sie auf \\\"Passwort vergessen\\\" klicken",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Ihr Passwort ist abgelaufen. Bitte setzen Sie Ihr Passwort zurück, indem Sie auf \"Passwort vergessen\" klicken",
"Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden",
"password or code is incorrect": "Passwort oder Code ist falsch",
"password or code is incorrect, you have %s remaining chances": "Das Passwort oder der Code ist falsch. Du hast noch %s Versuche übrig",
@@ -137,7 +137,7 @@
"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.": "Das Hinzufügen eines neuen Benutzers zur 'eingebauten' Organisation ist derzeit deaktiviert. Bitte beachten Sie: Alle Benutzer in der 'eingebauten' Organisation sind globale Administratoren in Casdoor. Siehe die Docs: https://casdoor.org/docs/basic/core-concepts#how -does-casdoor-manage-sich selbst. Wenn Sie immer noch einen Benutzer für die 'eingebaute' Organisation erstellen möchten, gehen Sie auf die Einstellungsseite der Organisation und aktivieren Sie die Option 'Habt Berechtigungszustimmung'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Die Berechtigung: \\\"%s\\\" existiert nicht"
"The permission: \"%s\" doesn't exist": "Die Berechtigung: \"%s\" existiert nicht"
},
"provider": {
"Invalid application id": "Ungültige Anwendungs-ID",
@@ -196,8 +196,8 @@
"Unknown type": "Unbekannter Typ",
"Wrong verification code!": "Falscher Bestätigungscode!",
"You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Bitte fügen Sie einen SMS-Anbieter zur \\\"Providers\\\"-Liste für die Anwendung hinzu: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Bitte fügen Sie einen E-Mail-Anbieter zur \\\"Providers\\\"-Liste für die Anwendung hinzu: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "Bitte fügen Sie einen SMS-Anbieter zur \"Providers\"-Liste für die Anwendung hinzu: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "Bitte fügen Sie einen E-Mail-Anbieter zur \"Providers\"-Liste für die Anwendung hinzu: %s",
"the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
},
"webauthn": {

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "The value \"%s\" for account field \"%s\" doesn't match the account item regex",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
@@ -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.": "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.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your IP address: %s has been banned according to the configuration of: ": "Your IP address: %s has been banned according to the configuration of: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Your password has expired. Please reset your password by clicking \"Forgot password\"",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect": "password or code is incorrect",
"password or code is incorrect, you have %s remaining chances": "password or code is incorrect, you have %s remaining chances",
@@ -137,7 +137,7 @@
"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.": "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."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
"The permission: \"%s\" doesn't exist": "The permission: \"%s\" doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
@@ -188,6 +188,7 @@
"verification": {
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"The forgot password feature is disabled": "The forgot password feature is disabled",
"The verification code has already been used!": "The verification code has already been used!",
"The verification code has not been sent yet!": "The verification code has not been sent yet!",
"Turing test failed.": "Turing test failed.",
@@ -196,8 +197,8 @@
"Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "please add a SMS provider to the \"Providers\" list for the application: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "please add an Email provider to the \"Providers\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "El usuario no está autorizado a iniciar sesión, por favor contacte al administrador",
"The user: %s doesn't exist in LDAP server": "El usuario: %s no existe en el servidor 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.": "El nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones o subrayados consecutivos, y no puede comenzar ni terminar con un guión o subrayado.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "El valor \\\"%s\\\" para el campo de cuenta \\\"%s\\\" no coincide con la expresión regular del elemento de cuenta",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "El valor \\\"%s\\\" para el campo de registro \\\"%s\\\" no coincide con la expresión regular del elemento de registro de la aplicación \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "El valor \"%s\" para el campo de cuenta \"%s\" no coincide con la expresión regular del elemento de cuenta",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "El valor \"%s\" para el campo de registro \"%s\" no coincide con la expresión regular del elemento de registro de la aplicación \"%s\"",
"Username already exists": "El nombre de usuario ya existe",
"Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
"Username cannot contain white spaces": "Nombre de usuario no puede contener espacios en blanco",
@@ -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.": "El nombre de usuario admite formato de correo electrónico. Además, el nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones bajos o guiones consecutivos y no puede comenzar ni terminar con un guión o guión bajo. También preste atención al formato del correo electrónico.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Has ingresado la contraseña o código incorrecto demasiadas veces, por favor espera %d minutos e intenta de nuevo",
"Your IP address: %s has been banned according to the configuration of: ": "Su dirección IP: %s ha sido bloqueada según la configuración de: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Su contraseña ha expirado. Restablezca su contraseña haciendo clic en \\\"Olvidé mi contraseña\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Su contraseña ha expirado. Restablezca su contraseña haciendo clic en \"Olvidé mi contraseña\"",
"Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono",
"password or code is incorrect": "contraseña o código incorrecto",
"password or code is incorrect, you have %s remaining chances": "Contraseña o código incorrecto, tienes %s intentos restantes",
@@ -137,7 +137,7 @@
"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.": "La adición de un nuevo usuario a la organización 'integrada' está actualmente deshabilitada. Tenga en cuenta: todos los usuarios de la organización 'integrada' son administradores globales en Casdoor. Consulte los docs: https://casdoor.org/docs/basic/core-concepts#how -does-casdoor-manage-itself. Si todavía desea crear un usuario para la organización 'integrada', vaya a la página de configuración de la organización y habilite la opción 'Tiene consentimiento de privilegios'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "El permiso: \\\"%s\\\" no existe"
"The permission: \"%s\" doesn't exist": "El permiso: \"%s\" no existe"
},
"provider": {
"Invalid application id": "Identificación de aplicación no válida",
@@ -196,8 +196,8 @@
"Unknown type": "Tipo desconocido",
"Wrong verification code!": "¡Código de verificación incorrecto!",
"You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "agregue un proveedor de SMS a la lista \\\"Proveedores\\\" para la aplicación: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "agregue un proveedor de correo electrónico a la lista \\\"Proveedores\\\" para la aplicación: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "agregue un proveedor de SMS a la lista \"Proveedores\" para la aplicación: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "agregue un proveedor de correo electrónico a la lista \"Proveedores\" para la aplicación: %s",
"the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
},
"webauthn": {

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "عدم موفقیت در افزودن کاربر",
"Get init score failed, error: %w": "عدم موفقیت در دریافت امتیاز اولیه، خطا: %w",
"Please sign out first": "لطفاً ابتدا خارج شوید",
"The application does not allow to sign up new account": "برنامه اجازه ثبت‌نام حساب جدید را نمی‌دهد"
},
"auth": {
"Challenge method should be S256": "روش چالش باید S256 باشد",
"DeviceCode Invalid": "کد دستگاه نامعتبر است",
"Failed to create user, user information is invalid: %s": "عدم موفقیت در ایجاد کاربر، اطلاعات کاربر نامعتبر است: %s",
"Failed to login in: %s": "عدم موفقیت در ورود: %s",
"Invalid token": "توکن نامعتبر",
"State expected: %s, but got: %s": "وضعیت مورد انتظار: %s، اما دریافت شد: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبت‌نام به‌عنوان حساب جدید از طریق %s نیست، لطفاً از روش دیگری برای ثبت‌نام استفاده کنید",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبت‌نام به‌عنوان حساب جدید نیست، لطفاً با پشتیبانی IT خود تماس بگیرید",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) در حال حاضر به حساب دیگری مرتبط است: %s (%s)",
"The application: %s does not exist": "برنامه: %s وجود ندارد",
"The application: %s has disabled users to signin": "برنامه: %s ورود کاربران را غیرفعال کرده است",
"The group: %s does not exist": "گروه: %s وجود ندارد",
"The login method: login with LDAP is not enabled for the application": "روش ورود: ورود با LDAP برای برنامه فعال نیست",
"The login method: login with SMS is not enabled for the application": "روش ورود: ورود با پیامک برای برنامه فعال نیست",
"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 organization: %s does not exist": "سازمان: %s وجود ندارد",
"The organization: %s has disabled users to signin": "سازمان: %s ورود کاربران را غیرفعال کرده است",
"The plan: %s does not exist": "طرح: %s وجود ندارد",
"The pricing: %s does not exist": "قیمت‌گذاری: %s وجود ندارد",
"The pricing: %s does not have plan: %s": "قیمت‌گذاری: %s طرح ندارد: %s",
"The provider: %s does not exist": "ارائه‌کننده: %s وجود ندارد",
"The provider: %s is not enabled for the application": "ارائه‌دهنده: %s برای برنامه فعال نیست",
"Unauthorized operation": "عملیات غیرمجاز",
"Unknown authentication type (not password or provider), form = %s": "نوع احراز هویت ناشناخته (نه رمز عبور و نه ارائه‌دهنده)، فرم = %s",
"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 قیمت‌گذاری پیش‌فرض ندارد",
"the application for user %s is not found": " برنامه برای کاربر %s پیدا نشد",
"the organization: %s is not found": "سازمان: %s پیدا نشد"
},
"cas": {
"Service %s and %s do not match": "سرویس %s و %s مطابقت ندارند"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s با نیازهای فرمت CIDR مطابقت ندارد: %s",
"Affiliation cannot be blank": "وابستگی نمی‌تواند خالی باشد",
"CIDR for IP: %s should not be empty": "CIDR برای IP: %s نباید خالی باشد",
"Default code does not match the code's matching rules": "کد پیش‌فرض با قوانین تطبیق کد مطابقت ندارد",
"DisplayName cannot be blank": "نام نمایشی نمی‌تواند خالی باشد",
"DisplayName is not valid real name": "نام نمایشی یک نام واقعی معتبر نیست",
"Email already exists": "ایمیل قبلاً وجود دارد",
"Email cannot be empty": "ایمیل نمی‌تواند خالی باشد",
"Email is invalid": "ایمیل نامعتبر است",
"Empty username.": "نام کاربری خالی است.",
"Face data does not exist, cannot log in": "داده‌های چهره وجود ندارد، نمی‌توان وارد شد",
"Face data mismatch": "عدم تطابق داده‌های چهره",
"Failed to parse client IP: %s": "پارس کردن IP مشتری ناموفق بود: %s",
"FirstName cannot be blank": "نام نمی‌تواند خالی باشد",
"Invitation code cannot be blank": "کد دعوت نمی‌تواند خالی باشد",
"Invitation code exhausted": "کد دعوت استفاده شده است",
"Invitation code is invalid": "کد دعوت نامعتبر است",
"Invitation code suspended": "کد دعوت معلق است",
"LDAP user name or password incorrect": "نام کاربری یا رمز عبور LDAP نادرست است",
"LastName cannot be blank": "نام خانوادگی نمی‌تواند خالی باشد",
"Multiple accounts with same uid, please check your ldap server": "چندین حساب با uid یکسان، لطفاً سرور LDAP خود را بررسی کنید",
"Organization does not exist": "سازمان وجود ندارد",
"Password cannot be empty": "رمز عبور نمی‌تواند خالی باشد",
"Phone already exists": "تلفن قبلاً وجود دارد",
"Phone cannot be empty": "تلفن نمی‌تواند خالی باشد",
"Phone number is invalid": "شماره تلفن نامعتبر است",
"Please register using the email corresponding to the invitation code": "لطفاً با استفاده از ایمیل مربوط به کد دعوت ثبت‌نام کنید",
"Please register using the phone corresponding to the invitation code": "لطفاً با استفاده از تلفن مربوط به کد دعوت ثبت‌نام کنید",
"Please register using the username corresponding to the invitation code": "لطفاً با استفاده از نام کاربری مربوط به کد دعوت ثبت‌نام کنید",
"Session outdated, please login again": "جلسه منقضی شده است، لطفاً دوباره وارد شوید",
"The invitation code has already been used": "کد دعوت قبلاً استفاده شده است",
"The password must contain at least one special character": "رمز عبور باید حداقل یک کاراکتر خاص داشته باشد",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "رمز عبور باید حداقل یک حرف بزرگ، یک حرف کوچک و یک رقم داشته باشد",
"The password must have at least 6 characters": "رمز عبور باید حداقل 6 کاراکتر داشته باشد",
"The password must have at least 8 characters": "رمز عبور باید حداقل 8 کاراکتر داشته باشد",
"The password must not contain any repeated characters": "رمز عبور نباید شامل کاراکترهای تکراری باشد",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "کاربر حذف شده است و نمی توان از آن برای ورود استفاده کرد، لطفا با مدیر تماس بگیرید",
"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\\\" با regex مورد نظر مطابقت ندارد",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "مقدار \\\"%s\\\" برای فیلد ثبت‌نام \\\"%s\\\" با regex مورد نظر برنامه \\\"%s\\\" مطابقت ندارد",
"Username already exists": "نام کاربری قبلاً وجود دارد",
"Username cannot be an email address": "نام کاربری نمی‌تواند یک آدرس ایمیل باشد",
"Username cannot contain white spaces": "نام کاربری نمی‌تواند حاوی فاصله باشد",
"Username cannot start with a digit": "نام کاربری نمی‌تواند با یک رقم شروع شود",
"Username is too long (maximum is 255 characters).": "نام کاربری بیش از حد طولانی است (حداکثر ۳۹ کاراکتر).",
"Username must have at least 2 characters": "نام کاربری باید حداقل ۲ کاراکتر داشته باشد",
"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.": "نام کاربری از فرمت ایمیل پشتیبانی می‌کند. همچنین نام کاربری تنها می‌تواند شامل کاراکترهای الفبایی-عددی، زیرخط یا خط تیره باشد، نمی‌تواند دارای خطوط تیره یا زیرخطوط متوالی باشد و نمی‌تواند با خط تیره یا زیرخط شروع یا پایان یابد. همچنین به فرمت ایمیل توجه کنید.",
"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 region is not allow to signup by phone": "منطقه شما اجازه ثبت‌نام با تلفن را ندارد",
"password or code is incorrect": "رمز عبور یا کد نادرست است",
"password or code is incorrect, you have %s remaining chances": "رمز عبور یا کد نادرست است، شما %s فرصت باقی‌مانده دارید",
"unsupported password type: %s": "نوع رمز عبور پشتیبانی نشده: %s"
},
"enforcer": {
"the adapter: %s is not found": "آداپتر: %s پیدا نشد"
},
"general": {
"Failed to import groups": "ورود گروه‌ها ناموفق بود",
"Failed to import users": "عدم موفقیت در وارد کردن کاربران",
"Missing parameter": "پارامتر گمشده",
"Only admin user can specify user": "فقط کاربر مدیر می‌تواند کاربر را مشخص کند",
"Please login first": "لطفاً ابتدا وارد شوید",
"The organization: %s should have one application at least": "سازمان: %s باید حداقل یک برنامه داشته باشد",
"The user: %s doesn't exist": "کاربر: %s وجود ندارد",
"Wrong userId": "شناسه کاربر اشتباه است",
"don't support captchaProvider: ": "از captchaProvider پشتیبانی نمی‌شود: ",
"this operation is not allowed in demo mode": "این عملیات در حالت دمو مجاز نیست",
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد"
},
"invitation": {
"Invitation %s does not exist": "دعوت‌نامه %s وجود ندارد"
},
"ldap": {
"Ldap server exist": "سرور LDAP وجود دارد"
},
"link": {
"Please link first": "لطفاً ابتدا پیوند دهید",
"This application has no providers": "این برنامه ارائه‌دهنده‌ای ندارد",
"This application has no providers of type": "این برنامه ارائه‌دهنده‌ای از نوع ندارد",
"This provider can't be unlinked": "این ارائه‌دهنده نمی‌تواند لغو پیوند شود",
"You are not the global admin, you can't unlink other users": "شما مدیر جهانی نیستید، نمی‌توانید کاربران دیگر را لغو پیوند کنید",
"You can't unlink yourself, you are not a member of any application": "شما نمی‌توانید خودتان را لغو پیوند کنید، شما عضو هیچ برنامه‌ای نیستید"
},
"organization": {
"Only admin can modify the %s.": "فقط مدیر می‌تواند %s را تغییر دهد.",
"The %s is immutable.": "%s غیرقابل تغییر است.",
"Unknown modify rule %s.": "قانون تغییر ناشناخته %s.",
"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\\\" وجود ندارد"
},
"provider": {
"Invalid application id": "شناسه برنامه نامعتبر",
"the provider: %s does not exist": "ارائه‌دهنده: %s وجود ندارد"
},
"resource": {
"User is nil for tag: avatar": "کاربر برای برچسب: آواتار تهی است",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "نام کاربری یا مسیر کامل فایل خالی است: نام کاربری = %s، مسیر کامل فایل = %s"
},
"saml": {
"Application %s not found": "برنامه %s یافت نشد"
},
"saml_sp": {
"provider %s's category is not SAML": "دسته‌بندی ارائه‌دهنده %s SAML نیست"
},
"service": {
"Empty parameters for emailForm: %v": "پارامترهای خالی برای emailForm: %v",
"Invalid Email receivers: %s": "گیرندگان ایمیل نامعتبر: %s",
"Invalid phone receivers: %s": "گیرندگان تلفن نامعتبر: %s"
},
"storage": {
"The objectKey: %s is not allowed": "objectKey: %s مجاز نیست",
"The provider type: %s is not supported": "نوع ارائه‌دهنده: %s پشتیبانی نمی‌شود"
},
"subscription": {
"Error": "خطا"
},
"token": {
"Grant_type: %s is not supported in this application": "grant_type: %s در این برنامه پشتیبانی نمی‌شود",
"Invalid application or wrong clientSecret": "برنامه نامعتبر یا clientSecret نادرست",
"Invalid client_id": "client_id نامعتبر",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "آدرس بازگشت: %s در لیست آدرس‌های بازگشت مجاز وجود ندارد",
"Token not found, invalid accessToken": "توکن یافت نشد، accessToken نامعتبر"
},
"user": {
"Display name cannot be empty": "نام نمایشی نمی‌تواند خالی باشد",
"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.": "رمز عبور جدید نمی‌تواند حاوی فاصله خالی باشد.",
"The new password must be different from your current password": "رمز عبور جدید باید با رمز عبور فعلی شما متفاوت باشد",
"the user's owner and name should not be empty": "مالک و نام کاربر نباید خالی باشند"
},
"util": {
"No application is found for userId: %s": "هیچ برنامه‌ای برای userId: %s یافت نشد",
"No provider for category: %s is found for application: %s": "هیچ ارائه‌دهنده‌ای برای دسته‌بندی: %s برای برنامه: %s یافت نشد",
"The provider: %s is not found": "ارائه‌دهنده: %s یافت نشد"
},
"verification": {
"Invalid captcha provider.": "ارائه‌دهنده کپچا نامعتبر.",
"Phone number is invalid in your region %s": "شماره تلفن در منطقه شما نامعتبر است %s",
"The verification code has already been used!": "کد تایید قبلاً استفاده شده است!",
"The verification code has not been sent yet!": "کد تأیید هنوز ارسال نشده است!",
"Turing test failed.": "تست تورینگ ناموفق بود.",
"Unable to get the email modify rule.": "عدم توانایی در دریافت قانون تغییر ایمیل.",
"Unable to get the phone modify rule.": "عدم توانایی در دریافت قانون تغییر تلفن.",
"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": "لطفاً یک ارائه‌کننده ایمیل به لیست \\\"ارائه‌کنندگان\\\" برای برنامه اضافه کنید: %s",
"the user does not exist, please sign up first": "کاربر وجود ندارد، لطفاً ابتدا ثبت‌نام کنید"
},
"webauthn": {
"Found no credentials for this user": "اطلاعات ورود برای این کاربر یافت نشد",
"Please call WebAuthnSigninBegin first": "لطفاً ابتدا WebAuthnSigninBegin را فراخوانی کنید"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Käyttäjän lisäys epäonnistui",
"Get init score failed, error: %w": "Alkupisteen haku epäonnistui, virhe: %w",
"Please sign out first": "Kirjaudu ensin ulos",
"The application does not allow to sign up new account": "Sovellus ei salli uuden tilin luomista"
},
"auth": {
"Challenge method should be S256": "Haasteen metodin tulee olla S256",
"DeviceCode Invalid": "DeviceCode virheellinen",
"Failed to create user, user information is invalid: %s": "Käyttäjän luonti epäonnistui, käyttäjätiedot ovat virheelliset: %s",
"Failed to login in: %s": "Sisäänkirjautuminen epäonnistui: %s",
"Invalid token": "Virheellinen token",
"State expected: %s, but got: %s": "Odotettiin tilaa: %s, mutta saatiin: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä kautta %s, käytä toista tapaa rekisteröityä",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä, ota yhteyttä IT-tukeen",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tili providerille: %s ja käyttäjälle: %s (%s) on jo linkitetty toiseen tiliin: %s (%s)",
"The application: %s does not exist": "Sovellus: %s ei ole olemassa",
"The application: %s has disabled users to signin": "Sovellus: %s on estänyt käyttäjien kirjautumisen",
"The group: %s does not exist": "Ryhmä: %s ei ole olemassa",
"The login method: login with LDAP is not enabled for the application": "Kirjautumistapa: kirjautuminen LDAP:n kautta ei ole käytössä sovelluksessa",
"The login method: login with SMS is not enabled for the application": "Kirjautumistapa: kirjautuminen SMS:n kautta ei ole käytössä sovelluksessa",
"The login method: login with email is not enabled for the application": "Kirjautumistapa: kirjautuminen sähköpostin kautta ei ole käytössä sovelluksessa",
"The login method: login with face is not enabled for the application": "Kirjautumistapa: kirjautuminen kasvotunnistuksella ei ole käytössä sovelluksessa",
"The login method: login with password is not enabled for the application": "Kirjautumistapa: kirjautuminen salasanalla ei ole käytössä sovelluksessa",
"The organization: %s does not exist": "Organisaatio: %s ei ole olemassa",
"The organization: %s has disabled users to signin": "Organisaatio: %s on estänyt käyttäjien kirjautumisen",
"The plan: %s does not exist": "Suunnitelma: %s ei ole olemassa",
"The pricing: %s does not exist": "Hinnoittelu: %s ei ole olemassa",
"The pricing: %s does not have plan: %s": "Hinnoittelu: %s ei sisällä suunnitelmaa: %s",
"The provider: %s does not exist": "Provider: %s ei ole olemassa",
"The provider: %s is not enabled for the application": "Provider: %s ei ole käytössä sovelluksessa",
"Unauthorized operation": "Luvaton toiminto",
"Unknown authentication type (not password or provider), form = %s": "Tuntematon todennustyyppi (ei salasana tai provider), form = %s",
"User's tag: %s is not listed in the application's tags": "Käyttäjän tagi: %s ei ole listattu sovelluksen tageissa",
"UserCode Expired": "UserCode vanhentunut",
"UserCode Invalid": "UserCode virheellinen",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "maksettu-käyttäjä %s ei ole aktiivista tai odottavaa tilausta ja sovellus: %s ei ole oletushinnoittelua",
"the application for user %s is not found": "sovellusta käyttäjälle %s ei löydy",
"the organization: %s is not found": "organisaatiota: %s ei löydy"
},
"cas": {
"Service %s and %s do not match": "Palvelu %s ja %s eivät täsmää"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s ei täytä CIDR-muodon vaatimuksia: %s",
"Affiliation cannot be blank": "Affiliaatio ei voi olla tyhjä",
"CIDR for IP: %s should not be empty": "CIDR IP:lle: %s ei saa olla tyhjä",
"Default code does not match the code's matching rules": "Oletuskoodi ei täsmää koodin täsmäyssääntöihin",
"DisplayName cannot be blank": "Näyttönimi ei voi olla tyhjä",
"DisplayName is not valid real name": "Näyttönimi ei ole kelvollinen oikea nimi",
"Email already exists": "Sähköposti on jo olemassa",
"Email cannot be empty": "Sähköposti ei voi olla tyhjä",
"Email is invalid": "Sähköposti on virheellinen",
"Empty username.": "Tyhjä käyttäjänimi.",
"Face data does not exist, cannot log in": "Kasvodataa ei ole olemassa, ei voi kirjautua",
"Face data mismatch": "Kasvodata ei täsmää",
"Failed to parse client IP: %s": "Client IP:n jäsentäminen epäonnistui: %s",
"FirstName cannot be blank": "Etunimi ei voi olla tyhjä",
"Invitation code cannot be blank": "Kutsukoodi ei voi olla tyhjä",
"Invitation code exhausted": "Kutsukoodi on käytetty loppuun",
"Invitation code is invalid": "Kutsukoodi on virheellinen",
"Invitation code suspended": "Kutsukoodi on keskeytetty",
"LDAP user name or password incorrect": "LDAP-käyttäjänimi tai salasana on virheellinen",
"LastName cannot be blank": "Sukunimi ei voi olla tyhjä",
"Multiple accounts with same uid, please check your ldap server": "Useita tilejä samalla uid:llä, tarkista ldap-palvelimesi",
"Organization does not exist": "Organisaatiota ei ole olemassa",
"Password cannot be empty": "Salasana ei voi olla tyhjä",
"Phone already exists": "Puhelinnumero on jo olemassa",
"Phone cannot be empty": "Puhelinnumero ei voi olla tyhjä",
"Phone number is invalid": "Puhelinnumero on virheellinen",
"Please register using the email corresponding to the invitation code": "Rekisteröidy käyttämällä kutsukoodiin vastaavaa sähköpostia",
"Please register using the phone corresponding to the invitation code": "Rekisteröidy käyttämällä kutsukoodiin vastaavaa puhelinnumeroa",
"Please register using the username corresponding to the invitation code": "Rekisteröidy käyttämällä kutsukoodiin vastaavaa käyttäjänimeä",
"Session outdated, please login again": "Istunto vanhentunut, kirjaudu uudelleen",
"The invitation code has already been used": "Kutsukoodi on jo käytetty",
"The password must contain at least one special character": "Salasanan on sisällettävä vähintään yksi erikoismerkki",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Salasanan on sisällettävä vähintään yksi iso kirjain, yksi pieni kirjain ja yksi numero",
"The password must have at least 6 characters": "Salasanassa on oltava vähintään 6 merkkiä",
"The password must have at least 8 characters": "Salasanassa on oltava vähintään 8 merkkiä",
"The password must not contain any repeated characters": "Salasana ei saa sisältää toistuvia merkkejä",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Käyttäjä on poistettu eikä sitä voi käyttää kirjautumiseen, ota yhteyttä järjestelmänvalvojaan",
"The user is forbidden to sign in, please contact the administrator": "Käyttäjän kirjautuminen on estetty, ota yhteyttä ylläpitäjään",
"The user: %s doesn't exist in LDAP server": "Käyttäjä: %s ei ole olemassa LDAP-palvelimessa",
"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.": "Käyttäjänimi voi sisältää vain alfanumeerisia merkkejä, alaviivoja tai tavuviivoja, ei voi sisältää peräkkäisiä tavuviivoja tai alaviivoja, eikä voi alkaa tai loppua tavuviivalla tai alaviivalla.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Arvo \\\"%s\\\" tili-kentälle \\\"%s\\\" ei täsmää tili-alkion regexiin",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Arvo \\\"%s\\\" rekisteröitymiskentälle \\\"%s\\\" ei täsmää sovelluksen \\\"%s\\\" rekisteröitymiskentän regexiin",
"Username already exists": "Käyttäjänimi on jo olemassa",
"Username cannot be an email address": "Käyttäjänimi ei voi olla sähköpostiosoite",
"Username cannot contain white spaces": "Käyttäjänimi ei voi sisältää välilyöntejä",
"Username cannot start with a digit": "Käyttäjänimi ei voi alkaa numerolla",
"Username is too long (maximum is 255 characters).": "Käyttäjänimi on liian pitkä (enintään 255 merkkiä).",
"Username must have at least 2 characters": "Käyttäjänimessä on oltava vähintään 2 merkkiä",
"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.": "Käyttäjänimi tukee sähköpostimuotoa. Lisäksi käyttäjänimi voi sisältää vain alfanumeerisia merkkejä, alaviivoja tai tavuviivoja, ei voi sisältää peräkkäisiä tavuviivoja tai alaviivoja, eikä voi alkaa tai loppua tavuviivalla tai alaviivalla. Huomioi myös sähköpostimuoto.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Olet syöttänyt väärän salasanan tai koodin liian monta kertaa, odota %d minuuttia ja yritä uudelleen",
"Your IP address: %s has been banned according to the configuration of: ": "IP-osoitteesi: %s on estetty asetuksen mukaan: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Salasanasi on vanhentunut. Nollaa salasanasi klikkaamalla \\\"Unohdin salasanan\\\"",
"Your region is not allow to signup by phone": "Alueellasi ei ole sallittua rekisteröityä puhelimella",
"password or code is incorrect": "salasana tai koodi on virheellinen",
"password or code is incorrect, you have %s remaining chances": "salasana tai koodi on virheellinen, sinulla on %s yritystä jäljellä",
"unsupported password type: %s": "ei-tuettu salasanatyyppi: %s"
},
"enforcer": {
"the adapter: %s is not found": "adapteria: %s ei löydy"
},
"general": {
"Failed to import groups": "Ryhmien tuonti epäonnistui",
"Failed to import users": "Käyttäjien tuonti epäonnistui",
"Missing parameter": "Parametri puuttuu",
"Only admin user can specify user": "Vain ylläpitäjä voi määrittää käyttäjän",
"Please login first": "Kirjaudu ensin sisään",
"The organization: %s should have one application at least": "Organisaatiolla: %s on oltava vähintään yksi sovellus",
"The user: %s doesn't exist": "Käyttäjää: %s ei ole olemassa",
"Wrong userId": "Väärä userId",
"don't support captchaProvider: ": "ei tueta captchaProvider: ",
"this operation is not allowed in demo mode": "tämä toiminto ei ole sallittu demo-tilassa",
"this operation requires administrator to perform": "tämä toiminto vaatii ylläpitäjän suorittamista"
},
"invitation": {
"Invitation %s does not exist": "Kutsua %s ei ole olemassa"
},
"ldap": {
"Ldap server exist": "LDAP-palvelin on olemassa"
},
"link": {
"Please link first": "Linkitä ensin",
"This application has no providers": "Tällä sovelluksella ei ole providereita",
"This application has no providers of type": "Tällä sovelluksella ei ole tämän tyyppisiä providereita",
"This provider can't be unlinked": "Tätä provideria ei voi irrottaa",
"You are not the global admin, you can't unlink other users": "Et ole globaali ylläpitäjä, et voi irrottaa muita käyttäjiä",
"You can't unlink yourself, you are not a member of any application": "Et voi irrottaa itseäsi, et ole minkään sovelluksen jäsen"
},
"organization": {
"Only admin can modify the %s.": "Vain ylläpitäjä voi muokata %s.",
"The %s is immutable.": "%s on muuttumaton.",
"Unknown modify rule %s.": "Tuntematon muokkaussääntö %s.",
"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.": "Uuden käyttäjän lisääminen 'built-in'-organisaatioon on tällä hetkellä poistettu käytöstä. Huomioi: Kaikki 'built-in'-organisaation käyttäjät ovat Casdoorin globaaleja ylläpitäjiä. Katso dokumentaatio: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Jos haluat silti luoda käyttäjän 'built-in'-organisaatiolle, siirry organisaation asetussivulle ja käytä käyttöön vaihtoehdon 'On oikeuksien suostumus'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Oikeutta: \\\"%s\\\" ei ole olemassa"
},
"provider": {
"Invalid application id": "Virheellinen sovelluksen id",
"the provider: %s does not exist": "provideria: %s ei ole olemassa"
},
"resource": {
"User is nil for tag: avatar": "Käyttäjä on nil tagille: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Käyttäjänimi tai fullFilePath on tyhjä: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Sovellusta %s ei löydy"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s:n kategoria ei ole SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Tyhjät parametrit emailFormille: %v",
"Invalid Email receivers: %s": "Virheelliset sähköpostivastaanottajat: %s",
"Invalid phone receivers: %s": "Virheelliset puhelinvastaanottajat: %s"
},
"storage": {
"The objectKey: %s is not allowed": "objectKey: %s ei ole sallittu",
"The provider type: %s is not supported": "Provider-tyyppiä: %s ei tueta"
},
"subscription": {
"Error": "Virhe"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s ei ole tuettu tässä sovelluksessa",
"Invalid application or wrong clientSecret": "Virheellinen sovellus tai väärä clientSecret",
"Invalid client_id": "Virheellinen client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Uudelleenohjaus-URI: %s ei ole sallittujen uudelleenohjaus-URI-listassa",
"Token not found, invalid accessToken": "Tokenia ei löydy, virheellinen accessToken"
},
"user": {
"Display name cannot be empty": "Näyttönimi ei voi olla tyhjä",
"MFA email is enabled but email is empty": "MFA-sähköposti on käytössä, mutta sähköposti on tyhjä",
"MFA phone is enabled but phone number is empty": "MFA-puhelin on käytössä, mutta puhelinnumero on tyhjä",
"New password cannot contain blank space.": "Uusi salasana ei voi sisältää välilyöntejä.",
"The new password must be different from your current password": "Uuden salasanan on oltava erilainen kuin nykyinen salasana",
"the user's owner and name should not be empty": "käyttäjän omistaja ja nimi eivät saa olla tyhjiä"
},
"util": {
"No application is found for userId: %s": "Sovellusta ei löydy userId:lle: %s",
"No provider for category: %s is found for application: %s": "Provideria kategorialle: %s ei löydy sovellukselle: %s",
"The provider: %s is not found": "Provideria: %s ei löydy"
},
"verification": {
"Invalid captcha provider.": "Virheellinen captcha-provider.",
"Phone number is invalid in your region %s": "Puhelinnumero on virheellinen alueellasi %s",
"The verification code has already been used!": "Vahvistuskoodi on jo käytetty!",
"The verification code has not been sent yet!": "Vahvistuskoodia ei ole vielä lähetetty!",
"Turing test failed.": "Turing-testi epäonnistui.",
"Unable to get the email modify rule.": "Sähköpostin muokkaussääntöä ei saada.",
"Unable to get the phone modify rule.": "Puhelimen muokkaussääntöä ei saada.",
"Unknown type": "Tuntematon tyyppi",
"Wrong verification code!": "Väärä vahvistuskoodi!",
"You should verify your code in %d min!": "Sinun tulee vahvistaa koodisi %d minuutissa!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "lisää SMS-provider \"Providers\"-listalle sovellukselle: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "lisää sähköposti-provider \"Providers\"-listalle sovellukselle: %s",
"the user does not exist, please sign up first": "käyttäjää ei ole olemassa, rekisteröidy ensin"
},
"webauthn": {
"Found no credentials for this user": "Tälle käyttäjälle ei löytynyt kirjautumistietoja",
"Please call WebAuthnSigninBegin first": "Kutsu ensin WebAuthnSigninBegin"
}
}

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "L'utilisateur est interdit de se connecter, veuillez contacter l'administrateur",
"The user: %s doesn't exist in LDAP server": "L'utilisateur : %s n'existe pas sur le serveur 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.": "Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, des traits soulignés ou des tirets, ne peut pas avoir de tirets ou de traits soulignés consécutifs et ne peut pas commencer ou se terminer par un tiret ou un trait souligné.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "La valeur \\\"%s\\\" pour le champ de compte \\\"%s\\\" ne correspond pas à l'expression régulière de l'élément de compte",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "La valeur \\\"%s\\\" pour le champ d'inscription \\\"%s\\\" ne correspond pas à l'expression régulière de l'élément d'inscription de l'application \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "La valeur \"%s\" pour le champ de compte \"%s\" ne correspond pas à l'expression régulière de l'élément de compte",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "La valeur \"%s\" pour le champ d'inscription \"%s\" ne correspond pas à l'expression régulière de l'élément d'inscription de l'application \"%s\"",
"Username already exists": "Nom d'utilisateur existe déjà",
"Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
"Username cannot contain white spaces": "Nom d'utilisateur ne peut pas contenir d'espaces blancs",
@@ -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.": "Le nom d'utilisateur prend en charge le format e-mail. De plus, il ne peut contenir que des caractères alphanumériques, des tirets bas ou des traits d'union, ne peut pas avoir de traits d'union ou de tirets bas consécutifs, et ne peut pas commencer ou se terminer par un trait d'union ou un tiret bas. Faites également attention au format de l'e-mail.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Vous avez entré le mauvais mot de passe ou code plusieurs fois, veuillez attendre %d minutes et réessayer",
"Your IP address: %s has been banned according to the configuration of: ": "Votre adresse IP : %s a été bannie selon la configuration de : ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Votre mot de passe a expiré. Veuillez le réinitialiser en cliquant sur \\\"Mot de passe oublié\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Votre mot de passe a expiré. Veuillez le réinitialiser en cliquant sur \"Mot de passe oublié\"",
"Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone",
"password or code is incorrect": "mot de passe ou code incorrect",
"password or code is incorrect, you have %s remaining chances": "Le mot de passe ou le code est incorrect, il vous reste %s chances",
@@ -137,7 +137,7 @@
"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.": "L'ajout d'un nouvel utilisateur à l'organisation « built-in » (intégrée) est actuellement désactivé. Veuillez noter : Tous les utilisateurs de l'organisation « built-in » sont des administrateurs globaux dans Casdoor. Consulter la documentation : https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Si vous souhaitez encore créer un utilisateur pour l'organisation « built-in », accédez à la page des paramètres de l'organisation et activez l'option « A le consentement aux privilèges »."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "La permission : \\\"%s\\\" n'existe pas"
"The permission: \"%s\" doesn't exist": "La permission : \"%s\" n'existe pas"
},
"provider": {
"Invalid application id": "Identifiant d'application invalide",
@@ -196,8 +196,8 @@
"Unknown type": "Type inconnu",
"Wrong verification code!": "Mauvais code de vérification !",
"You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "veuillez ajouter un fournisseur SMS à la liste \\\"Providers\\\" pour l'application : %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "veuillez ajouter un fournisseur d'e-mail à la liste \\\"Providers\\\" pour l'application : %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "veuillez ajouter un fournisseur SMS à la liste \"Providers\" pour l'application : %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "veuillez ajouter un fournisseur d'e-mail à la liste \"Providers\" pour l'application : %s",
"the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
},
"webauthn": {

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "הוספת משתמש נכשלה",
"Get init score failed, error: %w": "קבלת ניקוד התחלתי נכשלה, שגיאה: %w",
"Please sign out first": "אנא התנתק תחילה",
"The application does not allow to sign up new account": "האפליקציה אינה מאפשרת הרשמה של חשבון חדש"
},
"auth": {
"Challenge method should be S256": "שיטת האתגר חייבת להיות S256",
"DeviceCode Invalid": "קוד מכשיר שגוי",
"Failed to create user, user information is invalid: %s": "יצירת משתמש נכשלה, פרטי המשתמש שגויים: %s",
"Failed to login in: %s": "כניסה נכשלה: %s",
"Invalid token": "אסימון שגוי",
"State expected: %s, but got: %s": "מצב צפוי: %s, אך התקבל: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש דרך %s, אנא השתמש בדרך אחרת להרשמה",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש, אנא צור קשר עם התמיכה הטכנית",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "החשבון עבור ספק: %s ושם משתמש: %s (%s) כבר מקושר לחשבון אחר: %s (%s)",
"The application: %s does not exist": "האפליקציה: %s אינה קיימת",
"The application: %s has disabled users to signin": "האפליקציה: %s השביתה כניסת משתמשים",
"The group: %s does not exist": "הקבוצה: %s לא קיימת",
"The login method: login with LDAP is not enabled for the application": "שיטת הכניסה: כניסה עם LDAP אינה מאופשרת לאפליקציה",
"The login method: login with SMS is not enabled for the application": "שיטת הכניסה: כניסה עם SMS אינה מאופשרת לאפליקציה",
"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 organization: %s does not exist": "הארגון: %s אינו קיים",
"The organization: %s has disabled users to signin": "הארגון: %s השבית כניסת משתמשים",
"The plan: %s does not exist": "התוכנית: %s לא קיימת",
"The pricing: %s does not exist": "התמחור: %s לא קיים",
"The pricing: %s does not have plan: %s": "התמחור: %s אין לו תוכנית: %s",
"The provider: %s does not exist": "הספק: %s אינו קיים",
"The provider: %s is not enabled for the application": "הספק: %s אינו מאופשר לאפליקציה",
"Unauthorized operation": "פעולה לא מורשית",
"Unknown authentication type (not password or provider), form = %s": "סוג אימות לא ידוע (לא סיסמה או ספק), טופס = %s",
"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 אין לה תמחור ברירת מחדל",
"the application for user %s is not found": "האפליקציה עבור המשתמש %s לא נמצאה",
"the organization: %s is not found": "הארגון: %s לא נמצא"
},
"cas": {
"Service %s and %s do not match": "השירות %s ו-%s אינם תואמים"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s אינו עומד בדרישות התבנית CIDR: %s",
"Affiliation cannot be blank": "השתייכות אינה יכולה להיות ריקה",
"CIDR for IP: %s should not be empty": "CIDR עבור IP: %s לא צריך להיות ריק",
"Default code does not match the code's matching rules": "קוד ברירת המחדל אינו תואם לכללי ההתאמה של הקוד",
"DisplayName cannot be blank": "שם התצוגה אינו יכול להיות ריק",
"DisplayName is not valid real name": "שם התצוגה אינו שם אמיתי תקף",
"Email already exists": "האימייל כבר קיים",
"Email cannot be empty": "האימייל אינו יכול להיות ריק",
"Email is invalid": "האימייל אינו תקף",
"Empty username.": "שם משתמש ריק.",
"Face data does not exist, cannot log in": "נתוני פנים אינם קיימים, לא ניתן להיכנס",
"Face data mismatch": "אי התאמת נתוני פנים",
"Failed to parse client IP: %s": "ניתוח כתובת ה-IP של הלקוח נכשל: %s",
"FirstName cannot be blank": "שם פרטי אינו יכול להיות ריק",
"Invitation code cannot be blank": "קוד הזמנה אינו יכול להיות ריק",
"Invitation code exhausted": "קוד ההזמנה נוצל",
"Invitation code is invalid": "קוד ההזמנה שגוי",
"Invitation code suspended": "קוד ההזמנה הושעה",
"LDAP user name or password incorrect": "שם משתמש או סיסמה של LDAP שגויים",
"LastName cannot be blank": "שם משפחה אינו יכול להיות ריק",
"Multiple accounts with same uid, please check your ldap server": "מספר חשבונות עם אותו uid, אנא בדוק את שרת ה-LDAP שלך",
"Organization does not exist": "הארגון אינו קיים",
"Password cannot be empty": "הסיסמה אינה יכולה להיות ריקה",
"Phone already exists": "הטלפון כבר קיים",
"Phone cannot be empty": "הטלפון אינו יכול להיות ריק",
"Phone number is invalid": "מספר הטלפון אינו תקף",
"Please register using the email corresponding to the invitation code": "אנא הרשם באמצעות האימייל התואם לקוד ההזמנה",
"Please register using the phone corresponding to the invitation code": "אנא הרשם באמצעות הטלפון המתאים לקוד ההזמנה",
"Please register using the username corresponding to the invitation code": "אנא הרשם באמצעות שם המשתמש התואם לקוד ההזמנה",
"Session outdated, please login again": "הסשן פג תוקף, אנא התחבר שוב",
"The invitation code has already been used": "קוד ההזמנה כבר נוצל",
"The password must contain at least one special character": "הסיסמה חייבת להכיל לפחות תו מיוחד אחד",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "הסיסמה חייבת להכיל לפחות אות גדולה אחת, אות קטנה אחת וספרה אחת",
"The password must have at least 6 characters": "הסיסמה חייבת להכיל לפחות 6 תווים",
"The password must have at least 8 characters": "הסיסמה חייבת להכיל לפחות 8 תווים",
"The password must not contain any repeated characters": "הסיסמה אינה יכולה להכיל תווים חוזרים",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "המשתמש נמחק ולא ניתן להשתמש בו לכניסה, אנא צור קשר עם המנהל",
"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\\\" אינו תואם ל-regex של פריט החשבון",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "הערך \\\"%s\\\" עבור שדה ההרשמה \\\"%s\\\" אינו תואם ל-regex של פריט ההרשמה של האפליקציה \\\"%s\\\"",
"Username already exists": "שם המשתמש כבר קיים",
"Username cannot be an email address": "שם המשתמש לא יכול להיות כתובת אימייל",
"Username cannot contain white spaces": "שם המשתמש לא יכול להכיל רווחים",
"Username cannot start with a digit": "שם המשתמש לא יכול להתחיל בספרה",
"Username is too long (maximum is 255 characters).": "שם המשתמש ארוך מדי (מקסימום 255 תווים).",
"Username must have at least 2 characters": "שם המשתמש חייב להכיל לפחות 2 תווים",
"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.": "שם המשתמש תומך בתבנית אימייל. כמו כן, שם המשתמש יכול להכיל רק תווים אלפאנומריים, קווים תחתונים או מקפים, לא יכול להכיל מקפים או קווים תחתונים רצופים, ולא יכול להתחיל או להסתיים עם מקף או קו תחתון. שים לב גם לתבנית האימייל.",
"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 region is not allow to signup by phone": "האזור שלך אינו מאפשר הרשמה באמצעות טלפון",
"password or code is incorrect": "סיסמה או קוד שגויים",
"password or code is incorrect, you have %s remaining chances": "סיסמה או קוד שגויים, יש לך %s ניסיונות נותרים",
"unsupported password type: %s": "סוג סיסמה לא נתמך: %s"
},
"enforcer": {
"the adapter: %s is not found": "המתאם: %s לא נמצא"
},
"general": {
"Failed to import groups": "יבוא קבוצות נכשל",
"Failed to import users": "יבוא משתמשים נכשל",
"Missing parameter": "חסר פרמטר",
"Only admin user can specify user": "רק משתמש מנהל יכול לציין משתמש",
"Please login first": "אנא התחבר תחילה",
"The organization: %s should have one application at least": "הארגון: %s צריך להכיל לפחות אפליקציה אחת",
"The user: %s doesn't exist": "המשתמש: %s לא קיים",
"Wrong userId": "מזהה משתמש שגוי",
"don't support captchaProvider: ": "לא נתמך captchaProvider: ",
"this operation is not allowed in demo mode": "פעולה זו אינה מותרת במצב הדגמה",
"this operation requires administrator to perform": "פעולה זו דורשת מנהל לביצוע"
},
"invitation": {
"Invitation %s does not exist": "ההזמנה %s אינה קיימת"
},
"ldap": {
"Ldap server exist": "שרת LDAP קיים"
},
"link": {
"Please link first": "אנא קשר תחילה",
"This application has no providers": "לאפליקציה זו אין ספקים",
"This application has no providers of type": "לאפליקציה זו אין ספקים מסוג",
"This provider can't be unlinked": "ספק זה לא יכול להיות מנותק",
"You are not the global admin, you can't unlink other users": "אינך מנהל גלובלי, אינך יכול לנתק משתמשים אחרים",
"You can't unlink yourself, you are not a member of any application": "אינך יכול לנתק את עצמך, אינך חבר באף אפליקציה"
},
"organization": {
"Only admin can modify the %s.": "רק מנהל יכול לשנות את %s.",
"The %s is immutable.": "%s הוא בלתי ניתן לשינוי.",
"Unknown modify rule %s.": "כלל שינוי לא ידוע %s.",
"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.": "הוספת משתמש חדש לארגון 'מובנה' מושבת כעת. שימו לב: כל המשתמשים בארגון 'מובנה' הם מנהלים גלובליים ב-Casdoor. ראו את המסמכים: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. אם עדיין תרצו ליצור משתמש לארגון 'מובנה', עברו לדף הגדרות הארגון והפעילו את האפשרות 'יש הסכמה לזכויות'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "ההרשאה: \\\"%s\\\" אינה קיימת"
},
"provider": {
"Invalid application id": "מזהה אפליקציה שגוי",
"the provider: %s does not exist": "הספק: %s אינו קיים"
},
"resource": {
"User is nil for tag: avatar": "המשתמש הוא nil עבור התג: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "שם המשתמש או fullFilePath ריקים: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "אפליקציה %s לא נמצאה"
},
"saml_sp": {
"provider %s's category is not SAML": "הקטגוריה של הספק %s אינה SAML"
},
"service": {
"Empty parameters for emailForm: %v": "פרמטרים ריקים עבור emailForm: %v",
"Invalid Email receivers: %s": "מקבלי אימייל שגויים: %s",
"Invalid phone receivers: %s": "מקבלי SMS שגויים: %s"
},
"storage": {
"The objectKey: %s is not allowed": "המפתח objectKey: %s אינו מורשה",
"The provider type: %s is not supported": "סוג הספק: %s אינו נתמך"
},
"subscription": {
"Error": "שגיאה"
},
"token": {
"Grant_type: %s is not supported in this application": "סוג ההרשאה: %s אינו נתמך באפליקציה זו",
"Invalid application or wrong clientSecret": "אפליקציה שגויה או clientSecret שגוי",
"Invalid client_id": "client_id שגוי",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "כתובת הפניה: %s לא קיימת ברשימת כתובות הפניה המורשות",
"Token not found, invalid accessToken": "אסימון לא נמצא, accessToken שגוי"
},
"user": {
"Display name cannot be empty": "שם התצוגה אינו יכול להיות ריק",
"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.": "הסיסמה החדשה אינה יכולה להכיל רווחים.",
"The new password must be different from your current password": "הסיסמה החדשה חייבת להיות שונה מהסיסמה הנוכחית שלך",
"the user's owner and name should not be empty": "הבעלים והשם של המשתמש אינם יכולים להיות ריקים"
},
"util": {
"No application is found for userId: %s": "לא נמצאה אפליקציה עבור userId: %s",
"No provider for category: %s is found for application: %s": "לא נמצא ספק עבור הקטגוריה: %s עבור האפליקציה: %s",
"The provider: %s is not found": "הספק: %s לא נמצא"
},
"verification": {
"Invalid captcha provider.": "ספק captcha שגוי.",
"Phone number is invalid in your region %s": "מספר הטלפון אינו תקף באזורך %s",
"The verification code has already been used!": "קוד האימות כבר נוצל!",
"The verification code has not been sent yet!": "קוד האימות טרם נשלח!",
"Turing test failed.": "מבחן טיורינג נכשל.",
"Unable to get the email modify rule.": "לא ניתן לקבל את כלל שינוי האימייל.",
"Unable to get the phone modify rule.": "לא ניתן לקבל את כלל שינוי הטלפון.",
"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": "אנא הוסף ספק אימייל לרשימת \\\"ספקים\\\" עבור האפליקציה: %s",
"the user does not exist, please sign up first": "המשתמש אינו קיים, אנא הרשם תחילה"
},
"webauthn": {
"Found no credentials for this user": "לא נמצאו אישורים עבור משתמש זה",
"Please call WebAuthnSigninBegin first": "אנא קרא ל-WebAuthnSigninBegin תחילה"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Gagal menambahkan pengguna",
"Get init score failed, error: %w": "Gagal mendapatkan nilai inisiasi, kesalahan: %w",
"Please sign out first": "Silakan keluar terlebih dahulu",
"The application does not allow to sign up new account": "Aplikasi tidak memperbolehkan pendaftaran akun baru"
},
"auth": {
"Challenge method should be S256": "Metode tantangan harus S256",
"DeviceCode Invalid": "Kode perangkat tidak valid",
"Failed to create user, user information is invalid: %s": "Gagal membuat pengguna, informasi pengguna tidak valid: %s",
"Failed to login in: %s": "Gagal masuk: %s",
"Invalid token": "Token tidak valid",
"State expected: %s, but got: %s": "Diharapkan: %s, tapi diperoleh: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru melalui %s, silakan gunakan cara lain untuk mendaftar",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru, silakan hubungi dukungan IT Anda",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk penyedia: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
"The application: %s does not exist": "Aplikasi: %s tidak ada",
"The application: %s has disabled users to signin": "Aplikasi: %s telah menonaktifkan pengguna untuk masuk",
"The group: %s does not exist": "Grup: %s tidak ada",
"The login method: login with LDAP is not enabled for the application": "Metode masuk: masuk dengan LDAP tidak diaktifkan untuk aplikasi ini",
"The login method: login with SMS is not enabled for the application": "Metode masuk: masuk dengan SMS tidak diaktifkan untuk aplikasi ini",
"The login method: login with email is not enabled for the application": "Metode masuk: masuk dengan email tidak diaktifkan untuk aplikasi ini",
"The login method: login with face is not enabled for the application": "Metode masuk: masuk dengan wajah tidak diaktifkan untuk aplikasi ini",
"The login method: login with password is not enabled for the application": "Metode login: login dengan sandi tidak diaktifkan untuk aplikasi tersebut",
"The organization: %s does not exist": "Organisasi: %s tidak ada",
"The organization: %s has disabled users to signin": "Organisasi: %s telah menonaktifkan pengguna untuk masuk",
"The plan: %s does not exist": "Rencana: %s tidak ada",
"The pricing: %s does not exist": "Harga: %s tidak ada",
"The pricing: %s does not have plan: %s": "Harga: %s tidak memiliki rencana: %s",
"The provider: %s does not exist": "Penyedia: %s tidak ada",
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak sah",
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan sandi atau penyedia), formulir = %s",
"User's tag: %s is not listed in the application's tags": "Tag pengguna: %s tidak terdaftar dalam tag aplikasi",
"UserCode Expired": "Kode pengguna kedaluwarsa",
"UserCode Invalid": "Kode pengguna tidak valid",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "pengguna berbayar %s tidak memiliki langganan aktif atau tertunda dan aplikasi: %s tidak memiliki harga default",
"the application for user %s is not found": "aplikasi untuk pengguna %s tidak ditemukan",
"the organization: %s is not found": "organisasi: %s tidak ditemukan"
},
"cas": {
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s tidak memenuhi persyaratan format CIDR: %s",
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
"CIDR for IP: %s should not be empty": "CIDR untuk IP: %s tidak boleh kosong",
"Default code does not match the code's matching rules": "Kode default tidak cocok dengan aturan pencocokan kode",
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
"DisplayName is not valid real name": "DisplayName bukanlah nama asli yang valid",
"Email already exists": "Email sudah ada",
"Email cannot be empty": "Email tidak boleh kosong",
"Email is invalid": "Email tidak valid",
"Empty username.": "Nama pengguna kosong.",
"Face data does not exist, cannot log in": "Data wajah tidak ada, tidak dapat masuk",
"Face data mismatch": "Ketidakcocokan data wajah",
"Failed to parse client IP: %s": "Gagal mengurai IP klien: %s",
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
"Invitation code cannot be blank": "Kode undangan tidak boleh kosong",
"Invitation code exhausted": "Kode undangan habis",
"Invitation code is invalid": "Kode undangan tidak valid",
"Invitation code suspended": "Kode undangan ditangguhkan",
"LDAP user name or password incorrect": "Nama pengguna atau sandi LDAP salah",
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server LDAP Anda",
"Organization does not exist": "Organisasi tidak ada",
"Password cannot be empty": "Kata sandi tidak boleh kosong",
"Phone already exists": "Telepon sudah ada",
"Phone cannot be empty": "Telepon tidak boleh kosong",
"Phone number is invalid": "Nomor telepon tidak valid",
"Please register using the email corresponding to the invitation code": "Silakan daftar menggunakan email yang sesuai dengan kode undangan",
"Please register using the phone corresponding to the invitation code": "Silakan daftar menggunakan nomor telepon yang sesuai dengan kode undangan",
"Please register using the username corresponding to the invitation code": "Silakan daftar menggunakan nama pengguna yang sesuai dengan kode undangan",
"Session outdated, please login again": "Sesi kadaluwarsa, silakan masuk lagi",
"The invitation code has already been used": "Kode undangan sudah digunakan",
"The password must contain at least one special character": "Kata sandi harus berisi setidaknya satu karakter khusus",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Kata sandi harus berisi setidaknya satu huruf besar, satu huruf kecil dan satu angka",
"The password must have at least 6 characters": "Kata sandi harus memiliki setidaknya 6 karakter",
"The password must have at least 8 characters": "Kata sandi harus memiliki setidaknya 8 karakter",
"The password must not contain any repeated characters": "Kata sandi tidak boleh berisi karakter yang berulang",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Pengguna telah dihapus dan tidak dapat digunakan untuk masuk, silakan hubungi administrator",
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang masuk, silakan hubungi administrator",
"The user: %s doesn't exist in LDAP server": "Pengguna: %s tidak ada di server 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.": "Nama pengguna hanya bisa menggunakan karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki dua tanda hubung atau garis bawah berurutan, dan tidak boleh diawali atau diakhiri dengan tanda hubung atau garis bawah.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Nilai \\\"%s\\\" untuk bidang akun \\\"%s\\\" tidak cocok dengan regex item akun",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Nilai \\\"%s\\\" untuk bidang pendaftaran \\\"%s\\\" tidak cocok dengan regex item pendaftaran aplikasi \\\"%s\\\"",
"Username already exists": "Nama pengguna sudah ada",
"Username cannot be an email address": "Username tidak bisa menjadi alamat email",
"Username cannot contain white spaces": "Username tidak boleh mengandung spasi",
"Username cannot start with a digit": "Username tidak dapat dimulai dengan angka",
"Username is too long (maximum is 255 characters).": "Nama pengguna terlalu panjang (maksimum 255 karakter).",
"Username must have at least 2 characters": "Nama pengguna harus memiliki setidaknya 2 karakter",
"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.": "Nama pengguna mendukung format email. Nama pengguna hanya boleh berisi karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki garis bawah atau tanda hubung berturut-turut, dan tidak boleh diawali atau diakhiri dengan garis bawah atau tanda hubung. Perhatikan juga format email.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda telah memasukkan sandi atau kode yang salah terlalu sering, mohon tunggu selama %d menit lalu coba kembali",
"Your IP address: %s has been banned according to the configuration of: ": "Alamat IP Anda: %s telah diblokir sesuai konfigurasi: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Kata sandi Anda telah kedaluwarsa. Silakan atur ulang kata sandi dengan mengklik \\\"Lupa kata sandi\\\"",
"Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon",
"password or code is incorrect": "kata sandi atau kode salah",
"password or code is incorrect, you have %s remaining chances": "Sandi atau kode salah, Anda memiliki %s kesempatan tersisa",
"unsupported password type: %s": "jenis sandi tidak didukung: %s"
},
"enforcer": {
"the adapter: %s is not found": "adapter: %s tidak ditemukan"
},
"general": {
"Failed to import groups": "Gagal mengimpor grup",
"Failed to import users": "Gagal mengimpor pengguna",
"Missing parameter": "Parameter hilang",
"Only admin user can specify user": "Hanya pengguna admin yang dapat menentukan pengguna",
"Please login first": "Silahkan login terlebih dahulu",
"The organization: %s should have one application at least": "Organisasi: %s harus memiliki setidaknya satu aplikasi",
"The user: %s doesn't exist": "Pengguna: %s tidak ada",
"Wrong userId": "userId salah",
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:",
"this operation is not allowed in demo mode": "operasi ini tidak diizinkan dalam mode demo",
"this operation requires administrator to perform": "operasi ini memerlukan administrator untuk melakukannya"
},
"invitation": {
"Invitation %s does not exist": "Undangan %s tidak ada"
},
"ldap": {
"Ldap server exist": "Server ldap ada"
},
"link": {
"Please link first": "Silahkan tautkan terlebih dahulu",
"This application has no providers": "Aplikasi ini tidak memiliki penyedia",
"This application has no providers of type": " Aplikasi ini tidak memiliki penyedia tipe ",
"This provider can't be unlinked": "Penyedia layanan ini tidak dapat dipisahkan",
"You are not the global admin, you can't unlink other users": "Anda bukan admin global, Anda tidak dapat memutuskan tautan pengguna lain",
"You can't unlink yourself, you are not a member of any application": "Anda tidak dapat memutuskan tautan diri sendiri, karena Anda bukan anggota dari aplikasi apa pun"
},
"organization": {
"Only admin can modify the %s.": "Hanya admin yang dapat memodifikasi %s.",
"The %s is immutable.": "%s tidak dapat diubah.",
"Unknown modify rule %s.": "Aturan modifikasi tidak diketahui %s.",
"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.": "Penambahan pengguna baru ke organisasi 'built-in' (terintegrasi) saat ini dinonaktifkan. Perhatikan: Semua pengguna di organisasi 'built-in' adalah administrator global di Casdoor. Lihat dokumentasi: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Jika Anda masih ingin membuat pengguna untuk organisasi 'built-in', buka halaman pengaturan organisasi dan aktifkan opsi 'Memiliki persetujuan hak istimewa'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Izin: \\\"%s\\\" tidak ada"
},
"provider": {
"Invalid application id": "ID aplikasi tidak valid",
"the provider: %s does not exist": "penyedia: %s tidak ada"
},
"resource": {
"User is nil for tag: avatar": "Pengguna kosong untuk tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nama pengguna atau path lengkap file kosong: nama_pengguna = %s, path_lengkap_file = %s"
},
"saml": {
"Application %s not found": "Aplikasi %s tidak ditemukan"
},
"saml_sp": {
"provider %s's category is not SAML": "kategori penyedia %s bukan SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Parameter kosong untuk emailForm: %v",
"Invalid Email receivers: %s": "Penerima email tidak valid: %s",
"Invalid phone receivers: %s": "Penerima telepon tidak valid: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Kunci objek: %s tidak diizinkan",
"The provider type: %s is not supported": "Jenis penyedia: %s tidak didukung"
},
"subscription": {
"Error": "Kesalahan"
},
"token": {
"Grant_type: %s is not supported in this application": "Jenis grant (grant_type) %s tidak didukung dalam aplikasi ini",
"Invalid application or wrong clientSecret": "Aplikasi tidak valid atau clientSecret salah",
"Invalid client_id": "ID klien tidak valid",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI pengalihan: %s tidak ada dalam daftar URI Pengalihan yang diizinkan",
"Token not found, invalid accessToken": "Token tidak ditemukan, accessToken tidak valid"
},
"user": {
"Display name cannot be empty": "Nama tampilan tidak boleh kosong",
"MFA email is enabled but email is empty": "Email MFA diaktifkan tetapi email kosong",
"MFA phone is enabled but phone number is empty": "Telepon MFA diaktifkan tetapi nomor telepon kosong",
"New password cannot contain blank space.": "Sandi baru tidak boleh mengandung spasi kosong.",
"The new password must be different from your current password": "Kata sandi baru harus berbeda dari kata sandi Anda saat ini",
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
},
"util": {
"No application is found for userId: %s": "Tidak ditemukan aplikasi untuk userId: %s",
"No provider for category: %s is found for application: %s": "Tidak ditemukan penyedia untuk kategori: %s untuk aplikasi: %s",
"The provider: %s is not found": "Penyedia: %s tidak ditemukan"
},
"verification": {
"Invalid captcha provider.": "Penyedia captcha tidak valid.",
"Phone number is invalid in your region %s": "Nomor telepon tidak valid di wilayah anda %s",
"The verification code has already been used!": "Kode verifikasi sudah digunakan!",
"The verification code has not been sent yet!": "Kode verifikasi belum dikirim!",
"Turing test failed.": "Tes Turing gagal.",
"Unable to get the email modify rule.": "Tidak dapat memperoleh aturan modifikasi email.",
"Unable to get the phone modify rule.": "Tidak dapat memodifikasi aturan telepon.",
"Unknown type": "Tipe tidak diketahui",
"Wrong verification code!": "Kode verifikasi salah!",
"You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "silakan tambahkan penyedia SMS ke daftar \\\"Providers\\\" untuk aplikasi: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "silakan tambahkan penyedia Email ke daftar \\\"Providers\\\" untuk aplikasi: %s",
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
},
"webauthn": {
"Found no credentials for this user": "Tidak ditemukan kredensial untuk pengguna ini",
"Please call WebAuthnSigninBegin first": "Harap panggil WebAuthnSigninBegin terlebih dahulu"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Aggiunta utente fallita",
"Get init score failed, error: %w": "Errore iniziale punteggio: %w",
"Please sign out first": "Esegui prima il logout",
"The application does not allow to sign up new account": "L'app non consente registrazione"
},
"auth": {
"Challenge method should be S256": "Metodo challenge deve essere S256",
"DeviceCode Invalid": "Codice dispositivo non valido",
"Failed to create user, user information is invalid: %s": "Creazione utente fallita: dati non validi: %s",
"Failed to login in: %s": "Login fallito: %s",
"Invalid token": "Token non valido",
"State expected: %s, but got: %s": "Stato atteso: %s, ricevuto: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Account per provider: %s e utente: %s (%s) non esiste, registrazione tramite %s non consentita",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account per provider: %s e utente: %s (%s) non esiste, registrazione non consentita: contatta l'assistenza IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account per provider: %s e utente: %s (%s) già collegato a un altro account: %s (%s)",
"The application: %s does not exist": "L'app: %s non esiste",
"The application: %s has disabled users to signin": "L'applicazione: %s ha disabilitato l'accesso degli utenti",
"The group: %s does not exist": "Il gruppo: %s non esiste",
"The login method: login with LDAP is not enabled for the application": "Metodo di accesso: login con LDAP non abilitato per l'applicazione",
"The login method: login with SMS is not enabled for the application": "Metodo di accesso: login con SMS non abilitato per l'applicazione",
"The login method: login with email is not enabled for the application": "Metodo di accesso: login con email non abilitato per l'applicazione",
"The login method: login with face is not enabled for the application": "Metodo di accesso: login con riconoscimento facciale non abilitato per l'applicazione",
"The login method: login with password is not enabled for the application": "Login con password non abilitato per questa app",
"The organization: %s does not exist": "L'organizzazione: %s non esiste",
"The organization: %s has disabled users to signin": "L'organizzazione: %s ha disabilitato l'accesso degli utenti",
"The plan: %s does not exist": "Il piano: %s non esiste",
"The pricing: %s does not exist": "Il prezzo: %s non esiste",
"The pricing: %s does not have plan: %s": "Il prezzo: %s non ha il piano: %s",
"The provider: %s does not exist": "Il provider: %s non esiste",
"The provider: %s is not enabled for the application": "Il provider: %s non è abilitato per l'app",
"Unauthorized operation": "Operazione non autorizzata",
"Unknown authentication type (not password or provider), form = %s": "Tipo di autenticazione sconosciuto (non password o provider), form = %s",
"User's tag: %s is not listed in the application's tags": "Il tag dell'utente: %s non è presente nei tag dell'applicazione",
"UserCode Expired": "Codice utente scaduto",
"UserCode Invalid": "Codice utente non valido",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "L'utente a pagamento %s non ha una sottoscrizione attiva o in attesa e l'applicazione: %s non ha una tariffazione predefinita",
"the application for user %s is not found": "applicazione per l'utente %s non trovata",
"the organization: %s is not found": "organizzazione: %s non trovata"
},
"cas": {
"Service %s and %s do not match": "Servizi %s e %s non corrispondono"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s non soddisfa i requisiti di formato CIDR: %s",
"Affiliation cannot be blank": "Affiliazione obbligatoria",
"CIDR for IP: %s should not be empty": "Il CIDR per l'IP: %s non deve essere vuoto",
"Default code does not match the code's matching rules": "Il codice predefinito non rispetta le regole di corrispondenza del codice",
"DisplayName cannot be blank": "Nome visualizzato obbligatorio",
"DisplayName is not valid real name": "Nome visualizzato non valido",
"Email already exists": "Email già esistente",
"Email cannot be empty": "Email obbligatoria",
"Email is invalid": "Email non valida",
"Empty username.": "Username vuoto",
"Face data does not exist, cannot log in": "Dati facciali assenti, impossibile effettuare il login",
"Face data mismatch": "Mancata corrispondenza dei dati facciali",
"Failed to parse client IP: %s": "Impossibile analizzare l'IP del client: %s",
"FirstName cannot be blank": "Nome obbligatorio",
"Invitation code cannot be blank": "Il codice di invito non può essere vuoto",
"Invitation code exhausted": "Codice di invito esaurito",
"Invitation code is invalid": "Codice di invito non valido",
"Invitation code suspended": "Codice di invito sospeso",
"LDAP user name or password incorrect": "LDAP username o password errati",
"LastName cannot be blank": "Cognome obbligatorio",
"Multiple accounts with same uid, please check your ldap server": "UID duplicato, verifica il server LDAP",
"Organization does not exist": "Organizzazione inesistente",
"Password cannot be empty": "La password non può essere vuota",
"Phone already exists": "Telefono già esistente",
"Phone cannot be empty": "Telefono obbligatorio",
"Phone number is invalid": "Numero telefono non valido",
"Please register using the email corresponding to the invitation code": "Registrati con l'email corrispondente al codice di invito",
"Please register using the phone corresponding to the invitation code": "Registrati con il numero di telefono corrispondente al codice di invito",
"Please register using the username corresponding to the invitation code": "Registrati con il nome utente corrispondente al codice di invito",
"Session outdated, please login again": "Sessione scaduta, rieffettua il login",
"The invitation code has already been used": "Il codice di invito è già stato utilizzato",
"The password must contain at least one special character": "La password deve contenere almeno un carattere speciale",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "La password deve contenere almeno una lettera maiuscola, una lettera minuscola e una cifra",
"The password must have at least 6 characters": "La password deve avere almeno 6 caratteri",
"The password must have at least 8 characters": "La password deve avere almeno 8 caratteri",
"The password must not contain any repeated characters": "La password non deve contenere caratteri ripetuti",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "L'utente è stato eliminato e non può essere utilizzato per accedere, contattare l'amministratore",
"The user is forbidden to sign in, please contact the administrator": "Utente bloccato, contatta l'amministratore",
"The user: %s doesn't exist in LDAP server": "L'utente: %s non esiste nel server 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.": "Username solo caratteri alfanumerici, underscore o trattini. Non può iniziare/finire con trattino o underscore.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Il valore \\\"%s\\\" per il campo account \\\"%s\\\" non corrisponde alla regex dell'elemento account",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Il valore \\\"%s\\\" per il campo registrazione \\\"%s\\\" non corrisponde alla regex dell'elemento registrazione dell'applicazione \\\"%s\\\"",
"Username already exists": "Username già esistente",
"Username cannot be an email address": "Username non può essere un'email",
"Username cannot contain white spaces": "Username non può contenere spazi",
"Username cannot start with a digit": "Username non può iniziare con un numero",
"Username is too long (maximum is 255 characters).": "Username troppo lungo (max 255 caratteri)",
"Username must have at least 2 characters": "Username minimo 2 caratteri",
"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.": "Il nome utente supporta il formato email. Inoltre il nome utente può contenere solo caratteri alfanumerici, underscore o trattini, non può avere trattini o underscore consecutivi e non può iniziare o terminare con un trattino o underscore. Presta attenzione al formato email.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Troppi tentativi errati, attendi %d minuti",
"Your IP address: %s has been banned according to the configuration of: ": "Il tuo indirizzo IP: %s è stato bannato secondo la configurazione di: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "La tua password è scaduta. Reimposta la password cliccando \\\"Password dimenticata\\\"",
"Your region is not allow to signup by phone": "Registrazione via telefono non consentita nella tua regione",
"password or code is incorrect": "password o codice errati",
"password or code is incorrect, you have %s remaining chances": "password o codice errati, %s tentativi rimasti",
"unsupported password type: %s": "tipo password non supportato: %s"
},
"enforcer": {
"the adapter: %s is not found": "l'adapter: %s non è stato trovato"
},
"general": {
"Failed to import groups": "Impossibile importare i gruppi",
"Failed to import users": "Importazione utenti fallita",
"Missing parameter": "Parametro mancante",
"Only admin user can specify user": "Solo un utente amministratore può specificare l'utente",
"Please login first": "Effettua prima il login",
"The organization: %s should have one application at least": "L'organizzazione: %s deve avere almeno un'applicazione",
"The user: %s doesn't exist": "Utente: %s non esiste",
"Wrong userId": "ID utente errato",
"don't support captchaProvider: ": "captchaProvider non supportato: ",
"this operation is not allowed in demo mode": "questa operazione non è consentita in modalità demo",
"this operation requires administrator to perform": "questa operazione richiede un amministratore"
},
"invitation": {
"Invitation %s does not exist": "L'invito %s non esiste"
},
"ldap": {
"Ldap server exist": "Server LDAP esistente"
},
"link": {
"Please link first": "Collega prima",
"This application has no providers": "L'app non ha provider",
"This application has no providers of type": "L'app non ha provider di tipo",
"This provider can't be unlinked": "Questo provider non può essere scollegato",
"You are not the global admin, you can't unlink other users": "Non sei admin globale, non puoi scollegare altri utenti",
"You can't unlink yourself, you are not a member of any application": "Non puoi scollegarti, non sei membro di alcuna app"
},
"organization": {
"Only admin can modify the %s.": "Solo admin può modificare %s.",
"The %s is immutable.": "%s è immutabile.",
"Unknown modify rule %s.": "Regola modifica %s sconosciuta.",
"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.": "L'aggiunta di un nuovo utente all'organizzazione 'built-in' (integrata) è attualmente disabilitata. Si noti che tutti gli utenti nell'organizzazione 'built-in' sono amministratori globali in Casdoor. Consultare la documentazione: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Se si desidera comunque creare un utente per l'organizzazione 'built-in', andare alla pagina delle impostazioni dell'organizzazione e abilitare l'opzione 'Ha il consenso ai privilegi'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Il permesso: \\\"%s\\\" non esiste"
},
"provider": {
"Invalid application id": "ID app non valido",
"the provider: %s does not exist": "provider: %s non esiste"
},
"resource": {
"User is nil for tag: avatar": "Utente nullo per tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username o percorso file vuoti: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "App %s non trovata"
},
"saml_sp": {
"provider %s's category is not SAML": "Provider %s non è di tipo SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Parametri emailForm vuoti: %v",
"Invalid Email receivers: %s": "Destinatari email non validi: %s",
"Invalid phone receivers: %s": "Destinatari SMS non validi: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Chiave oggetto: %s non consentita",
"The provider type: %s is not supported": "Tipo provider: %s non supportato"
},
"subscription": {
"Error": "Errore"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s non supportato in questa app",
"Invalid application or wrong clientSecret": "App non valida o clientSecret errato",
"Invalid client_id": "client_id non valido",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI di redirect: %s non consentito",
"Token not found, invalid accessToken": "Token non trovato, accessToken non valido"
},
"user": {
"Display name cannot be empty": "Nome visualizzato obbligatorio",
"MFA email is enabled but email is empty": "L'email MFA è abilitata ma l'email è vuota",
"MFA phone is enabled but phone number is empty": "Il telefono MFA è abilitato ma il numero di telefono è vuoto",
"New password cannot contain blank space.": "Nuova password non può contenere spazi",
"The new password must be different from your current password": "La nuova password deve essere diversa dalla password attuale",
"the user's owner and name should not be empty": "il proprietario e il nome dell'utente non devono essere vuoti"
},
"util": {
"No application is found for userId: %s": "Nessuna app trovata per userId: %s",
"No provider for category: %s is found for application: %s": "Nessun provider per categoria: %s nell'app: %s",
"The provider: %s is not found": "Provider: %s non trovato"
},
"verification": {
"Invalid captcha provider.": "Provider captcha non valido",
"Phone number is invalid in your region %s": "Numero telefono non valido nella regione %s",
"The verification code has already been used!": "Il codice di verifica è già stato utilizzato!",
"The verification code has not been sent yet!": "Il codice di verifica non è ancora stato inviato!",
"Turing test failed.": "Test Turing fallito",
"Unable to get the email modify rule.": "Impossibile ottenere regola modifica email",
"Unable to get the phone modify rule.": "Impossibile ottenere regola modifica telefono",
"Unknown type": "Tipo sconosciuto",
"Wrong verification code!": "Codice verifica errato!",
"You should verify your code in %d min!": "Verifica codice entro %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "aggiungi un provider SMS all'elenco \\\"Providers\\\" per l'applicazione: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "aggiungi un provider Email all'elenco \\\"Providers\\\" per l'applicazione: %s",
"the user does not exist, please sign up first": "Utente inesistente, registrati prima"
},
"webauthn": {
"Found no credentials for this user": "Nessuna credenziale trovata per questo utente",
"Please call WebAuthnSigninBegin first": "Chiamare prima WebAuthnSigninBegin"
}
}

View File

@@ -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": "アカウントフィールド「%s」の値「%s」がアカウント項目の正規表現に一致しません",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "アプリケーション「%s」のサインアップ項目「%s」の値「%s」が正規表現に一致しません",
"Username already exists": "ユーザー名はすでに存在しています",
"Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
"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.": "ユーザー名はメール形式もサポートします。ユーザー名は英数字、アンダースコア、またはハイフンのみを含め、連続するハイフンやアンダースコアは使用できません。また、ハイフンまたはアンダースコアで始まったり終わったりすることもできません。メール形式にも注意してください。",
"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\"": "パスワードの有効期限が切れています。「パスワードを忘れた方はこちら」をクリックしてリセットしてください",
"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 回の試行機会があります",
@@ -137,7 +137,7 @@
"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": "権限「%s」は存在しません"
},
"provider": {
"Invalid application id": "アプリケーションIDが無効です",
@@ -196,8 +196,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": "アプリケーション「%s」の「Providers」リストに SMS プロバイダを追加してください",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "アプリケーション「%s」の「Providers」リストにメールプロバイダを追加してください",
"please add a SMS provider to the \"Providers\" list for the application: %s": "アプリケーション「%s」の「Providers」リストに SMS プロバイダを追加してください",
"please add an Email provider to the \"Providers\" list for the application: %s": "アプリケーション「%s」の「Providers」リストにメールプロバイダを追加してください",
"the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
},
"webauthn": {

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Gebruiker toevoegen mislukt",
"Get init score failed, error: %w": "Initiële score ophalen mislukt, fout: %w",
"Please sign out first": "Gelieve eerst uit te loggen",
"The application does not allow to sign up new account": "De applicatie staat geen nieuwe registraties toe"
},
"auth": {
"Challenge method should be S256": "Challenge-methode moet S256 zijn",
"DeviceCode Invalid": "DeviceCode ongeldig",
"Failed to create user, user information is invalid: %s": "Gebruiker aanmaken mislukt, gebruikersinformatie ongeldig: %s",
"Failed to login in: %s": "Inloggen mislukt: %s",
"Invalid token": "Ongeldige token",
"State expected: %s, but got: %s": "Verwachte state: %s, maar kreeg: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren via %s, gebruik een andere methode",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren, contacteer IT-support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account voor provider: %s en gebruikersnaam: %s (%s) is al gelinkt aan ander account: %s (%s)",
"The application: %s does not exist": "Applicatie: %s bestaat niet",
"The application: %s has disabled users to signin": "Қолданба: %s пайдаланушылардың кіруін өшірді",
"The group: %s does not exist": "Топ: %s жоқ",
"The login method: login with LDAP is not enabled for the application": "Inloggen via LDAP is niet ingeschakeld voor deze applicatie",
"The login method: login with SMS is not enabled for the application": "Inloggen via SMS is niet ingeschakeld voor deze applicatie",
"The login method: login with email is not enabled for the application": "Inloggen via e-mail is niet ingeschakeld voor deze applicatie",
"The login method: login with face is not enabled for the application": "Inloggen via gezichtsherkenning is niet ingeschakeld voor deze applicatie",
"The login method: login with password is not enabled for the application": "Inloggen via wachtwoord is niet ingeschakeld voor deze applicatie",
"The organization: %s does not exist": "Organisatie: %s bestaat niet",
"The organization: %s has disabled users to signin": "Ұйым: %s пайдаланушылардың кіруін өшірді",
"The plan: %s does not exist": "Жоспар: %s жоқ",
"The pricing: %s does not exist": "Баға: %s жоқ",
"The pricing: %s does not have plan: %s": "Баға: %s жоспары жоқ: %s",
"The provider: %s does not exist": "Provider: %s bestaat niet",
"The provider: %s is not enabled for the application": "Provider: %s is niet ingeschakeld voor de applicatie",
"Unauthorized operation": "Ongeautoriseerde handeling",
"Unknown authentication type (not password or provider), form = %s": "Onbekend authenticatietype (geen wachtwoord of provider), formulier = %s",
"User's tag: %s is not listed in the application's tags": "Gebruikerstag: %s staat niet in de applicatietags",
"UserCode Expired": "UserCode verlopen",
"UserCode Invalid": "UserCode ongeldig",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Betaalde gebruiker %s heeft geen actief of lopend abonnement en applicatie: %s heeft geen standaardprijzen",
"the application for user %s is not found": "Applicatie voor gebruiker %s niet gevonden",
"the organization: %s is not found": "Organisatie: %s niet gevonden"
},
"cas": {
"Service %s and %s do not match": "Service %s en %s komen niet overeen"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s voldoet niet aan CIDR-formaat: %s",
"Affiliation cannot be blank": "Organisatie mag niet leeg zijn",
"CIDR for IP: %s should not be empty": "CIDR voor IP: %s mag niet leeg zijn",
"Default code does not match the code's matching rules": "Standaardcode komt niet overeen met matching rules",
"DisplayName cannot be blank": "Weergavenaam mag niet leeg zijn",
"DisplayName is not valid real name": "Weergavenaam is geen geldige echte naam",
"Email already exists": "E-mailadres bestaat al",
"Email cannot be empty": "E-mailadres mag niet leeg zijn",
"Email is invalid": "Ongeldig e-mailadres",
"Empty username.": "Lege gebruikersnaam.",
"Face data does not exist, cannot log in": "Gezichtsgegevens ontbreken, inloggen niet mogelijk",
"Face data mismatch": "Gezichtsgegevens komen niet overeen",
"Failed to parse client IP: %s": "Client-IP parsen mislukt: %s",
"FirstName cannot be blank": "Voornaam mag niet leeg zijn",
"Invitation code cannot be blank": "Uitnodigingscode mag niet leeg zijn",
"Invitation code exhausted": "Uitnodigingscode uitgeput",
"Invitation code is invalid": "Uitnodigingscode ongeldig",
"Invitation code suspended": "Uitnodigingscode opgeschort",
"LDAP user name or password incorrect": "LDAP-gebruikersnaam of wachtwoord onjuist",
"LastName cannot be blank": "Achternaam mag niet leeg zijn",
"Multiple accounts with same uid, please check your ldap server": "Meerdere accounts met zelfde uid, controleer LDAP-server",
"Organization does not exist": "Organisatie bestaat niet",
"Password cannot be empty": "Wachtwoord mag niet leeg zijn",
"Phone already exists": "Telefoonnummer bestaat al",
"Phone cannot be empty": "Telefoonnummer mag niet leeg zijn",
"Phone number is invalid": "Ongeldig telefoonnummer",
"Please register using the email corresponding to the invitation code": "Registreer met het e-mailadres dat hoort bij de uitnodigingscode",
"Please register using the phone corresponding to the invitation code": "Registreer met het telefoonnummer dat hoort bij de uitnodigingscode",
"Please register using the username corresponding to the invitation code": "Registreer met de gebruikersnaam die hoort bij de uitnodigingscode",
"Session outdated, please login again": "Sessie verlopen, gelieve opnieuw in te loggen",
"The invitation code has already been used": "Uitnodigingscode is al gebruikt",
"The password must contain at least one special character": "Құпия сөз кемінде бір арнайы таңбаны қамтуы керек",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Құпия сөз кемінде бір бас әріпті, бір кіші әріпті және бір санды қамтуы керек",
"The password must have at least 6 characters": "Құпия сөз кемінде 6 таңбадан тұруы керек",
"The password must have at least 8 characters": "Құпия сөз кемінде 8 таңбадан тұруы керек",
"The password must not contain any repeated characters": "Құпия сөз қайталанатын таңбаларды қамтымауы керек",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Пайдаланушы жойылған және кіру үшін пайдалануға болмайды, әкімшіге хабарласыңыз",
"The user is forbidden to sign in, please contact the administrator": "Gebruiker mag niet inloggen, contacteer beheerder",
"The user: %s doesn't exist in LDAP server": "Gebruiker: %s bestaat niet in LDAP-server",
"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.": "Gebruikersnaam mag alleen alfanumerieke tekens, underscores of koppeltekens bevatten, geen opeenvolgende koppeltekens/underscores, en mag niet beginnen of eindigen met koppelteken of underscore.",
"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\\\"-нің тіркелу элементінің регекспімен сәйкес келмейді",
"Username already exists": "Gebruikersnaam bestaat al",
"Username cannot be an email address": "Gebruikersnaam mag geen e-mailadres zijn",
"Username cannot contain white spaces": "Gebruikersnaam mag geen spaties bevatten",
"Username cannot start with a digit": "Gebruikersnaam mag niet met cijfer beginnen",
"Username is too long (maximum is 255 characters).": "Gebruikersnaam te lang (maximaal 255 tekens).",
"Username must have at least 2 characters": "Gebruikersnaam moet minstens 2 tekens hebben",
"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.": "Gebruikersnaam ondersteunt e-mailformaat. Mag alleen alfanumerieke tekens, underscores of koppeltekens bevatten, geen opeenvolgende koppeltekens/underscores, en mag niet beginnen of eindigen met koppelteken of underscore. Let op e-mailformaat.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Te vaak verkeerd wachtwoord of code ingevoerd, wacht %d minuten en probeer opnieuw",
"Your IP address: %s has been banned according to the configuration of: ": "Je IP-adres: %s is verbannen volgens configuratie van: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Сіздің пароліңіз мерзімі аяқталды. Кликтап \\\"Парольді ұмытып қалдым\\\" нұсқасын қалпына келтіріңіз",
"Your region is not allow to signup by phone": "Registratie per telefoon niet toegestaan in jouw regio",
"password or code is incorrect": "wachtwoord of code onjuist",
"password or code is incorrect, you have %s remaining chances": "wachtwoord of code onjuist, nog %s pogingen over",
"unsupported password type: %s": "niet-ondersteund wachtwoordtype: %s"
},
"enforcer": {
"the adapter: %s is not found": "adapter: %s niet gevonden"
},
"general": {
"Failed to import groups": "Importeren groepen mislukt",
"Failed to import users": "Importeren gebruikers mislukt",
"Missing parameter": "Ontbrekende parameter",
"Only admin user can specify user": "Alleen beheerder mag gebruiker specificeren",
"Please login first": "Gelieve eerst in te loggen",
"The organization: %s should have one application at least": "Organisatie: %s moet minstens één applicatie hebben",
"The user: %s doesn't exist": "Gebruiker: %s bestaat niet",
"Wrong userId": "Verkeerde userId",
"don't support captchaProvider: ": "captchaProvider niet ondersteund: ",
"this operation is not allowed in demo mode": "deze handeling is niet toegestaan in demo-modus",
"this operation requires administrator to perform": "deze handeling vereist beheerder"
},
"invitation": {
"Invitation %s does not exist": "Шақыру %s жоқ"
},
"ldap": {
"Ldap server exist": "LDAP-server bestaat al"
},
"link": {
"Please link first": "Gelieve eerst te linken",
"This application has no providers": "Deze applicatie heeft geen providers",
"This application has no providers of type": "Deze applicatie heeft geen providers van type",
"This provider can't be unlinked": "Deze provider kan niet ontkoppeld worden",
"You are not the global admin, you can't unlink other users": "Je bent geen globale beheerder, je kunt andere gebruikers niet ontkoppelen",
"You can't unlink yourself, you are not a member of any application": "Je kunt jezelf niet ontkoppelen, je bent geen lid van enige applicatie"
},
"organization": {
"Only admin can modify the %s.": "Alleen beheerder kan %s wijzigen.",
"The %s is immutable.": "%s is onveranderlijk.",
"Unknown modify rule %s.": "Onbekende wijzigingsregel %s.",
"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' ұйымы үшін пайдаланушы жасауын қаласаңыз, ұйымның баптаулар бетinə оралыңыз және 'Рұқсатларға растау бар' опциясын қосу іске ашыңыз."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Рұқсат: \\\"%s\\\" жоқ"
},
"provider": {
"Invalid application id": "Ongeldige applicatie-ID",
"the provider: %s does not exist": "provider: %s bestaat niet"
},
"resource": {
"User is nil for tag: avatar": "Gebruiker is nil voor tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Gebruikersnaam of fullFilePath is leeg: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Applicatie %s niet gevonden"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s is niet van categorie SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Lege parameters voor emailForm: %v",
"Invalid Email receivers: %s": "Ongeldige e-mailontvangers: %s",
"Invalid phone receivers: %s": "Ongeldige telefoonontvangers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "objectKey: %s is niet toegestaan",
"The provider type: %s is not supported": "Providertype: %s wordt niet ondersteund"
},
"subscription": {
"Error": "Fout"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s wordt niet ondersteund in deze applicatie",
"Invalid application or wrong clientSecret": "Ongeldige applicatie of verkeerde clientSecret",
"Invalid client_id": "Ongeldige client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s staat niet in toegestane lijst",
"Token not found, invalid accessToken": "Token niet gevonden, ongeldige accessToken"
},
"user": {
"Display name cannot be empty": "Weergavenaam mag niet leeg zijn",
"MFA email is enabled but email is empty": "MFA-e-mail is ingeschakeld maar e-mailadres is leeg",
"MFA phone is enabled but phone number is empty": "MFA-telefoon is ingeschakeld maar telefoonnummer is leeg",
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten.",
"The new password must be different from your current password": "Жаңа құпия сөз ағымдағы құпия сөзіңізден өзгеше болуы керек",
"the user's owner and name should not be empty": "eigenaar en naam van gebruiker mogen niet leeg zijn"
},
"util": {
"No application is found for userId: %s": "Geen applicatie gevonden voor userId: %s",
"No provider for category: %s is found for application: %s": "Geen provider voor categorie: %s gevonden voor applicatie: %s",
"The provider: %s is not found": "Provider: %s niet gevonden"
},
"verification": {
"Invalid captcha provider.": "Ongeldige captcha-provider.",
"Phone number is invalid in your region %s": "Telefoonnummer ongeldig in regio %s",
"The verification code has already been used!": "Verificatiecode is al gebruikt!",
"The verification code has not been sent yet!": "Verificatiecode is nog niet verstuurd!",
"Turing test failed.": "Turing-test mislukt.",
"Unable to get the email modify rule.": "Kan e-mail-wijzigingsregel niet ophalen.",
"Unable to get the phone modify rule.": "Kan telefoon-wijzigingsregel niet ophalen.",
"Unknown type": "Onbekend type",
"Wrong verification code!": "Verkeerde verificatiecode!",
"You should verify your code in %d min!": "Verifieer je code binnen %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Күріспе: %s қолданбасының \\\"Провайдерлер\\\" тізіміне SMS провайдерін қосыңыз",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Күріспе: %s қолданбасының \\\"Провайдерлер\\\" тізіміне Электрондық пошта провайдерін қосыңыз",
"the user does not exist, please sign up first": "gebruiker bestaat niet, registreer eerst"
},
"webauthn": {
"Found no credentials for this user": "Бұл пайдаланушы үшін тіркелгі деректері табылмады",
"Please call WebAuthnSigninBegin first": "Roep eerst WebAuthnSigninBegin aan"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "사용자 추가 실패",
"Get init score failed, error: %w": "초기 점수 획득 실패, 오류: %w",
"Please sign out first": "먼저 로그아웃해주세요",
"The application does not allow to sign up new account": "이 응용 프로그램은 새로운 계정 가입을 허용하지 않습니다"
},
"auth": {
"Challenge method should be S256": "도전 방식은 S256이어야 합니다",
"DeviceCode Invalid": "장치 코드가 유효하지 않습니다",
"Failed to create user, user information is invalid: %s": "사용자를 만들지 못했습니다. 사용자 정보가 잘못되었습니다: %s",
"Failed to login in: %s": "로그인에 실패했습니다.: %s",
"Invalid token": "유효하지 않은 토큰",
"State expected: %s, but got: %s": "예상한 상태: %s, 실제 상태: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "제공자 계정: %s와 사용자 이름: %s (%s)은(는) 존재하지 않으며 %s를 통해 새 계정으로 가입하는 것이 허용되지 않습니다. 다른 방법으로 가입하십시오",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
"The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
"The application: %s has disabled users to signin": "애플리케이션: %s이(가) 사용자 로그인을 비활성화했습니다",
"The group: %s does not exist": "그룹: %s이(가) 존재하지 않습니다",
"The login method: login with LDAP is not enabled for the application": "LDAP를 이용한 로그인 방식이 이 애플리케이션에 활성화되어 있지 않습니다",
"The login method: login with SMS is not enabled for the application": "SMS를 이용한 로그인 방식이 이 애플리케이션에 활성화되어 있지 않습니다",
"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 organization: %s does not exist": "조직 %s이(가) 존재하지 않습니다",
"The organization: %s has disabled users to signin": "조직: %s이(가) 사용자 로그인을 비활성화했습니다",
"The plan: %s does not exist": "플랜: %s이(가) 존재하지 않습니다",
"The pricing: %s does not exist": "가격: %s이(가) 존재하지 않습니다",
"The pricing: %s does not have plan: %s": "가격: %s에는 플랜 %s이(가) 없습니다",
"The provider: %s does not exist": "제공자 %s이(가) 존재하지 않습니다",
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
"Unauthorized operation": "무단 조작",
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
"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에 기본 가격 책정이 설정되어 있지 않습니다",
"the application for user %s is not found": "사용자 %s의 애플리케이션을 찾을 수 없습니다",
"the organization: %s is not found": "조직 %s을(를) 찾을 수 없습니다"
},
"cas": {
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s이(가) CIDR 형식 요구사항을 충족하지 않습니다: %s",
"Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
"CIDR for IP: %s should not be empty": "IP %s의 CIDR은 비어 있을 수 없습니다",
"Default code does not match the code's matching rules": "기본 코드가 코드 일치 규칙과 맞지 않습니다",
"DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
"DisplayName is not valid real name": "DisplayName는 유효한 실제 이름이 아닙니다",
"Email already exists": "이메일이 이미 존재합니다",
"Email cannot be empty": "이메일은 비어 있을 수 없습니다",
"Email is invalid": "이메일이 유효하지 않습니다",
"Empty username.": "빈 사용자 이름.",
"Face data does not exist, cannot log in": "얼굴 데이터가 존재하지 않아 로그인할 수 없습니다",
"Face data mismatch": "얼굴 데이터가 일치하지 않습니다",
"Failed to parse client IP: %s": "클라이언트 IP %s을(를) 파싱하는 데 실패했습니다",
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
"Invitation code cannot be blank": "초대 코드는 비워둘 수 없습니다",
"Invitation code exhausted": "초대 코드가 모두 사용되었습니다",
"Invitation code is invalid": "초대 코드가 유효하지 않습니다",
"Invitation code suspended": "초대 코드가 일시 중지되었습니다",
"LDAP user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
"Organization does not exist": "조직은 존재하지 않습니다",
"Password cannot be empty": "비밀번호는 비워둘 수 없습니다",
"Phone already exists": "전화기는 이미 존재합니다",
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
"Phone number is invalid": "전화번호가 유효하지 않습니다",
"Please register using the email corresponding to the invitation code": "초대 코드에 해당하는 이메일로 가입해 주세요",
"Please register using the phone corresponding to the invitation code": "초대 코드에 해당하는 전화번호로 가입해 주세요",
"Please register using the username corresponding to the invitation code": "초대 코드에 해당하는 사용자 이름으로 가입해 주세요",
"Session outdated, please login again": "세션이 만료되었습니다. 다시 로그인해주세요",
"The invitation code has already been used": "초대 코드는 이미 사용되었습니다",
"The password must contain at least one special character": "비밀번호에는 하나 이상의 특수 문자가 포함되어야 합니다",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "비밀번호에는 하나 이상의 대문자, 소문자 및 숫자가 포함되어야 합니다",
"The password must have at least 6 characters": "비밀번호는 최소 6자 이상이어야 합니다",
"The password must have at least 8 characters": "비밀번호는 최소 8자 이상이어야 합니다",
"The password must not contain any repeated characters": "비밀번호에는 반복되는 문자가 포함될 수 없습니다",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "사용자가 삭제되어 로그인에 사용할 수 없습니다. 관리자에게 문의하세요",
"The user is forbidden to sign in, please contact the administrator": "사용자는 로그인이 금지되어 있습니다. 관리자에게 문의하십시오",
"The user: %s doesn't exist in LDAP server": "LDAP 서버에 사용자 %s이(가) 존재하지 않습니다",
"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\\\"의 가입 항목 정규식과 일치하지 않습니다",
"Username already exists": "사용자 이름이 이미 존재합니다",
"Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
"Username cannot contain white spaces": "사용자 이름에는 공백이 포함될 수 없습니다",
"Username cannot start with a digit": "사용자 이름은 숫자로 시작할 수 없습니다",
"Username is too long (maximum is 255 characters).": "사용자 이름이 너무 깁니다 (최대 255자).",
"Username must have at least 2 characters": "사용자 이름은 적어도 2개의 문자가 있어야 합니다",
"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.": "사용자 이름은 이메일 형식을 지원합니다. 또한 사용자 이름은 영숫자, 밑줄 또는 하이픈만 포함할 수 있으며, 연속된 하이픈이나 밑줄은 불가능하며 하이픈이나 밑줄로 시작하거나 끝날 수 없습니다. 이메일 형식에도 주의하세요.",
"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 region is not allow to signup by phone": "당신의 지역은 전화로 가입할 수 없습니다",
"password or code is incorrect": "비밀번호 또는 코드가 올바르지 않습니다",
"password or code is incorrect, you have %s remaining chances": "암호 또는 코드가 올바르지 않습니다. %s 번의 기회가 남아 있습니다",
"unsupported password type: %s": "지원되지 않는 암호 유형: %s"
},
"enforcer": {
"the adapter: %s is not found": "어댑터 %s을(를) 찾을 수 없습니다"
},
"general": {
"Failed to import groups": "그룹 가져오기 실패",
"Failed to import users": "사용자 가져오기를 실패했습니다",
"Missing parameter": "누락된 매개변수",
"Only admin user can specify user": "관리자만 사용자를 지정할 수 있습니다",
"Please login first": "먼저 로그인 하십시오",
"The organization: %s should have one application at least": "조직 %s에는 최소 하나의 애플리케이션이 있어야 합니다",
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
"Wrong userId": "잘못된 사용자 ID입니다",
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요",
"this operation is not allowed in demo mode": "이 작업은 데모 모드에서 허용되지 않습니다",
"this operation requires administrator to perform": "이 작업은 관리자 권한이 필요합니다"
},
"invitation": {
"Invitation %s does not exist": "초대 %s이(가) 존재하지 않습니다"
},
"ldap": {
"Ldap server exist": "LDAP 서버가 존재합니다"
},
"link": {
"Please link first": "먼저 링크해주세요",
"This application has no providers": "이 애플리케이션에는 제공자가 없습니다",
"This application has no providers of type": "이 응용 프로그램은 타입의 공급자가 없습니다",
"This provider can't be unlinked": "이 공급자는 연결이 해제될 수 없습니다",
"You are not the global admin, you can't unlink other users": "당신은 전역 관리자가 아니므로 다른 사용자와의 연결을 해제할 수 없습니다",
"You can't unlink yourself, you are not a member of any application": "당신은 어떤 애플리케이션의 회원이 아니기 때문에 스스로 링크를 해제할 수 없습니다"
},
"organization": {
"Only admin can modify the %s.": "관리자만 %s을(를) 수정할 수 있습니다.",
"The %s is immutable.": "%s 는 변경할 수 없습니다.",
"Unknown modify rule %s.": "미확인 수정 규칙 %s.",
"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\\\"이(가) 존재하지 않습니다"
},
"provider": {
"Invalid application id": "잘못된 애플리케이션 ID입니다",
"the provider: %s does not exist": "제공자 %s가 존재하지 않습니다"
},
"resource": {
"User is nil for tag: avatar": "사용자는 아바타 태그에 대해 nil입니다",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "사용자 이름 또는 전체 파일 경로가 비어 있습니다: 사용자 이름 = %s, 전체 파일 경로 = %s"
},
"saml": {
"Application %s not found": "어플리케이션 %s을(를) 찾을 수 없습니다"
},
"saml_sp": {
"provider %s's category is not SAML": "제공 업체 %s의 카테고리는 SAML이 아닙니다"
},
"service": {
"Empty parameters for emailForm: %v": "이메일 형식의 빈 매개 변수: %v",
"Invalid Email receivers: %s": "잘못된 이메일 수신자: %s",
"Invalid phone receivers: %s": "잘못된 전화 수신자: %s"
},
"storage": {
"The objectKey: %s is not allowed": "객체 키 : %s 는 허용되지 않습니다",
"The provider type: %s is not supported": "제공자 유형: %s은/는 지원되지 않습니다"
},
"subscription": {
"Error": "오류"
},
"token": {
"Grant_type: %s is not supported in this application": "그랜트 유형: %s은(는) 이 어플리케이션에서 지원되지 않습니다",
"Invalid application or wrong clientSecret": "잘못된 어플리케이션 또는 올바르지 않은 클라이언트 시크릿입니다",
"Invalid client_id": "잘못된 클라이언트 ID입니다",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "허용된 Redirect URI 목록에서 %s이(가) 존재하지 않습니다",
"Token not found, invalid accessToken": "토큰을 찾을 수 없습니다. 잘못된 액세스 토큰입니다"
},
"user": {
"Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
"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.": "새 비밀번호에는 공백이 포함될 수 없습니다.",
"The new password must be different from your current password": "새 비밀번호는 현재 비밀번호와 달라야 합니다",
"the user's owner and name should not be empty": "사용자의 소유자와 이름은 비워둘 수 없습니다"
},
"util": {
"No application is found for userId: %s": "어플리케이션을 찾을 수 없습니다. userId: %s",
"No provider for category: %s is found for application: %s": "어플리케이션 %s에서 %s 카테고리를 위한 공급자가 찾을 수 없습니다",
"The provider: %s is not found": "제공자: %s를 찾을 수 없습니다"
},
"verification": {
"Invalid captcha provider.": "잘못된 captcha 제공자입니다.",
"Phone number is invalid in your region %s": "전화 번호가 당신의 지역 %s에서 유효하지 않습니다",
"The verification code has already been used!": "인증 코드는 이미 사용되었습니다!",
"The verification code has not been sent yet!": "인증 코드가 아직 전송되지 않았습니다!",
"Turing test failed.": "튜링 테스트 실패.",
"Unable to get the email modify rule.": "이메일 수정 규칙을 가져올 수 없습니다.",
"Unable to get the phone modify rule.": "전화 수정 규칙을 가져올 수 없습니다.",
"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": "애플리케이션 %s의 \\\"제공자\\\" 목록에 SMS 제공자를 추가해 주세요",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "애플리케이션 %s의 \\\"제공자\\\" 목록에 이메일 제공자를 추가해 주세요",
"the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
},
"webauthn": {
"Found no credentials for this user": "이 사용자에 대한 자격 증명을 찾을 수 없습니다",
"Please call WebAuthnSigninBegin first": "WebAuthnSigninBegin을 먼저 호출해주세요"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Gagal tambah pengguna",
"Get init score failed, error: %w": "Gagal dapatkan skor awal, ralat: %w",
"Please sign out first": "Sila log keluar dahulu",
"The application does not allow to sign up new account": "Aplikasi tidak benarkan pendaftaran akaun baharu"
},
"auth": {
"Challenge method should be S256": "Kaedah cabaran mesti S256",
"DeviceCode Invalid": "Kod Peranti Tidak Sah",
"Failed to create user, user information is invalid: %s": "Gagal cipta pengguna, maklumat tidak sah: %s",
"Failed to login in: %s": "Gagal log masuk: %s",
"Invalid token": "Token tidak sah",
"State expected: %s, but got: %s": "Jangkaan keadaan: %s, tetapi dapat: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar melalui %s, sila guna cara lain",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar, sila hubungi sokongan IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) sudah dipautkan kepada akaun lain: %s (%s)",
"The application: %s does not exist": "Aplikasi: %s tidak wujud",
"The application: %s has disabled users to signin": "Aplikasi: %s telah melumpuhkan pengguna untuk log masuk",
"The group: %s does not exist": "Kumpulan: %s tidak wujud",
"The login method: login with LDAP is not enabled for the application": "Kaedah log masuk LDAP tidak dibenarkan untuk aplikasi ini",
"The login method: login with SMS is not enabled for the application": "Kaedah log masuk SMS tidak dibenarkan untuk aplikasi ini",
"The login method: login with email is not enabled for the application": "Kaedah log masuk emel tidak dibenarkan untuk aplikasi ini",
"The login method: login with face is not enabled for the application": "Kaedah log masuk muka tidak dibenarkan untuk aplikasi ini",
"The login method: login with password is not enabled for the application": "Kaedah log masuk kata laluan tidak dibenarkan untuk aplikasi ini",
"The organization: %s does not exist": "Organisasi: %s tidak wujud",
"The organization: %s has disabled users to signin": "Organisasi: %s telah melumpuhkan pengguna untuk log masuk",
"The plan: %s does not exist": "Pelan: %s tidak wujud",
"The pricing: %s does not exist": "Harga: %s tidak wujud",
"The pricing: %s does not have plan: %s": "Harga: %s tidak mempunyai pelan: %s",
"The provider: %s does not exist": "Pembekal: %s tidak wujud",
"The provider: %s is not enabled for the application": "Pembekal: %s tidak dibenarkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak dibenarkan",
"Unknown authentication type (not password or provider), form = %s": "Jenis pengesahan tidak diketahui (bukan kata laluan atau pembekal), borang = %s",
"User's tag: %s is not listed in the application's tags": "Tag pengguna: %s tidak tersenarai dalam tag aplikasi",
"UserCode Expired": "Kod Pengguna Tamat",
"UserCode Invalid": "Kod Pengguna Tidak Sah",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Pengguna berbayar %s tiada langganan aktif atau tertunda dan aplikasi: %s tiada harga lalai",
"the application for user %s is not found": "Aplikasi untuk pengguna %s tidak ditemui",
"the organization: %s is not found": "Organisasi: %s tidak ditemui"
},
"cas": {
"Service %s and %s do not match": "Perkhidmatan %s dan %s tidak sepadan"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s tidak memenuhi format CIDR: %s",
"Affiliation cannot be blank": "Afiliasi tidak boleh kosong",
"CIDR for IP: %s should not be empty": "CIDR untuk IP: %s tidak boleh kosong",
"Default code does not match the code's matching rules": "Kod lalai tidak sepadan dengan peraturan padanan",
"DisplayName cannot be blank": "Nama paparan tidak boleh kosong",
"DisplayName is not valid real name": "Nama paparan bukan nama sebenar yang sah",
"Email already exists": "Emel sudah wujud",
"Email cannot be empty": "Emel tidak boleh kosong",
"Email is invalid": "Emel tidak sah",
"Empty username.": "Nama pengguna kosong.",
"Face data does not exist, cannot log in": "Data muka tiada, tidak boleh log masuk",
"Face data mismatch": "Data muka tidak sepadan",
"Failed to parse client IP: %s": "Gagal huraikan IP klien: %s",
"FirstName cannot be blank": "Nama pertama tidak boleh kosong",
"Invitation code cannot be blank": "Kod jemputan tidak boleh kosong",
"Invitation code exhausted": "Kod jemputan habis",
"Invitation code is invalid": "Kod jemputan tidak sah",
"Invitation code suspended": "Kod jemputan digantung",
"LDAP user name or password incorrect": "Nama pengguna atau kata laluan LDAP salah",
"LastName cannot be blank": "Nama terakhir tidak boleh kosong",
"Multiple accounts with same uid, please check your ldap server": "Beberapa akaun dengan uid sama, sila semak pelayan ldap anda",
"Organization does not exist": "Organisasi tidak wujud",
"Password cannot be empty": "Kata laluan tidak boleh kosong",
"Phone already exists": "Telefon sudah wujud",
"Phone cannot be empty": "Telefon tidak boleh kosong",
"Phone number is invalid": "Nombor telefon tidak sah",
"Please register using the email corresponding to the invitation code": "Sila daftar dengan emel yang sepadan dengan kod jemputan",
"Please register using the phone corresponding to the invitation code": "Sila daftar dengan telefon yang sepadan dengan kod jemputan",
"Please register using the username corresponding to the invitation code": "Sila daftar dengan nama pengguna yang sepadan dengan kod jemputan",
"Session outdated, please login again": "Sesi tamat, sila log masuk semula",
"The invitation code has already been used": "Kod jemputan sudah digunakan",
"The password must contain at least one special character": "Kata laluan mesti mengandungi sekurang-kurangnya satu aksara khas",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Kata laluan mesti mengandungi sekurang-kurangnya satu huruf besar, satu huruf kecil dan satu digit",
"The password must have at least 6 characters": "Kata laluan mesti mempunyai sekurang-kurangnya 6 aksara",
"The password must have at least 8 characters": "Kata laluan mesti mempunyai sekurang-kurangnya 8 aksara",
"The password must not contain any repeated characters": "Kata laluan tidak boleh mengandungi aksara berulang",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Pengguna telah dipadamkan dan tidak boleh digunakan untuk log masuk, sila hubungi pentadbir",
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang log masuk, sila hubungi pentadbir",
"The user: %s doesn't exist in LDAP server": "Pengguna: %s tidak wujud dalam pelayan 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.": "Nama pengguna hanya boleh mengandungi alfanumerik, garis bawah atau sengkang, tidak boleh ada sengkang atau garis bawah berturutan, dan tidak boleh bermula atau berakhir dengan sengkang atau garis bawah.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Nilai \\\"%s\\\" untuk medan akaun \\\"%s\\\" tidak sepadan dengan regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Nilai \\\"%s\\\" untuk medan pendaftaran \\\"%s\\\" tidak sepadan dengan regex aplikasi \\\"%s\\\"",
"Username already exists": "Nama pengguna sudah wujud",
"Username cannot be an email address": "Nama pengguna tidak boleh jadi alamat emel",
"Username cannot contain white spaces": "Nama pengguna tidak boleh ada ruang putih",
"Username cannot start with a digit": "Nama pengguna tidak boleh bermula dengan nombor",
"Username is too long (maximum is 255 characters).": "Nama pengguna terlalu panjang (maksimum 255 aksara).",
"Username must have at least 2 characters": "Nama pengguna mesti sekurang-kurangnya 2 aksara",
"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.": "Nama pengguna menyokong format emel. Juga, nama hanya boleh alfanumerik, garis bawah atau sengkang, tanpa berturutan, tidak bermula atau berakhir dengan sengkang atau garis bawah.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda masukkan kata laluan atau kod salah terlalu banyak kali, sila tunggu %d minit dan cuba lagi",
"Your IP address: %s has been banned according to the configuration of: ": "Alamat IP anda: %s telah disekat mengikut konfigurasi: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Kata laluan anda tamat. Sila tetapkan semula dengan klik \\\"Lupa kata laluan\\\"",
"Your region is not allow to signup by phone": "Wilayah anda tidak dibenarkan daftar melalui telefon",
"password or code is incorrect": "kata laluan atau kod salah",
"password or code is incorrect, you have %s remaining chances": "kata laluan atau kod salah, anda ada %s peluang lagi",
"unsupported password type: %s": "jenis kata laluan tidak disokong: %s"
},
"enforcer": {
"the adapter: %s is not found": "penyesuai: %s tidak ditemui"
},
"general": {
"Failed to import groups": "Gagal import kumpulan",
"Failed to import users": "Gagal import pengguna",
"Missing parameter": "Parameter hilang",
"Only admin user can specify user": "Hanya pentadbir boleh tetapkan pengguna",
"Please login first": "Sila log masuk dahulu",
"The organization: %s should have one application at least": "Organisasi: %s mesti ada sekurang-kurangnya satu aplikasi",
"The user: %s doesn't exist": "Pengguna: %s tidak wujud",
"Wrong userId": "ID pengguna salah",
"don't support captchaProvider: ": "tidak sokong penyedia captcha: ",
"this operation is not allowed in demo mode": "operasi ini tidak dibenarkan dalam mod demo",
"this operation requires administrator to perform": "operasi ini perlukan pentadbir untuk jalankan"
},
"invitation": {
"Invitation %s does not exist": "Jemputan %s tidak wujud"
},
"ldap": {
"Ldap server exist": "Pelayan LDAP sudah wujud"
},
"link": {
"Please link first": "Sila pautkan dahulu",
"This application has no providers": "Aplikasi ini tiada pembekal",
"This application has no providers of type": "Aplikasi ini tiada pembekal jenis",
"This provider can't be unlinked": "Pembekal ini tidak boleh diputuskan",
"You are not the global admin, you can't unlink other users": "Anda bukan pentadbir global, anda tidak boleh putuskan pengguna lain",
"You can't unlink yourself, you are not a member of any application": "Anda tidak boleh putuskan diri sendiri, anda bukan ahli mana-mana aplikasi"
},
"organization": {
"Only admin can modify the %s.": "Hanya pentadbir boleh ubah %s.",
"The %s is immutable.": "%s tidak boleh diubah.",
"Unknown modify rule %s.": "Peraturan ubah %s tidak diketahui.",
"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.": "Penambahan pengguna baru ke organisasi 'built-in' (terdalam) kini dinyahdayakan. Ambil perhatian: Semua pengguna dalam organisasi 'built-in' adalah pentadbir global dalam Casdoor. Rujuk dokumen: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Jika anda masih ingin mencipta pengguna untuk organisasi 'built-in', pergi ke halaman tetapan organisasi dan aktifkan pilihan 'Mempunyai kebenaran keistimewaan'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Kebenaran: \\\"%s\\\" tidak wujud"
},
"provider": {
"Invalid application id": "ID aplikasi tidak sah",
"the provider: %s does not exist": "pembekal: %s tidak wujud"
},
"resource": {
"User is nil for tag: avatar": "Pengguna kosong untuk tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nama pengguna atau laluan fail kosong: nama = %s, laluan = %s"
},
"saml": {
"Application %s not found": "Aplikasi %s tidak ditemui"
},
"saml_sp": {
"provider %s's category is not SAML": "kategori pembekal %s bukan SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Parameter kosong untuk borang emel: %v",
"Invalid Email receivers: %s": "Penerima emel tidak sah: %s",
"Invalid phone receivers: %s": "Penerima telefon tidak sah: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Kunci objek: %s tidak dibenarkan",
"The provider type: %s is not supported": "Jenis pembekal: %s tidak disokong"
},
"subscription": {
"Error": "Ralat"
},
"token": {
"Grant_type: %s is not supported in this application": "Jenis pemberian: %s tidak disokong dalam aplikasi ini",
"Invalid application or wrong clientSecret": "Aplikasi tidak sah atau rahsia klien salah",
"Invalid client_id": "ID klien tidak sah",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI alih: %s tiada dalam senarai URI dibenarkan",
"Token not found, invalid accessToken": "Token tidak ditemui, token akses tidak sah"
},
"user": {
"Display name cannot be empty": "Nama paparan tidak boleh kosong",
"MFA email is enabled but email is empty": "MFA emel dibenarkan tetapi emel kosong",
"MFA phone is enabled but phone number is empty": "MFA telefon dibenarkan tetapi nombor telefon kosong",
"New password cannot contain blank space.": "Kata laluan baharu tidak boleh ada ruang kosong.",
"The new password must be different from your current password": "Kata laluan baru mesti berbeza daripada kata laluan semasa anda",
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
},
"util": {
"No application is found for userId: %s": "Tiada aplikasi ditemui untuk ID pengguna: %s",
"No provider for category: %s is found for application: %s": "Tiada pembekal untuk kategori: %s dalam aplikasi: %s",
"The provider: %s is not found": "Pembekal: %s tidak ditemui"
},
"verification": {
"Invalid captcha provider.": "Penyedia captcha tidak sah.",
"Phone number is invalid in your region %s": "Nombor telefon tidak sah dalam wilayah %s",
"The verification code has already been used!": "Kod pengesahan sudah digunakan!",
"The verification code has not been sent yet!": "Kod pengesahan belum dihantar!",
"Turing test failed.": "Ujian Turing gagal.",
"Unable to get the email modify rule.": "Tidak dapat peraturan ubah emel.",
"Unable to get the phone modify rule.": "Tidak dapat peraturan ubah telefon.",
"Unknown type": "Jenis tidak diketahui",
"Wrong verification code!": "Kod pengesahan salah!",
"You should verify your code in %d min!": "Sila sahkan kod anda dalam %d minit!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "sila tambah pembekal SMS ke senarai \"Providers\" untuk aplikasi: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "sila tambah pembekal Emel ke senarai \"Providers\" untuk aplikasi: %s",
"the user does not exist, please sign up first": "pengguna tidak wujud, sila daftar dahulu"
},
"webauthn": {
"Found no credentials for this user": "Tiada kelayakan dijumpai untuk pengguna ini",
"Please call WebAuthnSigninBegin first": "Sila panggil WebAuthnSigninBegin dahulu"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Gebruiker toevoegen mislukt",
"Get init score failed, error: %w": "Initiële score ophalen mislukt, fout: %w",
"Please sign out first": "Log eerst uit",
"The application does not allow to sign up new account": "Aanmelden is niet toegestaan"
},
"auth": {
"Challenge method should be S256": "Challenge-methode moet S256 zijn",
"DeviceCode Invalid": "Ongeldige apparaatcode",
"Failed to create user, user information is invalid: %s": "Aanmaken gebruiker mislukt, gegevens ongeldig: %s",
"Failed to login in: %s": "Inloggen mislukt: %s",
"Invalid token": "Ongeldige token",
"State expected: %s, but got: %s": "Verwachtte state: %s, gekregen: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Gebruiker bestaat niet; aanmelden via %s niet toegestaan, kies andere methode",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Gebruiker bestaat niet; aanmelden niet toegestaan, neem contact op met IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account al gekoppeld aan andere gebruiker: %s (%s)",
"The application: %s does not exist": "Applicatie %s bestaat niet",
"The application: %s has disabled users to signin": "De applicatie: %s heeft het aanmelden van gebruikers uitgeschakeld",
"The group: %s does not exist": "De groep: %s bestaat niet",
"The login method: login with LDAP is not enabled for the application": "LDAP-login uitgeschakeld voor deze app",
"The login method: login with SMS is not enabled for the application": "SMS-login uitgeschakeld voor deze app",
"The login method: login with email is not enabled for the application": "E-mail-login uitgeschakeld voor deze app",
"The login method: login with face is not enabled for the application": "Face-login uitgeschakeld voor deze app",
"The login method: login with password is not enabled for the application": "Wachtwoord-login uitgeschakeld voor deze app",
"The organization: %s does not exist": "Organisatie %s bestaat niet",
"The organization: %s has disabled users to signin": "De organisatie: %s heeft het aanmelden van gebruikers uitgeschakeld",
"The plan: %s does not exist": "Het plan: %s bestaat niet",
"The pricing: %s does not exist": "De prijsstelling: %s bestaat niet",
"The pricing: %s does not have plan: %s": "De prijsstelling: %s heeft geen plan: %s",
"The provider: %s does not exist": "Provider %s bestaat niet",
"The provider: %s is not enabled for the application": "Provider %s uitgeschakeld voor deze app",
"Unauthorized operation": "Niet toegestane handeling",
"Unknown authentication type (not password or provider), form = %s": "Onbekend authenticatietype, form = %s",
"User's tag: %s is not listed in the application's tags": "Tag %s ontbreekt in app-tags",
"UserCode Expired": "Gebruikerscode verlopen",
"UserCode Invalid": "Ongeldige gebruikerscode",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Betaald lid %s zonder actief abonnement en app %s heeft geen standaardprijs",
"the application for user %s is not found": "App voor gebruiker %s niet gevonden",
"the organization: %s is not found": "Organisatie %s niet gevonden"
},
"cas": {
"Service %s and %s do not match": "Services %s en %s komen niet overeen"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s voldoet niet aan CIDR-formaat: %s",
"Affiliation cannot be blank": "Affiliatie is verplicht",
"CIDR for IP: %s should not be empty": "CIDR voor IP %s mag niet leeg zijn",
"Default code does not match the code's matching rules": "Standaardcode komt niet overeen met regels",
"DisplayName cannot be blank": "Weergavenaam is verplicht",
"DisplayName is not valid real name": "Geen geldige echte naam",
"Email already exists": "E-mail bestaat al",
"Email cannot be empty": "E-mail is verplicht",
"Email is invalid": "Ongeldig e-mailadres",
"Empty username.": "Gebruikersnaam ontbreekt",
"Face data does not exist, cannot log in": "Geen face-gegevens, inloggen niet mogelijk",
"Face data mismatch": "Face-gegevens komen niet overeen",
"Failed to parse client IP: %s": "IP parsen mislukt: %s",
"FirstName cannot be blank": "Voornaam is verplicht",
"Invitation code cannot be blank": "Uitnodigingscode is verplicht",
"Invitation code exhausted": "Uitnodigingscode volledig gebruikt",
"Invitation code is invalid": "Ongeldige uitnodigingscode",
"Invitation code suspended": "Uitnodigingscode opgeschort",
"LDAP user name or password incorrect": "LDAP-gebruikersnaam of wachtwoord onjuist",
"LastName cannot be blank": "Achternaam is verplicht",
"Multiple accounts with same uid, please check your ldap server": "Meerdere accounts met zelfde uid, controleer LDAP-server",
"Organization does not exist": "Organisatie bestaat niet",
"Password cannot be empty": "Wachtwoord is verplicht",
"Phone already exists": "Telefoonnummer bestaat al",
"Phone cannot be empty": "Telefoonnummer is verplicht",
"Phone number is invalid": "Ongeldig telefoonnummer",
"Please register using the email corresponding to the invitation code": "Registreer met het e-mailadres dat bij de code hoort",
"Please register using the phone corresponding to the invitation code": "Registreer met het nummer dat bij de code hoort",
"Please register using the username corresponding to the invitation code": "Registreer met de gebruikersnaam die bij de code hoort",
"Session outdated, please login again": "Sessie verlopen, log opnieuw in",
"The invitation code has already been used": "Code al gebruikt",
"The password must contain at least one special character": "Het wachtwoord moet minstens één speciaal teken bevatten",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Het wachtwoord moet minstens één hoofdletter, één kleine letter en één cijfer bevatten",
"The password must have at least 6 characters": "Het wachtwoord moet minstens 6 tekens bevatten",
"The password must have at least 8 characters": "Het wachtwoord moet minstens 8 tekens bevatten",
"The password must not contain any repeated characters": "Het wachtwoord mag geen herhaalde tekens bevatten",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "De gebruiker is verwijderd en kan niet worden gebruikt om in te loggen, neem contact op met de beheerder",
"The user is forbidden to sign in, please contact the administrator": "Inloggen verboden, neem contact op met beheerder",
"The user: %s doesn't exist in LDAP server": "Gebruiker %s ontbreekt in 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.": "Gebruikersnaam: alleen letters, cijfers, _ of -; geen dubbele streepjes/underscores; mag niet beginnen/eindigen met streepje of underscore.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Waarde \"%s\" voor veld \"%s\" voldoet niet aan regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Waarde \"%s\" voor aanmeldveld \"%s\" voldoet niet aan regex van app \"%s\"",
"Username already exists": "Gebruikersnaam bestaat al",
"Username cannot be an email address": "Gebruikersnaam mag geen e-mailadres zijn",
"Username cannot contain white spaces": "Gebruikersnaam mag geen spaties bevatten",
"Username cannot start with a digit": "Gebruikersnaam mag niet met cijfer beginnen",
"Username is too long (maximum is 255 characters).": "Gebruikersnaam te lang (max 255 tekens)",
"Username must have at least 2 characters": "Minimaal 2 tekens",
"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.": "Gebruikersnaam kan e-mail zijn; alleen letters, cijfers, _ of -; geen dubbele streepjes/underscores; mag niet beginnen/eindigen met streepje of underscore.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Te vaak fout wachtwoord/code, wacht %d minuten",
"Your IP address: %s has been banned according to the configuration of: ": "IP-adres %s geblokkeerd volgens configuratie: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Wachtwoord verlopen; klik op \"Wachtwoord vergeten\"",
"Your region is not allow to signup by phone": "Registratie per telefoon niet toegestaan in jouw regio",
"password or code is incorrect": "Verkeerd wachtwoord of code",
"password or code is incorrect, you have %s remaining chances": "Verkeerd wachtwoord/code, nog %s pogingen",
"unsupported password type: %s": "Niet-ondersteund wachtwoordtype: %s"
},
"enforcer": {
"the adapter: %s is not found": "Adapter %s niet gevonden"
},
"general": {
"Failed to import groups": "Groepen importeren mislukt",
"Failed to import users": "Gebruikers importeren mislukt",
"Missing parameter": "Parameter ontbreekt",
"Only admin user can specify user": "Alleen beheerder mag gebruiker opgeven",
"Please login first": "Log eerst in",
"The organization: %s should have one application at least": "Organisatie %s moet minstens één app hebben",
"The user: %s doesn't exist": "Gebruiker %s bestaat niet",
"Wrong userId": "Verkeerde userId",
"don't support captchaProvider: ": "Captcha-provider niet ondersteund: ",
"this operation is not allowed in demo mode": "Handeling niet toegestaan in demo-modus",
"this operation requires administrator to perform": "Alleen beheerder kan deze handeling uitvoeren"
},
"invitation": {
"Invitation %s does not exist": "Uitnodiging %s bestaat niet"
},
"ldap": {
"Ldap server exist": "LDAP-server bestaat al"
},
"link": {
"Please link first": "Koppel eerst",
"This application has no providers": "App heeft geen providers",
"This application has no providers of type": "App heeft geen providers van dit type",
"This provider can't be unlinked": "Provider kan niet ontkoppeld worden",
"You are not the global admin, you can't unlink other users": "Je bent geen globale beheerder, kunt anderen niet ontkoppelen",
"You can't unlink yourself, you are not a member of any application": "Kan jezelf niet ontkoppelen; geen lid van een app"
},
"organization": {
"Only admin can modify the %s.": "Alleen beheerder kan %s wijzigen.",
"The %s is immutable.": "%s kan niet gewijzigd worden.",
"Unknown modify rule %s.": "Onbekende wijzigingsregel %s.",
"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.": "Het toevoegen van een nieuwe gebruiker aan de 'built-in' (ingebouwde) organisatie is momenteel uitgeschakeld. Let op: Alle gebruikers in de 'built-in' organisatie zijn globale beheerders in Casdoor. Raadpleeg de documentatie: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Als u toch een gebruiker wilt maken voor de 'built-in' organisatie, ga naar de instellingenpagina van de organisatie en schakel de optie 'Heeft bevoegdheidsgoedkeuring' in."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Permissie \"%s\" bestaat niet"
},
"provider": {
"Invalid application id": "Ongeldige app-id",
"the provider: %s does not exist": "Provider %s bestaat niet"
},
"resource": {
"User is nil for tag: avatar": "Gebruiker ontbreekt voor avatar-tag",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Gebruikersnaam of bestandspad leeg: %s / %s"
},
"saml": {
"Application %s not found": "App %s niet gevonden"
},
"saml_sp": {
"provider %s's category is not SAML": "Provider %s is geen SAML-type"
},
"service": {
"Empty parameters for emailForm: %v": "Lege parameters voor e-mailformulier: %v",
"Invalid Email receivers: %s": "Ongeldige e-mailontvangers: %s",
"Invalid phone receivers: %s": "Ongeldige telefoonontvangers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "ObjectKey %s niet toegestaan",
"The provider type: %s is not supported": "Providertype %s niet ondersteund"
},
"subscription": {
"Error": "Fout"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type %s wordt niet ondersteund",
"Invalid application or wrong clientSecret": "Ongeldige app of verkeerde clientSecret",
"Invalid client_id": "Ongeldige client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect-URI %s staat niet op de toegestane lijst",
"Token not found, invalid accessToken": "Token niet gevonden; ongeldige accessToken"
},
"user": {
"Display name cannot be empty": "Weergavenaam is verplicht",
"MFA email is enabled but email is empty": "MFA-e-mail ingeschakeld maar e-mailadres leeg",
"MFA phone is enabled but phone number is empty": "MFA-telefoon ingeschakeld maar nummer leeg",
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten",
"The new password must be different from your current password": "Het nieuwe wachtwoord moet verschillen van uw huidige wachtwoord",
"the user's owner and name should not be empty": "Eigenaar en naam van gebruiker mogen niet leeg zijn"
},
"util": {
"No application is found for userId: %s": "Geen app gevonden voor userId %s",
"No provider for category: %s is found for application: %s": "Geen provider voor categorie %s in app %s",
"The provider: %s is not found": "Provider %s niet gevonden"
},
"verification": {
"Invalid captcha provider.": "Ongeldige captcha-provider",
"Phone number is invalid in your region %s": "Telefoonnummer ongeldig in regio %s",
"The verification code has already been used!": "Verificatiecode al gebruikt!",
"The verification code has not been sent yet!": "Verificatiecode nog niet verzonden!",
"Turing test failed.": "Turing-test mislukt",
"Unable to get the email modify rule.": "Kan e-mail-wijzigingsregel niet ophalen",
"Unable to get the phone modify rule.": "Kan telefoon-wijzigingsregel niet ophalen",
"Unknown type": "Onbekend type",
"Wrong verification code!": "Verkeerde verificatiecode!",
"You should verify your code in %d min!": "Verifieer binnen %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Voeg een SMS-provider toe aan de Providers-lijst van app %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Voeg een e-mailprovider toe aan de Providers-lijst van app %s",
"the user does not exist, please sign up first": "Gebruiker bestaat niet; meld je eerst aan"
},
"webauthn": {
"Found no credentials for this user": "Geen inloggegevens gevonden voor deze gebruiker",
"Please call WebAuthnSigninBegin first": "Roep eerst WebAuthnSigninBegin aan"
}
}

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "Użytkownikowi zabroniono logowania, skontaktuj się z administratorem",
"The user: %s doesn't exist in LDAP server": "Użytkownik: %s nie istnieje w serwerze 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.": "Nazwa użytkownika może zawierać tylko znaki alfanumeryczne, podkreślenia lub myślniki, nie może mieć kolejnych myślników lub podkreśleń i nie może zaczynać się ani kończyć myślnikiem lub podkreśleniem.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Wartość \\\"%s\\\" dla pola konta \\\"%s\\\" nie pasuje do wyrażenia regularnego elementu konta",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Wartość \\\"%s\\\" dla pola rejestracji \\\"%s\\\" nie pasuje do wyrażenia regularnego elementu rejestracji aplikacji \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Wartość \"%s\" dla pola konta \"%s\" nie pasuje do wyrażenia regularnego elementu konta",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Wartość \"%s\" dla pola rejestracji \"%s\" nie pasuje do wyrażenia regularnego elementu rejestracji aplikacji \"%s\"",
"Username already exists": "Nazwa użytkownika już istnieje",
"Username cannot be an email address": "Nazwa użytkownika nie może być adresem email",
"Username cannot contain white spaces": "Nazwa użytkownika nie może zawierać spacji",
@@ -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.": "Nazwa użytkownika obsługuje format email. Również nazwa użytkownika może zawierać tylko znaki alfanumeryczne, podkreślenia lub myślniki, nie może mieć kolejnych myślników lub podkreśleń i nie może zaczynać się ani kończyć myślnikiem lub podkreśleniem. Zwróć też uwagę na format email.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Wprowadziłeś złe hasło lub kod zbyt wiele razy, poczekaj %d minut i spróbuj ponownie",
"Your IP address: %s has been banned according to the configuration of: ": "Twój adres IP: %s został zablokowany zgodnie z konfiguracją: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Twoje hasło wygasło. Zresetuj hasło klikając \\\"Zapomniałem hasła\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Twoje hasło wygasło. Zresetuj hasło klikając \"Zapomniałem hasła\"",
"Your region is not allow to signup by phone": "Twój region nie pozwala na rejestrację przez telefon",
"password or code is incorrect": "hasło lub kod jest nieprawidłowe",
"password or code is incorrect, you have %s remaining chances": "hasło lub kod jest nieprawidłowe, masz jeszcze %s prób",
@@ -137,7 +137,7 @@
"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.": "Dodawanie nowego użytkownika do organizacji „built-in' (wbudowanej) jest obecnie wyłączone. Należy zauważyć, że wszyscy użytkownicy w organizacji „built-in' są globalnymi administratorami w Casdoor. Zobacz dokumentację: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Jeśli nadal chcesz utworzyć użytkownika dla organizacji „built-in', przejdź do strony ustawień organizacji i włącz opcję „Ma zgodę na uprawnienia'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Uprawnienie: \\\"%s\\\" nie istnieje"
"The permission: \"%s\" doesn't exist": "Uprawnienie: \"%s\" nie istnieje"
},
"provider": {
"Invalid application id": "Nieprawidłowe id aplikacji",
@@ -196,8 +196,8 @@
"Unknown type": "Nieznany typ",
"Wrong verification code!": "Zły kod weryfikacyjny!",
"You should verify your code in %d min!": "Powinieneś zweryfikować swój kod w ciągu %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "proszę dodać dostawcę SMS do listy \\\"Providers\\\" dla aplikacji: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "proszę dodać dostawcę email do listy \\\"Providers\\\" dla aplikacji: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "proszę dodać dostawcę SMS do listy \"Providers\" dla aplikacji: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "proszę dodać dostawcę email do listy \"Providers\" dla aplikacji: %s",
"the user does not exist, please sign up first": "użytkownik nie istnieje, najpierw się zarejestruj"
},
"webauthn": {

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "O usuário está proibido de entrar, entre em contato com o administrador",
"The user: %s doesn't exist in LDAP server": "O usuário: %s não existe no servidor 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.": "O nome de usuário pode conter apenas caracteres alfanuméricos, sublinhados ou hífens, não pode ter hífens ou sublinhados consecutivos e não pode começar ou terminar com hífen ou sublinhado.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "O valor \\\"%s\\\" para o campo de conta \\\"%s\\\" não corresponde à expressão regular definida",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "O valor \\\"%s\\\" para o campo de registro \\\"%s\\\" não corresponde à expressão regular definida na aplicação \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "O valor \"%s\" para o campo de conta \"%s\" não corresponde à expressão regular definida",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "O valor \"%s\" para o campo de registro \"%s\" não corresponde à expressão regular definida na aplicação \"%s\"",
"Username already exists": "O nome de usuário já existe",
"Username cannot be an email address": "O nome de usuário não pode ser um endereço de e-mail",
"Username cannot contain white spaces": "O nome de usuário não pode conter espaços em branco",
@@ -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.": "O nome de usuário suporta o formato de e-mail. Além disso, pode conter apenas caracteres alfanuméricos, sublinhados ou hífens, não pode ter hífens ou sublinhados consecutivos e não pode começar ou terminar com hífen ou sublinhado. Também preste atenção ao formato do e-mail.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Você digitou a senha ou o código incorretos muitas vezes. Aguarde %d minutos e tente novamente",
"Your IP address: %s has been banned according to the configuration of: ": "Seu endereço IP: %s foi bloqueado de acordo com a configuração de: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Sua senha expirou. Por favor, redefina-a clicando em \\\"Esqueci a senha\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Sua senha expirou. Por favor, redefina-a clicando em \"Esqueci a senha\"",
"Your region is not allow to signup by phone": "Sua região não permite cadastro por telefone",
"password or code is incorrect": "Senha ou código incorreto",
"password or code is incorrect, you have %s remaining chances": "Senha ou código incorreto, você tem %s tentativas restantes",
@@ -137,7 +137,7 @@
"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.": "A adição de novos usuários à organização 'built-in' está desativada. Observe que todos os usuários nessa organização são administradores globais no Casdoor. Consulte a documentação: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Se ainda desejar criar um usuário, vá até a página de configurações da organização e habilite a opção 'Possui consentimento de privilégios'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "A permissão: \\\"%s\\\" não existe"
"The permission: \"%s\" doesn't exist": "A permissão: \"%s\" não existe"
},
"provider": {
"Invalid application id": "ID de aplicativo inválido",
@@ -196,8 +196,8 @@
"Unknown type": "Tipo desconhecido",
"Wrong verification code!": "Código de verificação incorreto!",
"You should verify your code in %d min!": "Você deve verificar seu código em %d minuto(s)!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Adicione um provedor de SMS à lista \\\"Providers\\\" do aplicativo: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Adicione um provedor de e-mail à lista \\\"Providers\\\" do aplicativo: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "Adicione um provedor de SMS à lista \"Providers\" do aplicativo: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "Adicione um provedor de e-mail à lista \"Providers\" do aplicativo: %s",
"the user does not exist, please sign up first": "O usuário não existe, cadastre-se primeiro"
},
"webauthn": {

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Не удалось добавить пользователя",
"Get init score failed, error: %w": "Не удалось получить исходный балл, ошибка: %w",
"Please sign out first": "Пожалуйста, сначала выйдите из системы",
"The application does not allow to sign up new account": "Приложение не позволяет зарегистрироваться новому аккаунту"
},
"auth": {
"Challenge method should be S256": "Метод проверки должен быть S256",
"DeviceCode Invalid": "Неверный код устройства",
"Failed to create user, user information is invalid: %s": "Не удалось создать пользователя, информация о пользователе недействительна: %s",
"Failed to login in: %s": "Не удалось войти в систему: %s",
"Invalid token": "Недействительный токен",
"State expected: %s, but got: %s": "Ожидался статус: %s, но получен: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Аккаунт провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован через %s, пожалуйста, используйте другой способ регистрации",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
"The application: %s does not exist": "Приложение: %s не существует",
"The application: %s has disabled users to signin": "Приложение: %s отключило вход пользователей",
"The group: %s does not exist": "Группа: %s не существует",
"The login method: login with LDAP is not enabled for the application": "Метод входа через LDAP отключен для этого приложения",
"The login method: login with SMS is not enabled for the application": "Метод входа через SMS отключен для этого приложения",
"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 organization: %s does not exist": "Организация: %s не существует",
"The organization: %s has disabled users to signin": "Организация: %s отключила вход пользователей",
"The plan: %s does not exist": "План: %s не существует",
"The pricing: %s does not exist": "Тариф: %s не существует",
"The pricing: %s does not have plan: %s": "Тариф: %s не имеет план: %s",
"The provider: %s does not exist": "Провайдер: %s не существует",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
"Unauthorized operation": "Несанкционированная операция",
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
"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 не имеет цены по умолчанию",
"the application for user %s is not found": "Приложение для пользователя %s не найдено",
"the organization: %s is not found": "Организация: %s не найдена"
},
"cas": {
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s не соответствует требованиям формата CIDR: %s",
"Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
"CIDR for IP: %s should not be empty": "CIDR для IP: %s не должен быть пустым",
"Default code does not match the code's matching rules": "Код по умолчанию не соответствует правилам соответствия кода",
"DisplayName cannot be blank": "Имя отображения не может быть пустым",
"DisplayName is not valid real name": "DisplayName не является действительным именем",
"Email already exists": "Электронная почта уже существует",
"Email cannot be empty": "Электронная почта не может быть пустой",
"Email is invalid": "Адрес электронной почты недействительный",
"Empty username.": "Пустое имя пользователя.",
"Face data does not exist, cannot log in": "Данные лица отсутствуют, вход невозможен",
"Face data mismatch": "Несоответствие данных лица",
"Failed to parse client IP: %s": "Не удалось разобрать IP клиента: %s",
"FirstName cannot be blank": "Имя не может быть пустым",
"Invitation code cannot be blank": "Код приглашения не может быть пустым",
"Invitation code exhausted": "Код приглашения исчерпан",
"Invitation code is invalid": "Код приглашения недействителен",
"Invitation code suspended": "Код приглашения приостановлен",
"LDAP user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
"LastName cannot be blank": "Фамилия не может быть пустой",
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
"Organization does not exist": "Организация не существует",
"Password cannot be empty": "Пароль не может быть пустым",
"Phone already exists": "Телефон уже существует",
"Phone cannot be empty": "Телефон не может быть пустым",
"Phone number is invalid": "Номер телефона является недействительным",
"Please register using the email corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя электронную почту, соответствующую коду приглашения",
"Please register using the phone corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя номер телефона, соответствующий коду приглашения",
"Please register using the username corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя имя пользователя, соответствующее коду приглашения",
"Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова",
"The invitation code has already been used": "Код приглашения уже использован",
"The password must contain at least one special character": "Пароль должен содержать хотя бы один специальный символ",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль должен содержать хотя бы одну заглавную букву, одну строчную букву и одну цифру",
"The password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
"The password must have at least 8 characters": "Пароль должен содержать не менее 8 символов",
"The password must not contain any repeated characters": "Пароль не должен содержать повторяющихся символов",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Пользователь был удален и не может быть использован для входа, пожалуйста, свяжитесь с администратором",
"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\\\"",
"Username already exists": "Имя пользователя уже существует",
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
"Username cannot start with a digit": "Имя пользователя не может начинаться с цифры",
"Username is too long (maximum is 255 characters).": "Имя пользователя слишком длинное (максимальная длина - 255 символов).",
"Username must have at least 2 characters": "Имя пользователя должно содержать не менее 2 символов",
"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.": "Имя пользователя поддерживает формат электронной почты. Также имя пользователя может содержать только буквенно-цифровые символы, подчеркивания или дефисы, не может иметь последовательных дефисов или подчеркиваний и не может начинаться или заканчиваться дефисом или подчеркиванием. Также обратите внимание на формат электронной почты.",
"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 region is not allow to signup by phone": "Ваш регион не разрешает регистрацию по телефону",
"password or code is incorrect": "пароль или код неверны",
"password or code is incorrect, you have %s remaining chances": "Неправильный пароль или код, у вас осталось %s попыток",
"unsupported password type: %s": "неподдерживаемый тип пароля: %s"
},
"enforcer": {
"the adapter: %s is not found": "адаптер: %s не найден"
},
"general": {
"Failed to import groups": "Не удалось импортировать группы",
"Failed to import users": "Не удалось импортировать пользователей",
"Missing parameter": "Отсутствующий параметр",
"Only admin user can specify user": "Только администратор может указать пользователя",
"Please login first": "Пожалуйста, сначала войдите в систему",
"The organization: %s should have one application at least": "Организация: %s должна иметь хотя бы одно приложение",
"The user: %s doesn't exist": "Пользователь %s не существует",
"Wrong userId": "Неверный идентификатор пользователя",
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
"this operation is not allowed in demo mode": "эта операция недоступна в демонстрационном режиме",
"this operation requires administrator to perform": "эта операция требует прав администратора"
},
"invitation": {
"Invitation %s does not exist": "Приглашение %s не существует"
},
"ldap": {
"Ldap server exist": "LDAP-сервер существует"
},
"link": {
"Please link first": "Пожалуйста, сначала установите ссылку",
"This application has no providers": "Это приложение не имеет провайдеров",
"This application has no providers of type": "Это приложение не имеет провайдеров данного типа",
"This provider can't be unlinked": "Этот провайдер не может быть отсоединен",
"You are not the global admin, you can't unlink other users": "Вы не являетесь глобальным администратором, вы не можете отсоединять других пользователей",
"You can't unlink yourself, you are not a member of any application": "Вы не можете отвязаться, так как вы не являетесь участником никакого приложения"
},
"organization": {
"Only admin can modify the %s.": "Только администратор может изменять %s.",
"The %s is immutable.": "%s неизменяемый.",
"Unknown modify rule %s.": "Неизвестное изменение правила %s.",
"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\\\" не существует"
},
"provider": {
"Invalid application id": "Неверный идентификатор приложения",
"the provider: %s does not exist": "Провайдер: %s не существует"
},
"resource": {
"User is nil for tag: avatar": "Пользователь равен нулю для тега: аватар",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Имя пользователя или полный путь к файлу пусты: имя_пользователя = %s, полный_путь_к_файлу = %s"
},
"saml": {
"Application %s not found": "Приложение %s не найдено"
},
"saml_sp": {
"provider %s's category is not SAML": "Категория провайдера %s не является SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v",
"Invalid Email receivers: %s": "Некорректные получатели электронной почты: %s",
"Invalid phone receivers: %s": "Некорректные получатели телефонных звонков: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Объект «objectKey: %s» не разрешен",
"The provider type: %s is not supported": "Тип провайдера: %s не поддерживается"
},
"subscription": {
"Error": "Ошибка"
},
"token": {
"Grant_type: %s is not supported in this application": "Тип предоставления: %s не поддерживается в данном приложении",
"Invalid application or wrong clientSecret": "Недействительное приложение или неправильный clientSecret",
"Invalid client_id": "Недействительный идентификатор клиента",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI перенаправления: %s не существует в списке разрешенных URI перенаправления",
"Token not found, invalid accessToken": "Токен не найден, недействительный accessToken"
},
"user": {
"Display name cannot be empty": "Отображаемое имя не может быть пустым",
"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.": "Новый пароль не может содержать пробелы.",
"The new password must be different from your current password": "Новый пароль должен отличаться от текущего пароля",
"the user's owner and name should not be empty": "владелец и имя пользователя не должны быть пустыми"
},
"util": {
"No application is found for userId: %s": "Не найдено заявки для пользователя с идентификатором: %s",
"No provider for category: %s is found for application: %s": "Нет провайдера для категории: %s для приложения: %s",
"The provider: %s is not found": "Поставщик: %s не найден"
},
"verification": {
"Invalid captcha provider.": "Недействительный поставщик CAPTCHA.",
"Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s",
"The verification code has already been used!": "Код подтверждения уже использован!",
"The verification code has not been sent yet!": "Код подтверждения еще не был отправлен!",
"Turing test failed.": "Тест Тьюринга не удался.",
"Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
"Unable to get the phone modify rule.": "Невозможно получить правило изменения телефона.",
"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",
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
},
"webauthn": {
"Found no credentials for this user": "Учетные данные для этого пользователя не найдены",
"Please call WebAuthnSigninBegin first": "Пожалуйста, сначала вызовите WebAuthnSigninBegin"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Nepodarilo sa pridať používateľa",
"Get init score failed, error: %w": "Získanie počiatočného skóre zlyhalo, chyba: %w",
"Please sign out first": "Najskôr sa prosím odhláste",
"The application does not allow to sign up new account": "Aplikácia neumožňuje registráciu nového účtu"
},
"auth": {
"Challenge method should be S256": "Metóda výzvy by mala byť S256",
"DeviceCode Invalid": "DeviceCode je neplatný",
"Failed to create user, user information is invalid: %s": "Nepodarilo sa vytvoriť používateľa, informácie o používateľovi sú neplatné: %s",
"Failed to login in: %s": "Prihlásenie zlyhalo: %s",
"Invalid token": "Neplatný token",
"State expected: %s, but got: %s": "Očakávaný stav: %s, ale dostali sme: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet cez %s, prosím použite iný spôsob registrácie",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet, prosím kontaktujte vašu IT podporu",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) je už prepojený s iným účtom: %s (%s)",
"The application: %s does not exist": "Aplikácia: %s neexistuje",
"The application: %s has disabled users to signin": "Aplikácia: %s zakázala prihlasovanie používateľov",
"The group: %s does not exist": "Skupina: %s neexistuje",
"The login method: login with LDAP is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou LDAP nie je pre aplikáciu povolená",
"The login method: login with SMS is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou SMS nie je pre aplikáciu povolená",
"The login method: login with email is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou e-mailu nie je pre aplikáciu povolená",
"The login method: login with face is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou tváre nie je pre aplikáciu povolená",
"The login method: login with password is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou hesla nie je pre aplikáciu povolená",
"The organization: %s does not exist": "Organizácia: %s neexistuje",
"The organization: %s has disabled users to signin": "Organizácia: %s zakázala prihlasovanie používateľov",
"The plan: %s does not exist": "Plán: %s neexistuje",
"The pricing: %s does not exist": "Cenník: %s neexistuje",
"The pricing: %s does not have plan: %s": "Cenník: %s nemá plán: %s",
"The provider: %s does not exist": "Poskytovatel: %s neexistuje",
"The provider: %s is not enabled for the application": "Poskytovateľ: %s nie je pre aplikáciu povolený",
"Unauthorized operation": "Neautorizovaná operácia",
"Unknown authentication type (not password or provider), form = %s": "Neznámy typ autentifikácie (nie heslo alebo poskytovateľ), forma = %s",
"User's tag: %s is not listed in the application's tags": "Štítok používateľa: %s nie je uvedený v štítkoch aplikácie",
"UserCode Expired": "UserCode je expirovaný",
"UserCode Invalid": "UserCode je neplatný",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "platiaci používateľ %s nemá aktívne alebo čakajúce predplatné a aplikácia: %s nemá predvolenú cenovú politiku",
"the application for user %s is not found": "aplikácia pre používateľa %s nebola nájdená",
"the organization: %s is not found": "organizácia: %s nebola nájdená"
},
"cas": {
"Service %s and %s do not match": "Služba %s a %s sa nezhodujú"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s nevyhovuje požiadavkám formátu CIDR: %s",
"Affiliation cannot be blank": "Príslušnosť nemôže byť prázdna",
"CIDR for IP: %s should not be empty": "CIDR pre IP: %s nesmie byť prázdny",
"Default code does not match the code's matching rules": "Predvolený kód nezodpovedá pravidlám zodpovedania kódu",
"DisplayName cannot be blank": "Zobrazované meno nemôže byť prázdne",
"DisplayName is not valid real name": "Zobrazované meno nie je platné skutočné meno",
"Email already exists": "E-mail už existuje",
"Email cannot be empty": "E-mail nemôže byť prázdny",
"Email is invalid": "E-mail je neplatný",
"Empty username.": "Prázdne používateľské meno.",
"Face data does not exist, cannot log in": "Dáta o tvári neexistujú, nemožno sa prihlásiť",
"Face data mismatch": "Nesúlad dát o tvári",
"Failed to parse client IP: %s": "Zlyhalo parsovanie IP adresy klienta: %s",
"FirstName cannot be blank": "Meno nemôže byť prázdne",
"Invitation code cannot be blank": "Kód pozvania nemôže byť prázdny",
"Invitation code exhausted": "Kód pozvania bol vyčerpaný",
"Invitation code is invalid": "Kód pozvania je neplatný",
"Invitation code suspended": "Kód pozvania bol pozastavený",
"LDAP user name or password incorrect": "LDAP používateľské meno alebo heslo sú nesprávne",
"LastName cannot be blank": "Priezvisko nemôže byť prázdne",
"Multiple accounts with same uid, please check your ldap server": "Viacero účtov s rovnakým uid, skontrolujte svoj ldap server",
"Organization does not exist": "Organizácia neexistuje",
"Password cannot be empty": "Heslo nemôže byť prázdne",
"Phone already exists": "Telefón už existuje",
"Phone cannot be empty": "Telefón nemôže byť prázdny",
"Phone number is invalid": "Telefónne číslo je neplatné",
"Please register using the email corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou e-mailu zodpovedajúceho kódu pozvania",
"Please register using the phone corresponding to the invitation code": "Prosím zaregistrujte se pomocí telefonu odpovídajícího pozvánkovému kódu",
"Please register using the username corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou používateľského mena zodpovedajúceho kódu pozvania",
"Session outdated, please login again": "Relácia je zastaraná, prosím, prihláste sa znova",
"The invitation code has already been used": "Kód pozvania už bol použitý",
"The password must contain at least one special character": "Heslo musí obsahovať aspoň jeden špeciálny znak",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Heslo musí obsahovať aspoň jedno veľké písmeno, jedno malé písmeno a jednu číslicu",
"The password must have at least 6 characters": "Heslo musí mať aspoň 6 znakov",
"The password must have at least 8 characters": "Heslo musí mať aspoň 8 znakov",
"The password must not contain any repeated characters": "Heslo nesmie obsahovať opakujúce sa znaky",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Používateľ bol odstránený a nie je možné ho použiť na prihlásenie, kontaktujte prosím správcu",
"The user is forbidden to sign in, please contact the administrator": "Používateľovi je zakázané prihlásenie, prosím, kontaktujte administrátora",
"The user: %s doesn't exist in LDAP server": "Používateľ: %s neexistuje na LDAP serveri",
"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.": "Používateľské meno môže obsahovať iba alfanumerické znaky, podtržníky alebo pomlčky, nemôže obsahovať po sebe idúce pomlčky alebo podtržníky a nemôže začínať alebo končiť pomlčkou alebo podtržníkom.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hodnota \\\"%s\\\" pre pole účtu \\\"%s\\\" nezodpovedá regulárnemu výrazu položky účtu",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Hodnota \\\"%s\\\" pre pole registrácie \\\"%s\\\" nezodpovedá regulárnemu výrazu položky registrácie aplikácie \\\"%s\\\"",
"Username already exists": "Používateľské meno už existuje",
"Username cannot be an email address": "Používateľské meno nemôže byť e-mailová adresa",
"Username cannot contain white spaces": "Používateľské meno nemôže obsahovať medzery",
"Username cannot start with a digit": "Používateľské meno nemôže začínať číslicou",
"Username is too long (maximum is 255 characters).": "Používateľské meno je príliš dlhé (maximum je 255 znakov).",
"Username must have at least 2 characters": "Používateľské meno musí mať aspoň 2 znaky",
"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.": "Používateľské meno podporuje formát e-mailu. Okrem toho môže používateľské meno obsahovať iba alfanumerické znaky, podčiarkovníky alebo pomlčky, nemôže mať po sebe idúce pomlčky alebo podčiarkovníky a nemôže začínať alebo končiť pomlčkou alebo podčiarkovníkom. Dajte tiež pozor na formát e-mailu.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali ste nesprávne heslo alebo kód príliš veľa krát, prosím, počkajte %d minút a skúste to znova",
"Your IP address: %s has been banned according to the configuration of: ": "Vaša IP adresa: %s bola zablokovaná podľa konfigurácie: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Vaše heslo je expirované. Prosím, resetujte svoje heslo kliknutím na \\\"Zabudnuté heslo\\\"",
"Your region is not allow to signup by phone": "Váš región neumožňuje registráciu cez telefón",
"password or code is incorrect": "heslo alebo kód je nesprávne",
"password or code is incorrect, you have %s remaining chances": "heslo alebo kód je nesprávne, máte %s zostávajúcich pokusov",
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
},
"enforcer": {
"the adapter: %s is not found": "adaptér: %s nebol nájdený"
},
"general": {
"Failed to import groups": "Import skupín zlyhal",
"Failed to import users": "Nepodarilo sa importovať používateľov",
"Missing parameter": "Chýbajúci parameter",
"Only admin user can specify user": "Iba administrátor môže určiť používateľa",
"Please login first": "Najskôr sa prosím prihláste",
"The organization: %s should have one application at least": "Organizácia: %s by mala mať aspoň jednu aplikáciu",
"The user: %s doesn't exist": "Používateľ: %s neexistuje",
"Wrong userId": "Nesprávne ID používateľa",
"don't support captchaProvider: ": "nepodporuje captchaProvider: ",
"this operation is not allowed in demo mode": "táto operácia nie je povolená v demo režime",
"this operation requires administrator to perform": "táto operácia vyžaduje vykonanie administrátorom"
},
"invitation": {
"Invitation %s does not exist": "Pozvánka %s neexistuje"
},
"ldap": {
"Ldap server exist": "LDAP server existuje"
},
"link": {
"Please link first": "Najskôr sa prosím prepojte",
"This application has no providers": "Táto aplikácia nemá žiadnych poskytovateľov",
"This application has no providers of type": "Táto aplikácia nemá poskytovateľov typu",
"This provider can't be unlinked": "Tento poskytovateľ nemôže byť odpojený",
"You are not the global admin, you can't unlink other users": "Nie ste globálny administrátor, nemôžete odpojiť iných používateľov",
"You can't unlink yourself, you are not a member of any application": "Nemôžete sa odpojiť, nie ste členom žiadnej aplikácie"
},
"organization": {
"Only admin can modify the %s.": "Len administrátor môže upravovať %s.",
"The %s is immutable.": "%s je nemenný.",
"Unknown modify rule %s.": "Neznáme pravidlo úprav %s.",
"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.": "Pridávanie nového používateľa do organizácie „built-in' (vložená) je momentálne zakázané. Všimnite si, že všetci používatelia v organizácii „built-in' sú globálni správcovia v Casdoor. Pozrite si dokumentáciu: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Ak stále chcete vytvoriť používateľa pre organizáciu „built-in', prejdite na stránku nastavení organizácie a povoľte možnosť „Má súhlas s privilégiami'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Povolenie: \\\"%s\\\" neexistuje"
},
"provider": {
"Invalid application id": "Neplatné id aplikácie",
"the provider: %s does not exist": "poskytovateľ: %s neexistuje"
},
"resource": {
"User is nil for tag: avatar": "Používateľ je nil pre tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Používateľské meno alebo fullFilePath je prázdny: používateľské meno = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Aplikácia %s nebola nájdená"
},
"saml_sp": {
"provider %s's category is not SAML": "kategória poskytovateľa %s nie je SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Prázdne parametre pre emailForm: %v",
"Invalid Email receivers: %s": "Neplatní príjemcovia e-mailu: %s",
"Invalid phone receivers: %s": "Neplatní príjemcovia telefónu: %s"
},
"storage": {
"The objectKey: %s is not allowed": "objectKey: %s nie je povolený",
"The provider type: %s is not supported": "Typ poskytovateľa: %s nie je podporovaný"
},
"subscription": {
"Error": "Chyba"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s nie je podporovaný v tejto aplikácii",
"Invalid application or wrong clientSecret": "Neplatná aplikácia alebo nesprávny clientSecret",
"Invalid client_id": "Neplatný client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s neexistuje v zozname povolených Redirect URI",
"Token not found, invalid accessToken": "Token nebol nájdený, neplatný accessToken"
},
"user": {
"Display name cannot be empty": "Zobrazované meno nemôže byť prázdne",
"MFA email is enabled but email is empty": "MFA e-mail je zapnutý, ale e-mail je prázdny",
"MFA phone is enabled but phone number is empty": "MFA telefón je zapnutý, ale telefónne číslo je prázdne",
"New password cannot contain blank space.": "Nové heslo nemôže obsahovať medzery.",
"The new password must be different from your current password": "Nové heslo sa musí líšiť od vášho aktuálneho hesla",
"the user's owner and name should not be empty": "vlastník a meno používateľa nesmú byť prázdne"
},
"util": {
"No application is found for userId: %s": "Nebola nájdená žiadna aplikácia pre userId: %s",
"No provider for category: %s is found for application: %s": "Pre aplikáciu: %s nebol nájdený žiadny poskytovateľ pre kategóriu: %s",
"The provider: %s is not found": "Poskytovateľ: %s nebol nájdený"
},
"verification": {
"Invalid captcha provider.": "Neplatný captcha poskytovateľ.",
"Phone number is invalid in your region %s": "Telefónne číslo je neplatné vo vašom regióne %s",
"The verification code has already been used!": "Overovací kód bol už použitý!",
"The verification code has not been sent yet!": "Overovací kód ešte nebol odoslaný!",
"Turing test failed.": "Test Turinga zlyhal.",
"Unable to get the email modify rule.": "Nepodarilo sa získať pravidlo úpravy e-mailu.",
"Unable to get the phone modify rule.": "Nepodarilo sa získať pravidlo úpravy telefónu.",
"Unknown type": "Neznámy typ",
"Wrong verification code!": "Nesprávny overovací kód!",
"You should verify your code in %d min!": "Overte svoj kód za %d minút!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "prosím pridajte SMS poskytovateľa do zoznamu \\\"Poskytovatelia\\\" pre aplikáciu: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "prosím pridajte e-mailového poskytovateľa do zoznamu \\\"Poskytovatelia\\\" pre aplikáciu: %s",
"the user does not exist, please sign up first": "používateľ neexistuje, prosím, zaregistrujte sa najskôr"
},
"webauthn": {
"Found no credentials for this user": "Pre tohto používateľa sa nenašli žiadne prihlasovacie údaje",
"Please call WebAuthnSigninBegin first": "Najskôr prosím zavolajte WebAuthnSigninBegin"
}
}

View File

@@ -1,207 +0,0 @@
{
"account": {
"Failed to add user": "Misslyckades lägga till användare",
"Get init score failed, error: %w": "Misslyckades hämta init-poäng, fel: %w",
"Please sign out first": "Logga ut först",
"The application does not allow to sign up new account": "Applikationen tillåter inte registrering av nytt konto"
},
"auth": {
"Challenge method should be S256": "Utmaningsmetoden ska vara S256",
"DeviceCode Invalid": "Ogiltig enhetskod",
"Failed to create user, user information is invalid: %s": "Misslyckades skapa användare, användarinformationen är ogiltig: %s",
"Failed to login in: %s": "Misslyckades logga in: %s",
"Invalid token": "Ogiltig token",
"State expected: %s, but got: %s": "Förväntat tillstånd: %s, men fick: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto via %s, använd ett annat sätt att registrera dig",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto, kontakta din IT-support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Kontot för leverantör: %s och användarnamn: %s (%s) är redan länkat till ett annat konto: %s (%s)",
"The application: %s does not exist": "Applikationen: %s finns inte",
"The application: %s has disabled users to signin": "Applikationen: %s har inaktiverat användarinloggning",
"The group: %s does not exist": "Gruppen: %s finns inte",
"The login method: login with LDAP is not enabled for the application": "Inloggningsmetoden: inloggning med LDAP är inte aktiverad för applikationen",
"The login method: login with SMS is not enabled for the application": "Inloggningsmetoden: inloggning med SMS är inte aktiverad för applikationen",
"The login method: login with email is not enabled for the application": "Inloggningsmetoden: inloggning med e-post är inte aktiverad för applikationen",
"The login method: login with face is not enabled for the application": "Inloggningsmetoden: inloggning med ansikte är inte aktiverad för applikationen",
"The login method: login with password is not enabled for the application": "Inloggningsmetoden: inloggning med lösenord är inte aktiverad för applikationen",
"The organization: %s does not exist": "Organisationen: %s finns inte",
"The organization: %s has disabled users to signin": "Organisationen: %s har inaktiverat användarinloggning",
"The plan: %s does not exist": "Planen: %s finns inte",
"The pricing: %s does not exist": "Prissättningen: %s finns inte",
"The pricing: %s does not have plan: %s": "Prissättningen: %s har inte plan: %s",
"The provider: %s does not exist": "Leverantören: %s finns inte",
"The provider: %s is not enabled for the application": "Leverantören: %s är inte aktiverad för applikationen",
"Unauthorized operation": "Obehörig åtgärd",
"Unknown authentication type (not password or provider), form = %s": "Okänd autentiseringstyp (inte lösenord eller leverantör), form = %s",
"User's tag: %s is not listed in the application's tags": "Användarens tagg: %s finns inte med i applikationens taggar",
"UserCode Expired": "Användarkoden har löpt ut",
"UserCode Invalid": "Ogiltig användarkod",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Betalningsanvändare %s har ingen aktiv eller väntande prenumeration och applikationen: %s har ingen standardprissättning",
"the application for user %s is not found": "Applikationen för användare %s hittades inte",
"the organization: %s is not found": "Organisationen: %s hittades inte"
},
"cas": {
"Service %s and %s do not match": "Tjänsten %s och %s matchar inte"
},
"check": {
"%s does not meet the CIDR format requirements: %s": "%s uppfyller inte CIDR-formatets krav: %s",
"Affiliation cannot be blank": "Tillhörighet får inte vara tom",
"CIDR for IP: %s should not be empty": "CIDR för IP: %s får inte vara tomt",
"Default code does not match the code's matching rules": "Standardkoden matchar inte kodens matchningsregler",
"DisplayName cannot be blank": "Visningsnamn får inte vara tomt",
"DisplayName is not valid real name": "Visningsnamn är inte ett giltigt riktigt namn",
"Email already exists": "E-postadressen finns redan",
"Email cannot be empty": "E-post får inte vara tomt",
"Email is invalid": "E-postadressen är ogiltig",
"Empty username.": "Tomt användarnamn.",
"Face data does not exist, cannot log in": "Ansiktsdata finns inte, kan inte logga in",
"Face data mismatch": "Ansiktsdata stämmer inte",
"Failed to parse client IP: %s": "Misslyckades tolka klient-IP: %s",
"FirstName cannot be blank": "Förnamn får inte vara tomt",
"Invitation code cannot be blank": "Inbjudningskod får inte vara tom",
"Invitation code exhausted": "Inbjudningskoden är slut",
"Invitation code is invalid": "Inbjudningskoden är ogiltig",
"Invitation code suspended": "Inbjudningskoden är avstängd",
"LDAP user name or password incorrect": "LDAP-användarnamn eller lösenord är felaktigt",
"LastName cannot be blank": "Efternamn får inte vara tomt",
"Multiple accounts with same uid, please check your ldap server": "Flera konton med samma uid, kontrollera din LDAP-server",
"Organization does not exist": "Organisationen finns inte",
"Password cannot be empty": "Lösenord får inte vara tomt",
"Phone already exists": "Telefonnumret finns redan",
"Phone cannot be empty": "Telefon får inte vara tomt",
"Phone number is invalid": "Telefonnumret är ogiltigt",
"Please register using the email corresponding to the invitation code": "Registrera dig med den e-postadress som motsvarar inbjudningskoden",
"Please register using the phone corresponding to the invitation code": "Registrera dig med det telefonnummer som motsvarar inbjudningskoden",
"Please register using the username corresponding to the invitation code": "Registrera dig med det användarnamn som motsvarar inbjudningskoden",
"Session outdated, please login again": "Sessionen har gått ut, logga in igen",
"The invitation code has already been used": "Inbjudningskoden har redan använts",
"The password must contain at least one special character": "Lösenordet måste innehålla minst ett specialtecken",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Lösenordet måste innehålla minst en stor bokstav, en liten bokstav och en siffra",
"The password must have at least 6 characters": "Lösenordet måste ha minst 6 tecken",
"The password must have at least 8 characters": "Lösenordet måste ha minst 8 tecken",
"The password must not contain any repeated characters": "Lösenordet får inte innehålla upprepade tecken",
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Användaren har tagits bort och kan inte användas för att logga in, kontakta administratören",
"The user is forbidden to sign in, please contact the administrator": "Användaren är förbjuden att logga in, kontakta administratören",
"The user: %s doesn't exist in LDAP server": "Användaren: %s finns inte i LDAP-servern",
"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.": "Användarnamnet får endast innehålla alfanumeriska tecken, understreck eller bindestreck, får inte ha flera understreck eller bindestreck i följd, och får inte börja eller sluta med ett understreck eller bindestreck.",
"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": "Användarnamnet finns redan",
"Username cannot be an email address": "Användarnamnet får inte vara en e-postadress",
"Username cannot contain white spaces": "Användarnamnet får inte innehålla mellanslag",
"Username cannot start with a digit": "Användarnamnet får inte börja med en siffra",
"Username is too long (maximum is 255 characters).": "Användarnamnet är för långt (max 255 tecken).",
"Username must have at least 2 characters": "Användarnamnet måste ha minst 2 tecken",
"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.": "Användarnamnet stöder e-postformat. Användarnamnet får endast innehålla alfanumeriska tecken, understreck eller bindestreck, får inte ha flera understreck eller bindestreck i följd, och får inte börja eller sluta med ett understreck eller bindestreck. Observera även e-postformatet.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Du har angett fel lösenord eller kod för många gånger, vänta %d minuter och försök igen",
"Your IP address: %s has been banned according to the configuration of: ": "Din IP-adress: %s har blockerats enligt konfigurationen av: ",
"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": "Din region tillåter inte registrering via telefon",
"password or code is incorrect": "lösenord eller kod är felaktig",
"password or code is incorrect, you have %s remaining chances": "lösenord eller kod är felaktig, du har %s försök kvar",
"unsupported password type: %s": "lösenordstypen stöds inte: %s"
},
"enforcer": {
"the adapter: %s is not found": "adaptern: %s hittades inte"
},
"general": {
"Failed to import groups": "Misslyckades importera grupper",
"Failed to import users": "Misslyckades importera användare",
"Missing parameter": "Saknad parameter",
"Only admin user can specify user": "Endast administratör kan ange användare",
"Please login first": "Logga in först",
"The organization: %s should have one application at least": "Organisationen: %s bör ha minst en applikation",
"The user: %s doesn't exist": "Användaren: %s finns inte",
"Wrong userId": "Fel användar-ID",
"don't support captchaProvider: ": "stödjer inte captcha-leverantör: ",
"this operation is not allowed in demo mode": "denna åtgärd är inte tillåten i demoläge",
"this operation requires administrator to perform": "denna åtgärd kräver administratör för att genomföras"
},
"invitation": {
"Invitation %s does not exist": "Inbjudan %s existerar inte"
},
"ldap": {
"Ldap server exist": "LDAP-servern finns redan"
},
"link": {
"Please link first": "Länka först",
"This application has no providers": "Denna applikation har inga leverantörer",
"This application has no providers of type": "Denna applikation har inga leverantörer av typen",
"This provider can't be unlinked": "Denna leverantör kan inte avlänkas",
"You are not the global admin, you can't unlink other users": "Du är inte global administratör, du kan inte avlänka andra användare",
"You can't unlink yourself, you are not a member of any application": "Du kan inte avlänka dig själv, du är inte medlem i någon applikation"
},
"organization": {
"Only admin can modify the %s.": "Endast administratör kan ändra %s.",
"The %s is immutable.": "%s är oföränderlig.",
"Unknown modify rule %s.": "Okänd ändringsregel %s.",
"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.": "Lägg till en ny användare i organisationen 'built-in' (inbyggd) är för närvarande inaktiverat. Observera att alla användare i organisationen 'built-in' är globala administratörer i Casdoor. Se dokumentationen: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Om du fortfarande vill skapa en användare för organisationen 'built-in', gå till organisationens inställningssida och aktivera alternativet 'Har privilegiekonsensus'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Behörigheten: \\\"%s\\\" finns inte"
},
"provider": {
"Invalid application id": "Ogiltigt applikations-ID",
"the provider: %s does not exist": "leverantören: %s finns inte"
},
"resource": {
"User is nil for tag: avatar": "Användaren är nil för taggen: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Användarnamn eller fullständig filsökväg är tom: användarnamn = %s, fullständig filsökväg = %s"
},
"saml": {
"Application %s not found": "Applikationen %s hittades inte"
},
"saml_sp": {
"provider %s's category is not SAML": "leverantören %s:s kategori är inte SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Tomma parametrar för e-postformulär: %v",
"Invalid Email receivers: %s": "Ogiltiga e-postmottagare: %s",
"Invalid phone receivers: %s": "Ogiltiga telefonmottagare: %s"
},
"storage": {
"The objectKey: %s is not allowed": "Objektnyckeln: %s är inte tillåten",
"The provider type: %s is not supported": "Leverantörstypen: %s stöds inte"
},
"subscription": {
"Error": "Fel"
},
"token": {
"Grant_type: %s is not supported in this application": "Grant_type: %s stöds inte i denna applikation",
"Invalid application or wrong clientSecret": "Ogiltig applikation eller fel clientSecret",
"Invalid client_id": "Ogiltigt client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Omdirigerings-URI: %s finns inte i listan över tillåtna omdirigerings-URI:er",
"Token not found, invalid accessToken": "Token hittades inte, ogiltig accessToken"
},
"user": {
"Display name cannot be empty": "Visningsnamn får inte vara tomt",
"MFA email is enabled but email is empty": "MFA-e-post är aktiverat men e-post är tom",
"MFA phone is enabled but phone number is empty": "MFA-telefon är aktiverat men telefonnummer är tomt",
"New password cannot contain blank space.": "Nytt lösenord får inte innehålla mellanslag.",
"The new password must be different from your current password": "Det nya lösenordet måste skilja sig från ditt nuvarande lösenord",
"the user's owner and name should not be empty": "användarens ägare och namn får inte vara tomma"
},
"util": {
"No application is found for userId: %s": "Ingen applikation hittades för användar-ID: %s",
"No provider for category: %s is found for application: %s": "Ingen leverantör för kategori: %s hittades för applikation: %s",
"The provider: %s is not found": "Leverantören: %s hittades inte"
},
"verification": {
"Invalid captcha provider.": "Ogiltig captcha-leverantör.",
"Phone number is invalid in your region %s": "Telefonnumret är ogiltigt i din region %s",
"The verification code has already been used!": "Verifieringskoden har redan använts!",
"The verification code has not been sent yet!": "Verifieringskoden har inte skickats än!",
"Turing test failed.": "Turing-test misslyckades.",
"Unable to get the email modify rule.": "Kunde inte hämta regeln för ändring av e-post.",
"Unable to get the phone modify rule.": "Kunde inte hämta regeln för ändring av telefon.",
"Unknown type": "Okänd typ",
"Wrong verification code!": "Fel verifieringskod!",
"You should verify your code in %d min!": "Du bör verifiera din kod inom %d min!",
"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": "användaren finns inte, registrera dig först"
},
"webauthn": {
"Found no credentials for this user": "Inga autentiseringsuppgifter hittades för denna användare",
"Please call WebAuthnSigninBegin first": "Anropa WebAuthnSigninBegin först"
}
}

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "Kullanıcı giriş yapmaktan men edildi, lütfen yönetici ile iletişime geçin",
"The user: %s doesn't exist in LDAP server": "Kullanıcı: %s LDAP sunucusunda mevcut değil",
"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.": "Kullanıcı adı yalnızca alfanümerik karakterler, alt çizgi veya tire içerebilir, ardışık tire veya alt çizgi içeremez ve tire veya alt çizgi ile başlayamaz veya bitemez.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hesap alanı \\\"%s\\\" için \\\"%s\\\" değeri, hesap öğesi regex'iyle eşleşmiyor",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Kayıt alanı \\\"%s\\\" için \\\"%s\\\" değeri, \\\"%s\\\" uygulamasının kayıt öğesi regex'iyle eşleşmiyor",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Hesap alanı \"%s\" için \"%s\" değeri, hesap öğesi regex'iyle eşleşmiyor",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Kayıt alanı \"%s\" için \"%s\" değeri, \"%s\" uygulamasının kayıt öğesi regex'iyle eşleşmiyor",
"Username already exists": "Kullanıcı adı zaten var",
"Username cannot be an email address": "Kullanıcı adı e-posta adresi olamaz",
"Username cannot contain white spaces": "Kullanıcı adı boşluk içeremez",
@@ -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.": "Kullanıcı adı e-posta biçimini destekler. Ayrıca kullanıcı adı yalnızca alfanümerik karakterler, alt çizgiler veya tireler içerebilir, ardışık tireler veya alt çizgiler olamaz ve tire veya alt çizgi ile başlayıp bitemez. Ayrıca e-posta biçimine dikkat edin.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Çok fazla hatalı şifre veya kod girdiniz, lütfen %d dakika bekleyin ve tekrar deneyin",
"Your IP address: %s has been banned according to the configuration of: ": "IP adresiniz: %s, yapılandırmaya göre yasaklandı: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Şifrenizin süresi doldu. Lütfen \\\"Şifremi unuttum\\\"a tıklayarak şifrenizi sıfırlayın",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Şifrenizin süresi doldu. Lütfen \"Şifremi unuttum\"a tıklayarak şifrenizi sıfırlayın",
"Your region is not allow to signup by phone": "Bölgeniz telefonla kayıt yapmaya izin verilmiyor",
"password or code is incorrect": "şifre veya kod yanlış",
"password or code is incorrect, you have %s remaining chances": "şifre veya kod yanlış, %s hakkınız kaldı",
@@ -137,7 +137,7 @@
"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' (yerleşik) organizasyona yeni bir kullanıcı ekleme şu anda devre dışı bırakılmıştır. Not: 'built-in' organizasyonundaki tüm kullanıcılar Casdoor'da genel yöneticilerdir. Belgelere bakın: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Hala 'built-in' organizasyonu için bir kullanıcı oluşturmak isterseniz, organizasyonun ayarları sayfasına gidin ve 'Yetki onayı var' seçeneğini etkinleştirin."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "İzin: \\\"%s\\\" mevcut değil"
"The permission: \"%s\" doesn't exist": "İzin: \"%s\" mevcut değil"
},
"provider": {
"Invalid application id": "Geçersiz uygulama id",
@@ -196,8 +196,8 @@
"Unknown type": "Bilinmeyen tür",
"Wrong verification code!": "Yanlış doğrulama kodu!",
"You should verify your code in %d min!": "Kodunuzu %d dakika içinde doğrulamalısınız!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "lütfen uygulama için \\\"Sağlayıcılar\\\" listesine bir SMS sağlayıcı ekleyin: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "lütfen uygulama için \\\"Sağlayıcılar\\\" listesine bir E-posta sağlayıcı ekleyin: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "lütfen uygulama için \"Sağlayıcılar\" listesine bir SMS sağlayıcı ekleyin: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "lütfen uygulama için \"Sağlayıcılar\" listesine bir E-posta sağlayıcı ekleyin: %s",
"the user does not exist, please sign up first": "kullanıcı mevcut değil, lütfen önce kaydolun"
},
"webauthn": {

View File

@@ -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": "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\\\"",
"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\\\"": "Ditt lösenord har gått ut. Återställ det genom att klicka på \\\"Glömt lösenord\\\"",
"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 спроб",
@@ -137,7 +137,7 @@
"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": "Behörigheten: \\\"%s\\\" finns inte"
"The permission: \"%s\" doesn't exist": "Behörigheten: \"%s\" finns inte"
},
"provider": {
"Invalid application id": "Недійсний id додатка",
@@ -196,8 +196,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": "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",
"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": {

View File

@@ -83,8 +83,8 @@
"The user is forbidden to sign in, please contact the administrator": "Người dùng bị cấm đăng nhập, vui lòng liên hệ với quản trị viên",
"The user: %s doesn't exist in LDAP server": "Người dùng: %s không tồn tại trên máy chủ 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.": "Tên người dùng chỉ có thể chứa các ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có hai ký tự gạch dưới hoặc gạch ngang liền kề và không được bắt đầu hoặc kết thúc bằng dấu gạch dưới hoặc gạch ngang.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Giá trị \\\"%s\\\" cho trường tài khoản \\\"%s\\\" không khớp với biểu thức chính quy của mục tài khoản",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Giá trị \\\"%s\\\" cho trường đăng ký \\\"%s\\\" không khớp với biểu thức chính quy của mục đăng ký trong ứng dụng \\\"%s\\\"",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Giá trị \"%s\" cho trường tài khoản \"%s\" không khớp với biểu thức chính quy của mục tài khoản",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Giá trị \"%s\" cho trường đăng ký \"%s\" không khớp với biểu thức chính quy của mục đăng ký trong ứng dụng \"%s\"",
"Username already exists": "Tên đăng nhập đã tồn tại",
"Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
"Username cannot contain white spaces": "Tên người dùng không thể chứa khoảng trắng",
@@ -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.": "Tên người dùng hỗ trợ định dạng email. Ngoài ra, tên người dùng chỉ có thể chứa ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có gạch ngang hoặc gạch dưới liên tiếp và không được bắt đầu hoặc kết thúc bằng gạch ngang hoặc gạch dưới. Đồng thời lưu ý định dạng email.",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Bạn đã nhập sai mật khẩu hoặc mã quá nhiều lần, vui lòng đợi %d phút và thử lại",
"Your IP address: %s has been banned according to the configuration of: ": "Địa chỉ IP của bạn: %s đã bị cấm theo cấu hình của: ",
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Mật khẩu của bạn đã hết hạn. Vui lòng đặt lại mật khẩu bằng cách nhấp vào \\\"Quên mật khẩu\\\"",
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Mật khẩu của bạn đã hết hạn. Vui lòng đặt lại mật khẩu bằng cách nhấp vào \"Quên mật khẩu\"",
"Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại",
"password or code is incorrect": "mật khẩu hoặc mã không đúng",
"password or code is incorrect, you have %s remaining chances": "Mật khẩu hoặc mã không chính xác, bạn còn %s lần cơ hội",
@@ -137,7 +137,7 @@
"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.": "Thêm người dùng mới vào tổ chức 'built-in' (tích hợp sẵn) hiện đang bị vô hiệu hóa. Lưu ý: Tất cả người dùng trong tổ chức 'built-in' đều là quản trị viên toàn cầu trong Casdoor. Xem tài liệu: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Nếu bạn vẫn muốn tạo người dùng cho tổ chức 'built-in', hãy truy cập trang cài đặt của tổ chức và bật tùy chọn 'Có đồng ý quyền đặc biệt'."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "Quyền: \\\"%s\\\" không tồn tại"
"The permission: \"%s\" doesn't exist": "Quyền: \"%s\" không tồn tại"
},
"provider": {
"Invalid application id": "Sai ID ứng dụng",
@@ -196,8 +196,8 @@
"Unknown type": "Loại không xác định",
"Wrong verification code!": "Mã xác thực sai!",
"You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "vui lòng thêm nhà cung cấp SMS vào danh sách \\\"Providers\\\" cho ứng dụng: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "vui lòng thêm nhà cung cấp Email vào danh sách \\\"Providers\\\" cho ứng dụng: %s",
"please add a SMS provider to the \"Providers\" list for the application: %s": "vui lòng thêm nhà cung cấp SMS vào danh sách \"Providers\" cho ứng dụng: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "vui lòng thêm nhà cung cấp Email vào danh sách \"Providers\" cho ứng dụng: %s",
"the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước"
},
"webauthn": {

View File

@@ -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": "值 \"%s\"在账户信息字段\"%s\" 中与应用的账户项正则表达式不匹配",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "值\"%s\"在注册字段\"%s\"中与应用\"%s\"的注册项正则表达式不匹配",
"Username already exists": "用户名已存在",
"Username cannot be an email address": "用户名不可以是邮箱地址",
"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.": "用户名支持电子邮件格式。此外,用户名只能包含字母数字字符、下划线或连字符,不能包含连续的连字符或下划线,也不能以连字符或下划线开头或结尾。同时请注意电子邮件格式。",
"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\"": "您的密码已过期。请点击 \"忘记密码\" 以重置密码",
"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 次尝试的机会",
@@ -137,7 +137,7 @@
"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": "权限: \"%s\" 不存在"
},
"provider": {
"Invalid application id": "无效的应用ID",
@@ -188,6 +188,7 @@
"verification": {
"Invalid captcha provider.": "非法的验证码提供商",
"Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s",
"The forgot password feature is disabled": "忘记密码功能已被禁用",
"The verification code has already been used!": "验证码已使用过!",
"The verification code has not been sent yet!": "验证码未发送!",
"Turing test failed.": "验证码还未发送",
@@ -196,8 +197,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": "请添加一个SMS提供商到应用 %s 的 \"提供商 \"列表",
"please add an Email provider to the \"Providers\" list for the application: %s": "请添加一个Email提供商到应用 %s 的 \"提供商 \"列表",
"the user does not exist, please sign up first": "用户不存在,请先注册"
},
"webauthn": {

View File

@@ -31,11 +31,12 @@ type CustomIdProvider struct {
Client *http.Client
Config *oauth2.Config
UserInfoURL string
TokenURL string
AuthURL string
UserMapping map[string]string
Scopes []string
UserInfoURL string
TokenURL string
AuthURL string
UserMapping map[string]string
Scopes []string
CodeVerifier string
}
func NewCustomIdProvider(idpInfo *ProviderInfo, redirectUrl string) *CustomIdProvider {
@@ -53,6 +54,7 @@ func NewCustomIdProvider(idpInfo *ProviderInfo, redirectUrl string) *CustomIdPro
idp.UserInfoURL = idpInfo.UserInfoURL
idp.UserMapping = idpInfo.UserMapping
idp.CodeVerifier = idpInfo.CodeVerifier
return idp
}
@@ -62,7 +64,11 @@ func (idp *CustomIdProvider) SetHttpClient(client *http.Client) {
func (idp *CustomIdProvider) GetToken(code string) (*oauth2.Token, error) {
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, idp.Client)
return idp.Config.Exchange(ctx, code)
var oauth2Opts []oauth2.AuthCodeOption
if idp.CodeVerifier != "" {
oauth2Opts = append(oauth2Opts, oauth2.VerifierOption(idp.CodeVerifier))
}
return idp.Config.Exchange(ctx, code, oauth2Opts...)
}
func getNestedValue(data map[string]interface{}, path string) (interface{}, error) {

View File

@@ -87,8 +87,9 @@ import (
)
type GothIdProvider struct {
Provider goth.Provider
Session goth.Session
Provider goth.Provider
Session goth.Session
CodeVerifier string
}
func NewGothIdProvider(providerType string, clientId string, clientSecret string, clientId2 string, clientSecret2 string, redirectUrl string, hostUrl string) (*GothIdProvider, error) {
@@ -448,7 +449,13 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
value = url.Values{}
value.Add("code", code)
if idp.Provider.Name() == "twitterv2" || idp.Provider.Name() == "fitbit" {
value.Add("oauth_verifier", "casdoor-verifier")
// Use dynamic code verifier if provided, otherwise fall back to static one
verifier := idp.CodeVerifier
if verifier == "" {
verifier = "casdoor-verifier"
}
// RFC 7636 PKCE uses 'code_verifier' parameter
value.Add("code_verifier", verifier)
}
}
accessToken, err := idp.Session.Authorize(idp.Provider, value)

View File

@@ -45,6 +45,7 @@ type ProviderInfo struct {
HostUrl string
RedirectUrl string
DisableSsl bool
CodeVerifier string
TokenURL string
AuthURL string
@@ -128,7 +129,9 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
case "Web3Onboard":
return NewWeb3OnboardIdProvider(), nil
case "Twitter":
return NewTwitterIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
provider := NewTwitterIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
provider.CodeVerifier = idpInfo.CodeVerifier
return provider, nil
case "Telegram":
return NewTelegramIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
default:

View File

@@ -28,8 +28,9 @@ import (
)
type TwitterIdProvider struct {
Client *http.Client
Config *oauth2.Config
Client *http.Client
Config *oauth2.Config
CodeVerifier string
}
func NewTwitterIdProvider(clientId string, clientSecret string, redirectUrl string) *TwitterIdProvider {
@@ -84,7 +85,12 @@ func (idp *TwitterIdProvider) GetToken(code string) (*oauth2.Token, error) {
params := url.Values{}
// params.Add("client_id", idp.Config.ClientID)
params.Add("redirect_uri", idp.Config.RedirectURL)
params.Add("code_verifier", "casdoor-verifier")
// Use dynamic code verifier if provided, otherwise fall back to static one
verifier := idp.CodeVerifier
if verifier == "" {
verifier = "casdoor-verifier"
}
params.Add("code_verifier", verifier)
params.Add("code", code)
params.Add("grant_type", "authorization_code")
req, err := http.NewRequest("POST", "https://api.twitter.com/2/oauth2/token", strings.NewReader(params.Encode()))

View File

@@ -45,30 +45,16 @@
"tags": [],
"languages": [
"en",
"zh",
"es",
"fr",
"de",
"id",
"ja",
"ko",
"ru",
"zh",
"vi",
"it",
"ms",
"pt",
"tr",
"ar",
"he",
"nl",
"pl",
"fi",
"sv",
"uk",
"kk",
"fa",
"cs",
"sk",
"az"
"uk"
],
"masterPassword": "",
"defaultPassword": "",
@@ -89,6 +75,8 @@
{"name": "Country code", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Country/Region", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Location", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Address", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Addresses", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Affiliation", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Title", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "ID card type", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
@@ -250,6 +238,7 @@
"phone": "",
"countryCode": "",
"address": [],
"addresses": [],
"affiliation": "",
"tag": "",
"score": 2000,

View File

@@ -216,6 +216,16 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
e.AddAttribute("sn", message.AttributeValue(user.LastName))
e.AddAttribute("givenName", message.AttributeValue(user.FirstName))
// Add POSIX attributes for Linux machine login support
e.AddAttribute("loginShell", getAttribute("loginShell", user))
e.AddAttribute("gecos", getAttribute("gecos", user))
// Add SSH public key if available
sshKey := getAttribute("sshPublicKey", user)
if sshKey != "" {
e.AddAttribute("sshPublicKey", sshKey)
}
// Add objectClass for posixAccount
e.AddAttribute("objectClass", "posixAccount")
for _, group := range user.Groups {
e.AddAttribute(ldapMemberOfAttr, message.AttributeValue(group))
}

View File

@@ -83,6 +83,45 @@ var ldapAttributesMapping = map[string]FieldRelation{
return message.AttributeValue(getUserPasswordWithType(user))
},
},
"loginShell": {
userField: "loginShell",
notSearchable: true,
fieldMapper: func(user *object.User) message.AttributeValue {
// Check user properties first, otherwise return default shell
if user.Properties != nil {
if shell, ok := user.Properties["loginShell"]; ok && shell != "" {
return message.AttributeValue(shell)
}
}
return message.AttributeValue("/bin/bash")
},
},
"gecos": {
userField: "gecos",
notSearchable: true,
fieldMapper: func(user *object.User) message.AttributeValue {
// GECOS field typically contains full name and other user info
// Format: Full Name,Room Number,Work Phone,Home Phone,Other
gecos := user.DisplayName
if gecos == "" {
gecos = user.Name
}
return message.AttributeValue(gecos)
},
},
"sshPublicKey": {
userField: "sshPublicKey",
notSearchable: true,
fieldMapper: func(user *object.User) message.AttributeValue {
// Return SSH public key from user properties
if user.Properties != nil {
if sshKey, ok := user.Properties["sshPublicKey"]; ok && sshKey != "" {
return message.AttributeValue(sshKey)
}
}
return message.AttributeValue("")
},
},
}
const ldapMemberOfAttr = "memberOf"

View File

@@ -71,45 +71,46 @@ type Application struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Logo string `xorm:"varchar(200)" json:"logo"`
Title string `xorm:"varchar(100)" json:"title"`
Favicon string `xorm:"varchar(200)" json:"favicon"`
Order int `json:"order"`
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
Description string `xorm:"varchar(100)" json:"description"`
Organization string `xorm:"varchar(100)" json:"organization"`
Cert string `xorm:"varchar(100)" json:"cert"`
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"`
DisableSignin bool `json:"disableSignin"`
EnableSigninSession bool `json:"enableSigninSession"`
EnableAutoSignin bool `json:"enableAutoSignin"`
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableExclusiveSignin bool `json:"enableExclusiveSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
DisableSamlAttributes bool `json:"disableSamlAttributes"`
UseEmailAsSamlNameId bool `json:"useEmailAsSamlNameId"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
OrgChoiceMode string `json:"orgChoiceMode"`
SamlReplyUrl string `xorm:"varchar(500)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
SignupItems []*SignupItem `xorm:"varchar(3000)" json:"signupItems"`
SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
CertPublicKey string `xorm:"-" json:"certPublicKey"`
Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
SamlHashAlgorithm string `xorm:"varchar(20)" json:"samlHashAlgorithm"`
IsShared bool `json:"isShared"`
IpRestriction string `json:"ipRestriction"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Logo string `xorm:"varchar(200)" json:"logo"`
Title string `xorm:"varchar(100)" json:"title"`
Favicon string `xorm:"varchar(200)" json:"favicon"`
Order int `json:"order"`
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
Description string `xorm:"varchar(100)" json:"description"`
Organization string `xorm:"varchar(100)" json:"organization"`
Cert string `xorm:"varchar(100)" json:"cert"`
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"`
DisableSignin bool `json:"disableSignin"`
EnableSigninSession bool `json:"enableSigninSession"`
EnableAutoSignin bool `json:"enableAutoSignin"`
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableExclusiveSignin bool `json:"enableExclusiveSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
DisableSamlAttributes bool `json:"disableSamlAttributes"`
EnableSamlAssertionSignature bool `json:"enableSamlAssertionSignature"`
UseEmailAsSamlNameId bool `json:"useEmailAsSamlNameId"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
OrgChoiceMode string `json:"orgChoiceMode"`
SamlReplyUrl string `xorm:"varchar(500)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
SignupItems []*SignupItem `xorm:"varchar(3000)" json:"signupItems"`
SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
CertPublicKey string `xorm:"-" json:"certPublicKey"`
Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
SamlHashAlgorithm string `xorm:"varchar(20)" json:"samlHashAlgorithm"`
IsShared bool `json:"isShared"`
IpRestriction string `json:"ipRestriction"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@@ -127,7 +128,7 @@ type Application struct {
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
TermsOfUse string `xorm:"varchar(200)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
ThemeData *ThemeData `xorm:"json" json:"themeData"`
@@ -592,10 +593,10 @@ func GetMaskedApplication(application *Application, userId string) *Application
}
application.Providers = providerItems
application.GrantTypes = nil
application.RedirectUris = nil
application.GrantTypes = []string{}
application.RedirectUris = []string{}
application.TokenFormat = "***"
application.TokenFields = nil
application.TokenFields = []string{}
application.ExpireInHours = -1
application.RefreshExpireInHours = -1
application.FailedSigninLimit = -1

View File

@@ -61,6 +61,8 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "Country code", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Address", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Addresses", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "ID card type", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
@@ -111,7 +113,7 @@ func initBuiltInOrganization() bool {
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
UserTypes: []string{},
Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "pt"},
Languages: []string{"en", "es", "fr", "de", "ja", "zh", "vi", "pt", "tr", "pl", "uk"},
InitScore: 2000,
AccountItems: getBuiltInAccountItems(),
EnableSoftDeletion: false,
@@ -296,26 +298,47 @@ func initBuiltInLdap() {
}
func initBuiltInProvider() {
provider, err := GetProvider(util.GetId("admin", "provider_captcha_default"))
if err != nil {
panic(err)
providers := []*Provider{
{
Owner: "admin",
Name: "provider_captcha_default",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Captcha Default",
Category: "Captcha",
Type: "Default",
},
{
Owner: "admin",
Name: "provider_balance",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Balance",
Category: "Payment",
Type: "Balance",
},
{
Owner: "admin",
Name: "provider_payment_dummy",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Dummy Payment",
Category: "Payment",
Type: "Dummy",
},
}
if provider != nil {
return
}
for _, provider := range providers {
existingProvider, err := GetProvider(util.GetId("admin", provider.Name))
if err != nil {
panic(err)
}
provider = &Provider{
Owner: "admin",
Name: "provider_captcha_default",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Captcha Default",
Category: "Captcha",
Type: "Default",
}
_, err = AddProvider(provider)
if err != nil {
panic(err)
if existingProvider != nil {
continue
}
_, err = AddProvider(provider)
if err != nil {
panic(err)
}
}
}
@@ -385,7 +408,7 @@ e = some(where (p.eft == allow))
m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
(r.subName == p.subName || p.subName == "*" || r.subName != "anonymous" && p.subName == "!anonymous") && \
(r.method == p.method || p.method == "*") && \
(r.urlPath == p.urlPath || p.urlPath == "*") && \
(keyMatch2(r.urlPath, p.urlPath) || p.urlPath == "*") && \
(r.objOwner == p.objOwner || p.objOwner == "*") && \
(r.objName == p.objName || p.objName == "*") || \
(r.subOwner == r.objOwner && r.subName == r.objName)`

View File

@@ -26,10 +26,35 @@ import (
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
goldap "github.com/go-ldap/ldap/v3"
"github.com/nyaruka/phonenumbers"
"github.com/thanhpk/randstr"
"golang.org/x/text/encoding/unicode"
)
// formatUserPhone processes phone number for a user based on their CountryCode
func formatUserPhone(u *User) {
if u.Phone == "" {
return
}
// 1. Normalize hint (e.g., "China" -> "CN") for the parser
countryHint := u.CountryCode
if strings.EqualFold(countryHint, "China") {
countryHint = "CN"
}
if len(countryHint) != 2 {
countryHint = "" // Only 2-letter codes are valid hints
}
// 2. Try parsing (Strictly using countryHint from LDAP)
num, err := phonenumbers.Parse(u.Phone, countryHint)
if err == nil && num != nil && phonenumbers.IsValidNumber(num) {
// Store a clean national number (digits only, without country prefix)
u.Phone = fmt.Sprint(num.GetNationalNumber())
}
}
type LdapConn struct {
Conn *goldap.Conn
IsAD bool
@@ -289,6 +314,7 @@ func AutoAdjustLdapUser(users []LdapUser) []LdapUser {
Mobile: util.ReturnAnyNotEmpty(user.Mobile, user.MobileTelephoneNumber, user.TelephoneNumber),
Address: util.ReturnAnyNotEmpty(user.Address, user.PostalAddress, user.RegisteredAddress),
Country: util.ReturnAnyNotEmpty(user.Country, user.CountryName),
CountryName: user.CountryName,
Attributes: user.Attributes,
}
}
@@ -361,6 +387,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
Avatar: organization.DefaultAvatar,
Email: syncUser.Email,
Phone: syncUser.Mobile,
CountryCode: syncUser.Country,
Address: []string{syncUser.Address},
Region: util.ReturnAnyNotEmpty(syncUser.Country, syncUser.CountryName),
Affiliation: affiliation,
@@ -369,6 +396,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
Ldap: syncUser.Uuid,
Properties: syncUser.Attributes,
}
formatUserPhone(newUser)
if ldap.DefaultGroup != "" {
newUser.Groups = []string{ldap.DefaultGroup}

View File

@@ -112,16 +112,14 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
originFrontend, originBackend := getOriginFromHost(host)
// If application is provided, use application-specific URLs
var issuer, authzEndpoint, jwksUri string
var issuer, jwksUri string
if applicationName != "" {
// Application-specific issuer and endpoints (owner is always "admin")
issuer = fmt.Sprintf("%s/.well-known/%s", originBackend, applicationName)
authzEndpoint = fmt.Sprintf("%s/login/oauth/authorize", originFrontend)
jwksUri = fmt.Sprintf("%s/.well-known/%s/jwks", originBackend, applicationName)
} else {
// Default global issuer and endpoints
issuer = originBackend
authzEndpoint = fmt.Sprintf("%s/login/oauth/authorize", originFrontend)
jwksUri = fmt.Sprintf("%s/.well-known/jwks", originBackend)
}
@@ -132,7 +130,7 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
// https://access.line.me/.well-known/openid-configuration
oidcDiscovery := OidcDiscovery{
Issuer: issuer,
AuthorizationEndpoint: authzEndpoint,
AuthorizationEndpoint: fmt.Sprintf("%s/login/oauth/authorize", originFrontend),
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),

View File

@@ -26,15 +26,12 @@ type Order struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
UpdateTime string `xorm:"varchar(100)" json:"updateTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
// Product Info
Products []string `xorm:"varchar(1000)" json:"products"` // Support for multiple products per order. Using varchar(1000) for simple JSON array storage; can be refactored to separate table if needed
ProductInfos []ProductInfo `xorm:"varchar(2000)" json:"productInfos"`
// Subscription Info (for subscription orders)
PricingName string `xorm:"varchar(100)" json:"pricingName"`
PlanName string `xorm:"varchar(100)" json:"planName"`
ProductInfos []ProductInfo `xorm:"mediumtext" json:"productInfos"`
// User Info
User string `xorm:"varchar(100)" json:"user"`
@@ -47,19 +44,20 @@ type Order struct {
// Order State
State string `xorm:"varchar(100)" json:"state"`
Message string `xorm:"varchar(2000)" json:"message"`
// Order Duration
StartTime string `xorm:"varchar(100)" json:"startTime"`
EndTime string `xorm:"varchar(100)" json:"endTime"`
}
type ProductInfo struct {
Owner string `json:"owner"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Image string `json:"image"`
Detail string `json:"detail"`
Image string `json:"image,omitempty"`
Detail string `json:"detail,omitempty"`
Price float64 `json:"price"`
IsRecharge bool `json:"isRecharge"`
Currency string `json:"currency,omitempty"`
IsRecharge bool `json:"isRecharge,omitempty"`
Quantity int `json:"quantity,omitempty"`
PricingName string `json:"pricingName,omitempty"`
PlanName string `json:"planName,omitempty"`
}
func GetOrderCount(owner, field, value string) (int64, error) {
@@ -137,6 +135,14 @@ func UpdateOrder(id string, order *Order) (bool, error) {
return false, nil
}
if o.State != order.State {
if order.State == "Created" {
order.UpdateTime = ""
} else {
order.UpdateTime = util.GetCurrentTime()
}
}
if !slices.Equal(o.Products, order.Products) {
existingInfos := make(map[string]ProductInfo, len(o.ProductInfos))
for _, info := range o.ProductInfos {
@@ -148,22 +154,25 @@ func UpdateOrder(id string, order *Order) (bool, error) {
if err != nil {
return false, err
}
price := 0.0
for _, product := range products {
if existingInfo, ok := existingInfos[product.Name]; ok {
productInfos = append(productInfos, existingInfo)
continue
}
productInfos = append(productInfos, ProductInfo{
productInfo := ProductInfo{
Name: product.Name,
DisplayName: product.DisplayName,
Image: product.Image,
Detail: product.Detail,
Price: product.Price,
IsRecharge: product.IsRecharge,
})
}
if existingInfo, ok := existingInfos[product.Name]; ok {
// Keep historical product info; do not overwrite with current product.
productInfo = existingInfo
}
price += productInfo.Price
productInfos = append(productInfos, productInfo)
}
order.ProductInfos = productInfos
order.Price = price
}
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(order)

View File

@@ -23,64 +23,83 @@ import (
"github.com/casdoor/casdoor/util"
)
func PlaceOrder(productId string, user *User, pricingName string, planName string, customPrice float64) (*Order, error) {
product, err := GetProduct(productId)
func PlaceOrder(owner string, reqProductInfos []ProductInfo, user *User) (*Order, error) {
if len(reqProductInfos) == 0 {
return nil, fmt.Errorf("order has no products")
}
productNames := make([]string, 0, len(reqProductInfos))
for _, reqInfo := range reqProductInfos {
if reqInfo.Name == "" {
return nil, fmt.Errorf("product name cannot be empty")
}
productNames = append(productNames, reqInfo.Name)
}
products, err := getOrderProducts(owner, productNames)
if err != nil {
return nil, err
}
if product == nil {
return nil, fmt.Errorf("the product: %s does not exist", productId)
productMap := make(map[string]Product, len(reqProductInfos))
for _, product := range products {
productMap[product.Name] = product
}
if !product.IsRecharge && product.Quantity <= 0 {
return nil, fmt.Errorf("the product: %s is out of stock", product.Name)
orderCurrency := products[0].Currency
if orderCurrency == "" {
orderCurrency = "USD"
}
productCurrency := product.Currency
if productCurrency == "" {
productCurrency = "USD"
if err := validateProductCurrencies(products, orderCurrency); err != nil {
return nil, err
}
var productPrice float64
if product.IsRecharge {
if customPrice <= 0 {
return nil, fmt.Errorf("the custom price should be greater than zero")
var productInfos []ProductInfo
orderPrice := 0.0
for _, productInfo := range reqProductInfos {
product := productMap[productInfo.Name]
var productPrice float64
if product.IsRecharge {
productPrice = productInfo.Price
if productPrice <= 0 {
return nil, fmt.Errorf("the custom price should be greater than zero")
}
} else {
productPrice = product.Price
}
productPrice = customPrice
} else {
productPrice = product.Price
}
orderName := fmt.Sprintf("order_%v", util.GenerateTimeId())
productNames := []string{product.Name}
productInfos := []ProductInfo{
{
productInfos = append(productInfos, ProductInfo{
Owner: owner,
Name: product.Name,
DisplayName: product.DisplayName,
Image: product.Image,
Detail: product.Detail,
Price: productPrice,
Currency: product.Currency,
IsRecharge: product.IsRecharge,
},
Quantity: productInfo.Quantity,
PricingName: productInfo.PricingName,
PlanName: productInfo.PlanName,
})
orderPrice += productPrice * float64(productInfo.Quantity)
}
orderName := fmt.Sprintf("order_%v", util.GenerateTimeId())
order := &Order{
Owner: product.Owner,
Owner: owner,
Name: orderName,
DisplayName: orderName,
CreatedTime: util.GetCurrentTime(),
DisplayName: fmt.Sprintf("Order for %s", strings.Join(productNames, ",")),
Products: productNames,
ProductInfos: productInfos,
PricingName: pricingName,
PlanName: planName,
User: user.Name,
Payment: "", // Payment will be set when user pays
Price: productPrice,
Currency: productCurrency,
Price: orderPrice,
Currency: orderCurrency,
State: "Created",
Message: "",
StartTime: util.GetCurrentTime(),
EndTime: "",
UpdateTime: "",
}
affected, err := AddOrder(order)
@@ -106,10 +125,14 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
if len(products) == 0 {
return nil, nil, fmt.Errorf("order has no products")
}
for _, product := range products {
if !product.IsRecharge && product.Quantity <= 0 {
return nil, nil, fmt.Errorf("the product: %s is out of stock", product.Name)
}
orderCurrency := order.Currency
if orderCurrency == "" {
orderCurrency = "USD"
}
if err := validateProductCurrencies(products, orderCurrency); err != nil {
return nil, nil, err
}
user, err := GetUser(util.GetId(order.Owner, order.User))
@@ -140,15 +163,20 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
orderProductInfos := order.ProductInfos
// Create a subscription when pricing and plan are provided
// This allows both free users and paid users to subscribe to plans
if order.PricingName != "" && order.PlanName != "" {
plan, err := GetPlan(util.GetId(owner, order.PlanName))
for i, productInfo := range orderProductInfos {
if productInfo.PricingName == "" || productInfo.PlanName == "" {
continue
}
plan, err := GetPlan(util.GetId(owner, productInfo.PlanName))
if err != nil {
return nil, nil, err
}
if plan == nil {
return nil, nil, fmt.Errorf("the plan: %s does not exist", order.PlanName)
return nil, nil, fmt.Errorf("the plan: %s does not exist", productInfo.PlanName)
}
sub, err := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
@@ -164,7 +192,9 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
return nil, nil, fmt.Errorf("failed to add subscription: %s", sub.Name)
}
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, order.PricingName, sub.Name)
if i == 0 {
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, productInfo.PricingName, sub.Name)
}
}
if baseProduct.SuccessUrl != "" {
@@ -177,9 +207,9 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
displayNames[i] = p.DisplayName
descriptions[i] = p.Description
}
reqProductName := strings.Join(productNames, "")
reqProductDisplayName := strings.Join(displayNames, "")
reqProductDescription := strings.Join(descriptions, "")
reqProductName := strings.Join(productNames, ", ")
reqProductDisplayName := strings.Join(displayNames, ", ")
reqProductDescription := strings.Join(descriptions, ", ")
payReq := &pp.PayReq{
ProviderName: providerName,
@@ -221,11 +251,11 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
Provider: provider.Name,
Type: provider.Type,
ProductName: reqProductName,
ProductDisplayName: reqProductDisplayName,
Detail: reqProductDescription,
Currency: order.Currency,
Price: order.Price,
Products: productNames,
ProductsDisplayName: reqProductDisplayName,
Detail: reqProductDescription,
Currency: order.Currency,
Price: order.Price,
User: user.Name,
Order: order.Name,
@@ -275,10 +305,10 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
hasRecharge := false
rechargeAmount := 0.0
for _, productInfo := range order.ProductInfos {
for _, productInfo := range orderProductInfos {
if productInfo.IsRecharge {
hasRecharge = true
rechargeAmount += productInfo.Price
rechargeAmount += productInfo.Price * float64(productInfo.Quantity)
}
}
@@ -313,7 +343,7 @@ func PayOrder(providerName, host, paymentEnv string, order *Order, lang string)
if provider.Type == "Dummy" || provider.Type == "Balance" {
order.State = "Paid"
order.Message = "Payment successful"
order.EndTime = util.GetCurrentTime()
order.UpdateTime = util.GetCurrentTime()
}
// Update order state first to avoid inconsistency
@@ -324,7 +354,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 == "Dummy" || provider.Type == "Balance" {
err = UpdateProductStock(products)
err = UpdateProductStock(orderProductInfos)
if err != nil {
return nil, nil, err
}
@@ -340,6 +370,6 @@ func CancelOrder(order *Order) (bool, error) {
order.State = "Canceled"
order.Message = "Canceled by user"
order.EndTime = util.GetCurrentTime()
order.UpdateTime = util.GetCurrentTime()
return UpdateOrder(order.GetId(), order)
}

View File

@@ -32,6 +32,7 @@ type AccountItem struct {
ViewRule string `json:"viewRule"`
ModifyRule string `json:"modifyRule"`
Regex string `json:"regex"`
Tab string `json:"tab"`
}
type ThemeData struct {
@@ -88,6 +89,7 @@ type Organization struct {
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
MfaRememberInHours int `json:"mfaRememberInHours"`
AccountMenu string `xorm:"varchar(20)" json:"accountMenu"`
AccountItems []*AccountItem `xorm:"mediumtext" json:"accountItems"`
OrgBalance float64 `json:"orgBalance"`

View File

@@ -31,11 +31,11 @@ type Payment struct {
Provider string `xorm:"varchar(100)" json:"provider"`
Type string `xorm:"varchar(100)" json:"type"`
// Product Info
ProductName string `xorm:"varchar(100)" json:"productName"`
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
Detail string `xorm:"varchar(255)" json:"detail"`
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`
Products []string `xorm:"varchar(1000)" json:"products"`
ProductsDisplayName string `xorm:"varchar(1000)" json:"productsDisplayName"`
Detail string `xorm:"varchar(255)" json:"detail"`
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`
// Payer Info
User string `xorm:"varchar(100)" json:"user"`
@@ -50,7 +50,8 @@ type Payment struct {
InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"`
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
// Order Info
Order string `xorm:"varchar(100)" json:"order"` // Internal order name
Order string `xorm:"varchar(100)" json:"order"` // Internal order name
OrderObj *Order `xorm:"-" json:"orderObj,omitempty"`
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"` // External payment provider's order ID
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
SuccessUrl string `xorm:"varchar(2000)" json:"successUrl"` // `successUrl` is redirected from `payUrl` after pay success
@@ -70,6 +71,11 @@ func GetPayments(owner string) ([]*Payment, error) {
return nil, err
}
err = ExtendPaymentWithOrder(payments)
if err != nil {
return nil, err
}
return payments, nil
}
@@ -80,6 +86,11 @@ func GetUserPayments(owner, user string) ([]*Payment, error) {
return nil, err
}
err = ExtendPaymentWithOrder(payments)
if err != nil {
return nil, err
}
return payments, nil
}
@@ -91,9 +102,49 @@ func GetPaginationPayments(owner string, offset, limit int, field, value, sortFi
return nil, err
}
err = ExtendPaymentWithOrder(payments)
if err != nil {
return nil, err
}
return payments, nil
}
func ExtendPaymentWithOrder(payments []*Payment) error {
ownerOrdersMap := make(map[string][]string)
for _, payment := range payments {
if payment.Order != "" {
ownerOrdersMap[payment.Owner] = append(ownerOrdersMap[payment.Owner], payment.Order)
}
}
ordersMap := make(map[string]*Order)
for owner, orderNames := range ownerOrdersMap {
if len(orderNames) == 0 {
continue
}
var orders []*Order
err := ormer.Engine.In("name", orderNames).Find(&orders, &Order{Owner: owner})
if err != nil {
return err
}
for _, order := range orders {
ordersMap[util.GetId(order.Owner, order.Name)] = order
}
}
for _, payment := range payments {
if payment.Order != "" {
orderId := util.GetId(payment.Owner, payment.Order)
if order, ok := ordersMap[orderId]; ok {
payment.OrderObj = order
}
}
}
return nil
}
func getPayment(owner string, name string) (*Payment, error) {
if owner == "" || name == "" {
return nil, nil
@@ -176,14 +227,11 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
return nil, nil, err
}
product, err := getProduct(owner, payment.ProductName)
// Check if the order products exist
_, err = getOrderProducts(owner, payment.Products)
if err != nil {
return nil, nil, err
}
if product == nil {
err = fmt.Errorf("the product: %s does not exist", payment.ProductName)
return nil, nil, err
}
notifyResult, err := pProvider.Notify(body, payment.OutOrderId)
if err != nil {
@@ -193,13 +241,13 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
return payment, notifyResult, nil
}
// Only check paid payment
if notifyResult.ProductDisplayName != "" && notifyResult.ProductDisplayName != product.DisplayName {
err = fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", notifyResult.ProductDisplayName, product.DisplayName)
if notifyResult.ProductDisplayName != "" && notifyResult.ProductDisplayName != payment.ProductsDisplayName {
err = fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", notifyResult.ProductDisplayName, payment.ProductsDisplayName)
return payment, nil, err
}
if notifyResult.Price != product.Price && !product.IsRecharge {
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, product.Price)
if notifyResult.Price != payment.Price {
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, payment.Price)
return payment, nil, err
}
@@ -213,18 +261,29 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
}
// Check if payment is already in a terminal state to prevent duplicate processing
if payment.State == pp.PaymentStatePaid || payment.State == pp.PaymentStateError ||
payment.State == pp.PaymentStateCanceled || payment.State == pp.PaymentStateTimeout {
if pp.IsTerminalState(payment.State) {
return payment, nil
}
// Determine the new payment state
var newState pp.PaymentState
var newMessage string
if err != nil {
payment.State = pp.PaymentStateError
payment.Message = err.Error()
newState = pp.PaymentStateError
newMessage = err.Error()
} else {
payment.State = notifyResult.PaymentStatus
payment.Message = notifyResult.NotifyMessage
newState = notifyResult.PaymentStatus
newMessage = notifyResult.NotifyMessage
}
// Check if the payment state would actually change
// This prevents duplicate webhook events when providers send redundant notifications
if payment.State == newState {
return payment, nil
}
payment.State = newState
payment.Message = newMessage
_, err = UpdatePayment(payment.GetId(), payment)
if err != nil {
return nil, err
@@ -242,16 +301,19 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
if payment.State == pp.PaymentStatePaid {
order.State = "Paid"
order.Message = "Payment successful"
order.EndTime = util.GetCurrentTime()
order.UpdateTime = util.GetCurrentTime()
} else if payment.State == pp.PaymentStateError {
order.State = "PaymentFailed"
order.Message = payment.Message
order.UpdateTime = util.GetCurrentTime()
} else if payment.State == pp.PaymentStateCanceled {
order.State = "Canceled"
order.Message = "Payment was cancelled"
order.UpdateTime = util.GetCurrentTime()
} else if payment.State == pp.PaymentStateTimeout {
order.State = "Timeout"
order.Message = "Payment timed out"
order.UpdateTime = util.GetCurrentTime()
}
_, err = UpdateOrder(order.GetId(), order)
if err != nil {
@@ -316,10 +378,11 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
hasRecharge := false
rechargeAmount := 0.0
for _, productInfo := range order.ProductInfos {
orderProductInfos := order.ProductInfos
for _, productInfo := range orderProductInfos {
if productInfo.IsRecharge {
hasRecharge = true
rechargeAmount += productInfo.Price
rechargeAmount += productInfo.Price * float64(productInfo.Quantity)
}
}
@@ -349,7 +412,7 @@ func NotifyPayment(body []byte, owner string, paymentName string, lang string) (
}
}
err = UpdateProductStock(products)
err = UpdateProductStock(orderProductInfos)
if err != nil {
return nil, err
}

View File

@@ -98,21 +98,21 @@ func GetProduct(id string) (*Product, error) {
return getProduct(owner, name)
}
func UpdateProductStock(products []Product) error {
func UpdateProductStock(productInfos []ProductInfo) error {
var (
affected int64
err error
)
for _, product := range products {
for _, product := range productInfos {
if product.IsRecharge {
affected, err = ormer.Engine.ID(core.PK{product.Owner, product.Name}).
Incr("sold", 1).
Incr("sold", product.Quantity).
Update(&Product{})
} else {
affected, err = ormer.Engine.ID(core.PK{product.Owner, product.Name}).
Where("quantity > 0").
Decr("quantity", 1).
Incr("sold", 1).
Where("quantity >= ?", product.Quantity).
Decr("quantity", product.Quantity).
Incr("sold", product.Quantity).
Update(&Product{})
}
@@ -172,13 +172,37 @@ func checkProduct(product *Product) error {
return fmt.Errorf("the product not exist")
}
for _, providerName := range product.Providers {
provider, err := getProvider(product.Owner, providerName)
if product.Currency == "" {
return fmt.Errorf("currency cannot be empty")
}
if len(product.Providers) == 0 {
providers, err := GetProvidersByCategory(product.Owner, "Payment")
if err != nil {
return err
}
if provider != nil && provider.Type == "Alipay" && product.Currency != "CNY" {
return fmt.Errorf("alipay provider only supports CNY, got: %s", product.Currency)
if len(providers) == 0 {
return fmt.Errorf("no payment provider available")
}
for _, provider := range providers {
if provider.Type != "Alipay" || product.Currency == "CNY" {
product.Providers = append(product.Providers, provider.Name)
}
}
if len(product.Providers) == 0 {
return fmt.Errorf("no compatible payment provider available for currency: %s", product.Currency)
}
} else {
for _, providerName := range product.Providers {
provider, err := getProvider(product.Owner, providerName)
if err != nil {
return err
}
if provider != nil && provider.Type == "Alipay" && product.Currency != "CNY" {
return fmt.Errorf("alipay provider only supports CNY, got: %s", product.Currency)
}
}
}
return nil
@@ -319,3 +343,19 @@ func getOrderProducts(owner string, productNames []string) ([]Product, error) {
}
return orderedProducts, nil
}
func validateProductCurrencies(products []Product, orderCurrency string) error {
for _, product := range products {
productCurrency := product.Currency
if productCurrency == "" {
productCurrency = "USD"
}
if productCurrency != orderCurrency {
return fmt.Errorf("products have different currencies, expected: %s, got: %s (product: %s)", orderCurrency, productCurrency, product.Name)
}
if !product.IsRecharge && product.Quantity <= 0 {
return fmt.Errorf("the product: %s is out of stock", product.Name)
}
}
return nil
}

View File

@@ -77,6 +77,7 @@ type Provider struct {
ProviderUrl string `xorm:"varchar(200)" json:"providerUrl"`
EnableProxy bool `json:"enableProxy"`
EnablePkce bool `json:"enablePkce"`
}
func GetMaskedProvider(provider *Provider, isMaskEnabled bool) *Provider {
@@ -132,6 +133,16 @@ func GetProviders(owner string) ([]*Provider, error) {
return providers, nil
}
func GetProvidersByCategory(owner string, category string) ([]*Provider, error) {
providers := []*Provider{}
err := ormer.Engine.Where("(owner = ? or owner = ?) and category = ?", "admin", owner, category).Desc("created_time").Find(&providers, &Provider{})
if err != nil {
return providers, err
}
return providers, nil
}
func GetGlobalProviders() ([]*Provider, error) {
providers := []*Provider{}
err := ormer.Engine.Desc("created_time").Find(&providers)

View File

@@ -80,7 +80,7 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
return nil, err
}
if action != "buy-product" {
if action != "buy-product" && action != "notify-payment" {
resp.Data = nil
}

View File

@@ -394,14 +394,17 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
// }
// Sign the assertion (SAML 2.0 best practice)
assertion := samlResponse.FindElement("./Assertion")
if assertion != nil {
assertionSig, err := ctx.ConstructSignature(assertion, true)
if err != nil {
return "", "", "", fmt.Errorf("err: Failed to sign SAML assertion, %s", err.Error())
// Only sign if EnableSamlAssertionSignature is true
if application.EnableSamlAssertionSignature {
assertion := samlResponse.FindElement("./Assertion")
if assertion != nil {
assertionSig, err := ctx.ConstructSignature(assertion, true)
if err != nil {
return "", "", "", fmt.Errorf("err: Failed to sign SAML assertion, %s", err.Error())
}
// Insert signature as the second child of assertion (after Issuer)
assertion.InsertChildAt(1, assertionSig)
}
// Insert signature as the second child of assertion (after Issuer)
assertion.InsertChildAt(1, assertionSig)
}
// Sign the response

View File

@@ -28,6 +28,8 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
} else if provider.Type == "Custom HTTP SMS" {
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.Title, provider.TemplateCode, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl, provider.EnableProxy)
} else if provider.Type == "Alibaba Cloud PNVS SMS" {
client, err = newPnvsSmsClient(provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.RegionId)
} else {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
}
@@ -48,7 +50,7 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
if provider.AppId != "" {
phoneNumbers = append([]string{provider.AppId}, phoneNumbers...)
}
} else if provider.Type == sender.Aliyun {
} else if provider.Type == sender.Aliyun || provider.Type == "Alibaba Cloud PNVS SMS" {
for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+86")
}

86
object/sms_pnvs.go Normal file
View File

@@ -0,0 +1,86 @@
// 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 (
"encoding/json"
"fmt"
"github.com/aliyun/alibaba-cloud-sdk-go/services/dypnsapi"
)
type PnvsSmsClient struct {
template string
sign string
core *dypnsapi.Client
}
func newPnvsSmsClient(accessId string, accessKey string, sign string, template string, regionId string) (*PnvsSmsClient, error) {
if regionId == "" {
regionId = "cn-hangzhou"
}
client, err := dypnsapi.NewClientWithAccessKey(regionId, accessId, accessKey)
if err != nil {
return nil, err
}
pnvsClient := &PnvsSmsClient{
template: template,
core: client,
sign: sign,
}
return pnvsClient, nil
}
func (c *PnvsSmsClient) SendMessage(param map[string]string, targetPhoneNumber ...string) error {
if len(targetPhoneNumber) == 0 {
return fmt.Errorf("missing parameter: targetPhoneNumber")
}
// PNVS sends to one phone number at a time
phoneNumber := targetPhoneNumber[0]
request := dypnsapi.CreateSendSmsVerifyCodeRequest()
request.Scheme = "https"
request.PhoneNumber = phoneNumber
request.TemplateCode = c.template
request.SignName = c.sign
// TemplateParam is optional for PNVS as it can auto-generate verification codes
// But if params are provided, we'll pass them
if len(param) > 0 {
templateParam, err := json.Marshal(param)
if err != nil {
return err
}
request.TemplateParam = string(templateParam)
}
response, err := c.core.SendSmsVerifyCode(request)
if err != nil {
return err
}
if response.Code != "OK" {
if response.Message != "" {
return fmt.Errorf(response.Message)
}
return fmt.Errorf("PNVS SMS send failed with code: %s", response.Code)
}
return nil
}

View File

@@ -370,3 +370,15 @@ func (p *ActiveDirectorySyncerProvider) adEntryToOriginalUser(entry *goldap.Entr
return user
}
// GetOriginalGroups retrieves all groups from Active Directory (not implemented yet)
func (p *ActiveDirectorySyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Active Directory group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *ActiveDirectorySyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Active Directory user group membership sync
return []string{}, nil
}

View File

@@ -21,6 +21,7 @@ import (
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/casdoor/casdoor/util"
@@ -126,12 +127,11 @@ func (p *AzureAdSyncerProvider) getAzureAdAccessToken() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "POST", tokenUrl, nil)
req, err := http.NewRequestWithContext(ctx, "POST", tokenUrl, strings.NewReader(data.Encode()))
if err != nil {
return "", err
}
req.URL.RawQuery = data.Encode()
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{Timeout: 30 * time.Second}
@@ -274,3 +274,15 @@ func (p *AzureAdSyncerProvider) getAzureAdOriginalUsers() ([]*OriginalUser, erro
return originalUsers, nil
}
// GetOriginalGroups retrieves all groups from Azure AD (not implemented yet)
func (p *AzureAdSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Azure AD group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *AzureAdSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Azure AD user group membership sync
return []string{}, nil
}

View File

@@ -60,9 +60,19 @@ func addSyncerJob(syncer *Syncer) error {
return err
}
// Sync groups as well
err = syncer.syncGroups()
if err != nil {
// Log error but don't fail the entire sync
fmt.Printf("Warning: syncGroups() error: %s\n", err.Error())
}
schedule := fmt.Sprintf("@every %ds", syncer.SyncInterval)
cron := getCronMap(syncer.Name)
_, err = cron.AddFunc(schedule, syncer.syncUsersNoError)
_, err = cron.AddFunc(schedule, func() {
syncer.syncUsersNoError()
syncer.syncGroupsNoError()
})
if err != nil {
return err
}

View File

@@ -164,3 +164,15 @@ func (t dsnConnector) Connect(ctx context.Context) (driver.Conn, error) {
func (t dsnConnector) Driver() driver.Driver {
return t.driver
}
// GetOriginalGroups retrieves all groups from Database (not implemented yet)
func (p *DatabaseSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Database group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *DatabaseSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Database user group membership sync
return []string{}, nil
}

View File

@@ -76,6 +76,7 @@ type DingtalkAccessTokenResp struct {
type DingtalkUser struct {
UserId string `json:"userid"`
UnionId string `json:"unionid"`
Name string `json:"name"`
Department []int `json:"dept_id_list"`
Position string `json:"title"`
@@ -353,10 +354,11 @@ func (p *DingtalkSyncerProvider) getDingtalkUsers() ([]*OriginalUser, error) {
// dingtalkUserToOriginalUser converts DingTalk user to Casdoor OriginalUser
func (p *DingtalkSyncerProvider) dingtalkUserToOriginalUser(dingtalkUser *DingtalkUser) *OriginalUser {
// Use job number as name if available, otherwise use userId
// Use unionid as name to be consistent with OAuth provider
// Fallback to userId if unionid is not available
userName := dingtalkUser.UserId
if dingtalkUser.JobNumber != "" {
userName = dingtalkUser.JobNumber
if dingtalkUser.UnionId != "" {
userName = dingtalkUser.UnionId
}
user := &OriginalUser{
@@ -382,3 +384,15 @@ func (p *DingtalkSyncerProvider) dingtalkUserToOriginalUser(dingtalkUser *Dingta
return user
}
// GetOriginalGroups retrieves all groups from DingTalk (not implemented yet)
func (p *DingtalkSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement DingTalk group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *DingtalkSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement DingTalk user group membership sync
return []string{}, nil
}

View File

@@ -101,6 +101,7 @@ func (p *GoogleWorkspaceSyncerProvider) getAdminService() (*admin.Service, error
PrivateKey: []byte(serviceAccount.PrivateKey),
Scopes: []string{
admin.AdminDirectoryUserReadonlyScope,
admin.AdminDirectoryGroupReadonlyScope,
},
TokenURL: google.JWTTokenURL,
Subject: adminEmail, // Impersonate the admin user
@@ -202,12 +203,189 @@ func (p *GoogleWorkspaceSyncerProvider) getGoogleWorkspaceOriginalUsers() ([]*Or
return nil, err
}
// Get all groups and their members to build a user-to-groups mapping
// This avoids N+1 queries by fetching group memberships upfront
userGroupsMap, err := p.buildUserGroupsMap(service)
if err != nil {
fmt.Printf("Warning: failed to fetch group memberships: %v. Users will have no groups assigned.\n", err)
userGroupsMap = make(map[string][]string)
}
// Convert Google Workspace users to Casdoor OriginalUser
originalUsers := []*OriginalUser{}
for _, gwUser := range gwUsers {
originalUser := p.googleWorkspaceUserToOriginalUser(gwUser)
// Assign groups from the pre-built map
if groups, exists := userGroupsMap[gwUser.PrimaryEmail]; exists {
originalUser.Groups = groups
} else {
originalUser.Groups = []string{}
}
originalUsers = append(originalUsers, originalUser)
}
return originalUsers, nil
}
// buildUserGroupsMap builds a map of user email to group emails by iterating through all groups
// and their members. This is more efficient than querying groups for each user individually.
func (p *GoogleWorkspaceSyncerProvider) buildUserGroupsMap(service *admin.Service) (map[string][]string, error) {
userGroupsMap := make(map[string][]string)
// Get all groups
groups, err := p.getGoogleWorkspaceGroups(service)
if err != nil {
return nil, fmt.Errorf("failed to fetch groups: %v", err)
}
// For each group, get its members and populate the user-to-groups map
for _, group := range groups {
members, err := p.getGroupMembers(service, group.Id)
if err != nil {
fmt.Printf("Warning: failed to get members for group %s: %v\n", group.Email, err)
continue
}
// Add this group to each member's group list
for _, member := range members {
userGroupsMap[member.Email] = append(userGroupsMap[member.Email], group.Email)
}
}
return userGroupsMap, nil
}
// getGroupMembers retrieves all members of a specific group
func (p *GoogleWorkspaceSyncerProvider) getGroupMembers(service *admin.Service, groupId string) ([]*admin.Member, error) {
allMembers := []*admin.Member{}
pageToken := ""
for {
call := service.Members.List(groupId).MaxResults(500)
if pageToken != "" {
call = call.PageToken(pageToken)
}
resp, err := call.Do()
if err != nil {
return nil, fmt.Errorf("failed to list members: %v", err)
}
allMembers = append(allMembers, resp.Members...)
// Handle pagination
if resp.NextPageToken == "" {
break
}
pageToken = resp.NextPageToken
}
return allMembers, nil
}
// GetOriginalGroups retrieves all groups from Google Workspace
func (p *GoogleWorkspaceSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// Get Admin SDK service
service, err := p.getAdminService()
if err != nil {
return nil, err
}
// Get all groups from Google Workspace
gwGroups, err := p.getGoogleWorkspaceGroups(service)
if err != nil {
return nil, err
}
// Convert Google Workspace groups to Casdoor OriginalGroup
originalGroups := []*OriginalGroup{}
for _, gwGroup := range gwGroups {
originalGroup := p.googleWorkspaceGroupToOriginalGroup(gwGroup)
originalGroups = append(originalGroups, originalGroup)
}
return originalGroups, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to
func (p *GoogleWorkspaceSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// Get Admin SDK service
service, err := p.getAdminService()
if err != nil {
return nil, err
}
// Get groups for the user
groupIds := []string{}
pageToken := ""
for {
call := service.Groups.List().UserKey(userId)
if pageToken != "" {
call = call.PageToken(pageToken)
}
resp, err := call.Do()
if err != nil {
return nil, fmt.Errorf("failed to list user groups: %v", err)
}
for _, group := range resp.Groups {
groupIds = append(groupIds, group.Email)
}
// Handle pagination
if resp.NextPageToken == "" {
break
}
pageToken = resp.NextPageToken
}
return groupIds, nil
}
// getGoogleWorkspaceGroups gets all groups from Google Workspace using Admin SDK API
func (p *GoogleWorkspaceSyncerProvider) getGoogleWorkspaceGroups(service *admin.Service) ([]*admin.Group, error) {
allGroups := []*admin.Group{}
pageToken := ""
// Get the customer ID (use "my_customer" for the domain)
customer := "my_customer"
for {
call := service.Groups.List().Customer(customer).MaxResults(500)
if pageToken != "" {
call = call.PageToken(pageToken)
}
resp, err := call.Do()
if err != nil {
return nil, fmt.Errorf("failed to list groups: %v", err)
}
allGroups = append(allGroups, resp.Groups...)
// Handle pagination
if resp.NextPageToken == "" {
break
}
pageToken = resp.NextPageToken
}
return allGroups, nil
}
// googleWorkspaceGroupToOriginalGroup converts Google Workspace group to Casdoor OriginalGroup
func (p *GoogleWorkspaceSyncerProvider) googleWorkspaceGroupToOriginalGroup(gwGroup *admin.Group) *OriginalGroup {
group := &OriginalGroup{
Id: gwGroup.Id,
Name: gwGroup.Email,
DisplayName: gwGroup.Name,
Description: gwGroup.Description,
Email: gwGroup.Email,
}
return group
}

View File

@@ -0,0 +1,204 @@
// Copyright 2025 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 (
"testing"
admin "google.golang.org/api/admin/directory/v1"
)
func TestGoogleWorkspaceUserToOriginalUser(t *testing.T) {
provider := &GoogleWorkspaceSyncerProvider{
Syncer: &Syncer{},
}
// Test case 1: Full Google Workspace user with all fields
gwUser := &admin.User{
Id: "user-123",
PrimaryEmail: "john.doe@example.com",
Name: &admin.UserName{
FullName: "John Doe",
GivenName: "John",
FamilyName: "Doe",
},
ThumbnailPhotoUrl: "https://example.com/avatar.jpg",
Suspended: false,
IsAdmin: true,
CreationTime: "2024-01-01T00:00:00Z",
}
originalUser := provider.googleWorkspaceUserToOriginalUser(gwUser)
// Verify basic fields
if originalUser.Id != "user-123" {
t.Errorf("Expected Id to be 'user-123', got '%s'", originalUser.Id)
}
if originalUser.Name != "john.doe@example.com" {
t.Errorf("Expected Name to be 'john.doe@example.com', got '%s'", originalUser.Name)
}
if originalUser.Email != "john.doe@example.com" {
t.Errorf("Expected Email to be 'john.doe@example.com', got '%s'", originalUser.Email)
}
if originalUser.DisplayName != "John Doe" {
t.Errorf("Expected DisplayName to be 'John Doe', got '%s'", originalUser.DisplayName)
}
if originalUser.FirstName != "John" {
t.Errorf("Expected FirstName to be 'John', got '%s'", originalUser.FirstName)
}
if originalUser.LastName != "Doe" {
t.Errorf("Expected LastName to be 'Doe', got '%s'", originalUser.LastName)
}
if originalUser.Avatar != "https://example.com/avatar.jpg" {
t.Errorf("Expected Avatar to be 'https://example.com/avatar.jpg', got '%s'", originalUser.Avatar)
}
if originalUser.IsForbidden != false {
t.Errorf("Expected IsForbidden to be false for non-suspended user, got %v", originalUser.IsForbidden)
}
if originalUser.IsAdmin != true {
t.Errorf("Expected IsAdmin to be true, got %v", originalUser.IsAdmin)
}
// Test case 2: Suspended Google Workspace user
suspendedUser := &admin.User{
Id: "user-456",
PrimaryEmail: "jane.doe@example.com",
Name: &admin.UserName{
FullName: "Jane Doe",
},
Suspended: true,
}
suspendedOriginalUser := provider.googleWorkspaceUserToOriginalUser(suspendedUser)
if suspendedOriginalUser.IsForbidden != true {
t.Errorf("Expected IsForbidden to be true for suspended user, got %v", suspendedOriginalUser.IsForbidden)
}
// Test case 3: User with no Name object (should not panic)
minimalUser := &admin.User{
Id: "user-789",
PrimaryEmail: "bob@example.com",
}
minimalOriginalUser := provider.googleWorkspaceUserToOriginalUser(minimalUser)
if minimalOriginalUser.DisplayName != "" {
t.Errorf("Expected DisplayName to be empty for minimal user, got '%s'", minimalOriginalUser.DisplayName)
}
// Test case 4: Display name construction from first/last name when FullName is empty
noFullNameUser := &admin.User{
Id: "user-101",
PrimaryEmail: "alice@example.com",
Name: &admin.UserName{
GivenName: "Alice",
FamilyName: "Jones",
},
}
noFullNameOriginalUser := provider.googleWorkspaceUserToOriginalUser(noFullNameUser)
if noFullNameOriginalUser.DisplayName != "Alice Jones" {
t.Errorf("Expected DisplayName to be constructed as 'Alice Jones', got '%s'", noFullNameOriginalUser.DisplayName)
}
}
func TestGoogleWorkspaceGroupToOriginalGroup(t *testing.T) {
provider := &GoogleWorkspaceSyncerProvider{
Syncer: &Syncer{},
}
// Test case 1: Full Google Workspace group with all fields
gwGroup := &admin.Group{
Id: "group-123",
Email: "team@example.com",
Name: "Engineering Team",
Description: "All engineering staff",
}
originalGroup := provider.googleWorkspaceGroupToOriginalGroup(gwGroup)
// Verify all fields
if originalGroup.Id != "group-123" {
t.Errorf("Expected Id to be 'group-123', got '%s'", originalGroup.Id)
}
if originalGroup.Name != "team@example.com" {
t.Errorf("Expected Name to be 'team@example.com', got '%s'", originalGroup.Name)
}
if originalGroup.DisplayName != "Engineering Team" {
t.Errorf("Expected DisplayName to be 'Engineering Team', got '%s'", originalGroup.DisplayName)
}
if originalGroup.Description != "All engineering staff" {
t.Errorf("Expected Description to be 'All engineering staff', got '%s'", originalGroup.Description)
}
if originalGroup.Email != "team@example.com" {
t.Errorf("Expected Email to be 'team@example.com', got '%s'", originalGroup.Email)
}
// Test case 2: Minimal group
minimalGroup := &admin.Group{
Id: "group-456",
Email: "minimal@example.com",
}
minimalOriginalGroup := provider.googleWorkspaceGroupToOriginalGroup(minimalGroup)
if minimalOriginalGroup.DisplayName != "" {
t.Errorf("Expected DisplayName to be empty for minimal group, got '%s'", minimalOriginalGroup.DisplayName)
}
if minimalOriginalGroup.Description != "" {
t.Errorf("Expected Description to be empty for minimal group, got '%s'", minimalOriginalGroup.Description)
}
}
func TestGetSyncerProviderGoogleWorkspace(t *testing.T) {
syncer := &Syncer{
Type: "Google Workspace",
Host: "admin@example.com",
}
provider := GetSyncerProvider(syncer)
if _, ok := provider.(*GoogleWorkspaceSyncerProvider); !ok {
t.Errorf("Expected GoogleWorkspaceSyncerProvider for type 'Google Workspace', got %T", provider)
}
}
func TestGoogleWorkspaceSyncerProviderEmptyMethods(t *testing.T) {
provider := &GoogleWorkspaceSyncerProvider{
Syncer: &Syncer{},
}
// Test AddUser returns error
_, err := provider.AddUser(&OriginalUser{})
if err == nil {
t.Error("Expected AddUser to return error for read-only syncer")
}
// Test UpdateUser returns error
_, err = provider.UpdateUser(&OriginalUser{})
if err == nil {
t.Error("Expected UpdateUser to return error for read-only syncer")
}
// Test Close returns no error
err = provider.Close()
if err != nil {
t.Errorf("Expected Close to return nil, got error: %v", err)
}
// Test InitAdapter returns no error
err = provider.InitAdapter()
if err != nil {
t.Errorf("Expected InitAdapter to return nil, got error: %v", err)
}
}

121
object/syncer_group.go Normal file
View File

@@ -0,0 +1,121 @@
// Copyright 2025 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"
"github.com/casdoor/casdoor/util"
)
func (syncer *Syncer) getOriginalGroups() ([]*OriginalGroup, error) {
provider := GetSyncerProvider(syncer)
return provider.GetOriginalGroups()
}
func (syncer *Syncer) createGroupFromOriginalGroup(originalGroup *OriginalGroup) *Group {
group := &Group{
Owner: syncer.Organization,
Name: originalGroup.Name,
CreatedTime: util.GetCurrentTime(),
UpdatedTime: util.GetCurrentTime(),
DisplayName: originalGroup.DisplayName,
Type: originalGroup.Type,
Manager: originalGroup.Manager,
IsEnabled: true,
IsTopGroup: true,
}
if originalGroup.Email != "" {
group.ContactEmail = originalGroup.Email
}
return group
}
func (syncer *Syncer) syncGroups() error {
fmt.Printf("Running syncGroups()..\n")
// Get existing groups from Casdoor
groups, err := GetGroups(syncer.Organization)
if err != nil {
line := fmt.Sprintf("[%s] %s\n", util.GetCurrentTime(), err.Error())
_, err2 := updateSyncerErrorText(syncer, line)
if err2 != nil {
panic(err2)
}
return err
}
// Get groups from the external system
oGroups, err := syncer.getOriginalGroups()
if err != nil {
line := fmt.Sprintf("[%s] %s\n", util.GetCurrentTime(), err.Error())
_, err2 := updateSyncerErrorText(syncer, line)
if err2 != nil {
panic(err2)
}
return err
}
fmt.Printf("Groups: %d, oGroups: %d\n", len(groups), len(oGroups))
// Create a map of existing groups by name
myGroups := map[string]*Group{}
for _, group := range groups {
myGroups[group.Name] = group
}
// Sync groups from external system to Casdoor
newGroups := []*Group{}
for _, oGroup := range oGroups {
if _, ok := myGroups[oGroup.Name]; !ok {
newGroup := syncer.createGroupFromOriginalGroup(oGroup)
fmt.Printf("New group: %v\n", newGroup)
newGroups = append(newGroups, newGroup)
} else {
// Group already exists, could update it here if needed
existingGroup := myGroups[oGroup.Name]
// Update group display name and other fields if they've changed
if existingGroup.DisplayName != oGroup.DisplayName {
existingGroup.DisplayName = oGroup.DisplayName
existingGroup.UpdatedTime = util.GetCurrentTime()
_, err = UpdateGroup(existingGroup.GetId(), existingGroup)
if err != nil {
fmt.Printf("Failed to update group %s: %v\n", existingGroup.Name, err)
} else {
fmt.Printf("Updated group: %s\n", existingGroup.Name)
}
}
}
}
if len(newGroups) != 0 {
_, err = AddGroupsInBatch(newGroups)
if err != nil {
return err
}
}
return nil
}
func (syncer *Syncer) syncGroupsNoError() {
err := syncer.syncGroups()
if err != nil {
fmt.Printf("syncGroupsNoError() error: %s\n", err.Error())
}
}

View File

@@ -14,6 +14,17 @@
package object
// OriginalGroup represents a group from an external system
type OriginalGroup struct {
Id string
Name string
DisplayName string
Description string
Type string
Manager string
Email string
}
// SyncerProvider defines the interface that all syncer implementations must satisfy.
// Different syncer types (Database, Keycloak, WeCom, Azure AD) implement this interface.
type SyncerProvider interface {
@@ -23,6 +34,12 @@ type SyncerProvider interface {
// GetOriginalUsers retrieves all users from the external system
GetOriginalUsers() ([]*OriginalUser, error)
// GetOriginalGroups retrieves all groups from the external system
GetOriginalGroups() ([]*OriginalGroup, error)
// GetOriginalUserGroups retrieves the group IDs that a user belongs to
GetOriginalUserGroups(userId string) ([]string, error)
// AddUser adds a new user to the external system
AddUser(user *OriginalUser) (bool, error)
@@ -49,6 +66,12 @@ func GetSyncerProvider(syncer *Syncer) SyncerProvider {
return &ActiveDirectorySyncerProvider{Syncer: syncer}
case "DingTalk":
return &DingtalkSyncerProvider{Syncer: syncer}
case "Lark":
return &LarkSyncerProvider{Syncer: syncer}
case "Okta":
return &OktaSyncerProvider{Syncer: syncer}
case "SCIM":
return &SCIMSyncerProvider{Syncer: syncer}
case "Keycloak":
return &KeycloakSyncerProvider{
DatabaseSyncerProvider: DatabaseSyncerProvider{Syncer: syncer},

View File

@@ -29,3 +29,15 @@ func (p *KeycloakSyncerProvider) GetOriginalUsers() ([]*OriginalUser, error) {
// Note: Keycloak-specific user mapping is handled in syncer_util.go
// via getOriginalUsersFromMap which checks syncer.Type == "Keycloak"
// GetOriginalGroups retrieves all groups from Keycloak (not implemented yet)
func (p *KeycloakSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Keycloak group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *KeycloakSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Keycloak user group membership sync
return []string{}, nil
}

428
object/syncer_lark.go Normal file
View File

@@ -0,0 +1,428 @@
// Copyright 2025 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 (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/casdoor/casdoor/util"
)
// LarkSyncerProvider implements SyncerProvider for Lark API-based syncers
type LarkSyncerProvider struct {
Syncer *Syncer
}
// InitAdapter initializes the Lark syncer (no database adapter needed)
func (p *LarkSyncerProvider) InitAdapter() error {
// Lark syncer doesn't need database adapter
return nil
}
// GetOriginalUsers retrieves all users from Lark API
func (p *LarkSyncerProvider) GetOriginalUsers() ([]*OriginalUser, error) {
return p.getLarkUsers()
}
// AddUser adds a new user to Lark (not supported for read-only API)
func (p *LarkSyncerProvider) AddUser(user *OriginalUser) (bool, error) {
// Lark syncer is typically read-only
return false, fmt.Errorf("adding users to Lark is not supported")
}
// UpdateUser updates an existing user in Lark (not supported for read-only API)
func (p *LarkSyncerProvider) UpdateUser(user *OriginalUser) (bool, error) {
// Lark syncer is typically read-only
return false, fmt.Errorf("updating users in Lark is not supported")
}
// TestConnection tests the Lark API connection
func (p *LarkSyncerProvider) TestConnection() error {
_, err := p.getLarkAccessToken()
return err
}
// Close closes any open connections (no-op for Lark API-based syncer)
func (p *LarkSyncerProvider) Close() error {
// Lark syncer doesn't maintain persistent connections
return nil
}
type LarkAccessTokenResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
TenantAccessToken string `json:"tenant_access_token"`
Expire int `json:"expire"`
}
type LarkUser struct {
UserId string `json:"user_id"`
UnionId string `json:"union_id"`
OpenId string `json:"open_id"`
Name string `json:"name"`
EnName string `json:"en_name"`
Email string `json:"email"`
Mobile string `json:"mobile"`
Gender int `json:"gender"`
Avatar *LarkAvatar `json:"avatar"`
Status *LarkStatus `json:"status"`
DepartmentIds []string `json:"department_ids"`
JobTitle string `json:"job_title"`
}
type LarkAvatar struct {
Avatar72 string `json:"avatar_72"`
Avatar240 string `json:"avatar_240"`
Avatar640 string `json:"avatar_640"`
AvatarOrigin string `json:"avatar_origin"`
}
type LarkStatus struct {
IsFrozen bool `json:"is_frozen"`
IsResigned bool `json:"is_resigned"`
IsActivated bool `json:"is_activated"`
IsExited bool `json:"is_exited"`
}
type LarkUserListResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Items []*LarkUser `json:"items"`
HasMore bool `json:"has_more"`
PageToken string `json:"page_token"`
} `json:"data"`
}
type LarkDeptListResp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data struct {
Items []struct {
DepartmentId string `json:"department_id"`
} `json:"items"`
HasMore bool `json:"has_more"`
PageToken string `json:"page_token"`
} `json:"data"`
}
// getLarkDomain returns the Lark API domain based on whether global endpoint is used
func (p *LarkSyncerProvider) getLarkDomain() string {
// syncer.Host can be used to specify custom endpoint
// If empty, default to global endpoint (larksuite.com)
if p.Syncer.Host != "" {
return p.Syncer.Host
}
return "https://open.larksuite.com"
}
// getLarkAccessToken gets access token from Lark API
func (p *LarkSyncerProvider) getLarkAccessToken() (string, error) {
// syncer.User should be the app_id
// syncer.Password should be the app_secret
appId := p.Syncer.User
if appId == "" {
return "", fmt.Errorf("app_id (user field) is required for Lark syncer")
}
appSecret := p.Syncer.Password
if appSecret == "" {
return "", fmt.Errorf("app_secret (password field) is required for Lark syncer")
}
domain := p.getLarkDomain()
apiUrl := fmt.Sprintf("%s/open-apis/auth/v3/tenant_access_token/internal", domain)
postData := map[string]string{
"app_id": appId,
"app_secret": appSecret,
}
data, err := p.postJSON(apiUrl, postData)
if err != nil {
return "", err
}
var tokenResp LarkAccessTokenResp
err = json.Unmarshal(data, &tokenResp)
if err != nil {
return "", err
}
if tokenResp.Code != 0 {
return "", fmt.Errorf("failed to get access token: code=%d, msg=%s",
tokenResp.Code, tokenResp.Msg)
}
return tokenResp.TenantAccessToken, nil
}
// getLarkDepartments gets all department IDs from Lark API
func (p *LarkSyncerProvider) getLarkDepartments(accessToken string) ([]string, error) {
domain := p.getLarkDomain()
allDeptIds := []string{"0"} // Start with root department
pageToken := ""
for {
apiUrl := fmt.Sprintf("%s/open-apis/contact/v3/departments?parent_department_id=0&fetch_child=true&page_size=50", domain)
if pageToken != "" {
apiUrl += fmt.Sprintf("&page_token=%s", pageToken)
}
data, err := p.getWithAuth(apiUrl, accessToken)
if err != nil {
return nil, err
}
var deptResp LarkDeptListResp
err = json.Unmarshal(data, &deptResp)
if err != nil {
return nil, err
}
if deptResp.Code != 0 {
return nil, fmt.Errorf("failed to get departments: code=%d, msg=%s",
deptResp.Code, deptResp.Msg)
}
for _, dept := range deptResp.Data.Items {
allDeptIds = append(allDeptIds, dept.DepartmentId)
}
if !deptResp.Data.HasMore {
break
}
pageToken = deptResp.Data.PageToken
}
return allDeptIds, nil
}
// getLarkUsersFromDept gets users from a specific department
func (p *LarkSyncerProvider) getLarkUsersFromDept(accessToken string, deptId string) ([]*LarkUser, error) {
domain := p.getLarkDomain()
allUsers := []*LarkUser{}
pageToken := ""
for {
apiUrl := fmt.Sprintf("%s/open-apis/contact/v3/users/find_by_department?department_id=%s&page_size=50", domain, deptId)
if pageToken != "" {
apiUrl += fmt.Sprintf("&page_token=%s", pageToken)
}
data, err := p.getWithAuth(apiUrl, accessToken)
if err != nil {
return nil, err
}
var userResp LarkUserListResp
err = json.Unmarshal(data, &userResp)
if err != nil {
return nil, err
}
if userResp.Code != 0 {
return nil, fmt.Errorf("failed to get users from dept %s: code=%d, msg=%s",
deptId, userResp.Code, userResp.Msg)
}
allUsers = append(allUsers, userResp.Data.Items...)
if !userResp.Data.HasMore {
break
}
pageToken = userResp.Data.PageToken
}
return allUsers, nil
}
// postJSON sends a POST request with JSON body
func (p *LarkSyncerProvider) postJSON(url string, data interface{}) ([]byte, error) {
jsonData, err := json.Marshal(data)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return respData, nil
}
// getWithAuth sends a GET request with authorization header
func (p *LarkSyncerProvider) getWithAuth(url string, accessToken string) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return data, nil
}
// getLarkUsers gets all users from Lark API
func (p *LarkSyncerProvider) getLarkUsers() ([]*OriginalUser, error) {
// Get access token
accessToken, err := p.getLarkAccessToken()
if err != nil {
return nil, err
}
// Get all departments
deptIds, err := p.getLarkDepartments(accessToken)
if err != nil {
return nil, err
}
// Get users from all departments (deduplicate by user_id)
userMap := make(map[string]*LarkUser)
for _, deptId := range deptIds {
users, err := p.getLarkUsersFromDept(accessToken, deptId)
if err != nil {
return nil, err
}
for _, user := range users {
// Deduplicate users by user_id
if _, exists := userMap[user.UserId]; !exists {
userMap[user.UserId] = user
}
}
}
// Convert Lark users to Casdoor OriginalUser
originalUsers := []*OriginalUser{}
for _, larkUser := range userMap {
originalUser := p.larkUserToOriginalUser(larkUser)
originalUsers = append(originalUsers, originalUser)
}
return originalUsers, nil
}
// larkUserToOriginalUser converts Lark user to Casdoor OriginalUser
func (p *LarkSyncerProvider) larkUserToOriginalUser(larkUser *LarkUser) *OriginalUser {
// Use user_id as name, fallback to union_id or open_id
userName := larkUser.UserId
if userName == "" && larkUser.UnionId != "" {
userName = larkUser.UnionId
}
if userName == "" && larkUser.OpenId != "" {
userName = larkUser.OpenId
}
user := &OriginalUser{
Id: larkUser.UserId,
Name: userName,
DisplayName: larkUser.Name,
Email: larkUser.Email,
Phone: larkUser.Mobile,
Title: larkUser.JobTitle,
Address: []string{},
Properties: map[string]string{},
Groups: []string{},
}
// Set avatar if available
if larkUser.Avatar != nil {
if larkUser.Avatar.Avatar240 != "" {
user.Avatar = larkUser.Avatar.Avatar240
} else if larkUser.Avatar.Avatar72 != "" {
user.Avatar = larkUser.Avatar.Avatar72
}
}
// Set gender
switch larkUser.Gender {
case 1:
user.Gender = "Male"
case 2:
user.Gender = "Female"
default:
user.Gender = ""
}
// Set IsForbidden based on status
// User is forbidden if frozen, resigned, not activated, or exited
if larkUser.Status != nil {
if larkUser.Status.IsFrozen || larkUser.Status.IsResigned || !larkUser.Status.IsActivated || larkUser.Status.IsExited {
user.IsForbidden = true
} else {
user.IsForbidden = false
}
}
// Set CreatedTime to current time if not set
if user.CreatedTime == "" {
user.CreatedTime = util.GetCurrentTime()
}
return user
}
// GetOriginalGroups retrieves all groups from Lark (not implemented yet)
func (p *LarkSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Lark group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *LarkSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Lark user group membership sync
return []string{}, nil
}

310
object/syncer_okta.go Normal file
View File

@@ -0,0 +1,310 @@
// Copyright 2025 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"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/casdoor/casdoor/util"
)
// OktaSyncerProvider implements SyncerProvider for Okta API-based syncers
type OktaSyncerProvider struct {
Syncer *Syncer
}
// InitAdapter initializes the Okta syncer (no database adapter needed)
func (p *OktaSyncerProvider) InitAdapter() error {
// Okta syncer doesn't need database adapter
return nil
}
// GetOriginalUsers retrieves all users from Okta API
func (p *OktaSyncerProvider) GetOriginalUsers() ([]*OriginalUser, error) {
return p.getOktaOriginalUsers()
}
// AddUser adds a new user to Okta (not supported for read-only API)
func (p *OktaSyncerProvider) AddUser(user *OriginalUser) (bool, error) {
// Okta syncer is typically read-only
return false, fmt.Errorf("adding users to Okta is not supported")
}
// UpdateUser updates an existing user in Okta (not supported for read-only API)
func (p *OktaSyncerProvider) UpdateUser(user *OriginalUser) (bool, error) {
// Okta syncer is typically read-only
return false, fmt.Errorf("updating users in Okta is not supported")
}
// TestConnection tests the Okta API connection
func (p *OktaSyncerProvider) TestConnection() error {
// Try to fetch first page of users to verify connection
_, _, err := p.getOktaUsers("")
return err
}
// Close closes any open connections (no-op for Okta API-based syncer)
func (p *OktaSyncerProvider) Close() error {
// Okta syncer doesn't maintain persistent connections
return nil
}
// OktaUser represents a user from Okta API
type OktaUser struct {
Id string `json:"id"`
Status string `json:"status"`
Created string `json:"created"`
Profile struct {
Login string `json:"login"`
Email string `json:"email"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
DisplayName string `json:"displayName"`
MobilePhone string `json:"mobilePhone"`
PrimaryPhone string `json:"primaryPhone"`
StreetAddress string `json:"streetAddress"`
City string `json:"city"`
State string `json:"state"`
ZipCode string `json:"zipCode"`
CountryCode string `json:"countryCode"`
PostalAddress string `json:"postalAddress"`
PreferredLanguage string `json:"preferredLanguage"`
Locale string `json:"locale"`
Timezone string `json:"timezone"`
Title string `json:"title"`
Department string `json:"department"`
Organization string `json:"organization"`
} `json:"profile"`
}
// parseLinkHeader parses the HTTP Link header
// Format: <https://example.com/api/v1/users?after=xyz>; rel="next"
func parseLinkHeader(header string) map[string]string {
links := make(map[string]string)
parts := strings.Split(header, ",")
for _, part := range parts {
section := strings.Split(strings.TrimSpace(part), ";")
if len(section) != 2 {
continue
}
url := strings.Trim(strings.TrimSpace(section[0]), "<>")
rel := strings.TrimSpace(section[1])
if strings.HasPrefix(rel, "rel=\"") && strings.HasSuffix(rel, "\"") {
relValue := rel[5 : len(rel)-1]
links[relValue] = url
}
}
return links
}
// getOktaUsers retrieves users from Okta API with pagination support
// Returns users and the next page link (if any)
func (p *OktaSyncerProvider) getOktaUsers(nextLink string) ([]*OktaUser, string, error) {
// syncer.Host should be the Okta domain (e.g., "dev-12345.okta.com" or full URL)
// syncer.Password should be the API token
domain := p.Syncer.Host
if domain == "" {
return nil, "", fmt.Errorf("Okta domain (host field) is required for Okta syncer")
}
apiToken := p.Syncer.Password
if apiToken == "" {
return nil, "", fmt.Errorf("API token (password field) is required for Okta syncer")
}
// Construct API URL
var apiUrl string
if nextLink != "" {
apiUrl = nextLink
} else {
// Remove https:// or http:// prefix if present in domain
domain = strings.TrimPrefix(strings.TrimPrefix(domain, "https://"), "http://")
apiUrl = fmt.Sprintf("https://%s/api/v1/users?limit=200", domain)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", apiUrl, nil)
if err != nil {
return nil, "", err
}
req.Header.Set("Authorization", "SSWS "+apiToken)
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, "", fmt.Errorf("failed to get users from Okta: status=%d, body=%s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, "", err
}
var users []*OktaUser
err = json.Unmarshal(body, &users)
if err != nil {
return nil, "", err
}
// Parse Link header for next page
// Link header format: <https://...>; rel="next"
nextPageLink := ""
linkHeader := resp.Header.Get("Link")
if linkHeader != "" {
links := parseLinkHeader(linkHeader)
if next, ok := links["next"]; ok {
nextPageLink = next
}
}
return users, nextPageLink, nil
}
// oktaUserToOriginalUser converts Okta user to Casdoor OriginalUser
func (p *OktaSyncerProvider) oktaUserToOriginalUser(oktaUser *OktaUser) *OriginalUser {
user := &OriginalUser{
Id: oktaUser.Id,
Name: oktaUser.Profile.Login,
DisplayName: oktaUser.Profile.DisplayName,
FirstName: oktaUser.Profile.FirstName,
LastName: oktaUser.Profile.LastName,
Email: oktaUser.Profile.Email,
Phone: oktaUser.Profile.MobilePhone,
Title: oktaUser.Profile.Title,
Language: oktaUser.Profile.PreferredLanguage,
Address: []string{},
Properties: map[string]string{},
Groups: []string{},
}
// Build address from street, city, state, zip
if oktaUser.Profile.StreetAddress != "" {
user.Address = append(user.Address, oktaUser.Profile.StreetAddress)
}
if oktaUser.Profile.City != "" {
user.Address = append(user.Address, oktaUser.Profile.City)
}
if oktaUser.Profile.State != "" {
user.Address = append(user.Address, oktaUser.Profile.State)
}
if oktaUser.Profile.ZipCode != "" {
user.Address = append(user.Address, oktaUser.Profile.ZipCode)
}
// Store additional properties
if oktaUser.Profile.Department != "" {
user.Properties["department"] = oktaUser.Profile.Department
}
if oktaUser.Profile.Organization != "" {
user.Properties["organization"] = oktaUser.Profile.Organization
}
if oktaUser.Profile.Timezone != "" {
user.Properties["timezone"] = oktaUser.Profile.Timezone
}
// Set IsForbidden based on status
// Okta status values: STAGED, PROVISIONED, ACTIVE, RECOVERY, PASSWORD_EXPIRED, LOCKED_OUT, SUSPENDED, DEPROVISIONED
if oktaUser.Status == "SUSPENDED" || oktaUser.Status == "DEPROVISIONED" || oktaUser.Status == "LOCKED_OUT" {
user.IsForbidden = true
} else {
user.IsForbidden = false
}
// If display name is empty, construct from first and last name
if user.DisplayName == "" && (user.FirstName != "" || user.LastName != "") {
user.DisplayName = strings.TrimSpace(fmt.Sprintf("%s %s", user.FirstName, user.LastName))
}
// If email is empty, use login as email (typically login is an email)
if user.Email == "" && oktaUser.Profile.Login != "" {
user.Email = oktaUser.Profile.Login
}
// If mobile phone is empty, try primary phone
if user.Phone == "" && oktaUser.Profile.PrimaryPhone != "" {
user.Phone = oktaUser.Profile.PrimaryPhone
}
// Set CreatedTime to current time if not set
if user.CreatedTime == "" {
user.CreatedTime = util.GetCurrentTime()
}
return user
}
// getOktaOriginalUsers is the main entry point for Okta syncer
func (p *OktaSyncerProvider) getOktaOriginalUsers() ([]*OriginalUser, error) {
allUsers := []*OktaUser{}
nextLink := ""
// Fetch all users with pagination
for {
users, next, err := p.getOktaUsers(nextLink)
if err != nil {
return nil, err
}
allUsers = append(allUsers, users...)
// If there's no next link, we've fetched all users
if next == "" {
break
}
nextLink = next
}
// Convert Okta users to Casdoor OriginalUser
originalUsers := []*OriginalUser{}
for _, oktaUser := range allUsers {
originalUser := p.oktaUserToOriginalUser(oktaUser)
originalUsers = append(originalUsers, originalUser)
}
return originalUsers, nil
}
// GetOriginalGroups retrieves all groups from Okta (not implemented yet)
func (p *OktaSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement Okta group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *OktaSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement Okta user group membership sync
return []string{}, nil
}

349
object/syncer_scim.go Normal file
View File

@@ -0,0 +1,349 @@
// Copyright 2025 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"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/casdoor/casdoor/util"
)
// SCIMSyncerProvider implements SyncerProvider for SCIM 2.0 API-based syncers
type SCIMSyncerProvider struct {
Syncer *Syncer
}
// InitAdapter initializes the SCIM syncer (no database adapter needed)
func (p *SCIMSyncerProvider) InitAdapter() error {
// SCIM syncer doesn't need database adapter
return nil
}
// GetOriginalUsers retrieves all users from SCIM API
func (p *SCIMSyncerProvider) GetOriginalUsers() ([]*OriginalUser, error) {
return p.getSCIMUsers()
}
// AddUser adds a new user to SCIM (not supported for read-only API)
func (p *SCIMSyncerProvider) AddUser(user *OriginalUser) (bool, error) {
// SCIM syncer is typically read-only
return false, fmt.Errorf("adding users to SCIM is not supported")
}
// UpdateUser updates an existing user in SCIM (not supported for read-only API)
func (p *SCIMSyncerProvider) UpdateUser(user *OriginalUser) (bool, error) {
// SCIM syncer is typically read-only
return false, fmt.Errorf("updating users in SCIM is not supported")
}
// TestConnection tests the SCIM API connection
func (p *SCIMSyncerProvider) TestConnection() error {
// Test by trying to fetch users with a limit of 1
endpoint := p.buildSCIMEndpoint()
endpoint = fmt.Sprintf("%s?startIndex=1&count=1", endpoint)
req, err := p.createSCIMRequest("GET", endpoint, nil)
if err != nil {
return err
}
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("SCIM connection test failed: status=%d, body=%s", resp.StatusCode, string(body))
}
return nil
}
// Close closes any open connections (no-op for SCIM API-based syncer)
func (p *SCIMSyncerProvider) Close() error {
// SCIM syncer doesn't maintain persistent connections
return nil
}
// SCIMName represents a SCIM user name structure
type SCIMName struct {
FamilyName string `json:"familyName"`
GivenName string `json:"givenName"`
Formatted string `json:"formatted"`
}
// SCIMEmail represents a SCIM user email structure
type SCIMEmail struct {
Value string `json:"value"`
Type string `json:"type"`
Primary bool `json:"primary"`
}
// SCIMPhoneNumber represents a SCIM user phone number structure
type SCIMPhoneNumber struct {
Value string `json:"value"`
Type string `json:"type"`
Primary bool `json:"primary"`
}
// SCIMAddress represents a SCIM user address structure
type SCIMAddress struct {
StreetAddress string `json:"streetAddress"`
Locality string `json:"locality"`
Region string `json:"region"`
PostalCode string `json:"postalCode"`
Country string `json:"country"`
Formatted string `json:"formatted"`
Type string `json:"type"`
Primary bool `json:"primary"`
}
// SCIMUser represents a SCIM 2.0 user resource
type SCIMUser struct {
ID string `json:"id"`
ExternalID string `json:"externalId"`
UserName string `json:"userName"`
Name SCIMName `json:"name"`
DisplayName string `json:"displayName"`
NickName string `json:"nickName"`
ProfileURL string `json:"profileUrl"`
Title string `json:"title"`
UserType string `json:"userType"`
PreferredLan string `json:"preferredLanguage"`
Locale string `json:"locale"`
Timezone string `json:"timezone"`
Active bool `json:"active"`
Emails []SCIMEmail `json:"emails"`
PhoneNumbers []SCIMPhoneNumber `json:"phoneNumbers"`
Addresses []SCIMAddress `json:"addresses"`
}
// SCIMListResponse represents a SCIM list response
type SCIMListResponse struct {
TotalResults int `json:"totalResults"`
ItemsPerPage int `json:"itemsPerPage"`
StartIndex int `json:"startIndex"`
Resources []*SCIMUser `json:"Resources"`
}
// buildSCIMEndpoint builds the SCIM API endpoint URL
func (p *SCIMSyncerProvider) buildSCIMEndpoint() string {
// syncer.Host should be the SCIM server URL (e.g., https://example.com/scim/v2)
host := strings.TrimSuffix(p.Syncer.Host, "/")
return fmt.Sprintf("%s/Users", host)
}
// createSCIMRequest creates an HTTP request with proper authentication
func (p *SCIMSyncerProvider) createSCIMRequest(method, url string, body io.Reader) (*http.Request, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return nil, err
}
// Set SCIM headers
req.Header.Set("Content-Type", "application/scim+json")
req.Header.Set("Accept", "application/scim+json")
// Add authentication
// syncer.User should be the authentication token or username
// syncer.Password should be the password or API key
if p.Syncer.User != "" && p.Syncer.Password != "" {
// Try Basic Auth
req.SetBasicAuth(p.Syncer.User, p.Syncer.Password)
} else if p.Syncer.Password != "" {
// Try Bearer token (assuming password field contains the token)
req.Header.Set("Authorization", "Bearer "+p.Syncer.Password)
} else if p.Syncer.User != "" {
// Try Bearer token (assuming user field contains the token)
req.Header.Set("Authorization", "Bearer "+p.Syncer.User)
}
return req, nil
}
// getSCIMUsers retrieves all users from SCIM API with pagination
func (p *SCIMSyncerProvider) getSCIMUsers() ([]*OriginalUser, error) {
allUsers := []*SCIMUser{}
startIndex := 1
count := 100 // Fetch 100 users per page
for {
endpoint := p.buildSCIMEndpoint()
endpoint = fmt.Sprintf("%s?startIndex=%d&count=%d", endpoint, startIndex, count)
req, err := p.createSCIMRequest("GET", endpoint, nil)
if err != nil {
return nil, err
}
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to get users: status=%d, body=%s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var listResp SCIMListResponse
err = json.Unmarshal(body, &listResp)
if err != nil {
return nil, err
}
allUsers = append(allUsers, listResp.Resources...)
// Check if we've fetched all users
if len(allUsers) >= listResp.TotalResults {
break
}
// Move to the next page
startIndex += count
}
// Convert SCIM users to Casdoor OriginalUser
originalUsers := []*OriginalUser{}
for _, scimUser := range allUsers {
originalUser := p.scimUserToOriginalUser(scimUser)
originalUsers = append(originalUsers, originalUser)
}
return originalUsers, nil
}
// scimUserToOriginalUser converts SCIM user to Casdoor OriginalUser
func (p *SCIMSyncerProvider) scimUserToOriginalUser(scimUser *SCIMUser) *OriginalUser {
user := &OriginalUser{
Id: scimUser.ID,
ExternalId: scimUser.ExternalID,
Name: scimUser.UserName,
DisplayName: scimUser.DisplayName,
FirstName: scimUser.Name.GivenName,
LastName: scimUser.Name.FamilyName,
Title: scimUser.Title,
Language: scimUser.PreferredLan,
Address: []string{},
Properties: map[string]string{},
Groups: []string{},
}
// If display name is from name structure
if user.DisplayName == "" && scimUser.Name.Formatted != "" {
user.DisplayName = scimUser.Name.Formatted
}
// If display name is still empty, construct from first and last name
if user.DisplayName == "" && (user.FirstName != "" || user.LastName != "") {
user.DisplayName = strings.TrimSpace(fmt.Sprintf("%s %s", user.FirstName, user.LastName))
}
// Extract primary email or first email
if len(scimUser.Emails) > 0 {
for _, email := range scimUser.Emails {
if email.Primary {
user.Email = email.Value
break
}
}
// If no primary email, use the first one
if user.Email == "" && len(scimUser.Emails) > 0 {
user.Email = scimUser.Emails[0].Value
}
}
// Extract primary phone or first phone
if len(scimUser.PhoneNumbers) > 0 {
for _, phone := range scimUser.PhoneNumbers {
if phone.Primary {
user.Phone = phone.Value
break
}
}
// If no primary phone, use the first one
if user.Phone == "" && len(scimUser.PhoneNumbers) > 0 {
user.Phone = scimUser.PhoneNumbers[0].Value
}
}
// Extract primary address or first address
if len(scimUser.Addresses) > 0 {
for _, addr := range scimUser.Addresses {
if addr.Primary {
if addr.Formatted != "" {
user.Address = []string{addr.Formatted}
} else {
user.Address = []string{addr.StreetAddress, addr.Locality, addr.Region, addr.PostalCode, addr.Country}
}
user.Location = addr.Locality
user.Region = addr.Region
break
}
}
// If no primary address, use the first one
if len(user.Address) == 0 && len(scimUser.Addresses) > 0 {
addr := scimUser.Addresses[0]
if addr.Formatted != "" {
user.Address = []string{addr.Formatted}
} else {
user.Address = []string{addr.StreetAddress, addr.Locality, addr.Region, addr.PostalCode, addr.Country}
}
user.Location = addr.Locality
user.Region = addr.Region
}
}
// Set IsForbidden based on Active status
user.IsForbidden = !scimUser.Active
// Set CreatedTime to current time if not set
if user.CreatedTime == "" {
user.CreatedTime = util.GetCurrentTime()
}
return user
}
// GetOriginalGroups retrieves all groups from SCIM (not implemented yet)
func (p *SCIMSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement SCIM group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *SCIMSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement SCIM user group membership sync
return []string{}, nil
}

198
object/syncer_scim_test.go Normal file
View File

@@ -0,0 +1,198 @@
// Copyright 2025 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 (
"testing"
)
func TestSCIMUserToOriginalUser(t *testing.T) {
provider := &SCIMSyncerProvider{
Syncer: &Syncer{
Host: "https://example.com/scim/v2",
User: "testuser",
Password: "testtoken",
},
}
// Test case 1: Full SCIM user with all fields
scimUser := &SCIMUser{
ID: "user-123",
ExternalID: "ext-123",
UserName: "john.doe",
DisplayName: "John Doe",
Name: SCIMName{
GivenName: "John",
FamilyName: "Doe",
Formatted: "John Doe",
},
Title: "Software Engineer",
PreferredLan: "en-US",
Active: true,
Emails: []SCIMEmail{
{Value: "john.doe@example.com", Primary: true, Type: "work"},
{Value: "john@personal.com", Primary: false, Type: "home"},
},
PhoneNumbers: []SCIMPhoneNumber{
{Value: "+1-555-1234", Primary: true, Type: "work"},
{Value: "+1-555-5678", Primary: false, Type: "mobile"},
},
Addresses: []SCIMAddress{
{
StreetAddress: "123 Main St",
Locality: "San Francisco",
Region: "CA",
PostalCode: "94102",
Country: "USA",
Formatted: "123 Main St, San Francisco, CA 94102, USA",
Primary: true,
Type: "work",
},
},
}
originalUser := provider.scimUserToOriginalUser(scimUser)
// Verify basic fields
if originalUser.Id != "user-123" {
t.Errorf("Expected Id to be 'user-123', got '%s'", originalUser.Id)
}
if originalUser.ExternalId != "ext-123" {
t.Errorf("Expected ExternalId to be 'ext-123', got '%s'", originalUser.ExternalId)
}
if originalUser.Name != "john.doe" {
t.Errorf("Expected Name to be 'john.doe', got '%s'", originalUser.Name)
}
if originalUser.DisplayName != "John Doe" {
t.Errorf("Expected DisplayName to be 'John Doe', got '%s'", originalUser.DisplayName)
}
if originalUser.FirstName != "John" {
t.Errorf("Expected FirstName to be 'John', got '%s'", originalUser.FirstName)
}
if originalUser.LastName != "Doe" {
t.Errorf("Expected LastName to be 'Doe', got '%s'", originalUser.LastName)
}
if originalUser.Title != "Software Engineer" {
t.Errorf("Expected Title to be 'Software Engineer', got '%s'", originalUser.Title)
}
if originalUser.Language != "en-US" {
t.Errorf("Expected Language to be 'en-US', got '%s'", originalUser.Language)
}
// Verify primary email is selected
if originalUser.Email != "john.doe@example.com" {
t.Errorf("Expected Email to be 'john.doe@example.com', got '%s'", originalUser.Email)
}
// Verify primary phone is selected
if originalUser.Phone != "+1-555-1234" {
t.Errorf("Expected Phone to be '+1-555-1234', got '%s'", originalUser.Phone)
}
// Verify address fields
if originalUser.Location != "San Francisco" {
t.Errorf("Expected Location to be 'San Francisco', got '%s'", originalUser.Location)
}
if originalUser.Region != "CA" {
t.Errorf("Expected Region to be 'CA', got '%s'", originalUser.Region)
}
// Verify active status is inverted to IsForbidden
if originalUser.IsForbidden != false {
t.Errorf("Expected IsForbidden to be false for active user, got %v", originalUser.IsForbidden)
}
// Test case 2: Inactive SCIM user
inactiveUser := &SCIMUser{
ID: "user-456",
UserName: "jane.doe",
Active: false,
}
inactiveOriginalUser := provider.scimUserToOriginalUser(inactiveUser)
if inactiveOriginalUser.IsForbidden != true {
t.Errorf("Expected IsForbidden to be true for inactive user, got %v", inactiveOriginalUser.IsForbidden)
}
// Test case 3: SCIM user with no primary email/phone (should use first)
noPrimaryUser := &SCIMUser{
ID: "user-789",
UserName: "bob.smith",
Emails: []SCIMEmail{
{Value: "bob@example.com", Primary: false, Type: "work"},
{Value: "bob@personal.com", Primary: false, Type: "home"},
},
PhoneNumbers: []SCIMPhoneNumber{
{Value: "+1-555-9999", Primary: false, Type: "work"},
},
}
noPrimaryOriginalUser := provider.scimUserToOriginalUser(noPrimaryUser)
if noPrimaryOriginalUser.Email != "bob@example.com" {
t.Errorf("Expected first email when no primary, got '%s'", noPrimaryOriginalUser.Email)
}
if noPrimaryOriginalUser.Phone != "+1-555-9999" {
t.Errorf("Expected first phone when no primary, got '%s'", noPrimaryOriginalUser.Phone)
}
// Test case 4: Display name construction from first/last name when empty
noDisplayNameUser := &SCIMUser{
ID: "user-101",
UserName: "alice.jones",
Name: SCIMName{
GivenName: "Alice",
FamilyName: "Jones",
},
}
noDisplayNameOriginalUser := provider.scimUserToOriginalUser(noDisplayNameUser)
if noDisplayNameOriginalUser.DisplayName != "Alice Jones" {
t.Errorf("Expected DisplayName to be constructed as 'Alice Jones', got '%s'", noDisplayNameOriginalUser.DisplayName)
}
}
func TestSCIMBuildEndpoint(t *testing.T) {
tests := []struct {
host string
expected string
}{
{"https://example.com/scim/v2", "https://example.com/scim/v2/Users"},
{"https://example.com/scim/v2/", "https://example.com/scim/v2/Users"},
{"http://localhost:8080/scim", "http://localhost:8080/scim/Users"},
}
for _, test := range tests {
provider := &SCIMSyncerProvider{
Syncer: &Syncer{Host: test.host},
}
endpoint := provider.buildSCIMEndpoint()
if endpoint != test.expected {
t.Errorf("For host '%s', expected endpoint '%s', got '%s'", test.host, test.expected, endpoint)
}
}
}
func TestGetSyncerProviderSCIM(t *testing.T) {
syncer := &Syncer{
Type: "SCIM",
Host: "https://example.com/scim/v2",
}
provider := GetSyncerProvider(syncer)
if _, ok := provider.(*SCIMSyncerProvider); !ok {
t.Errorf("Expected SCIMSyncerProvider for type 'SCIM', got %T", provider)
}
}

View File

@@ -303,3 +303,15 @@ func (p *WecomSyncerProvider) wecomUserToOriginalUser(wecomUser *WecomUser) *Ori
return user
}
// GetOriginalGroups retrieves all groups from WeCom (not implemented yet)
func (p *WecomSyncerProvider) GetOriginalGroups() ([]*OriginalGroup, error) {
// TODO: Implement WeCom group sync
return []*OriginalGroup{}, nil
}
// GetOriginalUserGroups retrieves the group IDs that a user belongs to (not implemented yet)
func (p *WecomSyncerProvider) GetOriginalUserGroups(userId string) ([]string, error) {
// TODO: Implement WeCom user group membership sync
return []string{}, nil
}

View File

@@ -58,59 +58,61 @@ type User struct {
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
DeletedTime string `xorm:"varchar(100)" json:"deletedTime"`
Id string `xorm:"varchar(100) index" json:"id"`
ExternalId string `xorm:"varchar(100) index" json:"externalId"`
Type string `xorm:"varchar(100)" json:"type"`
Password string `xorm:"varchar(150)" json:"password"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
FirstName string `xorm:"varchar(100)" json:"firstName"`
LastName string `xorm:"varchar(100)" json:"lastName"`
Avatar string `xorm:"text" json:"avatar"`
AvatarType string `xorm:"varchar(100)" json:"avatarType"`
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
Email string `xorm:"varchar(100) index" json:"email"`
EmailVerified bool `json:"emailVerified"`
Phone string `xorm:"varchar(100) index" json:"phone"`
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
Region string `xorm:"varchar(100)" json:"region"`
Location string `xorm:"varchar(100)" json:"location"`
Address []string `json:"address"`
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
Title string `xorm:"varchar(100)" json:"title"`
IdCardType string `xorm:"varchar(100)" json:"idCardType"`
IdCard string `xorm:"varchar(100) index" json:"idCard"`
RealName string `xorm:"varchar(100)" json:"realName"`
IsVerified bool `json:"isVerified"`
Homepage string `xorm:"varchar(100)" json:"homepage"`
Bio string `xorm:"varchar(100)" json:"bio"`
Tag string `xorm:"varchar(100)" json:"tag"`
Language string `xorm:"varchar(100)" json:"language"`
Gender string `xorm:"varchar(100)" json:"gender"`
Birthday string `xorm:"varchar(100)" json:"birthday"`
Education string `xorm:"varchar(100)" json:"education"`
Score int `json:"score"`
Karma int `json:"karma"`
Ranking int `json:"ranking"`
Balance float64 `json:"balance"`
BalanceCredit float64 `json:"balanceCredit"`
Currency string `xorm:"varchar(100)" json:"currency"`
BalanceCurrency string `xorm:"varchar(100)" json:"balanceCurrency"`
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
Hash string `xorm:"varchar(100)" json:"hash"`
PreHash string `xorm:"varchar(100)" json:"preHash"`
RegisterType string `xorm:"varchar(100)" json:"registerType"`
RegisterSource string `xorm:"varchar(100)" json:"registerSource"`
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
AccessToken string `xorm:"mediumtext" json:"accessToken"`
OriginalToken string `xorm:"mediumtext" json:"originalToken"`
Id string `xorm:"varchar(100) index" json:"id"`
ExternalId string `xorm:"varchar(100) index" json:"externalId"`
Type string `xorm:"varchar(100)" json:"type"`
Password string `xorm:"varchar(150)" json:"password"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
FirstName string `xorm:"varchar(100)" json:"firstName"`
LastName string `xorm:"varchar(100)" json:"lastName"`
Avatar string `xorm:"text" json:"avatar"`
AvatarType string `xorm:"varchar(100)" json:"avatarType"`
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
Email string `xorm:"varchar(100) index" json:"email"`
EmailVerified bool `json:"emailVerified"`
Phone string `xorm:"varchar(100) index" json:"phone"`
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
Region string `xorm:"varchar(100)" json:"region"`
Location string `xorm:"varchar(100)" json:"location"`
Address []string `json:"address"`
Addresses []*Address `xorm:"addresses blob" json:"addresses"`
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
Title string `xorm:"varchar(100)" json:"title"`
IdCardType string `xorm:"varchar(100)" json:"idCardType"`
IdCard string `xorm:"varchar(100) index" json:"idCard"`
RealName string `xorm:"varchar(100)" json:"realName"`
IsVerified bool `json:"isVerified"`
Homepage string `xorm:"varchar(100)" json:"homepage"`
Bio string `xorm:"varchar(100)" json:"bio"`
Tag string `xorm:"varchar(100)" json:"tag"`
Language string `xorm:"varchar(100)" json:"language"`
Gender string `xorm:"varchar(100)" json:"gender"`
Birthday string `xorm:"varchar(100)" json:"birthday"`
Education string `xorm:"varchar(100)" json:"education"`
Score int `json:"score"`
Karma int `json:"karma"`
Ranking int `json:"ranking"`
Balance float64 `json:"balance"`
BalanceCredit float64 `json:"balanceCredit"`
Currency string `xorm:"varchar(100)" json:"currency"`
BalanceCurrency string `xorm:"varchar(100)" json:"balanceCurrency"`
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
Hash string `xorm:"varchar(100)" json:"hash"`
PreHash string `xorm:"varchar(100)" json:"preHash"`
RegisterType string `xorm:"varchar(100)" json:"registerType"`
RegisterSource string `xorm:"varchar(100)" json:"registerSource"`
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
AccessToken string `xorm:"mediumtext" json:"accessToken"`
OriginalToken string `xorm:"mediumtext" json:"originalToken"`
OriginalRefreshToken string `xorm:"mediumtext" json:"originalRefreshToken"`
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
@@ -220,6 +222,7 @@ type User struct {
Invitation string `xorm:"varchar(100) index" json:"invitation"`
InvitationCode string `xorm:"varchar(100) index" json:"invitationCode"`
FaceIds []*FaceId `json:"faceIds"`
Cart []ProductInfo `xorm:"mediumtext" json:"cart"`
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
@@ -272,6 +275,16 @@ type MfaAccount struct {
Origin string `xorm:"varchar(100)" json:"origin"`
}
type Address struct {
Tag string `xorm:"varchar(100)" json:"tag"`
Line1 string `xorm:"varchar(100)" json:"line1"`
Line2 string `xorm:"varchar(100)" json:"line2"`
City string `xorm:"varchar(100)" json:"city"`
State string `xorm:"varchar(100)" json:"state"`
ZipCode string `xorm:"varchar(100)" json:"zipCode"`
Region string `xorm:"varchar(100)" json:"region"`
}
type FaceId struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"`
FaceIdData []float64 `json:"faceIdData"`
@@ -673,6 +686,9 @@ func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error)
if user.OriginalToken != "" {
user.OriginalToken = "***"
}
if user.OriginalRefreshToken != "" {
user.OriginalRefreshToken = "***"
}
}
if user.ManagedAccounts != nil {
@@ -845,6 +861,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "need_update_password", "ip_whitelist", "mfa_items", "mfa_remember_deadline",
"cart",
}
}
if isAdmin {

View File

@@ -188,6 +188,7 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
// Store the original OAuth provider token if available
if token != nil && token.AccessToken != "" {
user.OriginalToken = token.AccessToken
user.OriginalRefreshToken = token.RefreshToken
}
if userInfo.Id != "" {
@@ -917,6 +918,8 @@ func StringArrayToStruct[T any](stringArray [][]string) ([]*T, error) {
err = setReflectAttr[[]MfaAccount](&fv, v)
case reflect.TypeOf([]webauthn.Credential{}):
err = setReflectAttr[[]webauthn.Credential](&fv, v)
case reflect.TypeOf(map[string]string{}):
err = setReflectAttr[map[string]string](&fv, v)
}
if err != nil {

View File

@@ -24,6 +24,12 @@ const (
PaymentStateError PaymentState = "Error"
)
// IsTerminalState checks if a payment state is terminal (cannot transition to other states)
func IsTerminalState(state PaymentState) bool {
return state == PaymentStatePaid || state == PaymentStateError ||
state == PaymentStateCanceled || state == PaymentStateTimeout
}
const (
PaymentEnvWechatBrowser = "WechatBrowser"
)

View File

@@ -850,6 +850,69 @@
}
}
},
"/api/add-ticket": {
"post": {
"tags": [
"Ticket API"
],
"description": "add ticket",
"operationId": "ApiController.AddTicket",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the ticket",
"required": true,
"schema": {
"$ref": "#/definitions/object.Ticket"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-ticket-message": {
"post": {
"tags": [
"Ticket API"
],
"description": "add a message to a ticket",
"operationId": "ApiController.AddTicketMessage",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the ticket",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The message to add",
"required": true,
"schema": {
"$ref": "#/definitions/object.TicketMessage"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-token": {
"post": {
"tags": [
@@ -1707,6 +1770,34 @@
}
}
},
"/api/delete-ticket": {
"post": {
"tags": [
"Ticket API"
],
"description": "delete ticket",
"operationId": "ApiController.DeleteTicket",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the ticket",
"required": true,
"schema": {
"$ref": "#/definitions/object.Ticket"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/delete-token": {
"post": {
"tags": [
@@ -1891,6 +1982,23 @@
}
}
},
"/api/exit-impersonation-user": {
"post": {
"tags": [
"User API"
],
"description": "clear impersonation info for current session",
"operationId": "ApiController.ExitImpersonateUser",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/faceid-signin-begin": {
"get": {
"tags": [
@@ -1996,6 +2104,81 @@
}
}
},
"/api/get-all-actions": {
"get": {
"tags": [
"Enforcer API"
],
"description": "Get all actions for a user (Casbin API)",
"operationId": "ApiController.GetAllActions",
"parameters": [
{
"in": "query",
"name": "userId",
"description": "user id like built-in/admin",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/get-all-objects": {
"get": {
"tags": [
"Enforcer API"
],
"description": "Get all objects for a user (Casbin API)",
"operationId": "ApiController.GetAllObjects",
"parameters": [
{
"in": "query",
"name": "userId",
"description": "user id like built-in/admin",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/get-all-roles": {
"get": {
"tags": [
"Enforcer API"
],
"description": "Get all roles for a user (Casbin API)",
"operationId": "ApiController.GetAllRoles",
"parameters": [
{
"in": "query",
"name": "userId",
"description": "user id like built-in/admin",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/get-app-login": {
"get": {
"tags": [
@@ -3843,6 +4026,61 @@
}
}
},
"/api/get-ticket": {
"get": {
"tags": [
"Ticket API"
],
"description": "get ticket",
"operationId": "ApiController.GetTicket",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the ticket",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Ticket"
}
}
}
}
},
"/api/get-tickets": {
"get": {
"tags": [
"Ticket API"
],
"description": "get tickets",
"operationId": "ApiController.GetTickets",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The owner of tickets",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Ticket"
}
}
}
}
}
},
"/api/get-token": {
"get": {
"tags": [
@@ -4299,6 +4537,32 @@
}
}
},
"/api/impersonation-user": {
"post": {
"tags": [
"User API"
],
"description": "set impersonation user for current admin session",
"operationId": "ApiController.ImpersonateUser",
"parameters": [
{
"in": "formData",
"name": "username",
"description": "The username to impersonate (owner/name)",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/invoice-payment": {
"post": {
"tags": [
@@ -5219,8 +5483,16 @@
"tags": [
"Login API"
],
"description": "logout the current user from all applications",
"description": "logout the current user from all applications or current session only",
"operationId": "ApiController.SsoLogout",
"parameters": [
{
"in": "query",
"name": "logoutAll",
"description": "Whether to logout from all sessions. Accepted values: 'true', '1', or empty (default: true). Any other value means false.",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
@@ -5234,8 +5506,16 @@
"tags": [
"Login API"
],
"description": "logout the current user from all applications",
"description": "logout the current user from all applications or current session only",
"operationId": "ApiController.SsoLogout",
"parameters": [
{
"in": "query",
"name": "logoutAll",
"description": "Whether to logout from all sessions. Accepted values: 'true', '1', or empty (default: true). Any other value means false.",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
@@ -6082,6 +6362,41 @@
}
}
},
"/api/update-ticket": {
"post": {
"tags": [
"Ticket API"
],
"description": "update ticket",
"operationId": "ApiController.UpdateTicket",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the ticket",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The details of the ticket",
"required": true,
"schema": {
"$ref": "#/definitions/object.Ticket"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/update-token": {
"post": {
"tags": [
@@ -6564,14 +6879,18 @@
}
},
"definitions": {
"217748.\u003cnil\u003e.string": {
"232967.\u003cnil\u003e.string": {
"title": "string",
"type": "object"
},
"217806.string.string": {
"233025.string.string": {
"title": "string",
"type": "object"
},
"McpResponse": {
"title": "McpResponse",
"type": "object"
},
"Response": {
"title": "Response",
"type": "object"
@@ -6763,6 +7082,9 @@
"regex": {
"type": "string"
},
"tab": {
"type": "string"
},
"viewRule": {
"type": "string"
},
@@ -6814,6 +7136,33 @@
}
}
},
"object.Address": {
"title": "Address",
"type": "object",
"properties": {
"city": {
"type": "string"
},
"line1": {
"type": "string"
},
"line2": {
"type": "string"
},
"region": {
"type": "string"
},
"state": {
"type": "string"
},
"tag": {
"type": "string"
},
"zipCode": {
"type": "string"
}
}
},
"object.Application": {
"title": "Application",
"type": "object",
@@ -6837,6 +7186,10 @@
"type": "integer",
"format": "int64"
},
"cookieExpireInHours": {
"type": "integer",
"format": "int64"
},
"createdTime": {
"type": "string"
},
@@ -6870,6 +7223,9 @@
"enablePassword": {
"type": "boolean"
},
"enableSamlAssertionSignature": {
"type": "boolean"
},
"enableSamlC14n10": {
"type": "boolean"
},
@@ -7822,9 +8178,6 @@
"displayName": {
"type": "string"
},
"endTime": {
"type": "string"
},
"message": {
"type": "string"
},
@@ -7837,18 +8190,15 @@
"payment": {
"type": "string"
},
"planName": {
"type": "string"
},
"price": {
"type": "number",
"format": "double"
},
"pricingName": {
"type": "string"
},
"productName": {
"type": "string"
"productInfos": {
"type": "array",
"items": {
"$ref": "#/definitions/object.ProductInfo"
}
},
"products": {
"type": "array",
@@ -7856,10 +8206,10 @@
"type": "string"
}
},
"startTime": {
"state": {
"type": "string"
},
"state": {
"updateTime": {
"type": "string"
},
"user": {
@@ -7877,6 +8227,9 @@
"$ref": "#/definitions/object.AccountItem"
}
},
"accountMenu": {
"type": "string"
},
"balanceCredit": {
"type": "number",
"format": "double"
@@ -8090,9 +8443,6 @@
"invoiceUrl": {
"type": "string"
},
"isRecharge": {
"type": "boolean"
},
"message": {
"type": "string"
},
@@ -8102,6 +8452,9 @@
"order": {
"type": "string"
},
"orderObj": {
"$ref": "#/definitions/object.Order"
},
"outOrderId": {
"type": "string"
},
@@ -8127,10 +8480,13 @@
"type": "number",
"format": "double"
},
"productDisplayName": {
"type": "string"
"products": {
"type": "array",
"items": {
"type": "string"
}
},
"productName": {
"productsDisplayName": {
"type": "string"
},
"provider": {
@@ -8142,9 +8498,6 @@
"successUrl": {
"type": "string"
},
"tag": {
"type": "string"
},
"type": {
"type": "string"
},
@@ -8402,6 +8755,47 @@
}
}
},
"object.ProductInfo": {
"title": "ProductInfo",
"type": "object",
"properties": {
"currency": {
"type": "string"
},
"detail": {
"type": "string"
},
"displayName": {
"type": "string"
},
"image": {
"type": "string"
},
"isRecharge": {
"type": "boolean"
},
"name": {
"type": "string"
},
"owner": {
"type": "string"
},
"planName": {
"type": "string"
},
"price": {
"type": "number",
"format": "double"
},
"pricingName": {
"type": "string"
},
"quantity": {
"type": "integer",
"format": "int64"
}
}
},
"object.PrometheusInfo": {
"title": "PrometheusInfo",
"type": "object",
@@ -8482,6 +8876,9 @@
"emailRegex": {
"type": "string"
},
"enablePkce": {
"type": "boolean"
},
"enableProxy": {
"type": "boolean"
},
@@ -9024,6 +9421,63 @@
}
}
},
"object.Ticket": {
"title": "Ticket",
"type": "object",
"properties": {
"content": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"displayName": {
"type": "string"
},
"messages": {
"type": "array",
"items": {
"$ref": "#/definitions/object.TicketMessage"
}
},
"name": {
"type": "string"
},
"owner": {
"type": "string"
},
"state": {
"type": "string"
},
"title": {
"type": "string"
},
"updatedTime": {
"type": "string"
},
"user": {
"type": "string"
}
}
},
"object.TicketMessage": {
"title": "TicketMessage",
"type": "object",
"properties": {
"author": {
"type": "string"
},
"isAdmin": {
"type": "boolean"
},
"text": {
"type": "string"
},
"timestamp": {
"type": "string"
}
}
},
"object.Token": {
"title": "Token",
"type": "object",
@@ -9132,7 +9586,7 @@
"type": "string"
},
"category": {
"type": "string"
"$ref": "#/definitions/object.TransactionCategory"
},
"createdTime": {
"type": "string"
@@ -9159,7 +9613,7 @@
"type": "string"
},
"state": {
"$ref": "#/definitions/pp.PaymentState"
"type": "string"
},
"subtype": {
"type": "string"
@@ -9175,6 +9629,15 @@
}
}
},
"object.TransactionCategory": {
"title": "TransactionCategory",
"type": "string",
"enum": [
"TransactionCategoryPurchase = \"Purchase\"",
"TransactionCategoryRecharge = \"Recharge\""
],
"example": "Purchase"
},
"object.User": {
"title": "User",
"type": "object",
@@ -9194,6 +9657,12 @@
"type": "string"
}
},
"addresses": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Address"
}
},
"adfs": {
"type": "string"
},
@@ -9256,6 +9725,12 @@
"box": {
"type": "string"
},
"cart": {
"type": "array",
"items": {
"$ref": "#/definitions/object.ProductInfo"
}
},
"casdoor": {
"type": "string"
},
@@ -9569,6 +10044,9 @@
"onedrive": {
"type": "string"
},
"originalRefreshToken": {
"type": "string"
},
"originalToken": {
"type": "string"
},
@@ -9825,7 +10303,7 @@
"type": "object",
"properties": {
"aliases": {
"$ref": "#/definitions/217748.\u003cnil\u003e.string"
"$ref": "#/definitions/232967.\u003cnil\u003e.string"
},
"links": {
"type": "array",
@@ -9834,7 +10312,7 @@
}
},
"properties": {
"$ref": "#/definitions/217806.string.string"
"$ref": "#/definitions/233025.string.string"
},
"subject": {
"type": "string"

View File

@@ -548,6 +548,47 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-ticket:
post:
tags:
- Ticket API
description: add ticket
operationId: ApiController.AddTicket
parameters:
- in: body
name: body
description: The details of the ticket
required: true
schema:
$ref: '#/definitions/object.Ticket'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-ticket-message:
post:
tags:
- Ticket API
description: add a message to a ticket
operationId: ApiController.AddTicketMessage
parameters:
- in: query
name: id
description: The id ( owner/name ) of the ticket
required: true
type: string
- in: body
name: body
description: The message to add
required: true
schema:
$ref: '#/definitions/object.TicketMessage'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-token:
post:
tags:
@@ -1099,6 +1140,24 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-ticket:
post:
tags:
- Ticket API
description: delete ticket
operationId: ApiController.DeleteTicket
parameters:
- in: body
name: body
description: The details of the ticket
required: true
schema:
$ref: '#/definitions/object.Ticket'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-token:
post:
tags:
@@ -1218,6 +1277,17 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/exit-impersonation-user:
post:
tags:
- User API
description: clear impersonation info for current session
operationId: ApiController.ExitImpersonateUser
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/faceid-signin-begin:
get:
tags:
@@ -1287,6 +1357,54 @@ paths:
type: array
items:
$ref: '#/definitions/object.Adapter'
/api/get-all-actions:
get:
tags:
- Enforcer API
description: Get all actions for a user (Casbin API)
operationId: ApiController.GetAllActions
parameters:
- in: query
name: userId
description: user id like built-in/admin
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/get-all-objects:
get:
tags:
- Enforcer API
description: Get all objects for a user (Casbin API)
operationId: ApiController.GetAllObjects
parameters:
- in: query
name: userId
description: user id like built-in/admin
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/get-all-roles:
get:
tags:
- Enforcer API
description: Get all roles for a user (Casbin API)
operationId: ApiController.GetAllRoles
parameters:
- in: query
name: userId
description: user id like built-in/admin
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/get-app-login:
get:
tags:
@@ -2498,6 +2616,42 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/util.SystemInfo'
/api/get-ticket:
get:
tags:
- Ticket API
description: get ticket
operationId: ApiController.GetTicket
parameters:
- in: query
name: id
description: The id ( owner/name ) of the ticket
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Ticket'
/api/get-tickets:
get:
tags:
- Ticket API
description: get tickets
operationId: ApiController.GetTickets
parameters:
- in: query
name: owner
description: The owner of tickets
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Ticket'
/api/get-token:
get:
tags:
@@ -2797,6 +2951,23 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/impersonation-user:
post:
tags:
- User API
description: set impersonation user for current admin session
operationId: ApiController.ImpersonateUser
parameters:
- in: formData
name: username
description: The username to impersonate (owner/name)
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/invoice-payment:
post:
tags:
@@ -3406,8 +3577,13 @@ paths:
get:
tags:
- Login API
description: logout the current user from all applications
description: logout the current user from all applications or current session only
operationId: ApiController.SsoLogout
parameters:
- in: query
name: logoutAll
description: 'Whether to logout from all sessions. Accepted values: ''true'', ''1'', or empty (default: true). Any other value means false.'
type: string
responses:
"200":
description: The Response object
@@ -3416,8 +3592,13 @@ paths:
post:
tags:
- Login API
description: logout the current user from all applications
description: logout the current user from all applications or current session only
operationId: ApiController.SsoLogout
parameters:
- in: query
name: logoutAll
description: 'Whether to logout from all sessions. Accepted values: ''true'', ''1'', or empty (default: true). Any other value means false.'
type: string
responses:
"200":
description: The Response object
@@ -3971,6 +4152,29 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-ticket:
post:
tags:
- Ticket API
description: update ticket
operationId: ApiController.UpdateTicket
parameters:
- in: query
name: id
description: The id ( owner/name ) of the ticket
required: true
type: string
- in: body
name: body
description: The details of the ticket
required: true
schema:
$ref: '#/definitions/object.Ticket'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-token:
post:
tags:
@@ -4286,12 +4490,15 @@ paths:
schema:
$ref: '#/definitions/controllers.Response'
definitions:
217748.<nil>.string:
232967.<nil>.string:
title: string
type: object
217806.string.string:
233025.string.string:
title: string
type: object
McpResponse:
title: McpResponse
type: object
Response:
title: Response
type: object
@@ -4423,6 +4630,8 @@ definitions:
type: string
regex:
type: string
tab:
type: string
viewRule:
type: string
visible:
@@ -4456,6 +4665,24 @@ definitions:
type: boolean
user:
type: string
object.Address:
title: Address
type: object
properties:
city:
type: string
line1:
type: string
line2:
type: string
region:
type: string
state:
type: string
tag:
type: string
zipCode:
type: string
object.Application:
title: Application
type: object
@@ -4473,6 +4700,9 @@ definitions:
codeResendTimeout:
type: integer
format: int64
cookieExpireInHours:
type: integer
format: int64
createdTime:
type: string
defaultGroup:
@@ -4495,6 +4725,8 @@ definitions:
type: boolean
enablePassword:
type: boolean
enableSamlAssertionSignature:
type: boolean
enableSamlC14n10:
type: boolean
enableSamlCompress:
@@ -5136,8 +5368,6 @@ definitions:
type: string
displayName:
type: string
endTime:
type: string
message:
type: string
name:
@@ -5146,23 +5376,21 @@ definitions:
type: string
payment:
type: string
planName:
type: string
price:
type: number
format: double
pricingName:
type: string
productName:
type: string
productInfos:
type: array
items:
$ref: '#/definitions/object.ProductInfo'
products:
type: array
items:
type: string
startTime:
type: string
state:
type: string
updateTime:
type: string
user:
type: string
object.Organization:
@@ -5173,6 +5401,8 @@ definitions:
type: array
items:
$ref: '#/definitions/object.AccountItem'
accountMenu:
type: string
balanceCredit:
type: number
format: double
@@ -5317,14 +5547,14 @@ definitions:
type: string
invoiceUrl:
type: string
isRecharge:
type: boolean
message:
type: string
name:
type: string
order:
type: string
orderObj:
$ref: '#/definitions/object.Order'
outOrderId:
type: string
owner:
@@ -5342,9 +5572,11 @@ definitions:
price:
type: number
format: double
productDisplayName:
type: string
productName:
products:
type: array
items:
type: string
productsDisplayName:
type: string
provider:
type: string
@@ -5352,8 +5584,6 @@ definitions:
$ref: '#/definitions/pp.PaymentState'
successUrl:
type: string
tag:
type: string
type:
type: string
user:
@@ -5526,6 +5756,34 @@ definitions:
type: string
tag:
type: string
object.ProductInfo:
title: ProductInfo
type: object
properties:
currency:
type: string
detail:
type: string
displayName:
type: string
image:
type: string
isRecharge:
type: boolean
name:
type: string
owner:
type: string
planName:
type: string
price:
type: number
format: double
pricingName:
type: string
quantity:
type: integer
format: int64
object.PrometheusInfo:
title: PrometheusInfo
type: object
@@ -5581,6 +5839,8 @@ definitions:
type: string
emailRegex:
type: string
enablePkce:
type: boolean
enableProxy:
type: boolean
enableSignAuthnRequest:
@@ -5945,6 +6205,44 @@ definitions:
type: boolean
themeType:
type: string
object.Ticket:
title: Ticket
type: object
properties:
content:
type: string
createdTime:
type: string
displayName:
type: string
messages:
type: array
items:
$ref: '#/definitions/object.TicketMessage'
name:
type: string
owner:
type: string
state:
type: string
title:
type: string
updatedTime:
type: string
user:
type: string
object.TicketMessage:
title: TicketMessage
type: object
properties:
author:
type: string
isAdmin:
type: boolean
text:
type: string
timestamp:
type: string
object.Token:
title: Token
type: object
@@ -6020,7 +6318,7 @@ definitions:
application:
type: string
category:
type: string
$ref: '#/definitions/object.TransactionCategory'
createdTime:
type: string
currency:
@@ -6038,7 +6336,7 @@ definitions:
provider:
type: string
state:
$ref: '#/definitions/pp.PaymentState'
type: string
subtype:
type: string
tag:
@@ -6047,6 +6345,13 @@ definitions:
type: string
user:
type: string
object.TransactionCategory:
title: TransactionCategory
type: string
enum:
- TransactionCategoryPurchase = "Purchase"
- TransactionCategoryRecharge = "Recharge"
example: Purchase
object.User:
title: User
type: object
@@ -6061,6 +6366,10 @@ definitions:
type: array
items:
type: string
addresses:
type: array
items:
$ref: '#/definitions/object.Address'
adfs:
type: string
affiliation:
@@ -6103,6 +6412,10 @@ definitions:
type: string
box:
type: string
cart:
type: array
items:
$ref: '#/definitions/object.ProductInfo'
casdoor:
type: string
cloudfoundry:
@@ -6312,6 +6625,8 @@ definitions:
type: string
onedrive:
type: string
originalRefreshToken:
type: string
originalToken:
type: string
oura:
@@ -6484,13 +6799,13 @@ definitions:
type: object
properties:
aliases:
$ref: '#/definitions/217748.<nil>.string'
$ref: '#/definitions/232967.<nil>.string'
links:
type: array
items:
$ref: '#/definitions/object.WebFingerLink'
properties:
$ref: '#/definitions/217806.string.string'
$ref: '#/definitions/233025.string.string'
subject:
type: string
object.WebFingerLink:

View File

@@ -14,7 +14,10 @@
package util
import "slices"
import (
"slices"
"strings"
)
func DeleteVal(values []string, val string) []string {
newValues := []string{}
@@ -66,3 +69,23 @@ func HaveIntersection(arr1 []string, arr2 []string) bool {
return false
}
// HasTagInSlice checks if a comma-separated tag string contains any tag that matches the slice.
// For example, if userTag is "default-policy,project-admin" and slice is ["default-policy", "project-admin"],
// it will return true because "default-policy" and "project-admin" are both in the slice.
func HasTagInSlice(slice []string, userTag string) bool {
if userTag == "" {
return false
}
// Split the comma-separated tags and check each one
userTags := strings.Split(userTag, ",")
for _, tag := range userTags {
trimmedTag := strings.TrimSpace(tag)
if trimmedTag != "" && slices.Contains(slice, trimmedTag) {
return true
}
}
return false
}

View File

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

View File

@@ -17,7 +17,10 @@
package util
import (
"fmt"
"os"
"path"
"regexp"
"runtime"
"testing"
@@ -41,8 +44,27 @@ func TestGetMemoryUsage(t *testing.T) {
func TestGetVersionInfo(t *testing.T) {
versionInfo, err := GetVersionInfo()
assert.Nil(t, err)
t.Log(versionInfo)
assert.NoError(t, err)
assert.NotNil(t, versionInfo)
_, filename, _, _ := runtime.Caller(0)
variablePath := path.Join(path.Dir(filename), "variable.go")
content, err := os.ReadFile(variablePath)
assert.NoError(t, err)
replacements := map[*regexp.Regexp]string{
regexp.MustCompile(`Version\s*=\s*".*"`): fmt.Sprintf(`Version = "%s"`, versionInfo.Version),
regexp.MustCompile(`CommitId\s*=\s*".*"`): fmt.Sprintf(`CommitId = "%s"`, versionInfo.CommitId),
regexp.MustCompile(`CommitOffset\s*=\s*-?\d+`): fmt.Sprintf(`CommitOffset = %d`, versionInfo.CommitOffset),
}
updated := string(content)
for re, val := range replacements {
updated = re.ReplaceAllString(updated, val)
}
err = os.WriteFile(variablePath, []byte(updated), 0o644)
assert.NoError(t, err)
}
func TestGetVersion(t *testing.T) {
@@ -92,9 +114,3 @@ func TestGetVersion(t *testing.T) {
assert.Equal(t, 3, aheadCnt)
assert.Equal(t, "v1.257.0", releaseVersion)
}
func TestFromFile(t *testing.T) {
versionInfo, err := GetVersionInfoFromFile()
assert.Nil(t, err)
t.Log(versionInfo)
}

21
util/variable.go Normal file
View File

@@ -0,0 +1,21 @@
// 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 util
var (
Version = "dev"
CommitId = "unknown"
CommitOffset = 0
)

View File

@@ -420,7 +420,17 @@ class App extends Component {
}
if (query !== "") {
window.history.replaceState({}, document.title, this.getUrlWithoutQuery());
const returnUrl = params.get("returnUrl");
let newUrl;
if (returnUrl) {
const newParams = new URLSearchParams();
newParams.set("returnUrl", returnUrl);
newUrl = window.location.pathname + "?" + newParams.toString();
} else {
newUrl = this.getUrlWithoutQuery();
}
window.history.replaceState({}, document.title, newUrl);
}
AuthBackend.getAccount(query)

File diff suppressed because it is too large Load Diff

366
web/src/CartListPage.js Normal file
View File

@@ -0,0 +1,366 @@
// 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 {Link} from "react-router-dom";
import {Button, Table} from "antd";
import * as Setting from "./Setting";
import * as UserBackend from "./backend/UserBackend";
import * as OrderBackend from "./backend/OrderBackend";
import * as ProductBackend from "./backend/ProductBackend";
import i18next from "i18next";
import BaseListPage from "./BaseListPage";
import PopconfirmModal from "./common/modal/PopconfirmModal";
class CartListPage extends BaseListPage {
constructor(props) {
super(props);
this.state = {
...this.state,
data: [],
user: null,
isPlacingOrder: false,
loading: false,
pagination: {
current: 1,
pageSize: 10,
total: 0,
},
searchText: "",
searchedColumn: "",
};
}
clearCart() {
const user = Setting.deepCopy(this.state.user);
if (user === undefined || user === null) {
Setting.showMessage("error", i18next.t("general:Failed to delete"));
return;
}
user.cart = [];
UserBackend.updateUser(user.owner, user.name, user)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch();
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
placeOrder() {
if (this.state.isPlacingOrder) {
return;
}
const owner = this.state.user?.owner || this.props.account.owner;
const carts = this.state.data || [];
if (carts.length === 0) {
Setting.showMessage("error", i18next.t("product:Product list cannot be empty"));
return;
}
this.setState({isPlacingOrder: true});
const productInfos = carts.map(item => ({
name: item.name,
price: item.price,
quantity: item.quantity,
pricingName: item.pricingName,
planName: item.planName,
}));
OrderBackend.placeOrder(owner, productInfos, this.state.user?.name)
.then((res) => {
if (res.status === "ok") {
const order = res.data;
Setting.showMessage("success", i18next.t("product:Order created successfully"));
Setting.goToLink(`/orders/${order.owner}/${order.name}/pay`);
} else {
Setting.showMessage("error", `${i18next.t("product:Failed to create order")}: ${res.msg}`);
this.setState({isPlacingOrder: false});
}
})
.catch((error) => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
this.setState({isPlacingOrder: false});
});
}
deleteCart(record) {
const user = Setting.deepCopy(this.state.user);
if (user === undefined || user === null || !Array.isArray(user.cart)) {
Setting.showMessage("error", i18next.t("general:Failed to delete"));
return;
}
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;
}
user.cart.splice(index, 1);
UserBackend.updateUser(user.owner, user.name, user)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch();
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
renderTable(carts) {
const isEmpty = carts === undefined || carts === null || carts.length === 0;
const owner = this.state.user?.owner || this.props.account.owner;
let total = 0;
let currency = "";
if (carts && carts.length > 0) {
carts.forEach(item => {
total += item.price * item.quantity;
});
currency = carts[0].currency;
}
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "140px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/products/${owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Display name"),
dataIndex: "displayName",
key: "displayName",
width: "170px",
sorter: true,
},
{
title: i18next.t("product:Image"),
dataIndex: "image",
key: "image",
width: "170px",
render: (text, record, index) => {
return (
<a target="_blank" rel="noreferrer" href={text}>
<img src={text} alt={text} width={150} />
</a>
);
},
},
{
title: i18next.t("product:Price"),
dataIndex: "price",
key: "price",
width: "160px",
sorter: true,
render: (text, record) => {
const subtotal = (record.price * record.quantity).toFixed(2);
return Setting.getPriceDisplay(subtotal, record.currency);
},
},
{
title: i18next.t("pricing:Pricing name"),
dataIndex: "pricingName",
key: "pricingName",
width: "140px",
sorter: true,
render: (text, record) => {
if (!text) {return null;}
return (
<Link to={`/pricings/${owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("plan:Plan name"),
dataIndex: "planName",
key: "planName",
width: "140px",
sorter: true,
render: (text, record) => {
if (!text) {return null;}
return (
<Link to={`/plans/${owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("product:Quantity"),
dataIndex: "quantity",
key: "quantity",
width: "120px",
sorter: true,
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "160px",
fixed: Setting.isMobile() ? false : "right",
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`)}>
{i18next.t("product:Detail")}
</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteCart(record)}
>
</PopconfirmModal>
</div>
);
},
},
];
return (
<div>
<Table
scroll={{x: "max-content"}}
columns={columns}
dataSource={carts}
rowKey={(record, index) => `${record.name}-${record.pricingName}-${record.planName}-${index}`}
size="middle"
bordered
pagination={false}
title={() => {
return (
<div>
{i18next.t("general:Cart")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button size="small" onClick={() => this.props.history.push("/product-store")}>{i18next.t("general:Add")}</Button>
&nbsp;&nbsp;
<PopconfirmModal
size="small"
style={{marginRight: "8px"}}
text={i18next.t("general:Clear")}
title={i18next.t("general:Sure to delete") + `: ${i18next.t("general:Cart")} ?`}
onConfirm={() => this.clearCart()}
disabled={isEmpty}
/>
<Button type="primary" size="small" onClick={() => this.placeOrder()} disabled={isEmpty || this.state.isPlacingOrder} loading={this.state.isPlacingOrder}>{i18next.t("general:Place Order")}</Button>
</div>
);
}}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
{!isEmpty && (
<div style={{marginTop: "20px", display: "flex", flexDirection: "column", justifyContent: "center", alignItems: "center", gap: "20px"}}>
<div style={{display: "flex", alignItems: "center", fontSize: "18px", fontWeight: "bold"}}>
{i18next.t("product:Total Price")}:&nbsp;
<span style={{color: "red", fontSize: "28px"}}>
{Setting.getCurrencySymbol(currency)}{total.toFixed(2)} ({Setting.getCurrencyText(currency)})
</span>
</div>
<Button
type="primary"
size="large"
style={{height: "50px", fontSize: "20px", padding: "0 40px", borderRadius: "5px"}}
onClick={() => this.placeOrder()}
disabled={this.state.isPlacingOrder}
loading={this.state.isPlacingOrder}
>
{i18next.t("general:Place Order")}
</Button>
</div>
)}
</div>
);
}
fetch = (params = {}) => {
this.setState({loading: true});
const organizationName = this.props.account.owner;
const userName = this.props.account.name;
UserBackend.getUser(organizationName, userName)
.then(async(res) => {
if (res.status === "ok") {
const cartData = res.data.cart || [];
const productPromises = cartData.map(item =>
ProductBackend.getProduct(organizationName, item.name)
.then(pRes => {
if (pRes.status === "ok" && pRes.data) {
return {
...pRes.data,
pricingName: item.pricingName,
planName: item.planName,
quantity: item.quantity,
price: pRes.data.isRecharge ? item.price : pRes.data.price,
};
}
return item;
})
.catch(() => item)
);
const fullCartData = await Promise.all(productPromises);
this.setState({
loading: false,
data: fullCartData,
user: res.data,
pagination: {
...params.pagination,
total: fullCartData.length,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
this.setState({loading: false});
Setting.showMessage("error", res.msg);
}
})
.catch((error) => {
this.setState({
loading: false,
});
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
};
}
export default CartListPage;

View File

@@ -64,6 +64,7 @@ import ProductListPage from "./ProductListPage";
import ProductStorePage from "./ProductStorePage";
import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import CartListPage from "./CartListPage";
import OrderListPage from "./OrderListPage";
import OrderEditPage from "./OrderEditPage";
import OrderPayPage from "./OrderPayPage";
@@ -349,6 +350,7 @@ function ManagementPage(props) {
res.push(Setting.getItem(<Link style={{color: textColor}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/product-store">{i18next.t("general:Product Store")}</Link>, "/product-store"),
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
Setting.getItem(<Link to="/cart">{i18next.t("general:Cart")}</Link>, "/cart"),
Setting.getItem(<Link to="/orders">{i18next.t("general:Orders")}</Link>, "/orders"),
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
@@ -426,8 +428,13 @@ function ManagementPage(props) {
function renderLoginIfNotLoggedIn(component) {
if (props.account === null) {
const lastLoginOrg = localStorage.getItem("lastLoginOrg");
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/login" />;
if (lastLoginOrg) {
return <Redirect to={`/login/${lastLoginOrg}`} />;
} else {
return <Redirect to="/login" />;
}
} else if (props.account === undefined) {
return null;
} else if (props.account.needUpdatePassword) {
@@ -484,6 +491,7 @@ function ManagementPage(props) {
<Route exact path="/products" render={(props) => renderLoginIfNotLoggedIn(<ProductListPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName" render={(props) => renderLoginIfNotLoggedIn(<ProductEditPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => renderLoginIfNotLoggedIn(<ProductBuyPage account={account} {...props} />)} />
<Route exact path="/cart" render={(props) => renderLoginIfNotLoggedIn(<CartListPage account={account} {...props} />)} />
<Route exact path="/orders" render={(props) => renderLoginIfNotLoggedIn(<OrderListPage account={account} {...props} />)} />
<Route exact path="/orders/:organizationName/:orderName" render={(props) => renderLoginIfNotLoggedIn(<OrderEditPage account={account} {...props} />)} />
<Route exact path="/orders/:organizationName/:orderName/pay" render={(props) => renderLoginIfNotLoggedIn(<OrderPayPage account={account} {...props} />)} />

View File

@@ -158,16 +158,25 @@ class OrderEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("order:Product")}:
{i18next.t("order:Products")}:
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.order.productName} disabled={isViewMode} onChange={(value) => {
this.updateOrderField("productName", value);
}}>
{
this.state.products?.map((product, index) => <Option key={index} value={product.name}>{product.displayName}</Option>)
}
</Select>
<Select
mode="multiple"
style={{width: "100%"}}
value={this.state.order?.products || []}
disabled={isViewMode}
allowClear
options={(this.state.products || [])
.map((p) => ({
label: Setting.getLanguageText(p?.displayName) || p?.name,
value: p?.name,
}))
.filter((o) => o.value)}
onChange={(value) => {
this.updateOrderField("products", value);
}}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@@ -230,26 +239,6 @@ class OrderEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("order:Start time")}:
</Col>
<Col span={22} >
<Input value={this.state.order.startTime} onChange={e => {
this.updateOrderField("startTime", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("order:End time")}:
</Col>
<Col span={22} >
<Input value={this.state.order.endTime} onChange={e => {
this.updateOrderField("endTime", e.target.value);
}} />
</Col>
</Row>
</Card>
);
}

View File

@@ -14,13 +14,14 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Table} from "antd";
import {Button, Col, List, Row, Table, Tooltip} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as OrderBackend from "./backend/OrderBackend";
import i18next from "i18next";
import BaseListPage from "./BaseListPage";
import PopconfirmModal from "./common/modal/PopconfirmModal";
import {EditOutlined} from "@ant-design/icons";
class OrderListPage extends BaseListPage {
newOrder() {
@@ -31,13 +32,11 @@ class OrderListPage extends BaseListPage {
name: `order_${randomName}`,
createdTime: moment().format(),
displayName: `New Order - ${randomName}`,
productName: "",
products: [],
user: "",
payment: "",
state: "Created",
message: "",
startTime: moment().format(),
endTime: "",
};
}
@@ -138,46 +137,77 @@ class OrderListPage extends BaseListPage {
},
},
{
title: i18next.t("general:Display name"),
dataIndex: "displayName",
key: "displayName",
width: "170px",
sorter: true,
...this.getColumnSearchProps("displayName"),
},
{
title: i18next.t("order:Product"),
dataIndex: "productName",
key: "productName",
width: "170px",
sorter: true,
...this.getColumnSearchProps("productName"),
title: i18next.t("order:Products"),
dataIndex: "products",
key: "products",
...this.getColumnSearchProps("products"),
render: (text, record, index) => {
if (text === "") {
return "(empty)";
const productInfos = record?.productInfos || [];
if (productInfos.length === 0) {
return `(${i18next.t("general:empty")})`;
}
return (
<Link to={`/products/${record.owner}/${text}`}>
{text}
</Link>
<div>
<List
size="small"
locale={{emptyText: " "}}
dataSource={productInfos}
style={{
paddingTop: 8,
paddingBottom: 8,
}}
renderItem={(productInfo, i) => {
const price = productInfo.price || 0;
const number = productInfo.quantity || 1;
const currency = record.currency || "USD";
const productName = productInfo.displayName || productInfo.name;
return (
<List.Item>
<Row style={{width: "100%"}} wrap={false} gutter={[12, 0]}>
<Col flex="auto" style={{minWidth: 0}}>
<div style={{display: "flex", alignItems: "center", minWidth: 0}}>
<Tooltip placement="topLeft" title={i18next.t("general:Edit")}>
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productInfo.name}`)} />
</Tooltip>
<Tooltip placement="topLeft" title={productName}>
<Link to={`/products/${record.owner}/${productInfo.name}`} style={{display: "inline-block", maxWidth: "100%", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>
{productName}
</Link>
</Tooltip>
</div>
</Col>
<Col flex="none" style={{whiteSpace: "nowrap"}}>
<span style={{color: "#666"}}>
{Setting.getCurrencySymbol(currency)}{price} ({Setting.getCurrencyText(currency)}) × {number}
</span>
</Col>
</Row>
</List.Item>
);
}}
/>
</div>
);
},
},
{
title: i18next.t("order:Payment"),
dataIndex: "payment",
key: "payment",
width: "140px",
title: i18next.t("order:Price"),
dataIndex: "price",
key: "price",
width: "160px",
sorter: true,
...this.getColumnSearchProps("payment"),
...this.getColumnSearchProps("price"),
render: (text, record, index) => {
if (text === "") {
return "(empty)";
}
return (
<Link to={`/payments/${record.owner}/${text}`}>
{text}
const price = (record.price || 0).toFixed(2);
const currency = record.currency || "USD";
const priceDisplay = Setting.getPriceDisplay(price, currency);
return record.payment ? (
<Link to={`/payments/${record.owner}/${record.payment}`}>
{priceDisplay}
</Link>
) : (
<span>{priceDisplay}</span>
);
},
},
@@ -207,52 +237,30 @@ class OrderListPage extends BaseListPage {
sorter: true,
...this.getColumnSearchProps("state"),
},
{
title: i18next.t("order:Start time"),
dataIndex: "startTime",
key: "startTime",
width: "160px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("order:End time"),
dataIndex: "endTime",
key: "endTime",
width: "160px",
sorter: true,
render: (text, record, index) => {
if (text === "") {
return "(empty)";
}
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "240px",
width: "320px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
const isAdmin = Setting.isLocalAdminUser(this.props.account);
return (
<div style={{display: "flex", flexWrap: "wrap", gap: "8px"}}>
<Button onClick={() => this.props.history.push(`/orders/${record.owner}/${record.name}/pay`)} disabled={record.state !== "Created"}>
{i18next.t("order:Pay")}
<Button onClick={() => this.props.history.push(`/orders/${record.owner}/${record.name}/pay`)}>
{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")}
</Button>
<Button type="primary" onClick={() => this.props.history.push({pathname: `/orders/${record.owner}/${record.name}`, mode: isAdmin ? "edit" : "view"})}>{isAdmin ? i18next.t("general:Edit") : i18next.t("general:View")}</Button>
<PopconfirmModal
disabled={!isAdmin}
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteOrder(index)}
>
</PopconfirmModal>
{isAdmin && (
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteOrder(index)}
>
</PopconfirmModal>
)}
</div>
);
},

View File

@@ -22,15 +22,15 @@ import * as Setting from "./Setting";
class OrderPayPage extends React.Component {
constructor(props) {
super(props);
const params = new URLSearchParams(window.location.search);
this.state = {
owner: props?.match?.params?.organizationName ?? props?.match?.params?.owner ?? null,
orderName: props?.match?.params?.orderName ?? null,
order: null,
product: null,
firstProduct: null,
productInfos: [],
paymentEnv: "",
isProcessingPayment: false,
isViewMode: params.get("view") === "true",
isViewMode: false,
};
}
@@ -59,6 +59,8 @@ class OrderPayPage extends React.Component {
if (res.status === "ok") {
this.setState({
order: res.data,
productInfos: res.data?.productInfos,
isViewMode: res.data?.state !== "Created",
}, () => {
this.getProduct();
});
@@ -68,13 +70,19 @@ class OrderPayPage extends React.Component {
}
async getProduct() {
if (!this.state.order || !this.state.order.productName) {
if (!this.state.order) {
return;
}
const res = await ProductBackend.getProduct(this.state.order.owner, this.state.order.productName);
const firstProductName = this.state.order?.products?.[0] ?? this.state.order?.productInfos?.[0]?.name;
if (!firstProductName) {
return;
}
const res = await ProductBackend.getProduct(this.state.order.owner, firstProductName);
if (res.status === "ok") {
this.setState({
product: res.data,
firstProduct: res.data,
});
} else {
Setting.showMessage("error", res.msg);
@@ -82,11 +90,12 @@ class OrderPayPage extends React.Component {
}
getPrice(order) {
return `${Setting.getCurrencySymbol(order?.currency)}${order?.price} (${Setting.getCurrencyText(order)})`;
return `${Setting.getCurrencySymbol(order?.currency)}${order?.price} (${Setting.getCurrencyText(order?.currency)})`;
}
getProductPrice(product) {
return `${Setting.getCurrencySymbol(product?.currency)}${product?.price} (${Setting.getCurrencyText(product)})`;
const price = product.price * (product.quantity ?? 1);
return `${Setting.getCurrencySymbol(this.state.order?.currency)}${price.toFixed(2)} (${Setting.getCurrencyText(this.state.order?.currency)})`;
}
// Call Wechat Pay via jsapi
@@ -129,8 +138,8 @@ class OrderPayPage extends React.Component {
}
payOrder(provider) {
const {product, order} = this.state;
if (!product || !order) {
const {firstProduct, order} = this.state;
if (!firstProduct || !order) {
return;
}
@@ -172,6 +181,7 @@ class OrderPayPage extends React.Component {
getPayButton(provider, onClick) {
const providerTypeMap = {
"Dummy": i18next.t("product:Dummy"),
"Balance": i18next.t("user:Balance"),
"Alipay": i18next.t("product:Alipay"),
"WeChat Pay": i18next.t("product:WeChat Pay"),
"PayPal": i18next.t("product:PayPal"),
@@ -198,36 +208,91 @@ class OrderPayPage extends React.Component {
}
renderPaymentMethods() {
const {product} = this.state;
if (!product || !product.providerObjs || product.providerObjs.length === 0) {
const {firstProduct} = this.state;
if (!firstProduct || !firstProduct.providerObjs || firstProduct.providerObjs.length === 0) {
return <div>{i18next.t("product:There is no payment channel for this product.")}</div>;
}
return product.providerObjs.map(provider => {
return firstProduct.providerObjs.map(provider => {
return this.renderProviderButton(provider);
});
}
render() {
const {order, product} = this.state;
renderProduct(product) {
const isSubscriptionOrder = product.pricingName && product.planName;
if (!order || !product) {
return (
<div key={product.name} style={{marginBottom: "20px", border: "1px solid #f0f0f0", borderRadius: "2px", padding: "1px"}}>
<Descriptions bordered column={2} size="middle" labelStyle={{width: "150px"}}>
<Descriptions.Item label={i18next.t("general:Name")} span={2}>
<span style={{fontSize: 20, fontWeight: "500"}}>
{Setting.getLanguageText(product?.displayName)}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Image")} span={2}>
<img src={product?.image} alt={Setting.getLanguageText(product?.displayName)} height={90} style={{objectFit: "contain"}} />
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Price")} span={1}>
<span style={{fontSize: 18, fontWeight: "bold"}}>
{this.getProductPrice(product)}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Quantity")} span={1}>
<span style={{fontSize: 18}}>
{product.quantity ?? 1}
</span>
</Descriptions.Item>
{product?.detail && (
<Descriptions.Item label={i18next.t("product:Detail")} span={2}>
<span style={{fontSize: 16}}>{Setting.getLanguageText(product?.detail)}</span>
</Descriptions.Item>
)}
{isSubscriptionOrder && (
<>
<Descriptions.Item label={i18next.t("subscription:Subscription plan")} span={1}>
<span style={{fontSize: 16}}>{Setting.getLanguageText(product?.planName)}</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("subscription:Subscription pricing")} span={1}>
<span style={{fontSize: 16}}>{Setting.getLanguageText(product?.pricingName)}</span>
</Descriptions.Item>
</>
)}
</Descriptions>
</div>
);
}
render() {
const {order, productInfos} = this.state;
const updateTime = order?.updateTime || "";
const state = order?.state || "";
const updateTimeMap = {
Paid: i18next.t("order:Payment time"),
Canceled: i18next.t("order:Cancel time"),
PaymentFailed: i18next.t("order:Payment failed time"),
Timeout: i18next.t("order:Timeout time"),
};
const updateTimeLabel = updateTimeMap[state] || i18next.t("general:Updated time");
const shouldShowUpdateTime = state !== "Created" && updateTime !== "";
if (!order || !productInfos) {
return null;
}
const isSubscriptionOrder = order.pricingName && order.planName;
return (
<div className="login-content">
<Spin spinning={this.state.isProcessingPayment} size="large" tip={i18next.t("product:Processing payment...")} style={{paddingTop: "10%"}} >
<div style={{marginBottom: "20px"}}>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("order:Order Information")}</span>} bordered column={3}>
<Descriptions.Item label={i18next.t("order:Order ID")} span={3}>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("general:Order")}</span>} bordered column={3}>
<Descriptions.Item label={i18next.t("general:ID")} span={3}>
<span style={{fontSize: 16}}>
{order.name}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("order:Order Status")}>
<Descriptions.Item label={i18next.t("general:Status")}>
<span style={{fontSize: 16}}>
{order.state}
</span>
@@ -237,6 +302,13 @@ class OrderPayPage extends React.Component {
{Setting.getFormattedDate(order.createdTime)}
</span>
</Descriptions.Item>
{shouldShowUpdateTime && (
<Descriptions.Item label={updateTimeLabel}>
<span style={{fontSize: 16}}>
{Setting.getFormattedDate(updateTime)}
</span>
</Descriptions.Item>
)}
<Descriptions.Item label={i18next.t("general:User")}>
<span style={{fontSize: 16}}>
{order.user}
@@ -246,41 +318,14 @@ class OrderPayPage extends React.Component {
</div>
<div style={{marginBottom: "20px"}}>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("product:Product Information")}</span>} bordered column={3}>
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
<span style={{fontSize: 20}}>
{Setting.getLanguageText(product?.displayName)}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Image")} span={3}>
<img src={product?.image} alt={Setting.getLanguageText(product?.displayName)} height={90} style={{marginBottom: "20px"}} />
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Price")} span={3}>
<span style={{fontSize: 18, fontWeight: "bold"}}>
{this.getProductPrice(product)}
</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("product:Detail")} span={3}>
<span style={{fontSize: 16}}>{Setting.getLanguageText(product?.detail)}</span>
</Descriptions.Item>
</Descriptions>
<div style={{fontSize: Setting.isMobile() ? 18 : 24, fontWeight: "bold", marginBottom: "16px", color: "rgba(0, 0, 0, 0.85)"}}>
{i18next.t("product:Information")}
</div>
{productInfos.map(product => this.renderProduct(product))}
</div>
{isSubscriptionOrder && (
<div style={{marginBottom: "20px"}}>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("subscription:Subscription Information")}</span>} bordered column={3}>
<Descriptions.Item label={i18next.t("general:Plan")} span={3}>
<span style={{fontSize: 16}}>{order.planName}</span>
</Descriptions.Item>
<Descriptions.Item label={i18next.t("general:Pricing")} span={3}>
<span style={{fontSize: 16}}>{order.pricingName}</span>
</Descriptions.Item>
</Descriptions>
</div>
)}
<div>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("payment:Payment Information")}</span>} bordered column={3}>
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 18} : {fontSize: 24}}>{i18next.t("order:Payment")}</span>} bordered column={3}>
<Descriptions.Item label={i18next.t("product:Price")} span={3}>
<span style={{fontSize: 28, color: "red", fontWeight: "bold"}}>
{this.getPrice(order)}

View File

@@ -678,6 +678,16 @@ class OrganizationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Account menu"), i18next.t("organization:Account menu - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.accountMenu || "Horizontal"} onChange={(value => {this.updateOrganizationField("accountMenu", value);})}
options={[{value: "Horizontal", label: i18next.t("general:Horizontal")}, {value: "Vertical", label: i18next.t("general:Vertical")}].map(item => Setting.getOption(item.label, item.value))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("organization:Account items"), i18next.t("organization:Account items - Tooltip"))} :

View File

@@ -18,6 +18,7 @@ import {InfoCircleTwoTone} from "@ant-design/icons";
import * as PaymentBackend from "./backend/PaymentBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
import * as ProductBackend from "./backend/ProductBackend";
const {Option} = Select;
@@ -29,6 +30,7 @@ class PaymentEditPage extends React.Component {
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
paymentName: props.match.params.paymentName,
payment: null,
products: [],
isModalVisible: false,
isInvoiceLoading: false,
mode: props.location.mode !== undefined ? props.location.mode : "edit",
@@ -37,6 +39,7 @@ class PaymentEditPage extends React.Component {
UNSAFE_componentWillMount() {
this.getPayment();
this.getProducts();
}
getPayment() {
@@ -55,11 +58,23 @@ class PaymentEditPage extends React.Component {
});
}
getProducts() {
ProductBackend.getProducts(this.state.organizationName)
.then((res) => {
if (res.status === "ok") {
this.setState({
products: res.data,
});
} else {
Setting.showMessage("error", `Failed to get products: ${res.msg}`);
}
});
}
goToViewOrder() {
const payment = this.state.payment;
if (payment && payment.order) {
const viewUrl = `/orders/${payment.owner}/${payment.order}/pay?view=true`;
this.props.history.push(viewUrl);
this.props.history.push(`/orders/${payment.owner}/${payment.order}/pay`);
} else {
Setting.showMessage("error", i18next.t("order:Order not found"));
}
@@ -227,12 +242,25 @@ class PaymentEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("payment:Product"), i18next.t("payment:Product - Tooltip"))} :
{Setting.getLabel(i18next.t("payment:Products"), i18next.t("payment:Products - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.payment.productName} onChange={e => {
// this.updatePaymentField('productName', e.target.value);
}} />
<Select
mode="multiple"
style={{width: "100%"}}
value={this.state.payment?.products || []}
disabled={isViewMode}
allowClear
options={(this.state.products || [])
.map((p) => ({
label: Setting.getLanguageText(p?.displayName) || p?.name,
value: p?.name,
}))
.filter((o) => o.value)}
onChange={(value) => {
this.updatePaymentField("products", value);
}}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >

View File

@@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Table} from "antd";
import {Button, List, Table, Tooltip} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as PaymentBackend from "./backend/PaymentBackend";
@@ -22,6 +22,7 @@ import i18next from "i18next";
import BaseListPage from "./BaseListPage";
import * as Provider from "./auth/Provider";
import PopconfirmModal from "./common/modal/PopconfirmModal";
import {EditOutlined} from "@ant-design/icons";
class PaymentListPage extends BaseListPage {
newPayment() {
@@ -35,9 +36,9 @@ class PaymentListPage extends BaseListPage {
provider: "provider_pay_paypal",
type: "PayPal",
user: "admin",
productName: "computer-1",
productDisplayName: "A notebook computer",
detail: "This is a computer with excellent CPU, memory and disk",
products: [],
productsDisplayName: "",
detail: "This is a payment",
tag: "Promotion-1",
currency: "USD",
price: 300.00,
@@ -174,17 +175,46 @@ class PaymentListPage extends BaseListPage {
},
},
{
title: i18next.t("payment:Product"),
dataIndex: "productDisplayName",
key: "productDisplayName",
// width: '160px',
sorter: true,
...this.getColumnSearchProps("productDisplayName"),
title: i18next.t("order:Products"),
dataIndex: "products",
key: "products",
...this.getColumnSearchProps("products"),
render: (text, record, index) => {
const productInfos = record?.orderObj?.productInfos || [];
if (productInfos.length === 0) {
return `(${i18next.t("general:empty")})`;
}
return (
<Link to={`/products/${record.owner}/${record.productName}`}>
{text}
</Link>
<div>
<List
size="small"
locale={{emptyText: " "}}
dataSource={productInfos}
style={{
paddingTop: 8,
paddingBottom: 8,
}}
renderItem={(productInfo, i) => {
const price = productInfo.price * (productInfo.quantity || 1);
const currency = record.currency || "USD";
return (
<List.Item>
<div style={{display: "inline"}}>
<Tooltip placement="topLeft" title={i18next.t("general:Edit")}>
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/products/${record.owner}/${productInfo.name}`)} />
</Tooltip>
<Link to={`/products/${record.owner}/${productInfo.name}`}>
{productInfo.displayName || productInfo.name}
</Link>
<span style={{marginLeft: "8px", color: "#666"}}>
{Setting.getPriceDisplay(price, currency)}
</span>
</div>
</List.Item>
);
}}
/>
</div>
);
},
},
@@ -192,19 +222,11 @@ class PaymentListPage extends BaseListPage {
title: i18next.t("product:Price"),
dataIndex: "price",
key: "price",
width: "120px",
width: "160px",
sorter: true,
...this.getColumnSearchProps("price"),
},
{
title: i18next.t("payment:Currency"),
dataIndex: "currency",
key: "currency",
width: "120px",
sorter: true,
...this.getColumnSearchProps("currency"),
render: (text, record, index) => {
return Setting.getCurrencyWithFlag(text);
return Setting.getPriceDisplay(record.price, record.currency);
},
},
{

View File

@@ -150,8 +150,7 @@ class PaymentResultPage extends React.Component {
goToViewOrder() {
const payment = this.state.payment;
if (payment && payment.order) {
const viewUrl = `/orders/${payment.owner}/${payment.order}/pay?view=true`;
this.props.history.push(viewUrl);
this.props.history.push(`/orders/${payment.owner}/${payment.order}/pay`);
} else {
Setting.showMessage("error", i18next.t("order:Order not found"));
}
@@ -178,7 +177,7 @@ class PaymentResultPage extends React.Component {
<Result
status="success"
title={`${i18next.t("payment:Recharged successfully")}`}
subTitle={`${i18next.t("payment:You have successfully recharged")} ${payment.price} ${Setting.getCurrencyText(payment)}, ${i18next.t("payment:Your current balance is")} ${this.state.user?.balance} ${Setting.getCurrencyText(payment)}`}
subTitle={`${i18next.t("payment:You have successfully recharged")} ${payment.price} ${Setting.getCurrencyText(payment?.currency)}, ${i18next.t("payment:Your current balance is")} ${this.state.user?.balance} ${Setting.getCurrencyText(payment?.currency)}`}
extra={[
<Button type="primary" key="viewOrder" onClick={() => {
this.goToViewOrder();
@@ -202,7 +201,7 @@ class PaymentResultPage extends React.Component {
}
<Result
status="success"
title={`${i18next.t("payment:You have successfully completed the payment")}: ${payment.productDisplayName}`}
title={`${i18next.t("payment:You have successfully completed the payment")}: ${payment.productsDisplayName}`}
subTitle={i18next.t("payment:You can view your order details or return to the order list")}
extra={[
<Button type="primary" key="viewOrder" onClick={() => {
@@ -227,7 +226,7 @@ class PaymentResultPage extends React.Component {
}
<Result
status="info"
title={`${i18next.t("payment:The payment is still under processing")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}, ${i18next.t("payment:please wait for a few seconds...")}`}
title={`${i18next.t("payment:The payment is still under processing")}: ${payment.productsDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}, ${i18next.t("payment:please wait for a few seconds...")}`}
subTitle={i18next.t("payment:You can view your order details or return to the order list")}
extra={[
<Spin key="returnUrl" size="large" tip={i18next.t("payment:Processing...")} />,
@@ -243,7 +242,7 @@ class PaymentResultPage extends React.Component {
}
<Result
status="warning"
title={`${i18next.t("payment:The payment has been canceled")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
title={`${i18next.t("payment:The payment has been canceled")}: ${payment.productsDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
subTitle={i18next.t("payment:You can view your order details or return to the order list")}
extra={[
<Button type="primary" key="viewOrder" onClick={() => {
@@ -268,7 +267,7 @@ class PaymentResultPage extends React.Component {
}
<Result
status="warning"
title={`${i18next.t("payment:The payment has time out")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
title={`${i18next.t("payment:The payment has timed out")}: ${payment.productsDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
subTitle={i18next.t("payment:You can view your order details or return to the order list")}
extra={[
<Button type="primary" key="viewOrder" onClick={() => {
@@ -293,7 +292,7 @@ class PaymentResultPage extends React.Component {
}
<Result
status="error"
title={`${i18next.t("payment:The payment has failed")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
title={`${i18next.t("payment:The payment has failed")}: ${payment.productsDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
subTitle={`${i18next.t("payment:Failed reason")}: ${payment.message}`}
extra={[
<Button type="primary" key="viewOrder" onClick={() => {

View File

@@ -129,23 +129,15 @@ class PlanListPage extends BaseListPage {
sorter: true,
...this.getColumnSearchProps("displayName"),
},
{
title: i18next.t("payment:Currency"),
dataIndex: "currency",
key: "currency",
width: "120px",
sorter: true,
...this.getColumnSearchProps("currency"),
render: (text, record, index) => {
return Setting.getCurrencyWithFlag(text);
},
},
{
title: i18next.t("plan:Price"),
dataIndex: "price",
key: "price",
width: "130px",
width: "160px",
...this.getColumnSearchProps("price"),
render: (text, record, index) => {
return Setting.getPriceDisplay(record.price, record.currency);
},
},
{
title: i18next.t("plan:Period"),

View File

@@ -19,6 +19,7 @@ import * as ProductBackend from "./backend/ProductBackend";
import * as PlanBackend from "./backend/PlanBackend";
import * as PricingBackend from "./backend/PricingBackend";
import * as OrderBackend from "./backend/OrderBackend";
import * as UserBackend from "./backend/UserBackend";
import * as Setting from "./Setting";
class ProductBuyPage extends React.Component {
@@ -37,7 +38,8 @@ class ProductBuyPage extends React.Component {
pricing: props?.pricing ?? null,
plan: null,
isPlacingOrder: false,
customPrice: 0,
isAddingToCart: false,
customPrice: 100,
};
}
@@ -106,10 +108,16 @@ class ProductBuyPage extends React.Component {
product: res.data,
});
if (res.data.isRecharge && res.data.rechargeOptions?.length > 0) {
this.setState({
customPrice: res.data.rechargeOptions[0],
});
if (res.data.isRecharge) {
if (res.data.rechargeOptions?.length > 0) {
this.setState({
customPrice: res.data.rechargeOptions[0],
});
} else {
this.setState({
customPrice: 100,
});
}
}
} catch (err) {
Setting.showMessage("error", err.message);
@@ -126,7 +134,86 @@ class ProductBuyPage extends React.Component {
}
getPrice(product) {
return `${Setting.getCurrencySymbol(product?.currency)}${product?.price} (${Setting.getCurrencyText(product)})`;
return `${Setting.getCurrencySymbol(product?.currency)}${product?.price} (${Setting.getCurrencyText(product?.currency)})`;
}
addToCart(product) {
if (this.state.isAddingToCart) {
return;
}
this.setState({isAddingToCart: true});
const userOwner = this.props.account.owner;
const userName = this.props.account.name;
UserBackend.getUser(userOwner, userName)
.then((res) => {
if (res.status === "ok") {
const user = res.data;
const cart = user.cart || [];
let actualPrice = product.price;
if (product.isRecharge) {
actualPrice = this.state.customPrice;
if (actualPrice <= 0) {
Setting.showMessage("error", i18next.t("product:Custom price should be greater than zero"));
this.setState({isAddingToCart: false});
return;
}
}
const pricingName = this.state.pricingName || "";
const planName = this.state.planName || "";
if (cart.length > 0) {
const firstItem = cart[0];
if (firstItem.currency && product.currency && firstItem.currency !== product.currency) {
Setting.showMessage("error", i18next.t("product:The currency of the product you are adding is different from the currency of the items in the cart"));
this.setState({isAddingToCart: false});
return;
}
}
const existingItemIndex = cart.findIndex(item => item.name === product.name && item.price === actualPrice && (item.pricingName || "") === pricingName && (item.planName || "") === planName);
if (existingItemIndex !== -1) {
cart[existingItemIndex].quantity += 1;
} else {
const newProductInfo = {
name: product.name,
price: actualPrice,
currency: product.currency,
pricingName: pricingName,
planName: planName,
quantity: 1,
};
cart.push(newProductInfo);
}
user.cart = cart;
UserBackend.updateUser(user.owner, user.name, user)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully added"));
} else {
Setting.showMessage("error", res.msg);
}
})
.catch((error) => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
})
.finally(() => {
this.setState({isAddingToCart: false});
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${res.msg}`);
this.setState({isAddingToCart: false});
}
})
.catch((error) => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
this.setState({isAddingToCart: false});
});
}
placeOrder(product) {
@@ -134,11 +221,19 @@ class ProductBuyPage extends React.Component {
isPlacingOrder: true,
});
const productId = `${product.owner}/${product.name}`;
const pricingName = this.state.pricingName || "";
const planName = this.state.planName || "";
const customPrice = this.state.customPrice || 0;
OrderBackend.placeOrder(productId, pricingName, planName, this.state.userName ?? "", customPrice)
const productInfos = [{
name: product.name,
price: product.isRecharge ? customPrice : product.price,
pricingName: pricingName,
planName: planName,
quantity: 1,
}];
OrderBackend.placeOrder(product.owner, productInfos, this.state.userName ?? "")
.then((res) => {
if (res.status === "ok") {
const order = res.data;
@@ -206,7 +301,7 @@ class ProductBuyPage extends React.Component {
onChange={(e) => {this.setState({customPrice: e});}}
disabled={disableCustom}
/>
<span style={{fontSize: 16}}>{Setting.getCurrencyText(product)}</span>
<span style={{fontSize: 16}}>{Setting.getCurrencyText(product?.currency)}</span>
</Space>
</Space>
);
@@ -224,9 +319,10 @@ class ProductBuyPage extends React.Component {
const hasOptions = product.rechargeOptions && product.rechargeOptions.length > 0;
const disableCustom = product.disableCustomRecharge;
const isRechargeUnpurchasable = product.isRecharge && !hasOptions && disableCustom;
const isAmountZero = product.isRecharge && (this.state.customPrice === 0 || this.state.customPrice === null);
return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
<div style={{display: "flex", justifyContent: "center", alignItems: "center", gap: "20px"}}>
<Button
type="primary"
size="large"
@@ -238,11 +334,27 @@ class ProductBuyPage extends React.Component {
paddingRight: "60px",
}}
onClick={() => this.placeOrder(product)}
disabled={this.state.isPlacingOrder || isRechargeUnpurchasable}
disabled={this.state.isPlacingOrder || isRechargeUnpurchasable || isAmountZero}
loading={this.state.isPlacingOrder}
>
{i18next.t("order:Place Order")}
</Button>
<Button
type="default"
size="large"
style={{
height: "50px",
fontSize: "18px",
borderRadius: "30px",
paddingLeft: "30px",
paddingRight: "30px",
}}
onClick={() => this.addToCart(product)}
disabled={isRechargeUnpurchasable || this.state.isAddingToCart || isAmountZero}
loading={this.state.isAddingToCart}
>
{i18next.t("product:Add to cart")}
</Button>
</div>
);
}

View File

@@ -374,6 +374,14 @@ class ProductEditPage extends React.Component {
submitProductEdit(exitAfterSave) {
const product = Setting.deepCopy(this.state.product);
if (!product.currency) {
Setting.showMessage("error", i18next.t("product:Please select a currency"));
return;
}
if (!product.isCreatedByPlan && (!product.providers || product.providers.length === 0)) {
Setting.showMessage("error", i18next.t("product:Please select at least one payment provider"));
return;
}
if (product.isRecharge && product.disableCustomRecharge && (!product.rechargeOptions || product.rechargeOptions.length === 0)) {
Setting.showMessage("error", i18next.t("product:Please add at least one recharge option when custom amount is disabled"));
return;

View File

@@ -152,24 +152,16 @@ class ProductListPage extends BaseListPage {
sorter: true,
...this.getColumnSearchProps("tag"),
},
{
title: i18next.t("payment:Currency"),
dataIndex: "currency",
key: "currency",
width: "120px",
sorter: true,
...this.getColumnSearchProps("currency"),
render: (text, record, index) => {
return Setting.getCurrencyWithFlag(text);
},
},
{
title: i18next.t("product:Price"),
dataIndex: "price",
key: "price",
width: "120px",
width: "160px",
sorter: true,
...this.getColumnSearchProps("price"),
render: (text, record, index) => {
return Setting.getPriceDisplay(record.price, record.currency);
},
},
{
title: i18next.t("product:Quantity"),

View File

@@ -16,6 +16,7 @@ import React from "react";
import {Button, Card, Col, Row, Tag, Typography} from "antd";
import * as Setting from "./Setting";
import * as ProductBackend from "./backend/ProductBackend";
import * as UserBackend from "./backend/UserBackend";
import i18next from "i18next";
const {Text, Title} = Typography;
@@ -28,6 +29,7 @@ class ProductStorePage extends React.Component {
this.state = {
products: [],
loading: true,
addingToCartProducts: [],
};
}
@@ -57,11 +59,79 @@ class ProductStorePage extends React.Component {
});
}
addToCart(product) {
if (this.state.addingToCartProducts.includes(product.name)) {
return;
}
this.setState(prevState => ({addingToCartProducts: [...prevState.addingToCartProducts, product.name]}));
const userOwner = this.props.account.owner;
const userName = this.props.account.name;
UserBackend.getUser(userOwner, userName)
.then((res) => {
if (res.status === "ok") {
const user = res.data;
const cart = user.cart || [];
if (cart.length > 0) {
const firstItem = cart[0];
if (firstItem.currency && product.currency && firstItem.currency !== product.currency) {
Setting.showMessage("error", i18next.t("product:The currency of the product you are adding is different from the currency of the items in the cart"));
this.setState(prevState => ({addingToCartProducts: prevState.addingToCartProducts.filter(name => name !== product.name)}));
return;
}
}
const existingItemIndex = cart.findIndex(item => item.name === product.name && item.price === product.price);
if (existingItemIndex !== -1) {
cart[existingItemIndex].quantity += 1;
} else {
const newCartProductInfo = {
name: product.name,
price: product.price,
currency: product.currency,
pricingName: "",
planName: "",
quantity: 1,
};
cart.push(newCartProductInfo);
}
user.cart = cart;
UserBackend.updateUser(user.owner, user.name, user)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully added"));
} else {
Setting.showMessage("error", res.msg);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
})
.finally(() => {
this.setState(prevState => ({addingToCartProducts: prevState.addingToCartProducts.filter(name => name !== product.name)}));
});
} else {
Setting.showMessage("error", res.msg);
this.setState(prevState => ({addingToCartProducts: prevState.addingToCartProducts.filter(name => name !== product.name)}));
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
this.setState(prevState => ({addingToCartProducts: prevState.addingToCartProducts.filter(name => name !== product.name)}));
});
}
handleBuyProduct(product) {
this.props.history.push(`/products/${product.owner}/${product.name}/buy`);
}
renderProductCard(product) {
const isAdding = this.state.addingToCartProducts.includes(product.name);
return (
<Col xs={24} sm={12} md={8} lg={6} key={`${product.owner}/${product.name}`} style={{marginBottom: "20px"}}>
<Card
@@ -78,16 +148,32 @@ class ProductStorePage extends React.Component {
</div>
}
actions={[
<Button
key="buy"
type="primary"
onClick={(e) => {
e.stopPropagation();
this.handleBuyProduct(product);
}}
>
{i18next.t("product:Buy")}
</Button>,
<div key="actions" style={{display: "flex", justifyContent: "center", gap: "10px", width: "100%", padding: "0 10px"}} onClick={(e) => e.stopPropagation()}>
<Button
key="buy"
type="primary"
onClick={(e) => {
e.stopPropagation();
this.handleBuyProduct(product);
}}
>
{i18next.t("product:Buy")}
</Button>
{!product.isRecharge && (
<Button
key="add"
type="default"
onClick={(e) => {
e.stopPropagation();
this.addToCart(product);
}}
disabled={isAdding}
loading={isAdding}
>
{i18next.t("product:Add to cart")}
</Button>
)}
</div>,
]}
bodyStyle={{flex: 1, display: "flex", flexDirection: "column"}}
>

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