fix: resolve i18n no-restricted-imports lint warnings (#8704)

## Summary

Fix all i18n `no-restricted-imports` lint warnings and upgrade rules
from `warn` to `error`.

## Changes

- **What**: Migrate Vue components from `import { t/d } from '@/i18n'`
to `const { t } = useI18n()`. Migrate non-component `.ts` files from
`useI18n()` to `import { t/d } from '@/i18n'`. Allow `st` import from
`@/i18n` in Vue components (it wraps `te`/`t` for safe fallback
translation). Remove `@deprecated` tag from `i18n.ts` global exports
(still used by `st` and non-component code). Upgrade both lint rules
from `warn` to `error`.

## Review Focus

- The `st` helper is intentionally excluded from the Vue component
restriction since it provides safe fallback translation needed for
custom node definitions.

Fixes #8701

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8704-fix-resolve-i18n-no-restricted-imports-lint-warnings-2ff6d73d365081ae84d8eb0dfef24323)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-02-06 20:54:53 -08:00
committed by GitHub
parent c5431de123
commit 69c8c84aef
35 changed files with 117 additions and 66 deletions

View File

@@ -3,11 +3,11 @@ import { useEventListener, useTimeout } from '@vueuse/core'
import { partition } from 'es-toolkit'
import { storeToRefs } from 'pinia'
import { computed, ref, shallowRef } from 'vue'
import { useI18n } from 'vue-i18n'
import Popover from '@/components/ui/Popover.vue'
import Button from '@/components/ui/button/Button.vue'
import { extractVueNodeData } from '@/composables/graph/useGraphNodeManager'
import { t } from '@/i18n'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import { useBillingContext } from '@/composables/billing/useBillingContext'
import SubscribeToRunButton from '@/platform/cloud/subscription/components/SubscribeToRun.vue'
@@ -26,6 +26,7 @@ import { useQueueSettingsStore } from '@/stores/queueStore'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
const { t } = useI18n()
const commandStore = useCommandStore()
const executionStore = useExecutionStore()
const { batchCount } = storeToRefs(useQueueSettingsStore())

View File

@@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { downloadFile } from '@/base/common/downloadUtil'
import Popover from '@/components/ui/Popover.vue'
import Button from '@/components/ui/button/Button.vue'
import { d, t } from '@/i18n'
import { useMediaAssetActions } from '@/platform/assets/composables/useMediaAssetActions'
import { getOutputAssetMetadata } from '@/platform/assets/schemas/assetMetadataSchema'
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
@@ -26,6 +26,7 @@ import { formatDuration } from '@/utils/dateTimeUtil'
import { collectAllNodes } from '@/utils/graphTraversalUtil'
import { executeWidgetsCallback } from '@/utils/litegraphUtil'
const { t, d } = useI18n()
const mediaActions = useMediaAssetActions()
const { runButtonClick, selectedItem, selectedOutput } = defineProps<{

View File

@@ -1,5 +1,7 @@
<script setup lang="ts">
import { t } from '@/i18n'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
<template>

View File

@@ -4,13 +4,15 @@ import {
CollapsibleTrigger,
CollapsibleContent
} from 'reka-ui'
import { useI18n } from 'vue-i18n'
import WorkflowsSidebarTab from '@/components/sidebar/tabs/WorkflowsSidebarTab.vue'
import Button from '@/components/ui/button/Button.vue'
import Popover from '@/components/ui/Popover.vue'
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
import { t } from '@/i18n'
import { useCommandStore } from '@/stores/commandStore'
const { t } = useI18n()
</script>
<template>
<CollapsibleRoot class="flex flex-col">

View File

@@ -85,8 +85,8 @@
import { useIntervalFn } from '@vueuse/core'
import { Button } from 'primevue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { t } from '@/i18n'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useToastStore } from '@/platform/updates/common/toastStore'
@@ -98,6 +98,8 @@ import { useAudioRecorder } from '../composables/audio/useAudioRecorder'
import { useAudioWaveform } from '../composables/audio/useAudioWaveform'
import { formatTime } from '../utils/audioUtils'
const { t } = useI18n()
const props = defineProps<{
readonly?: boolean
nodeId: string

View File

@@ -1,8 +1,8 @@
<script setup lang="ts">
import Popover from 'primevue/popover'
import { computed, ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { t } from '@/i18n'
import { useToastStore } from '@/platform/updates/common/toastStore'
import type {
@@ -46,16 +46,28 @@ interface Props {
) => Promise<FormDropdownItem[]>
}
const props = withDefaults(defineProps<Props>(), {
placeholder: t('widgets.uploadSelect.placeholder'),
multiple: false,
uploadable: false,
disabled: false,
filterOptions: () => [],
sortOptions: () => getDefaultSortOptions(),
isSelected: (selected, item, _index) => selected.has(item.id),
searcher: defaultSearcher
})
const { t } = useI18n()
const {
placeholder,
multiple = false,
uploadable = false,
disabled = false,
accept,
filterOptions = [],
sortOptions = getDefaultSortOptions(),
showOwnershipFilter,
ownershipOptions,
showBaseModelFilter,
baseModelOptions,
isSelected = (selected, item, _index) => selected.has(item.id),
searcher = defaultSearcher,
items
} = defineProps<Props>()
const placeholderText = computed(
() => placeholder ?? t('widgets.uploadSelect.placeholder')
)
const selected = defineModel<Set<string>>('selected', {
default: () => new Set()
@@ -82,24 +94,22 @@ const triggerRef = useTemplateRef('triggerRef')
const isOpen = ref(false)
const maxSelectable = computed(() => {
if (props.multiple === true) return Infinity
if (typeof props.multiple === 'number') return props.multiple
if (multiple === true) return Infinity
if (typeof multiple === 'number') return multiple
return 1
})
const itemsKey = computed(() => props.items.map((item) => item.id).join('|'))
const itemsKey = computed(() => items.map((item) => item.id).join('|'))
const filteredItems = ref<FormDropdownItem[]>([])
const defaultSorter = computed<SortOption['sorter']>(() => {
const sorter = props.sortOptions.find(
(option) => option.id === 'default'
)?.sorter
return sorter || (({ items }) => items.slice())
const sorter = sortOptions.find((option) => option.id === 'default')?.sorter
return sorter || (({ items: i }) => i.slice())
})
const selectedSorter = computed<SortOption['sorter']>(() => {
if (sortSelected.value === 'default') return defaultSorter.value
const sorter = props.sortOptions.find(
const sorter = sortOptions.find(
(option) => option.id === sortSelected.value
)?.sorter
return sorter || defaultSorter.value
@@ -109,11 +119,11 @@ const sortedItems = computed(() => {
})
function internalIsSelected(item: FormDropdownItem, index: number): boolean {
return props.isSelected?.(selected.value, item, index) ?? false
return isSelected(selected.value, item, index)
}
const toggleDropdown = (event: Event) => {
if (props.disabled) return
if (disabled) return
if (popoverRef.value && triggerRef.value) {
popoverRef.value.toggle(event, triggerRef.value)
isOpen.value = !isOpen.value
@@ -128,7 +138,7 @@ const closeDropdown = () => {
}
function handleFileChange(event: Event) {
if (props.disabled) return
if (disabled) return
const target = event.target
if (!(target instanceof HTMLInputElement)) return
if (target.files) {
@@ -138,7 +148,7 @@ function handleFileChange(event: Event) {
}
function handleSelection(item: FormDropdownItem, index: number) {
if (props.disabled) return
if (disabled) return
const sel = selected.value
if (internalIsSelected(item, index)) {
sel.delete(item.id)
@@ -170,11 +180,9 @@ async function customSearcher(
isCleanup = true
cleanupFn?.()
})
await props
.searcher(query, props.items, (cb) => (cleanupFn = cb))
.then((results) => {
if (!isCleanup) filteredItems.value = results
})
await searcher(query, items, (cb) => (cleanupFn = cb)).then((results) => {
if (!isCleanup) filteredItems.value = results
})
}
</script>
@@ -183,7 +191,7 @@ async function customSearcher(
<FormDropdownInput
:files
:is-open
:placeholder
:placeholder="placeholderText"
:items
:max-selectable
:selected