Skip to content

Commit 7cf20c6

Browse files
baozhoutaoclaude
andauthored
fix(service-settings): complete zh-CN coverage for auth/sms/ai settings (objectui#2851 P1) (#3598)
* fix(service-settings): complete zh-CN coverage for auth/sms/ai settings The auth, sms and ai manifests grew new groups and fields (breach/complexity/ history/expiry password policy, anti-abuse, MFA, extra session controls, network allow-list; the whole SMS Delivery namespace; DeepSeek/DashScope/ Cloudflare/SiliconFlow/OpenRouter providers + conversation-title options) but the zh-CN bundle wasn't updated, so those settings rendered English under a Chinese UI (objectui#2851 P1). - Fill every missing zh-CN group title + field label/help for auth, sms, ai. - Add a coverage guard test that pins zh-CN to full title/group/key coverage of every built-in settings manifest (option labels excluded — several are codes/format specimens that must not be translated). localization option labels (timezones/currencies/format specimens) remain a tracked follow-up; ja-JP / es-ES parity likewise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(service-settings): zh-CN option labels + ja-JP/es-ES parity (objectui#2851 P1) Follow-up to the zh-CN auth/sms/ai fix: - zh-CN: translate the remaining localization option labels (timezone, locale, date/number format, currency, fiscal-year-start) — the settings bundle is now 100% covered, options included. - ja-JP / es-ES: bring both to full structural parity — extend auth (policy / anti-abuse / MFA / extra session controls / network) and ai (DeepSeek / DashScope / Cloudflare / SiliconFlow / OpenRouter + conversation titles), and add the previously-missing sms, company and localization namespaces. - Generalize the coverage guard to all three shipped non-English locales (renamed zh-CN.coverage.test.ts → settings-translation-coverage.test.ts). Package suite: 12 files / 175 tests pass (31 coverage assertions). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1f8390b commit 7cf20c6

4 files changed

Lines changed: 658 additions & 6 deletions

File tree

packages/services/service-settings/src/translations/es-ES.ts

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ export const esES: TranslationData = {
7676
title: 'Correo y contraseña',
7777
description: 'Controla el inicio de sesión local con correo/contraseña y el registro de autoservicio.',
7878
},
79+
password_policy: {
80+
title: 'Política de contraseñas',
81+
description: 'Límites de longitud que el proveedor de autenticación exige en el registro y el restablecimiento de contraseña.',
82+
},
83+
anti_abuse: {
84+
title: 'Antiabuso',
85+
description: 'Protección contra fuerza bruta: bloqueo de cuenta por identidad y límite de tasa por IP en los endpoints de autenticación.',
86+
},
87+
multi_factor: {
88+
title: 'Multifactor',
89+
description: 'Exige a los miembros proteger su cuenta con una aplicación de autenticación (TOTP).',
90+
},
91+
sessions: {
92+
title: 'Sesiones',
93+
description: 'Cuánto tiempo permanece válida una sesión iniciada.',
94+
},
95+
network: {
96+
title: 'Red',
97+
description: 'Restringe desde dónde pueden autenticarse los usuarios.',
98+
},
7999
social: {
80100
title: 'Inicio de sesión social',
81101
description:
@@ -86,6 +106,70 @@ export const esES: TranslationData = {
86106
email_password_enabled: { label: 'Habilitar inicio de sesión con correo/contraseña' },
87107
signup_enabled: { label: 'Permitir registro de autoservicio' },
88108
require_email_verification: { label: 'Requerir verificación de correo' },
109+
password_min_length: { label: 'Longitud mínima de contraseña' },
110+
password_max_length: { label: 'Longitud máxima de contraseña', help: 'Un límite superior protege frente a la denegación de servicio por el hasheo de contraseñas muy largas.' },
111+
password_reject_breached: {
112+
label: 'Rechazar contraseñas filtradas',
113+
help: 'Bloquea contraseñas presentes en corpus públicos de filtraciones mediante Have I Been Pwned (comprobación de rango con k-anonimato; la contraseña nunca se envía completa).',
114+
},
115+
password_require_complexity: {
116+
label: 'Requerir contraseñas complejas',
117+
help: 'Exige que las contraseñas combinen clases de caracteres (mayúsculas, minúsculas, dígitos, símbolos) en el registro y al cambiar/restablecer la contraseña.',
118+
},
119+
password_min_classes: {
120+
label: 'Clases de caracteres mínimas',
121+
help: 'Cuántas de las cuatro clases (mayúscula / minúscula / dígito / símbolo) debe incluir una contraseña.',
122+
},
123+
password_history_count: {
124+
label: 'Historial de contraseñas (sin reutilización)',
125+
help: 'Impide reutilizar este número de contraseñas anteriores al cambiar/restablecer. 0 desactiva la comprobación.',
126+
},
127+
password_expiry_days: {
128+
label: 'Caducidad de contraseña (días)',
129+
help: 'Fuerza un cambio de contraseña tras este número de días. 0 desactiva la caducidad. Mientras está caducada, el usuario no puede acceder a los datos hasta que la cambie.',
130+
},
131+
lockout_threshold: {
132+
label: 'Umbral de bloqueo de cuenta',
133+
help: 'Bloquea una cuenta tras este número de inicios de sesión fallidos consecutivos. 0 desactiva el bloqueo. Mientras está bloqueada, el inicio de sesión se rechaza incluso con la contraseña correcta.',
134+
},
135+
lockout_duration_minutes: {
136+
label: 'Duración del bloqueo (minutos)',
137+
help: 'Cuánto permanece bloqueada una cuenta una vez superado el umbral.',
138+
},
139+
rate_limit_max: {
140+
label: 'Límite de tasa de autenticación: máx. solicitudes',
141+
help: 'Máximo de solicitudes por IP y ventana a los endpoints de inicio de sesión / registro / restablecimiento de contraseña.',
142+
},
143+
rate_limit_window_seconds: {
144+
label: 'Límite de tasa de autenticación: ventana (segundos)',
145+
help: 'Ventana deslizante durante la cual se cuenta el tope de solicitudes anterior.',
146+
},
147+
mfa_required: {
148+
label: 'Requerir autenticación multifactor',
149+
help: 'Los usuarios sin un autenticador registrado quedan bloqueados del acceso a los datos al terminar su periodo de gracia. Activarlo también habilita la función de dos factores para que los usuarios puedan registrarse.',
150+
},
151+
mfa_grace_period_days: {
152+
label: 'Periodo de gracia de MFA (días)',
153+
help: 'Cuánto pueden aplazar el registro los usuarios antes del bloqueo definitivo. 0 bloquea de inmediato.',
154+
},
155+
session_expiry_days: { label: 'Duración de la sesión (días)', help: 'Una sesión caduca este número de días después del inicio de sesión.' },
156+
session_refresh_days: { label: 'Umbral de renovación (días)', help: 'Una sesión activa se extiende cuando es más antigua que esto.' },
157+
session_idle_timeout_minutes: {
158+
label: 'Tiempo de inactividad (minutos)',
159+
help: 'Cierra la sesión del usuario tras este número de minutos de inactividad. 0 desactiva.',
160+
},
161+
session_absolute_max_hours: {
162+
label: 'Duración absoluta de la sesión (horas)',
163+
help: 'Fuerza una reautenticación este número de horas después del inicio de sesión, independientemente de la actividad. 0 desactiva.',
164+
},
165+
max_concurrent_sessions_per_user: {
166+
label: 'Máx. sesiones simultáneas por usuario',
167+
help: 'Limita las sesiones iniciadas simultáneas por usuario; las más antiguas se cierran al superar el límite. 0 = ilimitado.',
168+
},
169+
allowed_ip_ranges: {
170+
label: 'Rangos de IP permitidos',
171+
help: 'Rangos CIDR o IP exactas (una por línea o separadas por comas), p. ej. 203.0.113.0/24. Cuando se define, se rechaza el inicio de sesión desde fuera de estos rangos. Vacío = sin restricción. Requiere un proxy de confianza que defina X-Forwarded-For.',
172+
},
89173
google_enabled: {
90174
label: 'Habilitar inicio de sesión con Google',
91175
help: 'Requiere un ID de cliente y un secreto de OAuth de Google desde Google Cloud Console.',
@@ -101,6 +185,83 @@ export const esES: TranslationData = {
101185
},
102186
},
103187

188+
sms: {
189+
title: 'Envío de SMS',
190+
description: 'Configuración del proveedor de SMS para inicio de sesión con OTP, invitaciones y notificaciones.',
191+
groups: {
192+
provider: { title: 'Proveedor', description: 'Elige cómo envía este espacio de trabajo los SMS salientes.' },
193+
aliyun: { title: 'Aliyun SMS' },
194+
twilio: { title: 'Twilio' },
195+
},
196+
keys: {
197+
provider: {
198+
label: 'Proveedor',
199+
options: { log: 'Ninguno (solo registro — sin envío real)', aliyun: 'Aliyun SMS', twilio: 'Twilio' },
200+
},
201+
aliyun_access_key_id: { label: 'AccessKey ID' },
202+
aliyun_access_key_secret: { label: 'AccessKey Secret' },
203+
aliyun_sign_name: { label: 'Nombre de firma (短信签名)' },
204+
aliyun_template_code: {
205+
label: 'Código de plantilla predeterminado',
206+
help: 'Se usa cuando un envío no lleva una plantilla explícita. Una plantilla genérica con una sola variable ${content} permite enviar SMS de notificación genéricos.',
207+
},
208+
twilio_account_sid: { label: 'Account SID' },
209+
twilio_auth_token: { label: 'Token de autenticación' },
210+
twilio_from_number: {
211+
label: 'Número de origen',
212+
help: 'Remitente en formato E.164, p. ej. +15005550006. Se necesita esto o un Messaging Service SID.',
213+
},
214+
twilio_messaging_service_sid: { label: 'Messaging Service SID' },
215+
},
216+
actions: { test: { label: 'Enviar SMS de prueba' } },
217+
},
218+
219+
company: {
220+
title: 'Empresa',
221+
description: 'Identidad de la entidad legal: nombre registrado, dirección, identificadores fiscales y contacto principal.',
222+
groups: {
223+
identity: { title: 'Identidad' },
224+
address: { title: 'Domicilio registrado' },
225+
contact: { title: 'Contacto' },
226+
},
227+
keys: {
228+
legal_name: { label: 'Razón social', help: 'Nombre legal registrado de la organización (puede diferir del nombre del espacio de trabajo).' },
229+
registration_number: { label: 'Número de registro', help: 'Número de registro / constitución de la empresa (p. ej. EIN, n.º de sociedad).' },
230+
tax_id: { label: 'ID fiscal / IVA', help: 'Identificador fiscal que aparece en las facturas (p. ej. IVA, GST, ABN).' },
231+
address_line1: { label: 'Dirección línea 1' },
232+
address_line2: { label: 'Dirección línea 2' },
233+
city: { label: 'Ciudad' },
234+
state: { label: 'Provincia / Estado' },
235+
postal_code: { label: 'Código postal' },
236+
country: { label: 'País', help: 'Código ISO 3166-1 alfa-2 (p. ej. US, GB, CN).' },
237+
phone: { label: 'Teléfono', help: 'Teléfono principal de la empresa (se recomienda E.164, p. ej. +1 415 555 0100).' },
238+
website: { label: 'Sitio web', help: 'Ejemplo: https://example.com' },
239+
primary_contact_name: { label: 'Nombre del contacto principal' },
240+
primary_contact_email: { label: 'Correo del contacto principal', help: 'Ejemplo: ops@example.com' },
241+
},
242+
},
243+
244+
localization: {
245+
title: 'Localización',
246+
description: 'Zona horaria, idioma, moneda y formatos de fecha/número predeterminados.',
247+
groups: {
248+
region: { title: 'Región' },
249+
formats: { title: 'Formatos' },
250+
finance: { title: 'Finanzas' },
251+
},
252+
keys: {
253+
timezone: { label: 'Zona horaria predeterminada', help: 'Zona IANA usada para resolver today()/daysFromNow, los buckets de fecha de analítica y las fechas/horas renderizadas.' },
254+
locale: { label: 'Idioma predeterminado', help: 'Configuración regional BCP-47 para los catálogos de mensajes y el formato de números/fechas.' },
255+
default_country: { label: 'País predeterminado', help: 'Código ISO 3166-1 alfa-2 (p. ej. US, GB, CN). Se usa para los valores por defecto de dirección y teléfono.' },
256+
date_format: { label: 'Formato de fecha' },
257+
time_format: { label: 'Formato de hora' },
258+
number_format: { label: 'Formato de número', help: 'Separadores de miles y decimales para los números mostrados.' },
259+
first_day_of_week: { label: 'Primer día de la semana', help: 'Ancla los buckets de analítica semanal y las cuadrículas de calendario.' },
260+
currency: { label: 'Moneda predeterminada', help: 'Código ISO 4217 aplicado cuando un campo de moneda no indica la suya.' },
261+
fiscal_year_start: { label: 'Inicio del año fiscal', help: 'Primer mes del año fiscal: determina «este trimestre / año fiscal» en los informes.' },
262+
},
263+
},
264+
104265
feature_flags: {
105266
title: 'Indicadores de función',
106267
description: 'Activa funciones experimentales y en beta para este espacio de trabajo.',
@@ -171,6 +332,12 @@ export const esES: TranslationData = {
171332
openai: { title: 'OpenAI' },
172333
anthropic: { title: 'Anthropic' },
173334
google: { title: 'Google' },
335+
deepseek: { title: 'DeepSeek', description: 'API compatible con OpenAI en https://api.deepseek.com. La Base URL se rellena automáticamente.' },
336+
dashscope: { title: '阿里通义 DashScope', description: 'Endpoint compatible con OpenAI en dashscope.aliyuncs.com/compatible-mode/v1. La Base URL se rellena automáticamente.' },
337+
cloudflare: { title: 'Cloudflare AI Gateway', description: 'Proxy de modelos compatibles con OpenAI a través de Cloudflare AI Gateway.' },
338+
siliconflow: { title: '硅基流动 SiliconFlow', description: 'Endpoint compatible con OpenAI en api.siliconflow.cn/v1. La Base URL se rellena automáticamente.' },
339+
openrouter: { title: 'OpenRouter', description: 'Enrutador multiproveedor en openrouter.ai/api/v1. La Base URL se rellena automáticamente.' },
340+
titles: { title: 'Títulos de conversación', description: 'Genera automáticamente un título de resumen breve para las conversaciones nuevas.' },
174341
defaults: { title: 'Valores predeterminados de generación',
175342
description: 'Se aplican cuando un agente o una solicitud de chat no especifica su propio valor.' },
176343
observability: { title: 'Observabilidad' },
@@ -188,6 +355,11 @@ export const esES: TranslationData = {
188355
openai: 'OpenAI',
189356
anthropic: 'Anthropic',
190357
google: 'Google Generative AI',
358+
deepseek: 'DeepSeek (compatible con OpenAI)',
359+
dashscope: '阿里通义 DashScope (compatible con OpenAI)',
360+
cloudflare: 'Cloudflare AI Gateway (compatible con OpenAI)',
361+
siliconflow: '硅基流动 SiliconFlow (compatible con OpenAI)',
362+
openrouter: 'OpenRouter (compatible con OpenAI)',
191363
},
192364
},
193365
gateway_model: { label: 'Modelo de Gateway',
@@ -206,6 +378,20 @@ export const esES: TranslationData = {
206378
google_api_key: { label: 'Clave de API de Google',
207379
help: 'Se reenvía como GOOGLE_GENERATIVE_AI_API_KEY. Se almacena cifrada en reposo.' },
208380
google_model: { label: 'Modelo' },
381+
deepseek_api_key: { label: 'Clave de API de DeepSeek', help: 'sk-…, emitida en platform.deepseek.com.' },
382+
deepseek_model: { label: 'Modelo', help: 'Ejemplos: deepseek-chat (V3), deepseek-reasoner (razonamiento R1).' },
383+
dashscope_api_key: { label: 'Clave de API de DashScope', help: 'sk-…, emitida en dashscope.console.aliyun.com.' },
384+
dashscope_model: { label: 'Modelo' },
385+
cloudflare_account_id: { label: 'ID de cuenta de Cloudflare', help: 'El ID hexadecimal de 32 caracteres de la URL de tu panel de Cloudflare.' },
386+
cloudflare_gateway_id: { label: 'ID de gateway', help: 'Nombre del gateway configurado en Cloudflare → AI Gateway. Por defecto `default`.' },
387+
cloudflare_api_key: { label: 'Token de Cloudflare AI Gateway', help: 'Emitido en AI Gateway → pestaña «API tokens» (cfut_… o sk_…).' },
388+
cloudflare_model: { label: 'Modelo' },
389+
siliconflow_api_key: { label: 'Clave de API de SiliconFlow' },
390+
siliconflow_model: { label: 'Modelo', help: 'Ejemplos: Qwen/Qwen2.5-72B-Instruct, deepseek-ai/DeepSeek-V3, meta-llama/Meta-Llama-3.1-8B-Instruct.' },
391+
openrouter_api_key: { label: 'Clave de API de OpenRouter', help: 'sk-or-…' },
392+
openrouter_model: { label: 'Modelo', help: 'Formato: provider/model (p. ej. anthropic/claude-3.5-sonnet, deepseek/deepseek-chat).' },
393+
title_generation_enabled: { label: 'Resumir automáticamente los títulos de conversación' },
394+
title_max_length: { label: 'Longitud máx. del título (caracteres)', help: 'Tope estricto del título generado. Todo lo que exceda se trunca en el servidor.' },
209395
temperature: { label: 'Temperatura',
210396
help: '0 = determinista, 2 = muy creativo.' },
211397
max_tokens: { label: 'Máximo de tokens de salida',

0 commit comments

Comments
 (0)