eslint added to enforce translations (#5150)

* eslint added and new string added

* strings and translation hook added

* more changes made

* missing translation added

* final errors resolve in progress

* all errors resolved

* fix(ui): fix missing import of `t()`

* fix(ui): use plurals for moving images to board translation

* fix(ui): fix typo in translation key

* fix(ui): do not use translation for "invoke ai"

* chore(ui): lint

---------

Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
This commit is contained in:
Rohinish 2023-11-25 09:16:19 +05:30 committed by GitHub
parent 667a2a3d84
commit 6e6d903f99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 185 additions and 69 deletions

View File

@ -20,10 +20,16 @@ module.exports = {
ecmaVersion: 2018,
sourceType: 'module',
},
plugins: ['react', '@typescript-eslint', 'eslint-plugin-react-hooks'],
plugins: [
'react',
'@typescript-eslint',
'eslint-plugin-react-hooks',
'i18next',
],
root: true,
rules: {
curly: 'error',
'i18next/no-literal-string': 2,
'react/jsx-no-bind': ['error', { allowBind: true }],
'react/jsx-curly-brace-presence': [
'error',

View File

@ -131,6 +131,7 @@
"concurrently": "^8.2.2",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-i18next": "^6.0.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.3",

View File

@ -1,6 +1,7 @@
{
"accessibility": {
"copyMetadataJson": "Copy metadata JSON",
"createIssue":"Create Issue",
"exitViewer": "Exit Viewer",
"flipHorizontally": "Flip Horizontally",
"flipVertically": "Flip Vertically",
@ -12,6 +13,7 @@
"nextImage": "Next Image",
"previousImage": "Previous Image",
"reset": "Reset",
"resetUI":"$t(accessibility.reset) UI",
"rotateClockwise": "Rotate Clockwise",
"rotateCounterClockwise": "Rotate Counter-Clockwise",
"showGalleryPanel": "Show Gallery Panel",
@ -38,6 +40,8 @@
"loading": "Loading...",
"menuItemAutoAdd": "Auto-add to this Board",
"move": "Move",
"movingImagesToBoard_one": "Moving {{count}} image to board:",
"movingImagesToBoard_other": "Moving {{count}} images to board:",
"myBoard": "My Board",
"noMatching": "No matching Boards",
"searchBoard": "Search Boards...",
@ -49,11 +53,13 @@
"common": {
"accept": "Accept",
"advanced": "Advanced",
"ai": "ai",
"areYouSure": "Are you sure?",
"auto": "Auto",
"back": "Back",
"batch": "Batch Manager",
"cancel": "Cancel",
"copyError":"$t(gallery.copy) Error",
"close": "Close",
"on": "On",
"checkpoint": "Checkpoint",
@ -67,6 +73,10 @@
"darkMode": "Dark Mode",
"discordLabel": "Discord",
"dontAskMeAgain": "Don't ask me again",
"error": "Error",
"file": "File",
"folder": "Folder",
"format":"format",
"generate": "Generate",
"githubLabel": "Github",
"hotkeysLabel": "Hotkeys",
@ -74,6 +84,8 @@
"imageFailedToLoad": "Unable to Load Image",
"img2img": "Image To Image",
"inpaint": "inpaint",
"input": "Input",
"installed": "Installed",
"langArabic": "العربية",
"langBrPortuguese": "Português do Brasil",
"langDutch": "Nederlands",
@ -101,6 +113,7 @@
"nodeEditor": "Node Editor",
"nodes": "Workflow Editor",
"nodesDesc": "A node based system for the generation of images is under development currently. Stay tuned for updates about this amazing feature.",
"notInstalled": "Not $t(common.installed)",
"openInNewTab": "Open in New Tab",
"outpaint": "outpaint",
"outputs": "Outputs",
@ -114,6 +127,7 @@
"safetensors": "Safetensors",
"settingsLabel": "Settings",
"simple": "Simple",
"somethingWentWrong": "Something went wrong",
"statusConnected": "Connected",
"statusConvertingModel": "Converting Model",
"statusDisconnected": "Disconnected",
@ -252,6 +266,7 @@
"embedding": {
"addEmbedding": "Add Embedding",
"incompatibleModel": "Incompatible base model:",
"noEmbeddingsLoaded": "No Embeddings Loaded",
"noMatchingEmbedding": "No matching Embeddings"
},
"queue": {
@ -330,7 +345,8 @@
"enableFailed": "Problem Enabling Invocation Cache",
"disable": "Disable",
"disableSucceeded": "Invocation Cache Disabled",
"disableFailed": "Problem Disabling Invocation Cache"
"disableFailed": "Problem Disabling Invocation Cache",
"useCache": "Use Cache"
},
"gallery": {
"allImagesLoaded": "All Images Loaded",
@ -339,6 +355,9 @@
"autoSwitchNewImages": "Auto-Switch to New Images",
"copy": "Copy",
"currentlyInUse": "This image is currently in use in the following features:",
"drop":"Drop",
"dropOrUpload":"$t(gallery.drop) or Upload",
"dropToUpload":"$t(gallery.drop) to Upload",
"deleteImage": "Delete Image",
"deleteImageBin": "Deleted images will be sent to your operating system's Bin.",
"deleteImagePermanent": "Deleted images cannot be restored.",
@ -348,7 +367,7 @@
"galleryImageSize": "Image Size",
"gallerySettings": "Gallery Settings",
"generations": "Generations",
"images": "Images",
"image": "image",
"loading": "Loading",
"loadMore": "Load More",
"maintainAspectRatio": "Maintain Aspect Ratio",
@ -360,6 +379,7 @@
"singleColumnLayout": "Single Column Layout",
"unableToLoad": "Unable to load Gallery",
"uploads": "Uploads",
"deleteSelection": "Delete Selection",
"downloadSelection": "Download Selection",
"preparingDownload": "Preparing Download",
"preparingDownloadFailed": "Problem Preparing Download"
@ -629,6 +649,7 @@
"closeAdvanced": "Close Advanced",
"config": "Config",
"configValidationMsg": "Path to the config file of your model.",
"conversionNotSupported": "Conversion Not Supported",
"convert": "Convert",
"convertingModelBegin": "Converting Model. Please wait.",
"convertToDiffusers": "Convert To Diffusers",
@ -754,6 +775,7 @@
"esrganModel": "ESRGAN Model",
"loading": "loading",
"noLoRAsAvailable": "No LoRAs available",
"noLoRAsLoaded":"No LoRAs Loaded",
"noMatchingLoRAs": "No matching LoRAs",
"noMatchingModels": "No matching Models",
"noModelsAvailable": "No models available",
@ -765,6 +787,7 @@
"nodes": {
"addNode": "Add Node",
"addNodeToolTip": "Add Node (Shift+A, Space)",
"addLinearView":"Add to Linear View",
"animatedEdges": "Animated Edges",
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
"boardField": "Board",
@ -885,6 +908,7 @@
"noMatchingNodes": "No matching nodes",
"noNodeSelected": "No node selected",
"nodeOpacity": "Node Opacity",
"nodeVersion": "Node Version",
"noOutputRecorded": "No outputs recorded",
"noOutputSchemaName": "No output schema name found in ref object",
"notes": "Notes",
@ -892,6 +916,7 @@
"oNNXModelField": "ONNX Model",
"oNNXModelFieldDescription": "ONNX model field.",
"outputField": "Output Field",
"outputFieldInInput": "Output field in input",
"outputFields": "Output Fields",
"outputNode": "Output node",
"outputSchemaNotFound": "Output schema not found",
@ -937,10 +962,14 @@
"uNetFieldDescription": "UNet submodel.",
"unhandledInputProperty": "Unhandled input property",
"unhandledOutputProperty": "Unhandled output property",
"unknownField": "Unknown Field",
"unknownField": "Unknown field",
"unknownFieldType": "$(nodes.unknownField) type",
"unknownNode": "Unknown Node",
"unknownNodeType":"$t(nodes.unknownNode) type",
"unknownTemplate": "Unknown Template",
"unknownInput": "Unknown input",
"unkownInvocation": "Unknown Invocation type",
"unknownOutput": "Unknown output",
"updateNode": "Update Node",
"updateAllNodes": "Update All Nodes",
"updateApp": "Update App",
@ -1171,7 +1200,8 @@
"clearIntermediatesWithCount_other": "Clear {{count}} Intermediates",
"intermediatesCleared_one": "Cleared {{count}} Intermediate",
"intermediatesCleared_other": "Cleared {{count}} Intermediates",
"intermediatesClearedFailed": "Problem Clearing Intermediates"
"intermediatesClearedFailed": "Problem Clearing Intermediates",
"reloadingIn": "Reloading in"
},
"toast": {
"addedToBoard": "Added to board",
@ -1199,6 +1229,7 @@
"initialImageNotSet": "Initial Image Not Set",
"initialImageNotSetDesc": "Could not load initial image",
"initialImageSet": "Initial Image Set",
"invalidUpload": "Invalid Upload",
"loadedWithWarnings": "Workflow Loaded with Warnings",
"maskSavedAssets": "Mask Saved to Assets",
"maskSentControlnetAssets": "Mask Sent to ControlNet & Assets",
@ -1517,7 +1548,7 @@
"clearCanvasHistoryConfirm": "Are you sure you want to clear the canvas history?",
"clearCanvasHistoryMessage": "Clearing the canvas history leaves your current canvas intact, but irreversibly clears the undo and redo history.",
"clearHistory": "Clear History",
"clearMask": "Clear Mask",
"clearMask": "Clear Mask (Shift+C)",
"colorPicker": "Color Picker",
"copyToClipboard": "Copy to Clipboard",
"cursorPosition": "Cursor Position",
@ -1544,6 +1575,7 @@
"redo": "Redo",
"resetView": "Reset View",
"saveBoxRegionOnly": "Save Box Region Only",
"saveMask":"Save $t(unifiedCanvas.mask)",
"saveToGallery": "Save To Gallery",
"scaledBoundingBox": "Scaled Bounding Box",
"showCanvasDebugInfo": "Show Additional Canvas Info",

View File

@ -2,6 +2,7 @@ import { Flex, Heading, Link, Text, useToast } from '@chakra-ui/react';
import IAIButton from 'common/components/IAIButton';
import newGithubIssueUrl from 'new-github-issue-url';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCopy, FaExternalLinkAlt } from 'react-icons/fa';
import { FaArrowRotateLeft } from 'react-icons/fa6';
import { serializeError } from 'serialize-error';
@ -13,6 +14,7 @@ type Props = {
const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
const toast = useToast();
const { t } = useTranslation();
const handleCopy = useCallback(() => {
const text = JSON.stringify(serializeError(error), null, 2);
@ -53,7 +55,7 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
p: 16,
}}
>
<Heading>Something went wrong</Heading>
<Heading>{t('common.somethingWentWrong')}</Heading>
<Flex
layerStyle="second"
sx={{
@ -80,13 +82,15 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
leftIcon={<FaArrowRotateLeft />}
onClick={resetErrorBoundary}
>
Reset UI
{t('accessibility.resetUI')}
</IAIButton>
<IAIButton leftIcon={<FaCopy />} onClick={handleCopy}>
Copy Error
{t('common.copyError')}
</IAIButton>
<Link href={url} isExternal>
<IAIButton leftIcon={<FaExternalLinkAlt />}>Create Issue</IAIButton>
<IAIButton leftIcon={<FaExternalLinkAlt />}>
{t('accessibility.createIssue')}
</IAIButton>
</Link>
</Flex>
</Flex>

View File

@ -1,6 +1,7 @@
import { Box, Flex, Heading } from '@chakra-ui/react';
import { memo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
type ImageUploadOverlayProps = {
isDragAccept: boolean;
@ -9,6 +10,7 @@ type ImageUploadOverlayProps = {
};
const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
const { t } = useTranslation();
const {
isDragAccept,
isDragReject: _isDragAccept,
@ -76,11 +78,13 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
}}
>
{isDragAccept ? (
<Heading size="lg">Drop to Upload</Heading>
<Heading size="lg">{t('gallery.dropToUpload')}</Heading>
) : (
<>
<Heading size="lg">Invalid Upload</Heading>
<Heading size="md">Must be single JPEG or PNG image</Heading>
<Heading size="lg">{t('toast.invalidUpload')}</Heading>
<Heading size="md">
{t('toast.uploadFailedInvalidUploadDesc')}
</Heading>
</>
)}
</Flex>

View File

@ -155,10 +155,10 @@ const IAICanvasMaskOptions = () => {
<IAIColorPicker color={maskColor} onChange={handleChangeMaskColor} />
</Box>
<IAIButton size="sm" leftIcon={<FaSave />} onClick={handleSaveMask}>
Save Mask
{t('unifiedCanvas.saveMask')}
</IAIButton>
<IAIButton size="sm" leftIcon={<FaTrash />} onClick={handleClearMask}>
{t('unifiedCanvas.clearMask')} (Shift+C)
{t('unifiedCanvas.clearMask')}
</IAIButton>
</Flex>
</IAIPopover>

View File

@ -110,8 +110,10 @@ const ChangeBoardModal = () => {
<AlertDialogBody>
<Flex sx={{ flexDir: 'column', gap: 4 }}>
<Text>
Moving {`${imagesToChange.length}`} image
{`${imagesToChange.length > 1 ? 's' : ''}`} to board:
{t('boards.movingImagesToBoard', {
count: imagesToChange.length,
})}
:
</Text>
<IAIMantineSearchableSelect
placeholder={

View File

@ -124,10 +124,10 @@ const DeleteImageModal = () => {
</AlertDialogBody>
<AlertDialogFooter>
<IAIButton ref={cancelRef} onClick={handleClose}>
Cancel
{t('boards.cancel')}
</IAIButton>
<IAIButton colorScheme="error" onClick={handleDelete} ml={3}>
Delete
{t('controlnet.delete')}
</IAIButton>
</AlertDialogFooter>
</AlertDialogContent>

View File

@ -1,6 +1,7 @@
import { Box, ChakraProps, Flex, Heading, Image, Text } from '@chakra-ui/react';
import { memo } from 'react';
import { TypesafeDraggableData } from '../types';
import { useTranslation } from 'react-i18next';
type OverlayDragImageProps = {
dragData: TypesafeDraggableData | null;
@ -26,6 +27,7 @@ const STYLES: ChakraProps['sx'] = {
};
const DragPreview = (props: OverlayDragImageProps) => {
const { t } = useTranslation();
if (!props.dragData) {
return null;
}
@ -89,7 +91,7 @@ const DragPreview = (props: OverlayDragImageProps) => {
}}
>
<Heading>{props.dragData.payload.imageDTOs.length}</Heading>
<Heading size="sm">Images</Heading>
<Heading size="sm">{t('parameters.images')}</Heading>
</Flex>
);
}

View File

@ -121,7 +121,7 @@ const ParamEmbeddingPopover = (props: Props) => {
_dark: { color: 'base.700' },
}}
>
<Text>No Embeddings Loaded</Text>
<Text>{t('embedding.noEmbeddingsLoaded')}</Text>
</Flex>
) : (
<IAIMantineSearchableSelect

View File

@ -32,6 +32,7 @@ import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { BoardDTO } from 'services/api/types';
import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu';
import { useTranslation } from 'react-i18next';
interface GalleryBoardProps {
board: BoardDTO;
@ -143,7 +144,7 @@ const GalleryBoard = ({
const handleChange = useCallback((newBoardName: string) => {
setLocalBoardName(newBoardName);
}, []);
const { t } = useTranslation();
return (
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
<Flex
@ -292,7 +293,9 @@ const GalleryBoard = ({
<IAIDroppable
data={droppableData}
dropLabel={<Text fontSize="md">Move</Text>}
dropLabel={
<Text fontSize="md">{t('unifiedCanvas.move')}</Text>
}
/>
</Flex>
</Tooltip>

View File

@ -19,6 +19,7 @@ import {
useGetBoardAssetsTotalQuery,
useGetBoardImagesTotalQuery,
} from 'services/api/endpoints/boards';
import { useTranslation } from 'react-i18next';
interface Props {
isSelected: boolean;
@ -71,7 +72,7 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
}),
[]
);
const { t } = useTranslation();
return (
<Box sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}>
<Flex
@ -161,7 +162,9 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
/>
<IAIDroppable
data={droppableData}
dropLabel={<Text fontSize="md">Move</Text>}
dropLabel={
<Text fontSize="md">{t('unifiedCanvas.move')}</Text>
}
/>
</Flex>
</Tooltip>

View File

@ -111,7 +111,7 @@ const MultipleSelectionMenuItems = () => {
icon={<FaTrash />}
onClickCapture={handleDeleteSelection}
>
Delete Selection
{t('gallery.deleteSelection')}
</MenuItem>
</>
);

View File

@ -113,7 +113,7 @@ const ImageGalleryContent = () => {
leftIcon={<FaImages />}
data-testid="images-tab"
>
{t('gallery.images')}
{t('parameters.images')}
</Tab>
<Tab
as={IAIButton}

View File

@ -49,7 +49,7 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
}}
>
<Flex gap={2}>
<Text fontWeight="semibold">File:</Text>
<Text fontWeight="semibold">{t('common.file')}:</Text>
<Link href={image.image_url} isExternal maxW="calc(100% - 3rem)">
{image.image_name}
<ExternalLinkIcon mx="2px" />

View File

@ -165,7 +165,7 @@ export default function FoundModelsList() {
},
}}
>
Installed
{t('common.installed')}
</Text>
)}
</Flex>
@ -215,7 +215,7 @@ export default function FoundModelsList() {
/>
<Flex p={2} gap={2}>
<Text sx={{ fontWeight: 600 }}>
Models Found: {foundModels.length}
{t('modelManager.modelsFound')}: {foundModels.length}
</Text>
<Text
sx={{
@ -226,7 +226,7 @@ export default function FoundModelsList() {
},
}}
>
Not Installed: {filteredModels.length}
{t('common.notInstalled')}: {filteredModels.length}
</Text>
</Flex>

View File

@ -76,7 +76,7 @@ function SearchFolderForm() {
_dark: { color: 'base.300' },
}}
>
Folder
{t('common.folder')}
</Text>
{!searchFolder ? (
<IAIInput

View File

@ -114,7 +114,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
{model.model_name}
</Text>
<Text fontSize="sm" color="base.400">
{MODEL_TYPE_MAP[model.base_model]} Model
{MODEL_TYPE_MAP[model.base_model]} {t('modelManager.model')}
</Text>
</Flex>
{![''].includes(model.base_model) ? (
@ -128,7 +128,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) {
_dark: { bg: 'error.400' },
}}
>
Conversion Not Supported
{t('modelManager.conversionNotSupported')}
</Badge>
)}
</Flex>

View File

@ -95,7 +95,7 @@ export default function DiffusersModelEdit(props: DiffusersModelEditProps) {
{model.model_name}
</Text>
<Text fontSize="sm" color="base.400">
{MODEL_TYPE_MAP[model.base_model]} Model
{MODEL_TYPE_MAP[model.base_model]} {t('modelManager.model')}
</Text>
</Flex>
<Divider />

View File

@ -95,8 +95,8 @@ export default function LoRAModelEdit(props: LoRAModelEditProps) {
{model.model_name}
</Text>
<Text fontSize="sm" color="base.400">
{MODEL_TYPE_MAP[model.base_model]} Model {' '}
{LORA_MODEL_FORMAT_MAP[model.model_format]} format
{MODEL_TYPE_MAP[model.base_model]} {t('modelManager.model')} {' '}
{LORA_MODEL_FORMAT_MAP[model.model_format]} {t('common.format')}
</Text>
</Flex>
<Divider />

View File

@ -10,6 +10,7 @@ import IAIDndImage from 'common/components/IAIDndImage';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants';
import { stateSelector } from 'app/store/store';
import { useTranslation } from 'react-i18next';
const selector = createSelector(stateSelector, ({ system, gallery }) => {
const imageDTO = gallery.selection[gallery.selection.length - 1];
@ -66,7 +67,7 @@ const Wrapper = (props: PropsWithChildren<{ nodeProps: NodeProps }>) => {
const handleMouseLeave = useCallback(() => {
setIsHovering(false);
}, []);
const { t } = useTranslation();
return (
<NodeWrapper
nodeId={props.nodeProps.id}
@ -99,7 +100,7 @@ const Wrapper = (props: PropsWithChildren<{ nodeProps: NodeProps }>) => {
_dark: { color: 'base.200' },
}}
>
Current Image
{t('nodes.currentImage')}
</Text>
</Flex>
<Flex

View File

@ -4,8 +4,10 @@ import { useEmbedWorkflow } from 'features/nodes/hooks/useEmbedWorkflow';
import { useWithWorkflow } from 'features/nodes/hooks/useWithWorkflow';
import { nodeEmbedWorkflowChanged } from 'features/nodes/store/nodesSlice';
import { ChangeEvent, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const EmbedWorkflowCheckbox = ({ nodeId }: { nodeId: string }) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const withWorkflow = useWithWorkflow(nodeId);
const embedWorkflow = useEmbedWorkflow(nodeId);
@ -27,7 +29,9 @@ const EmbedWorkflowCheckbox = ({ nodeId }: { nodeId: string }) => {
return (
<FormControl as={Flex} sx={{ alignItems: 'center', gap: 2, w: 'auto' }}>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>Workflow</FormLabel>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>
{t('metadata.workflow')}
</FormLabel>
<Checkbox
className="nopan"
size="sm"

View File

@ -75,7 +75,7 @@ const TooltipLabel = memo(({ nodeExecutionState }: TooltipLabelProps) => {
const { status, progress, progressImage } = nodeExecutionState;
const { t } = useTranslation();
if (status === NodeStatus.PENDING) {
return <Text>Pending</Text>;
return <Text>{t('queue.pending')}</Text>;
}
if (status === NodeStatus.IN_PROGRESS) {
if (progressImage) {

View File

@ -3,6 +3,7 @@ import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants';
import { memo } from 'react';
import NodeCollapseButton from '../common/NodeCollapseButton';
import NodeWrapper from '../common/NodeWrapper';
import { useTranslation } from 'react-i18next';
type Props = {
nodeId: string;
@ -19,6 +20,7 @@ const InvocationNodeUnknownFallback = ({
type,
selected,
}: Props) => {
const { t } = useTranslation();
return (
<NodeWrapper nodeId={nodeId} selected={selected}>
<Flex
@ -61,7 +63,7 @@ const InvocationNodeUnknownFallback = ({
}}
>
<Box>
<Text as="span">Unknown node type: </Text>
<Text as="span">{t('nodes.unknownNodeType')}: </Text>
<Text as="span" fontWeight={600}>
{type}
</Text>

View File

@ -4,8 +4,10 @@ import { useHasImageOutput } from 'features/nodes/hooks/useHasImageOutput';
import { useIsIntermediate } from 'features/nodes/hooks/useIsIntermediate';
import { nodeIsIntermediateChanged } from 'features/nodes/store/nodesSlice';
import { ChangeEvent, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const SaveToGalleryCheckbox = ({ nodeId }: { nodeId: string }) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const hasImageOutput = useHasImageOutput(nodeId);
const isIntermediate = useIsIntermediate(nodeId);
@ -27,7 +29,9 @@ const SaveToGalleryCheckbox = ({ nodeId }: { nodeId: string }) => {
return (
<FormControl as={Flex} sx={{ alignItems: 'center', gap: 2, w: 'auto' }}>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>Save to Gallery</FormLabel>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>
{t('hotkeys.saveToGallery.title')}
</FormLabel>
<Checkbox
className="nopan"
size="sm"

View File

@ -3,6 +3,7 @@ import { useAppDispatch } from 'app/store/storeHooks';
import { useUseCache } from 'features/nodes/hooks/useUseCache';
import { nodeUseCacheChanged } from 'features/nodes/store/nodesSlice';
import { ChangeEvent, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
const UseCacheCheckbox = ({ nodeId }: { nodeId: string }) => {
const dispatch = useAppDispatch();
@ -18,10 +19,12 @@ const UseCacheCheckbox = ({ nodeId }: { nodeId: string }) => {
},
[dispatch, nodeId]
);
const { t } = useTranslation();
return (
<FormControl as={Flex} sx={{ alignItems: 'center', gap: 2, w: 'auto' }}>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>Use Cache</FormLabel>
<FormLabel sx={{ fontSize: 'xs', mb: '1px' }}>
{t('invocationCache.useCache')}
</FormLabel>
<Checkbox
className="nopan"
size="sm"

View File

@ -79,7 +79,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
icon={<FaPlus />}
onClick={handleExposeField}
>
Add to Linear View
{t('nodes.addLinearView')}
</MenuItem>
);
}
@ -90,7 +90,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
icon={<FaMinus />}
onClick={handleUnexposeField}
>
Remove from Linear View
{t('nodes.removeLinearView')}
</MenuItem>
);
}
@ -102,6 +102,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
isExposed,
mayExpose,
nodeId,
t,
]);
const renderMenuFunc = useCallback(

View File

@ -49,8 +49,16 @@ const FieldTooltipContent = ({ nodeId, fieldName, kind }: Props) => {
{fieldTemplate.description}
</Text>
)}
{fieldTemplate && <Text>Type: {FIELDS[fieldTemplate.type].title}</Text>}
{isInputTemplate && <Text>Input: {startCase(fieldTemplate.input)}</Text>}
{fieldTemplate && (
<Text>
{t('parameters.type')}: {FIELDS[fieldTemplate.type].title}
</Text>
)}
{isInputTemplate && (
<Text>
{t('common.input')}: {startCase(fieldTemplate.input)}
</Text>
)}
</Flex>
);
};

View File

@ -7,6 +7,7 @@ import EditableFieldTitle from './EditableFieldTitle';
import FieldContextMenu from './FieldContextMenu';
import FieldHandle from './FieldHandle';
import InputFieldRenderer from './InputFieldRenderer';
import { useTranslation } from 'react-i18next';
interface Props {
nodeId: string;
@ -14,6 +15,7 @@ interface Props {
}
const InputField = ({ nodeId, fieldName }: Props) => {
const { t } = useTranslation();
const fieldTemplate = useFieldTemplate(nodeId, fieldName, 'input');
const doesFieldHaveValue = useDoesInputHaveValue(nodeId, fieldName);
@ -49,7 +51,7 @@ const InputField = ({ nodeId, fieldName }: Props) => {
<FormControl
sx={{ color: 'error.400', textAlign: 'left', fontSize: 'sm' }}
>
Unknown input: {fieldName}
{t('nodes.unknownInput')}: {fieldName}
</FormControl>
</InputFieldWrapper>
);

View File

@ -18,6 +18,7 @@ import VaeModelInputField from './inputs/VaeModelInputField';
import IPAdapterModelInputField from './inputs/IPAdapterModelInputField';
import T2IAdapterModelInputField from './inputs/T2IAdapterModelInputField';
import BoardInputField from './inputs/BoardInputField';
import { useTranslation } from 'react-i18next';
type InputFieldProps = {
nodeId: string;
@ -25,11 +26,16 @@ type InputFieldProps = {
};
const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
const { t } = useTranslation();
const field = useFieldData(nodeId, fieldName);
const fieldTemplate = useFieldTemplate(nodeId, fieldName, 'input');
if (fieldTemplate?.fieldKind === 'output') {
return <Box p={2}>Output field in input: {field?.type}</Box>;
return (
<Box p={2}>
{t('nodes.outputFieldInInput')}: {field?.type}
</Box>
);
}
if (
@ -249,7 +255,7 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
_dark: { color: 'error.300' },
}}
>
Unknown field type: {field?.type}
{t('nodes.unknownFieldType')}: {field?.type}
</Text>
</Box>
);

View File

@ -5,6 +5,7 @@ import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';
import { PropsWithChildren, memo } from 'react';
import FieldHandle from './FieldHandle';
import FieldTooltipContent from './FieldTooltipContent';
import { useTranslation } from 'react-i18next';
interface Props {
nodeId: string;
@ -12,6 +13,7 @@ interface Props {
}
const OutputField = ({ nodeId, fieldName }: Props) => {
const { t } = useTranslation();
const fieldTemplate = useFieldTemplate(nodeId, fieldName, 'output');
const {
@ -28,7 +30,7 @@ const OutputField = ({ nodeId, fieldName }: Props) => {
<FormControl
sx={{ color: 'error.400', textAlign: 'right', fontSize: 'sm' }}
>
Unknown output: {fieldName}
{t('nodes.unknownOutput')}: {fieldName}
</FormControl>
</OutputFieldWrapper>
);

View File

@ -16,6 +16,7 @@ import {
ImagePolymorphicInputFieldValue,
} from 'features/nodes/types/types';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { FaUndo } from 'react-icons/fa';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { PostUploadAction } from 'services/api/types';
@ -103,18 +104,24 @@ const ImageInputFieldComponent = (
export default memo(ImageInputFieldComponent);
const UploadElement = memo(() => (
<Text fontSize={16} fontWeight={600}>
Drop or Upload
</Text>
));
const UploadElement = memo(() => {
const { t } = useTranslation();
return (
<Text fontSize={16} fontWeight={600}>
{t('gallery.dropOrUpload')}
</Text>
);
});
UploadElement.displayName = 'UploadElement';
const DropLabel = memo(() => (
<Text fontSize={16} fontWeight={600}>
Drop
</Text>
));
const DropLabel = memo(() => {
const { t } = useTranslation();
return (
<Text fontSize={16} fontWeight={600}>
{t('gallery.drop')}
</Text>
);
});
DropLabel.displayName = 'DropLabel';

View File

@ -91,7 +91,7 @@ const LoRAModelInputFieldComponent = (
return (
<Flex sx={{ justifyContent: 'center', p: 2 }}>
<Text sx={{ fontSize: 'sm', color: 'base.500', _dark: 'base.700' }}>
No LoRAs Loaded
{t('models.noLoRAsLoaded')}
</Text>
</Flex>
);

View File

@ -90,7 +90,7 @@ const Content = (props: {
<EditableNodeTitle nodeId={props.node.data.id} />
<HStack>
<FormControl>
<FormLabel>Node Type</FormLabel>
<FormLabel>{t('nodes.nodeType')}</FormLabel>
<Text fontSize="sm" fontWeight={600}>
{props.template.title}
</Text>
@ -102,7 +102,7 @@ const Content = (props: {
w="full"
>
<FormControl isInvalid={needsUpdate}>
<FormLabel>Node Version</FormLabel>
<FormLabel>{t('nodes.nodeVersion')}</FormLabel>
<Text fontSize="sm" fontWeight={600}>
{props.node.data.version}
</Text>

View File

@ -10,6 +10,7 @@ import { memo, useCallback } from 'react';
import { FaUndo, FaUpload } from 'react-icons/fa';
import InitialImage from './InitialImage';
import { PostUploadAction } from 'services/api/types';
import { useTranslation } from 'react-i18next';
const selector = createSelector(
[stateSelector],
@ -38,6 +39,8 @@ const InitialImageDisplay = () => {
dispatch(clearInitialImage());
}, [dispatch]);
const { t } = useTranslation();
return (
<Flex
layerStyle="first"
@ -73,7 +76,7 @@ const InitialImageDisplay = () => {
},
}}
>
Initial Image
{t('metadata.initImage')}
</Text>
<Spacer />
<IAIIconButton

View File

@ -106,7 +106,7 @@ const QueueItemComponent = ({ queueItemDTO }: Props) => {
flexDir="column"
>
<Heading size="sm" color="error.500" _dark={{ color: 'error.400' }}>
Error
{t('common.error')}
</Heading>
<pre>{queueItem.error}</pre>
</Flex>

View File

@ -1,3 +1,5 @@
/* eslint-disable i18next/no-literal-string */
import { Flex, Image, Text } from '@chakra-ui/react';
import InvokeAILogoImage from 'assets/images/logo.png';
import { AnimatePresence, motion } from 'framer-motion';

View File

@ -423,7 +423,8 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
<Flex justifyContent="center">
<Text fontSize="lg">
<Text>
{t('settings.resetComplete')} Reloading in {countdown}...
{t('settings.resetComplete')} {t('settings.reloadingIn')}{' '}
{countdown}...
</Text>
</Text>
</Flex>

View File

@ -3517,6 +3517,14 @@ eslint-config-prettier@^9.0.0:
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f"
integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==
eslint-plugin-i18next@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-i18next/-/eslint-plugin-i18next-6.0.3.tgz#a388f9982deb040102c1c4498e4dc38d9bd6ffd9"
integrity sha512-RtQXYfg6PZCjejIQ/YG+dUj/x15jPhufJ9hUDGH0kCpJ6CkVMAWOQ9exU1CrbPmzeykxLjrXkjAaOZF/V7+DOA==
dependencies:
lodash "^4.17.21"
requireindex "~1.1.0"
eslint-plugin-react-hooks@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
@ -5617,6 +5625,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
requireindex@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
integrity sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==
requirejs-config-file@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc"