From c2a8ecdd5db0b33be79628bdb0ca10fdce776a8b Mon Sep 17 00:00:00 2001 From: Lantum-Brendan Date: Tue, 24 Feb 2026 23:33:51 +0100 Subject: [PATCH 01/13] chore: Align API configuration and harden authentication --- .env.example | 2 +- README.md | 4 +++- src/stores/auth.ts | 9 ++++++++- src/views/HomeView.vue | 2 +- src/views/LoginView.vue | 4 ++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 460e575..76a74b6 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -VITE_API_URL=http://localhost:8080 +VITE_API_URL=http://localhost:8090/api diff --git a/README.md b/README.md index 553d36a..272c6bd 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,11 @@ Create a `.env.local` file to configure the API connection: ```bash # FlatRun Agent API URL -VITE_API_URL=http://localhost:8090 +VITE_API_URL=http://localhost:8090/api ``` +`VITE_API_URL` must include the `/api` prefix used by the agent routes. + For production deployments: ```bash diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 0903156..1e9def7 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -63,9 +63,16 @@ export const useAuthStore = defineStore("auth", () => { const login = async (apiKey: string) => { loading.value = true; error.value = ""; + const normalizedApiKey = apiKey.trim(); + + if (!normalizedApiKey) { + error.value = "API key is required"; + loading.value = false; + return false; + } try { - const response = await apiClient.post("/auth/login", { api_key: apiKey }); + const response = await apiClient.post("/auth/login", { api_key: normalizedApiKey }); token.value = response.data.token; localStorage.setItem("auth_token", response.data.token); diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 9c92e1b..ff91650 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -395,7 +395,7 @@ const agentVersion = computed(() => statsStore.agentVersion); const lastUpdated = computed(() => statsStore.formatLastUpdated()); const apiEndpoint = computed(() => { - return window.location.hostname + ":8090"; + return import.meta.env.VITE_API_URL || `${window.location.protocol}//${window.location.hostname}:8090`; }); const systemHealth = computed(() => { diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue index 5535286..157ee87 100644 --- a/src/views/LoginView.vue +++ b/src/views/LoginView.vue @@ -107,7 +107,7 @@ - @@ -132,7 +132,7 @@ import Logo from "@/components/base/Logo.vue"; const router = useRouter(); const auth = useAuthStore(); -const loginMode = ref<"credentials" | "apikey">("credentials"); +const loginMode = ref<"credentials" | "apikey">("apikey"); const username = ref(""); const password = ref(""); const apiKey = ref(""); From 5373f80f6d9f6fb4eb692cc3586bd557bfd8eba9 Mon Sep 17 00:00:00 2001 From: Lantum-Brendan Date: Tue, 24 Feb 2026 23:45:24 +0100 Subject: [PATCH 02/13] feat(i18n): Implement dashboard internationalization with language selector --- package-lock.json | 62 +++++++++++ package.json | 1 + public/flags/de.svg | 1 + public/flags/es.svg | 1 + public/flags/fr.svg | 1 + public/flags/gb.svg | 1 + public/flags/it.svg | 5 + public/flags/pt.svg | 5 + src/components/LanguageSelector.vue | 148 ++++++++++++++++++++++++ src/composables/useLanguage.ts | 39 +++++++ src/config/languages.ts | 22 ++++ src/i18n/index.ts | 27 +++++ src/i18n/locales/de.json | 78 +++++++++++++ src/i18n/locales/en.json | 78 +++++++++++++ src/i18n/locales/es.json | 78 +++++++++++++ src/i18n/locales/fr.json | 78 +++++++++++++ src/i18n/locales/it.json | 78 +++++++++++++ src/i18n/locales/pt.json | 78 +++++++++++++ src/layouts/DashboardLayout.vue | 167 +++++++++++++++------------- src/main.ts | 2 + src/vite-env.d.ts | 5 + 21 files changed, 878 insertions(+), 77 deletions(-) create mode 100644 public/flags/de.svg create mode 100644 public/flags/es.svg create mode 100644 public/flags/fr.svg create mode 100644 public/flags/gb.svg create mode 100644 public/flags/it.svg create mode 100644 public/flags/pt.svg create mode 100644 src/components/LanguageSelector.vue create mode 100644 src/composables/useLanguage.ts create mode 100644 src/config/languages.ts create mode 100644 src/i18n/index.ts create mode 100644 src/i18n/locales/de.json create mode 100644 src/i18n/locales/en.json create mode 100644 src/i18n/locales/es.json create mode 100644 src/i18n/locales/fr.json create mode 100644 src/i18n/locales/it.json create mode 100644 src/i18n/locales/pt.json diff --git a/package-lock.json b/package-lock.json index 6bc5b9e..e30652b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "primevue": "^3.50.0", "vue": "^3.4.21", "vue-codemirror": "^6.1.1", + "vue-i18n": "^9.14.5", "vue-router": "^4.3.0" }, "devDependencies": { @@ -804,6 +805,47 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@intlify/core-base": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz", + "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", + "dependencies": { + "@intlify/message-compiler": "9.14.5", + "@intlify/shared": "9.14.5" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", + "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", + "dependencies": { + "@intlify/shared": "9.14.5", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz", + "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -5182,6 +5224,26 @@ "eslint": ">=6.0.0" } }, + "node_modules/vue-i18n": { + "version": "9.14.5", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz", + "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", + "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html", + "dependencies": { + "@intlify/core-base": "9.14.5", + "@intlify/shared": "9.14.5", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", diff --git a/package.json b/package.json index 005ce5c..d32ef7f 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "primevue": "^3.50.0", "vue": "^3.4.21", "vue-codemirror": "^6.1.1", + "vue-i18n": "^9.14.5", "vue-router": "^4.3.0" }, "devDependencies": { diff --git a/public/flags/de.svg b/public/flags/de.svg new file mode 100644 index 0000000..295d13e --- /dev/null +++ b/public/flags/de.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/flags/es.svg b/public/flags/es.svg new file mode 100644 index 0000000..e143324 --- /dev/null +++ b/public/flags/es.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/flags/fr.svg b/public/flags/fr.svg new file mode 100644 index 0000000..ec311d7 --- /dev/null +++ b/public/flags/fr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/flags/gb.svg b/public/flags/gb.svg new file mode 100644 index 0000000..91f11d8 --- /dev/null +++ b/public/flags/gb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/flags/it.svg b/public/flags/it.svg new file mode 100644 index 0000000..be3a5df --- /dev/null +++ b/public/flags/it.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/flags/pt.svg b/public/flags/pt.svg new file mode 100644 index 0000000..99b5fe6 --- /dev/null +++ b/public/flags/pt.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/LanguageSelector.vue b/src/components/LanguageSelector.vue new file mode 100644 index 0000000..08913d3 --- /dev/null +++ b/src/components/LanguageSelector.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/composables/useLanguage.ts b/src/composables/useLanguage.ts new file mode 100644 index 0000000..dda0a9b --- /dev/null +++ b/src/composables/useLanguage.ts @@ -0,0 +1,39 @@ +import { computed, watch } from "vue"; +import { useI18n } from "vue-i18n"; +import { languages, getLanguageByCode, getDefaultLanguage, type LanguageConfig } from "@/config/languages"; + +const STORAGE_KEY = "locale"; + +export function useLanguage() { + const { locale } = useI18n(); + + const currentLanguage = computed(() => getLanguageByCode(locale.value) || getDefaultLanguage()); + + const setLanguage = (code: string) => { + const lang = getLanguageByCode(code); + if (lang) { + locale.value = code; + localStorage.setItem(STORAGE_KEY, code); + } + }; + + const initializeLanguage = () => { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored && getLanguageByCode(stored)) { + locale.value = stored; + } else { + locale.value = getDefaultLanguage().code; + } + }; + + watch(locale, (newLocale) => { + localStorage.setItem(STORAGE_KEY, newLocale); + }); + + return { + languages, + currentLanguage, + setLanguage, + initializeLanguage, + }; +} diff --git a/src/config/languages.ts b/src/config/languages.ts new file mode 100644 index 0000000..d34ce3f --- /dev/null +++ b/src/config/languages.ts @@ -0,0 +1,22 @@ +export interface LanguageConfig { + code: string; + label: string; + flagPath: string; +} + +export const languages: LanguageConfig[] = [ + { code: "en", label: "English", flagPath: "/flags/gb.svg" }, + { code: "fr", label: "Français", flagPath: "/flags/fr.svg" }, + { code: "de", label: "Deutsch", flagPath: "/flags/de.svg" }, + { code: "es", label: "Español", flagPath: "/flags/es.svg" }, + { code: "pt", label: "Português", flagPath: "/flags/pt.svg" }, + { code: "it", label: "Italiano", flagPath: "/flags/it.svg" }, +]; + +export type LanguageCode = (typeof languages)[number]["code"]; + +export const getLanguageByCode = (code: string): LanguageConfig | undefined => { + return languages.find((lang) => lang.code === code); +}; + +export const getDefaultLanguage = (): LanguageConfig => languages[0]; diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..337bdf1 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,27 @@ +import { createI18n } from "vue-i18n"; +import en from "./locales/en.json"; +import fr from "./locales/fr.json"; +import de from "./locales/de.json"; +import es from "./locales/es.json"; +import pt from "./locales/pt.json"; +import it from "./locales/it.json"; + +export const messages = { + en, + fr, + de, + es, + pt, + it, +}; + +export type MessageSchema = typeof en; + +export const i18n = createI18n({ + legacy: false, + locale: localStorage.getItem("locale") || "en", + fallbackLocale: "en", + messages, +}); + +export default i18n; diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json new file mode 100644 index 0000000..a075a66 --- /dev/null +++ b/src/i18n/locales/de.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Startseite" + }, + "environment": { + "local": "Lokale Umgebung" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "System", + "databases": "Datenbanken", + "dns": "DNS", + "security": "Sicherheit", + "apps": "Apps", + "administration": "Verwaltung" + }, + "nav": { + "dashboard": "Dashboard", + "deployments": "Bereitstellungen", + "containers": "Container", + "images": "Images", + "volumes": "Volumes", + "networks": "Netzwerke", + "portMappings": "Port-Zuweisungen", + "infrastructure": "Infrastruktur", + "ports": "Ports", + "services": "Dienste", + "cronJobs": "Cron-Jobs", + "servers": "Server", + "zones": "Zonen", + "externalProviders": "Externe Anbieter", + "securityMonitoring": "Sicherheit & Überwachung", + "certificates": "Zertifikate", + "installedApps": "Installierte Apps", + "marketplace": "Marktplatz", + "settings": "Einstellungen", + "users": "Benutzer", + "apiKeys": "API-Schlüssel" + }, + "header": { + "running": "Läuft", + "stopped": "Gestoppt" + }, + "footer": { + "cpu": "CPU", + "memory": "Speicher", + "connected": "Verbunden", + "disconnected": "Getrennt", + "signOut": "Abmelden" + }, + "titles": { + "home": "Dashboard", + "deployments": "Bereitstellungen", + "deploymentDetail": "Bereitstellungsdetails", + "containers": "Container", + "images": "Images", + "volumes": "Volumes", + "networks": "Netzwerke", + "dockerPorts": "Port-Zuweisungen", + "infrastructure": "Infrastruktur", + "systemPorts": "System-Ports", + "services": "Systemdienste", + "cronJobs": "Cron-Jobs", + "databases": "Datenbankserver", + "security": "Sicherheit & Überwachung", + "certificates": "SSL-Zertifikate", + "dnsZones": "DNS-Zonen", + "dnsExternal": "Externe DNS-Anbieter", + "apps": "Installierte Apps", + "marketplace": "Marktplatz", + "settings": "Einstellungen", + "users": "Benutzer", + "apiKeys": "API-Schlüssel" + } + } +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json new file mode 100644 index 0000000..b4f71c8 --- /dev/null +++ b/src/i18n/locales/en.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Home" + }, + "environment": { + "local": "Local Environment" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "System", + "databases": "Databases", + "dns": "DNS", + "security": "Security", + "apps": "Apps", + "administration": "Administration" + }, + "nav": { + "dashboard": "Dashboard", + "deployments": "Deployments", + "containers": "Containers", + "images": "Images", + "volumes": "Volumes", + "networks": "Networks", + "portMappings": "Port Mappings", + "infrastructure": "Infrastructure", + "ports": "Ports", + "services": "Services", + "cronJobs": "Cron Jobs", + "servers": "Servers", + "zones": "Zones", + "externalProviders": "External Providers", + "securityMonitoring": "Security & Monitoring", + "certificates": "Certificates", + "installedApps": "Installed Apps", + "marketplace": "Marketplace", + "settings": "Settings", + "users": "Users", + "apiKeys": "API Keys" + }, + "header": { + "running": "Running", + "stopped": "Stopped" + }, + "footer": { + "cpu": "CPU", + "memory": "Memory", + "connected": "Connected", + "disconnected": "Disconnected", + "signOut": "Sign Out" + }, + "titles": { + "home": "Dashboard", + "deployments": "Deployments", + "deploymentDetail": "Deployment Details", + "containers": "Containers", + "images": "Images", + "volumes": "Volumes", + "networks": "Networks", + "dockerPorts": "Port Mappings", + "infrastructure": "Infrastructure", + "systemPorts": "System Ports", + "services": "System Services", + "cronJobs": "Cron Jobs", + "databases": "Database Servers", + "security": "Security & Monitoring", + "certificates": "SSL Certificates", + "dnsZones": "DNS Zones", + "dnsExternal": "External DNS Providers", + "apps": "Installed Apps", + "marketplace": "App Marketplace", + "settings": "Settings", + "users": "Users", + "apiKeys": "API Keys" + } + } +} diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json new file mode 100644 index 0000000..d2fb9ef --- /dev/null +++ b/src/i18n/locales/es.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Inicio" + }, + "environment": { + "local": "Entorno local" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "Sistema", + "databases": "Bases de datos", + "dns": "DNS", + "security": "Seguridad", + "apps": "Apps", + "administration": "Administración" + }, + "nav": { + "dashboard": "Panel de control", + "deployments": "Despliegues", + "containers": "Contenedores", + "images": "Imágenes", + "volumes": "Volúmenes", + "networks": "Redes", + "portMappings": "Mapeo de puertos", + "infrastructure": "Infraestructura", + "ports": "Puertos", + "services": "Servicios", + "cronJobs": "Tareas programadas", + "servers": "Servidores", + "zones": "Zonas", + "externalProviders": "Proveedores externos", + "securityMonitoring": "Seguridad y monitoreo", + "certificates": "Certificados", + "installedApps": "Apps instaladas", + "marketplace": "Marketplace", + "settings": "Configuración", + "users": "Usuarios", + "apiKeys": "Claves API" + }, + "header": { + "running": "En ejecución", + "stopped": "Detenido" + }, + "footer": { + "cpu": "CPU", + "memory": "Memoria", + "connected": "Conectado", + "disconnected": "Desconectado", + "signOut": "Cerrar sesión" + }, + "titles": { + "home": "Panel de control", + "deployments": "Despliegues", + "deploymentDetail": "Detalles del despliegue", + "containers": "Contenedores", + "images": "Imágenes", + "volumes": "Volúmenes", + "networks": "Redes", + "dockerPorts": "Mapeo de puertos", + "infrastructure": "Infraestructura", + "systemPorts": "Puertos del sistema", + "services": "Servicios del sistema", + "cronJobs": "Tareas programadas", + "databases": "Servidores de bases de datos", + "security": "Seguridad y monitoreo", + "certificates": "Certificados SSL", + "dnsZones": "Zonas DNS", + "dnsExternal": "Proveedores DNS externos", + "apps": "Apps instaladas", + "marketplace": "Marketplace", + "settings": "Configuración", + "users": "Usuarios", + "apiKeys": "Claves API" + } + } +} diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json new file mode 100644 index 0000000..4d7b912 --- /dev/null +++ b/src/i18n/locales/fr.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Accueil" + }, + "environment": { + "local": "Environnement local" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "Système", + "databases": "Bases de données", + "dns": "DNS", + "security": "Sécurité", + "apps": "Apps", + "administration": "Administration" + }, + "nav": { + "dashboard": "Tableau de bord", + "deployments": "Déploiements", + "containers": "Conteneurs", + "images": "Images", + "volumes": "Volumes", + "networks": "Réseaux", + "portMappings": "Redirections de ports", + "infrastructure": "Infrastructure", + "ports": "Ports", + "services": "Services", + "cronJobs": "Tâches planifiées", + "servers": "Serveurs", + "zones": "Zones", + "externalProviders": "Fournisseurs externes", + "securityMonitoring": "Sécurité & surveillance", + "certificates": "Certificats", + "installedApps": "Applications installées", + "marketplace": "Marketplace", + "settings": "Paramètres", + "users": "Utilisateurs", + "apiKeys": "Clés API" + }, + "header": { + "running": "En cours", + "stopped": "Arrêté" + }, + "footer": { + "cpu": "CPU", + "memory": "Mémoire", + "connected": "Connecté", + "disconnected": "Déconnecté", + "signOut": "Se déconnecter" + }, + "titles": { + "home": "Tableau de bord", + "deployments": "Déploiements", + "deploymentDetail": "Détails du déploiement", + "containers": "Conteneurs", + "images": "Images", + "volumes": "Volumes", + "networks": "Réseaux", + "dockerPorts": "Redirections de ports", + "infrastructure": "Infrastructure", + "systemPorts": "Ports système", + "services": "Services système", + "cronJobs": "Tâches planifiées", + "databases": "Serveurs de base de données", + "security": "Sécurité & surveillance", + "certificates": "Certificats SSL", + "dnsZones": "Zones DNS", + "dnsExternal": "Fournisseurs DNS externes", + "apps": "Applications installées", + "marketplace": "Marketplace", + "settings": "Paramètres", + "users": "Utilisateurs", + "apiKeys": "Clés API" + } + } +} diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json new file mode 100644 index 0000000..d735a9e --- /dev/null +++ b/src/i18n/locales/it.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Home" + }, + "environment": { + "local": "Ambiente locale" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "Sistema", + "databases": "Database", + "dns": "DNS", + "security": "Sicurezza", + "apps": "App", + "administration": "Amministrazione" + }, + "nav": { + "dashboard": "Dashboard", + "deployments": "Deployment", + "containers": "Container", + "images": "Immagini", + "volumes": "Volumi", + "networks": "Reti", + "portMappings": "Mappatura porte", + "infrastructure": "Infrastruttura", + "ports": "Porte", + "services": "Servizi", + "cronJobs": "Cron job", + "servers": "Server", + "zones": "Zone", + "externalProviders": "Provider esterni", + "securityMonitoring": "Sicurezza e monitoraggio", + "certificates": "Certificati", + "installedApps": "App installate", + "marketplace": "Marketplace", + "settings": "Impostazioni", + "users": "Utenti", + "apiKeys": "Chiavi API" + }, + "header": { + "running": "In esecuzione", + "stopped": "Arrestato" + }, + "footer": { + "cpu": "CPU", + "memory": "Memoria", + "connected": "Connesso", + "disconnected": "Disconnesso", + "signOut": "Esci" + }, + "titles": { + "home": "Dashboard", + "deployments": "Deployment", + "deploymentDetail": "Dettagli deployment", + "containers": "Container", + "images": "Immagini", + "volumes": "Volumi", + "networks": "Reti", + "dockerPorts": "Mappatura porte", + "infrastructure": "Infrastruttura", + "systemPorts": "Porte di sistema", + "services": "Servizi di sistema", + "cronJobs": "Cron job", + "databases": "Server database", + "security": "Sicurezza e monitoraggio", + "certificates": "Certificati SSL", + "dnsZones": "Zone DNS", + "dnsExternal": "Provider DNS esterni", + "apps": "App installate", + "marketplace": "Marketplace", + "settings": "Impostazioni", + "users": "Utenti", + "apiKeys": "Chiavi API" + } + } +} diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json new file mode 100644 index 0000000..1d037d3 --- /dev/null +++ b/src/i18n/locales/pt.json @@ -0,0 +1,78 @@ +{ + "dashboard": { + "breadcrumbs": { + "home": "Início" + }, + "environment": { + "local": "Ambiente local" + }, + "navGroups": { + "stacks": "Stacks", + "docker": "Docker", + "system": "Sistema", + "databases": "Bases de dados", + "dns": "DNS", + "security": "Segurança", + "apps": "Apps", + "administration": "Administração" + }, + "nav": { + "dashboard": "Painel de controlo", + "deployments": "Implementações", + "containers": "Contentores", + "images": "Imagens", + "volumes": "Volumes", + "networks": "Redes", + "portMappings": "Mapeamento de portas", + "infrastructure": "Infraestrutura", + "ports": "Portas", + "services": "Serviços", + "cronJobs": "Tarefas cron", + "servers": "Servidores", + "zones": "Zonas", + "externalProviders": "Fornecedores externos", + "securityMonitoring": "Segurança e monitorização", + "certificates": "Certificados", + "installedApps": "Apps instaladas", + "marketplace": "Marketplace", + "settings": "Definições", + "users": "Utilizadores", + "apiKeys": "Chaves API" + }, + "header": { + "running": "Em execução", + "stopped": "Parado" + }, + "footer": { + "cpu": "CPU", + "memory": "Memória", + "connected": "Ligado", + "disconnected": "Desligado", + "signOut": "Terminar sessão" + }, + "titles": { + "home": "Painel de controlo", + "deployments": "Implementações", + "deploymentDetail": "Detalhes da implementação", + "containers": "Contentores", + "images": "Imagens", + "volumes": "Volumes", + "networks": "Redes", + "dockerPorts": "Mapeamento de portas", + "infrastructure": "Infraestrutura", + "systemPorts": "Portas do sistema", + "services": "Serviços do sistema", + "cronJobs": "Tarefas cron", + "databases": "Servidores de bases de dados", + "security": "Segurança e monitorização", + "certificates": "Certificados SSL", + "dnsZones": "Zonas DNS", + "dnsExternal": "Fornecedores DNS externos", + "apps": "Apps instaladas", + "marketplace": "Marketplace", + "settings": "Definições", + "users": "Utilizadores", + "apiKeys": "Chaves API" + } + } +} diff --git a/src/layouts/DashboardLayout.vue b/src/layouts/DashboardLayout.vue index d441bd4..31ac9c6 100644 --- a/src/layouts/DashboardLayout.vue +++ b/src/layouts/DashboardLayout.vue @@ -8,7 +8,7 @@
- Local Environment + {{ $t("dashboard.environment.local") }}
@@ -16,13 +16,13 @@