chore: add missing i18n keys in sidebar, assets, toolbox, dropdowns (#6622)

This PR 

- adds missing locale keys for 3D viewer toast strings, assets sidebar
labels, and node error keys
- cleans up the selection toolbox, media previews, node components, and
widget uploader to rely on `$t`/`st` (exposed to template scope at
compile time) instead of importing from `useI18n`.
- updates `eslint.config.ts` to teach the Intlify rule about the locale
layout

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6622-chore-add-missing-i18n-keys-in-sidebar-assets-toolbox-dropdowns-2a36d73d365081ae8694eb4f8ebb822a)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
Christian Byrne
2025-11-11 13:27:07 -08:00
committed by GitHub
parent 3c550e953a
commit 2542449d45
21 changed files with 96 additions and 40 deletions

View File

@@ -71,8 +71,8 @@ const updateConsent = async () => {
} catch (error) {
toast.add({
severity: 'error',
summary: t('install.errorUpdatingConsent'),
detail: t('install.errorUpdatingConsentDetail'),
summary: t('install.settings.errorUpdatingConsent'),
detail: t('install.settings.errorUpdatingConsentDetail'),
life: 3000
})
} finally {

View File

@@ -33,7 +33,18 @@ const settings = {
],
noWarnOnMultipleProjects: true
})
]
],
'vue-i18n': {
localeDir: [
{
pattern: './src/locales/**/*.json',
localeKey: 'path',
localePattern:
/^\.?\/?src\/locales\/(?<locale>[A-Za-z0-9-]+)\/.+\.json$/
}
],
messageSyntaxVersion: '^9.0.0'
}
} as const
const commonParserOptions = {

View File

@@ -1,7 +1,7 @@
<template>
<Button
v-tooltip.top="{
value: t('commands.Comfy_Canvas_ToggleSelectedNodes_Bypass.label'),
value: $t('commands.Comfy_Canvas_ToggleSelectedNodes_Bypass.label'),
showDelay: 1000
}"
severity="secondary"
@@ -18,11 +18,9 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { useI18n } from 'vue-i18n'
import { useCommandStore } from '@/stores/commandStore'
const { t } = useI18n()
const commandStore = useCommandStore()
const toggleBypass = async () => {

View File

@@ -1,7 +1,7 @@
<template>
<Button
v-tooltip.top="{
value: $t('Edit Subgraph Widgets'),
value: $t('commands.Comfy_Graph_EditSubgraphWidgets.label'),
showDelay: 1000
}"
severity="secondary"

View File

@@ -2,7 +2,7 @@
<Button
v-if="isUnpackVisible"
v-tooltip.top="{
value: t('commands.Comfy_Graph_UnpackSubgraph.label'),
value: $t('commands.Comfy_Graph_UnpackSubgraph.label'),
showDelay: 1000
}"
severity="secondary"
@@ -17,7 +17,7 @@
<Button
v-else-if="isConvertVisible"
v-tooltip.top="{
value: t('commands.Comfy_Graph_ConvertToSubgraph.label'),
value: $t('commands.Comfy_Graph_ConvertToSubgraph.label'),
showDelay: 1000
}"
severity="secondary"
@@ -34,12 +34,10 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useSelectionState } from '@/composables/graph/useSelectionState'
import { useCommandStore } from '@/stores/commandStore'
const { t } = useI18n()
const commandStore = useCommandStore()
const { isSingleSubgraph, hasAnySelection } = useSelectionState()

View File

@@ -2,7 +2,7 @@
<Button
v-show="isDeletable"
v-tooltip.top="{
value: t('commands.Comfy_Canvas_DeleteSelectedItems.label'),
value: $t('commands.Comfy_Canvas_DeleteSelectedItems.label'),
showDelay: 1000
}"
severity="secondary"
@@ -17,13 +17,11 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useSelectionState } from '@/composables/graph/useSelectionState'
import type { Positionable } from '@/lib/litegraph/src/interfaces'
import { useCommandStore } from '@/stores/commandStore'
const { t } = useI18n()
const commandStore = useCommandStore()
const { selectedItems } = useSelectionState()

View File

@@ -1,7 +1,7 @@
<template>
<Button
v-tooltip.top="{
value: t('commands.Comfy_3DViewer_Open3DViewer.label'),
value: $t('commands.Comfy_3DViewer_Open3DViewer.label'),
showDelay: 1000
}"
severity="secondary"
@@ -15,7 +15,6 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { t } from '@/i18n'
import { useCommandStore } from '@/stores/commandStore'
const commandStore = useCommandStore()

View File

@@ -2,7 +2,7 @@
<Button
v-show="isSingleImageNode"
v-tooltip.top="{
value: t('commands.Comfy_MaskEditor_OpenMaskEditor.label'),
value: $t('commands.Comfy_MaskEditor_OpenMaskEditor.label'),
showDelay: 1000
}"
severity="secondary"
@@ -17,7 +17,6 @@
import Button from 'primevue/button'
import { useSelectionState } from '@/composables/graph/useSelectionState'
import { t } from '@/i18n'
import { useCommandStore } from '@/stores/commandStore'
const commandStore = useCommandStore()

View File

@@ -2,7 +2,7 @@
<Button
v-show="isVisible"
v-tooltip.top="{
value: t('commands.Comfy_PublishSubgraph.label'),
value: $t('commands.Comfy_PublishSubgraph.label'),
showDelay: 1000
}"
severity="secondary"
@@ -18,13 +18,11 @@
<script setup lang="ts">
import Button from 'primevue/button'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { SubgraphNode } from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useCommandStore } from '@/stores/commandStore'
const { t } = useI18n()
const commandStore = useCommandStore()
const canvasStore = useCanvasStore()

View File

@@ -1,12 +1,12 @@
<template>
<BaseModalLayout :content-title="$t('Checkpoints')">
<BaseModalLayout :content-title="$t('assetBrowser.checkpoints')">
<template #leftPanel>
<LeftSidePanel v-model="selectedNavItem" :nav-items="tempNavigation">
<template #header-icon>
<i class="text-neutral icon-[lucide--puzzle]" />
</template>
<template #header-title>
<span class="text-neutral text-base">{{ t('g.title') }}</span>
<span class="text-neutral text-base">{{ $t('g.title') }}</span>
</template>
</LeftSidePanel>
</template>
@@ -132,7 +132,6 @@
<script setup lang="ts">
import { computed, provide, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import IconButton from '@/components/button/IconButton.vue'
import IconTextButton from '@/components/button/IconTextButton.vue'
@@ -189,8 +188,6 @@ const tempNavigation = ref<(NavItemData | NavGroupData)[]>([
}
])
const { t } = useI18n()
const { onClose } = defineProps<{
onClose: () => void
}>()

View File

@@ -317,6 +317,10 @@
"label": "Toggle Node Library Sidebar",
"tooltip": "Node Library"
},
"Workspace_ToggleSidebarTab_assets": {
"label": "Toggle Assets Sidebar",
"tooltip": "Assets"
},
"Workspace_ToggleSidebarTab_queue": {
"label": "Toggle Queue Sidebar",
"tooltip": "Queue"
@@ -325,4 +329,4 @@
"label": "Toggle Workflows Sidebar",
"tooltip": "Workflows"
}
}
}

View File

@@ -204,6 +204,19 @@
"noAudioRecorded": "No audio recorded",
"nodesRunning": "nodes running",
"duplicate": "Duplicate",
"copied": "Copied",
"itemsCopiedToClipboard": "Items copied to clipboard",
"selectItemsToCopy": "Select items to copy",
"nothingToCopy": "Nothing to copy",
"selectItemsToDuplicate": "Select items to duplicate",
"nothingToDuplicate": "Nothing to duplicate",
"selectItemsToDelete": "Select items to delete",
"nothingToDelete": "Nothing to delete",
"batchRename": "Batch rename",
"enterBaseName": "Enter base name",
"enterNewName": "Enter new name",
"selectItemsToRename": "Select items to rename",
"nothingToRename": "Nothing to rename",
"moreWorkflows": "More workflows",
"seeTutorial": "See a tutorial",
"nodeRenderError": "Node Render Error",
@@ -612,11 +625,11 @@
"queue": "Queue",
"nodes": "Nodes",
"models": "Models",
"assets": "Assets",
"workflows": "Workflows",
"templates": "Templates",
"console": "Console",
"menu": "Menu",
"assets": "Assets",
"imported": "Imported",
"generated": "Generated"
},
@@ -1076,7 +1089,10 @@
"loading": "Loading templates...",
"noResults": "No templates found",
"noResultsHint": "Try adjusting your search or filters",
"allTemplates": "All Templates",
"modelFilter": "Model Filter",
"useCaseFilter": "Use Case",
"licenseFilter": "License",
"modelsSelected": "{count} Models",
"useCasesSelected": "{count} Use Cases",
"runsOnSelected": "{count} Runs On",
@@ -1095,6 +1111,11 @@
"templateNotFound": "Template \"{templateName}\" not found"
}
},
"templateWidgets": {
"sort": {
"searchPlaceholder": "Search..."
}
},
"graphCanvasMenu": {
"zoomIn": "Zoom In",
"zoomOut": "Zoom Out",
@@ -1836,8 +1857,25 @@
"failedToLoadBackgroundImage": "Failed to load background image",
"failedToLoadModel": "Failed to load 3D model",
"modelLoadedSuccessfully": "3D model loaded successfully",
"failedToUpdateBackgroundColor": "Failed to update background color",
"failedToToggleGrid": "Failed to toggle grid",
"failedToToggleCamera": "Failed to toggle camera",
"failedToUpdateFOV": "Failed to update field of view",
"failedToUpdateLightIntensity": "Failed to update light intensity",
"failedToUpdateBackgroundImage": "Failed to update background image",
"failedToUpdateUpDirection": "Failed to update up direction",
"failedToUpdateMaterialMode": "Failed to update material mode",
"failedToUpdateEdgeThreshold": "Failed to update edge threshold",
"failedToUploadBackgroundImage": "Failed to upload background image",
"failedToUpdateBackgroundRenderMode": "Failed to update background render mode to {mode}"
},
"nodeErrors": {
"render": "Node Render Error",
"content": "Node Content Error",
"header": "Node Header Error",
"slots": "Node Slots Error",
"widgets": "Node Widgets Error"
},
"auth": {
"apiKey": {
"title": "API Key",
@@ -2071,6 +2109,10 @@
"placeholderUnknown": "Select media..."
}
},
"widgetFileUpload": {
"dropPrompt": "Drop your file or",
"browseFiles": "Browse Files"
},
"nodeHelpPage": {
"inputs": "Inputs",
"outputs": "Outputs",
@@ -2236,6 +2278,7 @@
"cloudSurvey_steps_making": "What do you plan on making?",
"assetBrowser": {
"assets": "Assets",
"checkpoints": "Checkpoints",
"browseAssets": "Browse Assets",
"noAssetsFound": "No assets found",
"tryAdjustingFilters": "Try adjusting your search or filters",
@@ -2257,6 +2300,10 @@
"ariaLabel": {
"assetCard": "{name} - {type} asset",
"loadingAsset": "Loading asset"
},
"media": {
"threeDModelPlaceholder": "3D Model",
"audioPlaceholder": "Audio"
}
},
"mediaAsset": {

View File

@@ -7,10 +7,8 @@
class="icon-[lucide--box] text-3xl text-zinc-600 dark-theme:text-zinc-200"
/>
<span class="text-zinc-600 dark-theme:text-zinc-200">{{
$t('3D Model')
$t('assetBrowser.media.threeDModelPlaceholder')
}}</span>
</div>
</div>
</template>
<script setup lang="ts"></script>

View File

@@ -7,7 +7,7 @@
class="icon-[lucide--music] text-3xl text-zinc-600 dark-theme:text-zinc-200"
/>
<span class="text-zinc-600 dark-theme:text-zinc-200">{{
$t('Audio')
$t('assetBrowser.media.audioPlaceholder')
}}</span>
</div>
<audio

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ $t('Node Render Error') }}
{{ st('nodeErrors.render', 'Node Render Error') }}
</div>
<div
v-else
@@ -144,6 +144,7 @@ import { useI18n } from 'vue-i18n'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { toggleNodeOptions } from '@/composables/graph/useMoreOptionsMenu'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { st } from '@/i18n'
import { LGraphEventMode, LiteGraph } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ $t('Node Content Error') }}
{{ st('nodeErrors.content', 'Node Content Error') }}
</div>
<div v-else class="lg-node-content flex grow flex-col">
<!-- Default slot for custom content -->
@@ -26,6 +26,7 @@ import { computed, onErrorCaptured, ref } from 'vue'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { st } from '@/i18n'
import VideoPreview from '../VideoPreview.vue'
import ImagePreview from './ImagePreview.vue'

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="renderError" class="node-error p-4 text-sm text-red-500">
{{ $t('Node Header Error') }}
{{ st('nodeErrors.header', 'Node Header Error') }}
</div>
<div
v-else

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ $t('Node Slots Error') }}
{{ st('nodeErrors.slots', 'Node Slots Error') }}
</div>
<div v-else :class="cn('flex justify-between', unifiedWrapperClass)">
<div
@@ -38,6 +38,7 @@ import { computed, onErrorCaptured, ref } from 'vue'
import type { VueNodeData } from '@/composables/graph/useGraphNodeManager'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { st } from '@/i18n'
import type { INodeSlot } from '@/lib/litegraph/src/litegraph'
import {
linkedWidgetedInputs,

View File

@@ -1,6 +1,6 @@
<template>
<div v-if="renderError" class="node-error p-2 text-sm text-red-500">
{{ $t('Node Widgets Error') }}
{{ st('nodeErrors.widgets', 'Node Widgets Error') }}
</div>
<div
v-else
@@ -67,6 +67,7 @@ import type {
WidgetSlotMetadata
} from '@/composables/graph/useGraphNodeManager'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { st } from '@/i18n'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useNodeTooltips'
import WidgetDOM from '@/renderer/extensions/vueNodes/widgets/components/WidgetDOM.vue'

View File

@@ -23,7 +23,10 @@ describe('WidgetFileUpload File Handling', () => {
messages: {
en: {
...enMessages,
'Drop your file or': 'Drop your file or'
widgetFileUpload: {
dropPrompt: 'Drop your file or',
browseFiles: 'Browse Files'
}
}
}
})

View File

@@ -156,10 +156,12 @@
:style="{ borderColor: '#262729' }"
>
<div class="flex w-full flex-col items-center gap-2 py-4">
<span class="text-xs opacity-60"> {{ $t('Drop your file or') }} </span>
<span class="text-xs opacity-60">
{{ $t('widgetFileUpload.dropPrompt') }}
</span>
<div>
<Button
label="Browse Files"
:label="$t('widgetFileUpload.browseFiles')"
size="small"
severity="secondary"
class="text-xs"