Added es-ES (#1989)
* Additional English translations Provide an English version of the readme, and add a few translations that were missing elsewhere * Use browser language by default * Support 'en' and 'vi' as languages * Fixed: Browserslist: caniuse-lite is outdated. Full message was: Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest Why you should do it regularly: https://github.com/browserslist/update-db#readme * Added es-ES These changes were originally from https://github.com/rasta26/chatgpt-web although I did tweak the translations a bit. --------- Co-authored-by: Ed Burnette <ed.burnette@hiddenmind.ai>
This commit is contained in:
parent
15a6b19897
commit
60f1f71d27
|
@ -0,0 +1,14 @@
|
|||
### docker-compose Deployment Tutorial
|
||||
-Put the packaged front-end files in the `nginx/html` directory
|
||||
- ```shell
|
||||
# start up
|
||||
docker-compose up -d
|
||||
```
|
||||
- ```shell
|
||||
# Check the running status
|
||||
docker ps
|
||||
```
|
||||
- ```shell
|
||||
# end run
|
||||
docker-compose down
|
||||
```
|
|
@ -3,35 +3,35 @@ version: '3'
|
|||
services:
|
||||
app:
|
||||
container_name: chatgpt-web
|
||||
image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
||||
image: chenzhaoyu94/chatgpt-web # Always use latest, just pull the tag image again when updating
|
||||
ports:
|
||||
- 3002:3002
|
||||
environment:
|
||||
# 二选一
|
||||
# pick one of two
|
||||
OPENAI_API_KEY:
|
||||
# 二选一
|
||||
# pick one of two
|
||||
OPENAI_ACCESS_TOKEN:
|
||||
# API接口地址,可选,设置 OPENAI_API_KEY 时可用
|
||||
# API interface address, optional, available when OPENAI_API_KEY is set
|
||||
OPENAI_API_BASE_URL:
|
||||
# API模型,可选,设置 OPENAI_API_KEY 时可用
|
||||
# API model, optional, available when OPENAI_API_KEY is set
|
||||
OPENAI_API_MODEL:
|
||||
# 反向代理,可选
|
||||
# reverse proxy, optional
|
||||
API_REVERSE_PROXY:
|
||||
# 访问权限密钥,可选
|
||||
# Access permission key, optional
|
||||
AUTH_SECRET_KEY:
|
||||
# 每小时最大请求次数,可选,默认无限
|
||||
# The maximum number of requests per hour, optional, default unlimited
|
||||
MAX_REQUEST_PER_HOUR: 0
|
||||
# 超时,单位毫秒,可选
|
||||
# timeout in milliseconds, optional
|
||||
TIMEOUT_MS: 60000
|
||||
# Socks代理,可选,和 SOCKS_PROXY_PORT 一起时生效
|
||||
# Socks proxy, optional, works with SOCKS_PROXY_PORT
|
||||
SOCKS_PROXY_HOST:
|
||||
# Socks代理端口,可选,和 SOCKS_PROXY_HOST 一起时生效
|
||||
# Socks proxy port, optional, effective when combined with SOCKS_PROXY_HOST
|
||||
SOCKS_PROXY_PORT:
|
||||
# Socks代理用户名,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
||||
# Socks proxy username, optional, effective when combined with SOCKS_PROXY_HOST & SOCKS_PROXY_PORT
|
||||
SOCKS_PROXY_USERNAME:
|
||||
# Socks代理密码,可选,和 SOCKS_PROXY_HOST & SOCKS_PROXY_PORT 一起时生效
|
||||
# Socks proxy password, optional, effective when combined with SOCKS_PROXY_HOST & SOCKS_PROXY_PORT
|
||||
SOCKS_PROXY_PASSWORD:
|
||||
# HTTPS_PROXY 代理,可选
|
||||
# HTTPS_PROXY proxy, optional
|
||||
HTTPS_PROXY:
|
||||
nginx:
|
||||
container_name: nginx
|
||||
|
|
|
@ -4,7 +4,7 @@ server {
|
|||
charset utf-8;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
# 防止爬虫抓取
|
||||
# Prevent crawlers from crawling
|
||||
if ($http_user_agent ~* "360Spider|JikeSpider|Spider|spider|bot|Bot|2345Explorer|curl|wget|webZIP|qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot|NSPlayer|bingbot")
|
||||
{
|
||||
return 403;
|
||||
|
@ -16,7 +16,7 @@ server {
|
|||
}
|
||||
|
||||
location /api {
|
||||
proxy_set_header X-Real-IP $remote_addr; #转发用户IP
|
||||
proxy_set_header X-Real-IP $remote_addr; #Forward user IP
|
||||
proxy_pass http://app:3002;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
### docker-compose 部署教程
|
||||
- 将打包好的前端文件放到 `nginx/html` 目录下
|
||||
- ```shell
|
||||
# 启动
|
||||
docker-compose up -d
|
||||
```
|
||||
- ```shell
|
||||
# 查看运行状态
|
||||
docker ps
|
||||
```
|
||||
- ```shell
|
||||
# 结束运行
|
||||
docker-compose down
|
||||
```
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-cmn-Hans">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.21",
|
||||
"rimraf": "^4.2.0",
|
||||
"rimraf": "^4.3.0",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"typescript": "~4.9.5",
|
||||
"vite": "^4.2.0",
|
||||
|
|
2485
pnpm-lock.yaml
2485
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -54,11 +54,13 @@ const themeOptions: { label: string; key: Theme; icon: string }[] = [
|
|||
]
|
||||
|
||||
const languageOptions: { label: string; key: Language; value: Language }[] = [
|
||||
{ label: '简体中文', key: 'zh-CN', value: 'zh-CN' },
|
||||
{ label: '繁體中文', key: 'zh-TW', value: 'zh-TW' },
|
||||
{ label: 'English', key: 'en-US', value: 'en-US' },
|
||||
{ label: 'Español', key: 'es-ES', value: 'es-ES' },
|
||||
{ label: '한국어', key: 'ko-KR', value: 'ko-KR' },
|
||||
{ label: 'Русский язык', key: 'ru-RU', value: 'ru-RU' },
|
||||
{ label: 'Tiếng Việt', key: 'vi-VN', value: 'vi-VN' },
|
||||
{ label: '简体中文', key: 'zh-CN', value: 'zh-CN' },
|
||||
{ label: '繁體中文', key: 'zh-TW', value: 'zh-TW' },
|
||||
]
|
||||
|
||||
function updateUserInfo(options: Partial<UserInfo>) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { computed } from 'vue'
|
||||
import { enUS, koKR, ruRU, viVN, zhCN, zhTW } from 'naive-ui'
|
||||
import { enUS, esAR, koKR, ruRU, viVN, zhCN, zhTW } from 'naive-ui'
|
||||
import { useAppStore } from '@/store'
|
||||
import { setLocale } from '@/locales'
|
||||
|
||||
|
@ -11,6 +11,8 @@ export function useLanguage() {
|
|||
switch (appStore.language) {
|
||||
case 'en-US':
|
||||
return enUS
|
||||
case 'es-ES':
|
||||
return esAR
|
||||
case 'ko-KR':
|
||||
return koKR
|
||||
case 'vi-VN':
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
export default {
|
||||
common: {
|
||||
add: 'Agregar',
|
||||
addSuccess: 'Agregado con éxito',
|
||||
edit: 'Editar',
|
||||
editSuccess: 'Edición exitosa',
|
||||
delete: 'Borrar',
|
||||
deleteSuccess: 'Borrado con éxito',
|
||||
save: 'Guardar',
|
||||
saveSuccess: 'Guardado con éxito',
|
||||
reset: 'Reiniciar',
|
||||
action: 'Acción',
|
||||
export: 'Exportar',
|
||||
exportSuccess: 'Exportación exitosa',
|
||||
import: 'Importar',
|
||||
importSuccess: 'Importación exitosa',
|
||||
clear: 'Limpiar',
|
||||
clearSuccess: 'Limpieza exitosa',
|
||||
yes: 'Sí',
|
||||
no: 'No',
|
||||
confirm: 'Confirmar',
|
||||
download: 'Descargar',
|
||||
noData: 'Sin datos',
|
||||
wrong: 'Algo salió mal, inténtalo de nuevo más tarde.',
|
||||
success: 'Exitoso',
|
||||
failed: 'Fallido',
|
||||
verify: 'Verificar',
|
||||
unauthorizedTips: 'No autorizado, por favor verifique primero.',
|
||||
stopResponding: 'No responde',
|
||||
},
|
||||
chat: {
|
||||
newChatButton: 'Nueva conversación',
|
||||
newChatTitle: 'Nueva conversación',
|
||||
placeholder: 'Pregúntame lo que sea...(Shift + Enter = salto de línea, "/" para activar avisos)',
|
||||
placeholderMobile: 'Pregúntame lo que sea...',
|
||||
copy: 'Copiar',
|
||||
copied: 'Copiado',
|
||||
copyCode: 'Copiar código',
|
||||
copyFailed: 'Copia fallida',
|
||||
clearChat: 'Limpiar chat',
|
||||
clearChatConfirm: '¿Estás seguro de borrar este chat?',
|
||||
exportImage: 'Exportar imagen',
|
||||
exportImageConfirm: '¿Estás seguro de exportar este chat a png?',
|
||||
exportSuccess: 'Exportación exitosa',
|
||||
exportFailed: 'Exportación fallida',
|
||||
usingContext: 'Modo de contexto',
|
||||
turnOnContext: 'En el modo actual, el envío de mensajes llevará registros de chat anteriores.',
|
||||
turnOffContext: 'En el modo actual, el envío de mensajes no incluirá registros de conversaciones anteriores.',
|
||||
deleteMessage: 'Borrar mensaje',
|
||||
deleteMessageConfirm: '¿Estás seguro de eliminar este mensaje?',
|
||||
deleteHistoryConfirm: '¿Estás seguro de borrar esta historia?',
|
||||
clearHistoryConfirm: '¿Estás seguro de borrar el historial de chat?',
|
||||
preview: 'Avance',
|
||||
showRawText: 'Mostrar como texto sin formato',
|
||||
},
|
||||
setting: {
|
||||
setting: 'Configuración',
|
||||
general: 'General',
|
||||
advanced: 'Avanzado',
|
||||
config: 'Configurar',
|
||||
avatarLink: 'Enlace de avatar',
|
||||
name: 'Nombre',
|
||||
description: 'Descripción',
|
||||
role: 'Rol',
|
||||
temperature: 'Temperatura',
|
||||
top_p: 'Top_p',
|
||||
resetUserInfo: 'Restablecer información de usuario',
|
||||
chatHistory: 'Historial de chat',
|
||||
theme: 'Tema',
|
||||
language: 'Idioma',
|
||||
api: 'API',
|
||||
reverseProxy: 'Reverse Proxy',
|
||||
timeout: 'Tiempo de espera',
|
||||
socks: 'Socks',
|
||||
httpsProxy: 'HTTPS Proxy',
|
||||
balance: 'Saldo de API',
|
||||
monthlyUsage: 'Uso mensual de API',
|
||||
openSource: 'Este proyecto es de código abierto en',
|
||||
freeMIT: 'gratis y basado en la licencia MIT, ¡sin ningún tipo de comportamiento de pago!',
|
||||
stars: 'Si encuentras este proyecto útil, por favor dame una Estrella en GitHub o da un pequeño patrocinio, ¡gracias!',
|
||||
},
|
||||
store: {
|
||||
siderButton: 'Tienda rápida',
|
||||
local: 'Local',
|
||||
online: 'En línea',
|
||||
title: 'Título',
|
||||
description: 'Descripción',
|
||||
clearStoreConfirm: '¿Estás seguro de borrar los datos?',
|
||||
importPlaceholder: 'Pegue los datos JSON aquí',
|
||||
addRepeatTitleTips: 'Título duplicado, vuelva a ingresar',
|
||||
addRepeatContentTips: 'Contenido duplicado: {msg}, por favor vuelva a entrar',
|
||||
editRepeatTitleTips: 'Conflicto de título, revíselo',
|
||||
editRepeatContentTips: 'Conflicto de contenido {msg} , por favor vuelva a modificar',
|
||||
importError: 'Discrepancia de valor clave',
|
||||
importRepeatTitle: 'Título saltado repetidamente: {msg}',
|
||||
importRepeatContent: 'El contenido se salta repetidamente: {msg}',
|
||||
onlineImportWarning: 'Nota: ¡Compruebe la fuente del archivo JSON!',
|
||||
downloadError: 'Verifique el estado de la red y la validez del archivo JSON',
|
||||
},
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import type { App } from 'vue'
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import enUS from './en-US'
|
||||
import esES from './es-ES'
|
||||
import koKR from './ko-KR'
|
||||
import ruRU from './ru-RU'
|
||||
import viVN from './vi-VN'
|
||||
|
@ -19,6 +20,7 @@ const i18n = createI18n({
|
|||
allowComposition: true,
|
||||
messages: {
|
||||
'en-US': enUS,
|
||||
'es-ES': esES,
|
||||
'ko-KR': koKR,
|
||||
'ru-RU': ruRU,
|
||||
'vi-VN': viVN,
|
||||
|
|
|
@ -4,11 +4,13 @@ const LOCAL_NAME = 'appSetting'
|
|||
|
||||
export type Theme = 'light' | 'dark' | 'auto'
|
||||
|
||||
export type Language = 'en-US' | 'ko-KR' | 'ru-RU' | 'vi-VN' | 'zh-CN' | 'zh-TW'
|
||||
export type Language = 'en-US' | 'es-ES' | 'ko-KR' | 'ru-RU' | 'vi-VN' | 'zh-CN' | 'zh-TW'
|
||||
|
||||
const languageMap: { [key: string]: Language } = {
|
||||
'en': 'en-US',
|
||||
'en-US': 'en-US',
|
||||
'es': 'es-ES',
|
||||
'es-ES': 'es-ES',
|
||||
'ko': 'ko-KR',
|
||||
'ko-KR': 'ko-KR',
|
||||
'ru': 'ru-RU',
|
||||
|
|
|
@ -28,7 +28,7 @@ export function useScroll(): ScrollReturn {
|
|||
const scrollToBottomIfAtBottom = async () => {
|
||||
await nextTick()
|
||||
if (scrollRef.value) {
|
||||
const threshold = 100 // 阈值,表示滚动条到底部的距离阈值
|
||||
const threshold = 100 // Threshold, indicating the distance threshold to the bottom of the scroll bar.
|
||||
const distanceToBottom = scrollRef.value.scrollHeight - scrollRef.value.scrollTop - scrollRef.value.clientHeight
|
||||
if (distanceToBottom <= threshold)
|
||||
scrollRef.value.scrollTop = scrollRef.value.scrollHeight
|
||||
|
|
Loading…
Reference in New Issue