Merge branch 'github-main' into feature-plugin
This commit is contained in:
@@ -12,7 +12,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, linux]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -17,7 +17,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, linux]
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [self-hosted, linux]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -8,8 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
runs-on: [self-hosted, linux]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
/go.work*
|
||||
/logs
|
||||
/ui/node_modules
|
||||
/ui/build/*/*/*
|
||||
/ui/build/*.json
|
||||
/ui/build/*.html
|
||||
/ui/build/*.txt
|
||||
/vendor
|
||||
Thumbs*.db
|
||||
tmp
|
||||
|
||||
+3
-35
@@ -16,50 +16,25 @@ builds:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
# linux windows need cgomingw64-gcc
|
||||
- id: build-windows
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
|
||||
env:
|
||||
- CC=x86_64-w64-mingw32-gcc
|
||||
- CXX=x86_64-w64-mingw32-g++
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
# linux arm64 need cgo arm64
|
||||
- id: build-arm64
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
|
||||
env:
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
- CXX=aarch64-linux-gnu-g++
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
- id: build-arm7
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser
|
||||
env:
|
||||
- CC=arm-linux-gnueabihf-gcc
|
||||
- CXX=arm-linux-gnueabihf-g++
|
||||
- AR=arm-linux-gnueabihf-ar
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm
|
||||
goarm:
|
||||
- 7
|
||||
- id: build-darwin-arm64
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
env:
|
||||
- CC=oa64-clang
|
||||
- CXX=oa64-clang++
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
@@ -69,9 +44,6 @@ builds:
|
||||
- id: build-darwin-amd64
|
||||
main: ./cmd/answer/.
|
||||
binary: answer
|
||||
env:
|
||||
- CC=o64-clang
|
||||
- CXX=o64-clang++
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
@@ -82,8 +54,10 @@ builds:
|
||||
|
||||
archives:
|
||||
- replacements:
|
||||
darwin: Darwin
|
||||
darwin: macOS
|
||||
amd64: x86_64
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
@@ -95,10 +69,4 @@ changelog:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
|
||||
|
||||
# sudo apt-get install build-essential
|
||||
# sudo apt-get install gcc-multilib g++-multilib
|
||||
# sudo apt-get install gcc-mingw-w64
|
||||
# sudo apt-get -y install gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf
|
||||
# sudo apt-get install clang llvm
|
||||
# goreleaser release --snapshot --rm-dist
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.PHONY: build clean ui
|
||||
|
||||
VERSION=1.0.3
|
||||
VERSION=1.0.4
|
||||
BIN=answer
|
||||
DIR_SRC=./cmd/answer
|
||||
DOCKER_CMD=docker
|
||||
@@ -23,6 +23,8 @@ universal: generate
|
||||
generate:
|
||||
@$(GO) get github.com/google/wire/cmd/wire@v0.5.0
|
||||
@$(GO) get github.com/golang/mock/mockgen@v1.6.0
|
||||
@$(GO) install github.com/google/wire/cmd/wire@v0.5.0
|
||||
@$(GO) install github.com/golang/mock/mockgen@v1.6.0
|
||||
@$(GO) generate ./...
|
||||
@$(GO) mod tidy
|
||||
|
||||
@@ -39,6 +41,6 @@ install-ui-packages:
|
||||
@corepack prepare pnpm@v7.12.2 --activate
|
||||
|
||||
ui:
|
||||
@cd ui && pnpm install && pnpm build && cd -
|
||||
@cd ui && pnpm install && pnpm build && sed -i 's/%AnswerVersion%/'$(VERSION)'/g' ./build/index.html && cd -
|
||||
|
||||
all: clean build
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to `security@answer.dev`
|
||||
+9
-50
@@ -5808,8 +5808,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"object_id",
|
||||
"original_text",
|
||||
"parsed_text"
|
||||
"original_text"
|
||||
],
|
||||
"properties": {
|
||||
"mention_username_list": {
|
||||
@@ -5825,11 +5824,9 @@ const docTemplate = `{
|
||||
},
|
||||
"original_text": {
|
||||
"description": "original comment content",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed comment content",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 600,
|
||||
"minLength": 2
|
||||
},
|
||||
"reply_comment_id": {
|
||||
"description": "reply comment id",
|
||||
@@ -5923,17 +5920,11 @@ const docTemplate = `{
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"description": "content",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"description": "question_id",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -5945,29 +5936,20 @@ const docTemplate = `{
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"description": "content",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"edit_summary": {
|
||||
"description": "edit_summary",
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "id",
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"description": "question_id",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "title",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -7142,7 +7124,6 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"html",
|
||||
"tags",
|
||||
"title"
|
||||
],
|
||||
@@ -7153,12 +7134,6 @@ const docTemplate = `{
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
@@ -7291,7 +7266,6 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"html",
|
||||
"id",
|
||||
"tags",
|
||||
"title"
|
||||
@@ -7307,12 +7281,6 @@ const docTemplate = `{
|
||||
"description": "edit summary",
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"id": {
|
||||
"description": "question id",
|
||||
"type": "string"
|
||||
@@ -7878,10 +7846,6 @@ const docTemplate = `{
|
||||
"description": "original text",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed text",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug_name",
|
||||
"type": "string",
|
||||
@@ -7979,7 +7943,8 @@ const docTemplate = `{
|
||||
"schema.UpdateCommentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment_id"
|
||||
"comment_id",
|
||||
"original_text"
|
||||
],
|
||||
"properties": {
|
||||
"comment_id": {
|
||||
@@ -7988,11 +7953,9 @@ const docTemplate = `{
|
||||
},
|
||||
"original_text": {
|
||||
"description": "original comment content",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed comment content",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 600,
|
||||
"minLength": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8138,10 +8101,6 @@ const docTemplate = `{
|
||||
"description": "original text",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed text",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug_name",
|
||||
"type": "string",
|
||||
|
||||
+9
-50
@@ -5796,8 +5796,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"object_id",
|
||||
"original_text",
|
||||
"parsed_text"
|
||||
"original_text"
|
||||
],
|
||||
"properties": {
|
||||
"mention_username_list": {
|
||||
@@ -5813,11 +5812,9 @@
|
||||
},
|
||||
"original_text": {
|
||||
"description": "original comment content",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed comment content",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 600,
|
||||
"minLength": 2
|
||||
},
|
||||
"reply_comment_id": {
|
||||
"description": "reply comment id",
|
||||
@@ -5911,17 +5908,11 @@
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"description": "content",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"description": "question_id",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -5933,29 +5924,20 @@
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"description": "content",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"edit_summary": {
|
||||
"description": "edit_summary",
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"description": "id",
|
||||
"type": "string"
|
||||
},
|
||||
"question_id": {
|
||||
"description": "question_id",
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"description": "title",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -7130,7 +7112,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"html",
|
||||
"tags",
|
||||
"title"
|
||||
],
|
||||
@@ -7141,12 +7122,6 @@
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"tags": {
|
||||
"description": "tags",
|
||||
"type": "array",
|
||||
@@ -7279,7 +7254,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"html",
|
||||
"id",
|
||||
"tags",
|
||||
"title"
|
||||
@@ -7295,12 +7269,6 @@
|
||||
"description": "edit summary",
|
||||
"type": "string"
|
||||
},
|
||||
"html": {
|
||||
"description": "html",
|
||||
"type": "string",
|
||||
"maxLength": 65535,
|
||||
"minLength": 6
|
||||
},
|
||||
"id": {
|
||||
"description": "question id",
|
||||
"type": "string"
|
||||
@@ -7866,10 +7834,6 @@
|
||||
"description": "original text",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed text",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug_name",
|
||||
"type": "string",
|
||||
@@ -7967,7 +7931,8 @@
|
||||
"schema.UpdateCommentReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment_id"
|
||||
"comment_id",
|
||||
"original_text"
|
||||
],
|
||||
"properties": {
|
||||
"comment_id": {
|
||||
@@ -7976,11 +7941,9 @@
|
||||
},
|
||||
"original_text": {
|
||||
"description": "original comment content",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed comment content",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"maxLength": 600,
|
||||
"minLength": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8126,10 +8089,6 @@
|
||||
"description": "original text",
|
||||
"type": "string"
|
||||
},
|
||||
"parsed_text": {
|
||||
"description": "parsed text",
|
||||
"type": "string"
|
||||
},
|
||||
"slug_name": {
|
||||
"description": "slug_name",
|
||||
"type": "string",
|
||||
|
||||
+5
-38
@@ -147,9 +147,8 @@ definitions:
|
||||
type: string
|
||||
original_text:
|
||||
description: original comment content
|
||||
type: string
|
||||
parsed_text:
|
||||
description: parsed comment content
|
||||
maxLength: 600
|
||||
minLength: 2
|
||||
type: string
|
||||
reply_comment_id:
|
||||
description: reply comment id
|
||||
@@ -157,7 +156,6 @@ definitions:
|
||||
required:
|
||||
- object_id
|
||||
- original_text
|
||||
- parsed_text
|
||||
type: object
|
||||
schema.AddReportReq:
|
||||
properties:
|
||||
@@ -217,15 +215,10 @@ definitions:
|
||||
schema.AnswerAddReq:
|
||||
properties:
|
||||
content:
|
||||
description: content
|
||||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
html:
|
||||
description: html
|
||||
type: string
|
||||
question_id:
|
||||
description: question_id
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
@@ -233,24 +226,16 @@ definitions:
|
||||
schema.AnswerUpdateReq:
|
||||
properties:
|
||||
content:
|
||||
description: content
|
||||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
edit_summary:
|
||||
description: edit_summary
|
||||
type: string
|
||||
html:
|
||||
description: html
|
||||
type: string
|
||||
id:
|
||||
description: id
|
||||
type: string
|
||||
question_id:
|
||||
description: question_id
|
||||
type: string
|
||||
title:
|
||||
description: title
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
@@ -1096,11 +1081,6 @@ definitions:
|
||||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
html:
|
||||
description: html
|
||||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
tags:
|
||||
description: tags
|
||||
items:
|
||||
@@ -1113,7 +1093,6 @@ definitions:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
- html
|
||||
- tags
|
||||
- title
|
||||
type: object
|
||||
@@ -1205,11 +1184,6 @@ definitions:
|
||||
edit_summary:
|
||||
description: edit summary
|
||||
type: string
|
||||
html:
|
||||
description: html
|
||||
maxLength: 65535
|
||||
minLength: 6
|
||||
type: string
|
||||
id:
|
||||
description: question id
|
||||
type: string
|
||||
@@ -1225,7 +1199,6 @@ definitions:
|
||||
type: string
|
||||
required:
|
||||
- content
|
||||
- html
|
||||
- id
|
||||
- tags
|
||||
- title
|
||||
@@ -1605,9 +1578,6 @@ definitions:
|
||||
original_text:
|
||||
description: original text
|
||||
type: string
|
||||
parsed_text:
|
||||
description: parsed text
|
||||
type: string
|
||||
slug_name:
|
||||
description: slug_name
|
||||
maxLength: 35
|
||||
@@ -1680,12 +1650,12 @@ definitions:
|
||||
type: string
|
||||
original_text:
|
||||
description: original comment content
|
||||
type: string
|
||||
parsed_text:
|
||||
description: parsed comment content
|
||||
maxLength: 600
|
||||
minLength: 2
|
||||
type: string
|
||||
required:
|
||||
- comment_id
|
||||
- original_text
|
||||
type: object
|
||||
schema.UpdateFollowTagsReq:
|
||||
properties:
|
||||
@@ -1787,9 +1757,6 @@ definitions:
|
||||
original_text:
|
||||
description: original text
|
||||
type: string
|
||||
parsed_text:
|
||||
description: parsed text
|
||||
type: string
|
||||
slug_name:
|
||||
description: slug_name
|
||||
maxLength: 35
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+128
-119
@@ -3,242 +3,244 @@
|
||||
backend:
|
||||
base:
|
||||
success:
|
||||
other: "Success."
|
||||
other: Success.
|
||||
unknown:
|
||||
other: "Unknown error."
|
||||
other: Unknown error.
|
||||
request_format_error:
|
||||
other: "Request format is not valid."
|
||||
other: Request format is not valid.
|
||||
unauthorized_error:
|
||||
other: "Unauthorized."
|
||||
other: Unauthorized.
|
||||
database_error:
|
||||
other: "Data server error."
|
||||
|
||||
other: Data server error.
|
||||
role:
|
||||
name:
|
||||
user:
|
||||
other: "User"
|
||||
other: User
|
||||
admin:
|
||||
other: "Admin"
|
||||
other: Admin
|
||||
moderator:
|
||||
other: "Moderator"
|
||||
other: Moderator
|
||||
description:
|
||||
user:
|
||||
other: "Default with no special access."
|
||||
other: Default with no special access.
|
||||
admin:
|
||||
other: "Have the full power to access the site."
|
||||
other: Have the full power to access the site.
|
||||
moderator:
|
||||
other: "Has access to all posts except admin settings."
|
||||
|
||||
other: Has access to all posts except admin settings.
|
||||
email:
|
||||
other: "Email"
|
||||
other: Email
|
||||
password:
|
||||
other: "Password"
|
||||
|
||||
email_or_password_wrong_error: &email_or_password_wrong
|
||||
other: "Email and password do not match."
|
||||
|
||||
other: Password
|
||||
email_or_password_wrong_error:
|
||||
other: Email and password do not match.
|
||||
error:
|
||||
admin:
|
||||
email_or_password_wrong: *email_or_password_wrong
|
||||
email_or_password_wrong:
|
||||
other: Email and password do not match.
|
||||
answer:
|
||||
not_found:
|
||||
other: "Answer do not found."
|
||||
other: Answer do not found.
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
other: No permission to delete.
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: No permission to update.
|
||||
comment:
|
||||
edit_without_permission:
|
||||
other: "Comment are not allowed to edit."
|
||||
other: Comment are not allowed to edit.
|
||||
not_found:
|
||||
other: "Comment not found."
|
||||
other: Comment not found.
|
||||
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:
|
||||
duplicate:
|
||||
other: "Email already exists."
|
||||
other: Email already exists.
|
||||
need_to_be_verified:
|
||||
other: "Email should be verified."
|
||||
other: Email should be verified.
|
||||
verify_url_expired:
|
||||
other: "Email verified URL has expired, please resend the email."
|
||||
other: Email verified URL has expired, please resend the email.
|
||||
lang:
|
||||
not_found:
|
||||
other: "Language file not found."
|
||||
other: Language file not found.
|
||||
object:
|
||||
captcha_verification_failed:
|
||||
other: "Captcha wrong."
|
||||
other: Captcha wrong.
|
||||
disallow_follow:
|
||||
other: "You are not allowed to follow."
|
||||
other: You are not allowed to follow.
|
||||
disallow_vote:
|
||||
other: "You are not allowed to vote."
|
||||
other: You are not allowed to vote.
|
||||
disallow_vote_your_self:
|
||||
other: "You can't vote for your own post."
|
||||
other: You can't vote for your own post.
|
||||
not_found:
|
||||
other: "Object not found."
|
||||
other: Object not found.
|
||||
verification_failed:
|
||||
other: "Verification failed."
|
||||
other: Verification failed.
|
||||
email_or_password_incorrect:
|
||||
other: "Email and password do not match."
|
||||
other: Email and password do not match.
|
||||
old_password_verification_failed:
|
||||
other: "The old password verification failed"
|
||||
other: The old password verification failed
|
||||
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:
|
||||
not_found:
|
||||
other: "Question not found."
|
||||
other: Question not found.
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
other: No permission to delete.
|
||||
cannot_close:
|
||||
other: "No permission to close."
|
||||
other: No permission to close.
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: No permission to update.
|
||||
rank:
|
||||
fail_to_meet_the_condition:
|
||||
other: "Rank fail to meet the condition."
|
||||
other: Rank fail to meet the condition.
|
||||
report:
|
||||
handle_failed:
|
||||
other: "Report handle failed."
|
||||
other: Report handle failed.
|
||||
not_found:
|
||||
other: "Report not found."
|
||||
other: Report not found.
|
||||
tag:
|
||||
not_found:
|
||||
other: "Tag not found."
|
||||
other: Tag not found.
|
||||
recommend_tag_not_found:
|
||||
other: "Recommend Tag is not exist."
|
||||
other: Recommend Tag is not exist.
|
||||
recommend_tag_enter:
|
||||
other: "Please enter at least one required tag."
|
||||
other: Please enter at least one required tag.
|
||||
not_contain_synonym_tags:
|
||||
other: "Should not contain synonym tags."
|
||||
other: Should not contain synonym tags.
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: No permission to update.
|
||||
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:
|
||||
config_from_name_cannot_be_email:
|
||||
other: "The From Name cannot be a email address."
|
||||
other: The From Name cannot be a email address.
|
||||
theme:
|
||||
not_found:
|
||||
other: "Theme not found."
|
||||
other: Theme not found.
|
||||
revision:
|
||||
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:
|
||||
other: "No permission to Revision."
|
||||
other: No permission to Revision.
|
||||
user:
|
||||
email_or_password_wrong:
|
||||
other: *email_or_password_wrong
|
||||
other:
|
||||
other: Email and password do not match.
|
||||
not_found:
|
||||
other: "User not found."
|
||||
other: User not found.
|
||||
suspended:
|
||||
other: "User has been suspended."
|
||||
other: User has been suspended.
|
||||
username_invalid:
|
||||
other: "Username is invalid."
|
||||
other: Username is invalid.
|
||||
username_duplicate:
|
||||
other: "Username is already in use."
|
||||
other: Username is already in use.
|
||||
set_avatar:
|
||||
other: "Avatar set failed."
|
||||
other: Avatar set failed.
|
||||
cannot_update_your_role:
|
||||
other: "You cannot modify your role."
|
||||
other: You cannot modify your role.
|
||||
not_allowed_registration:
|
||||
other: "Currently the site is not open for registration"
|
||||
other: Currently the site is not open for registration
|
||||
config:
|
||||
read_config_failed:
|
||||
other: "Read config failed"
|
||||
other: Read config failed
|
||||
database:
|
||||
connection_failed:
|
||||
other: "Database connection failed"
|
||||
other: Database connection failed
|
||||
create_table_failed:
|
||||
other: "Create table failed"
|
||||
other: Create table failed
|
||||
install:
|
||||
create_config_failed:
|
||||
other: "Can’t create the config.yaml file."
|
||||
other: Can't create the config.yaml file.
|
||||
report:
|
||||
spam:
|
||||
name:
|
||||
other: "spam"
|
||||
other: spam
|
||||
desc:
|
||||
other: "This post is an advertisement, or vandalism. It is not useful or relevant to the current topic."
|
||||
other: This post is an advertisement, or vandalism. It is not useful or relevant
|
||||
to the current topic.
|
||||
rude:
|
||||
name:
|
||||
other: "rude or abusive"
|
||||
other: rude or abusive
|
||||
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:
|
||||
name:
|
||||
other: "a duplicate"
|
||||
other: a duplicate
|
||||
desc:
|
||||
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:
|
||||
name:
|
||||
other: "not an answer"
|
||||
other: not an answer
|
||||
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:
|
||||
name:
|
||||
other: "no longer needed"
|
||||
other: no longer needed
|
||||
desc:
|
||||
other: "This comment is outdated, conversational or not relevant to this post."
|
||||
other: This comment is outdated, conversational or not relevant to this post.
|
||||
other:
|
||||
name:
|
||||
other: "something else"
|
||||
other: something else
|
||||
desc:
|
||||
other: "This post requires staff attention for another reason not listed above."
|
||||
|
||||
other: This post requires staff attention for another reason not listed above.
|
||||
question:
|
||||
close:
|
||||
duplicate:
|
||||
name:
|
||||
other: "spam"
|
||||
other: spam
|
||||
desc:
|
||||
other: "This question has been asked before and already has an answer."
|
||||
other: This question has been asked before and already has an answer.
|
||||
guideline:
|
||||
name:
|
||||
other: "a community-specific reason"
|
||||
other: a community-specific reason
|
||||
desc:
|
||||
other: "This question doesn't meet a community guideline."
|
||||
other: This question doesn't meet a community guideline.
|
||||
multiple:
|
||||
name:
|
||||
other: "needs details or clarity"
|
||||
other: needs details or clarity
|
||||
desc:
|
||||
other: "This question currently includes multiple questions in one. It should focus on one problem only."
|
||||
other: This question currently includes multiple questions in one. It should
|
||||
focus on one problem only.
|
||||
other:
|
||||
name:
|
||||
other: "something else"
|
||||
other: something else
|
||||
desc:
|
||||
other: "This post requires another reason not listed above."
|
||||
other: This post requires another reason not listed above.
|
||||
operation_type:
|
||||
asked:
|
||||
other: "asked"
|
||||
other: asked
|
||||
answered:
|
||||
other: "answered"
|
||||
other: answered
|
||||
modified:
|
||||
other: "modified"
|
||||
other: modified
|
||||
notification:
|
||||
action:
|
||||
update_question:
|
||||
other: "updated question"
|
||||
other: updated question
|
||||
answer_the_question:
|
||||
other: "answered question"
|
||||
other: answered question
|
||||
update_answer:
|
||||
other: "updated answer"
|
||||
other: updated answer
|
||||
accept_answer:
|
||||
other: "accepted answer"
|
||||
other: accepted answer
|
||||
comment_question:
|
||||
other: "commented question"
|
||||
other: commented question
|
||||
comment_answer:
|
||||
other: "commented answer"
|
||||
other: commented answer
|
||||
reply_to_you:
|
||||
other: "replied to you"
|
||||
other: replied to you
|
||||
mention_you:
|
||||
other: "mentioned you"
|
||||
other: mentioned you
|
||||
your_question_is_closed:
|
||||
other: "Your question has been closed"
|
||||
other: Your question has been closed
|
||||
your_question_was_deleted:
|
||||
other: "Your question has been deleted"
|
||||
other: Your question has been deleted
|
||||
your_answer_was_deleted:
|
||||
other: "Your answer has been deleted"
|
||||
other: Your answer has been deleted
|
||||
your_comment_was_deleted:
|
||||
other: "Your comment has been deleted"
|
||||
other: Your comment has been deleted
|
||||
|
||||
# The following fields are used for interface presentation(Front-end)
|
||||
ui:
|
||||
@@ -494,7 +496,7 @@ ui:
|
||||
btn_flag: Flag
|
||||
btn_save_edits: Save edits
|
||||
btn_cancel: Cancel
|
||||
show_more: Show more comment
|
||||
show_more: Show more comments
|
||||
tip_question: >-
|
||||
Use comments to ask for more information or suggest improvements. Avoid
|
||||
answering questions in comments.
|
||||
@@ -510,6 +512,8 @@ ui:
|
||||
label: Revision
|
||||
answer:
|
||||
label: Answer
|
||||
feedback:
|
||||
characters: content must be at least 6 characters in length.
|
||||
edit_summary:
|
||||
label: Edit Summary
|
||||
placeholder: >-
|
||||
@@ -640,7 +644,7 @@ ui:
|
||||
msg:
|
||||
empty: Email cannot be empty.
|
||||
change_email:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
btn_cancel: Cancel
|
||||
btn_update: Update email address
|
||||
send_success: >-
|
||||
@@ -681,12 +685,12 @@ ui:
|
||||
display_name:
|
||||
label: Display Name
|
||||
msg: Display name cannot be empty.
|
||||
msg_range: Display name up to 30 characters
|
||||
msg_range: Display name up to 30 characters.
|
||||
username:
|
||||
label: Username
|
||||
caption: People can mention you as "@username".
|
||||
msg: Username cannot be empty.
|
||||
msg_range: Username up to 30 characters
|
||||
msg_range: Username up to 30 characters.
|
||||
character: 'Must use the character set "a-z", "0-9", " - . _"'
|
||||
avatar:
|
||||
label: Profile Image
|
||||
@@ -774,6 +778,7 @@ ui:
|
||||
<p>Are you sure you want to add another answer?</p><p>You could use the
|
||||
edit link to refine and improve your existing answer, instead.</p>
|
||||
empty: Answer cannot be empty.
|
||||
characters: content must be at least 6 characters in length.
|
||||
reopen:
|
||||
title: Reopen this post
|
||||
content: Are you sure you want to reopen?
|
||||
@@ -840,7 +845,7 @@ ui:
|
||||
modal_confirm:
|
||||
title: Error...
|
||||
account_result:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
success: Your new account is confirmed; you will be redirected to the home page.
|
||||
link: Continue to homepage
|
||||
invalid: >-
|
||||
@@ -853,7 +858,7 @@ ui:
|
||||
unsubscribe:
|
||||
page_title: Unsubscribe
|
||||
success_title: Unsubscribe Successful
|
||||
success_desc: You have been successfully removed from this subscriber list and won’t receive any further emails from us.
|
||||
success_desc: You have been successfully removed from this subscriber list and won't receive any further emails from us.
|
||||
link: Change settings
|
||||
question:
|
||||
following_tags: Following Tags
|
||||
@@ -915,7 +920,7 @@ ui:
|
||||
title: Answer
|
||||
next: Next
|
||||
done: Done
|
||||
config_yaml_error: Can’t create the config.yaml file.
|
||||
config_yaml_error: Can't create the config.yaml file.
|
||||
lang:
|
||||
label: Please Choose a Language
|
||||
db_type:
|
||||
@@ -946,7 +951,7 @@ ui:
|
||||
desc: >-
|
||||
You can create the <1>config.yaml</1> file manually in the
|
||||
<1>/var/wwww/xxx/</1> directory and paste the following text into it.
|
||||
info: "After you’ve done that, click “Next” button."
|
||||
info: After you've done that, click "Next" button.
|
||||
site_information: Site Information
|
||||
admin_account: Admin Account
|
||||
site_name:
|
||||
@@ -996,7 +1001,11 @@ ui:
|
||||
db_failed: Database connection failed
|
||||
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.
|
||||
|
||||
counts:
|
||||
views: views
|
||||
votes: votes
|
||||
answers: answers
|
||||
accepted: Accepted
|
||||
page_404:
|
||||
desc: "Unfortunately, this page doesn't exist."
|
||||
back_home: Back to homepage
|
||||
@@ -1004,7 +1013,7 @@ ui:
|
||||
desc: The server encountered an error and could not complete your request.
|
||||
back_home: Back to homepage
|
||||
page_maintenance:
|
||||
desc: "We are under maintenance, we’ll be back soon."
|
||||
desc: "We are under maintenance, we'll be back soon."
|
||||
nav_menus:
|
||||
dashboard: Dashboard
|
||||
contents: Contents
|
||||
@@ -1138,7 +1147,7 @@ ui:
|
||||
password:
|
||||
label: Password
|
||||
text: The user will be logged out and need to login again.
|
||||
msg: Password must be at 8 - 32 characters in length.
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
user_modal:
|
||||
@@ -1147,13 +1156,13 @@ ui:
|
||||
fields:
|
||||
display_name:
|
||||
label: Display Name
|
||||
msg: display_name must be at 4 - 30 characters in length.
|
||||
msg: Display Name must be at 3-30 characters in length.
|
||||
email:
|
||||
label: Email
|
||||
msg: Email is not valid.
|
||||
password:
|
||||
label: Password
|
||||
msg: Password must be at 8 - 32 characters in length.
|
||||
msg: Password must be at 8-32 characters in length.
|
||||
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
@@ -1274,14 +1283,14 @@ ui:
|
||||
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:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
label: Favicon (optional)
|
||||
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:
|
||||
page_title: Legal
|
||||
terms_of_service:
|
||||
|
||||
+80
-54
@@ -2,7 +2,7 @@
|
||||
backend:
|
||||
base:
|
||||
success:
|
||||
other: "Success."
|
||||
other: "Completado."
|
||||
unknown:
|
||||
other: "Error desconocido."
|
||||
request_format_error:
|
||||
@@ -88,17 +88,17 @@ backend:
|
||||
other: "Sin permiso para actualizar."
|
||||
rank:
|
||||
fail_to_meet_the_condition:
|
||||
other: "Rank fail to meet the condition."
|
||||
other: "El rango no cumple la condición."
|
||||
report:
|
||||
handle_failed:
|
||||
other: "Report handle failed."
|
||||
other: "Error en el manejador del reporte."
|
||||
not_found:
|
||||
other: "Report not found."
|
||||
other: "Informe no encontrado."
|
||||
tag:
|
||||
not_found:
|
||||
other: "Etiqueta no encontrada."
|
||||
recommend_tag_not_found:
|
||||
other: "Recommend Tag is not exist."
|
||||
other: "Etiqueta recomendada no existe."
|
||||
recommend_tag_enter:
|
||||
other: "Por favor, introduce al menos una de las etiquetas requeridas."
|
||||
not_contain_synonym_tags:
|
||||
@@ -109,13 +109,13 @@ backend:
|
||||
other: "No se puede establecer como sinónimo de una etiqueta la propia etiqueta."
|
||||
smtp:
|
||||
config_from_name_cannot_be_email:
|
||||
other: "The From Name cannot be a email address."
|
||||
other: "¡Su nombre de usuario no puede ser una dirección de correo electrónico!"
|
||||
theme:
|
||||
not_found:
|
||||
other: "Tema no encontrado."
|
||||
revision:
|
||||
review_underway:
|
||||
other: "Can't edit currently, there is a version in the review queue."
|
||||
other: "No se puede editar actualmente, hay una versión en la cola de revisiones."
|
||||
no_permission:
|
||||
other: "Sin permiso para revisar."
|
||||
user:
|
||||
@@ -236,9 +236,30 @@ backend:
|
||||
#The following fields are used for interface presentation(Front-end)
|
||||
ui:
|
||||
how_to_format:
|
||||
title: How to Format
|
||||
title: Cómo formatear
|
||||
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">Para hacer enlaces</p>
|
||||
<pre class="mb-2">
|
||||
<code><https://url.com><br/><br/>[Title](https://url.com)
|
||||
</code>
|
||||
</pre>
|
||||
</li>
|
||||
<li><p class="mb-2">Colocar saltos de línea entre párrafos</p></li>
|
||||
<li><p class="mb-2"><em>_cursiva_</em> o **<strong>negrita</strong>**</p></li>
|
||||
<li><p class="mb-2">Indentar el código con 4 espacios</p></li>
|
||||
<li><p class="mb-2">Citar colocando <code>></code> al inicio de la línea</p></li>
|
||||
<li><p class="mb-2">Escape con backticks <code>`como _esto_`</code></p></li>
|
||||
<li><p class="mb-2">Crear barreras de código con backticks <code>`</code></p>
|
||||
<pre class="mb-0">
|
||||
<code>
|
||||
```<br/>
|
||||
código aquí<br/>
|
||||
```
|
||||
</code>
|
||||
</pre>
|
||||
</li>
|
||||
</ul>
|
||||
pagination:
|
||||
prev: Anterior
|
||||
next: Siguiente
|
||||
@@ -287,13 +308,13 @@ ui:
|
||||
chart:
|
||||
text: Gráfica
|
||||
flow_chart: Diagrama de flujo
|
||||
sequence_diagram: Sequence diagram
|
||||
class_diagram: Class diagram
|
||||
state_diagram: State diagram
|
||||
entity_relationship_diagram: Entity relationship diagram
|
||||
user_defined_diagram: User defined diagram
|
||||
sequence_diagram: Diagrama de secuencia
|
||||
class_diagram: Diagrama de clase
|
||||
state_diagram: Diagrama de estado
|
||||
entity_relationship_diagram: Diagrama de relación de entidad
|
||||
user_defined_diagram: Diagrama definido por el usuario
|
||||
gantt_chart: Diagrama de Gantt
|
||||
pie_chart: Pie chart
|
||||
pie_chart: Grafico de torta
|
||||
code:
|
||||
text: Código
|
||||
add_code: Añadir código
|
||||
@@ -381,7 +402,7 @@ ui:
|
||||
heading: Encabezado
|
||||
cell: Celda
|
||||
close_modal:
|
||||
title: I am closing this post as...
|
||||
title: Estoy cerrando este post como...
|
||||
btn_cancel: Cancelar
|
||||
btn_submit: Enviar
|
||||
remark:
|
||||
@@ -389,11 +410,11 @@ ui:
|
||||
msg:
|
||||
empty: Por favor selecciona una razón.
|
||||
report_modal:
|
||||
flag_title: I am flagging to report this post as...
|
||||
close_title: I am closing this post as...
|
||||
review_question_title: Review question
|
||||
review_answer_title: Review answer
|
||||
review_comment_title: Review comment
|
||||
flag_title: Estoy marcando este post como...
|
||||
close_title: Estoy cerrando este post como...
|
||||
review_question_title: Revisar pregunta
|
||||
review_answer_title: Revisar respuesta
|
||||
review_comment_title: Revisar comentario
|
||||
btn_cancel: Cancelar
|
||||
btn_submit: Enviar
|
||||
remark:
|
||||
@@ -408,7 +429,7 @@ ui:
|
||||
label: Nombre a mostrar
|
||||
msg:
|
||||
empty: El nombre a mostrar no puede estar vacío.
|
||||
range: Display name up to 35 characters.
|
||||
range: Nombre a mostrar con un máximo de 35 caracteres.
|
||||
slug_name:
|
||||
label: URL amigable
|
||||
desc: 'Debe usar el conjunto de caracteres "a-z", "0-9", "+ # - ."'
|
||||
@@ -426,16 +447,16 @@ ui:
|
||||
history: Historial
|
||||
synonyms:
|
||||
title: Sinónimos
|
||||
text: The following tags will be remapped to
|
||||
text: Las siguientes etiquetas serán reasignadas a
|
||||
empty: No se encontraron sinónimos.
|
||||
btn_add: Añadir un sinónimo
|
||||
btn_edit: Editar
|
||||
btn_save: Guardar
|
||||
synonyms_text: The following tags will be remapped to
|
||||
synonyms_text: Las siguientes etiquetas serán reasignadas a
|
||||
delete:
|
||||
title: Eliminar esta etiqueta
|
||||
content: >-
|
||||
<p>We do not allowed deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
||||
<p>No se permite la eliminación de etiquetas con posts.</p><p>Por favor antes elimina esta etiqueta del post.</p>
|
||||
content2: '¿Estás seguro de que deseas borrarlo?'
|
||||
close: Cerrar
|
||||
edit_tag:
|
||||
@@ -508,16 +529,16 @@ ui:
|
||||
tag_label: preguntas
|
||||
search_placeholder: Filtrar por nombre de etiqueta
|
||||
no_desc: La etiqueta no tiene descripción.
|
||||
more: More
|
||||
more: Mas
|
||||
ask:
|
||||
title: Add Question
|
||||
edit_title: Edit Question
|
||||
default_reason: Edit question
|
||||
title: Agregar una pregunta
|
||||
edit_title: Editar pregunta
|
||||
default_reason: Editar pregunta
|
||||
similar_questions: Preguntas similares
|
||||
form:
|
||||
fields:
|
||||
revision:
|
||||
label: Revision
|
||||
label: Revisión
|
||||
title:
|
||||
label: Título
|
||||
placeholder: Sé preciso e imagina que le estás preguntando esto a una persona
|
||||
@@ -585,7 +606,8 @@ ui:
|
||||
msg:
|
||||
empty: No puede estar en blanco.
|
||||
login:
|
||||
page_title: Bienvenido a Answer
|
||||
page_title: Bienvenido a {{site_name}}
|
||||
login_to_continue: Inicia sesión para continuar
|
||||
info_sign: '¿No tienes cuenta? <1>Regístrate</1>'
|
||||
info_login: '¿Ya tienes una cuenta? <1>Inicia sesión</1>'
|
||||
agreements: Al registrarte, aceptas la <1>política de privacidad</1> y los <3>términos de servicio</3>.
|
||||
@@ -666,7 +688,7 @@ ui:
|
||||
custom: Propia
|
||||
btn_refresh: Actualizar
|
||||
custom_text: Puedes subir tu propia imagen.
|
||||
default: System
|
||||
default: Sistema
|
||||
msg: Por favor, sube una imagen
|
||||
bio:
|
||||
label: Sobre mí (opcional)
|
||||
@@ -693,11 +715,11 @@ ui:
|
||||
msg: El correo electrónico no puede estar vacío.
|
||||
password_title: Password
|
||||
current_pass:
|
||||
label: Current Password
|
||||
label: Contraseña actual
|
||||
msg:
|
||||
empty: Current Password cannot be empty.
|
||||
length: The length needs to be between 8 and 32.
|
||||
different: The two entered passwords do not match.
|
||||
empty: La contraseña actual no puede estar vacía.
|
||||
length: El largo necesita estar entre 8 y 32 caracteres.
|
||||
different: Las contraseñas no coinciden.
|
||||
new_pass:
|
||||
label: Nueva Contraseña
|
||||
pass_confirm:
|
||||
@@ -712,7 +734,7 @@ ui:
|
||||
update_password: Contraseña cambiada con éxito.
|
||||
flag_success: Gracias por reportar.
|
||||
forbidden_operate_self: No puedes modificar tu propio usuario
|
||||
review: Your revision will show after review.
|
||||
review: Tu revisión será visible luego de ser aprobada.
|
||||
related_question:
|
||||
title: Preguntas relacionadas
|
||||
btn: Añadir pregunta
|
||||
@@ -726,7 +748,7 @@ ui:
|
||||
Follow: Seguir
|
||||
Following: Siguiendo
|
||||
answered: respondida
|
||||
closed_in: Closed in
|
||||
closed_in: Cerrado el
|
||||
show_exist: Mostrar una pregunta existente.
|
||||
answers:
|
||||
title: Respuestas
|
||||
@@ -750,9 +772,13 @@ ui:
|
||||
delete:
|
||||
title: Eliminar esta publicación
|
||||
question: >-
|
||||
We do not recommend <strong>deleting questions with answers</strong> because doing so deprives future readers of this knowledge.</p><p>Repeated deletion of answered questions can result in your account being blocked from asking. Are you sure you wish to delete?
|
||||
No recomendamos <strong>borrar preguntas con respuestas</strong> porque esto priva a los lectores futuros de este conocimiento.
|
||||
</p><p>
|
||||
El borrado repetido de preguntas respondidas puede resultar en que tu cuenta se bloquee para hacer preguntas. ¿Estás seguro de que deseas borrarlo?
|
||||
answer_accepted: >-
|
||||
<p>We do not recommend <strong>deleting accepted answer</strong> because doing so deprives future readers of this knowledge. </p> Repeated deletion of accepted answers can result in your account being blocked from answering. Are you sure you wish to delete?
|
||||
<p>No recomendamos <strong>borrar la respuesta aceptada</strong> porque esto priva a los lectores futuros de este conocimiento.</p>
|
||||
|
||||
El borrado repetido de respuestas aceptadas puede resultar en que tu cuenta se bloquee para responder. ¿Estás seguro de que deseas borrarlo?
|
||||
other: '¿Estás seguro de que deseas borrarlo?'
|
||||
tip_question_deleted: Esta publicación ha sido eliminada
|
||||
tip_answer_deleted: Esta respuesta ha sido eliminada
|
||||
@@ -782,13 +808,13 @@ ui:
|
||||
newest: Más reciente
|
||||
active: Activas
|
||||
score: Puntuación
|
||||
more: More
|
||||
more: Mas
|
||||
tips:
|
||||
title: Advanced Search Tips
|
||||
tag: "<1>[tag]</1> search withing a tag"
|
||||
user: "<1>user:username</1> search by author"
|
||||
answer: "<1>answers:0</1> unanswered questions"
|
||||
score: "<1>score:3</1> posts with a 3+ score"
|
||||
title: Consejos de búsqueda avanzada
|
||||
tag: "<1>[tag]</1> búsqueda por etiquetas"
|
||||
user: "<1>user:username</1> búsqueda por autor"
|
||||
answer: "<1>answers:0</1> preguntas sin responder"
|
||||
score: "<1>score:3</1> Publicaciones con un puntaje de 3 o más"
|
||||
question: "<1>is:question</1> buscar preguntas"
|
||||
is_answer: "<1>is:answer</1> buscar respuestas"
|
||||
empty: No pudimos encontrar nada. <br /> Prueba a buscar con palabras diferentes o menos específicas.
|
||||
@@ -810,14 +836,14 @@ ui:
|
||||
Lo sentimos, este link de confirmación de cuenta ya no es válido. ¿Quizás tu cuenta ya está activa?
|
||||
confirm_new_email: Tu email ha sido actualizado.
|
||||
confirm_new_email_invalid: >-
|
||||
Sorry, this confirmation link is no longer valid. Perhaps your email was already changed?
|
||||
Lo siento, este enlace de confirmación ya no es válido. ¿Quizás ya se haya cambiado tu correo electrónico?
|
||||
unsubscribe:
|
||||
page_title: Desuscribir
|
||||
success_title: Desuscrito con éxito
|
||||
success_desc: Has sido eliminado con éxito de esta lista de suscriptores y no recibirás más correos electrónicos nuestros.
|
||||
link: Cambiar ajustes
|
||||
question:
|
||||
following_tags: Following Tags
|
||||
following_tags: Etiquetas seguidas
|
||||
edit: Editar
|
||||
save: Guardar
|
||||
follow_tag_tip: Sigue etiquetas para personalizar tu lista de preguntas.
|
||||
@@ -828,8 +854,8 @@ ui:
|
||||
questions: Preguntas
|
||||
answers: Respuestas
|
||||
newest: Más reciente
|
||||
active: Active
|
||||
frequent: Frequent
|
||||
active: Activo
|
||||
frequent: Frecuente
|
||||
score: Puntuación
|
||||
unanswered: Sin respuesta
|
||||
modified: modificada
|
||||
@@ -852,8 +878,8 @@ ui:
|
||||
score: Puntuación
|
||||
edit_profile: Editar perfil
|
||||
visited_x_days: "Visitado {{ count }} días"
|
||||
viewed: Viewed
|
||||
joined: Joined
|
||||
viewed: Visto
|
||||
joined: Unido
|
||||
last_login: Seen
|
||||
about_me: Sobre mí
|
||||
about_me_empty: "// ¡Hola Mundo!"
|
||||
@@ -1222,14 +1248,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+393
-392
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -1,26 +1,26 @@
|
||||
# all support language
|
||||
language_options:
|
||||
- label: "English(US)"
|
||||
value: "en_US"
|
||||
- label: "Español(ES)"
|
||||
value: "es_ES"
|
||||
- label: "Português(PT)"
|
||||
value: "pt_PT"
|
||||
- label: "Deutsch(DE)"
|
||||
value: "de_DE"
|
||||
- label: "Français(FR)"
|
||||
value: "fr_FR"
|
||||
- label: "日本語(JA)"
|
||||
value: "ja_JP"
|
||||
- label: "Italiano(IT)"
|
||||
value: "it_IT"
|
||||
- label: "Русский(RU)"
|
||||
value: "ru_RU"
|
||||
- label: "简体中文(CN)"
|
||||
value: "zh_CN"
|
||||
- label: "繁體中文(CN)"
|
||||
value: "zh_TW"
|
||||
- label: "English(US)"
|
||||
value: "en_US"
|
||||
- label: "Deutsch(DE)"
|
||||
value: "de_DE"
|
||||
- label: "Español(ES)"
|
||||
value: "es_ES"
|
||||
- label: "Français(FR)"
|
||||
value: "fr_FR"
|
||||
- label: "Italiano(IT)"
|
||||
value: "it_IT"
|
||||
- label: "日本語(JA)"
|
||||
value: "ja_JP"
|
||||
- label: "한국어(KO)"
|
||||
value: "ko_KR"
|
||||
- label: "Português(PT)"
|
||||
value: "pt_PT"
|
||||
- label: "Русский(RU)"
|
||||
value: "ru_RU"
|
||||
- label: "Tiếng Việt(VI)"
|
||||
value: "vi_VN"
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+5
-4
@@ -129,7 +129,7 @@ backend:
|
||||
username_invalid:
|
||||
other: "utente non valido"
|
||||
username_duplicate:
|
||||
other: "utente già in uso"
|
||||
other: "Nome utente già in uso"
|
||||
set_avatar:
|
||||
other: "Inserimento dell'Avatar non riuscito."
|
||||
cannot_update_your_role:
|
||||
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+4
-3
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+4
-3
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+152
-151
@@ -2,122 +2,122 @@
|
||||
backend:
|
||||
base:
|
||||
success:
|
||||
other: "Success."
|
||||
other: "Выполнено."
|
||||
unknown:
|
||||
other: "Unknown error."
|
||||
other: "Неизвестная ошибка."
|
||||
request_format_error:
|
||||
other: "Request format is not valid."
|
||||
other: "Формат файла не корректен."
|
||||
unauthorized_error:
|
||||
other: "Unauthorized."
|
||||
other: "Авторизация не выполнена."
|
||||
database_error:
|
||||
other: "Data server error."
|
||||
other: "Ошибка сервера данных."
|
||||
role:
|
||||
name:
|
||||
user:
|
||||
other: "User"
|
||||
other: "Пользователь"
|
||||
admin:
|
||||
other: "Admin"
|
||||
other: "Администратор"
|
||||
moderator:
|
||||
other: "Moderator"
|
||||
other: "Модератор"
|
||||
description:
|
||||
user:
|
||||
other: "Default with no special access."
|
||||
other: "По умолчанию, без специального доступа."
|
||||
admin:
|
||||
other: "Have the full power to access the site."
|
||||
other: "Имейте все полномочия для доступа к сайту."
|
||||
moderator:
|
||||
other: "Has access to all posts except admin settings."
|
||||
other: "Имеет доступ ко всем сообщениям, кроме настроек администратора."
|
||||
email:
|
||||
other: "Email"
|
||||
other: "Эл. почта"
|
||||
password:
|
||||
other: "Password"
|
||||
other: "Пароль"
|
||||
email_or_password_wrong_error:
|
||||
other: "Email and password do not match."
|
||||
other: "Неверное имя пользователя или пароль."
|
||||
error:
|
||||
admin:
|
||||
email_or_password_wrong:
|
||||
other: Email and password do not match.
|
||||
other: Неверное имя пользователя или пароль.
|
||||
answer:
|
||||
not_found:
|
||||
other: "Answer do not found."
|
||||
other: "Ответ не найден."
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
other: "Недостаточно прав для удаления."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: "Нет прав для обновления."
|
||||
comment:
|
||||
edit_without_permission:
|
||||
other: "Comment are not allowed to edit."
|
||||
other: "Комментарий не может редактироваться."
|
||||
not_found:
|
||||
other: "Comment not found."
|
||||
other: "Комментарий не найден."
|
||||
email:
|
||||
duplicate:
|
||||
other: "Email already exists."
|
||||
other: "Адрес электронной почты уже существует."
|
||||
need_to_be_verified:
|
||||
other: "Email should be verified."
|
||||
other: "Адрес электронной почты должен быть подтвержден."
|
||||
verify_url_expired:
|
||||
other: "Email verified URL has expired, please resend the email."
|
||||
other: "Срок действия подтверждённого адреса электронной почты истек, пожалуйста, отправьте письмо повторно."
|
||||
lang:
|
||||
not_found:
|
||||
other: "Language file not found."
|
||||
other: "Языковой файл не найден."
|
||||
object:
|
||||
captcha_verification_failed:
|
||||
other: "Captcha wrong."
|
||||
other: "Captcha введена неверно."
|
||||
disallow_follow:
|
||||
other: "You are not allowed to follow."
|
||||
other: "Вы не можете подписаться."
|
||||
disallow_vote:
|
||||
other: "You are not allowed to vote."
|
||||
other: "Вы не можете голосовать."
|
||||
disallow_vote_your_self:
|
||||
other: "You can't vote for your own post."
|
||||
other: "Вы не можете голосовать за собственный отзыв."
|
||||
not_found:
|
||||
other: "Object not found."
|
||||
other: "Объект не найден."
|
||||
verification_failed:
|
||||
other: "Verification failed."
|
||||
other: "Проверка не удалась."
|
||||
email_or_password_incorrect:
|
||||
other: "Email and password do not match."
|
||||
other: "Email или пароль не совпадают."
|
||||
old_password_verification_failed:
|
||||
other: "The old password verification failed"
|
||||
other: "Не удалось подтвердить старый пароль"
|
||||
new_password_same_as_previous_setting:
|
||||
other: "The new password is the same as the previous one."
|
||||
other: "Пароль не может быть таким же как прежний."
|
||||
question:
|
||||
not_found:
|
||||
other: "Question not found."
|
||||
other: "Вопрос не найден."
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
other: "Недостаточно прав для удаления."
|
||||
cannot_close:
|
||||
other: "No permission to close."
|
||||
other: "Нет разрешения на закрытие."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: "Нет разрешения на обновление."
|
||||
rank:
|
||||
fail_to_meet_the_condition:
|
||||
other: "Rank fail to meet the condition."
|
||||
other: "Ранг не соответствует условию."
|
||||
report:
|
||||
handle_failed:
|
||||
other: "Report handle failed."
|
||||
other: "Не удалось обработать отчет."
|
||||
not_found:
|
||||
other: "Report not found."
|
||||
other: "Отчет не найден."
|
||||
tag:
|
||||
not_found:
|
||||
other: "Tag not found."
|
||||
other: "Тег не найден."
|
||||
recommend_tag_not_found:
|
||||
other: "Recommend Tag is not exist."
|
||||
other: "Рекомендуемый тег не существует."
|
||||
recommend_tag_enter:
|
||||
other: "Please enter at least one required tag."
|
||||
other: "Пожалуйста, введите хотя бы один тег."
|
||||
not_contain_synonym_tags:
|
||||
other: "Should not contain synonym tags."
|
||||
other: "Не должно содержать теги синонимы."
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: "Нет прав для обновления."
|
||||
cannot_set_synonym_as_itself:
|
||||
other: "You cannot set the synonym of the current tag as itself."
|
||||
other: "Вы не можете установить синоним текущего тега."
|
||||
smtp:
|
||||
config_from_name_cannot_be_email:
|
||||
other: "The From Name cannot be a email address."
|
||||
other: "Имя пользователя не может быть адрес электронной почты."
|
||||
theme:
|
||||
not_found:
|
||||
other: "Theme not found."
|
||||
other: "Тема не найдена."
|
||||
revision:
|
||||
review_underway:
|
||||
other: "Can't edit currently, there is a version in the review queue."
|
||||
other: "В настоящее время не удается редактировать версию, в очереди на проверку."
|
||||
no_permission:
|
||||
other: "No permission to Revision."
|
||||
other: "Нет прав на ревизию."
|
||||
user:
|
||||
email_or_password_wrong:
|
||||
other:
|
||||
@@ -241,68 +241,68 @@ ui:
|
||||
<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:
|
||||
prev: Prev
|
||||
next: Next
|
||||
next: Следующий
|
||||
page_title:
|
||||
question: Question
|
||||
questions: Questions
|
||||
tag: Tag
|
||||
tags: Tags
|
||||
tag_wiki: tag wiki
|
||||
edit_tag: Edit Tag
|
||||
ask_a_question: Add Question
|
||||
edit_question: Edit Question
|
||||
edit_answer: Edit Answer
|
||||
search: Search
|
||||
posts_containing: Posts containing
|
||||
settings: Settings
|
||||
notifications: Notifications
|
||||
login: Log In
|
||||
sign_up: Sign Up
|
||||
account_recovery: Account Recovery
|
||||
account_activation: Account Activation
|
||||
confirm_email: Confirm Email
|
||||
account_suspended: Account Suspended
|
||||
admin: Admin
|
||||
change_email: Modify Email
|
||||
install: Answer Installation
|
||||
upgrade: Answer Upgrade
|
||||
maintenance: Website Maintenance
|
||||
users: Users
|
||||
question: Вопрос
|
||||
questions: Вопросы
|
||||
tag: Тэг
|
||||
tags: Теги
|
||||
tag_wiki: wiki тэг
|
||||
edit_tag: Изменить тег
|
||||
ask_a_question: Добавить вопрос
|
||||
edit_question: Редактировать вопрос
|
||||
edit_answer: Редактировать ответ
|
||||
search: Поиск
|
||||
posts_containing: Посты содержащие
|
||||
settings: Настройки
|
||||
notifications: Уведомления
|
||||
login: Вход
|
||||
sign_up: Регистрация
|
||||
account_recovery: Восстановление аккаунта
|
||||
account_activation: Активация учётной записи
|
||||
confirm_email: Подтвердить адрес электронной почты
|
||||
account_suspended: Аккаунт заблокирован
|
||||
admin: Управление
|
||||
change_email: Изменить Email
|
||||
install: Установка ответа
|
||||
upgrade: Обновить ответ
|
||||
maintenance: Обслуживание сайта
|
||||
users: Пользователи
|
||||
notifications:
|
||||
title: Notifications
|
||||
inbox: Inbox
|
||||
achievement: Achievements
|
||||
all_read: Mark all as read
|
||||
show_more: Show more
|
||||
title: Уведомления
|
||||
inbox: Входящие
|
||||
achievement: Достижения
|
||||
all_read: Отметить всё как прочитанное
|
||||
show_more: Показать еще
|
||||
suspended:
|
||||
title: Your Account has been Suspended
|
||||
until_time: "Your account was suspended until {{ time }}."
|
||||
forever: This user was suspended forever.
|
||||
end: You don't meet a community guideline.
|
||||
title: Ваш аккаунт заблокирован
|
||||
until_time: "Ваша учетная запись была заблокирована до {{ time }}."
|
||||
forever: Этот пользователь был навсегда заблокирован.
|
||||
end: Вы не соответствуете правилам сообщества.
|
||||
editor:
|
||||
blockquote:
|
||||
text: Blockquote
|
||||
text: Цитата
|
||||
bold:
|
||||
text: Strong
|
||||
text: Жирный
|
||||
chart:
|
||||
text: Chart
|
||||
flow_chart: Flow chart
|
||||
sequence_diagram: Sequence diagram
|
||||
class_diagram: Class diagram
|
||||
state_diagram: State diagram
|
||||
entity_relationship_diagram: Entity relationship diagram
|
||||
user_defined_diagram: User defined diagram
|
||||
gantt_chart: Gantt chart
|
||||
pie_chart: Pie chart
|
||||
text: Диаграмма
|
||||
flow_chart: Блок-схема
|
||||
sequence_diagram: Диаграмма последовательности
|
||||
class_diagram: Диаграмма классов
|
||||
state_diagram: Диаграмма состояний
|
||||
entity_relationship_diagram: Диаграмма связей сущностей
|
||||
user_defined_diagram: Пользовательская диаграмма
|
||||
gantt_chart: Диаграмма Гантта
|
||||
pie_chart: Круговая диаграмма
|
||||
code:
|
||||
text: Code Sample
|
||||
add_code: Add code sample
|
||||
text: Фрагмент кода
|
||||
add_code: Добавить пример кода
|
||||
form:
|
||||
fields:
|
||||
code:
|
||||
label: Code
|
||||
label: Код
|
||||
msg:
|
||||
empty: Code cannot be empty.
|
||||
empty: Код не может быть пустым.
|
||||
language:
|
||||
label: Language (optional)
|
||||
placeholder: Automatic detection
|
||||
@@ -385,79 +385,79 @@ ui:
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
remark:
|
||||
empty: Cannot be empty.
|
||||
empty: Не может быть пустым.
|
||||
msg:
|
||||
empty: Please select a reason.
|
||||
empty: Пожалуйста, выбери причину.
|
||||
report_modal:
|
||||
flag_title: I am flagging to report this post as...
|
||||
close_title: I am closing this post as...
|
||||
review_question_title: Review question
|
||||
review_answer_title: Review answer
|
||||
review_comment_title: Review comment
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
flag_title: Я помечаю, чтобы пожаловаться на эту публикацию как...
|
||||
close_title: Я закрываю этот пост как...
|
||||
review_question_title: Проверить вопрос
|
||||
review_answer_title: Проверить ответ
|
||||
review_comment_title: Просмотр комментариев
|
||||
btn_cancel: Отмена
|
||||
btn_submit: Отправить
|
||||
remark:
|
||||
empty: Cannot be empty.
|
||||
empty: Не может быть пустым.
|
||||
msg:
|
||||
empty: Please select a reason.
|
||||
empty: Пожалуйста, выбери причину.
|
||||
tag_modal:
|
||||
title: Create new tag
|
||||
title: Создать новый тег
|
||||
form:
|
||||
fields:
|
||||
display_name:
|
||||
label: Display Name
|
||||
label: Показывать имя
|
||||
msg:
|
||||
empty: Display name cannot be empty.
|
||||
range: Display name up to 35 characters.
|
||||
empty: Отображаемое название не может быть пустым.
|
||||
range: Отображаемое имя до 35 символов.
|
||||
slug_name:
|
||||
label: URL Slug
|
||||
desc: 'Must use the character set "a-z", "0-9", "+ # - ."'
|
||||
label: Идентификатор URL
|
||||
desc: 'Необходимо использовать набор символов "a-z", "0-9", "+ # - ."'
|
||||
msg:
|
||||
empty: URL slug cannot be empty.
|
||||
range: URL slug up to 35 characters.
|
||||
character: URL slug contains unallowed character set.
|
||||
empty: URL не может быть пустым.
|
||||
range: URL slug до 35 символов.
|
||||
character: URL slug содержит недопустимый набор символов.
|
||||
desc:
|
||||
label: Description (optional)
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
label: Описание (опционально)
|
||||
btn_cancel: Отмена
|
||||
btn_submit: Отправить
|
||||
tag_info:
|
||||
created_at: Created
|
||||
edited_at: Edited
|
||||
history: History
|
||||
created_at: Создано
|
||||
edited_at: Отредактировано
|
||||
history: История
|
||||
synonyms:
|
||||
title: Synonyms
|
||||
text: The following tags will be remapped to
|
||||
empty: No synonyms found.
|
||||
btn_add: Add a synonym
|
||||
btn_edit: Edit
|
||||
btn_save: Save
|
||||
synonyms_text: The following tags will be remapped to
|
||||
title: Синонимы
|
||||
text: Следующие теги будут переназначены на
|
||||
empty: Синонимы не найдены.
|
||||
btn_add: Добавить синоним
|
||||
btn_edit: Редактировать
|
||||
btn_save: Сохранить
|
||||
synonyms_text: Следующие теги будут переназначены на
|
||||
delete:
|
||||
title: Delete this tag
|
||||
title: Удалить этот тег
|
||||
content: >-
|
||||
<p>We do not allowed deleting tag with posts.</p><p>Please remove this tag from the posts first.</p>
|
||||
content2: Are you sure you wish to delete?
|
||||
close: Close
|
||||
<p>Мы не разрешаем удалять тег с сообщениями.</p><p>Пожалуйста, сначала удалите этот тег из сообщений</p>
|
||||
content2: Вы действительно хотите удалить?
|
||||
close: Закрыть
|
||||
edit_tag:
|
||||
title: Edit Tag
|
||||
default_reason: Edit tag
|
||||
title: Изменить тег
|
||||
default_reason: Правка тега
|
||||
form:
|
||||
fields:
|
||||
revision:
|
||||
label: Revision
|
||||
label: Редакция
|
||||
display_name:
|
||||
label: Display Name
|
||||
label: Показывать имя
|
||||
slug_name:
|
||||
label: URL Slug
|
||||
info: 'Must use the character set "a-z", "0-9", "+ # - ."'
|
||||
label: Идентификатор URL
|
||||
info: 'Необходимо использовать набор символов "a-z", "0-9", "+ # - ."'
|
||||
desc:
|
||||
label: Description
|
||||
label: Описание
|
||||
edit_summary:
|
||||
label: Edit Summary
|
||||
label: Изменить краткое описание
|
||||
placeholder: >-
|
||||
Briefly explain your changes (corrected spelling, fixed grammar, improved formatting)
|
||||
btn_save_edits: Save edits
|
||||
btn_cancel: Cancel
|
||||
Кратко опишите ваши изменения (исправленная орфография, исправленная грамматика, улучшенное форматирование)
|
||||
btn_save_edits: Сохранить изменения
|
||||
btn_cancel: Отмена
|
||||
dates:
|
||||
long_date: MMM D
|
||||
long_date_with_year: "MMM D, YYYY"
|
||||
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+1353
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
+18
-17
@@ -405,17 +405,17 @@ ui:
|
||||
form:
|
||||
fields:
|
||||
display_name:
|
||||
label: 显示名称(别名)
|
||||
label: 显示名称
|
||||
msg:
|
||||
empty: 不能为空
|
||||
range: 不能超过 35 个字符
|
||||
empty: 显示名称不能为空。
|
||||
range: 显示名称不能超过 35 个字符。
|
||||
slug_name:
|
||||
label: URL 固定链接
|
||||
desc: '必须使用字符集 "a-z"、"0-9"、"+ # - ."'
|
||||
desc: '必须由 "a-z", "0-9", "+ # - ." 组成'
|
||||
msg:
|
||||
empty: 不能为空
|
||||
range: 不能超过 35 个字符
|
||||
character: 包含非法字符
|
||||
empty: URL 固定链接不能为空。
|
||||
range: URL 固定链接不能超过 35 个字符。
|
||||
character: URL 固定链接包含非法字符。
|
||||
desc:
|
||||
label: 描述(可选)
|
||||
btn_cancel: 取消
|
||||
@@ -446,7 +446,7 @@ ui:
|
||||
revision:
|
||||
label: 编辑历史
|
||||
display_name:
|
||||
label: 名称
|
||||
label: 显示名称
|
||||
slug_name:
|
||||
label: URL 固定链接
|
||||
info: '必须由 "a-z", "0-9", "+ # - ." 组成'
|
||||
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: 不能为空
|
||||
login:
|
||||
page_title: 欢迎来到 Answer
|
||||
page_title: 欢迎来到 {{site_name}}
|
||||
login_to_continue: 登录以继续
|
||||
info_sign: 没有账户?<1>注册</1>
|
||||
info_login: 已经有一个账户?<1>登录</1>
|
||||
agreements: 登录即表示您同意<1>隐私政策</1>和<3>服务条款</3>。
|
||||
@@ -614,7 +615,7 @@ ui:
|
||||
msg:
|
||||
empty: 邮箱不能为空
|
||||
change_email:
|
||||
page_title: 欢迎来到 Answer
|
||||
page_title: 欢迎来到 {{site_name}}
|
||||
btn_cancel: 取消
|
||||
btn_update: 更新电子邮件地址
|
||||
send_success: >-
|
||||
@@ -651,8 +652,8 @@ ui:
|
||||
btn_name: 保存
|
||||
display_name:
|
||||
label: 昵称
|
||||
msg: 昵称不能为空
|
||||
msg_range: 昵称不能超过 30 个字符
|
||||
msg: 昵称不能为空。
|
||||
msg_range: 昵称不能超过 30 个字符。
|
||||
username:
|
||||
label: 用户名
|
||||
caption: 用户之间可以通过 "@用户名" 进行交互。
|
||||
@@ -803,7 +804,7 @@ ui:
|
||||
modal_confirm:
|
||||
title: 发生错误...
|
||||
account_result:
|
||||
page_title: 欢迎来到 Answer
|
||||
page_title: 欢迎来到 {{site_name}}
|
||||
success: 你的账号已通过验证,即将返回首页。
|
||||
link: 返回首页
|
||||
invalid: >-
|
||||
@@ -1102,7 +1103,7 @@ ui:
|
||||
fields:
|
||||
display_name:
|
||||
label: 昵称
|
||||
msg: 昵称的长度必须是4-30个字符。
|
||||
msg: 昵称的长度必须是 3-30 个字符。
|
||||
email:
|
||||
label: 邮箱
|
||||
msg: 电子邮箱无效。
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: 品牌
|
||||
logo:
|
||||
label: 图标
|
||||
label: Logo (可选)
|
||||
msg: 图标不能为空。
|
||||
text: 在你的网站左上方的Logo图标。使用一个高度为56,长宽比大于3:1的宽长方形图像。如果留空,将显示网站标题文本。
|
||||
mobile_logo:
|
||||
label: 移动端图标(可选)
|
||||
text: 在你的网站的移动版上使用的标志。使用一个高度为56的宽矩形图像。如果留空,将使用 "Logo"设置中的图像。
|
||||
square_icon:
|
||||
label: 方形图标
|
||||
label: 方形图标 (可选)
|
||||
msg: 方形图标不能为空。
|
||||
text: 用作元数据图标的基础的图像。最好是大于512x512。
|
||||
favicon:
|
||||
@@ -1247,7 +1248,7 @@ ui:
|
||||
page_title: 编辑
|
||||
recommend_tags:
|
||||
label: 推荐标签
|
||||
text: "请输入以上标签,每行一个标签。"
|
||||
text: "请在上方输入标签固定链接,每行一个标签。"
|
||||
required_tag:
|
||||
title: 必需的标签
|
||||
label: 根据需要设置推荐标签
|
||||
|
||||
+27
-26
@@ -10,24 +10,24 @@ backend:
|
||||
unauthorized_error:
|
||||
other: "未授權。"
|
||||
database_error:
|
||||
other: "Data server error."
|
||||
other: "資料庫錯誤。"
|
||||
role:
|
||||
name:
|
||||
user:
|
||||
other: "User"
|
||||
other: "使用者"
|
||||
admin:
|
||||
other: "Admin"
|
||||
other: "管理者"
|
||||
moderator:
|
||||
other: "Moderator"
|
||||
other: "版主"
|
||||
description:
|
||||
user:
|
||||
other: "Default with no special access."
|
||||
other: "預設沒有特別閱讀權限"
|
||||
admin:
|
||||
other: "Have the full power to access the site."
|
||||
other: "擁有所有權限"
|
||||
moderator:
|
||||
other: "Has access to all posts except admin settings."
|
||||
other: "可以訪問除了管理員設定以外的所有貼文"
|
||||
email:
|
||||
other: "Email"
|
||||
other: "電子郵件"
|
||||
password:
|
||||
other: "密碼"
|
||||
email_or_password_wrong_error:
|
||||
@@ -38,37 +38,37 @@ backend:
|
||||
other: 電子郵箱和密碼不匹配。
|
||||
answer:
|
||||
not_found:
|
||||
other: "Answer do not found."
|
||||
other: "無答案。"
|
||||
cannot_deleted:
|
||||
other: "No permission to delete."
|
||||
other: "無刪除權限。"
|
||||
cannot_update:
|
||||
other: "No permission to update."
|
||||
other: "無更新權限。"
|
||||
comment:
|
||||
edit_without_permission:
|
||||
other: "Comment are not allowed to edit."
|
||||
other: "不允許編輯評論。"
|
||||
not_found:
|
||||
other: "Comment not found."
|
||||
other: "無評論。"
|
||||
email:
|
||||
duplicate:
|
||||
other: "Email already exists."
|
||||
other: "該電子郵件已被使用"
|
||||
need_to_be_verified:
|
||||
other: "Email should be verified."
|
||||
other: "需驗證電子郵件信箱。"
|
||||
verify_url_expired:
|
||||
other: "Email verified URL has expired, please resend the email."
|
||||
other: "電子郵件驗證網址已過期,請重發確認郵件。"
|
||||
lang:
|
||||
not_found:
|
||||
other: "Language file not found."
|
||||
other: "無此語系檔。"
|
||||
object:
|
||||
captcha_verification_failed:
|
||||
other: "Captcha wrong."
|
||||
other: "驗證碼錯誤。"
|
||||
disallow_follow:
|
||||
other: "You are not allowed to follow."
|
||||
other: "你不能追蹤"
|
||||
disallow_vote:
|
||||
other: "You are not allowed to vote."
|
||||
other: "你不能投票"
|
||||
disallow_vote_your_self:
|
||||
other: "You can't vote for your own post."
|
||||
other: "你不能為自己的貼文投票"
|
||||
not_found:
|
||||
other: "Object not found."
|
||||
other: "找不到物件"
|
||||
verification_failed:
|
||||
other: "驗證失敗。"
|
||||
email_or_password_incorrect:
|
||||
@@ -88,7 +88,7 @@ backend:
|
||||
other: "沒有更新的權限。"
|
||||
rank:
|
||||
fail_to_meet_the_condition:
|
||||
other: "Rank fail to meet the condition."
|
||||
other: "無法為條件排序"
|
||||
report:
|
||||
handle_failed:
|
||||
other: "Report handle failed."
|
||||
@@ -585,7 +585,8 @@ ui:
|
||||
msg:
|
||||
empty: Cannot be empty.
|
||||
login:
|
||||
page_title: Welcome to Answer
|
||||
page_title: Welcome to {{site_name}}
|
||||
login_to_continue: Log in to continue
|
||||
info_sign: Don't have an account? <1>Sign up</1>
|
||||
info_login: Already have an account? <1>Log in</1>
|
||||
agreements: By registering, you agree to the <1>privacy policy</1> and <3>terms of service</3>.
|
||||
@@ -1222,14 +1223,14 @@ ui:
|
||||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo
|
||||
label: Logo (optional)
|
||||
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.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
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:
|
||||
label: Square Icon
|
||||
label: Square Icon (optional)
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
|
||||
@@ -38,6 +38,7 @@ func NewDB(debug bool, dataConf *Database) (*xorm.Engine, error) {
|
||||
dataConf.Driver = string(schemas.MYSQL)
|
||||
}
|
||||
if dataConf.Driver == string(schemas.SQLITE) {
|
||||
dataConf.Driver = "sqlite"
|
||||
dbFileDir := filepath.Dir(dataConf.Connection)
|
||||
log.Debugf("try to create database directory %s", dbFileDir)
|
||||
if err := dir.CreateDirIfNotExist(dbFileDir); err != nil {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
chineseTraditional "github.com/go-playground/locales/zh_Hant_TW"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/go-playground/validator/v10/non-standard/validators"
|
||||
"github.com/go-playground/validator/v10/translations/en"
|
||||
"github.com/go-playground/validator/v10/translations/es"
|
||||
"github.com/go-playground/validator/v10/translations/fr"
|
||||
@@ -98,9 +97,29 @@ func getTran(lo locales.Translator) ut.Translator {
|
||||
return tran
|
||||
}
|
||||
|
||||
func NotBlank(fl validator.FieldLevel) (res bool) {
|
||||
field := fl.Field()
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
trimSpace := strings.TrimSpace(field.String())
|
||||
res := len(trimSpace) > 0
|
||||
if !res {
|
||||
field.SetString(trimSpace)
|
||||
}
|
||||
return true
|
||||
case reflect.Chan, reflect.Map, reflect.Slice, reflect.Array:
|
||||
return field.Len() > 0
|
||||
case reflect.Ptr, reflect.Interface, reflect.Func:
|
||||
return !field.IsNil()
|
||||
default:
|
||||
return field.IsValid() && field.Interface() != reflect.Zero(field.Type()).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func createDefaultValidator(la i18n.Language) *validator.Validate {
|
||||
validate := validator.New()
|
||||
_ = validate.RegisterValidation("notblank", validators.NotBlank)
|
||||
// _ = validate.RegisterValidation("notblank", validators.NotBlank)
|
||||
_ = validate.RegisterValidation("notblank", NotBlank)
|
||||
validate.RegisterTagNameFunc(func(fld reflect.StructField) (res string) {
|
||||
defer func() {
|
||||
if len(res) > 0 {
|
||||
|
||||
@@ -33,7 +33,7 @@ var (
|
||||
"`user_id`",
|
||||
"`vote_count`",
|
||||
"`answer_count`",
|
||||
"0 as `accepted`",
|
||||
"CASE WHEN `accepted_answer_id` > 0 THEN 2 ELSE 0 END as `accepted`",
|
||||
"`question`.`status` as `status`",
|
||||
"`post_update_time`",
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ const (
|
||||
)
|
||||
|
||||
type AnswerAddReq struct {
|
||||
QuestionID string `json:"question_id" ` // question_id
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content" ` // content
|
||||
HTML string `json:"html" ` // html
|
||||
UserID string `json:"-" ` // user_id
|
||||
QuestionID string `json:"question_id"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
HTML string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (req *AnswerAddReq) Check() (errFields []*validator.FormErrorField, err error) {
|
||||
@@ -32,13 +32,13 @@ func (req *AnswerAddReq) Check() (errFields []*validator.FormErrorField, err err
|
||||
}
|
||||
|
||||
type AnswerUpdateReq struct {
|
||||
ID string `json:"id"` // id
|
||||
QuestionID string `json:"question_id" ` // question_id
|
||||
UserID string `json:"-" ` // user_id
|
||||
Title string `json:"title" ` // title
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"` // content
|
||||
HTML string `json:"html" ` // html
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"` // edit_summary
|
||||
ID string `json:"id"`
|
||||
QuestionID string `json:"question_id"`
|
||||
Title string `json:"title"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
HTML string `json:"-"`
|
||||
UserID string `json:"-"`
|
||||
NoNeedReview bool `json:"-"`
|
||||
// whether user can edit it
|
||||
CanEdit bool `json:"-"`
|
||||
|
||||
@@ -14,9 +14,9 @@ type AddCommentReq struct {
|
||||
// reply comment id
|
||||
ReplyCommentID string `validate:"omitempty" json:"reply_comment_id"`
|
||||
// original comment content
|
||||
OriginalText string `validate:"required" json:"original_text"`
|
||||
OriginalText string `validate:"required,notblank,gte=2,lte=600" json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `validate:"required" json:"parsed_text"`
|
||||
ParsedText string `json:"-"`
|
||||
// @ user id list
|
||||
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
|
||||
// user id
|
||||
@@ -47,9 +47,9 @@ type UpdateCommentReq struct {
|
||||
// comment id
|
||||
CommentID string `validate:"required" json:"comment_id"`
|
||||
// original comment content
|
||||
OriginalText string `validate:"omitempty" json:"original_text"`
|
||||
OriginalText string `validate:"required,notblank,gte=2,lte=600" json:"original_text"`
|
||||
// parsed comment content
|
||||
ParsedText string `validate:"omitempty" json:"parsed_text"`
|
||||
ParsedText string `json:"-"`
|
||||
// user id
|
||||
UserID string `json:"-"`
|
||||
IsAdmin bool `json:"-"`
|
||||
|
||||
@@ -41,11 +41,11 @@ type ReopenQuestionReq struct {
|
||||
|
||||
type QuestionAdd struct {
|
||||
// question title
|
||||
Title string `validate:"required,gte=6,lte=150" json:"title"`
|
||||
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
|
||||
// content
|
||||
Content string `validate:"required,gte=6,lte=65535" json:"content"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `validate:"required,gte=6,lte=65535" json:"html"`
|
||||
HTML string `json:"-"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"required,dive" json:"tags"`
|
||||
// user id
|
||||
@@ -90,11 +90,11 @@ type QuestionUpdate struct {
|
||||
// question id
|
||||
ID string `validate:"required" json:"id"`
|
||||
// question title
|
||||
Title string `validate:"required,gte=6,lte=150" json:"title"`
|
||||
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
|
||||
// content
|
||||
Content string `validate:"required,gte=6,lte=65535" json:"content"`
|
||||
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
|
||||
// html
|
||||
HTML string `validate:"required,gte=6,lte=65535" json:"html"`
|
||||
HTML string `json:"-"`
|
||||
// tags
|
||||
Tags []*TagItem `validate:"required,dive" json:"tags"`
|
||||
// edit summary
|
||||
|
||||
@@ -144,7 +144,7 @@ type TagItem struct {
|
||||
// original text
|
||||
OriginalText string `validate:"omitempty" json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `validate:"omitempty" json:"parsed_text"`
|
||||
ParsedText string `json:"-"`
|
||||
}
|
||||
|
||||
// RemoveTagReq delete tag request
|
||||
@@ -166,7 +166,7 @@ type UpdateTagReq struct {
|
||||
// original text
|
||||
OriginalText string `validate:"omitempty" json:"original_text"`
|
||||
// parsed text
|
||||
ParsedText string `validate:"omitempty" json:"parsed_text"`
|
||||
ParsedText string `json:"-"`
|
||||
// edit summary
|
||||
EditSummary string `validate:"omitempty" json:"edit_summary"`
|
||||
// user id
|
||||
|
||||
+1
-2
@@ -10,10 +10,9 @@
|
||||
|
||||
# production
|
||||
|
||||
|
||||
/build/*/*/*
|
||||
/build/*.json
|
||||
/build/*.ico
|
||||
/build/*.html
|
||||
/build/*.txt
|
||||
|
||||
# misc
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -1 +0,0 @@
|
||||
<!doctype html><html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><script defer="defer" src="/static/js/main.cb9bf782.js"></script><link href="/static/css/main.b8d8739f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="spin-mask"><noscript><style>#spin-mask{display:none!important}</style></noscript><style>@keyframes _doc-spin{to{transform:rotate(360deg)}}#spin-mask{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#fff;z-index:9999}#spin-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}#spin-container .spinner{box-sizing:border-box;display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25rem solid currentColor;border-right-color:transparent;color:rgba(108,117,125,.75);border-radius:50%;animation:.75s linear infinite _doc-spin}</style><div id="spin-container"><div class="spinner"></div></div></div></div></body></html>
|
||||
@@ -1 +0,0 @@
|
||||
# Build Static
|
||||
@@ -22,7 +22,9 @@
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"dayjs": "^1.11.5",
|
||||
"diff": "^5.1.0",
|
||||
"dompurify": "^2.4.3",
|
||||
"emoji-regex": "^10.2.1",
|
||||
"html-react-parser": "^3.0.8",
|
||||
"i18next": "^21.9.0",
|
||||
"katex": "^0.16.2",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -51,6 +53,7 @@
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/lodash": "^4.14.184",
|
||||
"@types/marked": "^4.0.6",
|
||||
|
||||
Generated
+1319
-93
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="generator" content="Answer %AnswerVersion% - https://github.com/answerdev/answer">
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -56,7 +56,7 @@ export interface QuestionParams {
|
||||
title: string;
|
||||
url_title?: string;
|
||||
content: string;
|
||||
html: string;
|
||||
html?: string;
|
||||
tags: Tag[];
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ export interface AnswerItem {
|
||||
|
||||
export interface PostAnswerReq {
|
||||
content: string;
|
||||
html: string;
|
||||
html?: string;
|
||||
question_id: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, memo } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classNames from 'classnames';
|
||||
@@ -7,7 +7,7 @@ import classNames from 'classnames';
|
||||
import { TextArea, Mentions } from '@/components';
|
||||
import { usePageUsers } from '@/hooks';
|
||||
|
||||
const Form = ({
|
||||
const Index = ({
|
||||
className = '',
|
||||
value: initialValue = '',
|
||||
onSendReply,
|
||||
@@ -18,7 +18,7 @@ const Form = ({
|
||||
const [value, setValue] = useState('');
|
||||
const pageUsers = usePageUsers();
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
|
||||
|
||||
const [validationErrorMsg, setValidationErrorMsg] = useState('');
|
||||
useEffect(() => {
|
||||
if (!initialValue) {
|
||||
return;
|
||||
@@ -32,6 +32,13 @@ const Form = ({
|
||||
const handleSelected = (val) => {
|
||||
setValue(val);
|
||||
};
|
||||
const handleSendReply = () => {
|
||||
onSendReply(value).catch((ex) => {
|
||||
if (ex.isError) {
|
||||
setValidationErrorMsg(ex.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -39,17 +46,27 @@ const Form = ({
|
||||
className,
|
||||
)}>
|
||||
<div>
|
||||
<Mentions pageUsers={pageUsers.getUsers()} onSelected={handleSelected}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
<div
|
||||
className={classNames('custom-form-control', {
|
||||
'is-invalid': validationErrorMsg,
|
||||
})}>
|
||||
<Mentions
|
||||
pageUsers={pageUsers.getUsers()}
|
||||
onSelected={handleSelected}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
</div>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{validationErrorMsg}
|
||||
</Form.Control.Feedback>
|
||||
</div>
|
||||
{type === 'edit' ? (
|
||||
<div className="d-flex flex-row flex-md-column ms-0 ms-md-2 mt-2 mt-md-0">
|
||||
<Button
|
||||
size="sm"
|
||||
className="text-nowrap "
|
||||
onClick={() => onSendReply(value)}>
|
||||
onClick={() => handleSendReply()}>
|
||||
{t('btn_save_edits')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -64,7 +81,7 @@ const Form = ({
|
||||
<Button
|
||||
size="sm"
|
||||
className="text-nowrap ms-0 ms-md-2 mt-2 mt-md-0"
|
||||
onClick={() => onSendReply(value)}>
|
||||
onClick={() => handleSendReply()}>
|
||||
{t('btn_add_comment')}
|
||||
</Button>
|
||||
)}
|
||||
@@ -72,4 +89,4 @@ const Form = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Form);
|
||||
export default memo(Index);
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
import { useState, memo } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { TextArea, Mentions } from '@/components';
|
||||
import { usePageUsers } from '@/hooks';
|
||||
|
||||
const Form = ({ userName, onSendReply, onCancel, mode }) => {
|
||||
const Index = ({ userName, onSendReply, onCancel, mode }) => {
|
||||
const [value, setValue] = useState('');
|
||||
const pageUsers = usePageUsers();
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
|
||||
|
||||
const [validationErrorMsg, setValidationErrorMsg] = useState('');
|
||||
const handleChange = (e) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
const handleSelected = (val) => {
|
||||
setValue(val);
|
||||
};
|
||||
const handleSendReply = () => {
|
||||
onSendReply(value).catch((ex) => {
|
||||
if (ex.isError) {
|
||||
setValidationErrorMsg(ex.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-2">
|
||||
@@ -24,18 +33,26 @@ const Form = ({ userName, onSendReply, onCancel, mode }) => {
|
||||
</div>
|
||||
<div className="d-flex mb-1 align-items-start flex-column flex-md-row">
|
||||
<div>
|
||||
<Mentions
|
||||
pageUsers={pageUsers.getUsers()}
|
||||
onSelected={handleSelected}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
<div
|
||||
className={classNames('custom-form-control', {
|
||||
'is-invalid': validationErrorMsg,
|
||||
})}>
|
||||
<Mentions
|
||||
pageUsers={pageUsers.getUsers()}
|
||||
onSelected={handleSelected}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
</div>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{validationErrorMsg}
|
||||
</Form.Control.Feedback>
|
||||
</div>
|
||||
<div className="d-flex flex-row flex-md-column ms-0 ms-md-2 mt-2 mt-md-0">
|
||||
<Button
|
||||
size="sm"
|
||||
className="text-nowrap"
|
||||
onClick={() => onSendReply(value)}>
|
||||
onClick={() => handleSendReply()}>
|
||||
{t('btn_add_comment')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -51,4 +68,4 @@ const Form = ({ userName, onSendReply, onCancel, mode }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Form);
|
||||
export default memo(Index);
|
||||
|
||||
@@ -10,7 +10,12 @@ import { marked } from 'marked';
|
||||
import * as Types from '@/common/interface';
|
||||
import { Modal } from '@/components';
|
||||
import { usePageUsers, useReportModal } from '@/hooks';
|
||||
import { matchedUsers, parseUserInfo, scrollTop, bgFadeOut } from '@/utils';
|
||||
import {
|
||||
matchedUsers,
|
||||
parseUserInfo,
|
||||
scrollToElementTop,
|
||||
bgFadeOut,
|
||||
} from '@/utils';
|
||||
import { tryNormalLogged } from '@/utils/guard';
|
||||
import {
|
||||
useQueryComments,
|
||||
@@ -43,7 +48,7 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||
const scrollCallback = useCallback((el, co) => {
|
||||
if (pageIndex === 0 && co.comment_id === commentId) {
|
||||
setTimeout(() => {
|
||||
scrollTop(el);
|
||||
scrollToElementTop(el);
|
||||
bgFadeOut(el);
|
||||
}, 100);
|
||||
}
|
||||
@@ -102,13 +107,14 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||
const handleSendReply = (item) => {
|
||||
const users = matchedUsers(item.value);
|
||||
const userNames = unionBy(users.map((user) => user.userName));
|
||||
const html = marked.parse(parseUserInfo(item.value));
|
||||
if (!item.value || !html) {
|
||||
return;
|
||||
}
|
||||
const commentMarkDown = parseUserInfo(item.value);
|
||||
const html = marked.parse(commentMarkDown);
|
||||
// if (!commentMarkDown || !html) {
|
||||
// return;
|
||||
// }
|
||||
const params = {
|
||||
object_id: objectId,
|
||||
original_text: item.value,
|
||||
original_text: commentMarkDown,
|
||||
mention_username_list: userNames,
|
||||
parsed_text: html,
|
||||
...(item.type === 'reply'
|
||||
@@ -119,7 +125,7 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||
};
|
||||
|
||||
if (item.type === 'edit') {
|
||||
updateComment({
|
||||
return updateComment({
|
||||
...params,
|
||||
comment_id: item.comment_id,
|
||||
}).then(() => {
|
||||
@@ -134,30 +140,29 @@ const Comment = ({ objectId, mode, commentId }) => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
addComment(params).then((res) => {
|
||||
if (item.type === 'reply') {
|
||||
const index = comments.findIndex(
|
||||
(comment) => comment.comment_id === item.comment_id,
|
||||
);
|
||||
comments[index].showReply = false;
|
||||
comments.splice(index + 1, 0, res);
|
||||
setComments([...comments]);
|
||||
} else {
|
||||
setComments([
|
||||
...comments.map((comment) => {
|
||||
if (comment.comment_id === item.comment_id) {
|
||||
comment.showReply = false;
|
||||
}
|
||||
return comment;
|
||||
}),
|
||||
res,
|
||||
]);
|
||||
}
|
||||
|
||||
setVisibleComment(false);
|
||||
});
|
||||
}
|
||||
return addComment(params).then((res) => {
|
||||
if (item.type === 'reply') {
|
||||
const index = comments.findIndex(
|
||||
(comment) => comment.comment_id === item.comment_id,
|
||||
);
|
||||
comments[index].showReply = false;
|
||||
comments.splice(index + 1, 0, res);
|
||||
setComments([...comments]);
|
||||
} else {
|
||||
setComments([
|
||||
...comments.map((comment) => {
|
||||
if (comment.comment_id === item.comment_id) {
|
||||
comment.showReply = false;
|
||||
}
|
||||
return comment;
|
||||
}),
|
||||
res,
|
||||
]);
|
||||
}
|
||||
|
||||
setVisibleComment(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (id) => {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { FC, memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classname from 'classnames';
|
||||
|
||||
import { Icon } from '@/components';
|
||||
|
||||
interface Props {
|
||||
data: {
|
||||
votes: number;
|
||||
answers: number;
|
||||
views: number;
|
||||
};
|
||||
showVotes?: boolean;
|
||||
showAnswers?: boolean;
|
||||
showViews?: boolean;
|
||||
showAccepted?: boolean;
|
||||
isAccepted?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
const Index: FC<Props> = ({
|
||||
data,
|
||||
showVotes = true,
|
||||
showAnswers = true,
|
||||
showViews = true,
|
||||
isAccepted = false,
|
||||
showAccepted = false,
|
||||
className = '',
|
||||
}) => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'counts' });
|
||||
|
||||
return (
|
||||
<div className={classname('d-flex align-items-center', className)}>
|
||||
{showVotes && (
|
||||
<div className="d-flex align-items-center">
|
||||
<Icon name="hand-thumbs-up-fill me-1" />
|
||||
<span>
|
||||
{data.votes} {t('votes')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAccepted && (
|
||||
<div className="d-flex align-items-center ms-3 text-success">
|
||||
<Icon name="check-circle-fill me-1" />
|
||||
<span>{t('accepted')}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAnswers && (
|
||||
<div
|
||||
className={`d-flex align-items-center ms-3 ${
|
||||
isAccepted ? 'text-success' : ''
|
||||
}`}>
|
||||
{isAccepted ? (
|
||||
<Icon name="check-circle-fill me-1" />
|
||||
) : (
|
||||
<Icon name="chat-square-text-fill me-1" />
|
||||
)}
|
||||
<span>
|
||||
{data.answers} {t('answers')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{showViews && (
|
||||
<span className="summary-stat ms-3">
|
||||
<Icon name="eye-fill" />
|
||||
<em className="fst-normal ms-1">
|
||||
{data.views} {t('views')}
|
||||
</em>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Index);
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from 'react';
|
||||
|
||||
import { markdownToHtml } from '@/services';
|
||||
import { htmlToReact } from '@/utils';
|
||||
|
||||
import { htmlRender } from './utils';
|
||||
|
||||
@@ -38,6 +39,7 @@ const Index = ({ value }, ref) => {
|
||||
}
|
||||
|
||||
previewRef.current?.scrollTo(0, scrollTop);
|
||||
|
||||
htmlRender(previewRef.current);
|
||||
}, [html]);
|
||||
useImperativeHandle(ref, () => {
|
||||
@@ -49,9 +51,9 @@ const Index = ({ value }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={previewRef}
|
||||
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt"
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
className="preview-wrap position-relative p-3 bg-light rounded text-break text-wrap mt-2 fmt">
|
||||
{htmlToReact(html)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Pagination } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
|
||||
import { scrollToDocTop } from '@/utils';
|
||||
|
||||
interface Props {
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
@@ -49,7 +51,7 @@ const PageItem = ({ page, currentPage, path }: PageItemProps) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigate(path);
|
||||
window.scrollTo(0, 0);
|
||||
scrollToDocTop();
|
||||
}}>
|
||||
{page}
|
||||
</Pagination.Item>
|
||||
@@ -91,7 +93,7 @@ const Index: FC<Props> = ({
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate(handleParams(currentPage - 1));
|
||||
window.scrollTo(0, 0);
|
||||
scrollToDocTop();
|
||||
}}>
|
||||
{t('prev')}
|
||||
</Pagination.Prev>
|
||||
@@ -186,7 +188,7 @@ const Index: FC<Props> = ({
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate(handleParams(currentPage + 1));
|
||||
window.scrollTo(0, 0);
|
||||
scrollToDocTop();
|
||||
}}>
|
||||
{t('next')}
|
||||
</Pagination.Next>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import type * as Type from '@/common/interface';
|
||||
import {
|
||||
Icon,
|
||||
Tag,
|
||||
Pagination,
|
||||
FormatTime,
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
BaseUserCard,
|
||||
QueryGroup,
|
||||
QuestionListLoader,
|
||||
Counts,
|
||||
} from '@/components';
|
||||
import { useQuestionList } from '@/services';
|
||||
|
||||
@@ -95,29 +95,15 @@ const QuestionList: FC<Props> = ({ source }) => {
|
||||
preFix={t(li.operation_type)}
|
||||
/>
|
||||
</div>
|
||||
<div className="ms-0 ms-md-3 mt-2 mt-md-0">
|
||||
<span>
|
||||
<Icon name="hand-thumbs-up-fill" />
|
||||
<em className="fst-normal ms-1">{li.vote_count}</em>
|
||||
</span>
|
||||
<span
|
||||
className={`ms-3 ${
|
||||
li.accepted_answer_id >= 1 ? 'text-success' : ''
|
||||
}`}>
|
||||
<Icon
|
||||
name={
|
||||
li.accepted_answer_id >= 1
|
||||
? 'check-circle-fill'
|
||||
: 'chat-square-text-fill'
|
||||
}
|
||||
/>
|
||||
<em className="fst-normal ms-1">{li.answer_count}</em>
|
||||
</span>
|
||||
<span className="summary-stat ms-3">
|
||||
<Icon name="eye-fill" />
|
||||
<em className="fst-normal ms-1">{li.view_count}</em>
|
||||
</span>
|
||||
</div>
|
||||
<Counts
|
||||
data={{
|
||||
votes: li.vote_count,
|
||||
answers: li.answer_count,
|
||||
views: li.view_count,
|
||||
}}
|
||||
isAccepted={li.accepted_answer_id >= 1}
|
||||
className="ms-0 ms-md-3 mt-2 mt-md-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="question-tags m-n1">
|
||||
{Array.isArray(li.tags)
|
||||
@@ -139,7 +125,7 @@ const QuestionList: FC<Props> = ({ source }) => {
|
||||
currentPage={curPage}
|
||||
totalSize={count}
|
||||
pageSize={pageSize}
|
||||
pathname="/questions"
|
||||
pathname={source === 'questions' ? '/questions' : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -155,6 +155,7 @@ const TagSelector: FC<IProps> = ({
|
||||
};
|
||||
const handleKeyDown = (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (!tags) {
|
||||
return;
|
||||
}
|
||||
@@ -166,13 +167,20 @@ const TagSelector: FC<IProps> = ({
|
||||
if (keyCode === 40 && currentIndex < tags.length - 1) {
|
||||
setCurrentIndex(currentIndex + 1);
|
||||
}
|
||||
if (
|
||||
keyCode === 13 &&
|
||||
currentIndex > -1 &&
|
||||
currentIndex <= tags.length - 1
|
||||
) {
|
||||
|
||||
if (keyCode === 13 && currentIndex > -1) {
|
||||
e.preventDefault();
|
||||
handleClick(tags[currentIndex]);
|
||||
|
||||
if (tags.length === 0) {
|
||||
tagModal.onShow(tag);
|
||||
return;
|
||||
}
|
||||
if (currentIndex <= tags.length - 1) {
|
||||
handleClick(tags[currentIndex]);
|
||||
if (currentIndex === tags.length - 1 && currentIndex > 0) {
|
||||
setCurrentIndex(currentIndex - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -32,6 +32,7 @@ import CustomizeTheme from './CustomizeTheme';
|
||||
import PageTags from './PageTags';
|
||||
import QuestionListLoader from './QuestionListLoader';
|
||||
import TagsLoader from './TagsLoader';
|
||||
import Counts from './Counts';
|
||||
|
||||
export {
|
||||
Avatar,
|
||||
@@ -70,5 +71,6 @@ export {
|
||||
PageTags,
|
||||
QuestionListLoader,
|
||||
TagsLoader,
|
||||
Counts,
|
||||
};
|
||||
export type { EditorRef, JSONSchema, UISchema };
|
||||
|
||||
@@ -143,21 +143,7 @@ const Ask = () => {
|
||||
const checkValidated = (): boolean => {
|
||||
const bol = true;
|
||||
const { title, content, tags, answer } = formData;
|
||||
if (!title.value) {
|
||||
// bol = false;
|
||||
// formData.title = {
|
||||
// value: '',
|
||||
// isInvalid: true,
|
||||
// errorMsg: t('form.fields.title.msg.empty'),
|
||||
// };
|
||||
} else if (Array.from(title.value).length > 150) {
|
||||
// bol = false;
|
||||
// formData.title = {
|
||||
// value: title.value,
|
||||
// isInvalid: true,
|
||||
// errorMsg: t('form.fields.title.msg.range'),
|
||||
// };
|
||||
} else {
|
||||
if (title.value && Array.from(title.value).length <= 150) {
|
||||
formData.title = {
|
||||
value: title.value,
|
||||
isInvalid: false,
|
||||
@@ -165,14 +151,7 @@ const Ask = () => {
|
||||
};
|
||||
}
|
||||
|
||||
if (!content.value) {
|
||||
// bol = false;
|
||||
// formData.content = {
|
||||
// value: '',
|
||||
// isInvalid: true,
|
||||
// errorMsg: t('form.fields.body.msg.empty'),
|
||||
// };
|
||||
} else {
|
||||
if (content.value) {
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: false,
|
||||
@@ -180,29 +159,16 @@ const Ask = () => {
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.value.length === 0) {
|
||||
// bol = false;
|
||||
// formData.tags = {
|
||||
// value: [],
|
||||
// isInvalid: true,
|
||||
// errorMsg: t('form.fields.tags.msg.empty'),
|
||||
// };
|
||||
} else {
|
||||
if (Array.isArray(tags.value) && tags.value.length > 0) {
|
||||
formData.tags = {
|
||||
value: tags.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
if (!answer.value) {
|
||||
// bol = false;
|
||||
// formData.answer = {
|
||||
// value: '',
|
||||
// isInvalid: true,
|
||||
// errorMsg: t('form.fields.answer.msg.empty'),
|
||||
// };
|
||||
} else {
|
||||
if (answer.value) {
|
||||
formData.answer = {
|
||||
value: answer.value,
|
||||
isInvalid: false,
|
||||
@@ -227,7 +193,6 @@ const Ask = () => {
|
||||
const params: Type.QuestionParams = {
|
||||
title: formData.title.value,
|
||||
content: formData.content.value,
|
||||
html: editorRef.current.getHtml(),
|
||||
tags: formData.tags.value,
|
||||
};
|
||||
if (isEdit) {
|
||||
@@ -261,7 +226,6 @@ const Ask = () => {
|
||||
postAnswer({
|
||||
question_id: id,
|
||||
content: formData.answer.value,
|
||||
html: editorRef2.current.getHtml(),
|
||||
})
|
||||
.then(() => {
|
||||
navigate(pathFactory.questionLanding(id, params.url_title));
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
FormatTime,
|
||||
htmlRender,
|
||||
} from '@/components';
|
||||
import { scrollTop, bgFadeOut } from '@/utils';
|
||||
import { scrollToElementTop, bgFadeOut } from '@/utils';
|
||||
import { AnswerItem } from '@/common/interface';
|
||||
import { acceptanceAnswer } from '@/services';
|
||||
|
||||
@@ -60,7 +60,7 @@ const Index: FC<Props> = ({
|
||||
if (aid === data.id) {
|
||||
setTimeout(() => {
|
||||
const element = answerRef.current;
|
||||
scrollTop(element);
|
||||
scrollToElementTop(element);
|
||||
if (!searchParams.get('commentId')) {
|
||||
bgFadeOut(answerRef.current);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import classNames from 'classnames';
|
||||
import { Editor, Modal, TextArea } from '@/components';
|
||||
import { FormDataType } from '@/common/interface';
|
||||
import { postAnswer } from '@/services';
|
||||
import { guard } from '@/utils';
|
||||
import { guard, handleFormError } from '@/utils';
|
||||
|
||||
interface Props {
|
||||
visible?: boolean;
|
||||
@@ -35,35 +35,60 @@ const Index: FC<Props> = ({ visible = false, data, callback }) => {
|
||||
const [focusType, setFocusType] = useState('');
|
||||
const [editorFocusState, setEditorFocusState] = useState(false);
|
||||
|
||||
const checkValidated = (): boolean => {
|
||||
let bol = true;
|
||||
const { content } = formData;
|
||||
|
||||
if (!content.value || Array.from(content.value.trim()).length < 6) {
|
||||
bol = false;
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: true,
|
||||
errorMsg: t('characters'),
|
||||
};
|
||||
} else {
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
});
|
||||
return bol;
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!guard.tryNormalLogged(true)) {
|
||||
return;
|
||||
}
|
||||
if (!formData.content.value) {
|
||||
setFormData({
|
||||
content: {
|
||||
value: '',
|
||||
isInvalid: true,
|
||||
errorMsg: t('empty'),
|
||||
},
|
||||
});
|
||||
if (!checkValidated()) {
|
||||
return;
|
||||
}
|
||||
postAnswer({
|
||||
question_id: data?.qid,
|
||||
content: formData.content.value,
|
||||
html: marked.parse(formData.content.value),
|
||||
}).then((res) => {
|
||||
setShowEditor(false);
|
||||
setFormData({
|
||||
content: {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
setShowEditor(false);
|
||||
setFormData({
|
||||
content: {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
},
|
||||
});
|
||||
callback?.(res.info);
|
||||
})
|
||||
.catch((ex) => {
|
||||
if (ex.isError) {
|
||||
const stateData = handleFormError(ex, formData);
|
||||
setFormData({ ...stateData });
|
||||
}
|
||||
});
|
||||
callback?.(res.info);
|
||||
});
|
||||
};
|
||||
|
||||
const clickBtn = () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import Pattern from '@/common/pattern';
|
||||
import { Pagination } from '@/components';
|
||||
import { loggedUserInfoStore, toastStore } from '@/stores';
|
||||
import { scrollTop } from '@/utils';
|
||||
import { scrollToElementTop } from '@/utils';
|
||||
import { usePageTags, usePageUsers } from '@/hooks';
|
||||
import type {
|
||||
ListResult,
|
||||
@@ -80,7 +80,7 @@ const Index = () => {
|
||||
if (page > 0 || order) {
|
||||
// scroll into view;
|
||||
const element = document.getElementById('answerHeader');
|
||||
scrollTop(element);
|
||||
scrollToElementTop(element);
|
||||
}
|
||||
|
||||
res.list.forEach((item) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Container, Row, Col, Form, Button, Card } from 'react-bootstrap';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import dayjs from 'dayjs';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { handleFormError } from '@/utils';
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import { Editor, EditorRef, Icon } from '@/components';
|
||||
@@ -19,11 +20,11 @@ import {
|
||||
import './index.scss';
|
||||
|
||||
interface FormDataItem {
|
||||
answer: Type.FormValue<string>;
|
||||
content: Type.FormValue<string>;
|
||||
description: Type.FormValue<string>;
|
||||
}
|
||||
const initFormData = {
|
||||
answer: {
|
||||
content: {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
@@ -35,7 +36,6 @@ const initFormData = {
|
||||
},
|
||||
};
|
||||
const Index = () => {
|
||||
const [formData, setFormData] = useState<FormDataItem>(initFormData);
|
||||
const { aid = '', qid = '' } = useParams();
|
||||
const [focusType, setForceType] = useState('');
|
||||
|
||||
@@ -43,6 +43,10 @@ const Index = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data } = useQueryAnswerInfo(aid);
|
||||
const [formData, setFormData] = useState<FormDataItem>(initFormData);
|
||||
|
||||
initFormData.content.value = data?.info.content || '';
|
||||
|
||||
const { data: revisions = [] } = useQueryRevisions(aid);
|
||||
|
||||
const editorRef = useRef<EditorRef>({
|
||||
@@ -51,18 +55,10 @@ const Index = () => {
|
||||
|
||||
const questionContentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
formData.answer.value = data.info.content;
|
||||
setFormData({ ...formData });
|
||||
}, [data]);
|
||||
|
||||
const handleAnswerChange = (value: string) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
answer: { ...formData.answer, value },
|
||||
content: { ...formData.content, value },
|
||||
});
|
||||
const handleSummaryChange = (evt) => {
|
||||
const v = evt.currentTarget.value;
|
||||
@@ -74,18 +70,18 @@ const Index = () => {
|
||||
|
||||
const checkValidated = (): boolean => {
|
||||
let bol = true;
|
||||
const { answer } = formData;
|
||||
const { content } = formData;
|
||||
|
||||
if (!answer.value) {
|
||||
if (!content.value || Array.from(content.value.trim()).length < 6) {
|
||||
bol = false;
|
||||
formData.answer = {
|
||||
value: '',
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: true,
|
||||
errorMsg: '标题不能为空',
|
||||
errorMsg: t('form.fields.answer.feedback.characters'),
|
||||
};
|
||||
} else {
|
||||
formData.answer = {
|
||||
value: answer.value,
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
@@ -105,29 +101,36 @@ const Index = () => {
|
||||
}
|
||||
|
||||
const params: Type.AnswerParams = {
|
||||
content: formData.answer.value,
|
||||
content: formData.content.value,
|
||||
html: editorRef.current.getHtml(),
|
||||
question_id: qid,
|
||||
id: aid,
|
||||
edit_summary: formData.description.value,
|
||||
};
|
||||
modifyAnswer(params).then((res) => {
|
||||
navigate(
|
||||
pathFactory.answerLanding({
|
||||
questionId: qid,
|
||||
slugTitle: data?.question?.url_title,
|
||||
answerId: aid,
|
||||
}),
|
||||
{
|
||||
state: { isReview: res?.wait_for_review },
|
||||
},
|
||||
);
|
||||
});
|
||||
modifyAnswer(params)
|
||||
.then((res) => {
|
||||
navigate(
|
||||
pathFactory.answerLanding({
|
||||
questionId: qid,
|
||||
slugTitle: data?.question?.url_title,
|
||||
answerId: aid,
|
||||
}),
|
||||
{
|
||||
state: { isReview: res?.wait_for_review },
|
||||
},
|
||||
);
|
||||
})
|
||||
.catch((ex) => {
|
||||
if (ex.isError) {
|
||||
const stateData = handleFormError(ex, formData);
|
||||
setFormData({ ...stateData });
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleSelectedRevision = (e) => {
|
||||
const index = e.target.value;
|
||||
const revision = revisions[index];
|
||||
formData.answer.value = revision.content.content;
|
||||
formData.content.value = revision.content.content;
|
||||
setFormData({ ...formData });
|
||||
};
|
||||
|
||||
@@ -190,7 +193,7 @@ const Index = () => {
|
||||
<Form.Group controlId="answer" className="mt-3">
|
||||
<Form.Label>{t('form.fields.answer.label')}</Form.Label>
|
||||
<Editor
|
||||
value={formData.answer.value}
|
||||
value={formData.content.value}
|
||||
onChange={handleAnswerChange}
|
||||
className={classNames(
|
||||
'form-control p-0',
|
||||
@@ -205,14 +208,14 @@ const Index = () => {
|
||||
ref={editorRef}
|
||||
/>
|
||||
<Form.Control
|
||||
value={formData.answer.value}
|
||||
value={formData.content.value}
|
||||
type="text"
|
||||
isInvalid={formData.answer.isInvalid}
|
||||
isInvalid={formData.content.isInvalid}
|
||||
readOnly
|
||||
hidden
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{formData.answer.errorMsg}
|
||||
{formData.content.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="edit_summary" className="my-3">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ListGroupItem } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import { Icon, Tag, FormatTime, BaseUserCard } from '@/components';
|
||||
import { Tag, FormatTime, BaseUserCard, Counts } from '@/components';
|
||||
import type { SearchResItem } from '@/common/interface';
|
||||
import { escapeRemove } from '@/utils';
|
||||
|
||||
@@ -51,23 +51,17 @@ const Index: FC<Props> = ({ data }) => {
|
||||
className="me-3"
|
||||
preFix={data.object_type === 'question' ? 'asked' : 'answered'}
|
||||
/>
|
||||
<div className="d-flex align-items-center my-2 my-sm-0">
|
||||
<div className="d-flex align-items-center me-3">
|
||||
<Icon name="hand-thumbs-up-fill me-1" />
|
||||
<span> {data.object?.vote_count}</span>
|
||||
</div>
|
||||
<div
|
||||
className={`d-flex align-items-center ${
|
||||
data.object?.accepted ? 'text-success' : ''
|
||||
}`}>
|
||||
{data.object?.accepted ? (
|
||||
<Icon name="check-circle-fill me-1" />
|
||||
) : (
|
||||
<Icon name="chat-square-text-fill me-1" />
|
||||
)}
|
||||
<span>{data.object?.answer_count}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Counts
|
||||
className="my-2 my-sm-0"
|
||||
showViews={false}
|
||||
isAccepted={data.object?.accepted}
|
||||
data={{
|
||||
votes: data.object?.vote_count,
|
||||
answers: data.object?.answer_count,
|
||||
views: 0,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{data.object?.excerpt && (
|
||||
|
||||
@@ -3,10 +3,12 @@ import { Container, Row, Col } from 'react-bootstrap';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { siteInfoStore } from '@/stores';
|
||||
import { usePageTags } from '@/hooks';
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'account_result' });
|
||||
const siteName = siteInfoStore((state) => state.siteInfo.name);
|
||||
const location = useLocation();
|
||||
usePageTags({
|
||||
title: t('account_activation', { keyPrefix: 'page_title' }),
|
||||
@@ -15,7 +17,9 @@ const Index: FC = () => {
|
||||
<Container className="pt-4 mt-2 mb-5">
|
||||
<Row className="justify-content-center">
|
||||
<Col lg={6}>
|
||||
<h3 className="text-center mt-3 mb-5">{t('page_title')}</h3>
|
||||
<h3 className="text-center mt-3 mb-5">
|
||||
{t('page_title', { site_name: siteName })}
|
||||
</h3>
|
||||
{location.pathname?.includes('success') && (
|
||||
<>
|
||||
<p className="text-center">{t('success')}</p>
|
||||
|
||||
@@ -2,18 +2,22 @@ import { FC, memo } from 'react';
|
||||
import { Container, Col } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { siteInfoStore } from '@/stores';
|
||||
import { usePageTags } from '@/hooks';
|
||||
|
||||
import SendEmail from './components/sendEmail';
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'change_email' });
|
||||
const siteName = siteInfoStore((state) => state.siteInfo.name);
|
||||
usePageTags({
|
||||
title: t('change_email', { keyPrefix: 'page_title' }),
|
||||
});
|
||||
return (
|
||||
<Container style={{ paddingTop: '4rem', paddingBottom: '6rem' }}>
|
||||
<h3 className="text-center mb-5">{t('page_title')}</h3>
|
||||
<h3 className="text-center mb-5">
|
||||
{t('page_title', { site_name: siteName })}
|
||||
</h3>
|
||||
<Col className="mx-auto" md={3}>
|
||||
<SendEmail />
|
||||
</Col>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Link, useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { loggedUserInfoStore } from '@/stores';
|
||||
import { loggedUserInfoStore, siteInfoStore } from '@/stores';
|
||||
import { changeEmailVerify, getLoggedUserInfo } from '@/services';
|
||||
|
||||
const Index: FC = () => {
|
||||
@@ -13,6 +13,7 @@ const Index: FC = () => {
|
||||
const [step, setStep] = useState('loading');
|
||||
|
||||
const updateUser = loggedUserInfoStore((state) => state.update);
|
||||
const siteName = siteInfoStore((state) => state.siteInfo.name);
|
||||
|
||||
useEffect(() => {
|
||||
const code = searchParams.get('code');
|
||||
@@ -38,7 +39,9 @@ const Index: FC = () => {
|
||||
<Container className="pt-4 mt-2 mb-5">
|
||||
<Row className="justify-content-center">
|
||||
<Col lg={6}>
|
||||
<h3 className="text-center mt-3 mb-5">{t('page_title')}</h3>
|
||||
<h3 className="text-center mt-3 mb-5">
|
||||
{t('page_title', { site_name: siteName })}
|
||||
</h3>
|
||||
{step === 'success' && (
|
||||
<>
|
||||
<p className="text-center">{t('confirm_new_email')}</p>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FC, memo } from 'react';
|
||||
import { ListGroup, ListGroupItem } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Icon, FormatTime, Tag } from '@/components';
|
||||
import { FormatTime, Tag, Counts } from '@/components';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
interface Props {
|
||||
@@ -35,21 +35,16 @@ const Index: FC<Props> = ({ visible, data }) => {
|
||||
<div className="d-flex align-items-center fs-14 text-secondary mb-2">
|
||||
<FormatTime
|
||||
time={item.create_time}
|
||||
className="me-4"
|
||||
className="me-3"
|
||||
preFix={t('answered')}
|
||||
/>
|
||||
|
||||
<div className="d-flex align-items-center me-3">
|
||||
<Icon name="hand-thumbs-up-fill me-1" />
|
||||
<span>{item?.vote_count}</span>
|
||||
</div>
|
||||
|
||||
{item.accepted === 2 && (
|
||||
<div className="d-flex align-items-center me-3 text-success">
|
||||
<Icon name="check-circle-fill me-1" />
|
||||
<span>{t('accepted')}</span>
|
||||
</div>
|
||||
)}
|
||||
<Counts
|
||||
data={{ votes: item?.vote_count, views: 0, answers: 0 }}
|
||||
showAnswers={false}
|
||||
showViews={false}
|
||||
showAccepted={item.accepted === 2}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{item.question_info?.tags?.map((tag) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FC, memo } from 'react';
|
||||
import { ListGroup, ListGroupItem } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Icon, FormatTime, Tag, BaseUserCard } from '@/components';
|
||||
import { FormatTime, Tag, BaseUserCard, Counts } from '@/components';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
interface Props {
|
||||
@@ -44,35 +44,23 @@ const Index: FC<Props> = ({ visible, tabName, data }) => {
|
||||
<span className="split-dot" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<FormatTime
|
||||
time={item.create_time}
|
||||
time={
|
||||
tabName === 'bookmarks' ? item.create_time : item.created_at
|
||||
}
|
||||
className="me-3"
|
||||
preFix={t('asked')}
|
||||
/>
|
||||
|
||||
<div className="d-flex align-items-center me-3">
|
||||
<Icon name="hand-thumbs-up-fill me-1" />
|
||||
<span>{item.vote_count}</span>
|
||||
</div>
|
||||
|
||||
{tabName !== 'answers' && (
|
||||
<div
|
||||
className={`d-flex align-items-center me-3 ${
|
||||
Number(item.accepted_answer_id) > 0 ? 'text-success' : ''
|
||||
}`}>
|
||||
{Number(item.accepted_answer_id) > 0 ? (
|
||||
<Icon name="check-circle-fill me-1" />
|
||||
) : (
|
||||
<Icon name="chat-square-text-fill me-1" />
|
||||
)}
|
||||
<span>{item.answer_count}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex align-items-center me-3">
|
||||
<Icon name="eye-fill me-1" />
|
||||
<span>{item.view_count}</span>
|
||||
</div>
|
||||
<Counts
|
||||
isAccepted={Number(item.accepted_answer_id) > 0}
|
||||
data={{
|
||||
votes: item.vote_count,
|
||||
answers: item.answer_count,
|
||||
views: item.view_count,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{item.tags?.map((tag) => {
|
||||
|
||||
@@ -32,9 +32,12 @@ const Index: FC<Props> = ({ data, type }) => {
|
||||
}>
|
||||
{type === 'answer' ? item.question_info.title : item.title}
|
||||
</a>
|
||||
|
||||
<div className="d-inline-block text-secondary ms-3 fs-14">
|
||||
<Icon name="hand-thumbs-up-fill" />
|
||||
<span> {item.vote_count}</span>
|
||||
<Icon name="hand-thumbs-up-fill me-1" />
|
||||
<span>
|
||||
{item.vote_count} {t('votes', { keyPrefix: 'counts' })}
|
||||
</span>
|
||||
</div>
|
||||
{type === 'question' && (
|
||||
<div
|
||||
@@ -47,7 +50,10 @@ const Index: FC<Props> = ({ data, type }) => {
|
||||
<Icon name="chat-square-text-fill" />
|
||||
)}
|
||||
|
||||
<span> {item.answer_count}</span>
|
||||
<span>
|
||||
{' '}
|
||||
{item.answer_count} {t('answers', { keyPrefix: 'counts' })}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
+28
-7
@@ -1,4 +1,6 @@
|
||||
import i18next from 'i18next';
|
||||
import parse from 'html-react-parser';
|
||||
import * as DOMPurify from 'dompurify';
|
||||
|
||||
const Diff = require('diff');
|
||||
|
||||
@@ -21,7 +23,7 @@ function formatCount($num: number): string {
|
||||
return res;
|
||||
}
|
||||
|
||||
function scrollTop(element) {
|
||||
function scrollToElementTop(element) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
@@ -36,6 +38,15 @@ function scrollTop(element) {
|
||||
});
|
||||
}
|
||||
|
||||
const scrollToDocTop = () => {
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const bgFadeOut = (el) => {
|
||||
if (el && !el.classList.contains('bg-fade-out')) {
|
||||
el.classList.add('bg-fade-out');
|
||||
@@ -160,14 +171,17 @@ function handleFormError(
|
||||
) {
|
||||
if (error.list?.length > 0) {
|
||||
error.list.forEach((item) => {
|
||||
data[item.error_field].isInvalid = true;
|
||||
data[item.error_field].errorMsg = item.error_msg;
|
||||
const errorFieldObject = data[item.error_field];
|
||||
if (errorFieldObject) {
|
||||
errorFieldObject.isInvalid = true;
|
||||
errorFieldObject.errorMsg = item.error_msg;
|
||||
}
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function diffText(newText: string, oldText: string): string {
|
||||
function diffText(newText: string, oldText?: string): string {
|
||||
if (!newText) {
|
||||
return '';
|
||||
}
|
||||
@@ -181,8 +195,6 @@ function diffText(newText: string, oldText: string): string {
|
||||
?.replace(/<input/gi, '<input');
|
||||
}
|
||||
const diff = Diff.diffChars(oldText, newText);
|
||||
console.log(diff);
|
||||
|
||||
const result = diff.map((part) => {
|
||||
if (part.added) {
|
||||
if (part.value.replace(/\n/g, '').length <= 0) {
|
||||
@@ -214,10 +226,18 @@ function diffText(newText: string, oldText: string): string {
|
||||
?.replace(/<input/gi, '<input');
|
||||
}
|
||||
|
||||
function htmlToReact(html: string) {
|
||||
const cleanedHtml = DOMPurify.sanitize(html, {
|
||||
USE_PROFILES: { html: true },
|
||||
});
|
||||
return parse(cleanedHtml);
|
||||
}
|
||||
|
||||
export {
|
||||
thousandthDivision,
|
||||
formatCount,
|
||||
scrollTop,
|
||||
scrollToElementTop,
|
||||
scrollToDocTop,
|
||||
bgFadeOut,
|
||||
matchedUsers,
|
||||
parseUserInfo,
|
||||
@@ -228,4 +248,5 @@ export {
|
||||
labelStyle,
|
||||
handleFormError,
|
||||
diffText,
|
||||
htmlToReact,
|
||||
};
|
||||
|
||||
@@ -267,7 +267,7 @@ export const initAppSettingsStore = async () => {
|
||||
};
|
||||
|
||||
export const shouldInitAppFetchData = () => {
|
||||
if (isIgnoredPath('/install')) {
|
||||
if (isIgnoredPath('/install') && window.location.pathname === '/install') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class Request {
|
||||
},
|
||||
(error) => {
|
||||
const { status, data: respData } = error.response || {};
|
||||
const { data = {}, msg = '' } = respData || {};
|
||||
const { data = {}, msg = '', reason = '' } = respData || {};
|
||||
if (status === 400) {
|
||||
// show error message
|
||||
if (data instanceof Object && data.err_type) {
|
||||
@@ -79,7 +79,13 @@ class Request {
|
||||
|
||||
if (data instanceof Array && data.length > 0) {
|
||||
// handle form error
|
||||
return Promise.reject({ isError: true, list: data });
|
||||
return Promise.reject({
|
||||
code: status,
|
||||
msg,
|
||||
reason,
|
||||
isError: true,
|
||||
list: data,
|
||||
});
|
||||
}
|
||||
|
||||
if (!data || Object.keys(data).length <= 0) {
|
||||
|
||||
Reference in New Issue
Block a user