Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions frontend/src/__tests__/translations.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe, expect, it } from 'vitest';
import { translations } from '@/translations';

// The panel falls back to English for a whole locale, not per key: a key
// missing from a locale that exists is shown to its users as the raw key.
type Key = keyof typeof translations.en;

function placeholders(text: string): string[] {
return (text.match(/\{\w+\}|(?<!\w):[a-z]\w*/g) ?? []).sort();
}

describe('translations', () => {
const englishKeys = Object.keys(translations.en).sort();

for (const [locale, strings] of Object.entries(translations)) {
it(`${locale} defines exactly the English keys`, () => {
expect(Object.keys(strings).sort()).toEqual(englishKeys);
});

it(`${locale} keeps the English placeholders`, () => {
for (const [key, text] of Object.entries(strings)) {
expect(placeholders(text), key).toEqual(placeholders(translations.en[key as Key]));
}
});
}
});
161 changes: 161 additions & 0 deletions frontend/src/translations/de.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
export const de = {
// General
ftp_users: 'FTP-Benutzer',
ftp_users_admin: 'FTP',
create_user: 'Benutzer erstellen',
edit_user: 'Benutzer bearbeiten',
delete_user: 'Benutzer löschen',
delete_user_confirm: 'Möchten Sie den Benutzer „{username}“ wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.',

// Node status
node_status: 'Node-Status',
status_installed: 'gameap-files ist installiert und läuft',
status_installing: 'Installation läuft...',
status_failed: 'Installation fehlgeschlagen',
status_not_installed: 'gameap-files ist nicht installiert',
install: 'Installieren',
retry_setup: 'Installation wiederholen',
update_installation: 'Aktualisieren',
update_installation_hint: 'Neueste Version von gameap-files neu installieren; Einstellungen und Benutzer bleiben erhalten',
installing: 'Wird installiert',
node_not_ready: 'gameap-files ist auf diesem Node nicht installiert. Bitte wenden Sie sich an den Administrator.',

// Setup configuration
configure_installation: 'Installation konfigurieren',
settings: 'Einstellungen',
ftp_settings: 'FTP-Einstellungen',
sftp_settings: 'SFTP-Einstellungen',
ftp_address: 'Bind-Adresse',
ftp_address_hint: 'Für alle Schnittstellen leer lassen',
ftp_port: 'FTP-Port',
passive_port_min: 'Passiver Port (min.)',
passive_port_max: 'Passiver Port (max.)',
public_host: 'Öffentlicher Host',
public_host_hint: 'Externe IP für den Passivmodus',
enable_tls: 'TLS aktivieren',
tls_implicit_port: 'Port für implizites TLS',
sftp_port: 'SFTP-Port',

// User fields
username: 'Benutzername',
username_placeholder: 'z. B. server1_user',
password: 'Passwort',
password_placeholder: 'Leer lassen für automatische Generierung',
password_hint: 'Wenn leer, wird automatisch ein sicheres Passwort generiert',
new_password: 'Neues Passwort',
new_password_placeholder: 'Leer lassen, um das aktuelle beizubehalten',
new_password_hint: 'Leer lassen, um das aktuelle Passwort beizubehalten',
home_dir: 'Home-Verzeichnis',
home_dir_placeholder: 'z. B. /srv/gameap/server1',
home_dir_hint: 'Wenn leer, wird das Serververzeichnis verwendet',
quota: 'Kontingent',
quota_placeholder: '10GB, 500MB oder 0 für unbegrenzt',
quota_hint: 'Größe mit Einheit angeben (B, KB, MB, GB, TB). Leer oder 0 = unbegrenzt',
enabled: 'Aktiviert',
disabled: 'Deaktiviert',
description: 'Beschreibung',
description_placeholder: 'Optionale Beschreibung des Benutzers',
status: 'Status',
actions: 'Aktionen',
unlimited: 'Unbegrenzt',

// User created
user_created: 'Benutzer erfolgreich erstellt',
password_warning: 'Dieses Passwort wird nur einmal angezeigt. Bitte speichern Sie es an einem sicheren Ort.',

// Access rules
access_rules: 'Zugriffsregeln',
add_rule: 'Regel hinzufügen',
path: 'Pfad',
path_placeholder: '/**',
permissions: 'Rechte',
read: 'Lesen',
write: 'Schreiben',
delete: 'Löschen',
list: 'Auflisten',
no_access_rules: 'Keine Zugriffsregeln konfiguriert',

// Virtual paths
virtual_paths: 'Virtuelle Pfade',
add_path: 'Pfad hinzufügen',
virtual: 'Virtueller Pfad',
virtual_placeholder: '/shared',
target: 'Zielpfad',
target_placeholder: '/srv/gameap/shared',
read_only: 'Nur Lesen',
no_virtual_paths: 'Keine virtuellen Pfade konfiguriert',

// SSH keys
ssh_keys: 'SSH-Schlüssel',
add_key: 'Schlüssel hinzufügen',
no_ssh_keys: 'Keine SSH-Schlüssel konfiguriert',
ssh_public_key: 'Öffentlicher SSH-Schlüssel',
ssh_key_placeholder: 'ssh-ed25519 AAAA... user@host',
ssh_key_hint: 'Fügen Sie Ihren öffentlichen SSH-Schlüssel ein (beginnt mit ssh-rsa, ssh-ed25519, ecdsa-sha2 usw.)',

// Actions
save: 'Speichern',
cancel: 'Abbrechen',
close: 'Schließen',
retry: 'Erneut versuchen',
refresh: 'Neu laden',
all: 'Alle',
installed_short: 'Installiert',
not_installed_short: 'Nicht installiert',
failed_short: 'Fehlgeschlagen',
search_nodes: 'Nach Name oder IP suchen...',
copy: 'Kopieren',
copied: 'Kopiert!',
copy_failed: 'Kopieren fehlgeschlagen',
create: 'Erstellen',
add: 'Hinzufügen',
edit: 'Bearbeiten',
remove: 'Entfernen',

// Admin page
admin_page_info: 'Zentrale Verwaltung der FTP-Benutzer',
admin_page_hint: 'Öffnen Sie einen bestimmten Server, um dessen FTP-Benutzer zu verwalten.',
nodes: 'Nodes',
users: 'Benutzer',
node_users: 'Node-Benutzer',
select_server: 'Server auswählen',
select_server_placeholder: 'Server auswählen...',
picker_search_servers: 'Server nach Name suchen...',
picker_no_servers: 'Auf diesem Node wurden keine Server gefunden',
picker_more: ':shown von :total angezeigt — Suche verfeinern',
picker_load_failed: 'Server konnten nicht geladen werden',
back_to_nodes: 'Zurück zu den Nodes',
create_ftp_user: 'FTP-Benutzer erstellen',
view_users: 'Benutzer anzeigen',
servers: 'Server',
users_label: 'Benutzer',
all_nodes: 'Alle Nodes',
all_servers: 'Alle Server',
all_statuses: 'Alle Status',
search_users: 'Benutzer suchen...',
expand_all: 'Alle ausklappen',
collapse_all: 'Alle einklappen',
no_nodes: 'Keine Nodes gefunden',
no_users_found: 'Keine Benutzer entsprechen den Filtern',
servers_count: '{count} Server',
users_count: '{count} Benutzer',
// Followed by " <username>?" in the delete dialog
confirm_delete_user: 'Möchten Sie den folgenden Benutzer wirklich löschen:',

// Empty states
no_users: 'Für diesen Server sind keine FTP-Benutzer konfiguriert',

// Errors
error_username_required: 'Benutzername ist erforderlich',
error_username_format: 'Der Benutzername muss mit einem Buchstaben beginnen und darf nur lateinische Buchstaben, Ziffern und Unterstriche enthalten (3-32 Zeichen)',
error_quota_format: 'Ungültiges Format. Verwenden Sie: 10GB, 500MB, 1TB usw.',
error_loading: 'Daten konnten nicht geladen werden',
error_saving: 'Änderungen konnten nicht gespeichert werden',
error_creating: 'Benutzer konnte nicht erstellt werden',
error_deleting: 'Benutzer konnte nicht gelöscht werden',

// Server ability titles (resolved by the panel's privilege UI
// as plugins.files.abilities.<name>)
'abilities.ftp-users-manage': 'FTP/SFTP-Benutzer verwalten',
'abilities.ftp-users-view': 'FTP/SFTP-Benutzer anzeigen',
};
161 changes: 161 additions & 0 deletions frontend/src/translations/es.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
export const es = {
// General
ftp_users: 'Usuarios FTP',
ftp_users_admin: 'FTP',
create_user: 'Crear usuario',
edit_user: 'Editar usuario',
delete_user: 'Eliminar usuario',
delete_user_confirm: '¿Estás seguro de que quieres eliminar el usuario "{username}"? Esta acción no se puede deshacer.',

// Node status
node_status: 'Estado del nodo',
status_installed: 'gameap-files está instalado y en ejecución',
status_installing: 'Instalación en curso...',
status_failed: 'Error de instalación',
status_not_installed: 'gameap-files no está instalado',
install: 'Instalar',
retry_setup: 'Reintentar instalación',
update_installation: 'Actualizar',
update_installation_hint: 'Reinstalar la última versión de gameap-files; se conservan la configuración y los usuarios',
installing: 'Instalando',
node_not_ready: 'gameap-files no está instalado en este nodo. Contacta con el administrador.',

// Setup configuration
configure_installation: 'Configurar instalación',
settings: 'Configuración',
ftp_settings: 'Configuración de FTP',
sftp_settings: 'Configuración de SFTP',
ftp_address: 'Dirección de escucha',
ftp_address_hint: 'Déjalo vacío para usar todas las interfaces',
ftp_port: 'Puerto FTP',
passive_port_min: 'Puerto pasivo (mín.)',
passive_port_max: 'Puerto pasivo (máx.)',
public_host: 'Host público',
public_host_hint: 'IP externa para el modo pasivo',
enable_tls: 'Activar TLS',
tls_implicit_port: 'Puerto de TLS implícito',
sftp_port: 'Puerto SFTP',

// User fields
username: 'Nombre de usuario',
username_placeholder: 'p. ej. server1_user',
password: 'Contraseña',
password_placeholder: 'Déjalo vacío para generarla automáticamente',
password_hint: 'Si lo dejas vacío, se generará automáticamente una contraseña segura',
new_password: 'Nueva contraseña',
new_password_placeholder: 'Déjalo vacío para mantener la actual',
new_password_hint: 'Déjalo vacío para mantener la contraseña actual',
home_dir: 'Directorio personal',
home_dir_placeholder: 'p. ej. /srv/gameap/server1',
home_dir_hint: 'Si lo dejas vacío, se usará el directorio del servidor',
quota: 'Cuota',
quota_placeholder: '10GB, 500MB o 0 (sin límite)',
quota_hint: 'Introduce el tamaño con la unidad (B, KB, MB, GB, TB). Vacío o 0 = sin límite',
enabled: 'Activo',
disabled: 'Inactivo',
description: 'Descripción',
description_placeholder: 'Descripción opcional del usuario',
status: 'Estado',
actions: 'Acciones',
unlimited: 'Sin límite',

// User created
user_created: 'Usuario creado correctamente',
password_warning: 'Esta contraseña solo se mostrará una vez. Guárdala en un lugar seguro.',

// Access rules
access_rules: 'Reglas de acceso',
add_rule: 'Añadir regla',
path: 'Ruta',
path_placeholder: '/**',
permissions: 'Permisos',
read: 'Leer',
write: 'Escribir',
delete: 'Eliminar',
list: 'Listar',
no_access_rules: 'No hay reglas de acceso configuradas',

// Virtual paths
virtual_paths: 'Rutas virtuales',
add_path: 'Añadir ruta',
virtual: 'Ruta virtual',
virtual_placeholder: '/shared',
target: 'Ruta de destino',
target_placeholder: '/srv/gameap/shared',
read_only: 'Solo lectura',
no_virtual_paths: 'No hay rutas virtuales configuradas',

// SSH keys
ssh_keys: 'Claves SSH',
add_key: 'Añadir clave',
no_ssh_keys: 'No hay claves SSH configuradas',
ssh_public_key: 'Clave pública SSH',
ssh_key_placeholder: 'ssh-ed25519 AAAA... user@host',
ssh_key_hint: 'Pega tu clave pública SSH (empieza por ssh-rsa, ssh-ed25519, ecdsa-sha2, etc.)',

// Actions
save: 'Guardar',
cancel: 'Cancelar',
close: 'Cerrar',
retry: 'Reintentar',
refresh: 'Recargar',
all: 'Todos',
installed_short: 'Instalado',
not_installed_short: 'No instalado',
failed_short: 'Error',
search_nodes: 'Buscar por nombre o IP...',
copy: 'Copiar',
copied: '¡Copiado!',
copy_failed: 'No se pudo copiar',
create: 'Crear',
add: 'Añadir',
edit: 'Editar',
remove: 'Quitar',

// Admin page
admin_page_info: 'Gestión centralizada de usuarios FTP',
admin_page_hint: 'Abre un servidor concreto para gestionar sus usuarios FTP.',
nodes: 'Nodos',
users: 'Usuarios',
node_users: 'Usuarios del nodo',
select_server: 'Seleccionar servidor',
select_server_placeholder: 'Elige un servidor...',
picker_search_servers: 'Buscar servidores por nombre...',
picker_no_servers: 'No se encontraron servidores en este nodo',
picker_more: 'Mostrando :shown de :total — acota la búsqueda',
picker_load_failed: 'No se pudieron cargar los servidores',
back_to_nodes: 'Volver a los nodos',
create_ftp_user: 'Crear usuario FTP',
view_users: 'Ver usuarios',
servers: 'servidor(es)',
users_label: 'usuario(s)',
all_nodes: 'Todos los nodos',
all_servers: 'Servidor: todos',
all_statuses: 'Estado: todos',
search_users: 'Buscar usuarios...',
expand_all: 'Expandir todo',
collapse_all: 'Contraer todo',
no_nodes: 'No se encontraron nodos',
no_users_found: 'Ningún usuario coincide con los filtros',
servers_count: '{count} servidor(es)',
users_count: '{count} usuario(s)',
// Followed by " <username>?" in the delete dialog
confirm_delete_user: '¿Estás seguro de que quieres eliminar el usuario',

// Empty states
no_users: 'No hay usuarios FTP configurados para este servidor',

// Errors
error_username_required: 'El nombre de usuario es obligatorio',
error_username_format: 'El nombre de usuario debe empezar por una letra y solo puede contener letras sin tildes ni ñ, números y guiones bajos (3-32 caracteres)',
error_quota_format: 'Formato no válido. Usa: 10GB, 500MB, 1TB, etc.',
error_loading: 'No se pudieron cargar los datos',
error_saving: 'No se pudieron guardar los cambios',
error_creating: 'No se pudo crear el usuario',
error_deleting: 'No se pudo eliminar el usuario',

// Server ability titles (resolved by the panel's privilege UI
// as plugins.files.abilities.<name>)
'abilities.ftp-users-manage': 'Gestionar usuarios FTP/SFTP',
'abilities.ftp-users-view': 'Ver usuarios FTP/SFTP',
};
6 changes: 4 additions & 2 deletions frontend/src/translations/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { de } from './de';
import { en } from './en';
import { es } from './es';
import { ru } from './ru';

export const translations = { en, ru };
export { en, ru };
export const translations = { en, ru, de, es };
export { de, en, es, ru };
Loading