Merge branch 'dev' into feat/ui-1.1.0

This commit is contained in:
haitaoo 2023-04-12 12:25:41 +08:00
commit f37218bc70
87 changed files with 5279 additions and 970 deletions

View File

@ -15,7 +15,7 @@ builds:
- id: build - id: build
main: ./cmd/answer/. main: ./cmd/answer/.
binary: answer binary: answer
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser ldflags: -s -w -X github.com/answerdev/answer/cmd.Version={{.Version}} -X github.com/answerdev/answer/cmd.Revision={{.ShortCommit}} -X github.com/answerdev/answer/cmd.Time={{.Date}} -X main.BuildUser=goreleaser
flags: -v flags: -v
goos: goos:
- linux - linux
@ -26,7 +26,7 @@ builds:
- id: build-windows - id: build-windows
main: ./cmd/answer/. main: ./cmd/answer/.
binary: answer binary: answer
ldflags: -s -w -X main.Version={{.Version}} -X main.Revision={{.ShortCommit}} -X main.Time={{.Date}} -X main.BuildUser=goreleaser ldflags: -s -w -X github.com/answerdev/answer/cmd.Version={{.Version}} -X github.com/answerdev/answer/cmd.Revision={{.ShortCommit}} -X github.com/answerdev/answer/cmd.Time={{.Date}} -X main.BuildUser=goreleaser
flags: -v flags: -v
goos: goos:
- windows - windows

View File

@ -1,6 +1,6 @@
.PHONY: build clean ui .PHONY: build clean ui
VERSION=1.0.7 VERSION=1.0.8
BIN=answer BIN=answer
DIR_SRC=./cmd/answer DIR_SRC=./cmd/answer
DOCKER_CMD=docker DOCKER_CMD=docker

23
charts/.helmignore Normal file
View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -1,6 +1,6 @@
apiVersion: v2 apiVersion: v2
name: answer name: answer
description: a simple answer deployments for kubernetes description: A simple answer deployments for kubernetes
type: application type: application
version: 0.1.0 version: 0.1.0
appVersion: "0.1.0" appVersion: "1.0.7"

View File

@ -1,2 +1,75 @@
# Helm Charts for Answer project # answer
An open-source knowledge-based community software. You can use it quickly to build Q&A community for your products, customers, teams, and more.
## Prerequisites
- Kubernetes 1.20+
## Configuration
The following table lists the configurable parameters of the answer chart and their default values.
| Parameter | Description | Default |
| --------- | ----------- | ------- |
| `replicaCount` | Number of answer replicas | `1` |
| `image.repository` | Image repository | `answerdev/answer` |
| `image.pullPolicy` | Image pull policy | `Always` |
| `image.tag` | Image tag | `latest` |
| `env` | Optional environment variables for answer | `LOG_LEVEL: INFO` |
| `extraContainers` | Optional sidecar containers to run along side answer | `[]` |
| `persistence.enabled` | Enable or disable persistence for the /data volume | `true` |
| `persistence.accessMode` | Specify the access mode of the persistent volume | `ReadWriteOnce` |
| `persistence.size` | The size of the persistent volume | `5Gi` |
| `persistence.annotations` | Annotations to add to the volume claim | `{}` |
| `imagePullSecrets` | Reference to one or more secrets to be used when pulling images | `[]` |
| `nameOverride` | nameOverride replaces the name of the chart in the Chart.yaml file, when this is used to construct Kubernetes object names. | |
| `fullnameOverride` | fullnameOverride completely replaces the generated name. | |
| `serviceAccount.create` | If `true`, create a new service account | `true` |
| `serviceAccount.annotations` | Annotations to add to the service account | `{}` |
| `serviceAccount.name` | Service account to be used. If not set and `serviceAccount.create` is `true`, a name is generated using the fullname template | |
| `podAnnotations` | Annotations to add to the answer pod | `{}` |
| `podSecurityContext` | Security context for the answer pod | `{}` refer to [Default Security Contexts](#default-security-contexts) |
| `securityContext` | Security context for the answer container | `{}` refer to [Default Security Contexts](#default-security-contexts) |
| `service.type` | The type of service to be used | `ClusterIP` |
| `service.port` | The port that the service should listen on for requests. Also used as the container port. | `80` |
| `ingress.enabled` | Enable or disable ingress. | `false` |
| `resources` | CPU/memory resource requests/limits | `{}` |
| `autoscaling.enabled` | Enable or disable pod autoscaling. If enabled, replicas are disabled. | `false` |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Node tolerations for pod assignment | `[]` |
| `affinity` | Node affinity for pod assignment | `{}` |
### Default Security Contexts
The default pod-level and container-level security contexts, below, adhere to the [restricted](https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted) Pod Security Standards policies.
Default pod-level securityContext:
```yaml
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
```
Default containerSecurityContext:
```yaml
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
```
### Installing with a Values file
```console
$ helm install answer -f values.yaml .
```
> **Tip**: You can use the default [values.yaml]
## TODO
Publish the chart to Artifacthub and add proper installation instructions. E.G.
> **NOTE**: This is not currently a valid installation option.
```console
$ helm repo add answerdev https://charts.answer.dev/
$ helm repo update
$ helm install answerdev/answer -n mynamespace
```

View File

@ -34,7 +34,7 @@ Create chart name and version as used by the chart label.
Common labels Common labels
*/}} */}}
{{- define "answer.labels" -}} {{- define "answer.labels" -}}
helm.sh/chart: {{ .Release.Name }} helm.sh/chart: {{ include "answer.chart" . }}
{{ include "answer.selectorLabels" . }} {{ include "answer.selectorLabels" . }}
{{- if .Chart.AppVersion }} {{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
@ -46,6 +46,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }}
Selector labels Selector labels
*/}} */}}
{{- define "answer.selectorLabels" -}} {{- define "answer.selectorLabels" -}}
app.kubernetes.io/name: answer app.kubernetes.io/name: {{ include "answer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }} {{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "answer.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "answer.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@ -1,9 +0,0 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: answer-config
namespace: {{ .Values.namespace | default "default" | quote }}
data:
default.yaml: |-
#

View File

@ -0,0 +1,88 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "answer.fullname" . }}
labels:
{{- include "answer.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "answer.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "answer.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "answer.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- if .Values.env }}
env:
{{- range .Values.env }}
- name: {{ .name }}
{{- if .value | quote }}
value: {{ .value | quote }}
{{- end }}
{{- if .valueFrom }}
valueFrom:
{{- toYaml .valueFrom | nindent 16 }}
{{- end }}
{{- end }}
{{- end }}
volumeMounts:
- name: data
mountPath: "/data"
{{- if .Values.extraContainers }}
{{- toYaml .Values.extraContainers | nindent 8 }}
{{- end }}
volumes:
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "answer.fullname" . }}-claim
{{- else }}
emptyDir: {}
{{- end -}}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

28
charts/templates/hpa.yaml Normal file
View File

@ -0,0 +1,28 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "answer.fullname" . }}
labels:
{{- include "answer.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "answer.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "answer.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "answer.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

25
charts/templates/pvc.yaml Normal file
View File

@ -0,0 +1,25 @@
{{- if .Values.persistence.enabled }}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ include "answer.fullname" . }}-claim
{{- with .Values.persistence.annotations }}
annotations:
{{ toYaml . | indent 4 }}
{{- end }}
labels:
{{- include "answer.labels" . | nindent 4 }}
spec:
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistence.storageClass }}"
{{- end }}
{{- end }}
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- end }}

View File

@ -1,17 +1,15 @@
---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: {{ include "answer.fullname" . }} name: {{ include "answer.fullname" . }}
labels: labels:
{{- include "answer.labels" . | nindent 4 }} {{- include "answer.labels" . | nindent 4 }}
namespace: {{ .Values.namespace | default "default" | quote }}
spec: spec:
type: ClusterIP type: {{ .Values.service.type }}
ports: ports:
- name: answer - port: {{ .Values.service.port }}
port: 80 targetPort: http
targetPort: 80
protocol: TCP protocol: TCP
name: http
selector: selector:
{{- include "answer.selectorLabels" . | nindent 4 }} {{- include "answer.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "answer.serviceAccountName" . }}
labels:
{{- include "answer.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,31 +0,0 @@
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: answer
namespace: {{ .Values.namespace | default "default" | quote }}
spec:
selector:
matchLabels:
{{- include "answer.labels" . | nindent 6 }}
serviceName: answer
replicas: 1
template:
metadata:
labels:
{{- include "answer.labels" . | nindent 8 }}
spec:
containers:
- name: answer
image: nginx:stable
ports:
- containerPort: 80
name: answer-ui
volumeMounts:
- name: config
mountPath: "/etc/answer.yaml"
subPath: default.yaml
volumes:
- name: config
configMap:
name: answer-config

View File

@ -1 +1,147 @@
namespace: default # Default values for answer.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: answerdev/answer
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
# Environment variables
# Configure environment variables below
# https://answer.dev/docs/env
env:
- name: LOG_LEVEL
# [DEBUG INFO WARN ERROR]
value: "INFO"
# uncomment the below values to use AUTO_INSTALL and not have to go through the setup process.
# Once used to do the initial setup, these variables wont be used moving forward.
# You must at a minimum comment AUTO_INSTALL after initial setup to prevent an error about the database already being initiated.
# - name: AUTO_INSTALL
# value: "true"
# - name: DB_TYPE
# value: "sqlite3"
# # DB_FILE Only for sqlite3
# - name: DB_FILE
# value: "/data/answer.db"
# - name: LANGUAGE
# value: "en-US"
# - name: SITE_NAME
# value: "MyAnswer"
# - name: SITE_URL
# value: "http://localhost:80"
# - name: CONTACT_EMAIL
# value: "support@mydomain.com"
# - name: ADMIN_NAME
# # lowercase
# value: "myadmin"
# - name: ADMIN_PASSWORD
# # 32 Characters MAX
# value: "MyInsecurePasswordInTheRepo!"
# # Use valueFrom to use a secret
# # valueFrom:
# # secretKeyRef:
# # key: answer-admin-password
# # name: answer-secrets
# - name: ADMIN_EMAIL
# value: "myAdmin@mydomain.com"
# Configure extra containers
extraContainers: []
# - name: cloudsql-proxy
# image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.1.2
# command:
# - /cloud-sql-proxy
# args:
# - project:region:instance
# - --port=5432
# - --auto-iam-authn
# ports:
# - containerPort: 5432
# Persistence for the /data volume
# Without persistence, your uploads and config.yaml will not be remembered between restarts.
persistence:
enabled: true
# If set to "-", storageClassName: "", which disables dynamic provisioning
# If undefined (the default) or set to null, no storageClassName spec is
# set, choosing the default provisioner. (gp2 on AWS, standard on
# GKE, AWS & OpenStack)
# storageClass: "-"
accessMode: ReadWriteOnce
size: 5Gi
annotations: {}
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: answer.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: answer-tls
# hosts:
# - answer.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}

View File

@ -53,6 +53,7 @@ func runApp() {
panic(err) panic(err)
} }
constant.Version = Version constant.Version = Version
constant.Revision = Revision
schema.AppStartTime = time.Now() schema.AppStartTime = time.Now()
defer cleanup() defer cleanup()

View File

@ -165,8 +165,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo) questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo)
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo) answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo)
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData) questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData)
questionController := controller.NewQuestionController(questionService, rankService)
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService) answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService)
questionController := controller.NewQuestionController(questionService, answerService, rankService)
dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData) dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData)
answerController := controller.NewAnswerController(answerService, rankService, dashboardService) answerController := controller.NewAnswerController(answerService, rankService, dashboardService)
searchParser := search_parser.NewSearchParser(tagCommonService, userCommon) searchParser := search_parser.NewSearchParser(tagCommonService, userCommon)

View File

@ -3063,6 +3063,45 @@ const docTemplate = `{
} }
} }
}, },
"/answer/api/v1/question/answer": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add question and answer",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "add question and answer",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.QuestionAddByAnswer"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/closemsglist": { "/answer/api/v1/question/closemsglist": {
"get": { "get": {
"security": [ "security": [
@ -6751,6 +6790,41 @@ const docTemplate = `{
} }
} }
}, },
"schema.QuestionAddByAnswer": {
"type": "object",
"required": [
"answer_content",
"content",
"tags",
"title"
],
"properties": {
"answer_content": {
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"content": {
"description": "content",
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"tags": {
"description": "tags",
"type": "array",
"items": {
"$ref": "#/definitions/schema.TagItem"
}
},
"title": {
"description": "question title",
"type": "string",
"maxLength": 150,
"minLength": 6
}
}
},
"schema.QuestionPageReq": { "schema.QuestionPageReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -7230,6 +7304,9 @@ const docTemplate = `{
"login": { "login": {
"$ref": "#/definitions/schema.SiteLoginResp" "$ref": "#/definitions/schema.SiteLoginResp"
}, },
"revision": {
"type": "string"
},
"site_seo": { "site_seo": {
"$ref": "#/definitions/schema.SiteSeoReq" "$ref": "#/definitions/schema.SiteSeoReq"
}, },

View File

@ -3051,6 +3051,45 @@
} }
} }
}, },
"/answer/api/v1/question/answer": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "add question and answer",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Question"
],
"summary": "add question and answer",
"parameters": [
{
"description": "question",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.QuestionAddByAnswer"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/question/closemsglist": { "/answer/api/v1/question/closemsglist": {
"get": { "get": {
"security": [ "security": [
@ -6739,6 +6778,41 @@
} }
} }
}, },
"schema.QuestionAddByAnswer": {
"type": "object",
"required": [
"answer_content",
"content",
"tags",
"title"
],
"properties": {
"answer_content": {
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"content": {
"description": "content",
"type": "string",
"maxLength": 65535,
"minLength": 6
},
"tags": {
"description": "tags",
"type": "array",
"items": {
"$ref": "#/definitions/schema.TagItem"
}
},
"title": {
"description": "question title",
"type": "string",
"maxLength": 150,
"minLength": 6
}
}
},
"schema.QuestionPageReq": { "schema.QuestionPageReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -7218,6 +7292,9 @@
"login": { "login": {
"$ref": "#/definitions/schema.SiteLoginResp" "$ref": "#/definitions/schema.SiteLoginResp"
}, },
"revision": {
"type": "string"
},
"site_seo": { "site_seo": {
"$ref": "#/definitions/schema.SiteSeoReq" "$ref": "#/definitions/schema.SiteSeoReq"
}, },

View File

@ -1019,6 +1019,33 @@ definitions:
- tags - tags
- title - title
type: object type: object
schema.QuestionAddByAnswer:
properties:
answer_content:
maxLength: 65535
minLength: 6
type: string
content:
description: content
maxLength: 65535
minLength: 6
type: string
tags:
description: tags
items:
$ref: '#/definitions/schema.TagItem'
type: array
title:
description: question title
maxLength: 150
minLength: 6
type: string
required:
- answer_content
- content
- tags
- title
type: object
schema.QuestionPageReq: schema.QuestionPageReq:
properties: properties:
orderCond: orderCond:
@ -1354,6 +1381,8 @@ definitions:
$ref: '#/definitions/schema.SiteInterfaceResp' $ref: '#/definitions/schema.SiteInterfaceResp'
login: login:
$ref: '#/definitions/schema.SiteLoginResp' $ref: '#/definitions/schema.SiteLoginResp'
revision:
type: string
site_seo: site_seo:
$ref: '#/definitions/schema.SiteSeoReq' $ref: '#/definitions/schema.SiteSeoReq'
theme: theme:
@ -3794,6 +3823,30 @@ paths:
summary: update question summary: update question
tags: tags:
- Question - Question
/answer/api/v1/question/answer:
post:
consumes:
- application/json
description: add question and answer
parameters:
- description: question
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.QuestionAddByAnswer'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: add question and answer
tags:
- Question
/answer/api/v1/question/closemsglist: /answer/api/v1/question/closemsglist:
get: get:
consumes: consumes:

13
go.mod
View File

@ -27,6 +27,7 @@ require (
github.com/mojocn/base64Captcha v1.3.5 github.com/mojocn/base64Captcha v1.3.5
github.com/ory/dockertest/v3 v3.9.1 github.com/ory/dockertest/v3 v3.9.1
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405
github.com/segmentfault/pacman v1.0.3 github.com/segmentfault/pacman v1.0.3
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05
@ -41,7 +42,7 @@ require (
github.com/tidwall/gjson v1.14.4 github.com/tidwall/gjson v1.14.4
github.com/yuin/goldmark v1.4.13 github.com/yuin/goldmark v1.4.13
golang.org/x/crypto v0.1.0 golang.org/x/crypto v0.1.0
golang.org/x/net v0.2.0 golang.org/x/net v0.7.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.14.2 modernc.org/sqlite v1.14.2
@ -64,14 +65,20 @@ require (
github.com/docker/docker v20.10.7+incompatible // indirect github.com/docker/docker v20.10.7+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect
github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f // indirect
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20190422055009-d6f9ba25cf48 // indirect
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 // indirect
github.com/dsoprea/go-png-image-structure v0.0.0-20190624104353-c9b28dcdc5c8 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.7 // indirect github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/swag v0.22.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/geo v0.0.0-20190812012225-f41920e961ce // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/css v1.0.0 // indirect
@ -121,8 +128,8 @@ require (
go.uber.org/zap v1.23.0 // indirect go.uber.org/zap v1.23.0 // indirect
golang.org/x/image v0.1.0 // indirect golang.org/x/image v0.1.0 // indirect
golang.org/x/mod v0.6.0 // indirect golang.org/x/mod v0.6.0 // indirect
golang.org/x/sys v0.2.0 // indirect golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.2.0 // indirect golang.org/x/tools v0.2.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

26
go.sum
View File

@ -143,6 +143,14 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f h1:vqfYiZ+xF0xJvl9SZ1kovmMgKjaGZIz/Hn8JDQdyd9A=
github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f/go.mod h1:DmMpU91/Ax6BAwoRkjgRCr2rmgEgS4tsmatfV7M+U+c=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20190422055009-d6f9ba25cf48 h1:9zARagUAxQJjibcDy+0+koUMR6sbX38L49Bk2Vni628=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20190422055009-d6f9ba25cf48/go.mod h1:H1hAaFyv9cRV1ywoHvaqVoNSThBvWZ0JarRBcV+FSnE=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-png-image-structure v0.0.0-20190624104353-c9b28dcdc5c8 h1:SVQfy5rBFZXzvGkU2MZ0RzpS912/1sJrEJ+FMmeaC9U=
github.com/dsoprea/go-png-image-structure v0.0.0-20190624104353-c9b28dcdc5c8/go.mod h1:Bf0nmcDFFRQBjZwr9qY6c0zTxKQa+Q8YWZmlYxXGxY0=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -173,6 +181,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -229,6 +239,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20190812012225-f41920e961ce h1:rqIKPpIcEgiNn0KYNFYD34TbMO86l4woyhNzSP+Oegs=
github.com/golang/geo v0.0.0-20190812012225-f41920e961ce/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -605,6 +617,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405 h1:2ieGkj4z/YPXVyQ2ayZUg3GwE1pYWd5f1RB6DzAOXKM=
github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405/go.mod h1:rIxVzVLKlBwLxO+lC+k/I4HJfRQcemg/f/76Xmmzsec=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/segmentfault/pacman v1.0.3 h1:/K8LJHQMiCaCIvC/e8GQITpYTEG6RH4KTLTZjPTghl4= github.com/segmentfault/pacman v1.0.3 h1:/K8LJHQMiCaCIvC/e8GQITpYTEG6RH4KTLTZjPTghl4=
@ -851,8 +865,9 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -945,12 +960,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -960,8 +977,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1144,6 +1161,7 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

1414
i18n/cy_GB.yaml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,17 @@ backend:
other: Unauthorized. other: Unauthorized.
database_error: database_error:
other: Data server error. other: Data server error.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -100,6 +111,8 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Rank fail to meet the condition. other: Rank fail to meet the condition.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Report handle failed. other: Report handle failed.

View File

@ -11,6 +11,17 @@ backend:
other: No autorizado. other: No autorizado.
database_error: database_error:
other: Error en el servidor de datos. other: Error en el servidor de datos.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Sin permiso para eliminar. other: Sin permiso para eliminar.
cannot_update: cannot_update:
other: Sin permiso para actualizar. other: Sin permiso para actualizar.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Edición del comentario no permitida. other: Edición del comentario no permitida.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: El rango no cumple la condición. other: El rango no cumple la condición.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Error en el manejador del reporte. other: Error en el manejador del reporte.
not_found: not_found:
other: Informe no encontrado. other: Informe no encontrado.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Etiqueta no encontrada. other: Etiqueta no encontrada.
recommend_tag_not_found: recommend_tag_not_found:
@ -282,6 +299,7 @@ ui:
tag: Etiqueta tag: Etiqueta
tags: Etiquetas tags: Etiquetas
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Editar etiqueta edit_tag: Editar etiqueta
ask_a_question: Añadir Pregunta ask_a_question: Añadir Pregunta
edit_question: Editar Pregunta edit_question: Editar Pregunta
@ -302,6 +320,9 @@ ui:
upgrade: Actualización de Answer upgrade: Actualización de Answer
maintenance: Mantenimiento del sitio web maintenance: Mantenimiento del sitio web
users: Usuarios users: Usuarios
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notificaciones title: Notificaciones
inbox: Buzón de entrada inbox: Buzón de entrada
@ -454,6 +475,7 @@ ui:
label: Description label: Description
btn_cancel: Cancelar btn_cancel: Cancelar
btn_submit: Enviar btn_submit: Enviar
btn_post: Post new tag
tag_info: tag_info:
created_at: Creado created_at: Creado
edited_at: Editado edited_at: Editado
@ -518,6 +540,7 @@ ui:
Utiliza los comentarios para pedir más información o sugerir mejoras y modificaciones. Evita responder preguntas en los comentarios. Utiliza los comentarios para pedir más información o sugerir mejoras y modificaciones. Evita responder preguntas en los comentarios.
tip_answer: >- tip_answer: >-
Usa comentarios para responder a otros usuarios o notificarles de cambios. Si estás añadiendo nueva información, edita tu publicación en vez de comentar. Usa comentarios para responder a otros usuarios o notificarles de cambios. Si estás añadiendo nueva información, edita tu publicación en vez de comentar.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Editar respuesta title: Editar respuesta
default_reason: Editar respuesta default_reason: Editar respuesta
@ -767,6 +790,11 @@ ui:
answered: respondida answered: respondida
closed_in: Cerrado el closed_in: Cerrado el
show_exist: Mostrar una pregunta existente. show_exist: Mostrar una pregunta existente.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Respuestas title: Respuestas
score: Puntuación score: Puntuación
@ -783,7 +811,14 @@ ui:
<p>¿Seguro que quieres añadir otra respuesta?</p><p>Puedes utilizar el enlace de edición para detallar y mejorar tu respuesta existente en su lugar.</p> <p>¿Seguro que quieres añadir otra respuesta?</p><p>Puedes utilizar el enlace de edición para detallar y mejorar tu respuesta existente en su lugar.</p>
empty: La respuesta no puede estar vacía. empty: La respuesta no puede estar vacía.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reabrir esta publicación title: Reabrir esta publicación
content: '¿Seguro que quieres reabrir esta publicación?' content: '¿Seguro que quieres reabrir esta publicación?'
success: Esta publicación ha sido reabierta success: Esta publicación ha sido reabierta
@ -1002,13 +1037,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1223,6 +1256,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1388,8 +1424,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Usuarios title: Usuarios
users_with_the_most_reputation: Usuarios con la reputación más alta users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Usuarios que más han votado users_with_the_most_vote: Users who voted the most this week
staffs: Nuestor equipo de la comunidad staffs: Nuestor equipo de la comunidad
reputation: reputación reputation: reputación
votes: votos votes: votos

View File

@ -11,6 +11,17 @@ backend:
other: Non autorisé. other: Non autorisé.
database_error: database_error:
other: Erreur du serveur de données. other: Erreur du serveur de données.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -35,9 +46,9 @@ backend:
error: error:
admin: admin:
cannot_update_their_password: cannot_update_their_password:
other: You cannot modify your password. other: Vous ne pouvez pas modifier votre mot de passe.
cannot_modify_self_status: cannot_modify_self_status:
other: You cannot modify your status. other: Vous ne pouvez pas modifier votre statut.
email_or_password_wrong: email_or_password_wrong:
other: L'email et le mot de passe ne correspondent pas. other: L'email et le mot de passe ne correspondent pas.
answer: answer:
@ -47,6 +58,8 @@ backend:
other: Pas de permission pour supprimer. other: Pas de permission pour supprimer.
cannot_update: cannot_update:
other: Pas de permission pour mettre à jour. other: Pas de permission pour mettre à jour.
question_closed_cannot_add:
other: Les questions sont fermées et ne peuvent pas être ajoutées.
comment: comment:
edit_without_permission: edit_without_permission:
other: Les commentaires ne sont pas autorisés à être modifiés. other: Les commentaires ne sont pas autorisés à être modifiés.
@ -85,7 +98,7 @@ backend:
other: Le nouveau mot de passe est le même que le précédent. other: Le nouveau mot de passe est le même que le précédent.
question: question:
already_deleted: already_deleted:
other: This post has been deleted. other: Ce message a été supprimé.
not_found: not_found:
other: Question non trouvée. other: Question non trouvée.
cannot_deleted: cannot_deleted:
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Le rang ne remplit pas la condition. other: Le rang ne remplit pas la condition.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: La gestion du rapport a échoué. other: La gestion du rapport a échoué.
not_found: not_found:
other: Rapport non trouvé. other: Rapport non trouvé.
tag: tag:
already_exist:
other: Le tag existe déjà.
not_found: not_found:
other: Tag non trouvé. other: Tag non trouvé.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Étiquette tag: Étiquette
tags: Étiquettes tags: Étiquettes
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Créer un tag
edit_tag: Modifier l'étiquette edit_tag: Modifier l'étiquette
ask_a_question: Ajouter une question ask_a_question: Ajouter une question
edit_question: Modifier la question edit_question: Modifier la question
@ -281,6 +299,9 @@ ui:
upgrade: Mise à jour d'Answer upgrade: Mise à jour d'Answer
maintenance: Maintenance du site maintenance: Maintenance du site
users: Utilisateurs users: Utilisateurs
http_404: Erreur HTTP 404
http_50X: Erreur HTTP 500
http_403: HTTP Error 403
notifications: notifications:
title: Notifications title: Notifications
inbox: Boîte de réception inbox: Boîte de réception
@ -424,7 +445,7 @@ ui:
range: Le nom doit contenir moins de 35 caractères. range: Le nom doit contenir moins de 35 caractères.
slug_name: slug_name:
label: URL simplifiée label: URL simplifiée
desc: 'Doit utiliser le jeu de caractères "a-z", "0-9", "+ # - ."' desc: Titre de 35 caractères maximum.
msg: msg:
empty: L'URL ne peut pas être vide. empty: L'URL ne peut pas être vide.
range: Titre de 35 caractères maximum. range: Titre de 35 caractères maximum.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Annuler btn_cancel: Annuler
btn_submit: Valider btn_submit: Valider
btn_post: Publier un nouveau tag
tag_info: tag_info:
created_at: Créé created_at: Créé
edited_at: Modifié edited_at: Modifié
@ -497,6 +519,7 @@ ui:
Utilisez les commentaires pour demander plus d'informations ou suggérer des améliorations. Évitez de répondre aux questions dans les commentaires. Utilisez les commentaires pour demander plus d'informations ou suggérer des améliorations. Évitez de répondre aux questions dans les commentaires.
tip_answer: >- tip_answer: >-
Utilisez des commentaires pour répondre à d'autres utilisateurs ou leur signaler des modifications. Si vous ajoutez de nouvelles informations, modifiez votre message au lieu de commenter. Utilisez des commentaires pour répondre à d'autres utilisateurs ou leur signaler des modifications. Si vous ajoutez de nouvelles informations, modifiez votre message au lieu de commenter.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Modifier la réponse title: Modifier la réponse
default_reason: Modifier la réponse default_reason: Modifier la réponse
@ -746,6 +769,11 @@ ui:
answered: répondu answered: répondu
closed_in: Fermé dans closed_in: Fermé dans
show_exist: Afficher la question existante. show_exist: Afficher la question existante.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Réponses title: Réponses
score: Score score: Score
@ -762,7 +790,14 @@ ui:
<p>Êtes-vous sûr de vouloir ajouter une autre réponse ?</p><p>Vous pouvez utiliser le lien d'édition pour affiner et améliorer votre réponse existante.</p> <p>Êtes-vous sûr de vouloir ajouter une autre réponse ?</p><p>Vous pouvez utiliser le lien d'édition pour affiner et améliorer votre réponse existante.</p>
empty: La réponse ne peut être vide. empty: La réponse ne peut être vide.
characters: le contenu doit comporter au moins 6 caractères. characters: le contenu doit comporter au moins 6 caractères.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Rouvrir ce message title: Rouvrir ce message
content: Êtes-vous sûr de vouloir rouvrir ? content: Êtes-vous sûr de vouloir rouvrir ?
success: Ce message a été rouvert success: Ce message a été rouvert
@ -788,7 +823,7 @@ ui:
approve: Approuver approve: Approuver
reject: Rejeter reject: Rejeter
skip: Ignorer skip: Ignorer
discard_draft: Discard draft discard_draft: Abandonner le brouillon
search: search:
title: Résultats de la recherche title: Résultats de la recherche
keywords: Mots-clés keywords: Mots-clés
@ -977,14 +1012,12 @@ ui:
votes: votes votes: votes
answers: réponses answers: réponses
accepted: Accepté accepted: Accepté
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Nous sommes désolés, mais cette page nexiste pas." desc_403: You dont have permission to access this page.
back_home: Retour à la page d'accueil desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500 back_home: Back to homepage
desc: Le serveur a rencontré une erreur et n'a pas pu répondre à votre requête.
back_home: Retour à la page d'accueil
page_maintenance: page_maintenance:
desc: "Nous sommes en maintenance, nous serons bientôt de retour." desc: "Nous sommes en maintenance, nous serons bientôt de retour."
nav_menus: nav_menus:
@ -1198,6 +1231,9 @@ ui:
label: Fuseau Horaire label: Fuseau Horaire
msg: Le fuseau horaire ne peut pas être vide. msg: Le fuseau horaire ne peut pas être vide.
text: Choisissez une ville dans le même fuseau horaire que vous. text: Choisissez une ville dans le même fuseau horaire que vous.
avatar:
label: Avatar par défaut
text: Pour les utilisateurs sans avatar personnalisé.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "Nous n'avons rien pu trouver." no_data: "Nous n'avons rien pu trouver."
users: users:
title: Utilisateurs title: Utilisateurs
users_with_the_most_reputation: Utilisateurs avec les scores de réputation les plus élevés users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Utilisateurs qui ont le plus voté users_with_the_most_vote: Users who voted the most this week
staffs: Staff de la communauté staffs: Staff de la communauté
reputation: réputation reputation: réputation
votes: votes votes: votes
@ -1372,7 +1408,7 @@ ui:
leave_page: Voulez-vous vraiment quitter la page ? leave_page: Voulez-vous vraiment quitter la page ?
changes_not_save: Impossible d'enregistrer vos modifications. changes_not_save: Impossible d'enregistrer vos modifications.
draft: draft:
discard_confirm: Are you sure you want to discard your draft? discard_confirm: Êtes-vous sûr de vouloir abandonner ce brouillon ?
messages: messages:
post_deleted: This post has been deleted. post_deleted: Ce message a été supprimé.

View File

@ -39,3 +39,6 @@ language_options:
- label: "Tiếng Việt" - label: "Tiếng Việt"
value: "vi_VN" value: "vi_VN"
progress: 0 progress: 0
- label: "Slovak"
value: "sk_SK"
progress: 100

View File

@ -11,6 +11,17 @@ backend:
other: Tidak diizinkan. other: Tidak diizinkan.
database_error: database_error:
other: Kesalahan data server. other: Kesalahan data server.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Tidak memiliki izin untuk menghapus. other: Tidak memiliki izin untuk menghapus.
cannot_update: cannot_update:
other: Tidak memiliki izin untuk memperbaharui. other: Tidak memiliki izin untuk memperbaharui.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Komentar tidak boleh diedit. other: Komentar tidak boleh diedit.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Peringkat gagal memenuhi syarat. other: Peringkat gagal memenuhi syarat.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Laporan penanganan gagal. other: Laporan penanganan gagal.
not_found: not_found:
other: Laporan tidak ditemukan. other: Laporan tidak ditemukan.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Tag tidak ditemukan. other: Tag tidak ditemukan.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Ubah Tag edit_tag: Ubah Tag
ask_a_question: Tambahkan Pertanyaan ask_a_question: Tambahkan Pertanyaan
edit_question: Sunting Pertanyaan edit_question: Sunting Pertanyaan
@ -281,6 +299,9 @@ ui:
upgrade: Meng-upgrade Answer upgrade: Meng-upgrade Answer
maintenance: Pemeliharaan Website maintenance: Pemeliharaan Website
users: Pengguna users: Pengguna
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Pemberitahuan title: Pemberitahuan
inbox: Kotak Masuk inbox: Kotak Masuk
@ -424,7 +445,7 @@ ui:
range: Display name up to 35 characters. range: Display name up to 35 characters.
slug_name: slug_name:
label: URL Slug label: URL Slug
desc: 'Must use the character set "a-z", "0-9", "+ # - ."' desc: URL slug up to 35 characters.
msg: msg:
empty: URL slug cannot be empty. empty: URL slug cannot be empty.
range: URL slug up to 35 characters. range: URL slug up to 35 characters.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Dibuat created_at: Dibuat
edited_at: Disunting edited_at: Disunting
@ -497,6 +519,7 @@ ui:
Gunakan komentar untuk meminta informasi lebih lanjut atau menyarankan perbaikan. Hindari menjawab pertanyaan di komentar. Gunakan komentar untuk meminta informasi lebih lanjut atau menyarankan perbaikan. Hindari menjawab pertanyaan di komentar.
tip_answer: >- tip_answer: >-
Gunakan komentar untuk membalas pengguna lain atau memberi tahu mereka tentang perubahan. Jika Anda menambahkan informasi baru, cukup edit posting Anda. Gunakan komentar untuk membalas pengguna lain atau memberi tahu mereka tentang perubahan. Jika Anda menambahkan informasi baru, cukup edit posting Anda.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Sunting jawaban title: Sunting jawaban
default_reason: Edit jawaban default_reason: Edit jawaban
@ -746,6 +769,11 @@ ui:
answered: dijawab answered: dijawab
closed_in: Ditutup pada closed_in: Ditutup pada
show_exist: Gunakan pertanyaan yang sudah ada. show_exist: Gunakan pertanyaan yang sudah ada.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Jawaban title: Jawaban
score: Nilai score: Nilai
@ -762,7 +790,14 @@ ui:
<p>Yakin ingin menambahkan jawaban lain?</p><p>Sebagai gantinya, Anda dapat menggunakan tautan edit untuk menyaring dan menyempurnakan jawaban anda.</p> <p>Yakin ingin menambahkan jawaban lain?</p><p>Sebagai gantinya, Anda dapat menggunakan tautan edit untuk menyaring dan menyempurnakan jawaban anda.</p>
empty: Jawaban tidak boleh kosong. empty: Jawaban tidak boleh kosong.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Buka kembali postingan ini title: Buka kembali postingan ini
content: Kamu yakin ingin membuka kembali? content: Kamu yakin ingin membuka kembali?
success: Postingan ini telah dibuka kembali success: Postingan ini telah dibuka kembali
@ -977,14 +1012,12 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Sayangnya, halaman ini tidak ada." desc_403: You dont have permission to access this page.
back_home: Kembali ke beranda desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500 back_home: Back to homepage
desc: Server mengalami kesalahan internal dan tidak dapat menyelesaikan permintaan Anda.
back_home: Kembali ke beranda
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
nav_menus: nav_menus:
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: Non autorizzato other: Non autorizzato
database_error: database_error:
other: Errore server dati other: Errore server dati
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Permesso per cancellare mancante. other: Permesso per cancellare mancante.
cannot_update: cannot_update:
other: Nessun permesso per l'aggiornamento. other: Nessun permesso per l'aggiornamento.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Non si hanno di privilegi sufficienti per modificare il commento other: Non si hanno di privilegi sufficienti per modificare il commento
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Condizioni non valide per il grado other: Condizioni non valide per il grado
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Gestione del report fallita other: Gestione del report fallita
not_found: not_found:
other: Report non trovato other: Report non trovato
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Etichetta non trovata other: Etichetta non trovata
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Modifica Tag edit_tag: Modifica Tag
ask_a_question: Aggiungi una domanda ask_a_question: Aggiungi una domanda
edit_question: Modifica Domanda edit_question: Modifica Domanda
@ -281,6 +299,9 @@ ui:
upgrade: Answer Upgrade upgrade: Answer Upgrade
maintenance: Website Maintenance maintenance: Website Maintenance
users: Users users: Users
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notifications title: Notifications
inbox: Inbox inbox: Inbox
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Created created_at: Created
edited_at: Edited edited_at: Edited
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -2,23 +2,34 @@
backend: backend:
base: base:
success: success:
other: Success. other: 成功
unknown: unknown:
other: Unknown error. other: 不明なエラー
request_format_error: request_format_error:
other: Request format is not valid. other: リクエスト形式が無効です。
unauthorized_error: unauthorized_error:
other: Unauthorized. other: 権限がありません。
database_error: database_error:
other: Data server error. other: データサーバーエラー
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
other: User other: ユーザー
admin: admin:
other: Admin other: 管理者
moderator: moderator:
other: Moderator other: モデレーター
description: description:
user: user:
other: Default with no special access. other: Default with no special access.
@ -27,90 +38,96 @@ backend:
moderator: moderator:
other: Has access to all posts except admin settings. other: Has access to all posts except admin settings.
email: email:
other: Email other: メールアドレス
password: password:
other: Password other: パスワード
email_or_password_wrong_error: email_or_password_wrong_error:
other: Email and password do not match. other: メールアドレスとパスワードが一致しません。
error: error:
admin: admin:
cannot_update_their_password: cannot_update_their_password:
other: You cannot modify your password. other: パスワードは変更できません。
cannot_modify_self_status: cannot_modify_self_status:
other: You cannot modify your status. other: ステータスを変更できません。
email_or_password_wrong: email_or_password_wrong:
other: Email and password do not match. other: メールアドレスとパスワードが一致しません。
answer: answer:
not_found: not_found:
other: Answer do not found. other: Answer do not found.
cannot_deleted: cannot_deleted:
other: No permission to delete. other: 削除する権限がありません。
cannot_update: cannot_update:
other: No permission to update. other: 更新する権限がありません。
question_closed_cannot_add:
other: 質問はクローズされて、追加できません。
comment: comment:
edit_without_permission: edit_without_permission:
other: Comment are not allowed to edit. other: コメントを編集することはできません。
not_found: not_found:
other: Comment not found. other: コメントが見つかりません。
cannot_edit_after_deadline: cannot_edit_after_deadline:
other: The comment time has been too long to modify. other: コメント時間が長すぎて変更できません。
email: email:
duplicate: duplicate:
other: Email already exists. other: メールアドレスは既に存在しています。
need_to_be_verified: need_to_be_verified:
other: Email should be verified. other: 電子メールを確認する必要があります。
verify_url_expired: verify_url_expired:
other: Email verified URL has expired, please resend the email. other: メール認証済みURLの有効期限が切れています。メールを再送信してください。
lang: lang:
not_found: not_found:
other: Language file not found. other: 言語ファイルが見つかりません。
object: object:
captcha_verification_failed: captcha_verification_failed:
other: Captcha wrong. other: Captchaが間違っています。
disallow_follow: disallow_follow:
other: You are not allowed to follow. other: フォローが許可されていません。
disallow_vote: disallow_vote:
other: You are not allowed to vote. other: 投票が許可されていません。
disallow_vote_your_self: disallow_vote_your_self:
other: You can't vote for your own post. other: 自分の投稿には投票できません。
not_found: not_found:
other: Object not found. other: オブジェクトが見つかりません。
verification_failed: verification_failed:
other: Verification failed. other: 認証に失敗しました。
email_or_password_incorrect: email_or_password_incorrect:
other: Email and password do not match. other: メールアドレスとパスワードが一致しません。
old_password_verification_failed: old_password_verification_failed:
other: The old password verification failed other: 古いパスワードの確認に失敗しました。
new_password_same_as_previous_setting: new_password_same_as_previous_setting:
other: The new password is the same as the previous one. other: 新しいパスワードは前のパスワードと同じです。
question: question:
already_deleted: already_deleted:
other: This post has been deleted. other: この投稿は削除されました。
not_found: not_found:
other: Question not found. other: 質問が見つかりません。
cannot_deleted: cannot_deleted:
other: No permission to delete. other: 削除する権限がありません。
cannot_close: cannot_close:
other: No permission to close. other: クローズする権限がありません。
cannot_update: cannot_update:
other: No permission to update. other: 更新する権限がありません。
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Rank fail to meet the condition. other: ランクは条件を満たしていません。
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Report handle failed. other: レポートの処理に失敗しました。
not_found: not_found:
other: Report not found. other: レポートが見つかりません。
tag: tag:
already_exist:
other: タグは既に存在します。
not_found: not_found:
other: Tag not found. other: タグが見つかりません。
recommend_tag_not_found: recommend_tag_not_found:
other: Recommend Tag is not exist. other: 推奨タグは存在しません。
recommend_tag_enter: recommend_tag_enter:
other: Please enter at least one required tag. other: 少なくとも 1 つの必須タグを入力してください。
not_contain_synonym_tags: not_contain_synonym_tags:
other: Should not contain synonym tags. other: 同義語のタグを含めないでください。
cannot_update: cannot_update:
other: No permission to update. other: No permission to update.
is_used_cannot_delete: is_used_cannot_delete:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Edit Tag edit_tag: Edit Tag
ask_a_question: Add Question ask_a_question: Add Question
edit_question: Edit Question edit_question: Edit Question
@ -281,6 +299,9 @@ ui:
upgrade: Answer Upgrade upgrade: Answer Upgrade
maintenance: Website Maintenance maintenance: Website Maintenance
users: Users users: Users
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notifications title: Notifications
inbox: Inbox inbox: Inbox
@ -294,12 +315,12 @@ ui:
end: You don't meet a community guideline. end: You don't meet a community guideline.
editor: editor:
blockquote: blockquote:
text: Blockquote text: 引用
bold: bold:
text: Strong text: Strong
chart: chart:
text: Chart text: Chart
flow_chart: Flow chart flow_chart: フローチャート
sequence_diagram: Sequence diagram sequence_diagram: Sequence diagram
class_diagram: Class diagram class_diagram: Class diagram
state_diagram: State diagram state_diagram: State diagram
@ -313,61 +334,61 @@ ui:
form: form:
fields: fields:
code: code:
label: Code label: コード
msg: msg:
empty: Code cannot be empty. empty: Code cannot be empty.
language: language:
label: Language label: 言語
placeholder: Automatic detection placeholder: 自動検出
btn_cancel: Cancel btn_cancel: キャンセル
btn_confirm: Add btn_confirm: 追加
formula: formula:
text: Formula text: 数式
options: options:
inline: Inline formula inline: Inline formula
block: Block formula block: Block formula
heading: heading:
text: Heading text: 見出し
options: options:
h1: Heading 1 h1: 見出し1
h2: Heading 2 h2: 見出し2
h3: Heading 3 h3: 見出し3
h4: Heading 4 h4: 見出し4
h5: Heading 5 h5: 見出し5
h6: Heading 6 h6: 見出し6
help: help:
text: Help text: ヘルプ
hr: hr:
text: Horizontal Rule text: Horizontal Rule
image: image:
text: Image text: 画像
add_image: Add image add_image: 画像を追加する
tab_image: Upload image tab_image: 画像をアップロードする
form_image: form_image:
fields: fields:
file: file:
label: Image File label: 画像ファイル
btn: Select image btn: 画像を選択する
msg: msg:
empty: File cannot be empty. empty: ファイルは空にできません。
only_image: Only image files are allowed. only_image: 画像ファイルのみが許可されています。
max_size: File size cannot exceed 4MB. max_size: ファイルサイズは4MBを超えることはできません。
desc: desc:
label: Description label: 説明
tab_url: Image URL tab_url: 画像URL
form_url: form_url:
fields: fields:
url: url:
label: Image URL label: 画像URL
msg: msg:
empty: Image URL cannot be empty. empty: 画像のURLは空にできません。
name: name:
label: Description label: 説明
btn_cancel: Cancel btn_cancel: キャンセル
btn_confirm: Add btn_confirm: 追加
uploading: Uploading uploading: アップロード中
indent: indent:
text: Indent text: インデント
outdent: outdent:
text: Outdent text: Outdent
italic: italic:
@ -424,7 +445,7 @@ ui:
range: Display name up to 35 characters. range: Display name up to 35 characters.
slug_name: slug_name:
label: URL Slug label: URL Slug
desc: 'Must use the character set "a-z", "0-9", "+ # - ."' desc: '文字セット「a-z」、「0-9」、「+ # -」を使用する必要があります。'
msg: msg:
empty: URL slug cannot be empty. empty: URL slug cannot be empty.
range: URL slug up to 35 characters. range: URL slug up to 35 characters.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Created created_at: Created
edited_at: Edited edited_at: Edited
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -701,17 +724,17 @@ ui:
label: Email Notifications label: Email Notifications
radio: "Answers to your questions, comments, and more" radio: "Answers to your questions, comments, and more"
account: account:
heading: Account heading: アカウント
change_email_btn: Change email change_email_btn: メールアドレスを変更する
change_pass_btn: Change password change_pass_btn: パスワードを変更する
change_email_info: >- change_email_info: >-
We've sent an email to that address. Please follow the confirmation instructions. We've sent an email to that address. Please follow the confirmation instructions.
email: email:
label: Email label: メールアドレス
msg: Email cannot be empty. msg: Email cannot be empty.
password_title: Password password_title: パスワード
current_pass: current_pass:
label: Current Password label: 現在のパスワード
msg: msg:
empty: Current Password cannot be empty. empty: Current Password cannot be empty.
length: The length needs to be between 8 and 32. length: The length needs to be between 8 and 32.
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: Unauthorized. other: Unauthorized.
database_error: database_error:
other: Data server error. other: Data server error.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: No permission to delete. other: No permission to delete.
cannot_update: cannot_update:
other: No permission to update. other: No permission to update.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Comment are not allowed to edit. other: Comment are not allowed to edit.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Rank fail to meet the condition. other: Rank fail to meet the condition.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Report handle failed. other: Report handle failed.
not_found: not_found:
other: Report not found. other: Report not found.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Tag not found. other: Tag not found.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Edit Tag edit_tag: Edit Tag
ask_a_question: Add Question ask_a_question: Add Question
edit_question: Edit Question edit_question: Edit Question
@ -281,6 +299,9 @@ ui:
upgrade: Answer Upgrade upgrade: Answer Upgrade
maintenance: Website Maintenance maintenance: Website Maintenance
users: Users users: Users
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notifications title: Notifications
inbox: Inbox inbox: Inbox
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Created created_at: Created
edited_at: Edited edited_at: Edited
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: Não autorizado. other: Não autorizado.
database_error: database_error:
other: Erro no servidor de dados. other: Erro no servidor de dados.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Sem permissão para remover. other: Sem permissão para remover.
cannot_update: cannot_update:
other: Sem permissão para atualizar. other: Sem permissão para atualizar.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Não é possível alterar comentários. other: Não é possível alterar comentários.
@ -68,7 +81,7 @@ backend:
captcha_verification_failed: captcha_verification_failed:
other: O Captcha está incorreto. other: O Captcha está incorreto.
disallow_follow: disallow_follow:
other: Você não possui autorização suficiente para seguir. other: Você não tem permissão para seguir.
disallow_vote: disallow_vote:
other: Você não possui permissão para votar. other: Você não possui permissão para votar.
disallow_vote_your_self: disallow_vote_your_self:
@ -97,14 +110,18 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: O nível não consegue satisfazer a condição. other: O nível não consegue satisfazer a condição.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Falha ao manusear relatório. other: Falha ao manusear relatório.
not_found: not_found:
other: Relatório não encontrado. other: Relatório não encontrado.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Marcador não encontrado. other: Marca não encontrada.
recommend_tag_not_found: recommend_tag_not_found:
other: O marcador recomendado não existe. other: O marcador recomendado não existe.
recommend_tag_enter: recommend_tag_enter:
@ -125,9 +142,9 @@ backend:
other: Tema não encontrado. other: Tema não encontrado.
revision: revision:
review_underway: review_underway:
other: Não é possível neste momento, há uma versão na fila de análise. other: Não é possível editar atualmente, há uma versão na fila de análise.
no_permission: no_permission:
other: Sem permissão para realizar Revisão. other: Sem permissão de modificação.
user: user:
email_or_password_wrong: email_or_password_wrong:
other: other:
@ -135,7 +152,7 @@ backend:
not_found: not_found:
other: Usuário não encontrado. other: Usuário não encontrado.
suspended: suspended:
other: O usuário foi suspenso. other: O utilizador foi suspenso.
username_invalid: username_invalid:
other: Nome de usuário inválido. other: Nome de usuário inválido.
username_duplicate: username_duplicate:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Edit Tag edit_tag: Edit Tag
ask_a_question: Add Question ask_a_question: Add Question
edit_question: Edit Question edit_question: Edit Question
@ -281,6 +299,9 @@ ui:
upgrade: Atualização do Answer upgrade: Atualização do Answer
maintenance: Manutenção do website maintenance: Manutenção do website
users: Usuários users: Usuários
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notificações title: Notificações
inbox: Caixa de entrada inbox: Caixa de entrada
@ -424,7 +445,7 @@ ui:
range: Display name up to 35 characters. range: Display name up to 35 characters.
slug_name: slug_name:
label: URL Slug label: URL Slug
desc: 'Must use the character set "a-z", "0-9", "+ # - ."' desc: URL slug up to 35 characters.
msg: msg:
empty: URL slug cannot be empty. empty: URL slug cannot be empty.
range: URL slug up to 35 characters. range: URL slug up to 35 characters.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Created created_at: Created
edited_at: Edited edited_at: Edited
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: Авторизация не выполнена. other: Авторизация не выполнена.
database_error: database_error:
other: Ошибка сервера данных. other: Ошибка сервера данных.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Недостаточно прав для удаления. other: Недостаточно прав для удаления.
cannot_update: cannot_update:
other: Нет прав для обновления. other: Нет прав для обновления.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Комментарий не может редактироваться. other: Комментарий не может редактироваться.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Ранг не соответствует условию. other: Ранг не соответствует условию.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Не удалось обработать отчет. other: Не удалось обработать отчет.
not_found: not_found:
other: Отчет не найден. other: Отчет не найден.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Тег не найден. other: Тег не найден.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Тэг tag: Тэг
tags: Теги tags: Теги
tag_wiki: wiki тэг tag_wiki: wiki тэг
create_tag: Create Tag
edit_tag: Изменить тег edit_tag: Изменить тег
ask_a_question: Добавить вопрос ask_a_question: Добавить вопрос
edit_question: Редактировать вопрос edit_question: Редактировать вопрос
@ -281,6 +299,9 @@ ui:
upgrade: Обновить ответ upgrade: Обновить ответ
maintenance: Обслуживание сайта maintenance: Обслуживание сайта
users: Пользователи users: Пользователи
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Уведомления title: Уведомления
inbox: Входящие inbox: Входящие
@ -424,7 +445,7 @@ ui:
range: Отображаемое имя до 35 символов. range: Отображаемое имя до 35 символов.
slug_name: slug_name:
label: Идентификатор URL label: Идентификатор URL
desc: 'Необходимо использовать набор символов "a-z", "0-9", "+ # - ."' desc: URL slug up to 35 characters.
msg: msg:
empty: URL slug не может быть пустым. empty: URL slug не может быть пустым.
range: URL slug до 35 символов. range: URL slug до 35 символов.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Отмена btn_cancel: Отмена
btn_submit: Отправить btn_submit: Отправить
btn_post: Post new tag
tag_info: tag_info:
created_at: Создано created_at: Создано
edited_at: Отредактировано edited_at: Отредактировано
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

1414
i18n/sk_SK.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,17 @@ backend:
other: İzin yok. other: İzin yok.
database_error: database_error:
other: Veri sunucu hatası. other: Veri sunucu hatası.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: Silme izni yok. other: Silme izni yok.
cannot_update: cannot_update:
other: Düzenleme izni yok. other: Düzenleme izni yok.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Yorum düzenleme izni yok. other: Yorum düzenleme izni yok.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Rank fail to meet the condition. other: Rank fail to meet the condition.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Rapor işlenemedi. other: Rapor işlenemedi.
not_found: not_found:
other: Rapor bulunamadı. other: Rapor bulunamadı.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Etiket bulunamadı. other: Etiket bulunamadı.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Etiket tag: Etiket
tags: Etiketler tags: Etiketler
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Etiketi Düzenle edit_tag: Etiketi Düzenle
ask_a_question: Soru Ekle ask_a_question: Soru Ekle
edit_question: Soruyu Düzenle edit_question: Soruyu Düzenle
@ -280,7 +298,10 @@ ui:
install: Answer Installation install: Answer Installation
upgrade: Answer Upgrade upgrade: Answer Upgrade
maintenance: Website Bakımı maintenance: Website Bakımı
users: Kullanıcı users: Kullanıcılar
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Bildirimler title: Bildirimler
inbox: Gelen Kutusu inbox: Gelen Kutusu
@ -424,7 +445,7 @@ ui:
range: Display name up to 35 characters. range: Display name up to 35 characters.
slug_name: slug_name:
label: URL Slug label: URL Slug
desc: 'Must use the character set "a-z", "0-9", "+ # - ."' desc: URL slug up to 35 characters.
msg: msg:
empty: URL slug cannot be empty. empty: URL slug cannot be empty.
range: URL slug up to 35 characters. range: URL slug up to 35 characters.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: İptal Et btn_cancel: İptal Et
btn_submit: Gönder btn_submit: Gönder
btn_post: Post new tag
tag_info: tag_info:
created_at: Oluşturuldu created_at: Oluşturuldu
edited_at: Düzenlendi edited_at: Düzenlendi
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: Unauthorized. other: Unauthorized.
database_error: database_error:
other: Data server error. other: Data server error.
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: No permission to delete. other: No permission to delete.
cannot_update: cannot_update:
other: No permission to update. other: No permission to update.
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: Comment are not allowed to edit. other: Comment are not allowed to edit.
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: Rank fail to meet the condition. other: Rank fail to meet the condition.
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: Report handle failed. other: Report handle failed.
not_found: not_found:
other: Report not found. other: Report not found.
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: Tag not found. other: Tag not found.
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: Tag tag: Tag
tags: Tags tags: Tags
tag_wiki: tag wiki tag_wiki: tag wiki
create_tag: Create Tag
edit_tag: Edit Tag edit_tag: Edit Tag
ask_a_question: Add Question ask_a_question: Add Question
edit_question: Edit Question edit_question: Edit Question
@ -281,6 +299,9 @@ ui:
upgrade: Answer Upgrade upgrade: Answer Upgrade
maintenance: Website Maintenance maintenance: Website Maintenance
users: Users users: Users
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: Notifications title: Notifications
inbox: Inbox inbox: Inbox
@ -424,7 +445,7 @@ ui:
range: Display name up to 35 characters. range: Display name up to 35 characters.
slug_name: slug_name:
label: URL Slug label: URL Slug
desc: 'Must use the character set "a-z", "0-9", "+ # - ."' desc: URL slug up to 35 characters.
msg: msg:
empty: URL slug cannot be empty. empty: URL slug cannot be empty.
range: URL slug up to 35 characters. range: URL slug up to 35 characters.
@ -433,6 +454,7 @@ ui:
label: Description label: Description
btn_cancel: Cancel btn_cancel: Cancel
btn_submit: Submit btn_submit: Submit
btn_post: Post new tag
tag_info: tag_info:
created_at: Created created_at: Created
edited_at: Edited edited_at: Edited
@ -497,6 +519,7 @@ ui:
Use comments to ask for more information or suggest improvements. Avoid answering questions in comments. Use comments to ask for more information or suggest improvements. Avoid answering questions in comments.
tip_answer: >- tip_answer: >-
Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting. Use comments to reply to other users or notify them of changes. If you are adding new information, edit your post instead of commenting.
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: Edit Answer title: Edit Answer
default_reason: Edit answer default_reason: Edit answer
@ -746,6 +769,11 @@ ui:
answered: answered answered: answered
closed_in: Closed in closed_in: Closed in
show_exist: Show existing question. show_exist: Show existing question.
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: Answers title: Answers
score: Score score: Score
@ -762,7 +790,14 @@ 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> <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. empty: Answer cannot be empty.
characters: content must be at least 6 characters in length. characters: content must be at least 6 characters in length.
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: Reopen this post title: Reopen this post
content: Are you sure you want to reopen? content: Are you sure you want to reopen?
success: This post has been reopened success: This post has been reopened
@ -977,13 +1012,11 @@ ui:
votes: votes votes: votes
answers: answers answers: answers
accepted: Accepted accepted: Accepted
page_404: page_error:
http_error: HTTP Error 404 http_error: HTTP Error {{ code }}
desc: "Unfortunately, this page doesn't exist." desc_403: You dont have permission to access this page.
back_home: Back to homepage desc_404: Unfortunately, this page doesn't exist.
page_50X: desc_50X: The server encountered an error and could not complete your request.
http_error: HTTP Error 500
desc: The server encountered an error and could not complete your request.
back_home: Back to homepage back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "We are under maintenance, we'll be back soon." desc: "We are under maintenance, we'll be back soon."
@ -1198,6 +1231,9 @@ ui:
label: Timezone label: Timezone
msg: Timezone cannot be empty. msg: Timezone cannot be empty.
text: Choose a city in the same timezone as you. text: Choose a city in the same timezone as you.
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1363,8 +1399,8 @@ ui:
no_data: "We couldn't find anything." no_data: "We couldn't find anything."
users: users:
title: Users title: Users
users_with_the_most_reputation: Users with the highest reputation scores users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: Users who voted the most users_with_the_most_vote: Users who voted the most this week
staffs: Our community staff staffs: Our community staff
reputation: reputation reputation: reputation
votes: votes votes: votes

View File

@ -11,6 +11,17 @@ backend:
other: 未授权。 other: 未授权。
database_error: database_error:
other: 数据服务器错误。 other: 数据服务器错误。
action:
report:
other: 举报
edit:
other: 编辑
delete:
other: 删除
close:
other: 关闭
reopen:
other: 重新打开
role: role:
name: name:
user: user:
@ -35,9 +46,9 @@ backend:
error: error:
admin: admin:
cannot_update_their_password: cannot_update_their_password:
other: You cannot modify your password. other: 您无法修改自己的密码。
cannot_modify_self_status: cannot_modify_self_status:
other: You cannot modify your status. other: 您无法修改自己的状态。
email_or_password_wrong: email_or_password_wrong:
other: 邮箱和密码不匹配。 other: 邮箱和密码不匹配。
answer: answer:
@ -48,7 +59,7 @@ backend:
cannot_update: cannot_update:
other: 没有更新权限。 other: 没有更新权限。
question_closed_cannot_add: question_closed_cannot_add:
other: 问题已关闭不可以新增回答 other: 问题已关闭,无法添加。
comment: comment:
edit_without_permission: edit_without_permission:
other: 不允许编辑评论。 other: 不允许编辑评论。
@ -87,7 +98,7 @@ backend:
other: 新密码与之前的设置相同 other: 新密码与之前的设置相同
question: question:
already_deleted: already_deleted:
other: This post has been deleted. other: 此问题已被删除.
not_found: not_found:
other: 问题未找到 other: 问题未找到
cannot_deleted: cannot_deleted:
@ -99,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: 级别不符合条件 other: 级别不符合条件
vote_fail_to_meet_the_condition:
other: 感谢您的投票。您至少需要{{ rank }}声望才能投票。
report: report:
handle_failed: handle_failed:
other: 报告处理失败 other: 报告处理失败
not_found: not_found:
other: 报告未找到 other: 报告未找到
tag: tag:
already_exist:
other: 标签已存在。
not_found: not_found:
other: 标签未找到 other: 标签未找到
recommend_tag_not_found: recommend_tag_not_found:
@ -263,6 +278,7 @@ ui:
tag: 标签 tag: 标签
tags: 标签 tags: 标签
tag_wiki: 标签 wiki tag_wiki: 标签 wiki
create_tag: 创建标签
edit_tag: 编辑标签 edit_tag: 编辑标签
ask_a_question: 提问题 ask_a_question: 提问题
edit_question: 编辑问题 edit_question: 编辑问题
@ -283,6 +299,9 @@ ui:
upgrade: Answer 升级 upgrade: Answer 升级
maintenance: 网站维护 maintenance: 网站维护
users: 用户 users: 用户
http_404: HTTP 错误 404
http_50X: HTTP 错误 500
http_403: HTTP 错误 403
notifications: notifications:
title: 通知 title: 通知
inbox: 收件箱 inbox: 收件箱
@ -426,7 +445,7 @@ ui:
range: 显示名称不能超过 35 个字符。 range: 显示名称不能超过 35 个字符。
slug_name: slug_name:
label: URL 固定链接 label: URL 固定链接
desc: '必须由 "a-z", "0-9", "+ # - ." 组成' desc: URL 地址不能超过 35 个字符。
msg: msg:
empty: URL 固定链接不能为空。 empty: URL 固定链接不能为空。
range: URL 固定链接不能超过 35 个字符。 range: URL 固定链接不能超过 35 个字符。
@ -435,6 +454,7 @@ ui:
label: 描述 label: 描述
btn_cancel: 取消 btn_cancel: 取消
btn_submit: 提交 btn_submit: 提交
btn_post: 发布新标签
tag_info: tag_info:
created_at: 创建于 created_at: 创建于
edited_at: 编辑于 edited_at: 编辑于
@ -499,6 +519,7 @@ ui:
使用评论提问更多信息或者提出改进意见。尽量避免使用评论功能回答问题。 使用评论提问更多信息或者提出改进意见。尽量避免使用评论功能回答问题。
tip_answer: >- tip_answer: >-
使用评论对回答者进行回复,或者通知回答者你已更新了问题的内容。如果要补充或者完善问题的内容,请在原问题中更改。 使用评论对回答者进行回复,或者通知回答者你已更新了问题的内容。如果要补充或者完善问题的内容,请在原问题中更改。
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: 编辑回答 title: 编辑回答
default_reason: 编辑回答 default_reason: 编辑回答
@ -584,7 +605,7 @@ ui:
placeholder: 搜索 placeholder: 搜索
footer: footer:
build_on: >- build_on: >-
基于<1>Answer</1>--为问答社区提供动力的开源软件。<br />Made with love © {{cc}}. 基于 <1>Answer</1> - 为问答社区提供动力的开源软件。<br />Made with love © {{cc}}.
upload_img: upload_img:
name: 更改图片 name: 更改图片
loading: 加载中... loading: 加载中...
@ -748,6 +769,11 @@ ui:
answered: 回答于 answered: 回答于
closed_in: 关闭于 closed_in: 关闭于
show_exist: 查看相关问题。 show_exist: 查看相关问题。
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: 个回答 title: 个回答
score: 评分 score: 评分
@ -764,7 +790,14 @@ ui:
<p>您确定要提交一个新的回答吗?</p><p>您可以直接编辑和改善您之前的回答的。</p> <p>您确定要提交一个新的回答吗?</p><p>您可以直接编辑和改善您之前的回答的。</p>
empty: 回答内容不能为空。 empty: 回答内容不能为空。
characters: 内容长度至少 6 个字符 characters: 内容长度至少 6 个字符
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: 重新打开
title: 重新打开这个帖子 title: 重新打开这个帖子
content: 确定要重新打开吗? content: 确定要重新打开吗?
success: 这个帖子已被重新打开 success: 这个帖子已被重新打开
@ -790,7 +823,7 @@ ui:
approve: 批准 approve: 批准
reject: 拒绝 reject: 拒绝
skip: 略过 skip: 略过
discard_draft: Discard draft discard_draft: 丢弃草稿
search: search:
title: 搜索结果 title: 搜索结果
keywords: 关键词 keywords: 关键词
@ -927,7 +960,7 @@ ui:
title: 创建 config.yaml title: 创建 config.yaml
label: 已创建 config.yaml 文件。 label: 已创建 config.yaml 文件。
desc: >- desc: >-
您可以手动在 <1>/var/wwww/xxx/</1> 目录中创建<1>config.yaml</1> 文件并粘贴以下文本。 您可以手动在 <1>/var/wwww/xxx/</1> 目录中创建 <1>config.yaml</1> 文件并粘贴以下文本。
info: 完成后,点击“下一步”按钮。 info: 完成后,点击“下一步”按钮。
site_information: 站点信息 site_information: 站点信息
admin_account: 管理员账户 admin_account: 管理员账户
@ -966,8 +999,8 @@ ui:
good_luck: "玩得愉快,祝您好运!" good_luck: "玩得愉快,祝您好运!"
warn_title: 警告 warn_title: 警告
warn_desc: >- warn_desc: >-
文件<1>config.yaml</1>已存在。如果您需要重置此文件中的任何配置项,请先删除它。 文件 <1>config.yaml</1> 已存在。如果您需要重置此文件中的任何配置项,请先删除它。
install_now: 您可以尝试<1>现在安装</1>。 install_now: 您可以尝试 <1>现在安装</1>。
installed: 已安裝 installed: 已安裝
installed_desc: >- installed_desc: >-
您似乎已经安装过了。要重新安装,请先清除旧的数据库表。 您似乎已经安装过了。要重新安装,请先清除旧的数据库表。
@ -981,10 +1014,10 @@ ui:
accepted: 已被采纳 accepted: 已被采纳
page_error: page_error:
http_error: HTTP Error {{ code }} http_error: HTTP Error {{ code }}
desc_403: 你无权访问此页面。 desc_403: You dont have permission to access this page.
desc_404: 很抱歉,此页面不存在。 desc_404: Unfortunately, this page doesn't exist.
desc_50X: 服务器遇到了一个错误,无法完成你的请求。 desc_50X: The server encountered an error and could not complete your request.
back_home: 回到主页 back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "我们正在进行维护,我们将很快回来。" desc: "我们正在进行维护,我们将很快回来。"
nav_menus: nav_menus:
@ -1181,7 +1214,7 @@ ui:
msg: 简短网站描述不能为空。 msg: 简短网站描述不能为空。
text: "简短的标语作为网站主页的标题Html 的 title 标签)。" text: "简短的标语作为网站主页的标题Html 的 title 标签)。"
desc: desc:
label: 站点描述 label: 网站介绍
msg: 网站描述不能为空。 msg: 网站描述不能为空。
text: "使用一句话描述本站作为网站的描述Html 的 meta 标签)。" text: "使用一句话描述本站作为网站的描述Html 的 meta 标签)。"
contact_email: contact_email:
@ -1199,6 +1232,9 @@ ui:
label: 时区 label: 时区
msg: 时区不能为空。 msg: 时区不能为空。
text: 选择一个与您相同时区的城市。 text: 选择一个与您相同时区的城市。
avatar:
label: 默认头像
text: 没有自定义头像的用户。
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1364,8 +1400,8 @@ ui:
no_data: "空空如也" no_data: "空空如也"
users: users:
title: 用户 title: 用户
users_with_the_most_reputation: 本周信誉积分最高的用户 users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: 本周投票最多的用户 users_with_the_most_vote: Users who voted the most this week
staffs: 我们的社区工作人员 staffs: 我们的社区工作人员
reputation: 声望值 reputation: 声望值
votes: 投票 votes: 投票
@ -1373,7 +1409,7 @@ ui:
leave_page: 确定要离开此页面? leave_page: 确定要离开此页面?
changes_not_save: 您的更改尚未保存 changes_not_save: 您的更改尚未保存
draft: draft:
discard_confirm: Are you sure you want to discard your draft? discard_confirm: 您确定要丢弃您的草稿吗?
messages: messages:
post_deleted: This post has been deleted. post_deleted: 该帖子已被删除。

View File

@ -11,6 +11,17 @@ backend:
other: 未授權。 other: 未授權。
database_error: database_error:
other: 資料庫錯誤。 other: 資料庫錯誤。
action:
report:
other: Flag
edit:
other: Edit
delete:
other: Delete
close:
other: Close
reopen:
other: Reopen
role: role:
name: name:
user: user:
@ -47,6 +58,8 @@ backend:
other: 沒有刪除權限。 other: 沒有刪除權限。
cannot_update: cannot_update:
other: 沒有更新權限。 other: 沒有更新權限。
question_closed_cannot_add:
other: Questions are closed and cannot be added.
comment: comment:
edit_without_permission: edit_without_permission:
other: 不允許編輯評論。 other: 不允許編輯評論。
@ -97,12 +110,16 @@ backend:
rank: rank:
fail_to_meet_the_condition: fail_to_meet_the_condition:
other: 級別不符合條件 other: 級別不符合條件
vote_fail_to_meet_the_condition:
other: Thanks for the feedback. You need at least {{ rank }} reputation to cast a vote.
report: report:
handle_failed: handle_failed:
other: 報告處理失敗。 other: 報告處理失敗。
not_found: not_found:
other: 找不到報告。 other: 找不到報告。
tag: tag:
already_exist:
other: Tag already exists.
not_found: not_found:
other: 找不到標籤。 other: 找不到標籤。
recommend_tag_not_found: recommend_tag_not_found:
@ -261,6 +278,7 @@ ui:
tag: 標籤 tag: 標籤
tags: 標籤 tags: 標籤
tag_wiki: 標籤 wiki tag_wiki: 標籤 wiki
create_tag: Create Tag
edit_tag: 編輯標籤 edit_tag: 編輯標籤
ask_a_question: 提問題 ask_a_question: 提問題
edit_question: 編輯問題 edit_question: 編輯問題
@ -281,6 +299,9 @@ ui:
upgrade: Answer 升級 upgrade: Answer 升級
maintenance: 網站維護 maintenance: 網站維護
users: 用戶 users: 用戶
http_404: HTTP Error 404
http_50X: HTTP Error 500
http_403: HTTP Error 403
notifications: notifications:
title: 通知 title: 通知
inbox: 收件夾 inbox: 收件夾
@ -424,7 +445,7 @@ ui:
range: 顯示名稱不能超過 35 個字符。 range: 顯示名稱不能超過 35 個字符。
slug_name: slug_name:
label: URL 固定連結 label: URL 固定連結
desc: '必須由 "a-z", "0-9", "+ # - ." 組成' desc: URL slug up to 35 characters.
msg: msg:
empty: URL 固定連結不能為空。 empty: URL 固定連結不能為空。
range: URL 固定連結不能超過 35 個字元。 range: URL 固定連結不能超過 35 個字元。
@ -433,6 +454,7 @@ ui:
label: 描述 label: 描述
btn_cancel: 取消 btn_cancel: 取消
btn_submit: 提交 btn_submit: 提交
btn_post: Post new tag
tag_info: tag_info:
created_at: 創建於 created_at: 創建於
edited_at: 編輯於 edited_at: 編輯於
@ -497,6 +519,7 @@ ui:
通过評論询问更多问题或提出改進建議。避免在評論中回答問題。 通过評論询问更多问题或提出改進建議。避免在評論中回答問題。
tip_answer: >- tip_answer: >-
使用評論回復其他用戶或通知他們进行更改。如果你要添加新的信息,請編輯你的帖子,而不是發表評論。 使用評論回復其他用戶或通知他們进行更改。如果你要添加新的信息,請編輯你的帖子,而不是發表評論。
tip_vote: It adds something useful to the post
edit_answer: edit_answer:
title: 編輯回答 title: 編輯回答
default_reason: 編輯回答 default_reason: 編輯回答
@ -746,6 +769,11 @@ ui:
answered: 回答於 answered: 回答於
closed_in: 關閉於 closed_in: 關閉於
show_exist: 顯示現有問題。 show_exist: 顯示現有問題。
useful: Useful
question_useful: It is useful and clear
question_un_useful: It is unclear or not useful
answer_useful: It is useful
answer_un_useful: It is not useful
answers: answers:
title: 個回答 title: 個回答
score: 評分 score: 評分
@ -762,7 +790,14 @@ ui:
<p>您確定要添加一個新的回答嗎?</p><p>您可以使用编辑链接来完善和改进您现有的答案。</p> <p>您確定要添加一個新的回答嗎?</p><p>您可以使用编辑链接来完善和改进您现有的答案。</p>
empty: 回答內容不能為空。 empty: 回答內容不能為空。
characters: 內容必須至少6個字元長度。 characters: 內容必須至少6個字元長度。
tips:
header_1: Thanks for your answer
li1_1: Please be sure to <strong>answer the question</strong>. Provide details and share your research.
li1_2: Back up any statements you make with references or personal experience.
header_2: But <strong>avoid</strong> ...
li2_1: Asking for help, seeking clarification, or responding to other answers.
reopen: reopen:
confirm_btn: Reopen
title: 重新打開這個貼文 title: 重新打開這個貼文
content: 確定要重新打開嗎? content: 確定要重新打開嗎?
success: 這個貼文已被重新打開 success: 這個貼文已被重新打開
@ -979,10 +1014,10 @@ ui:
accepted: 已採納 accepted: 已採納
page_error: page_error:
http_error: HTTP Error {{ code }} http_error: HTTP Error {{ code }}
desc_403: 你无权访问此頁面。 desc_403: You dont have permission to access this page.
desc_404: 很抱歉,此頁面不存在。 desc_404: Unfortunately, this page doesn't exist.
desc_50X: 伺服器遇到了一個錯誤,無法完成你的請求。 desc_50X: The server encountered an error and could not complete your request.
back_home: 回到首頁 back_home: Back to homepage
page_maintenance: page_maintenance:
desc: "我們正在維護中,很快就會回來。" desc: "我們正在維護中,很快就會回來。"
nav_menus: nav_menus:
@ -1196,6 +1231,9 @@ ui:
label: 時區 label: 時區
msg: 時區不能為空。 msg: 時區不能為空。
text: 選擇一個與您相同時區的城市。 text: 選擇一個與您相同時區的城市。
avatar:
label: Default Avatar
text: For users without a custom avatar of their own.
smtp: smtp:
page_title: SMTP page_title: SMTP
from_email: from_email:
@ -1361,8 +1399,8 @@ ui:
no_data: "我們找不到任何東西。" no_data: "我們找不到任何東西。"
users: users:
title: 用戶 title: 用戶
users_with_the_most_reputation: 信譽積分最高的用戶 users_with_the_most_reputation: Users with the highest reputation scores this week
users_with_the_most_vote: 投票最多的用戶 users_with_the_most_vote: Users who voted the most this week
staffs: 我們的社區工作人員 staffs: 我們的社區工作人員
reputation: 聲望值 reputation: 聲望值
votes: 選票 votes: 選票

View File

@ -30,7 +30,8 @@ const (
// object TagID AnswerList // object TagID AnswerList
// key equal database's table name // key equal database's table name
var ( var (
Version string = "" Version string = ""
Revision string = ""
PathIgnoreMap map[string]bool PathIgnoreMap map[string]bool

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"errors" "errors"
"net/http" "net/http"
"strings"
"github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/reason"
@ -73,3 +74,10 @@ func BindAndCheckReturnErr(ctx *gin.Context, data interface{}) (errFields []*val
errFields, _ = validator.GetValidatorByLang(lang).Check(data) errFields, _ = validator.GetValidatorByLang(lang).Check(data)
return errFields return errFields
} }
func MsgWithParameter(msg string, list map[string]string) string {
for k, v := range list {
msg = strings.Replace(msg, "{{ "+k+" }}", v, -1)
}
return msg
}

View File

@ -124,7 +124,7 @@ func (am *AuthUserMiddleware) AdminAuth() gin.HandlerFunc {
} }
userInfo, err := am.authService.GetAdminUserCacheInfo(ctx, token) userInfo, err := am.authService.GetAdminUserCacheInfo(ctx, token)
if err != nil { if err != nil {
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil) handler.HandleResponse(ctx, errors.Forbidden(reason.UnauthorizedError), nil)
ctx.Abort() ctx.Abort()
return return
} }

View File

@ -48,6 +48,7 @@ const (
TagIsUsedCannotDelete = "error.tag.is_used_cannot_delete" TagIsUsedCannotDelete = "error.tag.is_used_cannot_delete"
TagAlreadyExist = "error.tag.already_exist" TagAlreadyExist = "error.tag.already_exist"
RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition" RankFailToMeetTheCondition = "error.rank.fail_to_meet_the_condition"
VoteRankFailToMeetTheCondition = "error.rank.vote_fail_to_meet_the_condition"
ThemeNotFound = "error.theme.not_found" ThemeNotFound = "error.theme.not_found"
LangNotFound = "error.lang.not_found" LangNotFound = "error.lang.not_found"
ReportHandleFailed = "error.report.handle_failed" ReportHandleFailed = "error.report.handle_failed"

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/controller"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/pkg/converter" "github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/day" "github.com/answerdev/answer/pkg/day"
@ -41,6 +42,9 @@ var funcMap = template.FuncMap{
"templateHTML": func(data string) template.HTML { "templateHTML": func(data string) template.HTML {
return template.HTML(data) return template.HTML(data)
}, },
"formatLinkNofollow": func(data string) template.HTML {
return template.HTML(FormatLinkNofollow(data))
},
"translator": func(la i18n.Language, data string, params ...interface{}) string { "translator": func(la i18n.Language, data string, params ...interface{}) string {
trans := translator.GlobalTrans.Tr(la, data) trans := translator.GlobalTrans.Tr(la, data)
@ -116,3 +120,21 @@ var funcMap = template.FuncMap{
return htmltext.UrlTitle(title) return htmltext.UrlTitle(title)
}, },
} }
func FormatLinkNofollow(html string) string {
var hrefRegexp = regexp.MustCompile("(?m)<a.*?[^<]>.*?</a>")
match := hrefRegexp.FindAllString(html, -1)
if match != nil {
for _, v := range match {
hasNofollow := strings.Contains(v, "rel=\"nofollow\"")
hasSiteUrl := strings.Contains(v, controller.SiteUrl)
if !hasSiteUrl {
if !hasNofollow {
nofollowUrl := strings.Replace(v, "<a", "<a rel=\"nofollow\"", 1)
html = strings.Replace(html, v, nofollowUrl, 1)
}
}
}
}
return html
}

View File

@ -81,6 +81,7 @@ func (ac *AnswerController) RemoveAnswer(ctx *gin.Context) {
// @Success 200 {string} string "" // @Success 200 {string} string ""
func (ac *AnswerController) Get(ctx *gin.Context) { func (ac *AnswerController) Get(ctx *gin.Context) {
id := ctx.Query("id") id := ctx.Query("id")
id = uid.DeShortID(id)
userID := middleware.GetLoginUserIDFromContext(ctx) userID := middleware.GetLoginUserIDFromContext(ctx)
info, questionInfo, has, err := ac.answerService.Get(ctx, id, userID) info, questionInfo, has, err := ac.answerService.Get(ctx, id, userID)
@ -137,7 +138,6 @@ func (ac *AnswerController) Add(ctx *gin.Context) {
return return
} }
if !has { if !has {
// todo !has
handler.HandleResponse(ctx, nil, nil) handler.HandleResponse(ctx, nil, nil)
return return
} }
@ -181,6 +181,7 @@ func (ac *AnswerController) Update(ctx *gin.Context) {
return return
} }
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.QuestionID = uid.DeShortID(req.QuestionID)
canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.AnswerEdit, permission.AnswerEdit,
@ -232,6 +233,7 @@ func (ac *AnswerController) AnswerList(ctx *gin.Context) {
} }
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.QuestionID = uid.DeShortID(req.QuestionID)
canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.AnswerEdit, permission.AnswerEdit,
@ -272,6 +274,8 @@ func (ac *AnswerController) Accepted(ctx *gin.Context) {
} }
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.AnswerID = uid.DeShortID(req.AnswerID)
req.QuestionID = uid.DeShortID(req.QuestionID)
can, err := ac.rankService.CheckOperationPermission(ctx, req.UserID, permission.AnswerAccept, req.QuestionID) can, err := ac.rankService.CheckOperationPermission(ctx, req.UserID, permission.AnswerAccept, req.QuestionID)
if err != nil { if err != nil {
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
@ -301,6 +305,7 @@ func (ac *AnswerController) AdminSetAnswerStatus(ctx *gin.Context) {
if handler.BindAndCheck(ctx, req) { if handler.BindAndCheck(ctx, req) {
return return
} }
req.AnswerID = uid.DeShortID(req.AnswerID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)

View File

@ -14,18 +14,28 @@ import (
"github.com/answerdev/answer/pkg/converter" "github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/uid" "github.com/answerdev/answer/pkg/uid"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
) )
// QuestionController question controller // QuestionController question controller
type QuestionController struct { type QuestionController struct {
questionService *service.QuestionService questionService *service.QuestionService
answerService *service.AnswerService
rankService *rank.RankService rankService *rank.RankService
} }
// NewQuestionController new controller // NewQuestionController new controller
func NewQuestionController(questionService *service.QuestionService, rankService *rank.RankService) *QuestionController { func NewQuestionController(
return &QuestionController{questionService: questionService, rankService: rankService} questionService *service.QuestionService,
answerService *service.AnswerService,
rankService *rank.RankService,
) *QuestionController {
return &QuestionController{
questionService: questionService,
answerService: answerService,
rankService: rankService,
}
} }
// RemoveQuestion delete question // RemoveQuestion delete question
@ -159,6 +169,7 @@ func (qc *QuestionController) GetQuestion(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
return return
} }
info.ID = uid.EnShortID(info.ID)
handler.HandleResponse(ctx, nil, info) handler.HandleResponse(ctx, nil, info)
} }
@ -280,6 +291,109 @@ func (qc *QuestionController) AddQuestion(ctx *gin.Context) {
handler.HandleResponse(ctx, err, resp) handler.HandleResponse(ctx, err, resp)
} }
// AddQuestionByAnswer add question
// @Summary add question and answer
// @Description add question and answer
// @Tags Question
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Param data body schema.QuestionAddByAnswer true "question"
// @Success 200 {object} handler.RespBody
// @Router /answer/api/v1/question/answer [post]
func (qc *QuestionController) AddQuestionByAnswer(ctx *gin.Context) {
req := &schema.QuestionAddByAnswer{}
errFields := handler.BindAndCheckReturnErr(ctx, req)
if ctx.IsAborted() {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
permission.QuestionAdd,
permission.QuestionEdit,
permission.QuestionDelete,
permission.QuestionClose,
permission.QuestionReopen,
permission.TagUseReservedTag,
})
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.CanClose = canList[3]
req.CanReopen = canList[4]
req.CanUseReservedTag = canList[5]
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
questionReq := new(schema.QuestionAdd)
err = copier.Copy(questionReq, req)
if err != nil {
handler.HandleResponse(ctx, errors.Forbidden(reason.RequestFormatError), nil)
return
}
errList, err := qc.questionService.CheckAddQuestion(ctx, questionReq)
if err != nil {
errlist, ok := errList.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
}
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
resp, err := qc.questionService.AddQuestion(ctx, questionReq)
if err != nil {
errlist, ok := resp.([]*validator.FormErrorField)
if ok {
errFields = append(errFields, errlist...)
}
}
if len(errFields) > 0 {
handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields)
return
}
//add the question id to the answer
questionInfo, ok := resp.(*schema.QuestionInfo)
if ok {
answerReq := &schema.AnswerAddReq{}
answerReq.QuestionID = uid.DeShortID(questionInfo.ID)
answerReq.UserID = middleware.GetLoginUserIDFromContext(ctx)
answerReq.Content = req.AnswerContent
answerReq.HTML = req.AnswerHTML
answerID, err := qc.answerService.Insert(ctx, answerReq)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
info, questionInfo, has, err := qc.answerService.Get(ctx, answerID, req.UserID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !has {
handler.HandleResponse(ctx, nil, nil)
return
}
handler.HandleResponse(ctx, err, gin.H{
"info": info,
"question": questionInfo,
})
return
}
handler.HandleResponse(ctx, err, resp)
}
// UpdateQuestion update question // UpdateQuestion update question
// @Summary update question // @Summary update question
// @Description update question // @Description update question

View File

@ -5,6 +5,7 @@ import (
"github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/base/middleware"
"github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service" "github.com/answerdev/answer/internal/service"
"github.com/answerdev/answer/internal/service/permission" "github.com/answerdev/answer/internal/service/permission"
@ -52,7 +53,13 @@ func (rc *RevisionController) GetRevisionList(ctx *gin.Context) {
} }
resp, err := rc.revisionListService.GetRevisionList(ctx, req) resp, err := rc.revisionListService.GetRevisionList(ctx, req)
handler.HandleResponse(ctx, err, resp) list := make([]schema.GetRevisionResp, 0)
for _, item := range resp {
if item.Status == entity.RevisioNnormalStatus || item.Status == entity.RevisionReviewPassStatus {
list = append(list, item)
}
}
handler.HandleResponse(ctx, err, list)
} }
// GetUnreviewedRevisionList godoc // GetUnreviewedRevisionList godoc

View File

@ -31,7 +31,7 @@ func NewSiteinfoController(siteInfoService *siteinfo_common.SiteInfoCommonServic
// @Router /answer/api/v1/siteinfo [get] // @Router /answer/api/v1/siteinfo [get]
func (sc *SiteinfoController) GetSiteInfo(ctx *gin.Context) { func (sc *SiteinfoController) GetSiteInfo(ctx *gin.Context) {
var err error var err error
resp := &schema.SiteInfoResp{Version: constant.Version} resp := &schema.SiteInfoResp{Version: constant.Version, Revision: constant.Revision}
resp.General, err = sc.siteInfoService.GetSiteGeneral(ctx) resp.General, err = sc.siteInfoService.GetSiteGeneral(ctx)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -103,6 +103,7 @@ func (sc *SiteinfoController) GetManifestJson(ctx *gin.Context) {
resp := &schema.GetManifestJsonResp{ resp := &schema.GetManifestJsonResp{
ManifestVersion: 3, ManifestVersion: 3,
Version: constant.Version, Version: constant.Version,
Revision: constant.Revision,
ShortName: "Answer", ShortName: "Answer",
Name: "Answer.dev", Name: "Answer.dev",
Icons: map[string]string{ Icons: map[string]string{

View File

@ -23,6 +23,8 @@ import (
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
) )
var SiteUrl = ""
type TemplateController struct { type TemplateController struct {
scriptPath string scriptPath string
cssPath string cssPath string
@ -67,6 +69,7 @@ func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.TemplateSiteInf
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
SiteUrl = resp.General.SiteUrl
resp.Interface, err = tc.siteInfoService.GetSiteInterface(ctx) resp.Interface, err = tc.siteInfoService.GetSiteInterface(ctx)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@ -471,6 +474,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI
data["HeaderCode"] = siteInfo.CustomCssHtml.CustomHeader data["HeaderCode"] = siteInfo.CustomCssHtml.CustomHeader
data["FooterCode"] = siteInfo.CustomCssHtml.CustomFooter data["FooterCode"] = siteInfo.CustomCssHtml.CustomFooter
data["Version"] = constant.Version data["Version"] = constant.Version
data["Revision"] = constant.Revision
_, ok := data["path"] _, ok := data["path"]
if !ok { if !ok {
data["path"] = "" data["path"] = ""

View File

@ -529,9 +529,9 @@ func (uc *UserController) UserChangeEmailVerify(ctx *gin.Context) {
return return
} }
err := uc.userService.UserChangeEmailVerify(ctx, req.Content) resp, err := uc.userService.UserChangeEmailVerify(ctx, req.Content)
uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP()) uc.actionService.ActionRecordDel(ctx, schema.ActionRecordTypeEmail, ctx.ClientIP())
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, resp)
} }
// UserRanking get user ranking // UserRanking get user ranking

View File

@ -1,9 +1,12 @@
package controller package controller
import ( import (
"fmt"
"github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/base/middleware"
"github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service" "github.com/answerdev/answer/internal/service"
"github.com/answerdev/answer/internal/service/rank" "github.com/answerdev/answer/internal/service/rank"
@ -41,13 +44,16 @@ func (vc *VoteController) VoteUp(ctx *gin.Context) {
} }
req.ObjectID = uid.DeShortID(req.ObjectID) req.ObjectID = uid.DeShortID(req.ObjectID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, _, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, true) can, rank, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, true)
if err != nil { if err != nil {
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
return return
} }
if !can { if !can {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) lang := handler.GetLang(ctx)
msg := translator.Tr(lang, reason.VoteRankFailToMeetTheCondition)
msg = handler.MsgWithParameter(msg, map[string]string{"rank": fmt.Sprintf("%d", rank)})
handler.HandleResponse(ctx, errors.Forbidden(reason.VoteRankFailToMeetTheCondition).WithMsg(msg), nil)
return return
} }
@ -78,13 +84,16 @@ func (vc *VoteController) VoteDown(ctx *gin.Context) {
} }
req.ObjectID = uid.DeShortID(req.ObjectID) req.ObjectID = uid.DeShortID(req.ObjectID)
req.UserID = middleware.GetLoginUserIDFromContext(ctx) req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, _, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, false) can, rank, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, false)
if err != nil { if err != nil {
handler.HandleResponse(ctx, err, nil) handler.HandleResponse(ctx, err, nil)
return return
} }
if !can { if !can {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) lang := handler.GetLang(ctx)
msg := translator.Tr(lang, reason.VoteRankFailToMeetTheCondition)
msg = handler.MsgWithParameter(msg, map[string]string{"rank": fmt.Sprintf("%d", rank)})
handler.HandleResponse(ctx, errors.Forbidden(reason.VoteRankFailToMeetTheCondition).WithMsg(msg), nil)
return return
} }

View File

@ -5,6 +5,8 @@ import (
) )
const ( const (
// RevisioNnormalStatus this revision is Nnormal
RevisioNnormalStatus = 0
// RevisionUnreviewedStatus this revision is unreviewed // RevisionUnreviewedStatus this revision is unreviewed
RevisionUnreviewedStatus = 1 RevisionUnreviewedStatus = 1
// RevisionReviewPassStatus this revision is reviewed and approved by operator // RevisionReviewPassStatus this revision is reviewed and approved by operator

View File

@ -147,8 +147,8 @@ func (ar *authRepo) AddUserTokenMapping(ctx context.Context, userID, accessToken
return ar.data.Cache.SetString(ctx, key, string(content), constant.UserTokenCacheTime) return ar.data.Cache.SetString(ctx, key, string(content), constant.UserTokenCacheTime)
} }
// RemoveAllUserTokens Log out all users under this user id // RemoveUserTokens Log out all users under this user id
func (ar *authRepo) RemoveAllUserTokens(ctx context.Context, userID string) { func (ar *authRepo) RemoveUserTokens(ctx context.Context, userID string) {
key := constant.UserTokenMappingCacheKey + userID key := constant.UserTokenMappingCacheKey + userID
resp, _ := ar.data.Cache.GetString(ctx, key) resp, _ := ar.data.Cache.GetString(ctx, key)
mapping := make(map[string]bool, 0) mapping := make(map[string]bool, 0)

View File

@ -28,15 +28,15 @@ func NewUserRoleRelRepo(data *data.Data) role.UserRoleRelRepo {
func (ur *userRoleRelRepo) SaveUserRoleRel(ctx context.Context, userID string, roleID int) (err error) { func (ur *userRoleRelRepo) SaveUserRoleRel(ctx context.Context, userID string, roleID int) (err error) {
_, err = ur.data.DB.Transaction(func(session *xorm.Session) (interface{}, error) { _, err = ur.data.DB.Transaction(func(session *xorm.Session) (interface{}, error) {
item := &entity.UserRoleRel{UserID: userID} item := &entity.UserRoleRel{UserID: userID}
exist, err := ur.data.DB.Get(item) exist, err := session.Get(item)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if exist { if exist {
item.RoleID = roleID item.RoleID = roleID
_, err = ur.data.DB.ID(item.ID).Update(item) _, err = session.ID(item.ID).Update(item)
} else { } else {
_, err = ur.data.DB.Insert(&entity.UserRoleRel{UserID: userID, RoleID: roleID}) _, err = session.Insert(&entity.UserRoleRel{UserID: userID, RoleID: roleID})
} }
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -2,6 +2,7 @@ package tag_common
import ( import (
"context" "context"
"fmt"
"github.com/answerdev/answer/internal/base/data" "github.com/answerdev/answer/internal/base/data"
"github.com/answerdev/answer/internal/base/pager" "github.com/answerdev/answer/internal/base/pager"
@ -45,7 +46,7 @@ func (tr *tagCommonRepo) GetTagListByIDs(ctx context.Context, ids []string) (tag
// GetTagBySlugName get tag by slug name // GetTagBySlugName get tag by slug name
func (tr *tagCommonRepo) GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error) { func (tr *tagCommonRepo) GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error) {
tagInfo = &entity.Tag{} tagInfo = &entity.Tag{}
session := tr.data.DB.Where("slug_name = ?", slugName) session := tr.data.DB.Where("slug_name = LOWER(?)", slugName)
session.Where(builder.Eq{"status": entity.TagStatusAvailable}) session.Where(builder.Eq{"status": entity.TagStatusAvailable})
exist, err = session.Get(tagInfo) exist, err = session.Get(tagInfo)
if err != nil { if err != nil {
@ -60,7 +61,7 @@ func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, hasR
cond := &entity.Tag{} cond := &entity.Tag{}
session := tr.data.DB.Where("") session := tr.data.DB.Where("")
if name != "" { if name != "" {
session.Where("slug_name LIKE ? or display_name LIKE ?", name+"%", name+"%") session.Where("slug_name LIKE LOWER(?) or display_name LIKE ?", name+"%", name+"%")
} else { } else {
session.UseBool("recommend") session.UseBool("recommend")
cond.Recommend = true cond.Recommend = true
@ -106,6 +107,7 @@ func (tr *tagCommonRepo) GetReservedTagList(ctx context.Context) (tagList []*ent
// GetTagListByNames get tag list all like name // GetTagListByNames get tag list all like name
func (tr *tagCommonRepo) GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error) { func (tr *tagCommonRepo) GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error) {
tagList = make([]*entity.Tag, 0) tagList = make([]*entity.Tag, 0)
session := tr.data.DB.In("slug_name", names).UseBool("recommend", "reserved") session := tr.data.DB.In("slug_name", names).UseBool("recommend", "reserved")
// session.Where(builder.Eq{"status": entity.TagStatusAvailable}) // session.Where(builder.Eq{"status": entity.TagStatusAvailable})
@ -140,7 +142,7 @@ func (tr *tagCommonRepo) GetTagPage(ctx context.Context, page, pageSize int, tag
session := tr.data.DB.NewSession() session := tr.data.DB.NewSession()
if len(tag.SlugName) > 0 { if len(tag.SlugName) > 0 {
session.Where(builder.Or(builder.Like{"slug_name", tag.SlugName}, builder.Like{"display_name", tag.SlugName})) session.Where(builder.Or(builder.Like{"slug_name", fmt.Sprintf("LOWER(%s)", tag.SlugName)}, builder.Like{"display_name", tag.SlugName}))
tag.SlugName = "" tag.SlugName = ""
} }
session.Where(builder.Eq{"status": entity.TagStatusAvailable}) session.Where(builder.Eq{"status": entity.TagStatusAvailable})

View File

@ -186,6 +186,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
// question // question
r.POST("/question", a.questionController.AddQuestion) r.POST("/question", a.questionController.AddQuestion)
r.POST("/question/answer", a.questionController.AddQuestionByAnswer)
r.PUT("/question", a.questionController.UpdateQuestion) r.PUT("/question", a.questionController.UpdateQuestion)
r.DELETE("/question", a.questionController.RemoveQuestion) r.DELETE("/question", a.questionController.RemoveQuestion)
r.PUT("/question/status", a.questionController.CloseQuestion) r.PUT("/question/status", a.questionController.CloseQuestion)

View File

@ -27,6 +27,7 @@ type DashboardInfo struct {
type DashboardInfoVersion struct { type DashboardInfoVersion struct {
Version string `json:"version"` Version string `json:"version"`
Revision string `json:"revision"`
RemoteVersion string `json:"remote_version"` RemoteVersion string `json:"remote_version"`
} }

View File

@ -63,6 +63,33 @@ func (req *QuestionAdd) Check() (errFields []*validator.FormErrorField, err erro
return nil, nil return nil, nil
} }
type QuestionAddByAnswer struct {
// question title
Title string `validate:"required,notblank,gte=6,lte=150" json:"title"`
// content
Content string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
// html
HTML string `json:"-"`
AnswerContent string `validate:"required,notblank,gte=6,lte=65535" json:"answer_content"`
AnswerHTML string `json:"-"`
// tags
Tags []*TagItem `validate:"required,dive" json:"tags"`
// user id
UserID string `json:"-"`
QuestionPermission
}
func (req *QuestionAddByAnswer) Check() (errFields []*validator.FormErrorField, err error) {
req.HTML = converter.Markdown2HTML(req.Content)
req.AnswerHTML = converter.Markdown2HTML(req.AnswerContent)
for _, tag := range req.Tags {
if len(tag.OriginalText) > 0 {
tag.ParsedText = converter.Markdown2HTML(tag.OriginalText)
}
}
return nil, nil
}
type QuestionPermission struct { type QuestionPermission struct {
// whether user can add it // whether user can add it
CanAdd bool `json:"-"` CanAdd bool `json:"-"`

View File

@ -170,6 +170,7 @@ type SiteInfoResp struct {
CustomCssHtml *SiteCustomCssHTMLResp `json:"custom_css_html"` CustomCssHtml *SiteCustomCssHTMLResp `json:"custom_css_html"`
SiteSeo *SiteSeoReq `json:"site_seo"` SiteSeo *SiteSeoReq `json:"site_seo"`
Version string `json:"version"` Version string `json:"version"`
Revision string `json:"revision"`
} }
type TemplateSiteInfoResp struct { type TemplateSiteInfoResp struct {
General *SiteGeneralResp `json:"general"` General *SiteGeneralResp `json:"general"`
@ -225,6 +226,7 @@ type GetSMTPConfigResp struct {
type GetManifestJsonResp struct { type GetManifestJsonResp struct {
ManifestVersion int `json:"manifest_version"` ManifestVersion int `json:"manifest_version"`
Version string `json:"version"` Version string `json:"version"`
Revision string `json:"revision"`
ShortName string `json:"short_name"` ShortName string `json:"short_name"`
Name string `json:"name"` Name string `json:"name"`
Icons map[string]string `json:"icons"` Icons map[string]string `json:"icons"`

View File

@ -18,6 +18,7 @@ import (
usercommon "github.com/answerdev/answer/internal/service/user_common" usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/converter" "github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/obj" "github.com/answerdev/answer/pkg/obj"
"github.com/answerdev/answer/pkg/uid"
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
) )
@ -91,6 +92,10 @@ func (as *ActivityService) GetObjectTimeline(ctx context.Context, req *schema.Ge
item.CancelledAt = act.CancelledAt.Unix() item.CancelledAt = act.CancelledAt.Unix()
} }
if item.ObjectType == constant.QuestionObjectType || item.ObjectType == constant.AnswerObjectType {
item.ObjectID = uid.EnShortID(act.ObjectID)
}
// database save activity type is number, change to activity type string is like "question.asked". // database save activity type is number, change to activity type string is like "question.asked".
// so we need to cut the front part of '.' // so we need to cut the front part of '.'
_, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".") _, item.ActivityType, _ = strings.Cut(config.ID2KeyMapping[act.ActivityType], ".")

View File

@ -326,6 +326,7 @@ func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerA
if err != nil { if err != nil {
return err return err
} }
newAnswerInfo.ID = uid.DeShortID(newAnswerInfo.ID)
if !newAnswerInfoexist { if !newAnswerInfoexist {
return errors.BadRequest(reason.AnswerNotFound) return errors.BadRequest(reason.AnswerNotFound)
} }
@ -335,12 +336,13 @@ func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerA
if err != nil { if err != nil {
return err return err
} }
questionInfo.ID = uid.DeShortID(questionInfo.ID)
if !exist { if !exist {
return errors.BadRequest(reason.QuestionNotFound) return errors.BadRequest(reason.QuestionNotFound)
} }
if questionInfo.UserID != req.UserID { // if questionInfo.UserID != req.UserID {
return fmt.Errorf("no permission to set answer") // return fmt.Errorf("no permission to set answer")
} // }
if questionInfo.AcceptedAnswerID == req.AnswerID { if questionInfo.AcceptedAnswerID == req.AnswerID {
return nil return nil
} }
@ -351,6 +353,7 @@ func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerA
if err != nil { if err != nil {
return err return err
} }
oldAnswerInfo.ID = uid.DeShortID(oldAnswerInfo.ID)
} }
err = as.answerRepo.UpdateAccepted(ctx, req.AnswerID, req.QuestionID) err = as.answerRepo.UpdateAccepted(ctx, req.AnswerID, req.QuestionID)

View File

@ -20,7 +20,7 @@ type AuthRepo interface {
SetAdminUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error SetAdminUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error
RemoveAdminUserCacheInfo(ctx context.Context, accessToken string) (err error) RemoveAdminUserCacheInfo(ctx context.Context, accessToken string) (err error)
AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error) AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error)
RemoveAllUserTokens(ctx context.Context, userID string) RemoveUserTokens(ctx context.Context, userID string)
} }
// AuthService kit service // AuthService kit service
@ -85,9 +85,9 @@ func (as *AuthService) AddUserTokenMapping(ctx context.Context, userID, accessTo
return as.authRepo.AddUserTokenMapping(ctx, userID, accessToken) return as.authRepo.AddUserTokenMapping(ctx, userID, accessToken)
} }
// RemoveAllUserTokens Log out all users under this user id // RemoveUserTokens Log out all users under this user id
func (as *AuthService) RemoveAllUserTokens(ctx context.Context, userID string) { func (as *AuthService) RemoveUserTokens(ctx context.Context, userID string) {
as.authRepo.RemoveAllUserTokens(ctx, userID) as.authRepo.RemoveUserTokens(ctx, userID)
} }
//Admin //Admin

View File

@ -20,7 +20,6 @@ import (
"github.com/answerdev/answer/pkg/encryption" "github.com/answerdev/answer/pkg/encryption"
"github.com/answerdev/answer/pkg/htmltext" "github.com/answerdev/answer/pkg/htmltext"
"github.com/answerdev/answer/pkg/uid" "github.com/answerdev/answer/pkg/uid"
"github.com/davecgh/go-spew/spew"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log" "github.com/segmentfault/pacman/log"
@ -448,7 +447,6 @@ func (cs *CommentService) GetCommentPersonalWithPage(ctx context.Context, req *s
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} else { } else {
spew.Dump("==", objInfo)
commentResp.ObjectType = objInfo.ObjectType commentResp.ObjectType = objInfo.ObjectType
commentResp.Title = objInfo.Title commentResp.Title = objInfo.Title
commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title) commentResp.UrlTitle = htmltext.UrlTitle(objInfo.Title)

View File

@ -90,6 +90,7 @@ func (ds *DashboardService) StatisticalByCache(ctx context.Context) (*schema.Das
startTime := time.Now().Unix() - schema.AppStartTime.Unix() startTime := time.Now().Unix() - schema.AppStartTime.Unix()
dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime) dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime)
dashboardInfo.VersionInfo.Version = constant.Version dashboardInfo.VersionInfo.Version = constant.Version
dashboardInfo.VersionInfo.Revision = constant.Revision
return dashboardInfo, nil return dashboardInfo, nil
} }
@ -194,6 +195,7 @@ func (ds *DashboardService) Statistical(ctx context.Context) (*schema.DashboardI
dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime) dashboardInfo.AppStartTime = fmt.Sprintf("%d", startTime)
dashboardInfo.TimeZone = siteInfoInterface.TimeZone dashboardInfo.TimeZone = siteInfoInterface.TimeZone
dashboardInfo.VersionInfo.Version = constant.Version dashboardInfo.VersionInfo.Version = constant.Version
dashboardInfo.VersionInfo.Revision = constant.Revision
dashboardInfo.VersionInfo.RemoteVersion = ds.RemoteVersion(ctx) dashboardInfo.VersionInfo.RemoteVersion = ds.RemoteVersion(ctx)
return dashboardInfo, nil return dashboardInfo, nil
} }

View File

@ -11,6 +11,7 @@ import (
questioncommon "github.com/answerdev/answer/internal/service/question_common" questioncommon "github.com/answerdev/answer/internal/service/question_common"
tagcommon "github.com/answerdev/answer/internal/service/tag_common" tagcommon "github.com/answerdev/answer/internal/service/tag_common"
"github.com/answerdev/answer/pkg/obj" "github.com/answerdev/answer/pkg/obj"
"github.com/answerdev/answer/pkg/uid"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
) )
@ -50,6 +51,7 @@ func (os *ObjService) GetUnreviewedRevisionInfo(ctx context.Context, objectID st
if err != nil { if err != nil {
return nil, err return nil, err
} }
questionInfo.ID = uid.EnShortID(questionInfo.ID)
if !exist { if !exist {
break break
} }
@ -85,6 +87,7 @@ func (os *ObjService) GetUnreviewedRevisionInfo(ctx context.Context, objectID st
if !exist { if !exist {
break break
} }
questionInfo.ID = uid.EnShortID(questionInfo.ID)
objInfo = &schema.UnreviewedRevisionInfoInfo{ objInfo = &schema.UnreviewedRevisionInfoInfo{
ObjectID: answerInfo.ID, ObjectID: answerInfo.ID,
Title: questionInfo.Title, Title: questionInfo.Title,

View File

@ -3,24 +3,27 @@ package permission
import ( import (
"context" "context"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
) )
// GetAnswerPermission get answer permission // GetAnswerPermission get answer permission
func GetAnswerPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) ( func GetAnswerPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) (
actions []*schema.PermissionMemberAction) { actions []*schema.PermissionMemberAction) {
lang := handler.GetLangByCtx(ctx)
actions = make([]*schema.PermissionMemberAction, 0) actions = make([]*schema.PermissionMemberAction, 0)
if len(userID) > 0 { if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "report", Action: "report",
Name: "Flag", Name: translator.Tr(lang, reportActionName),
Type: "reason", Type: "reason",
}) })
} }
if canEdit || userID == creatorUserID { if canEdit || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "edit", Action: "edit",
Name: "Edit", Name: translator.Tr(lang, editActionName),
Type: "edit", Type: "edit",
}) })
} }
@ -28,7 +31,7 @@ func GetAnswerPermission(ctx context.Context, userID string, creatorUserID strin
if canDelete || userID == creatorUserID { if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "delete", Action: "delete",
Name: "Delete", Name: translator.Tr(lang, deleteActionName),
Type: "confirm", Type: "confirm",
}) })
} }

View File

@ -5,17 +5,20 @@ import (
"time" "time"
"github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/constant"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
) )
// GetCommentPermission get comment permission // GetCommentPermission get comment permission
func GetCommentPermission(ctx context.Context, userID string, creatorUserID string, func GetCommentPermission(ctx context.Context, userID string, creatorUserID string,
createdAt time.Time, canEdit, canDelete bool) (actions []*schema.PermissionMemberAction) { createdAt time.Time, canEdit, canDelete bool) (actions []*schema.PermissionMemberAction) {
lang := handler.GetLangByCtx(ctx)
actions = make([]*schema.PermissionMemberAction, 0) actions = make([]*schema.PermissionMemberAction, 0)
if len(userID) > 0 { if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "report", Action: "report",
Name: "Flag", Name: translator.Tr(lang, reportActionName),
Type: "reason", Type: "reason",
}) })
} }
@ -23,7 +26,7 @@ func GetCommentPermission(ctx context.Context, userID string, creatorUserID stri
if canEdit || (userID == creatorUserID && time.Now().Before(deadline)) { if canEdit || (userID == creatorUserID && time.Now().Before(deadline)) {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "edit", Action: "edit",
Name: "Edit", Name: translator.Tr(lang, editActionName),
Type: "edit", Type: "edit",
}) })
} }
@ -31,7 +34,7 @@ func GetCommentPermission(ctx context.Context, userID string, creatorUserID stri
if canDelete || userID == creatorUserID { if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "delete", Action: "delete",
Name: "Delete", Name: translator.Tr(lang, deleteActionName),
Type: "reason", Type: "reason",
}) })
} }

View File

@ -36,3 +36,11 @@ const (
TagAudit = "tag.audit" TagAudit = "tag.audit"
TagUseReservedTag = "tag.use_reserved_tag" TagUseReservedTag = "tag.use_reserved_tag"
) )
const (
reportActionName = "action.report"
editActionName = "action.edit"
deleteActionName = "action.delete"
closeActionName = "action.close"
reopenActionName = "action.reopen"
)

View File

@ -3,6 +3,8 @@ package permission
import ( import (
"context" "context"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
) )
@ -10,39 +12,40 @@ import (
func GetQuestionPermission(ctx context.Context, userID string, creatorUserID string, func GetQuestionPermission(ctx context.Context, userID string, creatorUserID string,
canEdit, canDelete, canClose, canReopen bool) ( canEdit, canDelete, canClose, canReopen bool) (
actions []*schema.PermissionMemberAction) { actions []*schema.PermissionMemberAction) {
lang := handler.GetLangByCtx(ctx)
actions = make([]*schema.PermissionMemberAction, 0) actions = make([]*schema.PermissionMemberAction, 0)
if len(userID) > 0 { if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "report", Action: "report",
Name: "Flag", Name: translator.Tr(lang, reportActionName),
Type: "reason", Type: "reason",
}) })
} }
if canEdit || userID == creatorUserID { if canEdit || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "edit", Action: "edit",
Name: "Edit", Name: translator.Tr(lang, editActionName),
Type: "edit", Type: "edit",
}) })
} }
if canClose { if canClose {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "close", Action: "close",
Name: "Close", Name: translator.Tr(lang, closeActionName),
Type: "confirm", Type: "confirm",
}) })
} }
if canReopen { if canReopen {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "reopen", Action: "reopen",
Name: "Reopen", Name: translator.Tr(lang, reopenActionName),
Type: "confirm", Type: "confirm",
}) })
} }
if canDelete || userID == creatorUserID { if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "delete", Action: "delete",
Name: "Delete", Name: translator.Tr(lang, deleteActionName),
Type: "confirm", Type: "confirm",
}) })
} }

View File

@ -3,17 +3,20 @@ package permission
import ( import (
"context" "context"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/translator"
"github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/schema"
) )
// GetTagPermission get tag permission // GetTagPermission get tag permission
func GetTagPermission(ctx context.Context, canEdit, canDelete bool) ( func GetTagPermission(ctx context.Context, canEdit, canDelete bool) (
actions []*schema.PermissionMemberAction) { actions []*schema.PermissionMemberAction) {
lang := handler.GetLangByCtx(ctx)
actions = make([]*schema.PermissionMemberAction, 0) actions = make([]*schema.PermissionMemberAction, 0)
if canEdit { if canEdit {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "edit", Action: "edit",
Name: "Edit", Name: translator.Tr(lang, editActionName),
Type: "edit", Type: "edit",
}) })
} }
@ -21,7 +24,7 @@ func GetTagPermission(ctx context.Context, canEdit, canDelete bool) (
if canDelete { if canDelete {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "delete", Action: "delete",
Name: "Delete", Name: translator.Tr(lang, deleteActionName),
Type: "reason", Type: "reason",
}) })
} }
@ -31,11 +34,12 @@ func GetTagPermission(ctx context.Context, canEdit, canDelete bool) (
// GetTagSynonymPermission get tag synonym permission // GetTagSynonymPermission get tag synonym permission
func GetTagSynonymPermission(ctx context.Context, canEdit bool) ( func GetTagSynonymPermission(ctx context.Context, canEdit bool) (
actions []*schema.PermissionMemberAction) { actions []*schema.PermissionMemberAction) {
lang := handler.GetLangByCtx(ctx)
actions = make([]*schema.PermissionMemberAction, 0) actions = make([]*schema.PermissionMemberAction, 0)
if canEdit { if canEdit {
actions = append(actions, &schema.PermissionMemberAction{ actions = append(actions, &schema.PermissionMemberAction{
Action: "edit", Action: "edit",
Name: "Edit", Name: translator.Tr(lang, editActionName),
Type: "edit", Type: "edit",
}) })
} }

View File

@ -335,12 +335,15 @@ func (qs *QuestionCommon) FormatQuestionsPage(
} else { } else {
item.Tags = make([]*schema.TagResp, 0) item.Tags = make([]*schema.TagResp, 0)
} }
userInfo := userInfoMap[item.Operator.ID] userInfo, ok := userInfoMap[item.Operator.ID]
if userInfo != nil { if ok {
item.Operator.DisplayName = userInfo.DisplayName if userInfo != nil {
item.Operator.Username = userInfo.Username item.Operator.DisplayName = userInfo.DisplayName
item.Operator.Rank = userInfo.Rank item.Operator.Username = userInfo.Username
item.Operator.Rank = userInfo.Rank
}
} }
} }
return formattedQuestions, nil return formattedQuestions, nil
} }
@ -414,6 +417,11 @@ func (qs *QuestionCommon) RemoveQuestion(ctx context.Context, req *schema.Remove
if !has { if !has {
return nil return nil
} }
if questionInfo.Status == entity.QuestionStatusDeleted {
return nil
}
questionInfo.Status = entity.QuestionStatusDeleted questionInfo.Status = entity.QuestionStatusDeleted
err = qs.questionRepo.UpdateQuestionStatus(ctx, questionInfo) err = qs.questionRepo.UpdateQuestionStatus(ctx, questionInfo)
if err != nil { if err != nil {

View File

@ -572,6 +572,7 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
// It's not you or the administrator that needs to be reviewed // It's not you or the administrator that needs to be reviewed
if !canUpdate { if !canUpdate {
revisionDTO.Status = entity.RevisionUnreviewedStatus revisionDTO.Status = entity.RevisionUnreviewedStatus
revisionDTO.UserID = req.UserID //use revision userid
} else { } else {
//Direct modification //Direct modification
revisionDTO.Status = entity.RevisionReviewPassStatus revisionDTO.Status = entity.RevisionReviewPassStatus

View File

@ -85,13 +85,42 @@ func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.Searc
return return
} }
ts.TagsFormatRecommendAndReserved(ctx, tags) ts.TagsFormatRecommendAndReserved(ctx, tags)
mainTagId := make([]string, 0)
for _, tag := range tags { for _, tag := range tags {
item := schema.SearchTagLikeResp{} if tag.MainTagID != 0 {
item.SlugName = tag.SlugName mainTagId = append(mainTagId, converter.IntToString(tag.MainTagID))
item.DisplayName = tag.DisplayName }
item.Recommend = tag.Recommend }
item.Reserved = tag.Reserved mainTagList, err := ts.tagCommonRepo.GetTagListByIDs(ctx, mainTagId)
resp = append(resp, item) if err != nil {
return
}
mainTagMap := make(map[string]*entity.Tag)
for _, tag := range mainTagList {
mainTagMap[tag.ID] = tag
}
for _, tag := range tags {
if tag.MainTagID != 0 {
_, ok := mainTagMap[converter.IntToString(tag.MainTagID)]
if ok {
tag.SlugName = mainTagMap[converter.IntToString(tag.MainTagID)].SlugName
tag.DisplayName = mainTagMap[converter.IntToString(tag.MainTagID)].DisplayName
tag.Reserved = mainTagMap[converter.IntToString(tag.MainTagID)].Reserved
tag.Recommend = mainTagMap[converter.IntToString(tag.MainTagID)].Recommend
}
}
}
RepetitiveTag := make(map[string]bool)
for _, tag := range tags {
if _, ok := RepetitiveTag[tag.SlugName]; !ok {
item := schema.SearchTagLikeResp{}
item.SlugName = tag.SlugName
item.DisplayName = tag.DisplayName
item.Recommend = tag.Recommend
item.Reserved = tag.Reserved
resp = append(resp, item)
RepetitiveTag[tag.SlugName] = true
}
} }
return resp, nil return resp, nil
} }
@ -233,8 +262,10 @@ func (ts *TagCommonService) AddTag(ctx context.Context, req *schema.AddTagReq) (
if exist { if exist {
return nil, errors.BadRequest(reason.TagAlreadyExist) return nil, errors.BadRequest(reason.TagAlreadyExist)
} }
SlugName := strings.ReplaceAll(req.SlugName, " ", "-")
SlugName = strings.ToLower(SlugName)
tagInfo := &entity.Tag{ tagInfo := &entity.Tag{
SlugName: strings.ReplaceAll(req.SlugName, " ", "-"), SlugName: SlugName,
DisplayName: req.DisplayName, DisplayName: req.DisplayName,
OriginalText: req.OriginalText, OriginalText: req.OriginalText,
ParsedText: req.ParsedText, ParsedText: req.ParsedText,
@ -535,7 +566,7 @@ func (ts *TagCommonService) ObjectChangeTag(ctx context.Context, objectTagData *
thisObjTagNameList := make([]string, 0) thisObjTagNameList := make([]string, 0)
thisObjTagIDList := make([]string, 0) thisObjTagIDList := make([]string, 0)
for _, t := range objectTagData.Tags { for _, t := range objectTagData.Tags {
// t.SlugName = strings.ToLower(t.SlugName) t.SlugName = strings.ToLower(t.SlugName)
thisObjTagNameList = append(thisObjTagNameList, t.SlugName) thisObjTagNameList = append(thisObjTagNameList, t.SlugName)
} }

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os" "os"
@ -19,6 +20,7 @@ import (
"github.com/answerdev/answer/pkg/uid" "github.com/answerdev/answer/pkg/uid"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
exifremove "github.com/scottleedavis/go-exif-remove"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
) )
@ -192,6 +194,7 @@ func (us *UploaderService) uploadFile(ctx *gin.Context, file *multipart.FileHead
return "", errors.InternalServer(reason.UnknownError).WithError(err).WithStack() return "", errors.InternalServer(reason.UnknownError).WithError(err).WithStack()
} }
defer src.Close() defer src.Close()
Dexif(filePath, filePath)
if !checker.IsSupportedImageFile(src, filepath.Ext(fileSubPath)) { if !checker.IsSupportedImageFile(src, filepath.Ext(fileSubPath)) {
return "", errors.BadRequest(reason.UploadFileUnsupportedFileFormat) return "", errors.BadRequest(reason.UploadFileUnsupportedFileFormat)
@ -200,3 +203,19 @@ func (us *UploaderService) uploadFile(ctx *gin.Context, file *multipart.FileHead
url = fmt.Sprintf("%s/uploads/%s", siteGeneral.SiteUrl, fileSubPath) url = fmt.Sprintf("%s/uploads/%s", siteGeneral.SiteUrl, fileSubPath)
return url, nil return url, nil
} }
func Dexif(filepath string, destpath string) error {
img, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}
noExifBytes, err := exifremove.Remove(img)
if err != nil {
return err
}
err = os.WriteFile(destpath, noExifBytes, 0644)
if err != nil {
return err
}
return nil
}

View File

@ -116,7 +116,7 @@ func (us *UserAdminService) UpdateUserRole(ctx context.Context, req *schema.Upda
return err return err
} }
us.authService.RemoveAllUserTokens(ctx, req.UserID) us.authService.RemoveUserTokens(ctx, req.UserID)
return return
} }
@ -179,7 +179,7 @@ func (us *UserAdminService) UpdateUserPassword(ctx context.Context, req *schema.
return err return err
} }
// logout this user // logout this user
us.authService.RemoveAllUserTokens(ctx, req.UserID) us.authService.RemoveUserTokens(ctx, req.UserID)
return return
} }

View File

@ -535,37 +535,66 @@ func (us *UserService) UserChangeEmailSendCode(ctx context.Context, req *schema.
} }
// UserChangeEmailVerify user change email verify code // UserChangeEmailVerify user change email verify code
func (us *UserService) UserChangeEmailVerify(ctx context.Context, content string) (err error) { func (us *UserService) UserChangeEmailVerify(ctx context.Context, content string) (resp *schema.GetUserResp, err error) {
data := &schema.EmailCodeContent{} data := &schema.EmailCodeContent{}
err = data.FromJSONString(content) err = data.FromJSONString(content)
if err != nil { if err != nil {
return errors.BadRequest(reason.EmailVerifyURLExpired) return nil, errors.BadRequest(reason.EmailVerifyURLExpired)
} }
_, exist, err := us.userRepo.GetByEmail(ctx, data.Email) _, exist, err := us.userRepo.GetByEmail(ctx, data.Email)
if err != nil { if err != nil {
return err return nil, err
} }
if exist { if exist {
return errors.BadRequest(reason.EmailDuplicate) return nil, errors.BadRequest(reason.EmailDuplicate)
} }
_, exist, err = us.userRepo.GetByUserID(ctx, data.UserID) userInfo, exist, err := us.userRepo.GetByUserID(ctx, data.UserID)
if err != nil { if err != nil {
return err return nil, err
} }
if !exist { if !exist {
return errors.BadRequest(reason.UserNotFound) return nil, errors.BadRequest(reason.UserNotFound)
} }
err = us.userRepo.UpdateEmail(ctx, data.UserID, data.Email) err = us.userRepo.UpdateEmail(ctx, data.UserID, data.Email)
if err != nil { if err != nil {
return errors.BadRequest(reason.UserNotFound) return nil, errors.BadRequest(reason.UserNotFound)
} }
err = us.userRepo.UpdateEmailStatus(ctx, data.UserID, entity.EmailStatusAvailable) err = us.userRepo.UpdateEmailStatus(ctx, data.UserID, entity.EmailStatusAvailable)
if err != nil { if err != nil {
return err return nil, err
} }
return nil
roleID, err := us.userRoleService.GetUserRole(ctx, userInfo.ID)
if err != nil {
log.Error(err)
}
resp = &schema.GetUserResp{}
resp.GetFromUserEntity(userInfo)
userCacheInfo := &entity.UserCacheInfo{
UserID: userInfo.ID,
EmailStatus: entity.EmailStatusAvailable,
UserStatus: userInfo.Status,
RoleID: roleID,
}
resp.AccessToken, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)
if err != nil {
return nil, err
}
// User verified email will update user email status. So user status cache should be updated.
if err = us.authService.SetUserStatus(ctx, userCacheInfo); err != nil {
return nil, err
}
resp.RoleID = userCacheInfo.RoleID
if resp.RoleID == role.RoleAdminID {
err = us.authService.SetAdminUserCacheInfo(ctx, resp.AccessToken, &entity.UserCacheInfo{UserID: userInfo.ID})
if err != nil {
return nil, err
}
}
return resp, nil
} }
// getSiteUrl get site url // getSiteUrl get site url

View File

@ -34,6 +34,10 @@ func Markdown2HTML(source string) string {
} }
html := buf.String() html := buf.String()
filter := bluemonday.UGCPolicy() filter := bluemonday.UGCPolicy()
filter.AllowStyling()
filter.RequireNoFollowOnLinks(false)
filter.RequireParseableURLs(false)
filter.RequireNoFollowOnFullyQualifiedLinks(false)
html = filter.Sanitize(html) html = filter.Sanitize(html)
return html return html
} }
@ -110,6 +114,7 @@ func (r *DangerousHTMLRenderer) renderLink(w util.BufWriter, source []byte, node
n := node.(*ast.Link) n := node.(*ast.Link)
if entering && r.renderLinkIsUrl(string(n.Destination)) { if entering && r.renderLinkIsUrl(string(n.Destination)) {
_, _ = w.WriteString("<a href=\"") _, _ = w.WriteString("<a href=\"")
// _, _ = w.WriteString("<a test=\"1\" rel=\"nofollow\" href=\"")
if r.Unsafe || !html.IsDangerousURL(n.Destination) { if r.Unsafe || !html.IsDangerousURL(n.Destination) {
_, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true))) _, _ = w.Write(util.EscapeHTML(util.URLEscape(n.Destination, true)))
} }

View File

@ -64,6 +64,5 @@ func TestUrlTitle(t *testing.T) {
for _, title := range list { for _, title := range list {
formatTitle := UrlTitle(title) formatTitle := UrlTitle(title)
spew.Dump(formatTitle) spew.Dump(formatTitle)
} }
} }

View File

@ -48,7 +48,7 @@ func EnShortID(id string) string {
if ShortIDSwitch { if ShortIDSwitch {
num, err := strconv.ParseInt(id, 10, 64) num, err := strconv.ParseInt(id, 10, 64)
if err != nil { if err != nil {
return "" return id
} }
return NumToShortID(num) return NumToShortID(num)
} }

View File

@ -336,6 +336,7 @@ export interface SiteSettings {
theme: AdminSettingsTheme; theme: AdminSettingsTheme;
site_seo: AdminSettingsSeo; site_seo: AdminSettingsSeo;
version: string; version: string;
revision: string;
} }
export interface AdminSettingBranding { export interface AdminSettingBranding {

View File

@ -11,6 +11,7 @@ const Index: FC = () => {
(state) => state.items, (state) => state.items,
); );
const appVersion = siteInfoStore((_) => _.version); const appVersion = siteInfoStore((_) => _.version);
const hashVersion = siteInfoStore((_) => _.revision);
const setAppGenerator = () => { const setAppGenerator = () => {
if (!appVersion) { if (!appVersion) {
return; return;
@ -19,7 +20,7 @@ const Index: FC = () => {
if (generatorMetaNode) { if (generatorMetaNode) {
generatorMetaNode.setAttribute( generatorMetaNode.setAttribute(
'content', 'content',
`Answer ${appVersion} - https://github.com/answerdev/answer`, `Answer ${appVersion} - https://github.com/answerdev/answer version ${hashVersion}`,
); );
} }
}; };

View File

@ -6,8 +6,9 @@ import { DEFAULT_SITE_NAME } from '@/common/constants';
interface SiteInfoType { interface SiteInfoType {
siteInfo: AdminSettingsGeneral; siteInfo: AdminSettingsGeneral;
version: string; version: string;
revision: string;
update: (params: AdminSettingsGeneral) => void; update: (params: AdminSettingsGeneral) => void;
updateVersion: (ver: string) => void; updateVersion: (ver: string, revision: string) => void;
} }
const siteInfo = create<SiteInfoType>((set) => ({ const siteInfo = create<SiteInfoType>((set) => ({
@ -20,6 +21,7 @@ const siteInfo = create<SiteInfoType>((set) => ({
permalink: 1, permalink: 1,
}, },
version: '', version: '',
revision: '',
update: (params) => update: (params) =>
set((_) => { set((_) => {
const o = { ..._.siteInfo, ...params }; const o = { ..._.siteInfo, ...params };
@ -30,9 +32,9 @@ const siteInfo = create<SiteInfoType>((set) => ({
siteInfo: o, siteInfo: o,
}; };
}), }),
updateVersion: (ver) => { updateVersion: (ver, revision) => {
set(() => { set(() => {
return { version: ver }; return { version: ver, revision };
}); });
}, },
})); }));

View File

@ -305,7 +305,9 @@ export const initAppSettingsStore = async () => {
const appSettings = await getAppSettings(); const appSettings = await getAppSettings();
if (appSettings) { if (appSettings) {
siteInfoStore.getState().update(appSettings.general); siteInfoStore.getState().update(appSettings.general);
siteInfoStore.getState().updateVersion(appSettings.version); siteInfoStore
.getState()
.updateVersion(appSettings.version, appSettings.revision);
interfaceStore.getState().update(appSettings.interface); interfaceStore.getState().update(appSettings.interface);
brandingStore.getState().update(appSettings.branding); brandingStore.getState().update(appSettings.branding);
loginSettingStore.getState().update(appSettings.login); loginSettingStore.getState().update(appSettings.login);

View File

@ -3,7 +3,7 @@
<div class="border-bottom py-2 comment-item border-top"> <div class="border-bottom py-2 comment-item border-top">
<div class="d-block"> <div class="d-block">
<div class="fmt fs-14"> <div class="fmt fs-14">
{{templateHTML .ParsedText}} {{formatLinkNofollow .ParsedText}}
</div> </div>
</div> </div>
<div class="d-flex justify-content-between fs-14"> <div class="d-flex justify-content-between fs-14">

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>{{.title}}</title> <title>{{.title}}</title>
<meta name="description" content="{{.description}}" data-rh="true" /> <meta name="description" content="{{.description}}" data-rh="true" />
<meta name="generator" content="Answer {{.Version}} - https://github.com/answerdev/answer"> <meta name="generator" content="Answer {{.Version}} - https://github.com/answerdev/answer version {{.Revision}}">
{{if .keywords }}<meta name="keywords" content="{{.keywords}}" data-rh="true" />{{end}} {{if .keywords }}<meta name="keywords" content="{{.keywords}}" data-rh="true" />{{end}}
<link rel="canonical" href="{{.siteinfo.Canonical}}" /> <link rel="canonical" href="{{.siteinfo.Canonical}}" />

View File

@ -28,7 +28,7 @@
{{end}} {{end}}
</div> </div>
<article class="fmt text-break text-wrap mt-4"> <article class="fmt text-break text-wrap mt-4">
{{templateHTML .detail.HTML}} {{formatLinkNofollow .detail.HTML}}
</article> </article>
<div class="mt-4"> <div class="mt-4">
<div role="group" class="btn-group"> <div role="group" class="btn-group">
@ -98,7 +98,7 @@
{{range .answers}} {{range .answers}}
<div id="10020000000000930" class="answer-item py-4"> <div id="10020000000000930" class="answer-item py-4">
<article class="fmt"> <article class="fmt">
{{templateHTML .HTML}} {{formatLinkNofollow .HTML}}
</article> </article>
<div class="d-flex align-items-center mt-4"> <div class="d-flex align-items-center mt-4">
<div class=""> <div class="">

View File

@ -8,7 +8,7 @@
>{{$.tag.SlugName}}</a >{{$.tag.SlugName}}</a
> >
</h3> </h3>
<p class="text-break">{{templateHTML $.tag.ParsedText}}</p> <p class="text-break">{{formatLinkNofollow $.tag.ParsedText}}</p>
</div> </div>
<div> <div>
<div class="mb-3 d-flex flex-wrap justify-content-between"> <div class="mb-3 d-flex flex-wrap justify-content-between">