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:
Ed Burnette 2023-12-07 01:23:52 -05:00 committed by GitHub
parent 15a6b19897
commit 60f1f71d27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1600 additions and 1067 deletions

14
docker-compose/README.md Normal file
View File

@ -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
```

View File

@ -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

View File

@ -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;
}

View File

@ -1,14 +0,0 @@
### docker-compose 部署教程
- 将打包好的前端文件放到 `nginx/html` 目录下
- ```shell
# 启动
docker-compose up -d
```
- ```shell
# 查看运行状态
docker ps
```
- ```shell
# 结束运行
docker-compose down
```

View File

@ -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">

View File

@ -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",

File diff suppressed because it is too large Load Diff

View File

@ -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>) {

View File

@ -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':

100
src/locales/es-ES.ts Normal file
View File

@ -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',
},
}

View File

@ -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,

View File

@ -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',

View File

@ -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