mirror of https://gitee.com/answerdev/answer.git
Merge branch 'dev' into feature-plugin
This commit is contained in:
commit
4a8ae2b166
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
||||||
.PHONY: build clean ui
|
.PHONY: build clean ui
|
||||||
|
|
||||||
VERSION=1.0.5
|
VERSION=1.0.6
|
||||||
BIN=answer
|
BIN=answer
|
||||||
DIR_SRC=./cmd/answer
|
DIR_SRC=./cmd/answer
|
||||||
DOCKER_CMD=docker
|
DOCKER_CMD=docker
|
||||||
|
|
|
@ -6429,9 +6429,6 @@ const docTemplate = `{
|
||||||
"schema.GetOtherUserInfoResp": {
|
"schema.GetOtherUserInfoResp": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"has": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"info": {
|
"info": {
|
||||||
"$ref": "#/definitions/schema.GetOtherUserInfoByUsernameResp"
|
"$ref": "#/definitions/schema.GetOtherUserInfoByUsernameResp"
|
||||||
}
|
}
|
||||||
|
@ -8222,7 +8219,6 @@ const docTemplate = `{
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"status": {
|
"status": {
|
||||||
"description": "user status",
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"normal",
|
"normal",
|
||||||
|
@ -8232,7 +8228,6 @@ const docTemplate = `{
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"description": "user id",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8528,6 +8523,10 @@ const docTemplate = `{
|
||||||
"label": {
|
"label": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"progress": {
|
||||||
|
"description": "Translation completion percentage",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"value": {
|
"value": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6417,9 +6417,6 @@
|
||||||
"schema.GetOtherUserInfoResp": {
|
"schema.GetOtherUserInfoResp": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"has": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"info": {
|
"info": {
|
||||||
"$ref": "#/definitions/schema.GetOtherUserInfoByUsernameResp"
|
"$ref": "#/definitions/schema.GetOtherUserInfoByUsernameResp"
|
||||||
}
|
}
|
||||||
|
@ -8210,7 +8207,6 @@
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"status": {
|
"status": {
|
||||||
"description": "user status",
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"normal",
|
"normal",
|
||||||
|
@ -8220,7 +8216,6 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"description": "user id",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8516,6 +8511,10 @@
|
||||||
"label": {
|
"label": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"progress": {
|
||||||
|
"description": "Translation completion percentage",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"value": {
|
"value": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
|
@ -557,8 +557,6 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
schema.GetOtherUserInfoResp:
|
schema.GetOtherUserInfoResp:
|
||||||
properties:
|
properties:
|
||||||
has:
|
|
||||||
type: boolean
|
|
||||||
info:
|
info:
|
||||||
$ref: '#/definitions/schema.GetOtherUserInfoByUsernameResp'
|
$ref: '#/definitions/schema.GetOtherUserInfoByUsernameResp'
|
||||||
type: object
|
type: object
|
||||||
|
@ -1819,7 +1817,6 @@ definitions:
|
||||||
schema.UpdateUserStatusReq:
|
schema.UpdateUserStatusReq:
|
||||||
properties:
|
properties:
|
||||||
status:
|
status:
|
||||||
description: user status
|
|
||||||
enum:
|
enum:
|
||||||
- normal
|
- normal
|
||||||
- suspended
|
- suspended
|
||||||
|
@ -1827,7 +1824,6 @@ definitions:
|
||||||
- inactive
|
- inactive
|
||||||
type: string
|
type: string
|
||||||
user_id:
|
user_id:
|
||||||
description: user id
|
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- status
|
- status
|
||||||
|
@ -2039,6 +2035,9 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
label:
|
label:
|
||||||
type: string
|
type: string
|
||||||
|
progress:
|
||||||
|
description: Translation completion percentage
|
||||||
|
type: integer
|
||||||
value:
|
value:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -36,13 +36,13 @@ require (
|
||||||
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05
|
github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a
|
github.com/swaggo/files v1.0.0
|
||||||
github.com/swaggo/gin-swagger v1.5.3
|
github.com/swaggo/gin-swagger v1.5.3
|
||||||
github.com/swaggo/swag v1.8.7
|
github.com/swaggo/swag v1.8.10
|
||||||
github.com/tidwall/gjson v1.14.4
|
github.com/tidwall/gjson v1.14.4
|
||||||
github.com/yuin/goldmark v1.4.13
|
github.com/yuin/goldmark v1.4.13
|
||||||
golang.org/x/crypto v0.1.0
|
golang.org/x/crypto v0.1.0
|
||||||
golang.org/x/net v0.1.0
|
golang.org/x/net v0.2.0
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
modernc.org/sqlite v1.14.2
|
modernc.org/sqlite v1.14.2
|
||||||
|
@ -123,7 +123,7 @@ require (
|
||||||
go.uber.org/zap v1.23.0 // indirect
|
go.uber.org/zap v1.23.0 // indirect
|
||||||
golang.org/x/image v0.1.0 // indirect
|
golang.org/x/image v0.1.0 // indirect
|
||||||
golang.org/x/mod v0.6.0 // indirect
|
golang.org/x/mod v0.6.0 // indirect
|
||||||
golang.org/x/sys v0.1.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.2.0 // indirect
|
golang.org/x/tools v0.2.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -670,13 +670,14 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
|
|
||||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
||||||
|
github.com/swaggo/files v1.0.0 h1:1gGXVIeUFCS/dta17rnP0iOpr6CXFwKD7EO5ID233e4=
|
||||||
|
github.com/swaggo/files v1.0.0/go.mod h1:N59U6URJLyU1PQgFqPM7wXLMhJx7QAolnvfQkqO13kc=
|
||||||
github.com/swaggo/gin-swagger v1.5.3 h1:8mWmHLolIbrhJJTflsaFoZzRBYVmEE7JZGIq08EiC0Q=
|
github.com/swaggo/gin-swagger v1.5.3 h1:8mWmHLolIbrhJJTflsaFoZzRBYVmEE7JZGIq08EiC0Q=
|
||||||
github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89Yp4uA50HpI=
|
github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89Yp4uA50HpI=
|
||||||
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
|
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
|
||||||
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
|
github.com/swaggo/swag v1.8.10 h1:eExW4bFa52WOjqRzRD58bgWsWfdFJso50lpbeTcmTfo=
|
||||||
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
github.com/swaggo/swag v1.8.10/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
|
@ -852,8 +853,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -946,11 +947,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Should not contain synonym tags.
|
other: Should not contain synonym tags.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: No permission to update.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,10 @@ backend:
|
||||||
other: Email and password do not match.
|
other: Email and password do not match.
|
||||||
error:
|
error:
|
||||||
admin:
|
admin:
|
||||||
|
cannot_update_their_password:
|
||||||
|
other: You cannot modify your password.
|
||||||
|
cannot_modify_self_status:
|
||||||
|
other: You cannot modify your status.
|
||||||
email_or_password_wrong:
|
email_or_password_wrong:
|
||||||
other: Email and password do not match.
|
other: Email and password do not match.
|
||||||
answer:
|
answer:
|
||||||
|
@ -81,6 +85,8 @@ backend:
|
||||||
new_password_same_as_previous_setting:
|
new_password_same_as_previous_setting:
|
||||||
other: The new password is the same as the previous one.
|
other: The new password is the same as the previous one.
|
||||||
question:
|
question:
|
||||||
|
already_deleted:
|
||||||
|
other: This post has been deleted.
|
||||||
not_found:
|
not_found:
|
||||||
other: Question not found.
|
other: Question not found.
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
|
@ -109,7 +115,7 @@ backend:
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: No permission to update.
|
||||||
is_used_cannot_delete:
|
is_used_cannot_delete:
|
||||||
other: "You cannot delete a tag that is in use"
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -837,6 +843,7 @@ ui:
|
||||||
approve: Approve
|
approve: Approve
|
||||||
reject: Reject
|
reject: Reject
|
||||||
skip: Skip
|
skip: Skip
|
||||||
|
discard_draft: Discard draft
|
||||||
search:
|
search:
|
||||||
title: Search Results
|
title: Search Results
|
||||||
keywords: Keywords
|
keywords: Keywords
|
||||||
|
@ -1033,9 +1040,11 @@ ui:
|
||||||
answers: answers
|
answers: answers
|
||||||
accepted: Accepted
|
accepted: Accepted
|
||||||
page_404:
|
page_404:
|
||||||
|
http_error: HTTP Error 404
|
||||||
desc: "Unfortunately, this page doesn't exist."
|
desc: "Unfortunately, this page doesn't exist."
|
||||||
back_home: Back to homepage
|
back_home: Back to homepage
|
||||||
page_50X:
|
page_50X:
|
||||||
|
http_error: HTTP Error 500
|
||||||
desc: The server encountered an error and could not complete your request.
|
desc: The server encountered an error and could not complete your request.
|
||||||
back_home: Back to homepage
|
back_home: Back to homepage
|
||||||
page_maintenance:
|
page_maintenance:
|
||||||
|
@ -1453,6 +1462,10 @@ ui:
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
prompt:
|
prompt:
|
||||||
leave_page: "Are you sure you want to leave the page?"
|
leave_page: Are you sure you want to leave the page?
|
||||||
changes_not_save: "Your changes may not be saved."
|
changes_not_save: Your changes may not be saved.
|
||||||
|
draft:
|
||||||
|
discard_confirm: Are you sure you want to discard your draft?
|
||||||
|
messages:
|
||||||
|
post_deleted: This post has been deleted.
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: No debe contener etiquetas sinónimas.
|
other: No debe contener etiquetas sinónimas.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: Sin permiso para actualizar.
|
other: Sin permiso para actualizar.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: No se puede establecer como sinónimo de una etiqueta la propia etiqueta.
|
other: No se puede establecer como sinónimo de una etiqueta la propia etiqueta.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -330,7 +332,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Código no puede estar vacío.
|
empty: Código no puede estar vacío.
|
||||||
language:
|
language:
|
||||||
label: Lenguage (opcional)
|
label: Language
|
||||||
placeholder: Detección automática
|
placeholder: Detección automática
|
||||||
btn_cancel: Cancelar
|
btn_cancel: Cancelar
|
||||||
btn_confirm: Añadir
|
btn_confirm: Añadir
|
||||||
|
@ -366,7 +368,7 @@ ui:
|
||||||
only_image: Solo se permiten archivos de imagen.
|
only_image: Solo se permiten archivos de imagen.
|
||||||
max_size: El tamaño del archivo no puede superar 4MB.
|
max_size: El tamaño del archivo no puede superar 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Descripción (opcional)
|
label: Description
|
||||||
tab_url: URL de la imagen
|
tab_url: URL de la imagen
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -375,7 +377,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: La URL de la imagen no puede estar vacía.
|
empty: La URL de la imagen no puede estar vacía.
|
||||||
name:
|
name:
|
||||||
label: Descripción (opcional)
|
label: Description
|
||||||
btn_cancel: Cancelar
|
btn_cancel: Cancelar
|
||||||
btn_confirm: Añadir
|
btn_confirm: Añadir
|
||||||
uploading: Subiendo
|
uploading: Subiendo
|
||||||
|
@ -395,7 +397,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: La dirección no puede estar vacía.
|
empty: La dirección no puede estar vacía.
|
||||||
name:
|
name:
|
||||||
label: Descripción (opcional)
|
label: Description
|
||||||
btn_cancel: Cancelar
|
btn_cancel: Cancelar
|
||||||
btn_confirm: Añadir
|
btn_confirm: Añadir
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -443,7 +445,7 @@ ui:
|
||||||
range: URL slug hasta 35 caracteres.
|
range: URL slug hasta 35 caracteres.
|
||||||
character: La URL amigable contiene caracteres no permitidos.
|
character: La URL amigable contiene caracteres no permitidos.
|
||||||
desc:
|
desc:
|
||||||
label: Descripción (opcional)
|
label: Description
|
||||||
btn_cancel: Cancelar
|
btn_cancel: Cancelar
|
||||||
btn_submit: Enviar
|
btn_submit: Enviar
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -460,9 +462,11 @@ ui:
|
||||||
synonyms_text: Las siguientes etiquetas serán reasignadas a
|
synonyms_text: Las siguientes etiquetas serán reasignadas a
|
||||||
delete:
|
delete:
|
||||||
title: Eliminar esta etiqueta
|
title: Eliminar esta etiqueta
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>No se permite la eliminación de etiquetas con posts.</p><p>Por favor antes elimina esta etiqueta del post.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: '¿Estás seguro de que deseas borrarlo?'
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Cerrar
|
close: Cerrar
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Editar etiqueta
|
title: Editar etiqueta
|
||||||
|
@ -698,13 +702,13 @@ ui:
|
||||||
default: Sistema
|
default: Sistema
|
||||||
msg: Por favor, sube una imagen
|
msg: Por favor, sube una imagen
|
||||||
bio:
|
bio:
|
||||||
label: Sobre mí (opcional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Sitio web (opcional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Formato del sitio web incorrecto
|
msg: Formato del sitio web incorrecto
|
||||||
location:
|
location:
|
||||||
label: Ubicación (opcional)
|
label: Location
|
||||||
placeholder: "Ciudad, País"
|
placeholder: "Ciudad, País"
|
||||||
notification:
|
notification:
|
||||||
heading: Notificaciones
|
heading: Notificaciones
|
||||||
|
@ -1188,11 +1192,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1202,14 +1206,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1261,18 +1257,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1338,6 +1334,7 @@ ui:
|
||||||
label: Inicio de sesión requerido
|
label: Inicio de sesión requerido
|
||||||
text: Sólo usuarios con sesión iniciada pueden acceder a esta comunidad.
|
text: Sólo usuarios con sesión iniciada pueden acceder a esta comunidad.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: no puede estar en blanco
|
empty: no puede estar en blanco
|
||||||
invalid: no es válido
|
invalid: no es válido
|
||||||
btn_submit: Guardar
|
btn_submit: Guardar
|
||||||
|
@ -1387,5 +1384,7 @@ ui:
|
||||||
staffs: Nuestor equipo de la comunidad
|
staffs: Nuestor equipo de la comunidad
|
||||||
reputation: reputación
|
reputation: reputación
|
||||||
votes: votos
|
votes: votos
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Ne dois pas contenir de tags synonymes.
|
other: Ne dois pas contenir de tags synonymes.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: Pas de permission pour mettre à jour.
|
other: Pas de permission pour mettre à jour.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: Vous ne pouvez pas supprimer un tag utilisé
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: Vous ne pouvez pas définir le synonyme de la balise actuelle comme elle-même.
|
other: Vous ne pouvez pas définir le synonyme de la balise actuelle comme elle-même.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Le code ne peut pas être vide.
|
empty: Le code ne peut pas être vide.
|
||||||
language:
|
language:
|
||||||
label: Langue (facultatif)
|
label: Langage
|
||||||
placeholder: Détection automatique
|
placeholder: Détection automatique
|
||||||
btn_cancel: Annuler
|
btn_cancel: Annuler
|
||||||
btn_confirm: Ajouter
|
btn_confirm: Ajouter
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Seules les images sont autorisées.
|
only_image: Seules les images sont autorisées.
|
||||||
max_size: La taille du fichier ne doit pas dépasser 4 Mo.
|
max_size: La taille du fichier ne doit pas dépasser 4 Mo.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optionnel)
|
label: Description
|
||||||
tab_url: URL de l'image
|
tab_url: URL de l'image
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: L'URL de l'image ne peut pas être vide.
|
empty: L'URL de l'image ne peut pas être vide.
|
||||||
name:
|
name:
|
||||||
label: Description (optionnel)
|
label: Description
|
||||||
btn_cancel: Annuler
|
btn_cancel: Annuler
|
||||||
btn_confirm: Ajouter
|
btn_confirm: Ajouter
|
||||||
uploading: Téléversement en cours
|
uploading: Téléversement en cours
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: L'URL ne peut pas être vide.
|
empty: L'URL ne peut pas être vide.
|
||||||
name:
|
name:
|
||||||
label: Description (facultatif)
|
label: Description
|
||||||
btn_cancel: Annuler
|
btn_cancel: Annuler
|
||||||
btn_confirm: Ajouter
|
btn_confirm: Ajouter
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: Titre de 35 caractères maximum.
|
range: Titre de 35 caractères maximum.
|
||||||
character: Le slug d'URL contient un jeu de caractères non autorisé.
|
character: Le slug d'URL contient un jeu de caractères non autorisé.
|
||||||
desc:
|
desc:
|
||||||
label: Description (facultatif)
|
label: Description
|
||||||
btn_cancel: Annuler
|
btn_cancel: Annuler
|
||||||
btn_submit: Valider
|
btn_submit: Valider
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: Les balises suivantes seront remappées en
|
synonyms_text: Les balises suivantes seront remappées en
|
||||||
delete:
|
delete:
|
||||||
title: Supprimer cette étiquette
|
title: Supprimer cette étiquette
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>Nous ne permettons pas de supprimer le tag avec les posts.</p><p>Veuillez d'abord supprimer ce tag des posts.</p>
|
<p>Nous ne permettons pas de <strong>supprimer un tag avec des posts.</strong></p><p>Veuillez d'abord supprimer ce tag des posts.</p>
|
||||||
content2: Etes-vous sûr de vouloir supprimer ?
|
tip_with_synonyms: >-
|
||||||
|
<p>Nous ne permettons pas de <strong>supprimer un tag avec des synonymes</strong>.</p> <p>Veuillez d'abord supprimer les synonymes de ce tag.</p>
|
||||||
|
tip: Êtes-vous sûr de vouloir supprimer ?
|
||||||
close: Fermer
|
close: Fermer
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Editer le tag
|
title: Editer le tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: Système
|
default: Système
|
||||||
msg: Veuillez charger un avatar
|
msg: Veuillez charger un avatar
|
||||||
bio:
|
bio:
|
||||||
label: À propos de moi (optionnel)
|
label: À propos de moi
|
||||||
website:
|
website:
|
||||||
label: Site web (facultatif)
|
label: Site Web
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Format du site web incorrect
|
msg: Format du site web incorrect
|
||||||
location:
|
location:
|
||||||
label: Emplacement (facultatif)
|
label: Position
|
||||||
placeholder: "Ville, Pays"
|
placeholder: "Ville, Pays"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Indiquez une URL valide.
|
validate: Indiquez une URL valide.
|
||||||
text: L'adresse de ce site.
|
text: L'adresse de ce site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Description courte (optionnel)
|
label: Description Courte
|
||||||
msg: La description courte ne peut pas être vide.
|
msg: La description courte ne peut pas être vide.
|
||||||
text: "La description courte, telle qu'elle est utilisée dans le tag titre de la page d'accueil."
|
text: "La description courte, telle qu'elle est utilisée dans le tag titre de la page d'accueil."
|
||||||
desc:
|
desc:
|
||||||
label: Description du site (optionnel)
|
label: Description du Site
|
||||||
msg: La description du site ne peut pas être vide.
|
msg: La description du site ne peut pas être vide.
|
||||||
text: "Décrivez ce site en une phrase, telle qu'elle est utilisée dans la balise meta description."
|
text: "Décrivez ce site en une phrase, telle qu'elle est utilisée dans la balise meta description."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: L'adresse email du responsable du site.
|
text: L'adresse email du responsable du site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optionnel)
|
|
||||||
msg: Le logo ne peut pas être vide.
|
|
||||||
text: Vous pouvez télécharger votre image ou la <1>réinitialiser</1> sur le texte du titre du site.
|
|
||||||
theme:
|
|
||||||
label: Thème
|
|
||||||
msg: Le thème ne peut pas être vide.
|
|
||||||
text: Sélectionne un thème existant.
|
|
||||||
language:
|
language:
|
||||||
label: Langue de l'interface
|
label: Langue de l'interface
|
||||||
msg: La langue de l'interface ne peut pas être vide.
|
msg: La langue de l'interface ne peut pas être vide.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Marque
|
page_title: Marque
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optionnel)
|
label: Logo
|
||||||
msg: Le logo ne peut pas être vide.
|
msg: Le logo ne peut pas être vide.
|
||||||
text: L'image du logo en haut à gauche de votre site. Utilisez une grande image rectangulaire avec une hauteur de 56 et un ratio d'aspect supérieur à 3:1. Si laissé vide, le titre du site sera affiché.
|
text: L'image du logo en haut à gauche de votre site. Utilisez une grande image rectangulaire avec une hauteur de 56 et un ratio d'aspect supérieur à 3:1. Si laissé vide, le titre du site sera affiché.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Logo mobile (optionnel)
|
label: Logo Mobile
|
||||||
text: Le logo utilisé sur la version mobile de votre site. Utilisez une image rectangulaire large avec une hauteur de 56. Si laissé vide, l'image du paramètre « logo » sera utilisée.
|
text: Le logo utilisé sur la version mobile de votre site. Utilisez une image rectangulaire large avec une hauteur de 56. Si laissé vide, l'image du paramètre « logo » sera utilisée.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Icône carrée (optionnel)
|
label: Icône carrée
|
||||||
msg: L'icône carrée ne peut pas être vide.
|
msg: L'icône carrée ne peut pas être vide.
|
||||||
text: Image utilisée comme base pour les icônes de métadonnées. Idéalement supérieure à 512x512.
|
text: Image utilisée comme base pour les icônes de métadonnées. Idéalement supérieure à 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optionnel)
|
label: Favicon
|
||||||
text: Une favicon pour votre site. Pour fonctionner correctement sur un CDN, il doit s'agir d'un png. Sera redimensionné en 32x32. Si laissé vide, « icône carrée » sera utilisé.
|
text: Une favicon pour votre site. Pour fonctionner correctement sur un CDN, il doit s'agir d'un png. Sera redimensionné en 32x32. Si laissé vide, « icône carrée » sera utilisé.
|
||||||
legal:
|
legal:
|
||||||
page_title: Légal
|
page_title: Légal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Connexion requise
|
label: Connexion requise
|
||||||
text: Seuls les utilisateurs connectés peuvent accéder à cette communauté.
|
text: Seuls les utilisateurs connectés peuvent accéder à cette communauté.
|
||||||
form:
|
form:
|
||||||
|
optional: (optionnel)
|
||||||
empty: ne peut pas être vide
|
empty: ne peut pas être vide
|
||||||
invalid: est invalide
|
invalid: est invalide
|
||||||
btn_submit: Sauvegarder
|
btn_submit: Sauvegarder
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Staff de la communauté
|
staffs: Staff de la communauté
|
||||||
reputation: réputation
|
reputation: réputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Voulez-vous vraiment quitter la page ?"
|
||||||
|
changes_not_save: "Impossible d'enregistrer vos modifications."
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,41 @@
|
||||||
# all support language
|
# all support language
|
||||||
language_options:
|
language_options:
|
||||||
- label: "English(US)"
|
- label: "English"
|
||||||
value: "en_US"
|
value: "en_US"
|
||||||
- label: "Español(ES)"
|
progress: 100
|
||||||
|
- label: "Español"
|
||||||
value: "es_ES"
|
value: "es_ES"
|
||||||
- label: "Português(PT)"
|
progress: 0
|
||||||
|
- label: "Português(BR)"
|
||||||
|
value: "pt_BR"
|
||||||
|
progress: 0
|
||||||
|
- label: "Português"
|
||||||
value: "pt_PT"
|
value: "pt_PT"
|
||||||
- label: "Deutsch(DE)"
|
progress: 0
|
||||||
|
- label: "Deutsch"
|
||||||
value: "de_DE"
|
value: "de_DE"
|
||||||
- label: "Français(FR)"
|
progress: 4
|
||||||
|
- label: "Français"
|
||||||
value: "fr_FR"
|
value: "fr_FR"
|
||||||
- label: "日本語(JA)"
|
progress: 100
|
||||||
|
- label: "日本語"
|
||||||
value: "ja_JP"
|
value: "ja_JP"
|
||||||
- label: "Italiano(IT)"
|
progress: 0
|
||||||
|
- label: "Italiano"
|
||||||
value: "it_IT"
|
value: "it_IT"
|
||||||
- label: "Русский(RU)"
|
progress: 16
|
||||||
|
- label: "Русский"
|
||||||
value: "ru_RU"
|
value: "ru_RU"
|
||||||
- label: "简体中文(CN)"
|
progress: 13
|
||||||
|
- label: "简体中文"
|
||||||
value: "zh_CN"
|
value: "zh_CN"
|
||||||
- label: "繁體中文(CN)"
|
progress: 100
|
||||||
|
- label: "繁體中文"
|
||||||
value: "zh_TW"
|
value: "zh_TW"
|
||||||
- label: "한국어(KO)"
|
progress: 100
|
||||||
|
- label: "한국어"
|
||||||
value: "ko_KR"
|
value: "ko_KR"
|
||||||
- label: "Tiếng Việt(VI)"
|
progress: 0
|
||||||
|
- label: "Tiếng Việt"
|
||||||
value: "vi_VN"
|
value: "vi_VN"
|
||||||
|
progress: 0
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Tidak boleh mengandung Tag sinonim.
|
other: Tidak boleh mengandung Tag sinonim.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: Tidak memiliki izin untuk memperbaharui.
|
other: Tidak memiliki izin untuk memperbaharui.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: Anda tidak bisa menetapkan sinonim dari tag saat ini dengan tag yang sama.
|
other: Anda tidak bisa menetapkan sinonim dari tag saat ini dengan tag yang sama.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code tidak boleh kosong.
|
empty: Code tidak boleh kosong.
|
||||||
language:
|
language:
|
||||||
label: Bahasa (opsional)
|
label: Language
|
||||||
placeholder: Deteksi otomatis
|
placeholder: Deteksi otomatis
|
||||||
btn_cancel: Batal
|
btn_cancel: Batal
|
||||||
btn_confirm: Tambah
|
btn_confirm: Tambah
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Hanya file Gambar yang diperbolehkan.
|
only_image: Hanya file Gambar yang diperbolehkan.
|
||||||
max_size: Ukuran file tidak boleh melebihi 4MB.
|
max_size: Ukuran file tidak boleh melebihi 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Deskripsi (opsional)
|
label: Description
|
||||||
tab_url: URL gambar
|
tab_url: URL gambar
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL gambar tidak boleh kosong.
|
empty: URL gambar tidak boleh kosong.
|
||||||
name:
|
name:
|
||||||
label: Deskripsi (opsional)
|
label: Description
|
||||||
btn_cancel: Batal
|
btn_cancel: Batal
|
||||||
btn_confirm: Tambah
|
btn_confirm: Tambah
|
||||||
uploading: Sedang mengunggah
|
uploading: Sedang mengunggah
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Batal
|
btn_cancel: Batal
|
||||||
btn_confirm: Tambah
|
btn_confirm: Tambah
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: Tag berikut akan dipetakan ulang ke
|
synonyms_text: Tag berikut akan dipetakan ulang ke
|
||||||
delete:
|
delete:
|
||||||
title: Hapus tagar ini
|
title: Hapus tagar ini
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Anda yakin ingin menghapusnya?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Tutup
|
close: Tutup
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Ubah Tag
|
title: Ubah Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Non deve contenere tag sinonimi.
|
other: Non deve contenere tag sinonimi.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: Nessun permesso per l'aggiornamento.
|
other: Nessun permesso per l'aggiornamento.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: Non puoi impostare il sinonimo del tag corrente come se stesso.
|
other: Non puoi impostare il sinonimo del tag corrente come se stesso.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Should not contain synonym tags.
|
other: Should not contain synonym tags.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: No permission to update.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Should not contain synonym tags.
|
other: Should not contain synonym tags.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: No permission to update.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
1624
i18n/pt_BR.yaml
1624
i18n/pt_BR.yaml
File diff suppressed because it is too large
Load Diff
271
i18n/pt_PT.yaml
271
i18n/pt_PT.yaml
|
@ -14,153 +14,155 @@ backend:
|
||||||
role:
|
role:
|
||||||
name:
|
name:
|
||||||
user:
|
user:
|
||||||
other: User
|
other: Usuário
|
||||||
admin:
|
admin:
|
||||||
other: Admin
|
other: Administrador
|
||||||
moderator:
|
moderator:
|
||||||
other: Moderator
|
other: Moderador
|
||||||
description:
|
description:
|
||||||
user:
|
user:
|
||||||
other: Default with no special access.
|
other: Padrão sem acesso especial.
|
||||||
admin:
|
admin:
|
||||||
other: Have the full power to access the site.
|
other: Possui acesso total ao site.
|
||||||
moderator:
|
moderator:
|
||||||
other: Has access to all posts except admin settings.
|
other: Possui acesso a todas as postagens exceto às configurações de usuários.
|
||||||
email:
|
email:
|
||||||
other: Email
|
other: E-mail
|
||||||
password:
|
password:
|
||||||
other: Password
|
other: Senha
|
||||||
email_or_password_wrong_error:
|
email_or_password_wrong_error:
|
||||||
other: Email and password do not match.
|
other: O e-mail e a palavra-passe não coincidem.
|
||||||
error:
|
error:
|
||||||
admin:
|
admin:
|
||||||
email_or_password_wrong:
|
email_or_password_wrong:
|
||||||
other: Email and password do not match.
|
other: O e-mail e a palavra-passe não coincidem.
|
||||||
answer:
|
answer:
|
||||||
not_found:
|
not_found:
|
||||||
other: Answer do not found.
|
other: Resposta não encontrada.
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
other: No permission to delete.
|
other: Sem permissão para remover.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Sem permissão para atualizar.
|
||||||
comment:
|
comment:
|
||||||
edit_without_permission:
|
edit_without_permission:
|
||||||
other: Comment are not allowed to edit.
|
other: Não é possível alterar comentários.
|
||||||
not_found:
|
not_found:
|
||||||
other: Comment not found.
|
other: Comentário não encontrado.
|
||||||
cannot_edit_after_deadline:
|
cannot_edit_after_deadline:
|
||||||
other: The comment time has been too long to modify.
|
other: O tempo do comentário foi muito longo para ser modificado.
|
||||||
email:
|
email:
|
||||||
duplicate:
|
duplicate:
|
||||||
other: Email already exists.
|
other: O e-mail já existe.
|
||||||
need_to_be_verified:
|
need_to_be_verified:
|
||||||
other: Email should be verified.
|
other: O e-mail deve ser verificado.
|
||||||
verify_url_expired:
|
verify_url_expired:
|
||||||
other: Email verified URL has expired, please resend the email.
|
other: O e-mail verificado URL expirou, por favor, reenvie o e-mail.
|
||||||
lang:
|
lang:
|
||||||
not_found:
|
not_found:
|
||||||
other: Language file not found.
|
other: Arquivo de idioma não encontrado.
|
||||||
object:
|
object:
|
||||||
captcha_verification_failed:
|
captcha_verification_failed:
|
||||||
other: Captcha wrong.
|
other: O Captcha está incorreto.
|
||||||
disallow_follow:
|
disallow_follow:
|
||||||
other: You are not allowed to follow.
|
other: Você não possui autorização suficiente para seguir.
|
||||||
disallow_vote:
|
disallow_vote:
|
||||||
other: You are not allowed to vote.
|
other: Você não possui permissão para votar.
|
||||||
disallow_vote_your_self:
|
disallow_vote_your_self:
|
||||||
other: You can't vote for your own post.
|
other: Você não pode votar na sua própria postagem.
|
||||||
not_found:
|
not_found:
|
||||||
other: Object not found.
|
other: Objeto não encontrado.
|
||||||
verification_failed:
|
verification_failed:
|
||||||
other: Verification failed.
|
other: A verificação falhou.
|
||||||
email_or_password_incorrect:
|
email_or_password_incorrect:
|
||||||
other: Email and password do not match.
|
other: O e-mail e a senha não correspondem.
|
||||||
old_password_verification_failed:
|
old_password_verification_failed:
|
||||||
other: The old password verification failed
|
other: Falha na verificação de senha antiga
|
||||||
new_password_same_as_previous_setting:
|
new_password_same_as_previous_setting:
|
||||||
other: The new password is the same as the previous one.
|
other: A nova senha é a mesma que a anterior.
|
||||||
question:
|
question:
|
||||||
not_found:
|
not_found:
|
||||||
other: Question not found.
|
other: Pergunta não encontrada.
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
other: No permission to delete.
|
other: Sem permissão para remover.
|
||||||
cannot_close:
|
cannot_close:
|
||||||
other: No permission to close.
|
other: Sem permissão para fechar.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Sem permissão para atualizar.
|
||||||
rank:
|
rank:
|
||||||
fail_to_meet_the_condition:
|
fail_to_meet_the_condition:
|
||||||
other: Rank fail to meet the condition.
|
other: O nível não consegue satisfazer a condição.
|
||||||
report:
|
report:
|
||||||
handle_failed:
|
handle_failed:
|
||||||
other: Report handle failed.
|
other: Falha ao manusear relatório.
|
||||||
not_found:
|
not_found:
|
||||||
other: Report not found.
|
other: Relatório não encontrado.
|
||||||
tag:
|
tag:
|
||||||
not_found:
|
not_found:
|
||||||
other: Tag not found.
|
other: Marcador não encontrado.
|
||||||
recommend_tag_not_found:
|
recommend_tag_not_found:
|
||||||
other: Recommend Tag is not exist.
|
other: O marcador recomendado não existe.
|
||||||
recommend_tag_enter:
|
recommend_tag_enter:
|
||||||
other: Please enter at least one required tag.
|
other: Por favor, insira pelo menos um marcador obrigatório.
|
||||||
not_contain_synonym_tags:
|
not_contain_synonym_tags:
|
||||||
other: Should not contain synonym tags.
|
other: Não deve conter marcadores sinónimos.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Sem permissão para atualizar.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: Não é possível excluir um marcador em uso
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: Você não pode definir o sinônimo do marcador atual como a si mesmo.
|
||||||
smtp:
|
smtp:
|
||||||
config_from_name_cannot_be_email:
|
config_from_name_cannot_be_email:
|
||||||
other: The From Name cannot be a email address.
|
other: Nome do remetente não pode ser um endereço de e-mail.
|
||||||
theme:
|
theme:
|
||||||
not_found:
|
not_found:
|
||||||
other: Theme not found.
|
other: Tema não encontrado.
|
||||||
revision:
|
revision:
|
||||||
review_underway:
|
review_underway:
|
||||||
other: Can't edit currently, there is a version in the review queue.
|
other: Não é possível neste momento, há uma versão na fila de análise.
|
||||||
no_permission:
|
no_permission:
|
||||||
other: No permission to Revision.
|
other: Sem permissão para realizar Revisão.
|
||||||
user:
|
user:
|
||||||
email_or_password_wrong:
|
email_or_password_wrong:
|
||||||
other:
|
other:
|
||||||
other: Email and password do not match.
|
other: O e-mail e a senha não conferem.
|
||||||
not_found:
|
not_found:
|
||||||
other: User not found.
|
other: Usuário não encontrado.
|
||||||
suspended:
|
suspended:
|
||||||
other: User has been suspended.
|
other: O usuário foi suspenso.
|
||||||
username_invalid:
|
username_invalid:
|
||||||
other: Username is invalid.
|
other: Nome de usuário inválido.
|
||||||
username_duplicate:
|
username_duplicate:
|
||||||
other: Username is already in use.
|
other: O nome de usuário já em uso.
|
||||||
set_avatar:
|
set_avatar:
|
||||||
other: Avatar set failed.
|
other: Configuração de avatar falhou.
|
||||||
cannot_update_your_role:
|
cannot_update_your_role:
|
||||||
other: You cannot modify your role.
|
other: Você não pode modificar a sua função.
|
||||||
not_allowed_registration:
|
not_allowed_registration:
|
||||||
other: Currently the site is not open for registration
|
other: O site não está aberto para novos registros
|
||||||
config:
|
config:
|
||||||
read_config_failed:
|
read_config_failed:
|
||||||
other: Read config failed
|
other: Falha ao ler configuração
|
||||||
database:
|
database:
|
||||||
connection_failed:
|
connection_failed:
|
||||||
other: Database connection failed
|
other: Falha ao conectar-se ao banco de dados
|
||||||
create_table_failed:
|
create_table_failed:
|
||||||
other: Create table failed
|
other: Falha ao criar tabela
|
||||||
install:
|
install:
|
||||||
create_config_failed:
|
create_config_failed:
|
||||||
other: Can't create the config.yaml file.
|
other: Não foi possível criar o arquivo de configuração.
|
||||||
upload:
|
upload:
|
||||||
unsupported_file_format:
|
unsupported_file_format:
|
||||||
other: Unsupported file format.
|
other: Formato de arquivo não suportado.
|
||||||
report:
|
report:
|
||||||
spam:
|
spam:
|
||||||
name:
|
name:
|
||||||
other: spam
|
other: spam
|
||||||
desc:
|
desc:
|
||||||
other: This post is an advertisement, or vandalism. It is not useful or relevant to the current topic.
|
other: Essa postagem é um anúncio, ou vandalismo. Não é útil ou relevante para o tópico atual.
|
||||||
rude:
|
rude:
|
||||||
name:
|
name:
|
||||||
other: rude or abusive
|
other: rude ou abusivo
|
||||||
desc:
|
desc:
|
||||||
other: A reasonable person would find this content inappropriate for respectful discourse.
|
other: A reasonable person would find this content inappropriate for respectful discourse.
|
||||||
duplicate:
|
duplicate:
|
||||||
|
@ -262,73 +264,73 @@ ui:
|
||||||
settings: Settings
|
settings: Settings
|
||||||
notifications: Notifications
|
notifications: Notifications
|
||||||
login: Log In
|
login: Log In
|
||||||
sign_up: Sign Up
|
sign_up: Registar-se
|
||||||
account_recovery: Account Recovery
|
account_recovery: Recuperação de conta
|
||||||
account_activation: Account Activation
|
account_activation: Ativação de conta
|
||||||
confirm_email: Confirm Email
|
confirm_email: Confirmar E-mail
|
||||||
account_suspended: Account Suspended
|
account_suspended: Conta suspensa
|
||||||
admin: Admin
|
admin: Administrador
|
||||||
change_email: Modify Email
|
change_email: Modificar e-mail
|
||||||
install: Answer Installation
|
install: Instalação do Answer
|
||||||
upgrade: Answer Upgrade
|
upgrade: Atualização do Answer
|
||||||
maintenance: Website Maintenance
|
maintenance: Manutenção do website
|
||||||
users: Users
|
users: Usuários
|
||||||
notifications:
|
notifications:
|
||||||
title: Notifications
|
title: Notificações
|
||||||
inbox: Inbox
|
inbox: Caixa de entrada
|
||||||
achievement: Achievements
|
achievement: Conquistas
|
||||||
all_read: Mark all as read
|
all_read: Marcar todos como lida
|
||||||
show_more: Show more
|
show_more: Mostrar mais
|
||||||
suspended:
|
suspended:
|
||||||
title: Your Account has been Suspended
|
title: A sua conta foi suspensa
|
||||||
until_time: "Your account was suspended until {{ time }}."
|
until_time: "Sua conta está suspensa até {{ time }}."
|
||||||
forever: This user was suspended forever.
|
forever: Este usuário foi suspenso permanentemente.
|
||||||
end: You don't meet a community guideline.
|
end: Você não atende a uma diretriz da comunidade.
|
||||||
editor:
|
editor:
|
||||||
blockquote:
|
blockquote:
|
||||||
text: Blockquote
|
text: Bloco de citação
|
||||||
bold:
|
bold:
|
||||||
text: Strong
|
text: Negrito
|
||||||
chart:
|
chart:
|
||||||
text: Chart
|
text: Gráfico
|
||||||
flow_chart: Flow chart
|
flow_chart: Gráfico de fluxo
|
||||||
sequence_diagram: Sequence diagram
|
sequence_diagram: Diagrama de sequência
|
||||||
class_diagram: Class diagram
|
class_diagram: Diagrama de classe
|
||||||
state_diagram: State diagram
|
state_diagram: Diagrama de estado
|
||||||
entity_relationship_diagram: Entity relationship diagram
|
entity_relationship_diagram: Diagrama de relacionamento de entidade
|
||||||
user_defined_diagram: User defined diagram
|
user_defined_diagram: Diagrama definido pelo usuário
|
||||||
gantt_chart: Gantt chart
|
gantt_chart: Gráfico de Gantt
|
||||||
pie_chart: Pie chart
|
pie_chart: Gráfico de pizza
|
||||||
code:
|
code:
|
||||||
text: Code Sample
|
text: Exemplo de código
|
||||||
add_code: Add code sample
|
add_code: Adicionar exemplo de código
|
||||||
form:
|
form:
|
||||||
fields:
|
fields:
|
||||||
code:
|
code:
|
||||||
label: Code
|
label: Código
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Código não pode ser vazio.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Idioma
|
||||||
placeholder: Automatic detection
|
placeholder: Deteção automática
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancelar
|
||||||
btn_confirm: Add
|
btn_confirm: Adicionar
|
||||||
formula:
|
formula:
|
||||||
text: Formula
|
text: Fórmula
|
||||||
options:
|
options:
|
||||||
inline: Inline formula
|
inline: Fórmula na linha
|
||||||
block: Block formula
|
block: Bloco de fórmula
|
||||||
heading:
|
heading:
|
||||||
text: Heading
|
text: Cabeçalho
|
||||||
options:
|
options:
|
||||||
h1: Heading 1
|
h1: Cabeçalho 1
|
||||||
h2: Heading 2
|
h2: Cabeçalho 2
|
||||||
h3: Heading 3
|
h3: Cabeçalho 3
|
||||||
h4: Heading 4
|
h4: Cabeçalho 4
|
||||||
h5: Heading 5
|
h5: Cabeçalho 5
|
||||||
h6: Heading 6
|
h6: Cabeçalho 6
|
||||||
help:
|
help:
|
||||||
text: Help
|
text: Ajuda
|
||||||
hr:
|
hr:
|
||||||
text: Horizontal Rule
|
text: Horizontal Rule
|
||||||
image:
|
image:
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Не должно содержать теги синонимы.
|
other: Не должно содержать теги синонимы.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: Нет прав для обновления.
|
other: Нет прав для обновления.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: Вы не можете установить синоним текущего тега.
|
other: Вы не можете установить синоним текущего тега.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Код не может быть пустым.
|
empty: Код не может быть пустым.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug до 35 символов.
|
range: URL slug до 35 символов.
|
||||||
character: URL slug содержит недопустимый набор символов.
|
character: URL slug содержит недопустимый набор символов.
|
||||||
desc:
|
desc:
|
||||||
label: Описание (опционально)
|
label: Description
|
||||||
btn_cancel: Отмена
|
btn_cancel: Отмена
|
||||||
btn_submit: Отправить
|
btn_submit: Отправить
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: Следующие теги будут переназначены на
|
synonyms_text: Следующие теги будут переназначены на
|
||||||
delete:
|
delete:
|
||||||
title: Удалить этот тег
|
title: Удалить этот тег
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>Мы не разрешаем удалять тег с сообщениями.</p><p>Пожалуйста, сначала удалите этот тег из сообщений</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Вы действительно хотите удалить?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Закрыть
|
close: Закрыть
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Изменить тег
|
title: Изменить тег
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
307
i18n/tr_TR.yaml
307
i18n/tr_TR.yaml
|
@ -2,23 +2,23 @@
|
||||||
backend:
|
backend:
|
||||||
base:
|
base:
|
||||||
success:
|
success:
|
||||||
other: Success.
|
other: Başarılı.
|
||||||
unknown:
|
unknown:
|
||||||
other: Unknown error.
|
other: Bilinmeyen hata.
|
||||||
request_format_error:
|
request_format_error:
|
||||||
other: Request format is not valid.
|
other: Request format is not valid.
|
||||||
unauthorized_error:
|
unauthorized_error:
|
||||||
other: Unauthorized.
|
other: İzin yok.
|
||||||
database_error:
|
database_error:
|
||||||
other: Data server error.
|
other: Veri sunucu hatası.
|
||||||
role:
|
role:
|
||||||
name:
|
name:
|
||||||
user:
|
user:
|
||||||
other: User
|
other: Kullanıcı
|
||||||
admin:
|
admin:
|
||||||
other: Admin
|
other: Yönetici
|
||||||
moderator:
|
moderator:
|
||||||
other: Moderator
|
other: Moderatör
|
||||||
description:
|
description:
|
||||||
user:
|
user:
|
||||||
other: Default with no special access.
|
other: Default with no special access.
|
||||||
|
@ -27,131 +27,133 @@ backend:
|
||||||
moderator:
|
moderator:
|
||||||
other: Has access to all posts except admin settings.
|
other: Has access to all posts except admin settings.
|
||||||
email:
|
email:
|
||||||
other: Email
|
other: E-Posta
|
||||||
password:
|
password:
|
||||||
other: Password
|
other: Parola
|
||||||
email_or_password_wrong_error:
|
email_or_password_wrong_error:
|
||||||
other: Email and password do not match.
|
other: E-Posta ve parola eşleşmiyor.
|
||||||
error:
|
error:
|
||||||
admin:
|
admin:
|
||||||
email_or_password_wrong:
|
email_or_password_wrong:
|
||||||
other: Email and password do not match.
|
other: E-Posta ve parola eşleşmiyor.
|
||||||
answer:
|
answer:
|
||||||
not_found:
|
not_found:
|
||||||
other: Answer do not found.
|
other: Cevap bulunamadı.
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
other: No permission to delete.
|
other: Silme izni yok.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Düzenleme izni yok.
|
||||||
comment:
|
comment:
|
||||||
edit_without_permission:
|
edit_without_permission:
|
||||||
other: Comment are not allowed to edit.
|
other: Yorum düzenleme izni yok.
|
||||||
not_found:
|
not_found:
|
||||||
other: Comment not found.
|
other: Yorum bulunamadı.
|
||||||
cannot_edit_after_deadline:
|
cannot_edit_after_deadline:
|
||||||
other: The comment time has been too long to modify.
|
other: The comment time has been too long to modify.
|
||||||
email:
|
email:
|
||||||
duplicate:
|
duplicate:
|
||||||
other: Email already exists.
|
other: Bu e-posta adresi kullanılmaktadır.
|
||||||
need_to_be_verified:
|
need_to_be_verified:
|
||||||
other: Email should be verified.
|
other: E-Posta doğrulanmalı.
|
||||||
verify_url_expired:
|
verify_url_expired:
|
||||||
other: Email verified URL has expired, please resend the email.
|
other: Email verified URL has expired, please resend the email.
|
||||||
lang:
|
lang:
|
||||||
not_found:
|
not_found:
|
||||||
other: Language file not found.
|
other: Dil dosyası bulunamadı.
|
||||||
object:
|
object:
|
||||||
captcha_verification_failed:
|
captcha_verification_failed:
|
||||||
other: Captcha wrong.
|
other: Captcha yanlış.
|
||||||
disallow_follow:
|
disallow_follow:
|
||||||
other: You are not allowed to follow.
|
other: Takip etme izniniz yok.
|
||||||
disallow_vote:
|
disallow_vote:
|
||||||
other: You are not allowed to vote.
|
other: Oy verme yetkiniz yok.
|
||||||
disallow_vote_your_self:
|
disallow_vote_your_self:
|
||||||
other: You can't vote for your own post.
|
other: Kendi mesajınız için oy kullanamazsınız.
|
||||||
not_found:
|
not_found:
|
||||||
other: Object not found.
|
other: Nesne bulunamadı.
|
||||||
verification_failed:
|
verification_failed:
|
||||||
other: Verification failed.
|
other: Doğrulama başarısız.
|
||||||
email_or_password_incorrect:
|
email_or_password_incorrect:
|
||||||
other: Email and password do not match.
|
other: E-Posta ve parola eşleşmiyor.
|
||||||
old_password_verification_failed:
|
old_password_verification_failed:
|
||||||
other: The old password verification failed
|
other: Eski parola doğrulaması başarısız
|
||||||
new_password_same_as_previous_setting:
|
new_password_same_as_previous_setting:
|
||||||
other: The new password is the same as the previous one.
|
other: Yeni parola bir önceki parolanızın aynısı.
|
||||||
question:
|
question:
|
||||||
not_found:
|
not_found:
|
||||||
other: Question not found.
|
other: Soru bulunamadı.
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
other: No permission to delete.
|
other: Silme izni yok.
|
||||||
cannot_close:
|
cannot_close:
|
||||||
other: No permission to close.
|
other: Kapatma izni yok.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Düzenleme izni yok.
|
||||||
rank:
|
rank:
|
||||||
fail_to_meet_the_condition:
|
fail_to_meet_the_condition:
|
||||||
other: Rank fail to meet the condition.
|
other: Rank fail to meet the condition.
|
||||||
report:
|
report:
|
||||||
handle_failed:
|
handle_failed:
|
||||||
other: Report handle failed.
|
other: Rapor işlenemedi.
|
||||||
not_found:
|
not_found:
|
||||||
other: Report not found.
|
other: Rapor bulunamadı.
|
||||||
tag:
|
tag:
|
||||||
not_found:
|
not_found:
|
||||||
other: Tag not found.
|
other: Etiket bulunamadı.
|
||||||
recommend_tag_not_found:
|
recommend_tag_not_found:
|
||||||
other: Recommend Tag is not exist.
|
other: Önerilen Etiket yok.
|
||||||
recommend_tag_enter:
|
recommend_tag_enter:
|
||||||
other: Please enter at least one required tag.
|
other: Lütfen en az bir adet gerekli etiket giriniz.
|
||||||
not_contain_synonym_tags:
|
not_contain_synonym_tags:
|
||||||
other: Should not contain synonym tags.
|
other: Should not contain synonym tags.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: Düzenleme izni yok.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
config_from_name_cannot_be_email:
|
config_from_name_cannot_be_email:
|
||||||
other: The From Name cannot be a email address.
|
other: Gönderen adı bir e-posta adresi olamaz.
|
||||||
theme:
|
theme:
|
||||||
not_found:
|
not_found:
|
||||||
other: Theme not found.
|
other: Tema bulunamadı.
|
||||||
revision:
|
revision:
|
||||||
review_underway:
|
review_underway:
|
||||||
other: Can't edit currently, there is a version in the review queue.
|
other: Can't edit currently, there is a version in the review queue.
|
||||||
no_permission:
|
no_permission:
|
||||||
other: No permission to Revision.
|
other: Revizyon izni yok.
|
||||||
user:
|
user:
|
||||||
email_or_password_wrong:
|
email_or_password_wrong:
|
||||||
other:
|
other:
|
||||||
other: Email and password do not match.
|
other: E-Posta ve parola eşleşmiyor.
|
||||||
not_found:
|
not_found:
|
||||||
other: User not found.
|
other: Kullanıcı bulunamadı.
|
||||||
suspended:
|
suspended:
|
||||||
other: User has been suspended.
|
other: Kullanıcı askıya alındı.
|
||||||
username_invalid:
|
username_invalid:
|
||||||
other: Username is invalid.
|
other: Kullanıcı adı geçersiz.
|
||||||
username_duplicate:
|
username_duplicate:
|
||||||
other: Username is already in use.
|
other: Kullanıcı adı zaten kullanımda.
|
||||||
set_avatar:
|
set_avatar:
|
||||||
other: Avatar set failed.
|
other: Avatar ayarlama başarısız.
|
||||||
cannot_update_your_role:
|
cannot_update_your_role:
|
||||||
other: You cannot modify your role.
|
other: Kendi rolünüzü değiştiremezsiniz.
|
||||||
not_allowed_registration:
|
not_allowed_registration:
|
||||||
other: Currently the site is not open for registration
|
other: Site şu anda kayıt olmaya açık değil
|
||||||
config:
|
config:
|
||||||
read_config_failed:
|
read_config_failed:
|
||||||
other: Read config failed
|
other: Read config failed
|
||||||
database:
|
database:
|
||||||
connection_failed:
|
connection_failed:
|
||||||
other: Database connection failed
|
other: Veri tabanı bağlantısı başarısız
|
||||||
create_table_failed:
|
create_table_failed:
|
||||||
other: Create table failed
|
other: Tablo oluşturulamadı
|
||||||
install:
|
install:
|
||||||
create_config_failed:
|
create_config_failed:
|
||||||
other: Can't create the config.yaml file.
|
other: Config.yaml dosyası oluşturulamadı.
|
||||||
upload:
|
upload:
|
||||||
unsupported_file_format:
|
unsupported_file_format:
|
||||||
other: Unsupported file format.
|
other: Desteklenmeyen dosya formatı.
|
||||||
report:
|
report:
|
||||||
spam:
|
spam:
|
||||||
name:
|
name:
|
||||||
|
@ -170,7 +172,7 @@ backend:
|
||||||
other: This question has been asked before and already has an answer.
|
other: This question has been asked before and already has an answer.
|
||||||
not_answer:
|
not_answer:
|
||||||
name:
|
name:
|
||||||
other: not an answer
|
other: cevap değil
|
||||||
desc:
|
desc:
|
||||||
other: This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether.
|
other: This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether.
|
||||||
not_need:
|
not_need:
|
||||||
|
@ -229,15 +231,15 @@ backend:
|
||||||
reply_to_you:
|
reply_to_you:
|
||||||
other: replied to you
|
other: replied to you
|
||||||
mention_you:
|
mention_you:
|
||||||
other: mentioned you
|
other: sizden bahsetti
|
||||||
your_question_is_closed:
|
your_question_is_closed:
|
||||||
other: Your question has been closed
|
other: Sorunuz kapatıldı
|
||||||
your_question_was_deleted:
|
your_question_was_deleted:
|
||||||
other: Your question has been deleted
|
other: Sorunuz silindi
|
||||||
your_answer_was_deleted:
|
your_answer_was_deleted:
|
||||||
other: Your answer has been deleted
|
other: Cevabınız silindi
|
||||||
your_comment_was_deleted:
|
your_comment_was_deleted:
|
||||||
other: Your comment has been deleted
|
other: Yorumunuz silindi
|
||||||
#The following fields are used for interface presentation(Front-end)
|
#The following fields are used for interface presentation(Front-end)
|
||||||
ui:
|
ui:
|
||||||
how_to_format:
|
how_to_format:
|
||||||
|
@ -245,53 +247,53 @@ ui:
|
||||||
desc: >-
|
desc: >-
|
||||||
<ul class="mb-0"><li><p class="mb-2">to make links</p><pre class="mb-2"><code><https://url.com><br/><br/>[Title](https://url.com)</code></pre></li><li><p class="mb-2">put returns between paragraphs</p></li><li><p class="mb-2"><em>_italic_</em> or **<strong>bold</strong>**</p></li><li><p class="mb-2">indent code by 4 spaces</p></li><li><p class="mb-2">quote by placing <code>></code> at start of line</p></li><li><p class="mb-2">backtick escapes <code>`like _this_`</code></p></li><li><p class="mb-2">create code fences with backticks <code>`</code></p><pre class="mb-0"><code>```<br/>code here<br/>```</code></pre></li></ul>
|
<ul class="mb-0"><li><p class="mb-2">to make links</p><pre class="mb-2"><code><https://url.com><br/><br/>[Title](https://url.com)</code></pre></li><li><p class="mb-2">put returns between paragraphs</p></li><li><p class="mb-2"><em>_italic_</em> or **<strong>bold</strong>**</p></li><li><p class="mb-2">indent code by 4 spaces</p></li><li><p class="mb-2">quote by placing <code>></code> at start of line</p></li><li><p class="mb-2">backtick escapes <code>`like _this_`</code></p></li><li><p class="mb-2">create code fences with backticks <code>`</code></p><pre class="mb-0"><code>```<br/>code here<br/>```</code></pre></li></ul>
|
||||||
pagination:
|
pagination:
|
||||||
prev: Prev
|
prev: Önceki
|
||||||
next: Next
|
next: Sonraki
|
||||||
page_title:
|
page_title:
|
||||||
question: Question
|
question: Soru
|
||||||
questions: Questions
|
questions: Sorular
|
||||||
tag: Tag
|
tag: Etiket
|
||||||
tags: Tags
|
tags: Etiketler
|
||||||
tag_wiki: tag wiki
|
tag_wiki: tag wiki
|
||||||
edit_tag: Edit Tag
|
edit_tag: Etiketi Düzenle
|
||||||
ask_a_question: Add Question
|
ask_a_question: Soru Ekle
|
||||||
edit_question: Edit Question
|
edit_question: Soruyu Düzenle
|
||||||
edit_answer: Edit Answer
|
edit_answer: Cevabı Düzenle
|
||||||
search: Search
|
search: Ara
|
||||||
posts_containing: Posts containing
|
posts_containing: Posts containing
|
||||||
settings: Settings
|
settings: Ayarlar
|
||||||
notifications: Notifications
|
notifications: Bildirimler
|
||||||
login: Log In
|
login: Oturum Aç
|
||||||
sign_up: Sign Up
|
sign_up: Üye Ol
|
||||||
account_recovery: Account Recovery
|
account_recovery: Hesap Kurtarma
|
||||||
account_activation: Account Activation
|
account_activation: Hesap Aktivasyonu
|
||||||
confirm_email: Confirm Email
|
confirm_email: E-Posta Adresinizi Onaylayın
|
||||||
account_suspended: Account Suspended
|
account_suspended: Hesap Askıya Alındı
|
||||||
admin: Admin
|
admin: Yönetici
|
||||||
change_email: Modify Email
|
change_email: E-Postayı değiştir
|
||||||
install: Answer Installation
|
install: Answer Installation
|
||||||
upgrade: Answer Upgrade
|
upgrade: Answer Upgrade
|
||||||
maintenance: Website Maintenance
|
maintenance: Website Bakımı
|
||||||
users: Users
|
users: Kullanıcı
|
||||||
notifications:
|
notifications:
|
||||||
title: Notifications
|
title: Bildirimler
|
||||||
inbox: Inbox
|
inbox: Gelen Kutusu
|
||||||
achievement: Achievements
|
achievement: Başarılar
|
||||||
all_read: Mark all as read
|
all_read: Tümünü okundu olarak işaretle
|
||||||
show_more: Show more
|
show_more: Daha fazla göster
|
||||||
suspended:
|
suspended:
|
||||||
title: Your Account has been Suspended
|
title: Hesabınız Askıya Alındı
|
||||||
until_time: "Your account was suspended until {{ time }}."
|
until_time: "Your account was suspended until {{ time }}."
|
||||||
forever: This user was suspended forever.
|
forever: This user was suspended forever.
|
||||||
end: You don't meet a community guideline.
|
end: You don't meet a community guideline.
|
||||||
editor:
|
editor:
|
||||||
blockquote:
|
blockquote:
|
||||||
text: Blockquote
|
text: Alıntı
|
||||||
bold:
|
bold:
|
||||||
text: Strong
|
text: Strong
|
||||||
chart:
|
chart:
|
||||||
text: Chart
|
text: Chart
|
||||||
flow_chart: Flow chart
|
flow_chart: Akış Şeması
|
||||||
sequence_diagram: Sequence diagram
|
sequence_diagram: Sequence diagram
|
||||||
class_diagram: Class diagram
|
class_diagram: Class diagram
|
||||||
state_diagram: State diagram
|
state_diagram: State diagram
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,10 +356,10 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: İptal
|
||||||
btn_confirm: Add
|
btn_confirm: Ekle
|
||||||
uploading: Uploading
|
uploading: Gönderiliyor
|
||||||
indent:
|
indent:
|
||||||
text: Indent
|
text: Indent
|
||||||
outdent:
|
outdent:
|
||||||
|
@ -365,54 +367,54 @@ ui:
|
||||||
italic:
|
italic:
|
||||||
text: Emphasis
|
text: Emphasis
|
||||||
link:
|
link:
|
||||||
text: Hyperlink
|
text: Bağlantı
|
||||||
add_link: Add hyperlink
|
add_link: Bağlantı ekle
|
||||||
form:
|
form:
|
||||||
fields:
|
fields:
|
||||||
url:
|
url:
|
||||||
label: URL
|
label: URL
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL boş olamaz.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: İptal Et
|
||||||
btn_confirm: Add
|
btn_confirm: Ekle
|
||||||
ordered_list:
|
ordered_list:
|
||||||
text: Numbered List
|
text: Numaralı liste
|
||||||
unordered_list:
|
unordered_list:
|
||||||
text: Bulleted List
|
text: Bulleted List
|
||||||
table:
|
table:
|
||||||
text: Table
|
text: Tablo
|
||||||
heading: Heading
|
heading: Başlık
|
||||||
cell: Cell
|
cell: Hücre
|
||||||
close_modal:
|
close_modal:
|
||||||
title: I am closing this post as...
|
title: I am closing this post as...
|
||||||
btn_cancel: Cancel
|
btn_cancel: İptal Et
|
||||||
btn_submit: Submit
|
btn_submit: Gönder
|
||||||
remark:
|
remark:
|
||||||
empty: Cannot be empty.
|
empty: Boş olamaz.
|
||||||
msg:
|
msg:
|
||||||
empty: Please select a reason.
|
empty: Lütfen bir sebep seçin.
|
||||||
report_modal:
|
report_modal:
|
||||||
flag_title: I am flagging to report this post as...
|
flag_title: I am flagging to report this post as...
|
||||||
close_title: I am closing this post as...
|
close_title: I am closing this post as...
|
||||||
review_question_title: Review question
|
review_question_title: Review question
|
||||||
review_answer_title: Review answer
|
review_answer_title: Review answer
|
||||||
review_comment_title: Review comment
|
review_comment_title: Review comment
|
||||||
btn_cancel: Cancel
|
btn_cancel: İptal Et
|
||||||
btn_submit: Submit
|
btn_submit: Gönder
|
||||||
remark:
|
remark:
|
||||||
empty: Cannot be empty.
|
empty: Boş olamaz.
|
||||||
msg:
|
msg:
|
||||||
empty: Please select a reason.
|
empty: Lütfen bir sebep seçin.
|
||||||
tag_modal:
|
tag_modal:
|
||||||
title: Create new tag
|
title: Yeni etiket oluştur
|
||||||
form:
|
form:
|
||||||
fields:
|
fields:
|
||||||
display_name:
|
display_name:
|
||||||
label: Display Name
|
label: Görüntülenen Ad
|
||||||
msg:
|
msg:
|
||||||
empty: Display name cannot be empty.
|
empty: Görünen ad boş olamaz.
|
||||||
range: Display name up to 35 characters.
|
range: Display name up to 35 characters.
|
||||||
slug_name:
|
slug_name:
|
||||||
label: URL Slug
|
label: URL Slug
|
||||||
|
@ -422,15 +424,15 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: İptal Et
|
||||||
btn_submit: Submit
|
btn_submit: Gönder
|
||||||
tag_info:
|
tag_info:
|
||||||
created_at: Created
|
created_at: Oluşturuldu
|
||||||
edited_at: Edited
|
edited_at: Düzenlendi
|
||||||
history: History
|
history: Geçmiş
|
||||||
synonyms:
|
synonyms:
|
||||||
title: Synonyms
|
title: Eş anlamlılar
|
||||||
text: The following tags will be remapped to
|
text: The following tags will be remapped to
|
||||||
empty: No synonyms found.
|
empty: No synonyms found.
|
||||||
btn_add: Add a synonym
|
btn_add: Add a synonym
|
||||||
|
@ -438,30 +440,32 @@ ui:
|
||||||
btn_save: Save
|
btn_save: Save
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Bu etiketi sil
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
close: Close
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
|
close: Kapat
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Etiketi Düzenle
|
||||||
default_reason: Edit tag
|
default_reason: Etiketi düzenle
|
||||||
form:
|
form:
|
||||||
fields:
|
fields:
|
||||||
revision:
|
revision:
|
||||||
label: Revision
|
label: Revizyon
|
||||||
display_name:
|
display_name:
|
||||||
label: Display Name
|
label: Görüntülenen Ad
|
||||||
slug_name:
|
slug_name:
|
||||||
label: URL Slug
|
label: URL Slug
|
||||||
info: 'Must use the character set "a-z", "0-9", "+ # - ."'
|
info: 'Must use the character set "a-z", "0-9", "+ # - ."'
|
||||||
desc:
|
desc:
|
||||||
label: Description
|
label: Açıklama
|
||||||
edit_summary:
|
edit_summary:
|
||||||
label: Edit Summary
|
label: Özeti Düzenle
|
||||||
placeholder: >-
|
placeholder: >-
|
||||||
Briefly explain your changes (corrected spelling, fixed grammar, improved formatting)
|
Briefly explain your changes (corrected spelling, fixed grammar, improved formatting)
|
||||||
btn_save_edits: Save edits
|
btn_save_edits: Düzenlemeleri kaydet
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
dates:
|
dates:
|
||||||
long_date: MMM D
|
long_date: MMM D
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ backend:
|
||||||
other: Should not contain synonym tags.
|
other: Should not contain synonym tags.
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: No permission to update.
|
other: No permission to update.
|
||||||
|
is_used_cannot_delete:
|
||||||
|
other: You cannot delete a tag that is in use
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: You cannot set the synonym of the current tag as itself.
|
other: You cannot set the synonym of the current tag as itself.
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -309,7 +311,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Code cannot be empty.
|
empty: Code cannot be empty.
|
||||||
language:
|
language:
|
||||||
label: Language (optional)
|
label: Language
|
||||||
placeholder: Automatic detection
|
placeholder: Automatic detection
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
|
@ -345,7 +347,7 @@ ui:
|
||||||
only_image: Only image files are allowed.
|
only_image: Only image files are allowed.
|
||||||
max_size: File size cannot exceed 4MB.
|
max_size: File size cannot exceed 4MB.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
tab_url: Image URL
|
tab_url: Image URL
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -354,7 +356,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: Image URL cannot be empty.
|
empty: Image URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
uploading: Uploading
|
uploading: Uploading
|
||||||
|
@ -374,7 +376,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: URL cannot be empty.
|
empty: URL cannot be empty.
|
||||||
name:
|
name:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_confirm: Add
|
btn_confirm: Add
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -422,7 +424,7 @@ ui:
|
||||||
range: URL slug up to 35 characters.
|
range: URL slug up to 35 characters.
|
||||||
character: URL slug contains unallowed character set.
|
character: URL slug contains unallowed character set.
|
||||||
desc:
|
desc:
|
||||||
label: Description (optional)
|
label: Description
|
||||||
btn_cancel: Cancel
|
btn_cancel: Cancel
|
||||||
btn_submit: Submit
|
btn_submit: Submit
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -439,9 +441,11 @@ ui:
|
||||||
synonyms_text: The following tags will be remapped to
|
synonyms_text: The following tags will be remapped to
|
||||||
delete:
|
delete:
|
||||||
title: Delete this tag
|
title: Delete this tag
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>We do not allow deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
<p>We do not allowed <strong>deleting tag with posts</strong>.</p> <p>Please remove this tag from the posts first.</p>
|
||||||
content2: Are you sure you wish to delete?
|
tip_with_synonyms: >-
|
||||||
|
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p> <p>Please remove the synonyms from this tag first.</p>
|
||||||
|
tip: Are you sure you wish to delete?
|
||||||
close: Close
|
close: Close
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: Edit Tag
|
title: Edit Tag
|
||||||
|
@ -677,13 +681,13 @@ ui:
|
||||||
default: System
|
default: System
|
||||||
msg: Please upload an avatar
|
msg: Please upload an avatar
|
||||||
bio:
|
bio:
|
||||||
label: About Me (optional)
|
label: About Me
|
||||||
website:
|
website:
|
||||||
label: Website (optional)
|
label: Website
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: Website incorrect format
|
msg: Website incorrect format
|
||||||
location:
|
location:
|
||||||
label: Location (optional)
|
label: Location
|
||||||
placeholder: "City, Country"
|
placeholder: "City, Country"
|
||||||
notification:
|
notification:
|
||||||
heading: Notifications
|
heading: Notifications
|
||||||
|
@ -1163,11 +1167,11 @@ ui:
|
||||||
validate: Please enter a valid URL.
|
validate: Please enter a valid URL.
|
||||||
text: The address of your site.
|
text: The address of your site.
|
||||||
short_desc:
|
short_desc:
|
||||||
label: Short Site Description (optional)
|
label: Short Site Description
|
||||||
msg: Short site description cannot be empty.
|
msg: Short site description cannot be empty.
|
||||||
text: "Short description, as used in the title tag on homepage."
|
text: "Short description, as used in the title tag on homepage."
|
||||||
desc:
|
desc:
|
||||||
label: Site Description (optional)
|
label: Site Description
|
||||||
msg: Site description cannot be empty.
|
msg: Site description cannot be empty.
|
||||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1177,14 +1181,6 @@ ui:
|
||||||
text: Email address of key contact responsible for this site.
|
text: Email address of key contact responsible for this site.
|
||||||
interface:
|
interface:
|
||||||
page_title: Interface
|
page_title: Interface
|
||||||
logo:
|
|
||||||
label: Logo (optional)
|
|
||||||
msg: Site logo cannot be empty.
|
|
||||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
|
||||||
theme:
|
|
||||||
label: Theme
|
|
||||||
msg: Theme cannot be empty.
|
|
||||||
text: Select an existing theme.
|
|
||||||
language:
|
language:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
msg: Interface language cannot be empty.
|
msg: Interface language cannot be empty.
|
||||||
|
@ -1236,18 +1232,18 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: Branding
|
page_title: Branding
|
||||||
logo:
|
logo:
|
||||||
label: Logo (optional)
|
label: Logo
|
||||||
msg: Logo cannot be empty.
|
msg: Logo cannot be empty.
|
||||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: Mobile Logo (optional)
|
label: Mobile Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||||
square_icon:
|
square_icon:
|
||||||
label: Square Icon (optional)
|
label: Square Icon
|
||||||
msg: Square icon cannot be empty.
|
msg: Square icon cannot be empty.
|
||||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||||
favicon:
|
favicon:
|
||||||
label: Favicon (optional)
|
label: Favicon
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||||
legal:
|
legal:
|
||||||
page_title: Legal
|
page_title: Legal
|
||||||
|
@ -1313,6 +1309,7 @@ ui:
|
||||||
label: Login required
|
label: Login required
|
||||||
text: Only logged in users can access this community.
|
text: Only logged in users can access this community.
|
||||||
form:
|
form:
|
||||||
|
optional: (optional)
|
||||||
empty: cannot be empty
|
empty: cannot be empty
|
||||||
invalid: is invalid
|
invalid: is invalid
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
|
@ -1362,5 +1359,7 @@ ui:
|
||||||
staffs: Our community staff
|
staffs: Our community staff
|
||||||
reputation: reputation
|
reputation: reputation
|
||||||
votes: votes
|
votes: votes
|
||||||
|
prompt:
|
||||||
|
leave_page: "Are you sure you want to leave the page?"
|
||||||
|
changes_not_save: "Your changes may not be saved."
|
||||||
|
|
||||||
|
|
102
i18n/zh_CN.yaml
102
i18n/zh_CN.yaml
|
@ -1,4 +1,5 @@
|
||||||
#The following fields are used for back-end
|
#The following fields are used for back-end
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
base:
|
base:
|
||||||
success:
|
success:
|
||||||
|
@ -49,7 +50,7 @@ backend:
|
||||||
not_found:
|
not_found:
|
||||||
other: 评论未找到。
|
other: 评论未找到。
|
||||||
cannot_edit_after_deadline:
|
cannot_edit_after_deadline:
|
||||||
other: The comment time has been too long to modify.
|
other: 评论时间太长,无法修改。
|
||||||
email:
|
email:
|
||||||
duplicate:
|
duplicate:
|
||||||
other: 邮箱已经存在。
|
other: 邮箱已经存在。
|
||||||
|
@ -80,6 +81,8 @@ backend:
|
||||||
new_password_same_as_previous_setting:
|
new_password_same_as_previous_setting:
|
||||||
other: 新密码与之前的设置相同
|
other: 新密码与之前的设置相同
|
||||||
question:
|
question:
|
||||||
|
already_deleted:
|
||||||
|
other: 该内容已被删除
|
||||||
not_found:
|
not_found:
|
||||||
other: 问题未找到
|
other: 问题未找到
|
||||||
cannot_deleted:
|
cannot_deleted:
|
||||||
|
@ -108,7 +111,7 @@ backend:
|
||||||
cannot_update:
|
cannot_update:
|
||||||
other: 没有更新标签权限。
|
other: 没有更新标签权限。
|
||||||
is_used_cannot_delete:
|
is_used_cannot_delete:
|
||||||
other: 您不能删除正在被使用的标签
|
other: 您不能删除正在使用的标签
|
||||||
cannot_set_synonym_as_itself:
|
cannot_set_synonym_as_itself:
|
||||||
other: 您不能将当前标签的同义词设置为本身。
|
other: 您不能将当前标签的同义词设置为本身。
|
||||||
smtp:
|
smtp:
|
||||||
|
@ -150,10 +153,10 @@ backend:
|
||||||
other: 创建表失败
|
other: 创建表失败
|
||||||
install:
|
install:
|
||||||
create_config_failed:
|
create_config_failed:
|
||||||
other: Can't create the config.yaml file.
|
other: 无法创建 config.yaml 文件。
|
||||||
upload:
|
upload:
|
||||||
unsupported_file_format:
|
unsupported_file_format:
|
||||||
other: Unsupported file format.
|
other: 不支持的文件格式。
|
||||||
report:
|
report:
|
||||||
spam:
|
spam:
|
||||||
name:
|
name:
|
||||||
|
@ -311,7 +314,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: 代码块不能为空
|
empty: 代码块不能为空
|
||||||
language:
|
language:
|
||||||
label: 语言 (可选)
|
label: 语言
|
||||||
placeholder: 自动识别
|
placeholder: 自动识别
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_confirm: 添加
|
btn_confirm: 添加
|
||||||
|
@ -347,7 +350,7 @@ ui:
|
||||||
only_image: 只能上传图片文件。
|
only_image: 只能上传图片文件。
|
||||||
max_size: 图片文件大小不能超过 4 MB。
|
max_size: 图片文件大小不能超过 4 MB。
|
||||||
desc:
|
desc:
|
||||||
label: 描述(可选)
|
label: 描述
|
||||||
tab_url: 网络图片
|
tab_url: 网络图片
|
||||||
form_url:
|
form_url:
|
||||||
fields:
|
fields:
|
||||||
|
@ -356,7 +359,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: 图片地址不能为空
|
empty: 图片地址不能为空
|
||||||
name:
|
name:
|
||||||
label: 图片描述(可选)
|
label: 描述
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_confirm: 添加
|
btn_confirm: 添加
|
||||||
uploading: 上传中...
|
uploading: 上传中...
|
||||||
|
@ -376,7 +379,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: 链接不能为空。
|
empty: 链接不能为空。
|
||||||
name:
|
name:
|
||||||
label: 链接描述(可选)
|
label: 描述
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_confirm: 添加
|
btn_confirm: 添加
|
||||||
ordered_list:
|
ordered_list:
|
||||||
|
@ -424,7 +427,7 @@ ui:
|
||||||
range: URL 固定链接不能超过 35 个字符。
|
range: URL 固定链接不能超过 35 个字符。
|
||||||
character: URL 固定链接包含非法字符。
|
character: URL 固定链接包含非法字符。
|
||||||
desc:
|
desc:
|
||||||
label: 描述(可选)
|
label: 描述
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_submit: 提交
|
btn_submit: 提交
|
||||||
tag_info:
|
tag_info:
|
||||||
|
@ -441,9 +444,11 @@ ui:
|
||||||
synonyms_text: 以下标签等同于
|
synonyms_text: 以下标签等同于
|
||||||
delete:
|
delete:
|
||||||
title: 删除标签
|
title: 删除标签
|
||||||
content: >-
|
tip_with_posts: >-
|
||||||
<p>不允许删除有关联问题的标签。</p><p>请先从关联的问题中删除此标签的引用。</p>
|
<p>我们不允许 <strong>删除带有同义词的标签</strong>。</p> <p>请先从此标签中删除同义词。</p>
|
||||||
content2: 确定要删除吗?
|
tip_with_synonyms: >-
|
||||||
|
<p>我们不允许 <strong>删除带有同义词的标签</strong>。</p> <p>请先从此标签中删除同义词。</p>
|
||||||
|
tip: 确定要删除吗?
|
||||||
close: 关闭
|
close: 关闭
|
||||||
edit_tag:
|
edit_tag:
|
||||||
title: 编辑标签
|
title: 编辑标签
|
||||||
|
@ -484,7 +489,7 @@ ui:
|
||||||
btn_flag: 举报
|
btn_flag: 举报
|
||||||
btn_save_edits: 保存
|
btn_save_edits: 保存
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
show_more: Show more comments
|
show_more: 显示更多评论
|
||||||
tip_question: >-
|
tip_question: >-
|
||||||
使用评论提问更多信息或者提出改进意见。尽量避免使用评论功能回答问题。
|
使用评论提问更多信息或者提出改进意见。尽量避免使用评论功能回答问题。
|
||||||
tip_answer: >-
|
tip_answer: >-
|
||||||
|
@ -499,7 +504,7 @@ ui:
|
||||||
answer:
|
answer:
|
||||||
label: 回答内容
|
label: 回答内容
|
||||||
feedback:
|
feedback:
|
||||||
characters: content must be at least 6 characters in length.
|
characters: 内容长度至少 6 个字符
|
||||||
edit_summary:
|
edit_summary:
|
||||||
label: 编辑概要
|
label: 编辑概要
|
||||||
placeholder: >-
|
placeholder: >-
|
||||||
|
@ -624,7 +629,7 @@ ui:
|
||||||
msg:
|
msg:
|
||||||
empty: 邮箱不能为空
|
empty: 邮箱不能为空
|
||||||
change_email:
|
change_email:
|
||||||
page_title: Welcome to {{site_name}}
|
page_title: 欢迎来到 {{site_name}}
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_update: 更新电子邮件地址
|
btn_update: 更新电子邮件地址
|
||||||
send_success: >-
|
send_success: >-
|
||||||
|
@ -662,12 +667,12 @@ ui:
|
||||||
display_name:
|
display_name:
|
||||||
label: 昵称
|
label: 昵称
|
||||||
msg: 昵称不能为空。
|
msg: 昵称不能为空。
|
||||||
msg_range: Display name up to 30 characters.
|
msg_range: 显示名称不能超过 30 个字符。
|
||||||
username:
|
username:
|
||||||
label: 用户名
|
label: 用户名
|
||||||
caption: 用户之间可以通过 "@用户名" 进行交互。
|
caption: 用户之间可以通过 "@用户名" 进行交互。
|
||||||
msg: 用户名不能为空
|
msg: 用户名不能为空
|
||||||
msg_range: Username up to 30 characters.
|
msg_range: 用户名不能超过 30 个字符。
|
||||||
character: '用户名只能由 "a-z", "0-9", " - . _" 组成'
|
character: '用户名只能由 "a-z", "0-9", " - . _" 组成'
|
||||||
avatar:
|
avatar:
|
||||||
label: 头像
|
label: 头像
|
||||||
|
@ -679,13 +684,13 @@ ui:
|
||||||
default: 系统
|
default: 系统
|
||||||
msg: 请上传头像
|
msg: 请上传头像
|
||||||
bio:
|
bio:
|
||||||
label: 关于我 (可选)
|
label: 关于我
|
||||||
website:
|
website:
|
||||||
label: 网站 (可选)
|
label: 网站
|
||||||
placeholder: "https://example.com"
|
placeholder: "https://example.com"
|
||||||
msg: 格式不正确
|
msg: 格式不正确
|
||||||
location:
|
location:
|
||||||
label: 位置 (可选)
|
label: 位置
|
||||||
placeholder: "城市, 国家"
|
placeholder: "城市, 国家"
|
||||||
notification:
|
notification:
|
||||||
heading: 通知
|
heading: 通知
|
||||||
|
@ -753,7 +758,7 @@ ui:
|
||||||
confirm_info: >-
|
confirm_info: >-
|
||||||
<p>您确定要提交一个新的回答吗?</p><p>您可以直接编辑和改善您之前的回答的。</p>
|
<p>您确定要提交一个新的回答吗?</p><p>您可以直接编辑和改善您之前的回答的。</p>
|
||||||
empty: 回答内容不能为空。
|
empty: 回答内容不能为空。
|
||||||
characters: content must be at least 6 characters in length.
|
characters: 内容长度至少 6 个字符
|
||||||
reopen:
|
reopen:
|
||||||
title: 重新打开这个帖子
|
title: 重新打开这个帖子
|
||||||
content: 确定要重新打开吗?
|
content: 确定要重新打开吗?
|
||||||
|
@ -814,7 +819,7 @@ ui:
|
||||||
modal_confirm:
|
modal_confirm:
|
||||||
title: 发生错误...
|
title: 发生错误...
|
||||||
account_result:
|
account_result:
|
||||||
page_title: Welcome to {{site_name}}
|
page_title: 欢迎来到 {{site_name}}
|
||||||
success: 你的账号已通过验证,即将返回首页。
|
success: 你的账号已通过验证,即将返回首页。
|
||||||
link: 返回首页
|
link: 返回首页
|
||||||
invalid: >-
|
invalid: >-
|
||||||
|
@ -825,7 +830,7 @@ ui:
|
||||||
unsubscribe:
|
unsubscribe:
|
||||||
page_title: 退订
|
page_title: 退订
|
||||||
success_title: 取消订阅成功
|
success_title: 取消订阅成功
|
||||||
success_desc: You have been successfully removed from this subscriber list and won't receive any further emails from us.
|
success_desc: 您已成功地从此订阅者列表中移除,并且将不会再收到我们的任何电子邮件。
|
||||||
link: 更改设置
|
link: 更改设置
|
||||||
question:
|
question:
|
||||||
following_tags: 已关注的标签
|
following_tags: 已关注的标签
|
||||||
|
@ -887,7 +892,7 @@ ui:
|
||||||
title: Answer
|
title: Answer
|
||||||
next: 下一步
|
next: 下一步
|
||||||
done: 完成
|
done: 完成
|
||||||
config_yaml_error: Can't create the config.yaml file.
|
config_yaml_error: 无法创建 config.yaml 文件。
|
||||||
lang:
|
lang:
|
||||||
label: 请选择一种语言
|
label: 请选择一种语言
|
||||||
db_type:
|
db_type:
|
||||||
|
@ -917,7 +922,7 @@ ui:
|
||||||
label: 已创建 config.yaml 文件。
|
label: 已创建 config.yaml 文件。
|
||||||
desc: >-
|
desc: >-
|
||||||
您可以手动在 <1>/var/wwww/xxx/</1> 目录中创建<1>config.yaml</1> 文件并粘贴以下文本。
|
您可以手动在 <1>/var/wwww/xxx/</1> 目录中创建<1>config.yaml</1> 文件并粘贴以下文本。
|
||||||
info: After you've done that, click "Next" button.
|
info: 完成后,点击“下一步”按钮。
|
||||||
site_information: 站点信息
|
site_information: 站点信息
|
||||||
admin_account: 管理员账户
|
admin_account: 管理员账户
|
||||||
site_name:
|
site_name:
|
||||||
|
@ -962,12 +967,12 @@ ui:
|
||||||
您似乎已经安装过了。要重新安装,请先清除旧的数据库表。
|
您似乎已经安装过了。要重新安装,请先清除旧的数据库表。
|
||||||
db_failed: 数据连接异常!
|
db_failed: 数据连接异常!
|
||||||
db_failed_desc: >-
|
db_failed_desc: >-
|
||||||
This either means that the database information in your <1>config.yaml</1> file is incorrect or that contact with the database server could not be established. This could mean your host’s database server is down.
|
这或者意味着数据库信息在 <1>config.yaml</1> 文件不正确,或者无法与数据库服务器建立联系。这可能意味着您的主机数据库服务器已关闭。
|
||||||
counts:
|
counts:
|
||||||
views: views
|
views: 次浏览
|
||||||
votes: votes
|
votes: 个点赞
|
||||||
answers: answers
|
answers: 个回答
|
||||||
accepted: Accepted
|
accepted: 已被采纳
|
||||||
page_404:
|
page_404:
|
||||||
desc: "很抱歉,此页面不存在。"
|
desc: "很抱歉,此页面不存在。"
|
||||||
back_home: 回到主页
|
back_home: 回到主页
|
||||||
|
@ -975,7 +980,7 @@ ui:
|
||||||
desc: 服务器遇到了一个错误,无法完成你的请求。
|
desc: 服务器遇到了一个错误,无法完成你的请求。
|
||||||
back_home: 回到主页
|
back_home: 回到主页
|
||||||
page_maintenance:
|
page_maintenance:
|
||||||
desc: "We are under maintenance, we'll be back soon."
|
desc: "我们正在进行维护,我们将很快回来。"
|
||||||
nav_menus:
|
nav_menus:
|
||||||
dashboard: 后台管理
|
dashboard: 后台管理
|
||||||
contents: 内容管理
|
contents: 内容管理
|
||||||
|
@ -1116,7 +1121,7 @@ ui:
|
||||||
password:
|
password:
|
||||||
label: 密码
|
label: 密码
|
||||||
text: 用户将被注销,需要再次登录。
|
text: 用户将被注销,需要再次登录。
|
||||||
msg: Password must be at 8-32 characters in length.
|
msg: 密码的长度必须是8-32个字符。
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_submit: 提交
|
btn_submit: 提交
|
||||||
user_modal:
|
user_modal:
|
||||||
|
@ -1125,13 +1130,13 @@ ui:
|
||||||
fields:
|
fields:
|
||||||
display_name:
|
display_name:
|
||||||
label: 昵称
|
label: 昵称
|
||||||
msg: Display Name must be at 3-30 characters in length.
|
msg: 显示名称长度必须为 3-30 个字符
|
||||||
email:
|
email:
|
||||||
label: 邮箱
|
label: 邮箱
|
||||||
msg: 电子邮箱无效。
|
msg: 电子邮箱无效。
|
||||||
password:
|
password:
|
||||||
label: 密码
|
label: 密码
|
||||||
msg: Password must be at 8-32 characters in length.
|
msg: 密码的长度必须是8-32个字符。
|
||||||
btn_cancel: 取消
|
btn_cancel: 取消
|
||||||
btn_submit: 提交
|
btn_submit: 提交
|
||||||
questions:
|
questions:
|
||||||
|
@ -1172,11 +1177,11 @@ ui:
|
||||||
validate: 请输入一个有效的 URL。
|
validate: 请输入一个有效的 URL。
|
||||||
text: 此网站的地址。
|
text: 此网站的地址。
|
||||||
short_desc:
|
short_desc:
|
||||||
label: 简短网站描述(可选)
|
label: 简短站点描述
|
||||||
msg: 简短网站描述不能为空。
|
msg: 简短网站描述不能为空。
|
||||||
text: "简短的标语,作为网站主页的标题(Html 的 title 标签)。"
|
text: "简短的标语,作为网站主页的标题(Html 的 title 标签)。"
|
||||||
desc:
|
desc:
|
||||||
label: 网站描述 (可选)
|
label: 站点描述
|
||||||
msg: 网站描述不能为空。
|
msg: 网站描述不能为空。
|
||||||
text: "使用一句话描述本站,作为网站的描述(Html 的 meta 标签)。"
|
text: "使用一句话描述本站,作为网站的描述(Html 的 meta 标签)。"
|
||||||
contact_email:
|
contact_email:
|
||||||
|
@ -1186,14 +1191,6 @@ ui:
|
||||||
text: 负责本网站的主要联系人的电子邮件地址。
|
text: 负责本网站的主要联系人的电子邮件地址。
|
||||||
interface:
|
interface:
|
||||||
page_title: 界面
|
page_title: 界面
|
||||||
logo:
|
|
||||||
label: Logo (可选)
|
|
||||||
msg: 不能为空
|
|
||||||
text: 可以上传图片,或者<1>重置</1>为站点标题。
|
|
||||||
theme:
|
|
||||||
label: 主题
|
|
||||||
msg: 不能为空
|
|
||||||
text: 选择一个主题
|
|
||||||
language:
|
language:
|
||||||
label: 界面语言
|
label: 界面语言
|
||||||
msg: 不能为空
|
msg: 不能为空
|
||||||
|
@ -1245,19 +1242,19 @@ ui:
|
||||||
branding:
|
branding:
|
||||||
page_title: 品牌
|
page_title: 品牌
|
||||||
logo:
|
logo:
|
||||||
label: Logo (可选)
|
label: 网站标志(Logo)
|
||||||
msg: 图标不能为空。
|
msg: 图标不能为空。
|
||||||
text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。
|
text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。
|
||||||
mobile_logo:
|
mobile_logo:
|
||||||
label: 移动端图标(可选)
|
label: 移动端 Logo
|
||||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
text: 在你的网站的移动版上使用的标志。使用一个高度为56的宽矩形图像。如果留空,将使用 "Logo"设置中的图像。
|
||||||
square_icon:
|
square_icon:
|
||||||
label: 方形图标 (可选)
|
label: 方形图标
|
||||||
msg: 方形图标不能为空。
|
msg: 方形图标不能为空。
|
||||||
text: 用作元数据图标的基础的图像。最好是大于512x512。
|
text: 用作元数据图标的基础的图像。最好是大于512x512。
|
||||||
favicon:
|
favicon:
|
||||||
label: 收藏夹图标(可选)
|
label: 收藏夹图标
|
||||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
text: 网站的图标。要在 CDN 正常工作,它必须是 png。 将调整大小到32x32。如果留空,将使用“方形图标”。
|
||||||
legal:
|
legal:
|
||||||
page_title: 法律条款
|
page_title: 法律条款
|
||||||
terms_of_service:
|
terms_of_service:
|
||||||
|
@ -1339,6 +1336,7 @@ ui:
|
||||||
activate: 启用
|
activate: 启用
|
||||||
settings: 设置
|
settings: 设置
|
||||||
form:
|
form:
|
||||||
|
optional: (选填)
|
||||||
empty: 不能为空
|
empty: 不能为空
|
||||||
invalid: 是无效的
|
invalid: 是无效的
|
||||||
btn_submit: 保存
|
btn_submit: 保存
|
||||||
|
@ -1388,5 +1386,7 @@ ui:
|
||||||
staffs: 我们的社区工作人员
|
staffs: 我们的社区工作人员
|
||||||
reputation: 声望值
|
reputation: 声望值
|
||||||
votes: 投票
|
votes: 投票
|
||||||
|
prompt:
|
||||||
|
leave_page: "确定要离开此页面?"
|
||||||
|
changes_not_save: "您的更改尚未保存"
|
||||||
|
|
||||||
|
|
1858
i18n/zh_TW.yaml
1858
i18n/zh_TW.yaml
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,7 @@ const (
|
||||||
QuestionCannotDeleted = "error.question.cannot_deleted"
|
QuestionCannotDeleted = "error.question.cannot_deleted"
|
||||||
QuestionCannotClose = "error.question.cannot_close"
|
QuestionCannotClose = "error.question.cannot_close"
|
||||||
QuestionCannotUpdate = "error.question.cannot_update"
|
QuestionCannotUpdate = "error.question.cannot_update"
|
||||||
|
QuestionAlreadyDeleted = "error.question.already_deleted"
|
||||||
AnswerNotFound = "error.answer.not_found"
|
AnswerNotFound = "error.answer.not_found"
|
||||||
AnswerCannotDeleted = "error.answer.cannot_deleted"
|
AnswerCannotDeleted = "error.answer.cannot_deleted"
|
||||||
AnswerCannotUpdate = "error.answer.cannot_update"
|
AnswerCannotUpdate = "error.answer.cannot_update"
|
||||||
|
@ -64,4 +65,6 @@ const (
|
||||||
TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself"
|
TagCannotSetSynonymAsItself = "error.tag.cannot_set_synonym_as_itself"
|
||||||
NotAllowedRegistration = "error.user.not_allowed_registration"
|
NotAllowedRegistration = "error.user.not_allowed_registration"
|
||||||
SMTPConfigFromNameCannotBeEmail = "error.smtp.config_from_name_cannot_be_email"
|
SMTPConfigFromNameCannotBeEmail = "error.smtp.config_from_name_cannot_be_email"
|
||||||
|
AdminCannotUpdateTheirPassword = "error.admin.cannot_update_their_password"
|
||||||
|
AdminCannotModifySelfStatus = "error.admin.cannot_modify_self_status"
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,6 +20,8 @@ var GlobalTrans i18n.Translator
|
||||||
type LangOption struct {
|
type LangOption struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
|
// Translation completion percentage
|
||||||
|
Progress int `json:"progress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultLangOption default language option. If user config the language is default, the language option is admin choose.
|
// DefaultLangOption default language option. If user config the language is default, the language option is admin choose.
|
||||||
|
@ -47,6 +49,7 @@ func NewTranslator(c *I18n) (tr i18n.Translator, err error) {
|
||||||
if filepath.Ext(file.Name()) != ".yaml" && file.Name() != "i18n.yaml" {
|
if filepath.Ext(file.Name()) != ".yaml" && file.Name() != "i18n.yaml" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
log.Debugf("try to read file: %s", file.Name())
|
||||||
buf, err := os.ReadFile(filepath.Join(c.BundleDir, file.Name()))
|
buf, err := os.ReadFile(filepath.Join(c.BundleDir, file.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read file failed: %s %s", file.Name(), err)
|
return nil, fmt.Errorf("read file failed: %s %s", file.Name(), err)
|
||||||
|
@ -96,6 +99,11 @@ func NewTranslator(c *I18n) (tr i18n.Translator, err error) {
|
||||||
return nil, fmt.Errorf("i18n file parsing failed: %s", err)
|
return nil, fmt.Errorf("i18n file parsing failed: %s", err)
|
||||||
}
|
}
|
||||||
LanguageOptions = s.LangOption
|
LanguageOptions = s.LangOption
|
||||||
|
for _, option := range LanguageOptions {
|
||||||
|
if option.Progress != 100 {
|
||||||
|
option.Label = fmt.Sprintf("%s (%d%%)", option.Label, option.Progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
return GlobalTrans, err
|
return GlobalTrans, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,18 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) {
|
||||||
|
|
||||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||||
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
|
||||||
|
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
|
||||||
|
permission.CommentAdd,
|
||||||
|
permission.CommentEdit,
|
||||||
|
permission.CommentDelete,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
handler.HandleResponse(ctx, err, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.CanAdd = canList[0]
|
||||||
|
req.CanEdit = canList[1]
|
||||||
|
req.CanDelete = canList[2]
|
||||||
can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, permission.CommentEdit, req.CommentID)
|
can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, permission.CommentEdit, req.CommentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.HandleResponse(ctx, err, nil)
|
handler.HandleResponse(ctx, err, nil)
|
||||||
|
@ -122,8 +134,8 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cc.commentService.UpdateComment(ctx, req)
|
resp, err := cc.commentService.UpdateComment(ctx, req)
|
||||||
handler.HandleResponse(ctx, err, nil)
|
handler.HandleResponse(ctx, err, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommentWithPage get comment page
|
// GetCommentWithPage get comment page
|
||||||
|
|
|
@ -41,7 +41,6 @@ func (u *LangController) GetLangMapping(ctx *gin.Context) {
|
||||||
// @Tags Lang
|
// @Tags Lang
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} handler.RespBody{}
|
// @Success 200 {object} handler.RespBody{}
|
||||||
// @Router /answer/api/v1/language/options [get]
|
|
||||||
// @Router /answer/admin/api/language/options [get]
|
// @Router /answer/admin/api/language/options [get]
|
||||||
func (u *LangController) GetAdminLangOptions(ctx *gin.Context) {
|
func (u *LangController) GetAdminLangOptions(ctx *gin.Context) {
|
||||||
handler.HandleResponse(ctx, nil, translator.LanguageOptions)
|
handler.HandleResponse(ctx, nil, translator.LanguageOptions)
|
||||||
|
|
|
@ -404,22 +404,17 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) {
|
||||||
req := &schema.GetOtherUserInfoByUsernameReq{}
|
req := &schema.GetOtherUserInfoByUsernameReq{}
|
||||||
req.Username = username
|
req.Username = username
|
||||||
userinfo, err := tc.templateRenderController.UserInfo(ctx, req)
|
userinfo, err := tc.templateRenderController.UserInfo(ctx, req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tc.Page404(ctx)
|
tc.Page404(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !userinfo.Has {
|
|
||||||
tc.Page404(ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
siteInfo := tc.SiteInfo(ctx)
|
siteInfo := tc.SiteInfo(ctx)
|
||||||
siteInfo.Canonical = fmt.Sprintf("%s/users/%s", siteInfo.General.SiteUrl, username)
|
siteInfo.Canonical = fmt.Sprintf("%s/users/%s", siteInfo.General.SiteUrl, username)
|
||||||
siteInfo.Title = fmt.Sprintf("%s - %s", username, siteInfo.General.Name)
|
siteInfo.Title = fmt.Sprintf("%s - %s", username, siteInfo.General.Name)
|
||||||
tc.html(ctx, http.StatusOK, "homepage.html", siteInfo, gin.H{
|
tc.html(ctx, http.StatusOK, "homepage.html", siteInfo, gin.H{
|
||||||
"userinfo": userinfo,
|
"userinfo": userinfo,
|
||||||
"bio": template.HTML(userinfo.Info.BioHTML),
|
"bio": template.HTML(userinfo.BioHTML),
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -451,6 +446,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI
|
||||||
if !ok {
|
if !ok {
|
||||||
data["path"] = ""
|
data["path"] = ""
|
||||||
}
|
}
|
||||||
|
ctx.Header("X-Frame-Options", "DENY")
|
||||||
ctx.HTML(code, tpl, data)
|
ctx.HTML(code, tpl, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,6 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (q *TemplateRenderController) UserInfo(ctx context.Context, req *schema.GetOtherUserInfoByUsernameReq) (resp *schema.GetOtherUserInfoResp, err error) {
|
func (q *TemplateRenderController) UserInfo(ctx context.Context, req *schema.GetOtherUserInfoByUsernameReq) (resp *schema.GetOtherUserInfoByUsernameResp, err error) {
|
||||||
return q.userService.GetOtherUserInfoByUsername(ctx, req.Username)
|
return q.userService.GetOtherUserInfoByUsername(ctx, req.Username)
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ func (uc *UserController) RetrievePassWord(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
|
_, _ = uc.actionService.ActionRecordAdd(ctx, schema.ActionRecordTypeFindPass, ctx.ClientIP())
|
||||||
_, err := uc.userService.RetrievePassWord(ctx, req)
|
err := uc.userService.RetrievePassWord(ctx, req)
|
||||||
handler.HandleResponse(ctx, err, nil)
|
handler.HandleResponse(ctx, err, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,7 @@ func (uc *UserController) UserLogout(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = uc.authService.RemoveUserCacheInfo(ctx, accessToken)
|
_ = uc.authService.RemoveUserCacheInfo(ctx, accessToken)
|
||||||
|
_ = uc.authService.RemoveAdminUserCacheInfo(ctx, accessToken)
|
||||||
handler.HandleResponse(ctx, nil, nil)
|
handler.HandleResponse(ctx, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@ func (uc *UserAdminController) UpdateUserStatus(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||||
|
|
||||||
err := uc.userService.UpdateUserStatus(ctx, req)
|
err := uc.userService.UpdateUserStatus(ctx, req)
|
||||||
handler.HandleResponse(ctx, err, nil)
|
handler.HandleResponse(ctx, err, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ type Answer struct {
|
||||||
|
|
||||||
type AnswerSearch struct {
|
type AnswerSearch struct {
|
||||||
Answer
|
Answer
|
||||||
|
IncludeDeleted bool `json:"include_deleted"`
|
||||||
|
LoginUserID string `json:"login_user_id"`
|
||||||
Order string `json:"order_by"` // default or updated
|
Order string `json:"order_by"` // default or updated
|
||||||
Page int `json:"page" form:"page"` // Query number of pages
|
Page int `json:"page" form:"page"` // Query number of pages
|
||||||
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
PageSize int `json:"page_size" form:"page_size"` // Search page size
|
||||||
|
|
|
@ -78,7 +78,7 @@ type InitEnvironmentResp struct {
|
||||||
// InitBaseInfoReq init base info request
|
// InitBaseInfoReq init base info request
|
||||||
type InitBaseInfoReq struct {
|
type InitBaseInfoReq struct {
|
||||||
Language string `validate:"required,gt=0,lte=30" json:"lang"`
|
Language string `validate:"required,gt=0,lte=30" json:"lang"`
|
||||||
SiteName string `validate:"required,gt=0,lte=30" json:"site_name"`
|
SiteName string `validate:"required,sanitizer,gt=0,lte=30" json:"site_name"`
|
||||||
SiteURL string `validate:"required,gt=0,lte=512,url" json:"site_url"`
|
SiteURL string `validate:"required,gt=0,lte=512,url" json:"site_url"`
|
||||||
ContactEmail string `validate:"required,email,gt=0,lte=500" json:"contact_email"`
|
ContactEmail string `validate:"required,email,gt=0,lte=500" json:"contact_email"`
|
||||||
AdminName string `validate:"required,gt=3,lte=30" json:"name"`
|
AdminName string `validate:"required,gt=3,lte=30" json:"name"`
|
||||||
|
|
|
@ -206,7 +206,9 @@ func (ar *answerRepo) SearchList(ctx context.Context, search *entity.AnswerSearc
|
||||||
default:
|
default:
|
||||||
session = session.OrderBy("adopted desc,vote_count desc,created_at asc")
|
session = session.OrderBy("adopted desc,vote_count desc,created_at asc")
|
||||||
}
|
}
|
||||||
session = session.And("status = ?", entity.AnswerStatusAvailable)
|
if !search.IncludeDeleted {
|
||||||
|
session = session.And("status = ? OR user_id = ?", entity.AnswerStatusAvailable, search.LoginUserID)
|
||||||
|
}
|
||||||
|
|
||||||
session = session.Limit(search.PageSize, offset)
|
session = session.Limit(search.PageSize, offset)
|
||||||
count, err = session.FindAndCount(&rows)
|
count, err = session.FindAndCount(&rows)
|
||||||
|
|
|
@ -68,3 +68,11 @@ func (cr *captchaRepo) GetCaptcha(ctx context.Context, key string) (captcha stri
|
||||||
}
|
}
|
||||||
return captcha, nil
|
return captcha, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cr *captchaRepo) DelCaptcha(ctx context.Context, key string) (err error) {
|
||||||
|
err = cr.data.Cache.Del(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -106,6 +106,7 @@ func (a *UIRouter) Register(r *gin.Engine) {
|
||||||
default:
|
default:
|
||||||
filePath = UIIndexFilePath
|
filePath = UIIndexFilePath
|
||||||
c.Header("content-type", "text/html;charset=utf-8")
|
c.Header("content-type", "text/html;charset=utf-8")
|
||||||
|
c.Header("X-Frame-Options", "DENY")
|
||||||
}
|
}
|
||||||
file, err := ui.Build.ReadFile(filePath)
|
file, err := ui.Build.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -83,6 +83,7 @@ type AnswerInfo struct {
|
||||||
VoteStatus string `json:"vote_status"`
|
VoteStatus string `json:"vote_status"`
|
||||||
VoteCount int `json:"vote_count"`
|
VoteCount int `json:"vote_count"`
|
||||||
QuestionInfo *QuestionInfo `json:"question_info,omitempty"`
|
QuestionInfo *QuestionInfo `json:"question_info,omitempty"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
|
||||||
// MemberActions
|
// MemberActions
|
||||||
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
MemberActions []*PermissionMemberAction `json:"member_actions"`
|
||||||
|
|
|
@ -2,10 +2,9 @@ package schema
|
||||||
|
|
||||||
// UpdateUserStatusReq update user request
|
// UpdateUserStatusReq update user request
|
||||||
type UpdateUserStatusReq struct {
|
type UpdateUserStatusReq struct {
|
||||||
// user id
|
|
||||||
UserID string `validate:"required" json:"user_id"`
|
UserID string `validate:"required" json:"user_id"`
|
||||||
// user status
|
|
||||||
Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
|
Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
|
||||||
|
LoginUserID string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -53,6 +53,12 @@ type UpdateCommentReq struct {
|
||||||
// user id
|
// user id
|
||||||
UserID string `json:"-"`
|
UserID string `json:"-"`
|
||||||
IsAdmin bool `json:"-"`
|
IsAdmin bool `json:"-"`
|
||||||
|
|
||||||
|
CanAdd bool `json:"-"`
|
||||||
|
// whether user can edit it
|
||||||
|
CanEdit bool `json:"-"`
|
||||||
|
// whether user can delete it
|
||||||
|
CanDelete bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req *UpdateCommentReq) Check() (errFields []*validator.FormErrorField, err error) {
|
func (req *UpdateCommentReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||||
|
|
|
@ -176,11 +176,20 @@ type AdminQuestionInfo struct {
|
||||||
UserInfo *UserBasicInfo `json:"user_info"`
|
UserInfo *UserBasicInfo `json:"user_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OperationLevel string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OperationLevelInfo OperationLevel = "info"
|
||||||
|
OperationLevelDanger OperationLevel = "danger"
|
||||||
|
OperationLevelWarning OperationLevel = "warning"
|
||||||
|
)
|
||||||
|
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
OperationType string `json:"operation_type"`
|
Type string `json:"type"`
|
||||||
OperationDescription string `json:"operation_description"`
|
Description string `json:"description"`
|
||||||
OperationMsg string `json:"operation_msg"`
|
Msg string `json:"msg"`
|
||||||
OperationTime int64 `json:"operation_time"`
|
Time int64 `json:"time"`
|
||||||
|
Level OperationLevel `json:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetCloseTypeResp struct {
|
type GetCloseTypeResp struct {
|
||||||
|
|
|
@ -90,7 +90,7 @@ type GetTagResp struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *GetTagResp) GetExcerpt() {
|
func (tr *GetTagResp) GetExcerpt() {
|
||||||
excerpt := strings.TrimSpace(tr.OriginalText)
|
excerpt := strings.TrimSpace(tr.ParsedText)
|
||||||
idx := strings.Index(excerpt, "\n")
|
idx := strings.Index(excerpt, "\n")
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
excerpt = excerpt[0:idx]
|
excerpt = excerpt[0:idx]
|
||||||
|
|
|
@ -316,7 +316,7 @@ func (req *UpdateInfoRequest) Check() (errFields []*validator.FormErrorField, er
|
||||||
return errFields, errors.BadRequest(reason.UsernameInvalid)
|
return errFields, errors.BadRequest(reason.UsernameInvalid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.BioHTML = converter.Markdown2HTML(req.Bio)
|
req.BioHTML = converter.Markdown2BasicHTML(req.Bio)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +393,6 @@ type GetOtherUserInfoByUsernameReq struct {
|
||||||
|
|
||||||
type GetOtherUserInfoResp struct {
|
type GetOtherUserInfoResp struct {
|
||||||
Info *GetOtherUserInfoByUsernameResp `json:"info"`
|
Info *GetOtherUserInfoByUsernameResp `json:"info"`
|
||||||
Has bool `json:"has"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserChangeEmailSendCodeReq struct {
|
type UserChangeEmailSendCodeReq struct {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
type CaptchaRepo interface {
|
type CaptchaRepo interface {
|
||||||
SetCaptcha(ctx context.Context, key, captcha string) (err error)
|
SetCaptcha(ctx context.Context, key, captcha string) (err error)
|
||||||
GetCaptcha(ctx context.Context, key string) (captcha string, err error)
|
GetCaptcha(ctx context.Context, key string) (captcha string, err error)
|
||||||
|
DelCaptcha(ctx context.Context, key string) (err error)
|
||||||
SetActionType(ctx context.Context, ip, actionType string, amount int) (err error)
|
SetActionType(ctx context.Context, ip, actionType string, amount int) (err error)
|
||||||
GetActionType(ctx context.Context, ip, actionType string) (amount int, err error)
|
GetActionType(ctx context.Context, ip, actionType string) (amount int, err error)
|
||||||
DelActionType(ctx context.Context, ip, actionType string) (err error)
|
DelActionType(ctx context.Context, ip, actionType string) (err error)
|
||||||
|
@ -143,6 +144,12 @@ func (cs *CaptchaService) GenerateCaptcha(ctx context.Context) (key, captchaBase
|
||||||
func (cs *CaptchaService) VerifyCaptcha(ctx context.Context, key, captcha string) (isCorrect bool, err error) {
|
func (cs *CaptchaService) VerifyCaptcha(ctx context.Context, key, captcha string) (isCorrect bool, err error) {
|
||||||
realCaptcha, err := cs.captchaRepo.GetCaptcha(ctx, key)
|
realCaptcha, err := cs.captchaRepo.GetCaptcha(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Error("VerifyCaptcha GetCaptcha Error", err.Error())
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
err = cs.captchaRepo.DelCaptcha(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("VerifyCaptcha DelCaptcha Error", err.Error())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(captcha) == realCaptcha, nil
|
return strings.TrimSpace(captcha) == realCaptcha, nil
|
||||||
|
|
|
@ -73,6 +73,7 @@ func (as *AnswerCommon) ShowFormat(ctx context.Context, data *entity.Answer) *sc
|
||||||
}
|
}
|
||||||
info.UserID = data.UserID
|
info.UserID = data.UserID
|
||||||
info.UpdateUserID = data.LastEditUserID
|
info.UpdateUserID = data.LastEditUserID
|
||||||
|
info.Status = data.Status
|
||||||
return &info
|
return &info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("UpdateLastAnswer error", err.Error())
|
log.Error("UpdateLastAnswer error", err.Error())
|
||||||
}
|
}
|
||||||
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
|
err = as.questionCommon.UpdatePostTime(ctx, req.QuestionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return insertData.ID, err
|
return insertData.ID, err
|
||||||
}
|
}
|
||||||
|
@ -232,6 +232,11 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if answerInfo.Status == entity.AnswerStatusDeleted {
|
||||||
|
err = errors.BadRequest(reason.AnswerCannotUpdate)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
//If the content is the same, ignore it
|
//If the content is the same, ignore it
|
||||||
if answerInfo.OriginalText == req.Content {
|
if answerInfo.OriginalText == req.Content {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -268,7 +273,7 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
|
||||||
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "updated_at", "last_edit_user_id"}); err != nil {
|
if err = as.answerRepo.UpdateAnswer(ctx, insertData, []string{"original_text", "parsed_text", "updated_at", "last_edit_user_id"}); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
err = as.questionCommon.UpdataPostTime(ctx, req.QuestionID)
|
err = as.questionCommon.UpdatePostTime(ctx, req.QuestionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return insertData.ID, err
|
return insertData.ID, err
|
||||||
}
|
}
|
||||||
|
@ -473,6 +478,8 @@ func (as *AnswerService) SearchList(ctx context.Context, req *schema.AnswerListR
|
||||||
dbSearch.Page = req.Page
|
dbSearch.Page = req.Page
|
||||||
dbSearch.PageSize = req.PageSize
|
dbSearch.PageSize = req.PageSize
|
||||||
dbSearch.Order = req.Order
|
dbSearch.Order = req.Order
|
||||||
|
dbSearch.IncludeDeleted = req.CanDelete
|
||||||
|
dbSearch.LoginUserID = req.UserID
|
||||||
answerOriginalList, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
|
answerOriginalList, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return list, count, err
|
return list, count, err
|
||||||
|
|
|
@ -209,24 +209,40 @@ func (cs *CommentService) RemoveComment(ctx context.Context, req *schema.RemoveC
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateComment update comment
|
// UpdateComment update comment
|
||||||
func (cs *CommentService) UpdateComment(ctx context.Context, req *schema.UpdateCommentReq) (err error) {
|
func (cs *CommentService) UpdateComment(ctx context.Context, req *schema.UpdateCommentReq) (
|
||||||
|
resp *schema.GetCommentResp, err error) {
|
||||||
|
resp = &schema.GetCommentResp{}
|
||||||
|
|
||||||
old, exist, err := cs.commentCommonRepo.GetComment(ctx, req.CommentID)
|
old, exist, err := cs.commentCommonRepo.GetComment(ctx, req.CommentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !exist {
|
if !exist {
|
||||||
return errors.BadRequest(reason.CommentNotFound)
|
return resp, errors.BadRequest(reason.CommentNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// user can edit the comment that was posted by himself before deadline.
|
// user can edit the comment that was posted by himself before deadline.
|
||||||
if !req.IsAdmin && (time.Now().After(old.CreatedAt.Add(constant.CommentEditDeadline))) {
|
if !req.IsAdmin && (time.Now().After(old.CreatedAt.Add(constant.CommentEditDeadline))) {
|
||||||
return errors.BadRequest(reason.CommentCannotEditAfterDeadline)
|
return resp, errors.BadRequest(reason.CommentCannotEditAfterDeadline)
|
||||||
}
|
}
|
||||||
|
|
||||||
comment := &entity.Comment{}
|
comment := &entity.Comment{}
|
||||||
_ = copier.Copy(comment, req)
|
_ = copier.Copy(comment, req)
|
||||||
comment.ID = req.CommentID
|
comment.ID = req.CommentID
|
||||||
return cs.commentRepo.UpdateComment(ctx, comment)
|
resp.SetFromComment(comment)
|
||||||
|
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID,
|
||||||
|
time.Now(), req.CanEdit, req.CanDelete)
|
||||||
|
userInfo, exist, err := cs.userCommon.GetUserBasicInfoByID(ctx, resp.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if exist {
|
||||||
|
resp.Username = userInfo.Username
|
||||||
|
resp.UserDisplayName = userInfo.DisplayName
|
||||||
|
resp.UserAvatar = userInfo.Avatar
|
||||||
|
resp.UserStatus = userInfo.Status
|
||||||
|
}
|
||||||
|
return resp, cs.commentRepo.UpdateComment(ctx, comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetComment get comment one
|
// GetComment get comment one
|
||||||
|
|
|
@ -89,6 +89,7 @@ func (ds *DashboardService) StatisticalByCache(ctx context.Context) (*schema.Das
|
||||||
}
|
}
|
||||||
startTime := time.Now().Unix() - schema.AppStartTime.Unix()
|
startTime := time.Now().Unix() - schema.AppStartTime.Unix()
|
||||||
dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime)
|
dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime)
|
||||||
|
dashboardInfo.VersionInfo.Version = constant.Version
|
||||||
return dashboardInfo, nil
|
return dashboardInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ func NewQuestionCommon(questionRepo QuestionRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qs *QuestionCommon) UpdataPv(ctx context.Context, questionID string) error {
|
func (qs *QuestionCommon) UpdatePv(ctx context.Context, questionID string) error {
|
||||||
return qs.questionRepo.UpdatePvCount(ctx, questionID)
|
return qs.questionRepo.UpdatePvCount(ctx, questionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,14 +112,14 @@ func (qs *QuestionCommon) UpdateLastAnswer(ctx context.Context, questionID, Answ
|
||||||
return qs.questionRepo.UpdateLastAnswer(ctx, question)
|
return qs.questionRepo.UpdateLastAnswer(ctx, question)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qs *QuestionCommon) UpdataPostTime(ctx context.Context, questionID string) error {
|
func (qs *QuestionCommon) UpdatePostTime(ctx context.Context, questionID string) error {
|
||||||
questioninfo := &entity.Question{}
|
questioninfo := &entity.Question{}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
questioninfo.ID = questionID
|
questioninfo.ID = questionID
|
||||||
questioninfo.PostUpdateTime = now
|
questioninfo.PostUpdateTime = now
|
||||||
return qs.questionRepo.UpdateQuestion(ctx, questioninfo, []string{"post_update_time"})
|
return qs.questionRepo.UpdateQuestion(ctx, questioninfo, []string{"post_update_time"})
|
||||||
}
|
}
|
||||||
func (qs *QuestionCommon) UpdataPostSetTime(ctx context.Context, questionID string, setTime time.Time) error {
|
func (qs *QuestionCommon) UpdatePostSetTime(ctx context.Context, questionID string, setTime time.Time) error {
|
||||||
questioninfo := &entity.Question{}
|
questioninfo := &entity.Question{}
|
||||||
questioninfo.ID = questionID
|
questioninfo.ID = questionID
|
||||||
questioninfo.PostUpdateTime = setTime
|
questioninfo.PostUpdateTime = setTime
|
||||||
|
@ -148,7 +148,7 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser
|
||||||
return showinfo, err
|
return showinfo, err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return showinfo, errors.BadRequest(reason.QuestionNotFound)
|
return showinfo, errors.NotFound(reason.QuestionNotFound)
|
||||||
}
|
}
|
||||||
showinfo = qs.ShowFormat(ctx, dbinfo)
|
showinfo = qs.ShowFormat(ctx, dbinfo)
|
||||||
|
|
||||||
|
@ -170,10 +170,11 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser
|
||||||
log.Error("json.Unmarshal QuestionCloseJson error", err.Error())
|
log.Error("json.Unmarshal QuestionCloseJson error", err.Error())
|
||||||
} else {
|
} else {
|
||||||
operation := &schema.Operation{}
|
operation := &schema.Operation{}
|
||||||
operation.OperationType = closeinfo.Name
|
operation.Type = closeinfo.Name
|
||||||
operation.OperationDescription = closeinfo.Description
|
operation.Description = closeinfo.Description
|
||||||
operation.OperationMsg = closemsg.CloseMsg
|
operation.Msg = closemsg.CloseMsg
|
||||||
operation.OperationTime = metainfo.CreatedAt.Unix()
|
operation.Time = metainfo.CreatedAt.Unix()
|
||||||
|
operation.Level = schema.OperationLevelInfo
|
||||||
showinfo.Operation = operation
|
showinfo.Operation = operation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -470,6 +470,10 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
|
||||||
if !has {
|
if !has {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if dbinfo.Status == entity.QuestionStatusDeleted {
|
||||||
|
err = errors.BadRequest(reason.QuestionCannotUpdate)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
question := &entity.Question{}
|
question := &entity.Question{}
|
||||||
|
@ -614,12 +618,23 @@ func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID s
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// If the question is deleted, only the administrator and the author can view it
|
||||||
|
if question.Status == entity.QuestionStatusDeleted && !per.CanReopen && question.UserID != userID {
|
||||||
|
return nil, errors.NotFound(reason.QuestionNotFound)
|
||||||
|
}
|
||||||
if question.Status != entity.QuestionStatusClosed {
|
if question.Status != entity.QuestionStatusClosed {
|
||||||
per.CanReopen = false
|
per.CanReopen = false
|
||||||
}
|
}
|
||||||
if question.Status == entity.QuestionStatusClosed {
|
if question.Status == entity.QuestionStatusClosed {
|
||||||
per.CanClose = false
|
per.CanClose = false
|
||||||
}
|
}
|
||||||
|
if question.Status == entity.QuestionStatusDeleted {
|
||||||
|
operation := &schema.Operation{}
|
||||||
|
operation.Msg = translator.Tr(handler.GetLangByCtx(ctx), reason.QuestionAlreadyDeleted)
|
||||||
|
operation.Level = schema.OperationLevelDanger
|
||||||
|
question.Operation = operation
|
||||||
|
}
|
||||||
|
|
||||||
question.Description = htmltext.FetchExcerpt(question.HTML, "...", 240)
|
question.Description = htmltext.FetchExcerpt(question.HTML, "...", 240)
|
||||||
question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID,
|
question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID,
|
||||||
per.CanEdit, per.CanDelete, per.CanClose, per.CanReopen)
|
per.CanEdit, per.CanDelete, per.CanClose, per.CanReopen)
|
||||||
|
@ -630,7 +645,7 @@ func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID s
|
||||||
func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID, loginUserID string,
|
func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID, loginUserID string,
|
||||||
per schema.QuestionPermission) (
|
per schema.QuestionPermission) (
|
||||||
resp *schema.QuestionInfo, err error) {
|
resp *schema.QuestionInfo, err error) {
|
||||||
err = qs.questioncommon.UpdataPv(ctx, questionID)
|
err = qs.questioncommon.UpdatePv(ctx, questionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ func (rs *RevisionService) revisionAuditAnswer(ctx context.Context, revisionitem
|
||||||
if saveerr != nil {
|
if saveerr != nil {
|
||||||
return saveerr
|
return saveerr
|
||||||
}
|
}
|
||||||
saveerr = rs.questionCommon.UpdataPostSetTime(ctx, answerinfo.QuestionID, PostUpdateTime)
|
saveerr = rs.questionCommon.UpdatePostSetTime(ctx, answerinfo.QuestionID, PostUpdateTime)
|
||||||
if saveerr != nil {
|
if saveerr != nil {
|
||||||
return saveerr
|
return saveerr
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !exist {
|
if !exist {
|
||||||
return nil, errors.BadRequest(reason.TagNotFound)
|
return nil, errors.NotFound(reason.TagNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = &schema.GetTagResp{}
|
resp = &schema.GetTagResp{}
|
||||||
|
@ -113,7 +113,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !exist {
|
if !exist {
|
||||||
return nil, errors.BadRequest(reason.TagNotFound)
|
return nil, errors.NotFound(reason.TagNotFound)
|
||||||
}
|
}
|
||||||
resp.MainTagSlugName = tagInfo.SlugName
|
resp.MainTagSlugName = tagInfo.SlugName
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,10 @@ func NewUserAdminService(
|
||||||
|
|
||||||
// UpdateUserStatus update user
|
// UpdateUserStatus update user
|
||||||
func (us *UserAdminService) UpdateUserStatus(ctx context.Context, req *schema.UpdateUserStatusReq) (err error) {
|
func (us *UserAdminService) UpdateUserStatus(ctx context.Context, req *schema.UpdateUserStatusReq) (err error) {
|
||||||
|
// Admin cannot modify their status
|
||||||
|
if req.UserID == req.LoginUserID {
|
||||||
|
return errors.BadRequest(reason.AdminCannotModifySelfStatus)
|
||||||
|
}
|
||||||
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
|
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -153,6 +157,10 @@ func (us *UserAdminService) AddUser(ctx context.Context, req *schema.AddUserReq)
|
||||||
|
|
||||||
// UpdateUserPassword update user password
|
// UpdateUserPassword update user password
|
||||||
func (us *UserAdminService) UpdateUserPassword(ctx context.Context, req *schema.UpdateUserPasswordReq) (err error) {
|
func (us *UserAdminService) UpdateUserPassword(ctx context.Context, req *schema.UpdateUserPasswordReq) (err error) {
|
||||||
|
// Users cannot modify their password
|
||||||
|
if req.UserID == req.LoginUserID {
|
||||||
|
return errors.BadRequest(reason.AdminCannotUpdateTheirPassword)
|
||||||
|
}
|
||||||
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
|
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -92,19 +92,17 @@ func (us *UserService) GetUserInfoByUserID(ctx context.Context, token, userID st
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us *UserService) GetOtherUserInfoByUsername(ctx context.Context, username string) (
|
func (us *UserService) GetOtherUserInfoByUsername(ctx context.Context, username string) (
|
||||||
resp *schema.GetOtherUserInfoResp, err error,
|
resp *schema.GetOtherUserInfoByUsernameResp, err error,
|
||||||
) {
|
) {
|
||||||
userInfo, exist, err := us.userRepo.GetByUsername(ctx, username)
|
userInfo, exist, err := us.userRepo.GetByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp = &schema.GetOtherUserInfoResp{}
|
|
||||||
if !exist {
|
if !exist {
|
||||||
return resp, nil
|
return nil, errors.NotFound(reason.UserNotFound)
|
||||||
}
|
}
|
||||||
resp.Has = true
|
resp = &schema.GetOtherUserInfoByUsernameResp{}
|
||||||
resp.Info = &schema.GetOtherUserInfoByUsernameResp{}
|
resp.GetFromUserEntity(userInfo)
|
||||||
resp.Info.GetFromUserEntity(userInfo)
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,13 +153,13 @@ func (us *UserService) EmailLogin(ctx context.Context, req *schema.UserEmailLogi
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrievePassWord .
|
// RetrievePassWord .
|
||||||
func (us *UserService) RetrievePassWord(ctx context.Context, req *schema.UserRetrievePassWordRequest) (string, error) {
|
func (us *UserService) RetrievePassWord(ctx context.Context, req *schema.UserRetrievePassWordRequest) error {
|
||||||
userInfo, has, err := us.userRepo.GetByEmail(ctx, req.Email)
|
userInfo, has, err := us.userRepo.GetByEmail(ctx, req.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
if !has {
|
if !has {
|
||||||
return "", errors.BadRequest(reason.UserNotFound)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// send email
|
// send email
|
||||||
|
@ -173,10 +171,10 @@ func (us *UserService) RetrievePassWord(ctx context.Context, req *schema.UserRet
|
||||||
verifyEmailURL := fmt.Sprintf("%s/users/password-reset?code=%s", us.getSiteUrl(ctx), code)
|
verifyEmailURL := fmt.Sprintf("%s/users/password-reset?code=%s", us.getSiteUrl(ctx), code)
|
||||||
title, body, err := us.emailService.PassResetTemplate(ctx, verifyEmailURL)
|
title, body, err := us.emailService.PassResetTemplate(ctx, verifyEmailURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
go us.emailService.SendAndSaveCode(ctx, req.Email, title, body, code, data.ToJSONString())
|
go us.emailService.SendAndSaveCode(ctx, req.Email, title, body, code, data.ToJSONString())
|
||||||
return code, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseRePassword
|
// UseRePassword
|
||||||
|
|
|
@ -35,6 +35,17 @@ func Markdown2HTML(source string) string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Markdown2BasicHTML convert markdown to html ,Only basic syntax can be used
|
||||||
|
func Markdown2BasicHTML(source string) string {
|
||||||
|
content := Markdown2HTML(source)
|
||||||
|
filter := bluemonday.NewPolicy()
|
||||||
|
filter.AllowElements("p", "b", "br")
|
||||||
|
filter.AllowAttrs("src").OnElements("img")
|
||||||
|
filter.AddSpaceWhenStrippingTag(true)
|
||||||
|
content = filter.Sanitize(content)
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
type DangerousHTMLFilterExtension struct {
|
type DangerousHTMLFilterExtension struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,21 @@ export const LOGGED_USER_STORAGE_KEY = '_a_lui_';
|
||||||
export const LOGGED_TOKEN_STORAGE_KEY = '_a_ltk_';
|
export const LOGGED_TOKEN_STORAGE_KEY = '_a_ltk_';
|
||||||
export const REDIRECT_PATH_STORAGE_KEY = '_a_rp_';
|
export const REDIRECT_PATH_STORAGE_KEY = '_a_rp_';
|
||||||
export const CAPTCHA_CODE_STORAGE_KEY = '_a_captcha_';
|
export const CAPTCHA_CODE_STORAGE_KEY = '_a_captcha_';
|
||||||
|
export const DRAFT_QUESTION_STORAGE_KEY = '_a_dq_';
|
||||||
|
export const DRAFT_ANSWER_STORAGE_KEY = '_a_da_';
|
||||||
|
export const DRAFT_TIMESIGH_STORAGE_KEY = '|_a_t_s_|';
|
||||||
|
|
||||||
|
export const IGNORE_PATH_LIST = [
|
||||||
|
'/users/login',
|
||||||
|
'/users/register',
|
||||||
|
'/users/account-recovery',
|
||||||
|
'/users/change-email',
|
||||||
|
'/users/password-reset',
|
||||||
|
'/users/account-activation',
|
||||||
|
'/users/account-activation/success',
|
||||||
|
'/users/account-activation/failed',
|
||||||
|
'/users/confirm-new-email',
|
||||||
|
];
|
||||||
|
|
||||||
export const ADMIN_LIST_STATUS = {
|
export const ADMIN_LIST_STATUS = {
|
||||||
// normal;
|
// normal;
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { unionBy } from 'lodash';
|
import { unionBy } from 'lodash';
|
||||||
import { marked } from 'marked';
|
|
||||||
|
|
||||||
import * as Types from '@/common/interface';
|
import * as Types from '@/common/interface';
|
||||||
import { Modal } from '@/components';
|
import { Modal } from '@/components';
|
||||||
|
@ -108,15 +107,11 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||||
const users = matchedUsers(item.value);
|
const users = matchedUsers(item.value);
|
||||||
const userNames = unionBy(users.map((user) => user.userName));
|
const userNames = unionBy(users.map((user) => user.userName));
|
||||||
const commentMarkDown = parseUserInfo(item.value);
|
const commentMarkDown = parseUserInfo(item.value);
|
||||||
const html = marked.parse(commentMarkDown);
|
|
||||||
// if (!commentMarkDown || !html) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
const params = {
|
const params = {
|
||||||
object_id: objectId,
|
object_id: objectId,
|
||||||
original_text: commentMarkDown,
|
original_text: commentMarkDown,
|
||||||
mention_username_list: userNames,
|
mention_username_list: userNames,
|
||||||
parsed_text: html,
|
|
||||||
...(item.type === 'reply'
|
...(item.type === 'reply'
|
||||||
? {
|
? {
|
||||||
reply_comment_id: item.comment_id,
|
reply_comment_id: item.comment_id,
|
||||||
|
@ -128,13 +123,13 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||||
return updateComment({
|
return updateComment({
|
||||||
...params,
|
...params,
|
||||||
comment_id: item.comment_id,
|
comment_id: item.comment_id,
|
||||||
}).then(() => {
|
}).then((res) => {
|
||||||
setComments(
|
setComments(
|
||||||
comments.map((comment) => {
|
comments.map((comment) => {
|
||||||
if (comment.comment_id === item.comment_id) {
|
if (comment.comment_id === item.comment_id) {
|
||||||
comment.showEdit = false;
|
comment.showEdit = false;
|
||||||
comment.parsed_text = html;
|
comment.parsed_text = res.parsed_text;
|
||||||
comment.original_text = item.value;
|
comment.original_text = res.original_text;
|
||||||
}
|
}
|
||||||
return comment;
|
return comment;
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -69,7 +69,10 @@ const Index: FC<Props> = ({
|
||||||
{objectType !== 'answer' && opts?.showTitle && (
|
{objectType !== 'answer' && opts?.showTitle && (
|
||||||
<h5
|
<h5
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: diffText(newData.title, oldData?.title),
|
__html: diffText(
|
||||||
|
newData.title?.replace(/</gi, '<'),
|
||||||
|
oldData?.title?.replace(/</gi, '<'),
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
className="mb-3"
|
className="mb-3"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -114,19 +114,8 @@ export function htmlRender(el: HTMLElement | null) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
el.querySelectorAll('table').forEach((table) => {
|
// remove change table style to htmlToReact function
|
||||||
if (
|
/**
|
||||||
(table.parentNode as HTMLDivElement)?.classList.contains(
|
* @description: You modify the DOM with other scripts after React has rendered the DOM. This way, on the next render cycle (re-render), React cannot find the DOM node it rendered before, because it has been modified or removed by other scripts.
|
||||||
'table-responsive',
|
*/
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.classList.add('table', 'table-bordered');
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.className = 'table-responsive';
|
|
||||||
table.parentNode?.replaceChild(div, table);
|
|
||||||
div.appendChild(table);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,10 @@ const Header: FC = () => {
|
||||||
};
|
};
|
||||||
const onLoginClick = (evt) => {
|
const onLoginClick = (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
if (location.pathname === '/users/login') {
|
||||||
|
window.location.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
floppyNavigation.navigateToLogin((loginPath) => {
|
floppyNavigation.navigateToLogin((loginPath) => {
|
||||||
navigate(loginPath, { replace: true });
|
navigate(loginPath, { replace: true });
|
||||||
});
|
});
|
||||||
|
|
|
@ -238,7 +238,6 @@ const SchemaForm: ForwardRefRenderFunction<IRef, IProps> = (
|
||||||
const errors = requiredValidator();
|
const errors = requiredValidator();
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
formData = errors.reduce((acc, cur) => {
|
formData = errors.reduce((acc, cur) => {
|
||||||
console.log('schema.properties[cur]', cur);
|
|
||||||
acc[cur] = {
|
acc[cur] = {
|
||||||
...formData[cur],
|
...formData[cur],
|
||||||
isInvalid: true,
|
isInvalid: true,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import useChangePasswordModal from './useChangePasswordModal';
|
||||||
import usePageTags from './usePageTags';
|
import usePageTags from './usePageTags';
|
||||||
import useLoginRedirect from './useLoginRedirect';
|
import useLoginRedirect from './useLoginRedirect';
|
||||||
import usePromptWithUnload from './usePrompt';
|
import usePromptWithUnload from './usePrompt';
|
||||||
|
import useImgViewer from './useImgViewer';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useTagModal,
|
useTagModal,
|
||||||
|
@ -24,4 +25,5 @@ export {
|
||||||
usePageTags,
|
usePageTags,
|
||||||
useLoginRedirect,
|
useLoginRedirect,
|
||||||
usePromptWithUnload,
|
usePromptWithUnload,
|
||||||
|
useImgViewer,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { useLayoutEffect, useState, MouseEvent, useEffect } from 'react';
|
||||||
|
import { Modal } from 'react-bootstrap';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
const root = ReactDOM.createRoot(div);
|
||||||
|
|
||||||
|
const useImgViewer = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [imgSrc, setImgSrc] = useState('');
|
||||||
|
const onClose = () => {
|
||||||
|
setVisible(false);
|
||||||
|
setImgSrc('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkIfInLink = (target) => {
|
||||||
|
let ret = false;
|
||||||
|
let el = target.parentElement;
|
||||||
|
while (el) {
|
||||||
|
if (el.nodeName.toLowerCase() === 'a') {
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
el = el.parentElement;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkClickForImgView = (evt: MouseEvent<HTMLElement>) => {
|
||||||
|
const { target } = evt;
|
||||||
|
// @ts-ignore
|
||||||
|
if (target.nodeName.toLowerCase() !== 'img') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const img = target as HTMLImageElement;
|
||||||
|
if (!img.naturalWidth || !img.naturalHeight) {
|
||||||
|
img.classList.add('broken');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const src = img.currentSrc || img.src;
|
||||||
|
if (src && checkIfInLink(img) === false) {
|
||||||
|
setImgSrc(src);
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
root.render(
|
||||||
|
<Modal
|
||||||
|
show={visible}
|
||||||
|
fullscreen
|
||||||
|
centered
|
||||||
|
scrollable
|
||||||
|
contentClassName="bg-transparent"
|
||||||
|
onHide={onClose}>
|
||||||
|
<Modal.Body onClick={onClose} className="p-0 d-flex">
|
||||||
|
<img
|
||||||
|
className="cursor-zoom-out img-fluid m-auto"
|
||||||
|
src={imgSrc}
|
||||||
|
alt={imgSrc}
|
||||||
|
/>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
onClose();
|
||||||
|
}, [location]);
|
||||||
|
return {
|
||||||
|
onClose,
|
||||||
|
checkClickForImgView,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useImgViewer;
|
|
@ -6,7 +6,7 @@ import {
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
// https://gist.github.com/chaance/2f3c14ec2351a175024f62fd6ba64aa6
|
// https://gist.github.com/chaance/2f3c14ec2351a175024f62fd6ba64aa6
|
||||||
// The link above is an example of implementing usePromt with useBlocer.
|
// The link above is an example of implementing usePrompt with useBlocker.
|
||||||
interface PromptProps {
|
interface PromptProps {
|
||||||
when: boolean;
|
when: boolean;
|
||||||
beforeUnload?: boolean;
|
beforeUnload?: boolean;
|
||||||
|
|
|
@ -47,7 +47,11 @@ const useReportModal = (callback?: () => void) => {
|
||||||
setShow(true);
|
setShow(true);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const asyncCallback = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
callback?.();
|
||||||
|
});
|
||||||
|
};
|
||||||
const handleRadio = (val) => {
|
const handleRadio = (val) => {
|
||||||
setInvalidState(false);
|
setInvalidState(false);
|
||||||
setContent({
|
setContent({
|
||||||
|
@ -93,8 +97,8 @@ const useReportModal = (callback?: () => void) => {
|
||||||
close_type: reportType.type,
|
close_type: reportType.type,
|
||||||
close_msg: content.value,
|
close_msg: content.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
callback?.();
|
|
||||||
onClose();
|
onClose();
|
||||||
|
asyncCallback();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,8 +113,8 @@ const useReportModal = (callback?: () => void) => {
|
||||||
msg: t('flag_success', { keyPrefix: 'toast' }),
|
msg: t('flag_success', { keyPrefix: 'toast' }),
|
||||||
variant: 'warning',
|
variant: 'warning',
|
||||||
});
|
});
|
||||||
callback?.();
|
|
||||||
onClose();
|
onClose();
|
||||||
|
asyncCallback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +125,8 @@ const useReportModal = (callback?: () => void) => {
|
||||||
flagged_type: reportType.type,
|
flagged_type: reportType.type,
|
||||||
id: params.id,
|
id: params.id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
callback?.();
|
|
||||||
onClose();
|
onClose();
|
||||||
|
asyncCallback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -120,6 +120,14 @@ a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-zoom-out {
|
||||||
|
cursor: zoom-out !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
img:not(a img, img.broken) {
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
.resize-none {
|
.resize-none {
|
||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { Container, Button } from 'react-bootstrap';
|
import { Container, Button } from 'react-bootstrap';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'page_404' });
|
const { t } = useTranslation('translation', { keyPrefix: 'page_404' });
|
||||||
|
useEffect(() => {
|
||||||
|
// auto height of container
|
||||||
|
const pageWrap = document.querySelector('.page-wrap');
|
||||||
|
pageWrap.style.display = 'contents';
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
pageWrap.style.display = 'block';
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Container className="d-flex flex-column justify-content-center align-items-center page-wrap">
|
<Container
|
||||||
|
className="d-flex flex-column justify-content-center align-items-center"
|
||||||
|
style={{ flex: 1 }}>
|
||||||
<div
|
<div
|
||||||
className="mb-4 text-secondary"
|
className="mb-4 text-secondary"
|
||||||
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
||||||
(=‘x‘=)
|
(=‘x‘=)
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center mb-4">{t('desc')}</div>
|
<h4 className="text-center">{t('http_error')}</h4>
|
||||||
|
<div className="text-center mb-3 fs-5">{t('desc')}</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Button as={Link} to="/" variant="link">
|
<Button as={Link} to="/" variant="link">
|
||||||
{t('back_home')}
|
{t('back_home')}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { Container, Button } from 'react-bootstrap';
|
import { Container, Button } from 'react-bootstrap';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'page_50X' });
|
const { t } = useTranslation('translation', { keyPrefix: 'page_50X' });
|
||||||
|
useEffect(() => {
|
||||||
|
// auto height of container
|
||||||
|
const pageWrap = document.querySelector('.page-wrap');
|
||||||
|
pageWrap.style.display = 'contents';
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
pageWrap.style.display = 'block';
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Container className="d-flex flex-column justify-content-center align-items-center page-wrap">
|
<Container className="d-flex flex-column justify-content-center align-items-center page-wrap">
|
||||||
<div
|
<div
|
||||||
|
@ -11,7 +21,9 @@ const Index = () => {
|
||||||
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
style={{ fontSize: '120px', lineHeight: 1.2 }}>
|
||||||
(=T^T=)
|
(=T^T=)
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center mb-3">{t('desc')}</div>
|
|
||||||
|
<h4 className="text-center">{t('http_error')}</h4>
|
||||||
|
<div className="text-center mb-3 fs-5">{t('desc')}</div>
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Button as={Link} to="/" variant="link">
|
<Button as={Link} to="/" variant="link">
|
||||||
{t('back_home')}
|
{t('back_home')}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Form, Button } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import type { FormDataType } from '@/common/interface';
|
import type { FormDataType } from '@/common/interface';
|
||||||
|
import Pattern from '@/common/pattern';
|
||||||
import Progress from '../Progress';
|
import Progress from '../Progress';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -54,8 +55,7 @@ const Index: FC<Props> = ({ visible, data, changeCallback, nextCallback }) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mailReg = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
|
if (contact_email.value && !Pattern.email.test(contact_email.value)) {
|
||||||
if (contact_email.value && !contact_email.value.match(mailReg)) {
|
|
||||||
bol = false;
|
bol = false;
|
||||||
data.contact_email = {
|
data.contact_email = {
|
||||||
value: contact_email.value,
|
value: contact_email.value,
|
||||||
|
@ -98,7 +98,7 @@ const Index: FC<Props> = ({ visible, data, changeCallback, nextCallback }) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email.value && !email.value.match(mailReg)) {
|
if (email.value && !Pattern.email.test(email.value)) {
|
||||||
bol = false;
|
bol = false;
|
||||||
data.email = {
|
data.email = {
|
||||||
value: email.value,
|
value: email.value,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { FC, memo } from 'react';
|
import { FC, memo, useEffect } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
import { HelmetProvider } from 'react-helmet-async';
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
|
|
||||||
import { SWRConfig } from 'swr';
|
import { SWRConfig } from 'swr';
|
||||||
|
|
||||||
import { toastStore, loginToContinueStore } from '@/stores';
|
import { toastStore, loginToContinueStore, notFoundStore } from '@/stores';
|
||||||
import {
|
import {
|
||||||
Header,
|
Header,
|
||||||
Footer,
|
Footer,
|
||||||
|
@ -14,13 +14,23 @@ import {
|
||||||
PageTags,
|
PageTags,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { LoginToContinueModal } from '@/components/Modal';
|
import { LoginToContinueModal } from '@/components/Modal';
|
||||||
|
import { useImgViewer } from '@/hooks';
|
||||||
|
import Component404 from '@/pages/404';
|
||||||
|
|
||||||
const Layout: FC = () => {
|
const Layout: FC = () => {
|
||||||
|
const location = useLocation();
|
||||||
const { msg: toastMsg, variant, clear: toastClear } = toastStore();
|
const { msg: toastMsg, variant, clear: toastClear } = toastStore();
|
||||||
const closeToast = () => {
|
const closeToast = () => {
|
||||||
toastClear();
|
toastClear();
|
||||||
};
|
};
|
||||||
|
const { visible: show404, hide: notFoundHide } = notFoundStore();
|
||||||
|
|
||||||
|
const imgViewer = useImgViewer();
|
||||||
const { show: showLoginToContinueModal } = loginToContinueStore();
|
const { show: showLoginToContinueModal } = loginToContinueStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
notFoundHide();
|
||||||
|
}, [location]);
|
||||||
return (
|
return (
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
<PageTags />
|
<PageTags />
|
||||||
|
@ -30,8 +40,11 @@ const Layout: FC = () => {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
}}>
|
}}>
|
||||||
<Header />
|
<Header />
|
||||||
<div className="position-relative page-wrap">
|
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
|
||||||
<Outlet />
|
<div
|
||||||
|
className="position-relative page-wrap"
|
||||||
|
onClick={imgViewer.checkClickForImgView}>
|
||||||
|
{show404 ? <Component404 /> : <Outlet />}
|
||||||
</div>
|
</div>
|
||||||
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { isEqual } from 'lodash';
|
||||||
import { usePageTags, usePromptWithUnload } from '@/hooks';
|
import { usePageTags, usePromptWithUnload } from '@/hooks';
|
||||||
import { Editor, EditorRef, TagSelector } from '@/components';
|
import { Editor, EditorRef, TagSelector } from '@/components';
|
||||||
import type * as Type from '@/common/interface';
|
import type * as Type from '@/common/interface';
|
||||||
|
import { DRAFT_QUESTION_STORAGE_KEY } from '@/common/constants';
|
||||||
import {
|
import {
|
||||||
saveQuestion,
|
saveQuestion,
|
||||||
questionDetail,
|
questionDetail,
|
||||||
|
@ -19,7 +20,7 @@ import {
|
||||||
useQueryQuestionByTitle,
|
useQueryQuestionByTitle,
|
||||||
getTagsBySlugName,
|
getTagsBySlugName,
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
import { handleFormError } from '@/utils';
|
import { handleFormError, SaveDraft, storageExpires } from '@/utils';
|
||||||
import { pathFactory } from '@/router/pathFactory';
|
import { pathFactory } from '@/router/pathFactory';
|
||||||
|
|
||||||
import SearchQuestion from './components/SearchQuestion';
|
import SearchQuestion from './components/SearchQuestion';
|
||||||
|
@ -32,6 +33,8 @@ interface FormDataItem {
|
||||||
edit_summary: Type.FormValue<string>;
|
edit_summary: Type.FormValue<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveDraft = new SaveDraft({ type: 'question' });
|
||||||
|
|
||||||
const Ask = () => {
|
const Ask = () => {
|
||||||
const initFormData = {
|
const initFormData = {
|
||||||
title: {
|
title: {
|
||||||
|
@ -66,6 +69,7 @@ const Ask = () => {
|
||||||
const [checked, setCheckState] = useState(false);
|
const [checked, setCheckState] = useState(false);
|
||||||
const [contentChanged, setContentChanged] = useState(false);
|
const [contentChanged, setContentChanged] = useState(false);
|
||||||
const [focusType, setForceType] = useState('');
|
const [focusType, setForceType] = useState('');
|
||||||
|
const [hasDraft, setHasDraft] = useState(false);
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setFormData(initFormData);
|
setFormData(initFormData);
|
||||||
setCheckState(false);
|
setCheckState(false);
|
||||||
|
@ -98,6 +102,34 @@ const Ask = () => {
|
||||||
isEdit ? '' : formData.title.value,
|
isEdit ? '' : formData.title.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const removeDraft = () => {
|
||||||
|
saveDraft.save.cancel();
|
||||||
|
saveDraft.remove();
|
||||||
|
setHasDraft(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!qid) {
|
||||||
|
initQueryTags();
|
||||||
|
const draft = storageExpires.get(DRAFT_QUESTION_STORAGE_KEY);
|
||||||
|
if (draft) {
|
||||||
|
formData.title.value = draft.title;
|
||||||
|
formData.content.value = draft.content;
|
||||||
|
formData.tags.value = draft.tags;
|
||||||
|
formData.answer.value = draft.answer;
|
||||||
|
setCheckState(Boolean(draft.answer));
|
||||||
|
setHasDraft(true);
|
||||||
|
setFormData({ ...formData });
|
||||||
|
} else {
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resetForm();
|
||||||
|
};
|
||||||
|
}, [qid]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { title, tags, content, answer } = formData;
|
const { title, tags, content, answer } = formData;
|
||||||
const { title: editTitle, tags: editTags, content: editContent } = immData;
|
const { title: editTitle, tags: editTags, content: editContent } = immData;
|
||||||
|
@ -118,11 +150,21 @@ const Ask = () => {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write
|
// write
|
||||||
if (title.value || tags.value.length > 0 || content.value || answer.value) {
|
if (title.value || tags.value.length > 0 || content.value || answer.value) {
|
||||||
|
// save draft
|
||||||
|
saveDraft.save({
|
||||||
|
params: {
|
||||||
|
title: title.value,
|
||||||
|
tags: tags.value,
|
||||||
|
content: content.value,
|
||||||
|
answer: answer.value,
|
||||||
|
},
|
||||||
|
callback: () => setHasDraft(true),
|
||||||
|
});
|
||||||
setContentChanged(true);
|
setContentChanged(true);
|
||||||
} else {
|
} else {
|
||||||
|
removeDraft();
|
||||||
setContentChanged(false);
|
setContentChanged(false);
|
||||||
}
|
}
|
||||||
}, [formData]);
|
}, [formData]);
|
||||||
|
@ -131,12 +173,6 @@ const Ask = () => {
|
||||||
when: contentChanged,
|
when: contentChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isEdit) {
|
|
||||||
resetForm();
|
|
||||||
initQueryTags();
|
|
||||||
}
|
|
||||||
}, [isEdit]);
|
|
||||||
const { data: revisions = [] } = useQueryRevisions(qid);
|
const { data: revisions = [] } = useQueryRevisions(qid);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -191,6 +227,14 @@ const Ask = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deleteDraft = () => {
|
||||||
|
const res = window.confirm(t('discard_confirm', { keyPrefix: 'draft' }));
|
||||||
|
if (res) {
|
||||||
|
removeDraft();
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
setContentChanged(false);
|
setContentChanged(false);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -248,6 +292,7 @@ const Ask = () => {
|
||||||
navigate(pathFactory.questionLanding(id));
|
navigate(pathFactory.questionLanding(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
removeDraft();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const backPage = () => {
|
const backPage = () => {
|
||||||
|
@ -376,10 +421,17 @@ const Ask = () => {
|
||||||
<Button type="submit" className="me-2">
|
<Button type="submit" className="me-2">
|
||||||
{isEdit ? t('btn_save_edits') : t('btn_post_question')}
|
{isEdit ? t('btn_save_edits') : t('btn_post_question')}
|
||||||
</Button>
|
</Button>
|
||||||
|
{isEdit && (
|
||||||
<Button variant="link" onClick={backPage}>
|
<Button variant="link" onClick={backPage}>
|
||||||
{t('cancel', { keyPrefix: 'btns' })}
|
{t('cancel', { keyPrefix: 'btns' })}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasDraft && (
|
||||||
|
<Button variant="link" onClick={deleteDraft}>
|
||||||
|
{t('discard_draft', { keyPrefix: 'btns' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!isEdit && (
|
{!isEdit && (
|
||||||
|
@ -411,7 +463,6 @@ const Ask = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Form.Control
|
<Form.Control
|
||||||
value={formData.answer.value}
|
|
||||||
type="text"
|
type="text"
|
||||||
isInvalid={formData.answer.isInvalid}
|
isInvalid={formData.answer.isInvalid}
|
||||||
hidden
|
hidden
|
||||||
|
@ -424,10 +475,15 @@ const Ask = () => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{checked && (
|
{checked && (
|
||||||
<Button type="submit" className="mt-3">
|
<div className="mt-3">
|
||||||
{t('post_question&answer')}
|
<Button type="submit">{t('post_question&answer')}</Button>
|
||||||
|
{hasDraft && (
|
||||||
|
<Button variant="link" className="ms-2" onClick={deleteDraft}>
|
||||||
|
{t('discard_draft', { keyPrefix: 'btns' })}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
||||||
|
|
|
@ -10,38 +10,38 @@ interface Props {
|
||||||
const Index: FC<Props> = ({ data }) => {
|
const Index: FC<Props> = ({ data }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Alert className="mb-4" variant="info">
|
<Alert className="mb-4" variant={data.level}>
|
||||||
|
{data.level === 'info' ? (
|
||||||
<div>
|
<div>
|
||||||
{data.operation_msg.indexOf('http') > -1 ? (
|
{data.msg.indexOf('http') > -1 ? (
|
||||||
<p>
|
<p>
|
||||||
{data.operation_description}{' '}
|
{data.description}{' '}
|
||||||
<a href={data.operation_msg} style={{ color: '#055160' }}>
|
<a href={data.msg} style={{ color: '#055160' }}>
|
||||||
<strong>{t('question_detail.show_exist')}</strong>
|
<strong>{t('question_detail.show_exist')}</strong>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p>
|
<p>{data.msg ? data.msg : data.description}</p>
|
||||||
{data.operation_msg
|
|
||||||
? data.operation_msg
|
|
||||||
: data.operation_description}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
<div className="fs-14">
|
<div className="fs-14">
|
||||||
{t('question_detail.closed_in')}{' '}
|
{t('question_detail.closed_in')}{' '}
|
||||||
<time
|
<time
|
||||||
dateTime={dayjs.unix(data.operation_time).tz().toISOString()}
|
dateTime={dayjs.unix(data.time).tz().toISOString()}
|
||||||
title={dayjs
|
title={dayjs
|
||||||
.unix(data.operation_time)
|
.unix(data.time)
|
||||||
.tz()
|
.tz()
|
||||||
.format(t('dates.long_date_with_time'))}>
|
.format(t('dates.long_date_with_time'))}>
|
||||||
{dayjs
|
{dayjs
|
||||||
.unix(data.operation_time)
|
.unix(data.time)
|
||||||
.tz()
|
.tz()
|
||||||
.format(t('dates.long_date_with_year'))}
|
.format(t('dates.long_date_with_year'))}
|
||||||
</time>
|
</time>
|
||||||
.
|
.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
data.msg
|
||||||
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { memo, FC, useEffect, useRef } from 'react';
|
import { memo, FC, useEffect, useRef } from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button, Alert } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link, useSearchParams } from 'react-router-dom';
|
import { Link, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -72,6 +72,11 @@ const Index: FC<Props> = ({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div id={data.id} ref={answerRef} className="answer-item py-4">
|
<div id={data.id} ref={answerRef} className="answer-item py-4">
|
||||||
|
{data.status === 10 && (
|
||||||
|
<Alert variant="danger" className="mb-4">
|
||||||
|
{t('post_deleted', { keyPrefix: 'messages' })}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
<article
|
<article
|
||||||
dangerouslySetInnerHTML={{ __html: data?.html }}
|
dangerouslySetInnerHTML={{ __html: data?.html }}
|
||||||
className="fmt text-break text-wrap"
|
className="fmt text-break text-wrap"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { memo, useState, FC } from 'react';
|
import { memo, useState, FC, useEffect } from 'react';
|
||||||
import { Form, Button } from 'react-bootstrap';
|
import { Form, Button } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ import { usePromptWithUnload } from '@/hooks';
|
||||||
import { Editor, Modal, TextArea } from '@/components';
|
import { Editor, Modal, TextArea } from '@/components';
|
||||||
import { FormDataType } from '@/common/interface';
|
import { FormDataType } from '@/common/interface';
|
||||||
import { postAnswer } from '@/services';
|
import { postAnswer } from '@/services';
|
||||||
import { guard, handleFormError } from '@/utils';
|
import { guard, handleFormError, SaveDraft, storageExpires } from '@/utils';
|
||||||
|
import { DRAFT_ANSWER_STORAGE_KEY } from '@/common/constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
|
@ -21,6 +22,8 @@ interface Props {
|
||||||
callback?: (obj) => void;
|
callback?: (obj) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveDraft = new SaveDraft({ type: 'answer' });
|
||||||
|
|
||||||
const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
const { t } = useTranslation('translation', {
|
const { t } = useTranslation('translation', {
|
||||||
keyPrefix: 'question_detail.write_answer',
|
keyPrefix: 'question_detail.write_answer',
|
||||||
|
@ -35,11 +38,51 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
const [showEditor, setShowEditor] = useState<boolean>(visible);
|
const [showEditor, setShowEditor] = useState<boolean>(visible);
|
||||||
const [focusType, setFocusType] = useState('');
|
const [focusType, setFocusType] = useState('');
|
||||||
const [editorFocusState, setEditorFocusState] = useState(false);
|
const [editorFocusState, setEditorFocusState] = useState(false);
|
||||||
|
const [hasDraft, setHasDraft] = useState(false);
|
||||||
|
|
||||||
usePromptWithUnload({
|
usePromptWithUnload({
|
||||||
when: Boolean(formData.content.value),
|
when: Boolean(formData.content.value),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const removeDraft = () => {
|
||||||
|
// immediately remove debounced save
|
||||||
|
saveDraft.save.cancel();
|
||||||
|
saveDraft.remove();
|
||||||
|
setHasDraft(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const draft = storageExpires.get(DRAFT_ANSWER_STORAGE_KEY);
|
||||||
|
if (draft?.questionId === data.qid && draft?.content) {
|
||||||
|
setFormData({
|
||||||
|
content: {
|
||||||
|
value: draft.content,
|
||||||
|
isInvalid: false,
|
||||||
|
errorMsg: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setShowEditor(true);
|
||||||
|
setHasDraft(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const draft = storageExpires.get(DRAFT_ANSWER_STORAGE_KEY);
|
||||||
|
const { content } = formData;
|
||||||
|
|
||||||
|
if (content.value) {
|
||||||
|
// save Draft
|
||||||
|
saveDraft.save({
|
||||||
|
questionId: data?.qid,
|
||||||
|
content: content.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
setHasDraft(true);
|
||||||
|
} else if (draft?.questionId === data.qid && !content.value) {
|
||||||
|
removeDraft();
|
||||||
|
}
|
||||||
|
}, [formData.content.value]);
|
||||||
|
|
||||||
const checkValidated = (): boolean => {
|
const checkValidated = (): boolean => {
|
||||||
let bol = true;
|
let bol = true;
|
||||||
const { content } = formData;
|
const { content } = formData;
|
||||||
|
@ -65,6 +108,24 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
return bol;
|
return bol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
setFormData({
|
||||||
|
content: {
|
||||||
|
value: '',
|
||||||
|
isInvalid: false,
|
||||||
|
errorMsg: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteDraft = () => {
|
||||||
|
const res = window.confirm(t('discard_confirm', { keyPrefix: 'draft' }));
|
||||||
|
if (res) {
|
||||||
|
removeDraft();
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (!guard.tryNormalLogged(true)) {
|
if (!guard.tryNormalLogged(true)) {
|
||||||
return;
|
return;
|
||||||
|
@ -86,6 +147,7 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
removeDraft();
|
||||||
callback?.(res.info);
|
callback?.(res.info);
|
||||||
})
|
})
|
||||||
.catch((ex) => {
|
.catch((ex) => {
|
||||||
|
@ -128,7 +190,6 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
setShowEditor(true);
|
setShowEditor(true);
|
||||||
setEditorFocusState(true);
|
setEditorFocusState(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form noValidate className="mt-4">
|
<Form noValidate className="mt-4">
|
||||||
{(!data.answered || showEditor) && (
|
{(!data.answered || showEditor) && (
|
||||||
|
@ -187,6 +248,11 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={clickBtn}>{t('btn_name')}</Button>
|
<Button onClick={clickBtn}>{t('btn_name')}</Button>
|
||||||
)}
|
)}
|
||||||
|
{hasDraft && (
|
||||||
|
<Button variant="link" className="ms-2" onClick={deleteDraft}>
|
||||||
|
{t('discard_draft', { keyPrefix: 'btns' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -56,6 +56,7 @@ const Index = () => {
|
||||||
const { setUsers } = usePageUsers();
|
const { setUsers } = usePageUsers();
|
||||||
const userInfo = loggedUserInfoStore((state) => state.user);
|
const userInfo = loggedUserInfoStore((state) => state.user);
|
||||||
const isAuthor = userInfo?.username === question?.user_info?.username;
|
const isAuthor = userInfo?.username === question?.user_info?.username;
|
||||||
|
const isAdmin = userInfo?.is_admin;
|
||||||
const isLogged = Boolean(userInfo?.access_token);
|
const isLogged = Boolean(userInfo?.access_token);
|
||||||
const { state: locationState } = useLocation();
|
const { state: locationState } = useLocation();
|
||||||
|
|
||||||
|
@ -76,7 +77,22 @@ const Index = () => {
|
||||||
page_size: 999,
|
page_size: 999,
|
||||||
});
|
});
|
||||||
if (res) {
|
if (res) {
|
||||||
setAnswers(res);
|
res.list = res.list?.filter((v) => {
|
||||||
|
// delete answers pnly show to author and admin and has searchparams aid
|
||||||
|
if (v.status === 10) {
|
||||||
|
if (
|
||||||
|
(v?.user_info.username === userInfo?.username || isAdmin) &&
|
||||||
|
aid === v.id
|
||||||
|
) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
|
||||||
|
setAnswers({ ...res, count: res.list.length });
|
||||||
if (page > 0 || order) {
|
if (page > 0 || order) {
|
||||||
// scroll into view;
|
// scroll into view;
|
||||||
const element = document.getElementById('answerHeader');
|
const element = document.getElementById('answerHeader');
|
||||||
|
@ -183,9 +199,7 @@ const Index = () => {
|
||||||
<Container className="pt-4 mt-2 mb-5 questionDetailPage">
|
<Container className="pt-4 mt-2 mb-5 questionDetailPage">
|
||||||
<Row className="justify-content-center">
|
<Row className="justify-content-center">
|
||||||
<Col xxl={7} lg={8} sm={12} className="mb-5 mb-md-0">
|
<Col xxl={7} lg={8} sm={12} className="mb-5 mb-md-0">
|
||||||
{question?.operation?.operation_type && (
|
{question?.operation?.level && <Alert data={question.operation} />}
|
||||||
<Alert data={question.operation} />
|
|
||||||
)}
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<ContentLoader />
|
<ContentLoader />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -18,7 +18,7 @@ const Index: FC<Props> = ({ visible, introduction, data }) => {
|
||||||
<h5 className="mb-3">{t('about_me')}</h5>
|
<h5 className="mb-3">{t('about_me')}</h5>
|
||||||
{introduction ? (
|
{introduction ? (
|
||||||
<div
|
<div
|
||||||
className="mb-4 text-break"
|
className="mb-4 text-break fmt"
|
||||||
dangerouslySetInnerHTML={{ __html: introduction }}
|
dangerouslySetInnerHTML={{ __html: introduction }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
usePersonalTop,
|
usePersonalTop,
|
||||||
usePersonalListByTabName,
|
usePersonalListByTabName,
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
|
import type { UserInfoRes } from '@/common/interface';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UserInfo,
|
UserInfo,
|
||||||
|
@ -47,8 +48,8 @@ const Personal: FC = () => {
|
||||||
tabName,
|
tabName,
|
||||||
);
|
);
|
||||||
let pageTitle = '';
|
let pageTitle = '';
|
||||||
if (userInfo) {
|
if (userInfo?.username) {
|
||||||
pageTitle = `${userInfo.info.display_name} (${userInfo.info.username})`;
|
pageTitle = `${userInfo?.display_name} (${userInfo?.username})`;
|
||||||
}
|
}
|
||||||
const { count = 0, list = [] } = listData?.[tabName] || {};
|
const { count = 0, list = [] } = listData?.[tabName] || {};
|
||||||
usePageTags({
|
usePageTags({
|
||||||
|
@ -57,11 +58,11 @@ const Personal: FC = () => {
|
||||||
return (
|
return (
|
||||||
<Container className="pt-4 mt-2 mb-5">
|
<Container className="pt-4 mt-2 mb-5">
|
||||||
<Row className="justify-content-center">
|
<Row className="justify-content-center">
|
||||||
{userInfo?.info?.status !== 'normal' && userInfo?.info?.status_msg && (
|
{userInfo?.status !== 'normal' && userInfo?.status_msg && (
|
||||||
<Alert data={userInfo?.info.status_msg} />
|
<Alert data={userInfo?.status_msg} />
|
||||||
)}
|
)}
|
||||||
<Col xxl={7} lg={8} sm={12}>
|
<Col xxl={7} lg={8} sm={12}>
|
||||||
<UserInfo data={userInfo?.info} />
|
<UserInfo data={userInfo as UserInfoRes} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col
|
<Col
|
||||||
xxl={3}
|
xxl={3}
|
||||||
|
@ -88,11 +89,11 @@ const Personal: FC = () => {
|
||||||
<Col xxl={7} lg={8} sm={12}>
|
<Col xxl={7} lg={8} sm={12}>
|
||||||
<Overview
|
<Overview
|
||||||
visible={tabName === 'overview'}
|
visible={tabName === 'overview'}
|
||||||
introduction={userInfo?.info?.bio_html}
|
introduction={userInfo?.bio_html || ''}
|
||||||
data={topData}
|
data={topData}
|
||||||
/>
|
/>
|
||||||
<ListHead
|
<ListHead
|
||||||
count={tabName === 'reputation' ? userInfo?.info?.rank : count}
|
count={tabName === 'reputation' ? Number(userInfo?.rank) : count}
|
||||||
sort={order}
|
sort={order}
|
||||||
visible={tabName !== 'overview'}
|
visible={tabName !== 'overview'}
|
||||||
tabName={tabName}
|
tabName={tabName}
|
||||||
|
@ -120,17 +121,14 @@ const Personal: FC = () => {
|
||||||
</Col>
|
</Col>
|
||||||
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
|
||||||
<h5 className="mb-3">{t('stats')}</h5>
|
<h5 className="mb-3">{t('stats')}</h5>
|
||||||
{userInfo?.info && (
|
{userInfo?.created_at && (
|
||||||
<>
|
<>
|
||||||
<div className="text-secondary">
|
<div className="text-secondary">
|
||||||
<FormatTime
|
<FormatTime time={userInfo.created_at} preFix={t('joined')} />
|
||||||
time={userInfo.info.created_at}
|
|
||||||
preFix={t('joined')}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-secondary">
|
<div className="text-secondary">
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={userInfo.info.last_login_date}
|
time={userInfo.last_login_date}
|
||||||
preFix={t('last_login')}
|
preFix={t('last_login')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Error50X from '@/pages/50X';
|
||||||
|
// import Page404 from '@/pages/404';
|
||||||
|
|
||||||
|
const Index = () => {
|
||||||
|
return <Error50X />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Index;
|
|
@ -2,9 +2,10 @@ import { Suspense, lazy } from 'react';
|
||||||
import { RouteObject } from 'react-router-dom';
|
import { RouteObject } from 'react-router-dom';
|
||||||
|
|
||||||
import Layout from '@/pages/Layout';
|
import Layout from '@/pages/Layout';
|
||||||
import ErrorBoundary from '@/pages/50X';
|
|
||||||
import baseRoutes, { RouteNode } from '@/router/routes';
|
import baseRoutes, { RouteNode } from './routes';
|
||||||
import RouteGuard from '@/router/RouteGuard';
|
import RouteGuard from './RouteGuard';
|
||||||
|
import RouteErrorBoundary from './RouteErrorBoundary';
|
||||||
|
|
||||||
const routes: RouteNode[] = [];
|
const routes: RouteNode[] = [];
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
|
||||||
) : (
|
) : (
|
||||||
<Layout />
|
<Layout />
|
||||||
);
|
);
|
||||||
rn.errorElement = <ErrorBoundary />;
|
rn.errorElement = <RouteErrorBoundary />;
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* cannot use a fully dynamic import statement
|
* cannot use a fully dynamic import statement
|
||||||
|
@ -37,6 +38,7 @@ const routeWrapper = (routeNodes: RouteNode[], root: RouteNode[]) => {
|
||||||
)}
|
)}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
|
rn.errorElement = <RouteErrorBoundary />;
|
||||||
}
|
}
|
||||||
root.push(rn);
|
root.push(rn);
|
||||||
const children = Array.isArray(rn.children) ? rn.children : null;
|
const children = Array.isArray(rn.children) ? rn.children : null;
|
||||||
|
|
|
@ -8,7 +8,10 @@ export const usePersonalInfoByName = (username: string) => {
|
||||||
const apiUrl = '/answer/api/v1/personal/user/info';
|
const apiUrl = '/answer/api/v1/personal/user/info';
|
||||||
const { data, error, mutate } = useSWR<Type.UserInfoRes, Error>(
|
const { data, error, mutate } = useSWR<Type.UserInfoRes, Error>(
|
||||||
username ? `${apiUrl}?username=${username}` : null,
|
username ? `${apiUrl}?username=${username}` : null,
|
||||||
request.instance.get,
|
(url) =>
|
||||||
|
request.get(url, {
|
||||||
|
allow404: true,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
|
|
|
@ -47,7 +47,9 @@ export const useTagInfo = ({ id = '', name = '' }) => {
|
||||||
name = encodeURIComponent(name);
|
name = encodeURIComponent(name);
|
||||||
apiUrl = `/answer/api/v1/tag?name=${name}`;
|
apiUrl = `/answer/api/v1/tag?name=${name}`;
|
||||||
}
|
}
|
||||||
const { data, error } = useSWR<Type.TagInfo>(apiUrl, request.instance.get);
|
const { data, error } = useSWR<Type.TagInfo>(apiUrl, (url) =>
|
||||||
|
request.get(url, { allow404: true }),
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
isLoading: !data && !error,
|
isLoading: !data && !error,
|
||||||
|
|
|
@ -171,6 +171,7 @@ export const saveQuestion = (params: Type.QuestionParams) => {
|
||||||
export const questionDetail = (id: string) => {
|
export const questionDetail = (id: string) => {
|
||||||
return request.get<Type.QuestionDetailRes>(
|
return request.get<Type.QuestionDetailRes>(
|
||||||
`/answer/api/v1/question/info?id=${id}`,
|
`/answer/api/v1/question/info?id=${id}`,
|
||||||
|
{ allow404: true },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import pageTagStore from './pageTags';
|
||||||
import customizeStore from './customize';
|
import customizeStore from './customize';
|
||||||
import themeSettingStore from './themeSetting';
|
import themeSettingStore from './themeSetting';
|
||||||
import loginToContinueStore from './loginToContinue';
|
import loginToContinueStore from './loginToContinue';
|
||||||
|
import notFoundStore from './notFound';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
toastStore,
|
toastStore,
|
||||||
|
@ -23,4 +24,5 @@ export {
|
||||||
themeSettingStore,
|
themeSettingStore,
|
||||||
seoSettingStore,
|
seoSettingStore,
|
||||||
loginToContinueStore,
|
loginToContinueStore,
|
||||||
|
notFoundStore,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import create from 'zustand';
|
||||||
|
|
||||||
|
interface NotFoundType {
|
||||||
|
visible: boolean;
|
||||||
|
show: () => void;
|
||||||
|
hide: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notFound = create<NotFoundType>((set) => ({
|
||||||
|
visible: false,
|
||||||
|
show: () => {
|
||||||
|
set(() => {
|
||||||
|
return { visible: true };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hide: () => {
|
||||||
|
set(() => {
|
||||||
|
return { visible: false };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default notFound;
|
|
@ -237,7 +237,26 @@ function htmlToReact(html: string) {
|
||||||
const cleanedHtml = DOMPurify.sanitize(html, {
|
const cleanedHtml = DOMPurify.sanitize(html, {
|
||||||
USE_PROFILES: { html: true },
|
USE_PROFILES: { html: true },
|
||||||
});
|
});
|
||||||
return parse(cleanedHtml);
|
|
||||||
|
const ele = document.createElement('div');
|
||||||
|
ele.innerHTML = cleanedHtml;
|
||||||
|
|
||||||
|
ele.querySelectorAll('table').forEach((table) => {
|
||||||
|
if (
|
||||||
|
(!table || (table.parentNode as HTMLDivElement))?.classList.contains(
|
||||||
|
'table-responsive',
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.classList.add('table', 'table-bordered');
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'table-responsive';
|
||||||
|
table.parentNode?.replaceChild(div, table);
|
||||||
|
div.appendChild(table);
|
||||||
|
});
|
||||||
|
return parse(ele.innerHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -63,7 +63,7 @@ export const deriveLoginState = (): TLoginState => {
|
||||||
return ls;
|
return ls;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isIgnoredPath = (ignoredPath: string | string[]) => {
|
export const isIgnoredPath = (ignoredPath: string | string[]) => {
|
||||||
if (!Array.isArray(ignoredPath)) {
|
if (!Array.isArray(ignoredPath)) {
|
||||||
ignoredPath = [ignoredPath];
|
ignoredPath = [ignoredPath];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
export { default as request } from './request';
|
export { default as request } from './request';
|
||||||
export { default as Storage } from './storage';
|
export { default as Storage } from './storage';
|
||||||
export { floppyNavigation } from './floppyNavigation';
|
export { floppyNavigation } from './floppyNavigation';
|
||||||
|
export { default as storageExpires } from './storageWithExpires';
|
||||||
|
export { default as SaveDraft } from './saveDraft';
|
||||||
|
|
||||||
export * from './common';
|
export * from './common';
|
||||||
export * from './color';
|
export * from './color';
|
||||||
|
|
|
@ -2,19 +2,24 @@ import axios, { AxiosResponse } from 'axios';
|
||||||
import type { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
|
import type { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
|
||||||
|
|
||||||
import { Modal } from '@/components';
|
import { Modal } from '@/components';
|
||||||
import { loggedUserInfoStore, toastStore } from '@/stores';
|
import { loggedUserInfoStore, toastStore, notFoundStore } from '@/stores';
|
||||||
import { LOGGED_TOKEN_STORAGE_KEY } from '@/common/constants';
|
import { LOGGED_TOKEN_STORAGE_KEY, IGNORE_PATH_LIST } from '@/common/constants';
|
||||||
import { RouteAlias } from '@/router/alias';
|
import { RouteAlias } from '@/router/alias';
|
||||||
import { getCurrentLang } from '@/utils/localize';
|
import { getCurrentLang } from '@/utils/localize';
|
||||||
|
|
||||||
import Storage from './storage';
|
import Storage from './storage';
|
||||||
import { floppyNavigation } from './floppyNavigation';
|
import { floppyNavigation } from './floppyNavigation';
|
||||||
|
import { isIgnoredPath } from './guard';
|
||||||
|
|
||||||
const baseConfig = {
|
const baseConfig = {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface APIconfig extends AxiosRequestConfig {
|
||||||
|
allow404: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
instance: AxiosInstance;
|
instance: AxiosInstance;
|
||||||
|
|
||||||
|
@ -49,6 +54,9 @@ class Request {
|
||||||
(error) => {
|
(error) => {
|
||||||
const { status, data: respData } = error.response || {};
|
const { status, data: respData } = error.response || {};
|
||||||
const { data = {}, msg = '', reason = '' } = respData || {};
|
const { data = {}, msg = '', reason = '' } = respData || {};
|
||||||
|
|
||||||
|
console.log('response error:', error);
|
||||||
|
|
||||||
if (status === 400) {
|
if (status === 400) {
|
||||||
// show error message
|
// show error message
|
||||||
if (data instanceof Object && data.err_type) {
|
if (data instanceof Object && data.err_type) {
|
||||||
|
@ -99,12 +107,14 @@ class Request {
|
||||||
// 401: Re-login required
|
// 401: Re-login required
|
||||||
if (status === 401) {
|
if (status === 401) {
|
||||||
// clear userinfo
|
// clear userinfo
|
||||||
|
notFoundStore.getState().hide();
|
||||||
loggedUserInfoStore.getState().clear();
|
loggedUserInfoStore.getState().clear();
|
||||||
floppyNavigation.navigateToLogin();
|
floppyNavigation.navigateToLogin();
|
||||||
return Promise.reject(false);
|
return Promise.reject(false);
|
||||||
}
|
}
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
// Permission interception
|
// Permission interception
|
||||||
|
notFoundStore.getState().hide();
|
||||||
if (data?.type === 'url_expired') {
|
if (data?.type === 'url_expired') {
|
||||||
// url expired
|
// url expired
|
||||||
floppyNavigation.navigate(RouteAlias.activationFailed, () => {
|
floppyNavigation.navigate(RouteAlias.activationFailed, () => {
|
||||||
|
@ -135,6 +145,14 @@ class Request {
|
||||||
}
|
}
|
||||||
return Promise.reject(false);
|
return Promise.reject(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status === 404 && error.config?.allow404) {
|
||||||
|
if (isIgnoredPath(IGNORE_PATH_LIST)) {
|
||||||
|
return Promise.reject(false);
|
||||||
|
}
|
||||||
|
notFoundStore.getState().show();
|
||||||
|
return Promise.reject(false);
|
||||||
|
}
|
||||||
if (status >= 500) {
|
if (status >= 500) {
|
||||||
console.error(
|
console.error(
|
||||||
`Request failed with status code ${status}, ${msg || ''}`,
|
`Request failed with status code ${status}, ${msg || ''}`,
|
||||||
|
@ -149,7 +167,7 @@ class Request {
|
||||||
return this.instance.request(config);
|
return this.instance.request(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
public get<T = any>(url: string, config?: APIconfig): Promise<T> {
|
||||||
return this.instance.get(url, config);
|
return this.instance.get(url, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DRAFT_QUESTION_STORAGE_KEY,
|
||||||
|
DRAFT_ANSWER_STORAGE_KEY,
|
||||||
|
} from '@/common/constants';
|
||||||
|
import { storageExpires as storage } from '@/utils';
|
||||||
|
|
||||||
|
export type QuestionDraft = {
|
||||||
|
params: {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
tags: any[];
|
||||||
|
answer: string;
|
||||||
|
};
|
||||||
|
callback?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AnswerDraft = {
|
||||||
|
questionId: string;
|
||||||
|
content: string;
|
||||||
|
callback?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DraftType = {
|
||||||
|
type: 'question' | 'answer';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DraftParams = QuestionDraft | AnswerDraft;
|
||||||
|
|
||||||
|
class SaveDraft {
|
||||||
|
type: DraftType['type'];
|
||||||
|
|
||||||
|
status: 'save' | 'remove';
|
||||||
|
|
||||||
|
constructor({ type = 'question' }: DraftType) {
|
||||||
|
this.type = type;
|
||||||
|
this.status = 'save';
|
||||||
|
}
|
||||||
|
|
||||||
|
save = debounce((data: DraftParams) => {
|
||||||
|
// TODO
|
||||||
|
if (this.status === 'remove') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.type === 'question') {
|
||||||
|
const { params, callback } = data as QuestionDraft;
|
||||||
|
|
||||||
|
this.storeDraft(params, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.type === 'answer') {
|
||||||
|
const { content, questionId, callback } = data as AnswerDraft;
|
||||||
|
if (!questionId || !content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.storeDraft({ content, questionId }, callback);
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.status = 'remove';
|
||||||
|
const that = this;
|
||||||
|
if (this.type === 'question') {
|
||||||
|
storage.remove(DRAFT_QUESTION_STORAGE_KEY, () => {
|
||||||
|
that.status = 'save';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.type === 'answer') {
|
||||||
|
storage.remove(DRAFT_ANSWER_STORAGE_KEY, () => {
|
||||||
|
that.status = 'save';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private storeDraft = (params: any, callback) => {
|
||||||
|
const key =
|
||||||
|
this.type === 'question'
|
||||||
|
? DRAFT_QUESTION_STORAGE_KEY
|
||||||
|
: DRAFT_ANSWER_STORAGE_KEY;
|
||||||
|
storage.set(key, params);
|
||||||
|
callback?.();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SaveDraft;
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { DRAFT_TIMESIGH_STORAGE_KEY as timeSign } from '@/common/constants';
|
||||||
|
|
||||||
|
const store = {
|
||||||
|
storage: localStorage || window.localStorage,
|
||||||
|
set(key: string, value, time?: number): void {
|
||||||
|
const t = time || Date.now() + 1000 * 60 * 60 * 24 * 7; // default 7 days
|
||||||
|
try {
|
||||||
|
this.storage.setItem(key, `${t}${timeSign}${JSON.stringify(value)}`);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
console.error('set storage error: the key is', key);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get(key: string): any {
|
||||||
|
const timeSignLen = timeSign.length;
|
||||||
|
let index = 0;
|
||||||
|
let time = 0;
|
||||||
|
let res;
|
||||||
|
try {
|
||||||
|
res = this.storage.getItem(key);
|
||||||
|
} catch {
|
||||||
|
console.error('get storage error: the key is', key);
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
index = res.indexOf(timeSign);
|
||||||
|
time = +res.slice(0, index);
|
||||||
|
if (time > new Date().getTime()) {
|
||||||
|
res = res.slice(index + timeSignLen);
|
||||||
|
try {
|
||||||
|
res = JSON.parse(res);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// timeout remove storage
|
||||||
|
res = null;
|
||||||
|
this.storage.removeItem(key);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
remove(key: string, callback?: () => void): void {
|
||||||
|
this.storage.removeItem(key);
|
||||||
|
callback?.();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default store;
|
|
@ -3,28 +3,28 @@
|
||||||
<div class="justify-content-center row">
|
<div class="justify-content-center row">
|
||||||
<div class="col-xxl-7 col-lg-8 col-sm-12">
|
<div class="col-xxl-7 col-lg-8 col-sm-12">
|
||||||
<div class="d-flex flex-column flex-md-row mb-4">
|
<div class="d-flex flex-column flex-md-row mb-4">
|
||||||
<a href="/users/{{.userinfo.Info.Username}}"><img
|
<a href="/users/{{.userinfo.Username}}"><img
|
||||||
src="{{.userinfo.Info.Avatar}}"
|
src="{{.userinfo.Avatar}}"
|
||||||
width="160px" height="160px" class="rounded" alt="" /></a>
|
width="160px" height="160px" class="rounded" alt="" /></a>
|
||||||
<div class="ms-0 ms-md-4 mt-4 mt-md-0">
|
<div class="ms-0 ms-md-4 mt-4 mt-md-0">
|
||||||
<div class="d-flex align-items-center mb-2">
|
<div class="d-flex align-items-center mb-2">
|
||||||
<a class="link-dark h3 mb-0" href="/users/{{.userinfo.Info.Username}}">{{.userinfo.Info.DisplayName}}</a>
|
<a class="link-dark h3 mb-0" href="/users/{{.userinfo.Username}}">{{.userinfo.DisplayName}}</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-secondary mb-4">@{{.userinfo.Info.Username}}</div>
|
<div class="text-secondary mb-4">@{{.userinfo.Username}}</div>
|
||||||
<div class="d-flex flex-wrap mb-3">
|
<div class="d-flex flex-wrap mb-3">
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
<strong class="fs-5">{{.userinfo.Info.Rank}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_reputation"}}</span>
|
<strong class="fs-5">{{.userinfo.Rank}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_reputation"}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="me-3">
|
<div class="me-3">
|
||||||
<strong class="fs-5">{{.userinfo.Info.AnswerCount}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_answers"}}</span>
|
<strong class="fs-5">{{.userinfo.AnswerCount}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_answers"}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong class="fs-5">{{.userinfo.Info.QuestionCount}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_questions"}}</span>
|
<strong class="fs-5">{{.userinfo.QuestionCount}}</strong><span class="text-secondary"> {{translator $.language "ui.personal.x_questions"}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if .userinfo.Info.Website }}
|
{{if .userinfo.Website }}
|
||||||
<div class="d-flex align-items-center"><i class="br bi-house-door-fill me-2"></i><a class="link-secondary" href="{{.userinfo.Info.Website}}">{{.userinfo.Info.Website}}</a></div>
|
<div class="d-flex align-items-center"><i class="br bi-house-door-fill me-2"></i><a class="link-secondary" href="{{.userinfo.Website}}">{{.userinfo.Website}}</a></div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="d-flex text-secondary"></div>
|
<div class="d-flex text-secondary"></div>
|
||||||
|
|
Loading…
Reference in New Issue