Compare commits

...

6 Commits

Author SHA1 Message Date
Ruben Fiszel
1b1e3d80b1 feat: add i18n for editor buttons, deploy, preview, and log viewer
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-06 19:32:30 +00:00
Ruben Fiszel
2f66f9c173 feat: default to english, expand to 197 keys, persist locale in localStorage
- Default locale is now English (not auto-detected)
- Alert banner on home shows when browser language differs, offering to switch
- Language preference stored in localStorage (wm_locale) for long persistence
- Expanded from 90 to 197 translation keys covering actions, editor, pages
- All 7 language files updated to match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 18:05:42 +00:00
Ruben Fiszel
a6edbe2a5b feat: add dismissable language detection banner on home page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:34:35 +00:00
Ruben Fiszel
ca406382b2 feat: add auto-detected locale with explicit locale picker in user menu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:28:47 +00:00
Ruben Fiszel
6632aa8310 fix: consolidate duplicate import in UserMenu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:22:43 +00:00
Ruben Fiszel
b66fbf22f3 feat: add i18n for sidebar buttons and links with 8 language support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:21:25 +00:00
28 changed files with 2477 additions and 163 deletions

View File

@@ -2,6 +2,7 @@
import { Button } from '$lib/components/common'
import { Save, CornerDownLeft } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
import { t } from '$lib/i18n/t.svelte'
const {
loading = false,
@@ -56,12 +57,12 @@
dropdownOpen = detail
}}
>
Deploy
{t('editor.deploy')}
{#snippet tooltip()}
<div class="flex flex-row gap-2 w-80 p-4 bg-surface rounded-lg shadow-lg dark:border z-[5001]">
<input
type="text"
placeholder="Deployment message"
placeholder={t('editor.deployment_message')}
bind:value={deploymentMsg}
onkeydown={async (e) => {
if (e.key === 'Enter') {
@@ -77,7 +78,7 @@
endIcon={{ icon: CornerDownLeft }}
loading={loadingSave}
>
Deploy
{t('editor.deploy')}
</Button>
</div>
{/snippet}

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { t } from '$lib/i18n/t.svelte'
import {
FlowService,
type Flow,
@@ -279,7 +280,7 @@
if (!forceSave && orderedJsonStringify(draftOrDeployed) === orderedJsonStringify(current)) {
sendUserToast('No changes detected, ignoring', false, [
{
label: 'Save anyway',
label: t('editor.save_anyway'),
callback: () => {
saveDraftInternal(true)
}
@@ -821,21 +822,21 @@
if (untrack(() => customUi).topBar?.extraDeployOptions != false) {
if (savedFlow?.draft_only === false || savedFlow?.draft_only === undefined) {
dropdownItems.push({
label: 'Exit & see details',
label: t('editor.exit_see_details'),
onClick: () => onDetails?.({ path: $pathStore })
})
}
if (!untrack(() => newFlow)) {
dropdownItems.push({
label: 'Fork',
label: t('action.fork'),
onClick: () => window.open(`/flows/add?template=${initialPath}`)
})
}
if (!untrack(() => newFlow) && !isCloudHosted() && !isRuleActive('DisableWorkspaceForking')) {
dropdownItems.push({
label: 'Edit in workspace fork',
label: t('editor.edit_in_fork'),
onClick: () => window.open(buildForkEditUrl('flow', initialPath))
})
}
@@ -880,7 +881,7 @@
return [
...baseMenuItems,
{
displayName: 'Undo',
displayName: t('action.undo'),
icon: Undo,
action: () => handleUndo(),
disabled: $history.index === 0,
@@ -888,14 +889,14 @@
separatorTop: baseMenuItems.length > 0
},
{
displayName: 'Redo',
displayName: t('action.redo'),
icon: Redo,
action: () => handleRedo(),
disabled: $history.index === $history.history.length - 1,
shortcut: `${mod}⇧Z`
},
{
displayName: 'Tutorials',
displayName: t('sidebar.tutorials'),
icon: BookOpen,
separatorTop: true,
extra: (() => {
@@ -912,7 +913,7 @@
})(),
submenuItems: [
{
displayName: 'Build a flow',
displayName: t('flow.build_a_flow'),
action: () => flowTutorials?.runTutorialById('flow-live-tutorial'),
icon: $tutorialsToDo.includes(getTutorialIndex('flow-live-tutorial'))
? Circle
@@ -922,7 +923,7 @@
: 'green'
},
{
displayName: 'Fix a broken flow',
displayName: t('flow.fix_broken_flow'),
action: () => flowTutorials?.runTutorialById('troubleshoot-flow'),
icon: $tutorialsToDo.includes(getTutorialIndex('troubleshoot-flow'))
? Circle
@@ -932,20 +933,20 @@
: 'green'
},
{
displayName: 'Reset tutorials',
displayName: t('flow.reset_tutorials'),
action: () => resetAllTodos(),
icon: RefreshCw,
separatorTop: true
},
{
displayName: 'Skip tutorials',
displayName: t('flow.skip_tutorials'),
action: () => skipAllTodos(),
icon: CheckCheck
}
]
},
{
displayName: 'Test flow & record',
displayName: t('flow.test_flow_record'),
icon: Disc,
action: () => flowPreviewButtons?.openRecordingPreview()
}
@@ -960,7 +961,7 @@
...(customUi?.topBar?.history != false
? [
{
displayName: 'Deployment History',
displayName: t('editor.deployment_history'),
icon: HistoryIcon,
action: () => {
flowHistory?.open()
@@ -972,12 +973,12 @@
...(customUi?.topBar?.export != false
? [
{
displayName: 'Export',
displayName: t('editor.export'),
icon: FileJson,
action: () => jsonViewerDrawer?.openDrawer()
},
{
displayName: 'Edit in YAML',
displayName: t('editor.edit_in_yaml'),
icon: FileJson,
action: () => yamlEditorDrawer?.openDrawer(),
disabled: hasAiDiff
@@ -987,7 +988,7 @@
...(customUi?.topBar?.settings != false
? [
{
displayName: 'Flow settings',
displayName: t('editor.flow_settings'),
icon: Settings,
action: () => {
select('settings-metadata')
@@ -1225,7 +1226,7 @@
disabled={!savedFlow}
startIcon={{ icon: DiffIcon }}
>
Diff
{t('editor.diff')}
</Button>
{/if}
<FlowPreviewButtons
@@ -1258,7 +1259,7 @@
disabled={(!newFlow && !savedFlow) || loading}
shortCut={{ key: 'S' }}
>
Draft
{t('editor.draft')}
</Button>
{/if}

View File

@@ -12,6 +12,7 @@
<script lang="ts">
import { ClipboardCopy, Download, Expand, Loader2 } from 'lucide-svelte'
import { t } from '$lib/i18n/t.svelte'
import { Button, Drawer, DrawerContent } from './common'
import { copyToClipboard } from '$lib/utils'
import { base } from '$lib/base'
@@ -55,7 +56,7 @@
noMaxH = false,
noAutoScroll = false,
download = true,
customEmptyMessage = 'No logs are available yet',
customEmptyMessage = t('editor.no_logs_available'),
tagLabel = undefined,
noPadding = false,
navigationId = undefined
@@ -273,14 +274,16 @@
>{#if content}{@const len =
(content?.length ?? 0) +
(loadedFromObjectStore?.length ?? 0)}{#if splitHtml}{@html splitHtml.before}<button
onclick={getStoreLogs}
>Show more... <Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
>{@html splitHtml.after}{:else if downloadStartUrl}<button
onclick={getStoreLogs}
>Show more... <Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
><br />{@html html}{:else if len > LOG_LIMIT}(truncated to the last {LOG_LIMIT} characters)...<br
>{@html splitHtml.after}{:else if downloadStartUrl}<button onclick={getStoreLogs}
>Show more... <Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
><br
/>{@html html}{:else if len > LOG_LIMIT}(truncated to the last {LOG_LIMIT} characters)...<br
/><button onclick={() => showMoreTruncate(len)}>Show more..</button><br
/>{@html html}{:else}{@html html}{/if}{:else if isLoading}Waiting for job to start...{:else}No logs are available yet{/if}</pre
/>{@html html}{:else}{@html html}{/if}{:else if isLoading}{t(
'editor.waiting_for_job'
)}{:else}{t('editor.no_logs_available')}{/if}</pre
>
</div>
</DrawerContent>
@@ -356,19 +359,18 @@
noPadding ? '' : 'p-2'
)}
>{#if content}{@const len =
(content?.length ?? 0) +
(loadedFromObjectStore?.length ?? 0)}{#if splitHtml}<span>{@html splitHtml.before}</span><button
onclick={getStoreLogs}
>Show more... &nbsp;<Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
><span>{@html splitHtml.after}</span
>{:else if downloadStartUrl}<button
(content?.length ?? 0) + (loadedFromObjectStore?.length ?? 0)}{#if splitHtml}<span
>{@html splitHtml.before}</span
><button onclick={getStoreLogs}
>Show more... &nbsp;<Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
><span>{@html splitHtml.after}</span>{:else if downloadStartUrl}<button
onclick={getStoreLogs}
>Show more... &nbsp;<Tooltip>{tooltipText(prefixInfo)}</Tooltip></button
><br /><span>{@html html}</span
>{:else if len > LOG_LIMIT}<button onclick={() => showMoreTruncate(len)}
>Show more..</button
>&nbsp;({LOG_LIMIT}/{len} chars)<br /><span>{@html html}</span
>{:else}<span>{@html html}</span>{/if}{:else if !isLoading}<span>{customEmptyMessage}</span>{/if}</pre
><br /><span>{@html html}</span>{:else if len > LOG_LIMIT}<button
onclick={() => showMoreTruncate(len)}>Show more..</button
>&nbsp;({LOG_LIMIT}/{len} chars)<br /><span>{@html html}</span>{:else}<span
>{@html html}</span
>{/if}{:else if !isLoading}<span>{customEmptyMessage}</span>{/if}</pre
>
</div>
</div>

View File

@@ -2,6 +2,7 @@
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { t } from '$lib/i18n/t.svelte'
import {
DraftService,
type NewScript,
@@ -824,13 +825,13 @@
initialPath != '' && customUi?.topBar?.extraDeployOptions != false
? [
{
label: 'Deploy & Stay here',
label: t('editor.deploy_stay'),
onClick: () => {
handleEditScript(true)
}
},
{
label: 'Fork',
label: t('action.fork'),
onClick: () => {
window.open(`/scripts/add?template=${initialPath}`)
}
@@ -838,7 +839,7 @@
...(!isCloudHosted() && !isRuleActive('DisableWorkspaceForking')
? [
{
label: 'Edit in workspace fork',
label: t('editor.edit_in_fork'),
onClick: () => {
window.open(buildForkEditUrl('script', initialPath))
}
@@ -848,7 +849,7 @@
...(customUi?.topBar?.diff !== false && savedScript && diffDrawer
? [
{
label: 'Show diff',
label: t('editor.show_diff'),
onClick: async () => {
if (!savedScript) {
return
@@ -877,7 +878,7 @@
...(!script.draft_only && script.kind === 'script' && !script.auto_kind
? [
{
label: 'Exit & See details',
label: t('editor.exit_see_details'),
onClick: () => {
onSeeDetails?.({ path: initialPath })
}
@@ -887,7 +888,7 @@
...(isWorkflowAsCode(script.content, script.language)
? [
{
label: 'Export as YAML/JSON',
label: t('editor.export_yaml_json'),
onClick: () => {
wacExportDrawer?.open(script)
}
@@ -900,7 +901,7 @@
if (dropdownItems.length === 0 && isWorkflowAsCode(script.content, script.language)) {
dropdownItems = [
{
label: 'Export as YAML/JSON',
label: t('editor.export_yaml_json'),
onClick: () => {
wacExportDrawer?.open(script)
}
@@ -2026,7 +2027,7 @@
on:click={() => (metadataOpen = true)}
startIcon={{ icon: Settings }}
>
<span class="hidden lg:flex"> Settings </span>
<span class="hidden lg:flex"> {t('editor.settings')} </span>
</Button>
{/if}
<Button
@@ -2038,7 +2039,7 @@
disabled={initialPath != '' && !savedScript}
shortCut={{ key: 'S' }}
>
<span class="hidden lg:flex"> Draft </span>
<span class="hidden lg:flex"> {t('editor.draft')} </span>
</Button>
<DeployButton

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { buildWsUrl } from '$lib/wsUrl'
import { t } from '$lib/i18n/t.svelte'
import { processSecretArgs } from './secretArgUtils'
import type { Schema, SupportedLanguage } from '$lib/common'
import {
@@ -1520,7 +1521,7 @@
width="20px"
spin="fast"
/>
Cancel
{t('editor.cancel')}
</Button>
{:else}
{@const disableTriggerButton =
@@ -1533,7 +1534,7 @@
startIcon={{ icon: Play, classes: 'animate-none' }}
shortCut={{ Icon: CornerDownLeft }}
>
Test
{t('editor.test')}
</Button>
{#if !disableTriggerButton}
<CaptureButton on:openTriggers />
@@ -1557,7 +1558,7 @@
size="xs"
items={[
{
displayName: 'Test & record',
displayName: t('editor.test_and_record'),
icon: Disc,
action: () => recordAndTest()
}
@@ -1628,9 +1629,7 @@
{/key}
{#if showPsCommonParams}
<div class="mt-2">
<PowerShellCommonParams
bind:args={psCommonParams}
/>
<PowerShellCommonParams bind:args={psCommonParams} />
</div>
{/if}
</div>

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { Drawer, DrawerContent, UndoRedo } from '$lib/components/common'
import Button from '$lib/components/common/button/Button.svelte'
import { t } from '$lib/i18n/t.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import { AppService, DraftService, type Policy } from '$lib/gen'
@@ -401,7 +402,7 @@
if (!forceSave && orderedJsonStringify(draftOrDeployed) === orderedJsonStringify(current)) {
sendUserToast('No changes detected, ignoring', false, [
{
label: 'Save anyway',
label: t('editor.save_anyway'),
callback: () => {
saveDraft(true)
}
@@ -560,7 +561,7 @@
let moreItems = $derived([
{
displayName: 'Deployment history',
displayName: t('editor.deployment_history'),
icon: History,
action: () => {
historyBrowserDrawerOpen = true
@@ -568,14 +569,14 @@
disabled: !savedApp
},
{
displayName: 'Export',
displayName: t('editor.export'),
icon: FileJson,
action: () => {
appExport?.open($app)
}
},
{
displayName: 'Public URL',
displayName: t('app.public_url'),
icon: Globe,
action: async () => {
const secretUrl = await AppService.getPublicSecretOfApp({
@@ -595,14 +596,14 @@
// },
{
displayName: 'App inputs',
displayName: t('app.app_inputs'),
icon: FormInput,
action: () => {
inputsDrawerOpen = true
}
},
{
displayName: 'Schedule reports',
displayName: t('app.schedule_reports'),
icon: FileClock,
action: () => {
appReportingDrawerOpen = true
@@ -610,7 +611,7 @@
disabled: !savedApp || savedApp.draft_only
},
{
displayName: 'Diff',
displayName: t('editor.diff'),
icon: DiffIcon,
action: async () => {
if (!savedApp) {
@@ -638,21 +639,21 @@
},
// App debug menu
{
displayName: 'Troubleshoot panel',
displayName: t('app.troubleshoot'),
icon: Bug,
action: () => {
debugAppDrawerOpen = true
}
},
{
displayName: 'Lazy mode',
displayName: t('app.lazy_mode'),
icon: Zap,
action: () => {
lazyDrawerOpen = true
}
},
{
displayName: 'Hub export',
displayName: t('app.hub_export'),
icon: FileUp,
action: () => {
appExport?.open(toStatic($app, $staticExporter, $summary).app)
@@ -742,7 +743,7 @@
{#if $appPath == ''}
<Drawer bind:open={draftDrawerOpen} size="800px">
<DrawerContent title="Initial draft save" on:close={() => closeDraftDrawer()}>
<DrawerContent title={t('editor.save_initial_draft')} on:close={() => closeDraftDrawer()}>
{#snippet actions()}
<div>
<Button
@@ -752,7 +753,7 @@
unifiedSize="md"
variant="accent"
>
Save initial draft
{t('editor.save_initial_draft')}
</Button>
</div>
{/snippet}
@@ -1079,7 +1080,7 @@
startIcon={{ icon: Bug }}
>
<div class="flex flex-row gap-1 items-center">
<div>Debug runs</div>
<div>{t('app.debug_runs')}</div>
<div class="text-2xs {hasErrors ? '' : 'text-primary'}"
>({$jobs?.length > 99 ? '99+' : ($jobs?.length ?? 0)})</div
>
@@ -1106,7 +1107,7 @@
disabled={!newApp && !savedApp}
shortCut={{ key: 'S' }}
>
Draft
{t('editor.draft')}
</Button>
<Button
variant="accent"
@@ -1117,7 +1118,7 @@
dropdownItems={$appPath != ''
? () => [
{
label: 'Fork',
label: t('action.fork'),
onClick: () => {
window.open(`/apps/add?template=${appPath}`)
}
@@ -1125,7 +1126,7 @@
...(!isCloudHosted() && !isRuleActive('DisableWorkspaceForking')
? [
{
label: 'Edit in workspace fork',
label: t('editor.edit_in_fork'),
onClick: () => {
window.open(buildForkEditUrl('app', $appPath))
}
@@ -1135,7 +1136,7 @@
]
: undefined}
>
Deploy
{t('editor.deploy')}
</Button>
</div>
</div>

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { Eye, Pen } from 'lucide-svelte'
import { getContext } from 'svelte'
import { t } from '$lib/i18n/t.svelte'
import type { AppViewerContext } from '../types'
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
@@ -24,7 +25,7 @@
>
{#snippet children({ item })}
<ToggleButton
label="Editor"
label={t('editor.editor')}
value="dnd"
icon={Pen}
disabled={loading}
@@ -33,7 +34,7 @@
{item}
/>
<ToggleButton
label="Preview"
label={t('editor.preview')}
value="preview"
icon={Eye}
tooltip="Preview mode"

View File

@@ -1,4 +1,5 @@
<script lang="ts">
import { t } from '$lib/i18n/t.svelte'
import DisplayResult from '$lib/components/DisplayResult.svelte'
import LogViewer from '$lib/components/LogViewer.svelte'
import { Splitpanes, Pane } from 'svelte-splitpanes'
@@ -58,7 +59,7 @@
{#if testIsLoading}
<Loader2 class="animate-spin" />
{:else}
Test to see the result here
{t('editor.test_to_see_result')}
{/if}
</div>
{/if}

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import ConfirmationModal from './ConfirmationModal.svelte'
import Button from '../button/Button.svelte'
import { t } from '$lib/i18n/t.svelte'
import { type Value } from '$lib/utils'
import type { DiffDrawerI } from '$lib/components/diff_drawer'
@@ -26,14 +27,14 @@
<ConfirmationModal
{open}
title={'New version deployed by ' + deployedBy}
confirmationText="Override"
confirmationText={t('confirm.override')}
on:canceled={() => {
open = false
}}
on:confirmed={() => confirmCallback()}
>
<div class="flex flex-col w-full space-y-4">
<span>A new version was deployed while you were editing this one.</span>
<span>{t('confirm.new_version_deployed')}</span>
{#if diffDrawer}
<Button
wrapperClasses="self-start"
@@ -51,12 +52,12 @@
current: currentValue,
title: 'Deployed <> Current',
button: {
text: 'Override anyway',
text: t('confirm.override_anyway'),
onClick: () => confirmCallback()
}
})
}}
>Show diff
>{t('editor.show_diff')}
</Button>
{/if}
</div>

View File

@@ -3,6 +3,7 @@
import { beforeNavigate } from '$app/navigation'
import { goto as gotoUrl } from '$app/navigation'
import Button from '../button/Button.svelte'
import { t } from '$lib/i18n/t.svelte'
import type DiffDrawer from '$lib/components/DiffDrawer.svelte'
import {
cleanValueProperties,
@@ -96,8 +97,8 @@
<ConfirmationModal
{open}
title="Unsaved changes detected"
confirmationText="Discard changes"
title={t('confirm.unsaved_changes_detected')}
confirmationText={t('confirm.discard_changes')}
on:canceled={() => {
open = false
}}
@@ -113,7 +114,7 @@
}}
>
<div class="flex flex-col w-full space-y-4">
<span>Are you sure you want to discard the changes you have made? </span>
<span>{t('confirm.discard_changes_question')} </span>
{#if savedValue && modifiedValue && diffDrawer}
<Button
wrapperClasses="self-start"
@@ -132,7 +133,7 @@
current: modifiedValue,
defaultDiffType: 'draft',
button: {
text: 'Leave anyway',
text: t('confirm.leave_anyway'),
onClick: () => {
if (goingTo) {
bypassBeforeNavigate = true
@@ -143,7 +144,7 @@
}
})
}}
>Show diff
>{t('editor.show_diff')}
</Button>
{/if}
</div>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { Button, Drawer, DrawerContent } from '$lib/components/common'
import { t } from '$lib/i18n/t.svelte'
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
import ScriptEditor from '$lib/components/ScriptEditor.svelte'
@@ -135,8 +136,8 @@
<ConfirmationModal
open={unsavedModalOpen}
title="Unsaved changes detected"
confirmationText="Discard changes"
title={t('confirm.unsaved_changes_detected')}
confirmationText={t('confirm.discard_changes')}
on:canceled={() => {
unsavedModalOpen = false
}}
@@ -148,7 +149,7 @@
}}
>
<div class="flex flex-col w-full space-y-4">
<span>Are you sure you want to discard the changes you have made? </span>
<span>{t('confirm.discard_changes_question')} </span>
<Button
wrapperClasses="self-start"
variant="default"
@@ -167,7 +168,7 @@
original: savedScript,
current: script,
button: {
text: 'Close anyway',
text: t('confirm.leave_anyway'),
onClick: () => {
closeAnyway = true
diffDrawer?.closeDrawer()
@@ -175,7 +176,7 @@
}
})
}}
>Show diff
>{t('editor.show_diff')}
</Button>
</div>
</ConfirmationModal>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { t } from '$lib/i18n/t.svelte'
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
import FlowPreviewContent from '$lib/components/FlowPreviewContent.svelte'
@@ -162,7 +163,7 @@
]
: undefined}
>
Test flow
{t('flow.test_flow')}
</Button>
{#if !loading}

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy'
import { t } from '$lib/i18n/t.svelte'
import {
type CompletedJob,
@@ -209,7 +210,7 @@
{#if previewIsLoading}
<Loader2 class="animate-spin" />
{:else}
Test to see the result here
{t('editor.test_to_see_result')}
{/if}
</span>
<Tooltip

View File

@@ -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">

View File

@@ -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 })}

View File

@@ -11,11 +11,15 @@
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, getLocale, setLocale } from '$lib/i18n/t.svelte'
import { SUPPORTED_LOCALES, LOCALE_NAMES } from '$lib/i18n/index'
let showLanguages = $state(false)
let darkMode: boolean = $state(false)
@@ -53,16 +57,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 +87,43 @@
{:else}
<Moon size={16} />
{/if}
Switch theme
{t('user.switch_theme')}
</MenuItem>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class={twMerge(itemClass, 'cursor-pointer')}
role="button"
tabindex="0"
onclick={(e) => {
e.stopPropagation()
showLanguages = !showLanguages
}}
>
<Languages size={16} />
{t('user.language')}: {LOCALE_NAMES[getLocale()]}
</div>
{#if showLanguages}
<div class="border-t border-b py-1">
{#each SUPPORTED_LOCALES as loc (loc)}
<MenuItem
onClick={() => setLocale(loc)}
class={twMerge(
itemClass,
'pl-8',
getLocale() === loc ? 'font-semibold text-primary' : ''
)}
{item}
>
{LOCALE_NAMES[loc]}
</MenuItem>
{/each}
</div>
{/if}
<MenuItem onClick={() => logout()} class={itemClass} {item}>
<LogOut size={16} />
Sign out
{t('user.sign_out')}
</MenuItem>
</div>
@@ -125,7 +160,7 @@
}}
{item}
>
Upgrade
{t('user.upgrade')}
</MenuItem>
{/if}
{:else}
@@ -136,7 +171,7 @@
}}
{item}
>
Premium plan
{t('user.premium_plan')}
</MenuItem>
{/if}
</div>

View File

@@ -0,0 +1,23 @@
export const SUPPORTED_LOCALES = ['en', 'fr', 'de', 'es', 'pt', 'ja', 'zh', 'ko'] as const
export type Locale = (typeof SUPPORTED_LOCALES)[number]
export const LOCALE_NAMES: Record<Locale, string> = {
en: 'English',
fr: 'Français',
de: 'Deutsch',
es: 'Español',
pt: 'Português',
ja: '日本語',
zh: '中文',
ko: '한국어'
}
/** Map browser language (e.g. "fr-FR") to the closest supported locale, fallback to 'en' */
export function detectLocale(): Locale {
if (typeof navigator === 'undefined') return 'en'
for (const lang of navigator.languages ?? [navigator.language]) {
const prefix = lang.split('-')[0].toLowerCase() as Locale
if (SUPPORTED_LOCALES.includes(prefix)) return prefix
}
return 'en'
}

View File

@@ -0,0 +1,264 @@
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',
'action.duplicate': 'Duplizieren',
'action.fork': 'Fork',
'action.rename': 'Umbenennen',
'action.move': 'Verschieben',
'action.copy_path': 'Pfad kopieren',
'action.export': 'Exportieren',
'action.undo': 'Rückgängig',
'action.redo': 'Wiederholen',
'action.test': 'Testen',
'action.view': 'Ansehen',
'action.next': 'Weiter',
'action.previous': 'Zurück',
'action.remove': 'Entfernen',
'action.enable': 'Aktivieren',
'action.disable': 'Deaktivieren',
'action.see_permissions': 'Berechtigungen',
'action.deploy_to_prod': 'Auf Prod deployen',
'action.refresh_token': 'Token erneuern',
'action.view_runs': 'Ausführungen',
'action.view_code': 'Code ansehen',
'action.audit_logs': 'Audit-Logs',
'action.schedule': 'Planen',
'action.versions': 'Versionen',
'action.delete_draft': 'Draft löschen',
'action.publish_to_hub': 'Im Hub veröffentlichen',
'action.run_now': 'Jetzt ausführen',
'action.manage': 'Verwalten',
// Page headers
'page.home': 'Start',
'page.variables': 'Variablen',
'page.resources': 'Ressourcen',
'page.schedules': 'Zeitpläne',
'page.workers': 'Workers',
'page.folders': 'Ordner',
'page.groups': 'Gruppen',
'page.runs': 'Ausführungen',
// Home page
'home.create_a': 'Erstellen:',
'home.workspace': 'Workspace',
'home.hub': 'Hub',
'home.scripts': 'Skripte',
'home.flows': 'Flows',
'home.apps': 'Apps',
'home.hub_flow': 'Hub-Flow',
'home.hub_app': 'Hub-App',
'home.view_on_hub': 'Im Hub ansehen',
'home.content': 'Inhalte',
// Variables page
'variables.new_variable': 'Neue Variable',
'variables.new_contextual': 'Neue Kontextvariable',
'variables.contextual': 'Kontext',
'variables.custom_contextual': 'Eigene Kontextvariablen',
'variables.contextual_vars': 'Kontextvariablen',
'variables.no_variables': 'Keine Variablen gefunden',
'variables.path': 'Pfad',
'variables.value': 'Wert',
'variables.description': 'Beschreibung',
// Resources page
'resources.new_resource': 'Neue Ressource',
// Schedules page
'schedules.duplicate': 'Zeitplan duplizieren',
'schedules.view_script': 'Skript ansehen',
'schedules.view_flow': 'Flow ansehen',
// Runs page
'runs.cancel_jobs': 'Jobs abbrechen',
'runs.rerun_jobs': 'Jobs neu starten',
'runs.cancel_all': 'Alle gefilterten Jobs abbrechen',
'runs.rerun_all': 'Alle gefilterten Jobs neu starten',
'runs.force_cancel': 'Erzwungen abbrechen',
// Editor - common
'editor.save': 'Speichern',
'editor.deploy': 'Deployen',
'editor.deploy_stay': 'Bereitstellen & Hier bleiben',
'editor.draft': 'Draft',
'editor.test': 'Testen',
'editor.test_and_record': 'Testen & aufzeichnen',
'editor.cancel': 'Abbrechen',
'editor.settings': 'Einstellungen',
'editor.history': 'Verlauf',
'editor.versions_history': 'Versionsverlauf',
'editor.deployment_history': 'Deployment-Verlauf',
'editor.edit_in_yaml': 'In YAML bearbeiten',
'editor.flow_settings': 'Flow-Einstellungen',
'editor.export': 'Exportieren',
'editor.export_yaml_json': 'Als YAML/JSON exportieren',
'editor.diff': 'Diff',
'editor.show_diff': 'Diff anzeigen',
'editor.editor': 'Editor',
'editor.preview': 'Vorschau',
'editor.main': 'Main',
'editor.preprocessor': 'Preprocessor',
'editor.diagram': 'Diagramm',
'editor.save_to_workspace': 'Im Workspace speichern',
'editor.save_initial_draft': 'Erstentwurf speichern',
'editor.exit_see_details': 'Beenden & Details',
'editor.edit_in_fork': 'Im Fork bearbeiten',
'editor.save_anyway': 'Trotzdem speichern',
'editor.invite_others': 'Andere einladen',
'editor.deployment_message': 'Bereitstellungsnachricht',
'editor.test_to_see_result': 'Testen, um das Ergebnis hier zu sehen',
'editor.no_logs_available': 'Noch keine Logs verfügbar',
'editor.waiting_for_job': 'Warten auf Jobstart...',
// Flow editor
'flow.test_flow': 'Flow testen',
'flow.test_flow_record': 'Flow testen & aufzeichnen',
'flow.build_a_flow': 'Flow erstellen',
'flow.fix_broken_flow': 'Flow reparieren',
'flow.reset_tutorials': 'Tutorials zurücksetzen',
'flow.skip_tutorials': 'Tutorials überspringen',
'flow.create_group': 'Gruppe erstellen',
'flow.test_iteration': 'Iteration testen',
'flow.skip_failures': 'Fehler überspringen',
'flow.run_parallel': 'Parallel ausführen',
// App editor
'app.public_url': 'Öffentliche URL',
'app.app_inputs': 'App-Eingaben',
'app.schedule_reports': 'Berichte planen',
'app.troubleshoot': 'Fehlerbehebung',
'app.lazy_mode': 'Lazy-Modus',
'app.hub_export': 'Hub-Export',
'app.debug_runs': 'Debug-Ausführungen',
// Status labels
'status.default': 'Standard',
'status.primary': 'Primär',
'status.modified': 'Geändert',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'Workspace',
'tab.contextual': 'Kontext',
'tab.hub': 'Hub',
'tab.scripts': 'Skripte',
'tab.flows': 'Flows',
'tab.apps': 'Apps',
// Row actions (scripts/flows/apps)
'row.view_json': 'JSON/YAML ansehen',
'row.move_rename': 'Verschieben/Umbenennen',
'row.duplicate_fork': 'Duplizieren/Fork',
'row.go_to_public_page': 'Öffentliche Seite',
'row.deployments': 'Deployments',
// Confirmation dialogs
'confirm.delete_forever': 'Endgültig löschen',
'confirm.empty_trashbin': 'Papierkorb leeren',
'confirm.discard_changes': 'Änderungen verwerfen',
'confirm.override': 'Überschreiben',
'confirm.unsaved_changes_detected': 'Nicht gespeicherte Änderungen erkannt',
'confirm.discard_changes_question':
'Sind Sie sicher, dass Sie die vorgenommenen Änderungen verwerfen möchten?',
'confirm.leave_anyway': 'Trotzdem verlassen',
'confirm.override_anyway': 'Trotzdem überschreiben',
'confirm.new_version_deployed':
'Eine neue Version wurde bereitgestellt, während Sie diese bearbeitet haben.',
// Groups/Folders
'groups.manage_group': 'Gruppe verwalten',
'groups.new_group': 'Neue Gruppe',
'folders.manage_folder': 'Ordner verwalten',
'folders.new_folder': 'Neuer Ordner',
// Triggers
'triggers.suspend_job': 'Job pausieren',
'triggers.more_triggers': 'Weitere Trigger',
'triggers.scheduled_poll': 'Geplante Abfrage',
// Misc
'misc.all_workspaces': 'Alle Workspaces',
'misc.instance_settings': 'Instanzeinstellungen',
'misc.search': 'Suchen',
'misc.filter': 'Filtern',
'misc.tree_view': 'Baumansicht',
'misc.loading': 'Laden...'
}

View File

@@ -0,0 +1,262 @@
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',
'action.duplicate': 'Duplicate',
'action.fork': 'Fork',
'action.rename': 'Rename',
'action.move': 'Move',
'action.copy_path': 'Copy path',
'action.export': 'Export',
'action.undo': 'Undo',
'action.redo': 'Redo',
'action.test': 'Test',
'action.view': 'View',
'action.next': 'Next',
'action.previous': 'Previous',
'action.remove': 'Remove',
'action.enable': 'Enable',
'action.disable': 'Disable',
'action.see_permissions': 'See Permissions',
'action.deploy_to_prod': 'Deploy to prod/staging',
'action.refresh_token': 'Refresh token',
'action.view_runs': 'View runs',
'action.view_code': 'View code',
'action.audit_logs': 'Audit logs',
'action.schedule': 'Schedule',
'action.versions': 'Versions',
'action.delete_draft': 'Delete Draft',
'action.publish_to_hub': 'Publish to Hub',
'action.run_now': 'Run now',
'action.manage': 'Manage',
// Page headers
'page.home': 'Home',
'page.variables': 'Variables',
'page.resources': 'Resources',
'page.schedules': 'Schedules',
'page.workers': 'Workers',
'page.folders': 'Folders',
'page.groups': 'Groups',
'page.runs': 'Runs',
// Home page
'home.create_a': 'Create a',
'home.workspace': 'Workspace',
'home.hub': 'Hub',
'home.scripts': 'Scripts',
'home.flows': 'Flows',
'home.apps': 'Apps',
'home.hub_flow': 'Hub flow',
'home.hub_app': 'Hub app',
'home.view_on_hub': 'View on the Hub',
'home.content': 'Content',
// Variables page
'variables.new_variable': 'New variable',
'variables.new_contextual': 'New contextual variable',
'variables.contextual': 'Contextual',
'variables.custom_contextual': 'Custom contextual variables',
'variables.contextual_vars': 'Contextual variables',
'variables.no_variables': 'No variables found',
'variables.path': 'Path',
'variables.value': 'Value',
'variables.description': 'Description',
// Resources page
'resources.new_resource': 'New resource',
// Schedules page
'schedules.duplicate': 'Duplicate schedule',
'schedules.view_script': 'View Script',
'schedules.view_flow': 'View Flow',
// Runs page
'runs.cancel_jobs': 'Cancel jobs',
'runs.rerun_jobs': 'Re-run jobs',
'runs.cancel_all': 'Cancel all jobs matching filters',
'runs.rerun_all': 'Re-run all jobs matching filters',
'runs.force_cancel': 'Force Cancel',
// Editor - common
'editor.save': 'Save',
'editor.deploy': 'Deploy',
'editor.deploy_stay': 'Deploy & Stay here',
'editor.draft': 'Draft',
'editor.test': 'Test',
'editor.test_and_record': 'Test & record',
'editor.cancel': 'Cancel',
'editor.settings': 'Settings',
'editor.history': 'History',
'editor.versions_history': 'Versions History',
'editor.deployment_history': 'Deployment History',
'editor.edit_in_yaml': 'Edit in YAML',
'editor.flow_settings': 'Flow settings',
'editor.export': 'Export',
'editor.export_yaml_json': 'Export as YAML/JSON',
'editor.diff': 'Diff',
'editor.show_diff': 'Show diff',
'editor.editor': 'Editor',
'editor.preview': 'Preview',
'editor.main': 'Main',
'editor.preprocessor': 'Preprocessor',
'editor.diagram': 'Diagram',
'editor.save_to_workspace': 'Save to workspace',
'editor.save_initial_draft': 'Save initial draft',
'editor.exit_see_details': 'Exit & see details',
'editor.edit_in_fork': 'Edit in workspace fork',
'editor.save_anyway': 'Save anyway',
'editor.invite_others': 'Invite others',
'editor.deployment_message': 'Deployment message',
'editor.test_to_see_result': 'Test to see the result here',
'editor.no_logs_available': 'No logs are available yet',
'editor.waiting_for_job': 'Waiting for job to start...',
// Flow editor
'flow.test_flow': 'Test flow',
'flow.test_flow_record': 'Test flow & record',
'flow.build_a_flow': 'Build a flow',
'flow.fix_broken_flow': 'Fix a broken flow',
'flow.reset_tutorials': 'Reset tutorials',
'flow.skip_tutorials': 'Skip tutorials',
'flow.create_group': 'Create group',
'flow.test_iteration': 'Test an iteration',
'flow.skip_failures': 'Skip failures',
'flow.run_parallel': 'Run in parallel',
// App editor
'app.public_url': 'Public URL',
'app.app_inputs': 'App inputs',
'app.schedule_reports': 'Schedule reports',
'app.troubleshoot': 'Troubleshoot panel',
'app.lazy_mode': 'Lazy mode',
'app.hub_export': 'Hub export',
'app.debug_runs': 'Debug runs',
// Status labels
'status.default': 'Default',
'status.primary': 'Primary',
'status.modified': 'Modified',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'Workspace',
'tab.contextual': 'Contextual',
'tab.hub': 'Hub',
'tab.scripts': 'Scripts',
'tab.flows': 'Flows',
'tab.apps': 'Apps',
// Row actions (scripts/flows/apps)
'row.view_json': 'View JSON/YAML',
'row.move_rename': 'Move/Rename',
'row.duplicate_fork': 'Duplicate/Fork',
'row.go_to_public_page': 'Go to public page',
'row.deployments': 'Deployments',
// Confirmation dialogs
'confirm.delete_forever': 'Delete forever',
'confirm.empty_trashbin': 'Empty trashbin',
'confirm.discard_changes': 'Discard changes',
'confirm.override': 'Override',
'confirm.unsaved_changes_detected': 'Unsaved changes detected',
'confirm.discard_changes_question': 'Are you sure you want to discard the changes you have made?',
'confirm.leave_anyway': 'Leave anyway',
'confirm.override_anyway': 'Override anyway',
'confirm.new_version_deployed': 'A new version was deployed while you were editing this one.',
// Groups/Folders
'groups.manage_group': 'Manage group',
'groups.new_group': 'New group',
'folders.manage_folder': 'Manage folder',
'folders.new_folder': 'New folder',
// Triggers
'triggers.suspend_job': 'Suspend job execution',
'triggers.more_triggers': 'More triggers',
'triggers.scheduled_poll': 'Scheduled Poll',
// Misc
'misc.all_workspaces': 'All workspaces',
'misc.instance_settings': 'Instance settings',
'misc.search': 'Search',
'misc.filter': 'Filter',
'misc.tree_view': 'Tree view',
'misc.loading': 'Loading...'
} as const
export type MessageKey = keyof typeof en

View File

@@ -0,0 +1,263 @@
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',
'action.duplicate': 'Duplicar',
'action.fork': 'Fork',
'action.rename': 'Renombrar',
'action.move': 'Mover',
'action.copy_path': 'Copiar ruta',
'action.export': 'Exportar',
'action.undo': 'Deshacer',
'action.redo': 'Rehacer',
'action.test': 'Probar',
'action.view': 'Ver',
'action.next': 'Siguiente',
'action.previous': 'Anterior',
'action.remove': 'Quitar',
'action.enable': 'Activar',
'action.disable': 'Desactivar',
'action.see_permissions': 'Ver permisos',
'action.deploy_to_prod': 'Desplegar a prod/staging',
'action.refresh_token': 'Renovar token',
'action.view_runs': 'Ver ejecuciones',
'action.view_code': 'Ver código',
'action.audit_logs': 'Auditoría',
'action.schedule': 'Programar',
'action.versions': 'Versiones',
'action.delete_draft': 'Eliminar Draft',
'action.publish_to_hub': 'Publicar en Hub',
'action.run_now': 'Ejecutar ahora',
'action.manage': 'Gestionar',
// Page headers
'page.home': 'Inicio',
'page.variables': 'Variables',
'page.resources': 'Recursos',
'page.schedules': 'Programados',
'page.workers': 'Workers',
'page.folders': 'Carpetas',
'page.groups': 'Grupos',
'page.runs': 'Ejecuciones',
// Home page
'home.create_a': 'Crear un',
'home.workspace': 'Espacio',
'home.hub': 'Hub',
'home.scripts': 'Scripts',
'home.flows': 'Flujos',
'home.apps': 'Apps',
'home.hub_flow': 'Flujo de Hub',
'home.hub_app': 'App de Hub',
'home.view_on_hub': 'Ver en el Hub',
'home.content': 'Contenido',
// Variables page
'variables.new_variable': 'Nueva variable',
'variables.new_contextual': 'Nueva var. contextual',
'variables.contextual': 'Contextual',
'variables.custom_contextual': 'Variables contextuales',
'variables.contextual_vars': 'Variables contextuales',
'variables.no_variables': 'No se encontraron variables',
'variables.path': 'Ruta',
'variables.value': 'Valor',
'variables.description': 'Descripción',
// Resources page
'resources.new_resource': 'Nuevo recurso',
// Schedules page
'schedules.duplicate': 'Duplicar programa',
'schedules.view_script': 'Ver Script',
'schedules.view_flow': 'Ver Flujo',
// Runs page
'runs.cancel_jobs': 'Cancelar trabajos',
'runs.rerun_jobs': 'Reiniciar trabajos',
'runs.cancel_all': 'Cancelar todos según filtros',
'runs.rerun_all': 'Reiniciar todos según filtros',
'runs.force_cancel': 'Forzar cancelación',
// Editor - common
'editor.save': 'Guardar',
'editor.deploy': 'Desplegar',
'editor.deploy_stay': 'Desplegar y Quedarse aquí',
'editor.draft': 'Draft',
'editor.test': 'Probar',
'editor.test_and_record': 'Probar y grabar',
'editor.cancel': 'Cancelar',
'editor.settings': 'Configuración',
'editor.history': 'Historial',
'editor.versions_history': 'Historial de versiones',
'editor.deployment_history': 'Historial de despliegues',
'editor.edit_in_yaml': 'Editar en YAML',
'editor.flow_settings': 'Ajustes del flujo',
'editor.export': 'Exportar',
'editor.export_yaml_json': 'Exportar como YAML/JSON',
'editor.diff': 'Diff',
'editor.show_diff': 'Mostrar diferencias',
'editor.editor': 'Editor',
'editor.preview': 'Vista previa',
'editor.main': 'Principal',
'editor.preprocessor': 'Preprocesador',
'editor.diagram': 'Diagrama',
'editor.save_to_workspace': 'Guardar en espacio',
'editor.save_initial_draft': 'Guardar borrador inicial',
'editor.exit_see_details': 'Salir y ver detalles',
'editor.edit_in_fork': 'Editar en fork',
'editor.save_anyway': 'Guardar de todos modos',
'editor.invite_others': 'Invitar a otros',
'editor.deployment_message': 'Mensaje de despliegue',
'editor.test_to_see_result': 'Prueba para ver el resultado aquí',
'editor.no_logs_available': 'Aún no hay logs disponibles',
'editor.waiting_for_job': 'Esperando a que inicie el trabajo...',
// Flow editor
'flow.test_flow': 'Probar flujo',
'flow.test_flow_record': 'Probar flujo y grabar',
'flow.build_a_flow': 'Crear un flujo',
'flow.fix_broken_flow': 'Reparar flujo roto',
'flow.reset_tutorials': 'Reiniciar tutoriales',
'flow.skip_tutorials': 'Omitir tutoriales',
'flow.create_group': 'Crear grupo',
'flow.test_iteration': 'Probar iteración',
'flow.skip_failures': 'Omitir errores',
'flow.run_parallel': 'Ejecutar en paralelo',
// App editor
'app.public_url': 'URL pública',
'app.app_inputs': 'Entradas de app',
'app.schedule_reports': 'Programar informes',
'app.troubleshoot': 'Panel de diagnóstico',
'app.lazy_mode': 'Modo lazy',
'app.hub_export': 'Exportar a Hub',
'app.debug_runs': 'Ejecuciones de depuración',
// Status labels
'status.default': 'Por defecto',
'status.primary': 'Primario',
'status.modified': 'Modificado',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'Espacio',
'tab.contextual': 'Contextual',
'tab.hub': 'Hub',
'tab.scripts': 'Scripts',
'tab.flows': 'Flujos',
'tab.apps': 'Apps',
// Row actions (scripts/flows/apps)
'row.view_json': 'Ver JSON/YAML',
'row.move_rename': 'Mover/Renombrar',
'row.duplicate_fork': 'Duplicar/Fork',
'row.go_to_public_page': 'Ir a página pública',
'row.deployments': 'Despliegues',
// Confirmation dialogs
'confirm.delete_forever': 'Eliminar para siempre',
'confirm.empty_trashbin': 'Vaciar papelera',
'confirm.discard_changes': 'Descartar cambios',
'confirm.override': 'Sobrescribir',
'confirm.unsaved_changes_detected': 'Cambios no guardados detectados',
'confirm.discard_changes_question':
'¿Estás seguro de que deseas descartar los cambios realizados?',
'confirm.leave_anyway': 'Salir de todos modos',
'confirm.override_anyway': 'Sobrescribir de todos modos',
'confirm.new_version_deployed': 'Se desplegó una nueva versión mientras editabas esta.',
// Groups/Folders
'groups.manage_group': 'Gestionar grupo',
'groups.new_group': 'Nuevo grupo',
'folders.manage_folder': 'Gestionar carpeta',
'folders.new_folder': 'Nueva carpeta',
// Triggers
'triggers.suspend_job': 'Suspender ejecución',
'triggers.more_triggers': 'Más disparadores',
'triggers.scheduled_poll': 'Sondeo programado',
// Misc
'misc.all_workspaces': 'Todos los espacios',
'misc.instance_settings': 'Ajustes de instancia',
'misc.search': 'Buscar',
'misc.filter': 'Filtrar',
'misc.tree_view': 'Vista de árbol',
'misc.loading': 'Cargando...'
}

View File

@@ -0,0 +1,264 @@
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',
'action.duplicate': 'Dupliquer',
'action.fork': 'Fork',
'action.rename': 'Renommer',
'action.move': 'Déplacer',
'action.copy_path': 'Copier le chemin',
'action.export': 'Exporter',
'action.undo': 'Annuler',
'action.redo': 'Rétablir',
'action.test': 'Tester',
'action.view': 'Voir',
'action.next': 'Suivant',
'action.previous': 'Précédent',
'action.remove': 'Retirer',
'action.enable': 'Activer',
'action.disable': 'Désactiver',
'action.see_permissions': 'Voir les permissions',
'action.deploy_to_prod': 'Déployer en prod/staging',
'action.refresh_token': 'Rafraîchir le token',
'action.view_runs': 'Voir les exécutions',
'action.view_code': 'Voir le code',
'action.audit_logs': "Journaux d'audit",
'action.schedule': 'Planifier',
'action.versions': 'Versions',
'action.delete_draft': 'Supprimer le Draft',
'action.publish_to_hub': 'Publier sur le Hub',
'action.run_now': 'Lancer maintenant',
'action.manage': 'Gérer',
// Page headers
'page.home': 'Accueil',
'page.variables': 'Variables',
'page.resources': 'Ressources',
'page.schedules': 'Plannings',
'page.workers': 'Workers',
'page.folders': 'Dossiers',
'page.groups': 'Groupes',
'page.runs': 'Exécutions',
// Home page
'home.create_a': 'Créer un',
'home.workspace': 'Espace',
'home.hub': 'Hub',
'home.scripts': 'Scripts',
'home.flows': 'Flows',
'home.apps': 'Apps',
'home.hub_flow': 'Flow du Hub',
'home.hub_app': 'App du Hub',
'home.view_on_hub': 'Voir sur le Hub',
'home.content': 'Contenu',
// Variables page
'variables.new_variable': 'Nouvelle variable',
'variables.new_contextual': 'Nouvelle var. contextuelle',
'variables.contextual': 'Contextuelle',
'variables.custom_contextual': 'Variables contextuelles',
'variables.contextual_vars': 'Variables contextuelles',
'variables.no_variables': 'Aucune variable trouvée',
'variables.path': 'Chemin',
'variables.value': 'Valeur',
'variables.description': 'Description',
// Resources page
'resources.new_resource': 'Nouvelle ressource',
// Schedules page
'schedules.duplicate': 'Dupliquer le planning',
'schedules.view_script': 'Voir le script',
'schedules.view_flow': 'Voir le flow',
// Runs page
'runs.cancel_jobs': 'Annuler les jobs',
'runs.rerun_jobs': 'Relancer les jobs',
'runs.cancel_all': 'Annuler tous les jobs filtrés',
'runs.rerun_all': 'Relancer tous les jobs filtrés',
'runs.force_cancel': "Forcer l'annulation",
// Editor - common
'editor.save': 'Enregistrer',
'editor.deploy': 'Déployer',
'editor.deploy_stay': 'Déployer & Rester ici',
'editor.draft': 'Draft',
'editor.test': 'Tester',
'editor.test_and_record': 'Tester & enregistrer',
'editor.cancel': 'Annuler',
'editor.settings': 'Paramètres',
'editor.history': 'Historique',
'editor.versions_history': 'Historique des versions',
'editor.deployment_history': 'Historique des déploiements',
'editor.edit_in_yaml': 'Éditer en YAML',
'editor.flow_settings': 'Paramètres du flow',
'editor.export': 'Exporter',
'editor.export_yaml_json': 'Exporter en YAML/JSON',
'editor.diff': 'Diff',
'editor.show_diff': 'Afficher le diff',
'editor.editor': 'Éditeur',
'editor.preview': 'Aperçu',
'editor.main': 'Principal',
'editor.preprocessor': 'Préprocesseur',
'editor.diagram': 'Diagramme',
'editor.save_to_workspace': "Enregistrer dans l'espace",
'editor.save_initial_draft': 'Sauvegarder le brouillon initial',
'editor.exit_see_details': 'Quitter & voir détails',
'editor.edit_in_fork': 'Éditer dans le fork',
'editor.save_anyway': 'Enregistrer quand même',
'editor.invite_others': "Inviter d'autres",
'editor.deployment_message': 'Message de déploiement',
'editor.test_to_see_result': 'Testez pour voir le résultat ici',
'editor.no_logs_available': 'Aucun log disponible pour le moment',
'editor.waiting_for_job': 'En attente du démarrage du job...',
// Flow editor
'flow.test_flow': 'Tester le flow',
'flow.test_flow_record': 'Tester & enregistrer',
'flow.build_a_flow': 'Créer un flow',
'flow.fix_broken_flow': 'Réparer un flow',
'flow.reset_tutorials': 'Réinitialiser les tutoriels',
'flow.skip_tutorials': 'Passer les tutoriels',
'flow.create_group': 'Créer un groupe',
'flow.test_iteration': 'Tester une itération',
'flow.skip_failures': 'Ignorer les erreurs',
'flow.run_parallel': 'Lancer en parallèle',
// App editor
'app.public_url': 'URL publique',
'app.app_inputs': "Entrées de l'app",
'app.schedule_reports': 'Planifier les rapports',
'app.troubleshoot': 'Dépannage',
'app.lazy_mode': 'Mode lazy',
'app.hub_export': 'Export Hub',
'app.debug_runs': 'Exécutions debug',
// Status labels
'status.default': 'Défaut',
'status.primary': 'Principal',
'status.modified': 'Modifié',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'Espace',
'tab.contextual': 'Contextuel',
'tab.hub': 'Hub',
'tab.scripts': 'Scripts',
'tab.flows': 'Flows',
'tab.apps': 'Apps',
// Row actions (scripts/flows/apps)
'row.view_json': 'Voir JSON/YAML',
'row.move_rename': 'Déplacer/Renommer',
'row.duplicate_fork': 'Dupliquer/Fork',
'row.go_to_public_page': 'Aller à la page publique',
'row.deployments': 'Déploiements',
// Confirmation dialogs
'confirm.delete_forever': 'Supprimer définitivement',
'confirm.empty_trashbin': 'Vider la corbeille',
'confirm.discard_changes': 'Abandonner les modifications',
'confirm.override': 'Écraser',
'confirm.unsaved_changes_detected': 'Modifications non sauvegardées détectées',
'confirm.discard_changes_question':
'Êtes-vous sûr de vouloir annuler les modifications effectuées ?',
'confirm.leave_anyway': 'Quitter quand même',
'confirm.override_anyway': 'Remplacer quand même',
'confirm.new_version_deployed':
'Une nouvelle version a été déployée pendant que vous éditiez celle-ci.',
// Groups/Folders
'groups.manage_group': 'Gérer le groupe',
'groups.new_group': 'Nouveau groupe',
'folders.manage_folder': 'Gérer le dossier',
'folders.new_folder': 'Nouveau dossier',
// Triggers
'triggers.suspend_job': "Suspendre l'exécution",
'triggers.more_triggers': 'Plus de déclencheurs',
'triggers.scheduled_poll': 'Sondage planifié',
// Misc
'misc.all_workspaces': 'Tous les espaces',
'misc.instance_settings': "Paramètres d'instance",
'misc.search': 'Rechercher',
'misc.filter': 'Filtrer',
'misc.tree_view': 'Vue arborescente',
'misc.loading': 'Chargement...'
}

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
}

View File

@@ -0,0 +1,262 @@
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': '更新',
'action.duplicate': '複製',
'action.fork': 'フォーク',
'action.rename': '名前変更',
'action.move': '移動',
'action.copy_path': 'パスをコピー',
'action.export': 'エクスポート',
'action.undo': '元に戻す',
'action.redo': 'やり直す',
'action.test': 'テスト',
'action.view': '表示',
'action.next': '次へ',
'action.previous': '前へ',
'action.remove': '削除',
'action.enable': '有効化',
'action.disable': '無効化',
'action.see_permissions': '権限を表示',
'action.deploy_to_prod': '本番/ステージングにデプロイ',
'action.refresh_token': 'トークンを更新',
'action.view_runs': '実行を表示',
'action.view_code': 'コードを表示',
'action.audit_logs': '監査ログ',
'action.schedule': 'スケジュール',
'action.versions': 'バージョン',
'action.delete_draft': 'Draftを削除',
'action.publish_to_hub': 'Hubに公開',
'action.run_now': '今すぐ実行',
'action.manage': '管理',
// Page headers
'page.home': 'ホーム',
'page.variables': '変数',
'page.resources': 'リソース',
'page.schedules': 'スケジュール',
'page.workers': 'ワーカー',
'page.folders': 'フォルダ',
'page.groups': 'グループ',
'page.runs': '実行',
// Home page
'home.create_a': '作成:',
'home.workspace': 'ワークスペース',
'home.hub': 'Hub',
'home.scripts': 'スクリプト',
'home.flows': 'フロー',
'home.apps': 'アプリ',
'home.hub_flow': 'Hubフロー',
'home.hub_app': 'Hubアプリ',
'home.view_on_hub': 'Hubで表示',
'home.content': 'コンテンツ',
// Variables page
'variables.new_variable': '新しい変数',
'variables.new_contextual': '新しいコンテキスト変数',
'variables.contextual': 'コンテキスト',
'variables.custom_contextual': 'カスタムコンテキスト変数',
'variables.contextual_vars': 'コンテキスト変数',
'variables.no_variables': '変数が見つかりません',
'variables.path': 'パス',
'variables.value': '値',
'variables.description': '説明',
// Resources page
'resources.new_resource': '新しいリソース',
// Schedules page
'schedules.duplicate': 'スケジュールを複製',
'schedules.view_script': 'スクリプトを表示',
'schedules.view_flow': 'フローを表示',
// Runs page
'runs.cancel_jobs': 'ジョブをキャンセル',
'runs.rerun_jobs': 'ジョブを再実行',
'runs.cancel_all': 'フィルター一致のジョブを全てキャンセル',
'runs.rerun_all': 'フィルター一致のジョブを全て再実行',
'runs.force_cancel': '強制キャンセル',
// Editor - common
'editor.save': '保存',
'editor.deploy': 'デプロイ',
'editor.deploy_stay': 'デプロイしてここに留まる',
'editor.draft': 'Draft',
'editor.test': 'テスト',
'editor.test_and_record': 'テストして記録',
'editor.cancel': 'キャンセル',
'editor.settings': '設定',
'editor.history': '履歴',
'editor.versions_history': 'バージョン履歴',
'editor.deployment_history': 'デプロイ履歴',
'editor.edit_in_yaml': 'YAMLで編集',
'editor.flow_settings': 'フロー設定',
'editor.export': 'エクスポート',
'editor.export_yaml_json': 'YAML/JSONとしてエクスポート',
'editor.diff': '差分',
'editor.show_diff': '差分を表示',
'editor.editor': 'エディタ',
'editor.preview': 'プレビュー',
'editor.main': 'メイン',
'editor.preprocessor': 'プリプロセッサ',
'editor.diagram': 'ダイアグラム',
'editor.save_to_workspace': 'ワークスペースに保存',
'editor.save_initial_draft': '初期ドラフトを保存',
'editor.exit_see_details': '終了して詳細を表示',
'editor.edit_in_fork': 'フォークで編集',
'editor.save_anyway': 'そのまま保存',
'editor.invite_others': '他のユーザーを招待',
'editor.deployment_message': 'デプロイメッセージ',
'editor.test_to_see_result': 'テストして結果をここで確認',
'editor.no_logs_available': 'ログはまだありません',
'editor.waiting_for_job': 'ジョブの開始を待機中...',
// Flow editor
'flow.test_flow': 'フローをテスト',
'flow.test_flow_record': 'フローをテストして記録',
'flow.build_a_flow': 'フローを作成',
'flow.fix_broken_flow': 'エラーのあるフローを修正',
'flow.reset_tutorials': 'チュートリアルをリセット',
'flow.skip_tutorials': 'チュートリアルをスキップ',
'flow.create_group': 'グループを作成',
'flow.test_iteration': 'イテレーションをテスト',
'flow.skip_failures': '失敗をスキップ',
'flow.run_parallel': '並列実行',
// App editor
'app.public_url': '公開URL',
'app.app_inputs': 'アプリ入力',
'app.schedule_reports': 'レポートをスケジュール',
'app.troubleshoot': 'トラブルシュートパネル',
'app.lazy_mode': '遅延モード',
'app.hub_export': 'Hubエクスポート',
'app.debug_runs': 'デバッグ実行',
// Status labels
'status.default': 'デフォルト',
'status.primary': 'プライマリ',
'status.modified': '変更済み',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'ワークスペース',
'tab.contextual': 'コンテキスト',
'tab.hub': 'Hub',
'tab.scripts': 'スクリプト',
'tab.flows': 'フロー',
'tab.apps': 'アプリ',
// Row actions (scripts/flows/apps)
'row.view_json': 'JSON/YAMLを表示',
'row.move_rename': '移動/名前変更',
'row.duplicate_fork': '複製/フォーク',
'row.go_to_public_page': '公開ページを表示',
'row.deployments': 'デプロイ',
// Confirmation dialogs
'confirm.delete_forever': '完全に削除',
'confirm.empty_trashbin': 'ゴミ箱を空にする',
'confirm.discard_changes': '変更を破棄',
'confirm.override': '上書き',
'confirm.unsaved_changes_detected': '未保存の変更が検出されました',
'confirm.discard_changes_question': '行った変更を破棄してもよろしいですか?',
'confirm.leave_anyway': 'それでも離れる',
'confirm.override_anyway': 'それでも上書きする',
'confirm.new_version_deployed': '編集中に新しいバージョンがデプロイされました。',
// Groups/Folders
'groups.manage_group': 'グループを管理',
'groups.new_group': '新しいグループ',
'folders.manage_folder': 'フォルダを管理',
'folders.new_folder': '新しいフォルダ',
// Triggers
'triggers.suspend_job': 'ジョブ実行を一時停止',
'triggers.more_triggers': 'その他のトリガー',
'triggers.scheduled_poll': 'スケジュールポーリング',
// Misc
'misc.all_workspaces': '全ワークスペース',
'misc.instance_settings': 'インスタンス設定',
'misc.search': '検索',
'misc.filter': 'フィルター',
'misc.tree_view': 'ツリー表示',
'misc.loading': '読み込み中...'
}

View File

@@ -0,0 +1,262 @@
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': '새로고침',
'action.duplicate': '복제',
'action.fork': '포크',
'action.rename': '이름 변경',
'action.move': '이동',
'action.copy_path': '경로 복사',
'action.export': '내보내기',
'action.undo': '실행 취소',
'action.redo': '다시 실행',
'action.test': '테스트',
'action.view': '보기',
'action.next': '다음',
'action.previous': '이전',
'action.remove': '제거',
'action.enable': '활성화',
'action.disable': '비활성화',
'action.see_permissions': '권한 보기',
'action.deploy_to_prod': '프로덕션/스테이징에 배포',
'action.refresh_token': '토큰 갱신',
'action.view_runs': '실행 보기',
'action.view_code': '코드 보기',
'action.audit_logs': '감사 로그',
'action.schedule': '스케줄',
'action.versions': '버전',
'action.delete_draft': 'Draft 삭제',
'action.publish_to_hub': 'Hub에 게시',
'action.run_now': '지금 실행',
'action.manage': '관리',
// Page headers
'page.home': '홈',
'page.variables': '변수',
'page.resources': '리소스',
'page.schedules': '스케줄',
'page.workers': '워커',
'page.folders': '폴더',
'page.groups': '그룹',
'page.runs': '실행',
// Home page
'home.create_a': '생성:',
'home.workspace': '워크스페이스',
'home.hub': 'Hub',
'home.scripts': '스크립트',
'home.flows': '플로우',
'home.apps': '앱',
'home.hub_flow': 'Hub 플로우',
'home.hub_app': 'Hub 앱',
'home.view_on_hub': 'Hub에서 보기',
'home.content': '콘텐츠',
// Variables page
'variables.new_variable': '새 변수',
'variables.new_contextual': '새 컨텍스트 변수',
'variables.contextual': '컨텍스트',
'variables.custom_contextual': '사용자 정의 컨텍스트 변수',
'variables.contextual_vars': '컨텍스트 변수',
'variables.no_variables': '변수를 찾을 수 없습니다',
'variables.path': '경로',
'variables.value': '값',
'variables.description': '설명',
// Resources page
'resources.new_resource': '새 리소스',
// Schedules page
'schedules.duplicate': '스케줄 복제',
'schedules.view_script': '스크립트 보기',
'schedules.view_flow': '플로우 보기',
// Runs page
'runs.cancel_jobs': '작업 취소',
'runs.rerun_jobs': '작업 재실행',
'runs.cancel_all': '필터와 일치하는 모든 작업 취소',
'runs.rerun_all': '필터와 일치하는 모든 작업 재실행',
'runs.force_cancel': '강제 취소',
// Editor - common
'editor.save': '저장',
'editor.deploy': '배포',
'editor.deploy_stay': '배포 후 여기 머물기',
'editor.draft': 'Draft',
'editor.test': '테스트',
'editor.test_and_record': '테스트 및 기록',
'editor.cancel': '취소',
'editor.settings': '설정',
'editor.history': '히스토리',
'editor.versions_history': '버전 히스토리',
'editor.deployment_history': '배포 히스토리',
'editor.edit_in_yaml': 'YAML로 편집',
'editor.flow_settings': '플로우 설정',
'editor.export': '내보내기',
'editor.export_yaml_json': 'YAML/JSON으로 내보내기',
'editor.diff': '차이점',
'editor.show_diff': '차이점 보기',
'editor.editor': '편집기',
'editor.preview': '미리보기',
'editor.main': '메인',
'editor.preprocessor': '전처리기',
'editor.diagram': '다이어그램',
'editor.save_to_workspace': '워크스페이스에 저장',
'editor.save_initial_draft': '초기 드래프트 저장',
'editor.exit_see_details': '종료 후 상세 보기',
'editor.edit_in_fork': '포크에서 편집',
'editor.save_anyway': '그래도 저장',
'editor.invite_others': '다른 사용자 초대',
'editor.deployment_message': '배포 메시지',
'editor.test_to_see_result': '테스트하여 결과를 여기에서 확인',
'editor.no_logs_available': '아직 사용 가능한 로그가 없습니다',
'editor.waiting_for_job': '작업 시작 대기 중...',
// Flow editor
'flow.test_flow': '플로우 테스트',
'flow.test_flow_record': '플로우 테스트 및 기록',
'flow.build_a_flow': '플로우 만들기',
'flow.fix_broken_flow': '오류 플로우 수정',
'flow.reset_tutorials': '튜토리얼 초기화',
'flow.skip_tutorials': '튜토리얼 건너뛰기',
'flow.create_group': '그룹 생성',
'flow.test_iteration': '반복 테스트',
'flow.skip_failures': '실패 건너뛰기',
'flow.run_parallel': '병렬 실행',
// App editor
'app.public_url': '공개 URL',
'app.app_inputs': '앱 입력',
'app.schedule_reports': '보고서 예약',
'app.troubleshoot': '문제 해결 패널',
'app.lazy_mode': '지연 모드',
'app.hub_export': 'Hub 내보내기',
'app.debug_runs': '디버그 실행',
// Status labels
'status.default': '기본',
'status.primary': '주요',
'status.modified': '수정됨',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': '워크스페이스',
'tab.contextual': '컨텍스트',
'tab.hub': 'Hub',
'tab.scripts': '스크립트',
'tab.flows': '플로우',
'tab.apps': '앱',
// Row actions (scripts/flows/apps)
'row.view_json': 'JSON/YAML 보기',
'row.move_rename': '이동/이름 변경',
'row.duplicate_fork': '복제/포크',
'row.go_to_public_page': '공개 페이지로 이동',
'row.deployments': '배포',
// Confirmation dialogs
'confirm.delete_forever': '영구 삭제',
'confirm.empty_trashbin': '휴지통 비우기',
'confirm.discard_changes': '변경 사항 취소',
'confirm.override': '덮어쓰기',
'confirm.unsaved_changes_detected': '저장되지 않은 변경 사항이 감지되었습니다',
'confirm.discard_changes_question': '변경 사항을 정말 취소하시겠습니까?',
'confirm.leave_anyway': '그래도 나가기',
'confirm.override_anyway': '그래도 덮어쓰기',
'confirm.new_version_deployed': '편집하는 동안 새 버전이 배포되었습니다.',
// Groups/Folders
'groups.manage_group': '그룹 관리',
'groups.new_group': '새 그룹',
'folders.manage_folder': '폴더 관리',
'folders.new_folder': '새 폴더',
// Triggers
'triggers.suspend_job': '작업 실행 일시 중지',
'triggers.more_triggers': '더 많은 트리거',
'triggers.scheduled_poll': '예약 폴링',
// Misc
'misc.all_workspaces': '모든 워크스페이스',
'misc.instance_settings': '인스턴스 설정',
'misc.search': '검색',
'misc.filter': '필터',
'misc.tree_view': '트리 보기',
'misc.loading': '로딩 중...'
}

View File

@@ -0,0 +1,262 @@
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',
'action.duplicate': 'Duplicar',
'action.fork': 'Fork',
'action.rename': 'Renomear',
'action.move': 'Mover',
'action.copy_path': 'Copiar caminho',
'action.export': 'Exportar',
'action.undo': 'Desfazer',
'action.redo': 'Refazer',
'action.test': 'Testar',
'action.view': 'Visualizar',
'action.next': 'Próximo',
'action.previous': 'Anterior',
'action.remove': 'Remover',
'action.enable': 'Ativar',
'action.disable': 'Desativar',
'action.see_permissions': 'Ver permissões',
'action.deploy_to_prod': 'Implantar em prod/staging',
'action.refresh_token': 'Atualizar token',
'action.view_runs': 'Ver execuções',
'action.view_code': 'Ver código',
'action.audit_logs': 'Auditoria',
'action.schedule': 'Agendar',
'action.versions': 'Versões',
'action.delete_draft': 'Excluir Draft',
'action.publish_to_hub': 'Publicar no Hub',
'action.run_now': 'Executar agora',
'action.manage': 'Gerenciar',
// 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',
'page.runs': 'Execuções',
// Home page
'home.create_a': 'Criar um',
'home.workspace': 'Workspace',
'home.hub': 'Hub',
'home.scripts': 'Scripts',
'home.flows': 'Flows',
'home.apps': 'Apps',
'home.hub_flow': 'Flow do Hub',
'home.hub_app': 'App do Hub',
'home.view_on_hub': 'Ver no Hub',
'home.content': 'Conteúdo',
// Variables page
'variables.new_variable': 'Nova variável',
'variables.new_contextual': 'Nova variável contextual',
'variables.contextual': 'Contextual',
'variables.custom_contextual': 'Variáveis contextuais personalizadas',
'variables.contextual_vars': 'Variáveis contextuais',
'variables.no_variables': 'Nenhuma variável encontrada',
'variables.path': 'Caminho',
'variables.value': 'Valor',
'variables.description': 'Descrição',
// Resources page
'resources.new_resource': 'Novo recurso',
// Schedules page
'schedules.duplicate': 'Duplicar agendamento',
'schedules.view_script': 'Ver Script',
'schedules.view_flow': 'Ver Flow',
// Runs page
'runs.cancel_jobs': 'Cancelar jobs',
'runs.rerun_jobs': 'Reexecutar jobs',
'runs.cancel_all': 'Cancelar todos os jobs com filtros',
'runs.rerun_all': 'Reexecutar todos os jobs com filtros',
'runs.force_cancel': 'Forçar cancelamento',
// Editor - common
'editor.save': 'Salvar',
'editor.deploy': 'Implantar',
'editor.deploy_stay': 'Implantar e Ficar aqui',
'editor.draft': 'Draft',
'editor.test': 'Testar',
'editor.test_and_record': 'Testar e gravar',
'editor.cancel': 'Cancelar',
'editor.settings': 'Configurações',
'editor.history': 'Histórico',
'editor.versions_history': 'Histórico de versões',
'editor.deployment_history': 'Histórico de implantação',
'editor.edit_in_yaml': 'Editar em YAML',
'editor.flow_settings': 'Configurações do flow',
'editor.export': 'Exportar',
'editor.export_yaml_json': 'Exportar como YAML/JSON',
'editor.diff': 'Diff',
'editor.show_diff': 'Mostrar diferenças',
'editor.editor': 'Editor',
'editor.preview': 'Pré-visualizar',
'editor.main': 'Principal',
'editor.preprocessor': 'Pré-processador',
'editor.diagram': 'Diagrama',
'editor.save_to_workspace': 'Salvar no workspace',
'editor.save_initial_draft': 'Salvar rascunho inicial',
'editor.exit_see_details': 'Sair e ver detalhes',
'editor.edit_in_fork': 'Editar no fork',
'editor.save_anyway': 'Salvar mesmo assim',
'editor.invite_others': 'Convidar outros',
'editor.deployment_message': 'Mensagem de implantação',
'editor.test_to_see_result': 'Teste para ver o resultado aqui',
'editor.no_logs_available': 'Nenhum log disponível ainda',
'editor.waiting_for_job': 'Aguardando início do trabalho...',
// Flow editor
'flow.test_flow': 'Testar flow',
'flow.test_flow_record': 'Testar flow e gravar',
'flow.build_a_flow': 'Criar um flow',
'flow.fix_broken_flow': 'Corrigir um flow com erro',
'flow.reset_tutorials': 'Reiniciar tutoriais',
'flow.skip_tutorials': 'Pular tutoriais',
'flow.create_group': 'Criar grupo',
'flow.test_iteration': 'Testar uma iteração',
'flow.skip_failures': 'Ignorar falhas',
'flow.run_parallel': 'Executar em paralelo',
// App editor
'app.public_url': 'URL pública',
'app.app_inputs': 'Entradas do app',
'app.schedule_reports': 'Agendar relatórios',
'app.troubleshoot': 'Painel de diagnóstico',
'app.lazy_mode': 'Modo lazy',
'app.hub_export': 'Exportar para Hub',
'app.debug_runs': 'Execuções de depuração',
// Status labels
'status.default': 'Padrão',
'status.primary': 'Primário',
'status.modified': 'Modificado',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': 'Workspace',
'tab.contextual': 'Contextual',
'tab.hub': 'Hub',
'tab.scripts': 'Scripts',
'tab.flows': 'Flows',
'tab.apps': 'Apps',
// Row actions (scripts/flows/apps)
'row.view_json': 'Ver JSON/YAML',
'row.move_rename': 'Mover/Renomear',
'row.duplicate_fork': 'Duplicar/Fork',
'row.go_to_public_page': 'Ir para página pública',
'row.deployments': 'Implantações',
// Confirmation dialogs
'confirm.delete_forever': 'Excluir permanentemente',
'confirm.empty_trashbin': 'Esvaziar lixeira',
'confirm.discard_changes': 'Descartar alterações',
'confirm.override': 'Sobrescrever',
'confirm.unsaved_changes_detected': 'Alterações não salvas detectadas',
'confirm.discard_changes_question': 'Tem certeza de que deseja descartar as alterações feitas?',
'confirm.leave_anyway': 'Sair mesmo assim',
'confirm.override_anyway': 'Substituir mesmo assim',
'confirm.new_version_deployed': 'Uma nova versão foi implantada enquanto você editava esta.',
// Groups/Folders
'groups.manage_group': 'Gerenciar grupo',
'groups.new_group': 'Novo grupo',
'folders.manage_folder': 'Gerenciar pasta',
'folders.new_folder': 'Nova pasta',
// Triggers
'triggers.suspend_job': 'Suspender execução do job',
'triggers.more_triggers': 'Mais gatilhos',
'triggers.scheduled_poll': 'Polling agendado',
// Misc
'misc.all_workspaces': 'Todos os workspaces',
'misc.instance_settings': 'Configurações da instância',
'misc.search': 'Buscar',
'misc.filter': 'Filtrar',
'misc.tree_view': 'Visualização em árvore',
'misc.loading': 'Carregando...'
}

View File

@@ -0,0 +1,262 @@
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': '刷新',
'action.duplicate': '复制',
'action.fork': 'Fork',
'action.rename': '重命名',
'action.move': '移动',
'action.copy_path': '复制路径',
'action.export': '导出',
'action.undo': '撤销',
'action.redo': '重做',
'action.test': '测试',
'action.view': '查看',
'action.next': '下一步',
'action.previous': '上一步',
'action.remove': '移除',
'action.enable': '启用',
'action.disable': '禁用',
'action.see_permissions': '查看权限',
'action.deploy_to_prod': '部署到生产/预发布',
'action.refresh_token': '刷新令牌',
'action.view_runs': '查看运行',
'action.view_code': '查看代码',
'action.audit_logs': '审计日志',
'action.schedule': '定时',
'action.versions': '版本',
'action.delete_draft': '删除Draft',
'action.publish_to_hub': '发布到Hub',
'action.run_now': '立即运行',
'action.manage': '管理',
// Page headers
'page.home': '首页',
'page.variables': '变量',
'page.resources': '资源',
'page.schedules': '定时任务',
'page.workers': 'Workers',
'page.folders': '文件夹',
'page.groups': '分组',
'page.runs': '运行',
// Home page
'home.create_a': '创建',
'home.workspace': '工作区',
'home.hub': 'Hub',
'home.scripts': '脚本',
'home.flows': '流程',
'home.apps': '应用',
'home.hub_flow': 'Hub流程',
'home.hub_app': 'Hub应用',
'home.view_on_hub': '在Hub上查看',
'home.content': '内容',
// Variables page
'variables.new_variable': '新建变量',
'variables.new_contextual': '新建上下文变量',
'variables.contextual': '上下文',
'variables.custom_contextual': '自定义上下文变量',
'variables.contextual_vars': '上下文变量',
'variables.no_variables': '未找到变量',
'variables.path': '路径',
'variables.value': '值',
'variables.description': '描述',
// Resources page
'resources.new_resource': '新建资源',
// Schedules page
'schedules.duplicate': '复制定时任务',
'schedules.view_script': '查看脚本',
'schedules.view_flow': '查看流程',
// Runs page
'runs.cancel_jobs': '取消任务',
'runs.rerun_jobs': '重新运行任务',
'runs.cancel_all': '取消所有符合筛选条件的任务',
'runs.rerun_all': '重新运行所有符合筛选条件的任务',
'runs.force_cancel': '强制取消',
// Editor - common
'editor.save': '保存',
'editor.deploy': '部署',
'editor.deploy_stay': '部署并留在此处',
'editor.draft': 'Draft',
'editor.test': '测试',
'editor.test_and_record': '测试并记录',
'editor.cancel': '取消',
'editor.settings': '设置',
'editor.history': '历史',
'editor.versions_history': '版本历史',
'editor.deployment_history': '部署历史',
'editor.edit_in_yaml': '以YAML编辑',
'editor.flow_settings': '流程设置',
'editor.export': '导出',
'editor.export_yaml_json': '导出为 YAML/JSON',
'editor.diff': '差异对比',
'editor.show_diff': '显示差异',
'editor.editor': '编辑器',
'editor.preview': '预览',
'editor.main': '主程序',
'editor.preprocessor': '预处理器',
'editor.diagram': '流程图',
'editor.save_to_workspace': '保存到工作区',
'editor.save_initial_draft': '保存初始草稿',
'editor.exit_see_details': '退出并查看详情',
'editor.edit_in_fork': '在分支中编辑',
'editor.save_anyway': '仍然保存',
'editor.invite_others': '邀请其他人',
'editor.deployment_message': '部署消息',
'editor.test_to_see_result': '测试以在此处查看结果',
'editor.no_logs_available': '暂无可用日志',
'editor.waiting_for_job': '等待任务启动...',
// Flow editor
'flow.test_flow': '测试流程',
'flow.test_flow_record': '测试流程并记录',
'flow.build_a_flow': '创建流程',
'flow.fix_broken_flow': '修复异常流程',
'flow.reset_tutorials': '重置教程',
'flow.skip_tutorials': '跳过教程',
'flow.create_group': '创建分组',
'flow.test_iteration': '测试迭代',
'flow.skip_failures': '跳过失败',
'flow.run_parallel': '并行运行',
// App editor
'app.public_url': '公开URL',
'app.app_inputs': '应用输入',
'app.schedule_reports': '定时报告',
'app.troubleshoot': '排障面板',
'app.lazy_mode': '懒加载模式',
'app.hub_export': 'Hub导出',
'app.debug_runs': '调试运行',
// Status labels
'status.default': '默认',
'status.primary': '主要',
'status.modified': '已修改',
'status.draft': 'Draft',
// Tabs - common
'tab.workspace': '工作区',
'tab.contextual': '上下文',
'tab.hub': 'Hub',
'tab.scripts': '脚本',
'tab.flows': '流程',
'tab.apps': '应用',
// Row actions (scripts/flows/apps)
'row.view_json': '查看JSON/YAML',
'row.move_rename': '移动/重命名',
'row.duplicate_fork': '复制/Fork',
'row.go_to_public_page': '前往公开页面',
'row.deployments': '部署记录',
// Confirmation dialogs
'confirm.delete_forever': '永久删除',
'confirm.empty_trashbin': '清空回收站',
'confirm.discard_changes': '放弃更改',
'confirm.override': '覆盖',
'confirm.unsaved_changes_detected': '检测到未保存的更改',
'confirm.discard_changes_question': '您确定要放弃所做的更改吗?',
'confirm.leave_anyway': '仍然离开',
'confirm.override_anyway': '仍然覆盖',
'confirm.new_version_deployed': '在您编辑此版本时,已部署了新版本。',
// Groups/Folders
'groups.manage_group': '管理分组',
'groups.new_group': '新建分组',
'folders.manage_folder': '管理文件夹',
'folders.new_folder': '新建文件夹',
// Triggers
'triggers.suspend_job': '暂停任务执行',
'triggers.more_triggers': '更多触发器',
'triggers.scheduled_poll': '定时轮询',
// Misc
'misc.all_workspaces': '所有工作区',
'misc.instance_settings': '实例设置',
'misc.search': '搜索',
'misc.filter': '筛选',
'misc.tree_view': '树形视图',
'misc.loading': '加载中...'
}

View File

@@ -0,0 +1,55 @@
import { type Locale, SUPPORTED_LOCALES, detectLocale } from './index'
import { en, type MessageKey } from './messages/en'
import { messages } from './messages/index'
/**
* Persist locale in localStorage directly (not via getLocalSetting)
* to survive as long as possible — localStorage has no expiry.
*/
const STORAGE_KEY = 'wm_locale'
function readStored(): Locale | undefined {
try {
const v = localStorage.getItem(STORAGE_KEY)
if (v && SUPPORTED_LOCALES.includes(v as Locale)) return v as Locale
} catch {}
return undefined
}
function writeStored(l: Locale) {
try {
localStorage.setItem(STORAGE_KEY, l)
} catch {}
}
/** Default is English. User can switch to any supported locale. */
let locale = $state<Locale>(readStored() ?? 'en')
export function getLocale(): Locale {
return locale
}
/** Whether the user's browser language differs from the active locale */
export function detectedDiffers(): boolean {
return detectLocale() !== locale
}
/** The browser-detected locale (for the alert banner) */
export function getDetectedLocale(): Locale {
return detectLocale()
}
export function setLocale(l: Locale) {
locale = l
writeStored(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]
}

View File

@@ -41,6 +41,12 @@
import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials'
import TutorialBanner from '$lib/components/home/TutorialBanner.svelte'
import NoDirectDeployAlert from '$lib/components/NoDirectDeployAlert.svelte'
import { detectedDiffers, getDetectedLocale, setLocale } from '$lib/i18n/t.svelte'
import { LOCALE_NAMES } from '$lib/i18n/index'
import { getLocalSetting, storeLocalSetting } from '$lib/utils'
import { X, Languages } from 'lucide-svelte'
let i18nBannerDismissed = $state(getLocalSetting('i18n_banner_dismissed') === 'true')
type Tab = 'hub' | 'workspace'
@@ -117,7 +123,8 @@
setTimeout(() => {
workspaceTutorials?.runTutorialById('workspace-onboarding')
}, 500)
} else if (tutorialParam === 'workspace-onboarding-operator') { // Small delay to ensure page is fully loaded
} else if (tutorialParam === 'workspace-onboarding-operator') {
// Small delay to ensure page is fully loaded
setTimeout(() => {
workspaceTutorials?.runTutorialById('workspace-onboarding-operator')
}, 500)
@@ -279,6 +286,33 @@
Windmill instance, such as keeping resource types up to date.
</Alert>
{/if}
{#if !i18nBannerDismissed && detectedDiffers()}
<div
class="flex items-center gap-3 rounded-md border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950 px-4 py-3 my-4 text-sm text-secondary"
>
<Languages size={16} class="flex-shrink-0 text-blue-500" />
<span class="flex-1">
Windmill is available in {LOCALE_NAMES[getDetectedLocale()]}.
<button
class="underline text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-200"
onclick={() => setLocale(getDetectedLocale())}
>
Switch to {LOCALE_NAMES[getDetectedLocale()]}
</button>
or change the language anytime from the user menu.
</span>
<button
onclick={() => {
i18nBannerDismissed = true
storeLocalSetting('i18n_banner_dismissed', 'true')
}}
class="p-1 rounded hover:bg-surface-hover text-secondary hover:text-primary flex-shrink-0"
aria-label="Dismiss"
>
<X size={16} />
</button>
</div>
{/if}
<PageHeader
title="Home"
childrenWrapperDivClasses="flex-1 flex flex-row gap-4 flex-wrap justify-end items-center"
@@ -293,7 +327,7 @@
<TutorialBanner />
<NoDirectDeployAlert onUpdateCanEditStatus={(v) => showCreateButtons = v}/>
<NoDirectDeployAlert onUpdateCanEditStatus={(v) => (showCreateButtons = v)} />
{#if !$userStore?.operator}
<div class="w-full overflow-auto scrollbar-hidden pb-2">