Initial commit — Stupid Simple Network Inventory
Application web d'inventaire réseau manuel avec FastAPI, Vue 3 et Docker. Inclut l'authentification JWT, la découverte ICMP, et la topologie en cards CSS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,477 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const locale = ref(localStorage.getItem('locale') || 'fr')
|
||||
|
||||
export function setLocale(lang) {
|
||||
locale.value = lang
|
||||
localStorage.setItem('locale', lang)
|
||||
}
|
||||
|
||||
const LANGS = {
|
||||
fr: {
|
||||
// Sidebar / App
|
||||
tabTopology: 'Topologie',
|
||||
tabNetworks: 'Réseaux',
|
||||
tabDevices: 'Équipements',
|
||||
discovery: '🔍 Découverte auto',
|
||||
statsNetworks: 'Réseaux',
|
||||
statsDevices: 'Équip.',
|
||||
exportJson: '⬇ Export JSON',
|
||||
importJson: '⬆ Import JSON',
|
||||
loadError: 'Erreur de chargement : ',
|
||||
importFailed: 'Import échoué : ',
|
||||
importTooLarge: 'Fichier trop volumineux (max 5 Mo).',
|
||||
// TopologyGraph
|
||||
ping: 'Ping',
|
||||
pinging: 'Ping en cours…',
|
||||
refreshPing: 'Rafraîchir le ping',
|
||||
wan: 'Internet / WAN',
|
||||
gateway: 'Passerelle inter-VLAN',
|
||||
unassigned: 'Non assigné',
|
||||
noDevice: 'Aucun équipement',
|
||||
noDevices: 'Aucun équipement à afficher.',
|
||||
noDevicesHint: 'Commencez par créer des réseaux et des équipements.',
|
||||
reachable: 'Joignable',
|
||||
unreachable: 'Injoignable',
|
||||
openWebUI: "Ouvrir l'interface web",
|
||||
// DeviceManager
|
||||
devices: 'Équipements',
|
||||
addDevice: '+ Ajouter un équipement',
|
||||
noDevicesConfigured: 'Aucun équipement configuré. Commencez par en ajouter un.',
|
||||
searchPlaceholder: 'Nom, IP…',
|
||||
filterType: 'Type',
|
||||
filterNetwork: 'Réseau',
|
||||
filterBrand: 'Marque',
|
||||
filterVirt: 'Virt',
|
||||
clearFilters: '✕ Effacer',
|
||||
noDevicesFiltered: 'Aucun équipement ne correspond aux filtres sélectionnés.',
|
||||
editDevice: "Modifier l'équipement",
|
||||
newDevice: 'Nouvel équipement',
|
||||
fieldName: 'Nom *',
|
||||
fieldType: 'Type *',
|
||||
fieldDescription: 'Description',
|
||||
isGateway: 'Passerelle inter-VLAN',
|
||||
isLivebox: 'Livebox / Box FAI',
|
||||
accessUrl: "URL d'accès",
|
||||
runtimeType: "Type d'environnement d'exécution",
|
||||
notSpecified: '— Non précisé',
|
||||
baremetal: 'Bare-metal',
|
||||
lxcContainer: 'Conteneur LXC',
|
||||
vmQemu: 'VM QEMU/KVM',
|
||||
networkInterfaces: 'Interfaces réseau',
|
||||
addInterface: '+ Interface',
|
||||
noInterface: 'Aucune interface configurée',
|
||||
cancel: 'Annuler',
|
||||
save: 'Enregistrer',
|
||||
create: 'Créer',
|
||||
badgeGateway: 'Passerelle',
|
||||
badgeLivebox: 'Livebox',
|
||||
confirmDeleteDevice: '{0} et tous ses liens ?',
|
||||
confirmDeleteNetwork: '{0} ?',
|
||||
saveError: 'Erreur lors de la sauvegarde',
|
||||
deleteError: 'Erreur lors de la suppression',
|
||||
descPlaceholder: 'Rôle, OS, notes…',
|
||||
// Device types
|
||||
typeServer: 'Serveur',
|
||||
typeSwitch: 'Switch',
|
||||
typeRouter: 'Routeur',
|
||||
typeNas: 'NAS',
|
||||
typeGateway: 'Passerelle',
|
||||
typeLivebox: 'Livebox',
|
||||
typeCamera: 'Caméra IP',
|
||||
typeTemperature: 'Sonde température/humidité',
|
||||
typeSensor: 'Capteur (mouvement, ouverture…)',
|
||||
typeHub: 'Hub domotique',
|
||||
typeSmartPlug: 'Prise connectée',
|
||||
typeAlarm: 'Alarme / Détecteur',
|
||||
typeLight: 'Éclairage connecté',
|
||||
typeDoorbell: 'Sonnette / Interphone',
|
||||
typeAccessPoint: 'Borne WiFi / Access Point',
|
||||
typeDesktop: 'Ordinateur fixe',
|
||||
typeLaptop: 'Ordinateur portable',
|
||||
typeOther: 'Autre',
|
||||
virtBaremetal: 'Bare-metal',
|
||||
virtLxc: 'LXC',
|
||||
virtQemu: 'VM QEMU',
|
||||
// VlanManager
|
||||
networks: 'Réseaux',
|
||||
addNetwork: '+ Ajouter un réseau',
|
||||
noNetworksConfigured: 'Aucun réseau configuré. Commencez par en créer un.',
|
||||
colType: 'Type',
|
||||
colName: 'Nom',
|
||||
colSubnet: 'Sous-réseau',
|
||||
colColor: 'Couleur',
|
||||
colActions: 'Actions',
|
||||
editNetwork: 'Modifier le réseau',
|
||||
newNetwork: 'Nouveau réseau',
|
||||
vlanId: 'ID VLAN',
|
||||
vlanIdHint: '(laisser vide pour LAN classique)',
|
||||
subnet: 'Sous-réseau CIDR',
|
||||
color: 'Couleur',
|
||||
subnetPlaceholder: 'ex: 192.168.10.0/24',
|
||||
// DiscoveryModal
|
||||
autoDiscovery: 'Découverte automatique',
|
||||
dnsServer: 'Serveur DNS',
|
||||
dnsHint: 'Le reverse DNS sera interrogé sur ce serveur pour résoudre les noms.',
|
||||
vlansToScan: 'VLANs à scanner',
|
||||
vlansHint: 'Seuls les VLANs avec un sous-réseau CIDR configuré peuvent être scannés.',
|
||||
noCidrWarning: "Aucun VLAN n'a de CIDR configuré. Renseignez-les dans l'onglet VLANs.",
|
||||
noCidr: 'pas de CIDR',
|
||||
startDiscovery: 'Lancer la découverte',
|
||||
scanning: 'Scan en cours…',
|
||||
scanAddresses: 'adresses sur',
|
||||
scanVlans: 'VLAN(s)',
|
||||
scanNote: 'Chaque hôte est pingé puis interrogé en DNS.',
|
||||
hostsFound: 'hôte(s) découvert(s)',
|
||||
addressesScanned: 'adresses scannées',
|
||||
newHosts: 'nouveaux',
|
||||
noHosts: 'Aucun hôte actif trouvé sur les plages sélectionnées.',
|
||||
colIp: 'IP',
|
||||
colDns: 'Nom (DNS)',
|
||||
colStatus: 'Statut',
|
||||
statusExisting: 'Déjà présent',
|
||||
statusNew: 'Nouveau',
|
||||
newScan: 'Nouveau scan',
|
||||
importingBtn: 'Import…',
|
||||
importBtn: 'Importer',
|
||||
dnsRequired: 'Veuillez renseigner un serveur DNS.',
|
||||
selectVlan: 'Sélectionnez au moins un VLAN.',
|
||||
importError: "Erreur lors de l'import.",
|
||||
scanError: 'Erreur lors du scan.',
|
||||
// Theme / Lang
|
||||
lightTheme: 'Thème clair',
|
||||
darkTheme: 'Thème sombre',
|
||||
// Auth
|
||||
loginTitle: 'Connexion',
|
||||
loginUsername: 'Nom d\'utilisateur',
|
||||
loginPassword: 'Mot de passe',
|
||||
loginBtn: 'Se connecter',
|
||||
loginError: 'Identifiants incorrects.',
|
||||
logout: 'Déconnexion',
|
||||
accountSettings: 'Paramètres du compte',
|
||||
currentPassword: 'Mot de passe actuel',
|
||||
newUsername: 'Nouveau nom d\'utilisateur',
|
||||
newPassword: 'Nouveau mot de passe',
|
||||
confirmPassword: 'Confirmer le mot de passe',
|
||||
leaveBlankToKeep: 'Laisser vide pour ne pas modifier',
|
||||
passwordMismatch: 'Les mots de passe ne correspondent pas.',
|
||||
accountUpdated: 'Compte mis à jour.',
|
||||
wrongPassword: 'Mot de passe actuel incorrect.',
|
||||
mustChangePasswordWarning: 'Pour des raisons de sécurité, vous devez changer votre mot de passe avant de continuer.',
|
||||
newPasswordRequired: 'Un nouveau mot de passe est requis.',
|
||||
passwordTooShort: 'Le mot de passe doit contenir au moins 8 caractères.',
|
||||
passwordTooWeak: 'Le mot de passe doit contenir au moins une lettre et un chiffre.',
|
||||
usernameInvalid: "Le nom d'utilisateur ne peut contenir que des lettres, chiffres, . _ - (1 à 64 caractères).",
|
||||
tooManyAttempts: 'Trop de tentatives, réessayez plus tard.',
|
||||
},
|
||||
|
||||
en: {
|
||||
tabTopology: 'Topology',
|
||||
tabNetworks: 'Networks',
|
||||
tabDevices: 'Devices',
|
||||
discovery: '🔍 Auto discovery',
|
||||
statsNetworks: 'Networks',
|
||||
statsDevices: 'Devices',
|
||||
exportJson: '⬇ Export JSON',
|
||||
importJson: '⬆ Import JSON',
|
||||
loadError: 'Loading error: ',
|
||||
importFailed: 'Import failed: ',
|
||||
importTooLarge: 'File too large (max 5 MB).',
|
||||
ping: 'Ping',
|
||||
pinging: 'Pinging…',
|
||||
refreshPing: 'Refresh ping',
|
||||
wan: 'Internet / WAN',
|
||||
gateway: 'Inter-VLAN Gateway',
|
||||
unassigned: 'Unassigned',
|
||||
noDevice: 'No devices',
|
||||
noDevices: 'No devices to display.',
|
||||
noDevicesHint: 'Start by creating networks and devices.',
|
||||
reachable: 'Reachable',
|
||||
unreachable: 'Unreachable',
|
||||
openWebUI: 'Open web interface',
|
||||
devices: 'Devices',
|
||||
addDevice: '+ Add device',
|
||||
noDevicesConfigured: 'No devices configured. Start by adding one.',
|
||||
searchPlaceholder: 'Name, IP…',
|
||||
filterType: 'Type',
|
||||
filterNetwork: 'Network',
|
||||
filterBrand: 'Brand',
|
||||
filterVirt: 'Virt',
|
||||
clearFilters: '✕ Clear',
|
||||
noDevicesFiltered: 'No devices match the selected filters.',
|
||||
editDevice: 'Edit device',
|
||||
newDevice: 'New device',
|
||||
fieldName: 'Name *',
|
||||
fieldType: 'Type *',
|
||||
fieldDescription: 'Description',
|
||||
isGateway: 'Inter-VLAN gateway',
|
||||
isLivebox: 'ISP Box / Router',
|
||||
accessUrl: 'Access URL',
|
||||
runtimeType: 'Runtime environment',
|
||||
notSpecified: '— Not specified',
|
||||
baremetal: 'Bare-metal',
|
||||
lxcContainer: 'LXC container',
|
||||
vmQemu: 'VM QEMU/KVM',
|
||||
networkInterfaces: 'Network interfaces',
|
||||
addInterface: '+ Interface',
|
||||
noInterface: 'No interface configured',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
create: 'Create',
|
||||
badgeGateway: 'Gateway',
|
||||
badgeLivebox: 'ISP Box',
|
||||
confirmDeleteDevice: '{0} and all its links?',
|
||||
confirmDeleteNetwork: '{0}?',
|
||||
saveError: 'Error while saving',
|
||||
deleteError: 'Error while deleting',
|
||||
descPlaceholder: 'Role, OS, notes…',
|
||||
typeServer: 'Server',
|
||||
typeSwitch: 'Switch',
|
||||
typeRouter: 'Router',
|
||||
typeNas: 'NAS',
|
||||
typeGateway: 'Gateway',
|
||||
typeLivebox: 'ISP Box',
|
||||
typeCamera: 'IP Camera',
|
||||
typeTemperature: 'Temperature/humidity sensor',
|
||||
typeSensor: 'Sensor (motion, door…)',
|
||||
typeHub: 'Home automation hub',
|
||||
typeSmartPlug: 'Smart plug',
|
||||
typeAlarm: 'Alarm / Detector',
|
||||
typeLight: 'Smart light',
|
||||
typeDoorbell: 'Doorbell / Intercom',
|
||||
typeAccessPoint: 'WiFi Access Point',
|
||||
typeDesktop: 'Desktop computer',
|
||||
typeLaptop: 'Laptop',
|
||||
typeOther: 'Other',
|
||||
virtBaremetal: 'Bare-metal',
|
||||
virtLxc: 'LXC',
|
||||
virtQemu: 'VM QEMU',
|
||||
networks: 'Networks',
|
||||
addNetwork: '+ Add network',
|
||||
noNetworksConfigured: 'No networks configured. Start by creating one.',
|
||||
colType: 'Type',
|
||||
colName: 'Name',
|
||||
colSubnet: 'Subnet',
|
||||
colColor: 'Color',
|
||||
colActions: 'Actions',
|
||||
editNetwork: 'Edit network',
|
||||
newNetwork: 'New network',
|
||||
vlanId: 'VLAN ID',
|
||||
vlanIdHint: '(leave empty for plain LAN)',
|
||||
subnet: 'CIDR subnet',
|
||||
color: 'Color',
|
||||
subnetPlaceholder: 'e.g. 192.168.10.0/24',
|
||||
autoDiscovery: 'Auto discovery',
|
||||
dnsServer: 'DNS server',
|
||||
dnsHint: 'Reverse DNS will be queried on this server to resolve hostnames.',
|
||||
vlansToScan: 'VLANs to scan',
|
||||
vlansHint: 'Only VLANs with a configured CIDR subnet can be scanned.',
|
||||
noCidrWarning: 'No VLAN has a CIDR configured. Set them in the Networks tab.',
|
||||
noCidr: 'no CIDR',
|
||||
startDiscovery: 'Start discovery',
|
||||
scanning: 'Scanning…',
|
||||
scanAddresses: 'addresses on',
|
||||
scanVlans: 'VLAN(s)',
|
||||
scanNote: 'Each host is pinged then queried via DNS.',
|
||||
hostsFound: 'host(s) found',
|
||||
addressesScanned: 'addresses scanned',
|
||||
newHosts: 'new',
|
||||
noHosts: 'No active hosts found on the selected ranges.',
|
||||
colIp: 'IP',
|
||||
colDns: 'Name (DNS)',
|
||||
colStatus: 'Status',
|
||||
statusExisting: 'Already present',
|
||||
statusNew: 'New',
|
||||
newScan: 'New scan',
|
||||
importingBtn: 'Importing…',
|
||||
importBtn: 'Import',
|
||||
dnsRequired: 'Please enter a DNS server.',
|
||||
selectVlan: 'Select at least one VLAN.',
|
||||
importError: 'Error during import.',
|
||||
scanError: 'Error during scan.',
|
||||
lightTheme: 'Light theme',
|
||||
darkTheme: 'Dark theme',
|
||||
// Auth
|
||||
loginTitle: 'Sign in',
|
||||
loginUsername: 'Username',
|
||||
loginPassword: 'Password',
|
||||
loginBtn: 'Sign in',
|
||||
loginError: 'Incorrect credentials.',
|
||||
logout: 'Logout',
|
||||
accountSettings: 'Account settings',
|
||||
currentPassword: 'Current password',
|
||||
newUsername: 'New username',
|
||||
newPassword: 'New password',
|
||||
confirmPassword: 'Confirm password',
|
||||
leaveBlankToKeep: 'Leave blank to keep unchanged',
|
||||
passwordMismatch: 'Passwords do not match.',
|
||||
accountUpdated: 'Account updated.',
|
||||
wrongPassword: 'Current password is incorrect.',
|
||||
mustChangePasswordWarning: 'For security reasons, you must change your password before continuing.',
|
||||
newPasswordRequired: 'A new password is required.',
|
||||
passwordTooShort: 'Password must be at least 8 characters.',
|
||||
passwordTooWeak: 'Password must contain at least one letter and one digit.',
|
||||
usernameInvalid: 'Username may only contain letters, digits, . _ - (1 to 64 characters).',
|
||||
tooManyAttempts: 'Too many attempts, please try again later.',
|
||||
},
|
||||
|
||||
es: {
|
||||
tabTopology: 'Topología',
|
||||
tabNetworks: 'Redes',
|
||||
tabDevices: 'Equipos',
|
||||
discovery: '🔍 Descubrimiento auto',
|
||||
statsNetworks: 'Redes',
|
||||
statsDevices: 'Equipos',
|
||||
exportJson: '⬇ Exportar JSON',
|
||||
importJson: '⬆ Importar JSON',
|
||||
loadError: 'Error de carga: ',
|
||||
importFailed: 'Importación fallida: ',
|
||||
importTooLarge: 'Archivo demasiado grande (máx 5 MB).',
|
||||
ping: 'Ping',
|
||||
pinging: 'Ping en curso…',
|
||||
refreshPing: 'Actualizar ping',
|
||||
wan: 'Internet / WAN',
|
||||
gateway: 'Pasarela inter-VLAN',
|
||||
unassigned: 'Sin asignar',
|
||||
noDevice: 'Sin equipos',
|
||||
noDevices: 'No hay equipos que mostrar.',
|
||||
noDevicesHint: 'Empiece creando redes y equipos.',
|
||||
reachable: 'Alcanzable',
|
||||
unreachable: 'No alcanzable',
|
||||
openWebUI: 'Abrir interfaz web',
|
||||
devices: 'Equipos',
|
||||
addDevice: '+ Añadir equipo',
|
||||
noDevicesConfigured: 'No hay equipos configurados. Empiece añadiendo uno.',
|
||||
searchPlaceholder: 'Nombre, IP…',
|
||||
filterType: 'Tipo',
|
||||
filterNetwork: 'Red',
|
||||
filterBrand: 'Marca',
|
||||
filterVirt: 'Virt',
|
||||
clearFilters: '✕ Borrar',
|
||||
noDevicesFiltered: 'Ningún equipo coincide con los filtros seleccionados.',
|
||||
editDevice: 'Editar equipo',
|
||||
newDevice: 'Nuevo equipo',
|
||||
fieldName: 'Nombre *',
|
||||
fieldType: 'Tipo *',
|
||||
fieldDescription: 'Descripción',
|
||||
isGateway: 'Pasarela inter-VLAN',
|
||||
isLivebox: 'Router / Box ISP',
|
||||
accessUrl: 'URL de acceso',
|
||||
runtimeType: 'Entorno de ejecución',
|
||||
notSpecified: '— No especificado',
|
||||
baremetal: 'Bare-metal',
|
||||
lxcContainer: 'Contenedor LXC',
|
||||
vmQemu: 'VM QEMU/KVM',
|
||||
networkInterfaces: 'Interfaces de red',
|
||||
addInterface: '+ Interfaz',
|
||||
noInterface: 'Sin interfaces configuradas',
|
||||
cancel: 'Cancelar',
|
||||
save: 'Guardar',
|
||||
create: 'Crear',
|
||||
badgeGateway: 'Pasarela',
|
||||
badgeLivebox: 'Router ISP',
|
||||
confirmDeleteDevice: '{0} y todos sus enlaces?',
|
||||
confirmDeleteNetwork: '{0}?',
|
||||
saveError: 'Error al guardar',
|
||||
deleteError: 'Error al eliminar',
|
||||
descPlaceholder: 'Rol, SO, notas…',
|
||||
typeServer: 'Servidor',
|
||||
typeSwitch: 'Switch',
|
||||
typeRouter: 'Router',
|
||||
typeNas: 'NAS',
|
||||
typeGateway: 'Pasarela',
|
||||
typeLivebox: 'Router ISP',
|
||||
typeCamera: 'Cámara IP',
|
||||
typeTemperature: 'Sonda de temperatura/humedad',
|
||||
typeSensor: 'Sensor (movimiento, apertura…)',
|
||||
typeHub: 'Hub domótico',
|
||||
typeSmartPlug: 'Enchufe inteligente',
|
||||
typeAlarm: 'Alarma / Detector',
|
||||
typeLight: 'Iluminación inteligente',
|
||||
typeDoorbell: 'Timbre / Portero',
|
||||
typeAccessPoint: 'Punto de acceso WiFi',
|
||||
typeDesktop: 'Ordenador de sobremesa',
|
||||
typeLaptop: 'Portátil',
|
||||
typeOther: 'Otro',
|
||||
virtBaremetal: 'Bare-metal',
|
||||
virtLxc: 'LXC',
|
||||
virtQemu: 'VM QEMU',
|
||||
networks: 'Redes',
|
||||
addNetwork: '+ Añadir red',
|
||||
noNetworksConfigured: 'No hay redes configuradas. Empiece creando una.',
|
||||
colType: 'Tipo',
|
||||
colName: 'Nombre',
|
||||
colSubnet: 'Subred',
|
||||
colColor: 'Color',
|
||||
colActions: 'Acciones',
|
||||
editNetwork: 'Editar red',
|
||||
newNetwork: 'Nueva red',
|
||||
vlanId: 'ID de VLAN',
|
||||
vlanIdHint: '(dejar vacío para LAN simple)',
|
||||
subnet: 'Subred CIDR',
|
||||
color: 'Color',
|
||||
subnetPlaceholder: 'ej: 192.168.10.0/24',
|
||||
autoDiscovery: 'Descubrimiento automático',
|
||||
dnsServer: 'Servidor DNS',
|
||||
dnsHint: 'El DNS inverso será consultado en este servidor para resolver nombres.',
|
||||
vlansToScan: 'VLANs a escanear',
|
||||
vlansHint: 'Solo los VLANs con subred CIDR configurada pueden escanearse.',
|
||||
noCidrWarning: 'Ninguna VLAN tiene CIDR configurado. Configúrelo en la pestaña Redes.',
|
||||
noCidr: 'sin CIDR',
|
||||
startDiscovery: 'Iniciar descubrimiento',
|
||||
scanning: 'Escaneo en curso…',
|
||||
scanAddresses: 'direcciones en',
|
||||
scanVlans: 'VLAN(s)',
|
||||
scanNote: 'Cada host es pingado y luego consultado en DNS.',
|
||||
hostsFound: 'host(s) descubierto(s)',
|
||||
addressesScanned: 'direcciones escaneadas',
|
||||
newHosts: 'nuevos',
|
||||
noHosts: 'No se encontraron hosts activos en los rangos seleccionados.',
|
||||
colIp: 'IP',
|
||||
colDns: 'Nombre (DNS)',
|
||||
colStatus: 'Estado',
|
||||
statusExisting: 'Ya presente',
|
||||
statusNew: 'Nuevo',
|
||||
newScan: 'Nuevo escaneo',
|
||||
importingBtn: 'Importando…',
|
||||
importBtn: 'Importar',
|
||||
dnsRequired: 'Por favor ingrese un servidor DNS.',
|
||||
selectVlan: 'Seleccione al menos una VLAN.',
|
||||
importError: 'Error durante la importación.',
|
||||
scanError: 'Error durante el escaneo.',
|
||||
lightTheme: 'Tema claro',
|
||||
darkTheme: 'Tema oscuro',
|
||||
// Auth
|
||||
loginTitle: 'Iniciar sesión',
|
||||
loginUsername: 'Nombre de usuario',
|
||||
loginPassword: 'Contraseña',
|
||||
loginBtn: 'Iniciar sesión',
|
||||
loginError: 'Credenciales incorrectas.',
|
||||
logout: 'Cerrar sesión',
|
||||
accountSettings: 'Configuración de cuenta',
|
||||
currentPassword: 'Contraseña actual',
|
||||
newUsername: 'Nuevo nombre de usuario',
|
||||
newPassword: 'Nueva contraseña',
|
||||
confirmPassword: 'Confirmar contraseña',
|
||||
leaveBlankToKeep: 'Dejar en blanco para no cambiar',
|
||||
passwordMismatch: 'Las contraseñas no coinciden.',
|
||||
accountUpdated: 'Cuenta actualizada.',
|
||||
wrongPassword: 'La contraseña actual es incorrecta.',
|
||||
mustChangePasswordWarning: 'Por razones de seguridad, debe cambiar su contraseña antes de continuar.',
|
||||
newPasswordRequired: 'Se requiere una nueva contraseña.',
|
||||
passwordTooShort: 'La contraseña debe tener al menos 8 caracteres.',
|
||||
passwordTooWeak: 'La contraseña debe contener al menos una letra y un número.',
|
||||
usernameInvalid: 'El nombre de usuario solo puede contener letras, dígitos, . _ - (1 a 64 caracteres).',
|
||||
tooManyAttempts: 'Demasiados intentos, inténtelo de nuevo más tarde.',
|
||||
},
|
||||
}
|
||||
|
||||
export function t(key) {
|
||||
return LANGS[locale.value]?.[key] ?? LANGS['fr'][key] ?? key
|
||||
}
|
||||
|
||||
export function tFmt(key, ...args) {
|
||||
let str = LANGS[locale.value]?.[key] ?? LANGS['fr'][key] ?? key
|
||||
args.forEach((arg, i) => { str = str.replace(`{${i}}`, arg) })
|
||||
return str
|
||||
}
|
||||
Reference in New Issue
Block a user