mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 23:50:08 +00:00
refactor: move ellipsis and punctuation into i18n translation strings (#8573)
## Summary
Move ellipsis and punctuation characters into i18n translation strings
for proper internationalization support.
## Changes
- Add 12 new translation keys with punctuation included:
- Placeholder keys with trailing ellipsis (e.g.,
`searchNodesPlaceholder: "Search Nodes..."`)
- `downloadWithSize` with interpolation: `"Download ({size})"`
- `completedWithCheckmark`: `"Completed ✓"`
- Prompt keys with colons (e.g., `enterNewNamePrompt: "Enter new
name:"`)
- Update 20 files to use new translation keys instead of string
concatenation
## Review Focus
This eliminates string concatenation patterns like `$t('key') + '...'`
that break proper internationalization, since different languages may
use different punctuation or may not need ellipsis/colons in the same
contexts.
Fixes #7333
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Standardized localization across the app: unified search placeholders
and input hints; updated dialog prompt texts for renaming,
saving/exporting, and related prompts.
* **New Features**
* Download buttons now show file size via localized text.
* Completed status displays a localized label with a checkmark.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8573-refactor-move-ellipsis-and-punctuation-into-i18n-translation-strings-2fc6d73d365081828ad3f257bcac7799)
by [Unito](https://www.unito.io)
This commit is contained in:
@@ -46,7 +46,7 @@ class ComfyPropertiesPanel {
|
||||
constructor(readonly page: Page) {
|
||||
this.root = page.getByTestId(TestIds.propertiesPanel.root)
|
||||
this.panelTitle = this.root.locator('h3')
|
||||
this.searchBox = this.root.getByPlaceholder('Search...')
|
||||
this.searchBox = this.root.getByPlaceholder(/^Search/)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
@click="triggerDownload"
|
||||
>
|
||||
<i class="pi pi-download" />
|
||||
{{ $t('g.download') + ' (' + fileSize + ')' }}
|
||||
{{ $t('g.downloadWithSize', { size: fileSize }) }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="(status === null || status === 'error') && !!props.url"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
:title="props.url"
|
||||
@click="download.triggerBrowserDownload"
|
||||
>
|
||||
{{ $t('g.download') + ' (' + fileSize + ')' }}
|
||||
{{ $t('g.downloadWithSize', { size: fileSize }) }}
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<template #header>
|
||||
<SearchBox
|
||||
v-model="filters['global'].value"
|
||||
:placeholder="$t('g.searchKeybindings') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', { subject: $t('g.keybindings') })
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ const isFavorited = computed(() =>
|
||||
async function handleRename() {
|
||||
const newLabel = await dialogService.prompt({
|
||||
title: t('g.rename'),
|
||||
message: t('g.enterNewName') + ':',
|
||||
message: t('g.enterNewNamePrompt'),
|
||||
defaultValue: widget.label,
|
||||
placeholder: widget.name
|
||||
})
|
||||
|
||||
@@ -118,7 +118,9 @@ const suggestions = ref<ComfyNodeDefImpl[]>([])
|
||||
const hoveredSuggestion = ref<ComfyNodeDefImpl | null>(null)
|
||||
const currentQuery = ref('')
|
||||
const placeholder = computed(() => {
|
||||
return filters.length === 0 ? t('g.searchNodes') + '...' : ''
|
||||
return filters.length === 0
|
||||
? t('g.searchPlaceholder', { subject: t('g.nodes') })
|
||||
: ''
|
||||
})
|
||||
|
||||
const nodeDefStore = useNodeDefStore()
|
||||
|
||||
@@ -25,7 +25,11 @@
|
||||
<SearchBox
|
||||
ref="searchBoxRef"
|
||||
v-model:model-value="searchQuery"
|
||||
:placeholder="$t('g.searchModels') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', {
|
||||
subject: $t('sideToolbar.labels.models')
|
||||
})
|
||||
"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
v-model:model-value="searchQuery"
|
||||
data-testid="node-library-search"
|
||||
class="node-lib-search-box"
|
||||
:placeholder="$t('g.searchNodes') + '...'"
|
||||
:placeholder="$t('g.searchPlaceholder', { subject: $t('g.nodes') })"
|
||||
filter-icon="pi pi-filter"
|
||||
:filters
|
||||
@search="handleSearch"
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
ref="searchBoxRef"
|
||||
v-model:model-value="searchQuery"
|
||||
class="workflows-search-box"
|
||||
:placeholder="$t('g.searchWorkflows') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', { subject: $t('g.workflow') })
|
||||
"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -190,7 +190,7 @@ export function useJobMenu(
|
||||
if (settingStore.get('Comfy.PromptFilename')) {
|
||||
const input = await useDialogService().prompt({
|
||||
title: t('workflowService.exportWorkflow'),
|
||||
message: t('workflowService.enterFilename') + ':',
|
||||
message: t('workflowService.enterFilenamePrompt'),
|
||||
defaultValue: filename
|
||||
})
|
||||
if (!input) return
|
||||
|
||||
@@ -199,7 +199,7 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
|
||||
const newName = await dialogService.prompt({
|
||||
title: t('g.rename'),
|
||||
message: t('workflowService.enterFilename') + ':',
|
||||
message: t('workflowService.enterFilenamePrompt'),
|
||||
defaultValue: workflow.filename
|
||||
})
|
||||
if (!newName || newName === workflow.filename) return
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"searchKeybindings": "Search Keybindings",
|
||||
"searchExtensions": "Search Extensions",
|
||||
"search": "Search",
|
||||
"searchPlaceholder": "Search...",
|
||||
"searchPlaceholder": "Search {subject}...",
|
||||
"downloadWithSize": "Download ({size})",
|
||||
"noResultsFound": "No Results Found",
|
||||
"noResults": "No Results",
|
||||
"searchFailedMessage": "We couldn't find any settings matching your search. Try adjusting your search terms.",
|
||||
@@ -152,6 +153,8 @@
|
||||
"custom": "Custom",
|
||||
"command": "Command",
|
||||
"keybinding": "Keybinding",
|
||||
"keybindings": "Keybindings",
|
||||
"extensions": "Extensions",
|
||||
"upload": "Upload",
|
||||
"export": "Export",
|
||||
"workflow": "Workflow",
|
||||
@@ -192,6 +195,7 @@
|
||||
"missing": "Missing",
|
||||
"inProgress": "In progress",
|
||||
"completed": "Completed",
|
||||
"completedWithCheckmark": "Completed ✓",
|
||||
"downloading": "Downloading",
|
||||
"interrupted": "Interrupted",
|
||||
"queued": "Queued",
|
||||
@@ -255,6 +259,7 @@
|
||||
"batchRename": "Batch rename",
|
||||
"enterBaseName": "Enter base name",
|
||||
"enterNewName": "Enter new name",
|
||||
"enterNewNamePrompt": "Enter new name:",
|
||||
"selectItemsToRename": "Select items to rename",
|
||||
"nothingToRename": "Nothing to rename",
|
||||
"moreWorkflows": "More workflows",
|
||||
@@ -1003,6 +1008,7 @@
|
||||
"workflowService": {
|
||||
"exportWorkflow": "Export Workflow",
|
||||
"enterFilename": "Enter the filename",
|
||||
"enterFilenamePrompt": "Enter the filename:",
|
||||
"saveWorkflow": "Save workflow"
|
||||
},
|
||||
"subgraphStore": {
|
||||
@@ -1012,6 +1018,7 @@
|
||||
"overwriteBlueprintTitle": "Overwrite existing blueprint?",
|
||||
"overwriteBlueprint": "Saving will overwrite the current blueprint with your changes",
|
||||
"blueprintName": "Subgraph name",
|
||||
"blueprintNamePrompt": "Subgraph name:",
|
||||
"promoteOutsideSubgraph": "Can't promote widget when not in subgraph",
|
||||
"publish": "Publish Subgraph",
|
||||
"publishSuccess": "Saved to Nodes Library",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
v-model="searchQuery"
|
||||
:autofocus="true"
|
||||
size="lg"
|
||||
:placeholder="$t('g.searchPlaceholder')"
|
||||
:placeholder="$t('g.searchPlaceholder', { subject: '' })"
|
||||
class="max-w-96"
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<div class="flex gap-3 items-center">
|
||||
<SearchBox
|
||||
:model-value="searchQuery"
|
||||
:placeholder="$t('sideToolbar.searchAssets') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', { subject: $t('sideToolbar.labels.assets') })
|
||||
"
|
||||
@update:model-value="handleSearchChange"
|
||||
/>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<template #header>
|
||||
<SearchBox
|
||||
v-model="filters['global'].value"
|
||||
:placeholder="$t('g.searchExtensions') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', { subject: $t('g.extensions') })
|
||||
"
|
||||
/>
|
||||
<Message
|
||||
v-if="hasChanges"
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
<SearchBox
|
||||
v-model:model-value="searchQuery"
|
||||
class="settings-search-box mb-2 w-full"
|
||||
:placeholder="$t('g.searchSettings') + '...'"
|
||||
:placeholder="
|
||||
$t('g.searchPlaceholder', { subject: $t('g.settings') })
|
||||
"
|
||||
:debounce-time="128"
|
||||
autofocus
|
||||
@search="handleSearch"
|
||||
|
||||
@@ -55,7 +55,7 @@ export function useWorkflowActionsService() {
|
||||
if (settingStore.get('Comfy.PromptFilename')) {
|
||||
const input = await dialogService.prompt({
|
||||
title: t('workflowService.exportWorkflow'),
|
||||
message: t('workflowService.enterFilename') + ':',
|
||||
message: t('workflowService.enterFilenamePrompt'),
|
||||
defaultValue: filename
|
||||
})
|
||||
// User cancelled the prompt
|
||||
|
||||
@@ -35,7 +35,7 @@ export const useWorkflowService = () => {
|
||||
if (settingStore.get('Comfy.PromptFilename')) {
|
||||
let filename = await dialogService.prompt({
|
||||
title: t('workflowService.exportWorkflow'),
|
||||
message: t('workflowService.enterFilename') + ':',
|
||||
message: t('workflowService.enterFilenamePrompt'),
|
||||
defaultValue: defaultName
|
||||
})
|
||||
if (!filename) return null
|
||||
|
||||
@@ -161,7 +161,7 @@ export class ComfyWorkflow extends UserFile {
|
||||
async promptSave(): Promise<string | null> {
|
||||
return await useDialogService().prompt({
|
||||
title: t('workflowService.saveWorkflow'),
|
||||
message: t('workflowService.enterFilename') + ':',
|
||||
message: t('workflowService.enterFilenamePrompt'),
|
||||
defaultValue: this.filename
|
||||
})
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ function handleFocus(event: FocusEvent) {
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
class="bg-transparent border-0 outline-0 ring-0 h-5 w-full my-1.5 mx-2"
|
||||
:placeholder="$t('g.searchPlaceholder')"
|
||||
:placeholder="$t('g.searchPlaceholder', { subject: '' })"
|
||||
:autofocus
|
||||
@focus="handleFocus"
|
||||
/>
|
||||
|
||||
@@ -85,7 +85,7 @@ export function getExtraOptionsForWidget(
|
||||
callback: async () => {
|
||||
const newLabel = await useDialogService().prompt({
|
||||
title: t('g.rename'),
|
||||
message: t('g.enterNewName') + ':',
|
||||
message: t('g.enterNewNamePrompt'),
|
||||
defaultValue: widget.label,
|
||||
placeholder: widget.name
|
||||
})
|
||||
|
||||
@@ -130,7 +130,7 @@ export const useSubgraphStore = defineStore('subgraph', () => {
|
||||
override async promptSave(): Promise<string | null> {
|
||||
return await useDialogService().prompt({
|
||||
title: t('subgraphStore.saveBlueprint'),
|
||||
message: t('subgraphStore.blueprintName') + ':',
|
||||
message: t('subgraphStore.blueprintNamePrompt'),
|
||||
defaultValue: this.filename
|
||||
})
|
||||
}
|
||||
@@ -270,7 +270,7 @@ export const useSubgraphStore = defineStore('subgraph', () => {
|
||||
//prompt name
|
||||
const name = await useDialogService().prompt({
|
||||
title: t('subgraphStore.saveBlueprint'),
|
||||
message: t('subgraphStore.blueprintName') + ':',
|
||||
message: t('subgraphStore.blueprintNamePrompt'),
|
||||
defaultValue: subgraphNode.title
|
||||
})
|
||||
if (!name) return
|
||||
|
||||
@@ -239,7 +239,7 @@ onBeforeUnmount(() => {
|
||||
{{
|
||||
isTaskInProgress(index)
|
||||
? t('g.inProgress')
|
||||
: t('g.completed') + ' ✓'
|
||||
: t('g.completedWithCheckmark')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user