feat: add i18n for sidebar buttons and links with 8 language support

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-04-06 15:21:25 +00:00
parent 31df861ee2
commit b66fbf22f3
14 changed files with 909 additions and 72 deletions
@@ -38,6 +38,7 @@
import MenuButton, { sidebarClasses } from './MenuButton.svelte'
import MenuLink from './MenuLink.svelte'
import type { FavoriteKind } from './FavoriteMenu.svelte'
import { t } from '$lib/i18n/t.svelte'
let darkMode: boolean = $state(false)
let showExtraTriggers = $state(false)
let menubarEl: HTMLElement | undefined = $state()
@@ -55,14 +56,14 @@
let mainMenuLinks = $derived(
[
{ label: 'Home', id: 'home', href: `${base}/`, icon: Home },
{ label: 'Runs', id: 'runs', href: `${base}/runs`, icon: Play },
{ label: 'Schedules', id: 'schedules', href: `${base}/schedules`, icon: Calendar },
{ label: t('sidebar.home'), id: 'home', href: `${base}/`, icon: Home },
{ label: t('sidebar.runs'), id: 'runs', href: `${base}/runs`, icon: Play },
{ label: t('sidebar.schedules'), id: 'schedules', href: `${base}/schedules`, icon: Calendar },
// Add Tutorials to main menu only if not all completed and not skipped
...($tutorialsToDo.length > 0 && !$skippedAll
? [
{
label: 'Tutorials',
label: t('sidebar.tutorials'),
id: 'tutorials',
href: `${base}/tutorials`,
icon: GraduationCap
@@ -89,37 +90,37 @@
let secondMenuLinks: SecondMenuLink[] = $derived(
[
{
label: 'Resources',
label: t('sidebar.resources'),
id: 'resources',
href: `${base}/resources`
},
{
label: 'Variables',
label: t('sidebar.variables'),
id: 'variables',
href: `${base}/variables`
},
{
label: 'Assets',
label: t('sidebar.assets'),
id: 'assets',
href: `${base}/assets`
},
{
label: 'Groups',
label: t('sidebar.groups'),
id: 'groups',
href: `${base}/groups`
},
{
label: 'Folders',
label: t('sidebar.folders'),
id: 'folders',
href: `${base}/folders`
},
{
label: 'Workers',
label: t('sidebar.workers'),
id: 'workers',
href: `${base}/workers`
},
{
label: 'Audit logs',
label: t('sidebar.audit_logs'),
id: 'audit_logs',
href: `${base}/audit_logs`
}
@@ -129,30 +130,60 @@
let allTriggerLinks: TriggerMenuLink[] = $derived(
(
[
{ label: 'Custom HTTP routes', id: 'triggers', href: `${base}/routes`, kind: 'http' },
{
label: 'Websocket triggers',
label: t('operator.custom_http_routes'),
id: 'triggers',
href: `${base}/routes`,
kind: 'http'
},
{
label: t('operator.websocket_triggers'),
id: 'triggers',
href: `${base}/websocket_triggers`,
kind: 'ws'
},
{
label: 'Postgres triggers',
label: t('operator.postgres_triggers'),
id: 'triggers',
href: `${base}/postgres_triggers`,
kind: 'postgres'
},
{ label: 'Kafka triggers', id: 'triggers', href: `${base}/kafka_triggers`, kind: 'kafka' },
{ label: 'NATS triggers', id: 'triggers', href: `${base}/nats_triggers`, kind: 'nats' },
{ label: 'SQS triggers', id: 'triggers', href: `${base}/sqs_triggers`, kind: 'sqs' },
{
label: 'GCP Pub/Sub triggers',
label: t('operator.kafka_triggers'),
id: 'triggers',
href: `${base}/kafka_triggers`,
kind: 'kafka'
},
{
label: t('operator.nats_triggers'),
id: 'triggers',
href: `${base}/nats_triggers`,
kind: 'nats'
},
{
label: t('operator.sqs_triggers'),
id: 'triggers',
href: `${base}/sqs_triggers`,
kind: 'sqs'
},
{
label: t('operator.gcp_triggers'),
id: 'triggers',
href: `${base}/gcp_triggers`,
kind: 'gcp'
},
{ label: 'MQTT triggers', id: 'triggers', href: `${base}/mqtt_triggers`, kind: 'mqtt' },
{ label: 'Email triggers', id: 'triggers', href: `${base}/email_triggers`, kind: 'email' }
{
label: t('operator.mqtt_triggers'),
id: 'triggers',
href: `${base}/mqtt_triggers`,
kind: 'mqtt'
},
{
label: t('operator.email_triggers'),
id: 'triggers',
href: `${base}/email_triggers`,
kind: 'email'
}
] as TriggerMenuLink[]
).filter(filterLink)
)
@@ -231,7 +262,7 @@
{item}
>
<Settings size={14} />
Account settings
{t('user.account_settings')}
</MenuItem>
</div>
@@ -259,7 +290,7 @@
{:else}
<Moon size={14} />
{/if}
Switch theme
{t('user.switch_theme')}
</MenuItem>
<MenuItem
href="{base}/user/workspaces"
@@ -288,7 +319,7 @@
{item}
>
<ServerCog size={14} />
Instance settings
{t('sidebar.instance')}
</MenuItem>
{/if}
@@ -303,7 +334,7 @@
{item}
>
<LogOut size={14} />
Sign out
{t('user.sign_out')}
</MenuItem>
</div>
<div role="none">
@@ -73,6 +73,7 @@
} from '$lib/components/meltComponents'
import MenuButton from './MenuButton.svelte'
import GoogleCloudIcon from '../icons/GoogleCloudIcon.svelte'
import { t } from '$lib/i18n/t.svelte'
async function leaveWorkspace() {
await WorkspaceService.leaveWorkspace({ workspace: $workspaceStore ?? '' })
@@ -133,13 +134,13 @@
hasNewChangelogs = false
}
const thirdMenuLinks = [
let thirdMenuLinks = $derived([
{
label: 'Help',
label: t('sidebar.help'),
icon: HelpCircle,
subItems: [
{
label: 'Tutorials',
label: t('sidebar.tutorials'),
href: `${base}/tutorials`,
icon: GraduationCap,
aiId: 'sidebar-menu-link-tutorials',
@@ -147,7 +148,7 @@
external: false
},
{
label: 'Docs',
label: t('sidebar.docs'),
href: 'https://www.windmill.dev/docs/intro/',
icon: BookOpen,
aiId: 'sidebar-menu-link-docs',
@@ -155,7 +156,7 @@
external: true
},
{
label: 'Feedbacks',
label: t('sidebar.feedbacks'),
href: 'https://discord.gg/V7PM2YHsPB',
icon: DiscordIcon,
aiId: 'sidebar-menu-link-feedbacks',
@@ -163,7 +164,7 @@
external: true
},
{
label: 'Issues',
label: t('sidebar.issues'),
href: 'https://github.com/windmill-labs/windmill/issues/new',
icon: Github,
aiId: 'sidebar-menu-link-issues',
@@ -171,7 +172,7 @@
external: true
},
{
label: 'Changelog',
label: t('sidebar.changelog'),
href: 'https://www.windmill.dev/changelog/',
icon: Newspaper,
aiId: 'sidebar-menu-link-changelog',
@@ -180,7 +181,7 @@
}
]
}
]
])
interface Props {
numUnacknowledgedCriticalAlerts?: number
@@ -205,7 +206,7 @@
)
let mainMenuLinks = $derived([
{
label: 'Home',
label: t('sidebar.home'),
href: `${base}/`,
icon: Home,
aiId: 'sidebar-menu-link-home',
@@ -213,7 +214,7 @@
"Button to navigate to home which contains all the user's scripts, flows and apps"
},
{
label: 'Runs',
label: t('sidebar.runs'),
href: `${base}/runs`,
icon: Play,
aiId: 'sidebar-menu-link-runs',
@@ -225,7 +226,7 @@
}
},
{
label: 'Variables',
label: t('sidebar.variables'),
href: `${base}/variables`,
icon: DollarSign,
disabled: $userStore?.operator,
@@ -233,7 +234,7 @@
aiDescription: 'Button to navigate to variables'
},
{
label: 'Resources',
label: t('sidebar.resources'),
href: `${base}/resources`,
icon: Boxes,
disabled: $userStore?.operator,
@@ -241,7 +242,7 @@
aiDescription: 'Button to navigate to resources'
},
{
label: 'Assets',
label: t('sidebar.assets'),
href: `${base}/assets`,
icon: Pyramid,
disabled: $userStore?.operator,
@@ -252,7 +253,7 @@
...($tutorialsToDo.length > 0 && !$skippedAll
? [
{
label: 'Tutorials',
label: t('sidebar.tutorials'),
href: `${base}/tutorials`,
icon: GraduationCap,
aiId: 'sidebar-menu-link-tutorials-main',
@@ -263,7 +264,7 @@
])
let defaultExtraTriggerLinks = $derived([
{
label: 'HTTP',
label: t('sidebar.http'),
href: '/routes',
icon: Route,
disabled: $userStore?.operator,
@@ -272,7 +273,7 @@
aiDescription: 'Button to navigate to HTTP routes'
},
{
label: 'WebSockets',
label: t('sidebar.websockets'),
href: '/websocket_triggers',
icon: Unplug,
disabled: $userStore?.operator,
@@ -281,7 +282,7 @@
aiDescription: 'Button to navigate to websocket triggers'
},
{
label: 'Postgres',
label: t('sidebar.postgres'),
href: '/postgres_triggers',
icon: Database,
disabled: $userStore?.operator,
@@ -290,7 +291,7 @@
aiDescription: 'Button to navigate to Postgres triggers'
},
{
label: 'Kafka' + ($enterpriseLicense ? '' : ' (EE)'),
label: t('sidebar.kafka') + ($enterpriseLicense ? '' : ' (EE)'),
href: '/kafka_triggers',
icon: KafkaIcon,
disabled: $userStore?.operator || !$enterpriseLicense,
@@ -299,7 +300,7 @@
aiDescription: 'Button to navigate to Kafka triggers'
},
{
label: 'NATS' + ($enterpriseLicense ? '' : ' (EE)'),
label: t('sidebar.nats') + ($enterpriseLicense ? '' : ' (EE)'),
href: '/nats_triggers',
icon: NatsIcon,
disabled: $userStore?.operator || !$enterpriseLicense,
@@ -308,7 +309,7 @@
aiDescription: 'Button to navigate to NATS triggers'
},
{
label: 'SQS' + ($enterpriseLicense ? '' : ' (EE)'),
label: t('sidebar.sqs') + ($enterpriseLicense ? '' : ' (EE)'),
href: '/sqs_triggers',
icon: AwsIcon,
disabled: $userStore?.operator || !$enterpriseLicense,
@@ -317,7 +318,7 @@
aiDescription: 'Button to navigate to SQS triggers'
},
{
label: 'GCP Pub/Sub' + ($enterpriseLicense ? '' : ' (EE)'),
label: t('sidebar.gcp_pubsub') + ($enterpriseLicense ? '' : ' (EE)'),
href: '/gcp_triggers',
icon: GoogleCloudIcon,
disabled: $userStore?.operator || !$enterpriseLicense,
@@ -326,7 +327,7 @@
aiDescription: 'Button to navigate to GCP Pub/Sub triggers'
},
{
label: 'MQTT',
label: t('sidebar.mqtt'),
href: '/mqtt_triggers',
icon: MqttIcon,
disabled: $userStore?.operator,
@@ -335,7 +336,7 @@
aiDescription: 'Button to navigate to MQTT triggers'
},
{
label: 'Email',
label: t('sidebar.email'),
href: '/email_triggers',
icon: MailIcon,
disabled: $userStore?.operator,
@@ -361,7 +362,7 @@
let triggerMenuLinks = $derived([
{
label: 'Schedules',
label: t('sidebar.schedules'),
href: `${base}/schedules`,
icon: Calendar,
disabled: !SIDEBAR_SHOW_SCHEDULES || $userStore?.operator,
@@ -385,14 +386,14 @@
// disabled: !$userStore?.is_admin && !$userStore?.is_super_admin
// },
{
label: 'Settings',
label: t('sidebar.settings'),
icon: Settings,
aiId: 'sidebar-menu-link-settings',
aiDescription:
'Button to navigate to settings, including account, workspace, and instance settings',
subItems: [
{
label: 'Account',
label: t('sidebar.account'),
href: '#user-settings',
icon: UserCog,
aiId: 'sidebar-menu-link-account',
@@ -402,7 +403,7 @@
...($userStore?.is_admin || $superadmin
? [
{
label: 'Workspace',
label: t('sidebar.workspace'),
href: `${base}/workspace_settings`,
icon: FolderCog,
aiId: 'sidebar-menu-link-workspace',
@@ -414,7 +415,7 @@
...($superadmin
? [
{
label: 'Instance',
label: t('sidebar.instance'),
href: '#superadmin-settings',
icon: ServerCog,
aiId: 'sidebar-menu-link-instance',
@@ -426,7 +427,7 @@
...(!$superadmin && !$userStore?.is_admin
? [
{
label: 'Leave workspace',
label: t('sidebar.leave_workspace'),
action: () => {
leaveWorkspaceModal = true
},
@@ -439,7 +440,7 @@
...($workspaceStore?.startsWith('wm-fork-')
? [
{
label: 'Delete Forked Workspace',
label: t('sidebar.delete_fork'),
action: () => {
deleteWorkspaceForkModal = true
},
@@ -452,7 +453,7 @@
disabled: $userStore?.operator
},
{
label: 'Workers',
label: t('sidebar.workers'),
href: `${base}/workers`,
icon: ServerCog,
disabled: $userStore?.operator,
@@ -460,13 +461,13 @@
aiDescription: 'Button to navigate to workers'
},
{
label: 'Folders & Groups',
label: t('sidebar.folders_groups'),
icon: FolderOpen,
aiId: 'sidebar-menu-link-folders-groups',
aiDescription: 'Button to navigate to folders and groups',
subItems: [
{
label: 'Folders',
label: t('sidebar.folders'),
href: `${base}/folders`,
icon: FolderOpen,
disabled: $userStore?.operator,
@@ -475,7 +476,7 @@
faIcon: undefined
},
{
label: 'Groups',
label: t('sidebar.groups'),
href: `${base}/groups`,
icon: UserCog,
disabled: $userStore?.operator,
@@ -488,13 +489,13 @@
},
$devopsRole || $userStore?.is_admin
? {
label: 'Logs',
label: t('sidebar.logs'),
icon: Logs,
aiId: 'sidebar-menu-link-logs',
aiDescription: 'Button to navigate to logs',
subItems: [
{
label: 'Audit logs',
label: t('sidebar.audit_logs'),
href: `${base}/audit_logs`,
icon: Eye,
aiId: 'sidebar-menu-link-audit-logs',
@@ -503,7 +504,7 @@
...($devopsRole
? [
{
label: 'Service logs',
label: t('sidebar.service_logs'),
href: `${base}/service_logs`,
icon: Logs,
aiId: 'sidebar-menu-link-service-logs',
@@ -514,7 +515,7 @@
...($enterpriseLicense
? [
{
label: 'Critical alerts',
label: t('sidebar.critical_alerts'),
action: () => {
isCriticalAlertsUIOpen.set(true)
},
@@ -528,7 +529,7 @@
]
}
: {
label: 'Audit logs',
label: t('sidebar.audit_logs'),
href: `${base}/audit_logs`,
icon: Eye,
disabled: $userStore?.operator,
@@ -552,7 +553,7 @@
<div class="pt-4">
<div
class="text-secondary text-[0.5rem] uppercase transition-opacity"
class:opacity-0={isCollapsed}>Triggers</div
class:opacity-0={isCollapsed}>{t('sidebar.triggers')}</div
>
<Menubar class="flex flex-col gap-1">
{#snippet children({ createMenu })}
@@ -11,11 +11,14 @@
import { USER_SETTINGS_HASH } from './settings'
import { isCloudHosted } from '$lib/cloud'
import { twMerge } from 'tailwind-merge'
import { Crown, ServerCog, LogOut, Moon, Settings, Sun, User } from 'lucide-svelte'
import { Crown, ServerCog, LogOut, Moon, Settings, Sun, User, Languages } from 'lucide-svelte'
import DarkModeObserver from '../DarkModeObserver.svelte'
import MenuButton from './MenuButton.svelte'
import { Menu, MenuItem } from '$lib/components/meltComponents'
import { type MenubarBuilders } from '@melt-ui/svelte'
import { t } from '$lib/i18n/t.svelte'
import { getLocale, setLocale } from '$lib/i18n/t.svelte'
import { SUPPORTED_LOCALES, LOCALE_NAMES } from '$lib/i18n/index'
let darkMode: boolean = $state(false)
@@ -53,16 +56,16 @@
</p>
<span class="text-xs text-primary flex flex-row gap-2 items-center">
{#if $userStore?.is_admin}
Admin of this workspace <Crown size={14} />
{t('user.admin_of_workspace')} <Crown size={14} />
{:else if $userStore?.operator}
Operator in this workspace <ServerCog size={14} />
{t('user.operator_in_workspace')} <ServerCog size={14} />
{/if}
</span>
</div>
<div class="py-1">
<MenuItem href={USER_SETTINGS_HASH} class={itemClass} {item}>
<Settings size={16} />
Account settings
{t('user.account_settings')}
</MenuItem>
<MenuItem
@@ -83,12 +86,25 @@
{:else}
<Moon size={16} />
{/if}
Switch theme
{t('user.switch_theme')}
</MenuItem>
<MenuItem
onClick={() => {
const currentIndex = SUPPORTED_LOCALES.indexOf(getLocale())
const nextIndex = (currentIndex + 1) % SUPPORTED_LOCALES.length
setLocale(SUPPORTED_LOCALES[nextIndex])
}}
class={itemClass}
{item}
>
<Languages size={16} />
{t('user.language')}: {LOCALE_NAMES[getLocale()]}
</MenuItem>
<MenuItem onClick={() => logout()} class={itemClass} {item}>
<LogOut size={16} />
Sign out
{t('user.sign_out')}
</MenuItem>
</div>
@@ -125,7 +141,7 @@
}}
{item}
>
Upgrade
{t('user.upgrade')}
</MenuItem>
{/if}
{:else}
@@ -136,7 +152,7 @@
}}
{item}
>
Premium plan
{t('user.premium_plan')}
</MenuItem>
{/if}
</div>
+14
View File
@@ -0,0 +1,14 @@
export const SUPPORTED_LOCALES = ['en', 'fr', 'de', 'es', 'pt', 'ja', 'zh', 'ko'] as const
export type Locale = (typeof SUPPORTED_LOCALES)[number]
export const DEFAULT_LOCALE: Locale = 'en'
export const LOCALE_NAMES: Record<Locale, string> = {
en: 'English',
fr: 'Français',
de: 'Deutsch',
es: 'Español',
pt: 'Português',
ja: '日本語',
zh: '中文',
ko: '한국어'
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const de: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': 'Start',
'sidebar.runs': 'Ausführungen',
'sidebar.variables': 'Variablen',
'sidebar.resources': 'Ressourcen',
'sidebar.assets': 'Assets',
'sidebar.tutorials': 'Tutorials',
// Sidebar - triggers section
'sidebar.triggers': 'Trigger',
'sidebar.schedules': 'Zeitpläne',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'E-Mail',
// Sidebar - settings
'sidebar.settings': 'Einstellungen',
'sidebar.account': 'Konto',
'sidebar.workspace': 'Workspace',
'sidebar.instance': 'Instanz',
'sidebar.leave_workspace': 'Verlassen',
'sidebar.delete_fork': 'Fork löschen',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': 'Ordner & Gruppen',
'sidebar.folders': 'Ordner',
'sidebar.groups': 'Gruppen',
'sidebar.logs': 'Protokolle',
'sidebar.audit_logs': 'Audit',
'sidebar.service_logs': 'Dienste',
'sidebar.critical_alerts': 'Kritische Alerts',
// Sidebar - help
'sidebar.help': 'Hilfe',
'sidebar.docs': 'Doku',
'sidebar.feedbacks': 'Feedback',
'sidebar.issues': 'Issues',
'sidebar.changelog': 'Changelog',
// User menu
'user.account_settings': 'Kontoeinstellungen',
'user.switch_theme': 'Design wechseln',
'user.sign_out': 'Abmelden',
'user.upgrade': 'Upgrade',
'user.premium_plan': 'Premium-Plan',
'user.admin_of_workspace': 'Admin',
'user.operator_in_workspace': 'Operator',
'user.language': 'Sprache',
// Operator menu
'operator.custom_http_routes': 'HTTP-Routen',
'operator.websocket_triggers': 'WebSocket-Trigger',
'operator.postgres_triggers': 'Postgres-Trigger',
'operator.kafka_triggers': 'Kafka-Trigger',
'operator.nats_triggers': 'NATS-Trigger',
'operator.sqs_triggers': 'SQS-Trigger',
'operator.gcp_triggers': 'GCP-Trigger',
'operator.mqtt_triggers': 'MQTT-Trigger',
'operator.email_triggers': 'E-Mail-Trigger',
// Common actions
'action.save': 'Speichern',
'action.cancel': 'Abbrechen',
'action.delete': 'Löschen',
'action.edit': 'Bearbeiten',
'action.create': 'Erstellen',
'action.run': 'Ausführen',
'action.deploy': 'Deployen',
'action.share': 'Teilen',
'action.close': 'Schließen',
'action.confirm': 'Bestätigen',
'action.refresh': 'Aktualisieren',
// Page headers
'page.home': 'Start',
'page.variables': 'Variablen',
'page.resources': 'Ressourcen',
'page.schedules': 'Zeitpläne',
'page.workers': 'Workers',
'page.folders': 'Ordner',
'page.groups': 'Gruppen'
}
+90
View File
@@ -0,0 +1,90 @@
export const en = {
// Sidebar - main nav
'sidebar.home': 'Home',
'sidebar.runs': 'Runs',
'sidebar.variables': 'Variables',
'sidebar.resources': 'Resources',
'sidebar.assets': 'Assets',
'sidebar.tutorials': 'Tutorials',
// Sidebar - triggers section
'sidebar.triggers': 'Triggers',
'sidebar.schedules': 'Schedules',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'Email',
// Sidebar - settings
'sidebar.settings': 'Settings',
'sidebar.account': 'Account',
'sidebar.workspace': 'Workspace',
'sidebar.instance': 'Instance',
'sidebar.leave_workspace': 'Leave workspace',
'sidebar.delete_fork': 'Delete Forked Workspace',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': 'Folders & Groups',
'sidebar.folders': 'Folders',
'sidebar.groups': 'Groups',
'sidebar.logs': 'Logs',
'sidebar.audit_logs': 'Audit logs',
'sidebar.service_logs': 'Service logs',
'sidebar.critical_alerts': 'Critical alerts',
// Sidebar - help
'sidebar.help': 'Help',
'sidebar.docs': 'Docs',
'sidebar.feedbacks': 'Feedbacks',
'sidebar.issues': 'Issues',
'sidebar.changelog': 'Changelog',
// User menu
'user.account_settings': 'Account settings',
'user.switch_theme': 'Switch theme',
'user.sign_out': 'Sign out',
'user.upgrade': 'Upgrade',
'user.premium_plan': 'Premium plan',
'user.admin_of_workspace': 'Admin of this workspace',
'user.operator_in_workspace': 'Operator in this workspace',
'user.language': 'Language',
// Operator menu
'operator.custom_http_routes': 'Custom HTTP routes',
'operator.websocket_triggers': 'Websocket triggers',
'operator.postgres_triggers': 'Postgres triggers',
'operator.kafka_triggers': 'Kafka triggers',
'operator.nats_triggers': 'NATS triggers',
'operator.sqs_triggers': 'SQS triggers',
'operator.gcp_triggers': 'GCP Pub/Sub triggers',
'operator.mqtt_triggers': 'MQTT triggers',
'operator.email_triggers': 'Email triggers',
// Common actions
'action.save': 'Save',
'action.cancel': 'Cancel',
'action.delete': 'Delete',
'action.edit': 'Edit',
'action.create': 'Create',
'action.run': 'Run',
'action.deploy': 'Deploy',
'action.share': 'Share',
'action.close': 'Close',
'action.confirm': 'Confirm',
'action.refresh': 'Refresh',
// Page headers
'page.home': 'Home',
'page.variables': 'Variables',
'page.resources': 'Resources',
'page.schedules': 'Schedules',
'page.workers': 'Workers',
'page.folders': 'Folders',
'page.groups': 'Groups'
} as const
export type MessageKey = keyof typeof en
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const es: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': 'Inicio',
'sidebar.runs': 'Ejecuciones',
'sidebar.variables': 'Variables',
'sidebar.resources': 'Recursos',
'sidebar.assets': 'Assets',
'sidebar.tutorials': 'Tutoriales',
// Sidebar - triggers section
'sidebar.triggers': 'Disparadores',
'sidebar.schedules': 'Programados',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'Email',
// Sidebar - settings
'sidebar.settings': 'Ajustes',
'sidebar.account': 'Cuenta',
'sidebar.workspace': 'Espacio',
'sidebar.instance': 'Instancia',
'sidebar.leave_workspace': 'Salir',
'sidebar.delete_fork': 'Eliminar fork',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': 'Carpetas y Grupos',
'sidebar.folders': 'Carpetas',
'sidebar.groups': 'Grupos',
'sidebar.logs': 'Registros',
'sidebar.audit_logs': 'Auditoría',
'sidebar.service_logs': 'Servicios',
'sidebar.critical_alerts': 'Alertas críticas',
// Sidebar - help
'sidebar.help': 'Ayuda',
'sidebar.docs': 'Docs',
'sidebar.feedbacks': 'Comentarios',
'sidebar.issues': 'Issues',
'sidebar.changelog': 'Changelog',
// User menu
'user.account_settings': 'Mi cuenta',
'user.switch_theme': 'Cambiar tema',
'user.sign_out': 'Cerrar sesión',
'user.upgrade': 'Mejorar plan',
'user.premium_plan': 'Plan premium',
'user.admin_of_workspace': 'Admin',
'user.operator_in_workspace': 'Operador',
'user.language': 'Idioma',
// Operator menu
'operator.custom_http_routes': 'Rutas HTTP',
'operator.websocket_triggers': 'Triggers WebSocket',
'operator.postgres_triggers': 'Triggers Postgres',
'operator.kafka_triggers': 'Triggers Kafka',
'operator.nats_triggers': 'Triggers NATS',
'operator.sqs_triggers': 'Triggers SQS',
'operator.gcp_triggers': 'Triggers GCP',
'operator.mqtt_triggers': 'Triggers MQTT',
'operator.email_triggers': 'Triggers email',
// Common actions
'action.save': 'Guardar',
'action.cancel': 'Cancelar',
'action.delete': 'Eliminar',
'action.edit': 'Editar',
'action.create': 'Crear',
'action.run': 'Ejecutar',
'action.deploy': 'Desplegar',
'action.share': 'Compartir',
'action.close': 'Cerrar',
'action.confirm': 'Confirmar',
'action.refresh': 'Actualizar',
// Page headers
'page.home': 'Inicio',
'page.variables': 'Variables',
'page.resources': 'Recursos',
'page.schedules': 'Programados',
'page.workers': 'Workers',
'page.folders': 'Carpetas',
'page.groups': 'Grupos'
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const fr: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': 'Accueil',
'sidebar.runs': 'Exécutions',
'sidebar.variables': 'Variables',
'sidebar.resources': 'Ressources',
'sidebar.assets': 'Assets',
'sidebar.tutorials': 'Tutoriels',
// Sidebar - triggers section
'sidebar.triggers': 'Déclencheurs',
'sidebar.schedules': 'Plannings',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'Email',
// Sidebar - settings
'sidebar.settings': 'Paramètres',
'sidebar.account': 'Compte',
'sidebar.workspace': 'Espace',
'sidebar.instance': 'Instance',
'sidebar.leave_workspace': 'Quitter',
'sidebar.delete_fork': 'Supprimer le fork',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': 'Dossiers & Groupes',
'sidebar.folders': 'Dossiers',
'sidebar.groups': 'Groupes',
'sidebar.logs': 'Journaux',
'sidebar.audit_logs': 'Audit',
'sidebar.service_logs': 'Services',
'sidebar.critical_alerts': 'Alertes critiques',
// Sidebar - help
'sidebar.help': 'Aide',
'sidebar.docs': 'Docs',
'sidebar.feedbacks': 'Retours',
'sidebar.issues': 'Issues',
'sidebar.changelog': 'Changelog',
// User menu
'user.account_settings': 'Mon compte',
'user.switch_theme': 'Changer de thème',
'user.sign_out': 'Déconnexion',
'user.upgrade': 'Passer en premium',
'user.premium_plan': 'Plan premium',
'user.admin_of_workspace': 'Admin de cet espace',
'user.operator_in_workspace': 'Opérateur',
'user.language': 'Langue',
// Operator menu
'operator.custom_http_routes': 'Routes HTTP',
'operator.websocket_triggers': 'Déclencheurs WS',
'operator.postgres_triggers': 'Déclencheurs PG',
'operator.kafka_triggers': 'Déclencheurs Kafka',
'operator.nats_triggers': 'Déclencheurs NATS',
'operator.sqs_triggers': 'Déclencheurs SQS',
'operator.gcp_triggers': 'Déclencheurs GCP',
'operator.mqtt_triggers': 'Déclencheurs MQTT',
'operator.email_triggers': 'Déclencheurs email',
// Common actions
'action.save': 'Enregistrer',
'action.cancel': 'Annuler',
'action.delete': 'Supprimer',
'action.edit': 'Modifier',
'action.create': 'Créer',
'action.run': 'Lancer',
'action.deploy': 'Déployer',
'action.share': 'Partager',
'action.close': 'Fermer',
'action.confirm': 'Confirmer',
'action.refresh': 'Actualiser',
// Page headers
'page.home': 'Accueil',
'page.variables': 'Variables',
'page.resources': 'Ressources',
'page.schedules': 'Plannings',
'page.workers': 'Workers',
'page.folders': 'Dossiers',
'page.groups': 'Groupes'
}
+22
View File
@@ -0,0 +1,22 @@
import type { Locale } from '../index'
import { en, type MessageKey } from './en'
import { fr } from './fr'
import { de } from './de'
import { es } from './es'
import { pt } from './pt'
import { ja } from './ja'
import { zh } from './zh'
import { ko } from './ko'
export type { MessageKey }
export const messages: Record<Locale, Record<MessageKey, string>> = {
en,
fr,
de,
es,
pt,
ja,
zh,
ko
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const ja: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': 'ホーム',
'sidebar.runs': '実行',
'sidebar.variables': '変数',
'sidebar.resources': 'リソース',
'sidebar.assets': 'アセット',
'sidebar.tutorials': 'チュートリアル',
// Sidebar - triggers section
'sidebar.triggers': 'トリガー',
'sidebar.schedules': 'スケジュール',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'メール',
// Sidebar - settings
'sidebar.settings': '設定',
'sidebar.account': 'アカウント',
'sidebar.workspace': 'ワークスペース',
'sidebar.instance': 'インスタンス',
'sidebar.leave_workspace': '退出',
'sidebar.delete_fork': 'フォークを削除',
'sidebar.workers': 'ワーカー',
'sidebar.folders_groups': 'フォルダとグループ',
'sidebar.folders': 'フォルダ',
'sidebar.groups': 'グループ',
'sidebar.logs': 'ログ',
'sidebar.audit_logs': '監査ログ',
'sidebar.service_logs': 'サービスログ',
'sidebar.critical_alerts': '重要なアラート',
// Sidebar - help
'sidebar.help': 'ヘルプ',
'sidebar.docs': 'ドキュメント',
'sidebar.feedbacks': 'フィードバック',
'sidebar.issues': 'Issues',
'sidebar.changelog': '変更履歴',
// User menu
'user.account_settings': 'アカウント設定',
'user.switch_theme': 'テーマ切替',
'user.sign_out': 'ログアウト',
'user.upgrade': 'アップグレード',
'user.premium_plan': 'プレミアムプラン',
'user.admin_of_workspace': '管理者',
'user.operator_in_workspace': 'オペレーター',
'user.language': '言語',
// Operator menu
'operator.custom_http_routes': 'HTTPルート',
'operator.websocket_triggers': 'WebSocketトリガー',
'operator.postgres_triggers': 'Postgresトリガー',
'operator.kafka_triggers': 'Kafkaトリガー',
'operator.nats_triggers': 'NATSトリガー',
'operator.sqs_triggers': 'SQSトリガー',
'operator.gcp_triggers': 'GCPトリガー',
'operator.mqtt_triggers': 'MQTTトリガー',
'operator.email_triggers': 'メールトリガー',
// Common actions
'action.save': '保存',
'action.cancel': 'キャンセル',
'action.delete': '削除',
'action.edit': '編集',
'action.create': '作成',
'action.run': '実行',
'action.deploy': 'デプロイ',
'action.share': '共有',
'action.close': '閉じる',
'action.confirm': '確認',
'action.refresh': '更新',
// Page headers
'page.home': 'ホーム',
'page.variables': '変数',
'page.resources': 'リソース',
'page.schedules': 'スケジュール',
'page.workers': 'ワーカー',
'page.folders': 'フォルダ',
'page.groups': 'グループ'
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const ko: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': '홈',
'sidebar.runs': '실행',
'sidebar.variables': '변수',
'sidebar.resources': '리소스',
'sidebar.assets': '에셋',
'sidebar.tutorials': '튜토리얼',
// Sidebar - triggers section
'sidebar.triggers': '트리거',
'sidebar.schedules': '스케줄',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': '이메일',
// Sidebar - settings
'sidebar.settings': '설정',
'sidebar.account': '계정',
'sidebar.workspace': '워크스페이스',
'sidebar.instance': '인스턴스',
'sidebar.leave_workspace': '나가기',
'sidebar.delete_fork': '포크 삭제',
'sidebar.workers': '워커',
'sidebar.folders_groups': '폴더 및 그룹',
'sidebar.folders': '폴더',
'sidebar.groups': '그룹',
'sidebar.logs': '로그',
'sidebar.audit_logs': '감사 로그',
'sidebar.service_logs': '서비스 로그',
'sidebar.critical_alerts': '심각한 알림',
// Sidebar - help
'sidebar.help': '도움말',
'sidebar.docs': '문서',
'sidebar.feedbacks': '피드백',
'sidebar.issues': 'Issues',
'sidebar.changelog': '변경 내역',
// User menu
'user.account_settings': '계정 설정',
'user.switch_theme': '테마 전환',
'user.sign_out': '로그아웃',
'user.upgrade': '업그레이드',
'user.premium_plan': '프리미엄 플랜',
'user.admin_of_workspace': '관리자',
'user.operator_in_workspace': '운영자',
'user.language': '언어',
// Operator menu
'operator.custom_http_routes': 'HTTP 라우트',
'operator.websocket_triggers': 'WebSocket 트리거',
'operator.postgres_triggers': 'Postgres 트리거',
'operator.kafka_triggers': 'Kafka 트리거',
'operator.nats_triggers': 'NATS 트리거',
'operator.sqs_triggers': 'SQS 트리거',
'operator.gcp_triggers': 'GCP 트리거',
'operator.mqtt_triggers': 'MQTT 트리거',
'operator.email_triggers': '이메일 트리거',
// Common actions
'action.save': '저장',
'action.cancel': '취소',
'action.delete': '삭제',
'action.edit': '편집',
'action.create': '생성',
'action.run': '실행',
'action.deploy': '배포',
'action.share': '공유',
'action.close': '닫기',
'action.confirm': '확인',
'action.refresh': '새로고침',
// Page headers
'page.home': '홈',
'page.variables': '변수',
'page.resources': '리소스',
'page.schedules': '스케줄',
'page.workers': '워커',
'page.folders': '폴더',
'page.groups': '그룹'
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const pt: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': 'Início',
'sidebar.runs': 'Execuções',
'sidebar.variables': 'Variáveis',
'sidebar.resources': 'Recursos',
'sidebar.assets': 'Assets',
'sidebar.tutorials': 'Tutoriais',
// Sidebar - triggers section
'sidebar.triggers': 'Gatilhos',
'sidebar.schedules': 'Agendamentos',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': 'Email',
// Sidebar - settings
'sidebar.settings': 'Configurações',
'sidebar.account': 'Conta',
'sidebar.workspace': 'Workspace',
'sidebar.instance': 'Instância',
'sidebar.leave_workspace': 'Sair',
'sidebar.delete_fork': 'Excluir fork',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': 'Pastas e Grupos',
'sidebar.folders': 'Pastas',
'sidebar.groups': 'Grupos',
'sidebar.logs': 'Logs',
'sidebar.audit_logs': 'Auditoria',
'sidebar.service_logs': 'Serviços',
'sidebar.critical_alerts': 'Alertas críticos',
// Sidebar - help
'sidebar.help': 'Ajuda',
'sidebar.docs': 'Docs',
'sidebar.feedbacks': 'Feedbacks',
'sidebar.issues': 'Issues',
'sidebar.changelog': 'Changelog',
// User menu
'user.account_settings': 'Minha conta',
'user.switch_theme': 'Mudar tema',
'user.sign_out': 'Sair',
'user.upgrade': 'Upgrade',
'user.premium_plan': 'Plano premium',
'user.admin_of_workspace': 'Admin',
'user.operator_in_workspace': 'Operador',
'user.language': 'Idioma',
// Operator menu
'operator.custom_http_routes': 'Rotas HTTP',
'operator.websocket_triggers': 'Triggers WebSocket',
'operator.postgres_triggers': 'Triggers Postgres',
'operator.kafka_triggers': 'Triggers Kafka',
'operator.nats_triggers': 'Triggers NATS',
'operator.sqs_triggers': 'Triggers SQS',
'operator.gcp_triggers': 'Triggers GCP',
'operator.mqtt_triggers': 'Triggers MQTT',
'operator.email_triggers': 'Triggers email',
// Common actions
'action.save': 'Salvar',
'action.cancel': 'Cancelar',
'action.delete': 'Excluir',
'action.edit': 'Editar',
'action.create': 'Criar',
'action.run': 'Executar',
'action.deploy': 'Implantar',
'action.share': 'Compartilhar',
'action.close': 'Fechar',
'action.confirm': 'Confirmar',
'action.refresh': 'Atualizar',
// Page headers
'page.home': 'Início',
'page.variables': 'Variáveis',
'page.resources': 'Recursos',
'page.schedules': 'Agendamentos',
'page.workers': 'Workers',
'page.folders': 'Pastas',
'page.groups': 'Grupos'
}
+90
View File
@@ -0,0 +1,90 @@
import type { MessageKey } from './en'
export const zh: Record<MessageKey, string> = {
// Sidebar - main nav
'sidebar.home': '首页',
'sidebar.runs': '运行',
'sidebar.variables': '变量',
'sidebar.resources': '资源',
'sidebar.assets': '资产',
'sidebar.tutorials': '教程',
// Sidebar - triggers section
'sidebar.triggers': '触发器',
'sidebar.schedules': '定时任务',
'sidebar.http': 'HTTP',
'sidebar.websockets': 'WebSockets',
'sidebar.postgres': 'Postgres',
'sidebar.kafka': 'Kafka',
'sidebar.nats': 'NATS',
'sidebar.sqs': 'SQS',
'sidebar.gcp_pubsub': 'GCP Pub/Sub',
'sidebar.mqtt': 'MQTT',
'sidebar.email': '邮件',
// Sidebar - settings
'sidebar.settings': '设置',
'sidebar.account': '账户',
'sidebar.workspace': '工作区',
'sidebar.instance': '实例',
'sidebar.leave_workspace': '退出',
'sidebar.delete_fork': '删除分支',
'sidebar.workers': 'Workers',
'sidebar.folders_groups': '文件夹和分组',
'sidebar.folders': '文件夹',
'sidebar.groups': '分组',
'sidebar.logs': '日志',
'sidebar.audit_logs': '审计日志',
'sidebar.service_logs': '服务日志',
'sidebar.critical_alerts': '严重告警',
// Sidebar - help
'sidebar.help': '帮助',
'sidebar.docs': '文档',
'sidebar.feedbacks': '反馈',
'sidebar.issues': 'Issues',
'sidebar.changelog': '更新日志',
// User menu
'user.account_settings': '账户设置',
'user.switch_theme': '切换主题',
'user.sign_out': '退出登录',
'user.upgrade': '升级',
'user.premium_plan': '高级版',
'user.admin_of_workspace': '管理员',
'user.operator_in_workspace': '操作员',
'user.language': '语言',
// Operator menu
'operator.custom_http_routes': 'HTTP路由',
'operator.websocket_triggers': 'WebSocket触发器',
'operator.postgres_triggers': 'Postgres触发器',
'operator.kafka_triggers': 'Kafka触发器',
'operator.nats_triggers': 'NATS触发器',
'operator.sqs_triggers': 'SQS触发器',
'operator.gcp_triggers': 'GCP触发器',
'operator.mqtt_triggers': 'MQTT触发器',
'operator.email_triggers': '邮件触发器',
// Common actions
'action.save': '保存',
'action.cancel': '取消',
'action.delete': '删除',
'action.edit': '编辑',
'action.create': '创建',
'action.run': '运行',
'action.deploy': '部署',
'action.share': '共享',
'action.close': '关闭',
'action.confirm': '确认',
'action.refresh': '刷新',
// Page headers
'page.home': '首页',
'page.variables': '变量',
'page.resources': '资源',
'page.schedules': '定时任务',
'page.workers': 'Workers',
'page.folders': '文件夹',
'page.groups': '分组'
}
+33
View File
@@ -0,0 +1,33 @@
import { getLocalSetting, storeLocalSetting } from '$lib/utils'
import { type Locale, DEFAULT_LOCALE, SUPPORTED_LOCALES } from './index'
import { en, type MessageKey } from './messages/en'
import { messages } from './messages/index'
function getInitialLocale(): Locale {
const stored = getLocalSetting('locale')
if (stored && SUPPORTED_LOCALES.includes(stored as Locale)) {
return stored as Locale
}
return DEFAULT_LOCALE
}
let locale = $state<Locale>(getInitialLocale())
export function getLocale(): Locale {
return locale
}
export function setLocale(l: Locale) {
locale = l
storeLocalSetting('locale', l)
if (typeof document !== 'undefined') {
document.documentElement.lang = l
}
}
export function t(key: MessageKey): string {
if (locale === 'en') {
return en[key]
}
return messages[locale]?.[key] ?? en[key]
}