Missed Translations

This commit is contained in:
mickr777
2023-09-13 21:15:36 +10:00
parent 0f0366f1f3
commit 3920d5c90d
20 changed files with 323 additions and 150 deletions

View File

@ -1,4 +1,5 @@
import { FieldType, FieldUIConfig } from './types';
import i18n from 'i18next';
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
export const COLOR_TOKEN_VALUE = 500;
@ -102,73 +103,73 @@ export const isPolymorphicItemType = (
export const FIELDS: Record<FieldType, FieldUIConfig> = {
boolean: {
color: 'green.500',
description: 'Booleans are true or false.',
title: 'Boolean',
description: i18n.t('nodes.booleanDescription'),
title: i18n.t('nodes.boolean'),
},
BooleanCollection: {
color: 'green.500',
description: 'A collection of booleans.',
title: 'Boolean Collection',
description: i18n.t('nodes.booleanCollectionDescription'),
title: i18n.t('nodes.booleanCollection'),
},
BooleanPolymorphic: {
color: 'green.500',
description: 'A collection of booleans.',
title: 'Boolean Polymorphic',
description: i18n.t('nodes.booleanPolymorphicDescription'),
title: i18n.t('nodes.booleanPolymorphic'),
},
ClipField: {
color: 'green.500',
description: 'Tokenizer and text_encoder submodels.',
title: 'Clip',
description: i18n.t('nodes.clipFieldDescription'),
title: i18n.t('nodes.clipField'),
},
Collection: {
color: 'base.500',
description: 'TODO',
title: 'Collection',
description: i18n.t('nodes.collectionDescription'),
title: i18n.t('nodes.collection'),
},
CollectionItem: {
color: 'base.500',
description: 'TODO',
title: 'Collection Item',
description: i18n.t('nodes.collectionItemDescription'),
title: i18n.t('nodes.collectionItem'),
},
ColorCollection: {
color: 'pink.300',
description: 'A collection of colors.',
title: 'Color Collection',
description: i18n.t('nodes.colorCollectionDescription'),
title: i18n.t('nodes.colorCollection'),
},
ColorField: {
color: 'pink.300',
description: 'A RGBA color.',
title: 'Color',
description: i18n.t('nodes.colorFieldDescription'),
title: i18n.t('nodes.colorField'),
},
ColorPolymorphic: {
color: 'pink.300',
description: 'A collection of colors.',
title: 'Color Polymorphic',
description: i18n.t('nodes.colorPolymorphicDescription'),
title: i18n.t('nodes.colorPolymorphic'),
},
ConditioningCollection: {
color: 'cyan.500',
description: 'Conditioning may be passed between nodes.',
title: 'Conditioning Collection',
description: i18n.t('nodes.conditioningCollectionDescription'),
title: i18n.t('nodes.conditioningCollection'),
},
ConditioningField: {
color: 'cyan.500',
description: 'Conditioning may be passed between nodes.',
title: 'Conditioning',
description: i18n.t('nodes.conditioningFieldDescription'),
title: i18n.t('nodes.conditioningField'),
},
ConditioningPolymorphic: {
color: 'cyan.500',
description: 'Conditioning may be passed between nodes.',
title: 'Conditioning Polymorphic',
description: i18n.t('nodes.conditioningPolymorphicDescription'),
title: i18n.t('nodes.conditioningPolymorphic'),
},
ControlCollection: {
color: 'teal.500',
description: 'Control info passed between nodes.',
title: 'Control Collection',
description: i18n.t('nodes.controlCollectionDescription'),
title: i18n.t('nodes.controlCollection'),
},
ControlField: {
color: 'teal.500',
description: 'Control info passed between nodes.',
title: 'Control',
description: i18n.t('nodes.controlFieldDescription'),
title: i18n.t('nodes.controlField'),
},
ControlNetModelField: {
color: 'teal.500',
@ -182,132 +183,132 @@ export const FIELDS: Record<FieldType, FieldUIConfig> = {
},
DenoiseMaskField: {
color: 'blue.300',
description: 'Denoise Mask may be passed between nodes',
title: 'Denoise Mask',
description: i18n.t('nodes.denoiseMaskFieldDescription'),
title: i18n.t('nodes.denoiseMaskField'),
},
enum: {
color: 'blue.500',
description: 'Enums are values that may be one of a number of options.',
title: 'Enum',
description: i18n.t('nodes.enumDescription'),
title: i18n.t('nodes.enum'),
},
float: {
color: 'orange.500',
description: 'Floats are numbers with a decimal point.',
title: 'Float',
description: i18n.t('nodes.floatDescription'),
title: i18n.t('nodes.float'),
},
FloatCollection: {
color: 'orange.500',
description: 'A collection of floats.',
title: 'Float Collection',
description: i18n.t('nodes.floatCollectionDescription'),
title: i18n.t('nodes.floatCollection'),
},
FloatPolymorphic: {
color: 'orange.500',
description: 'A collection of floats.',
title: 'Float Polymorphic',
description: i18n.t('nodes.floatPolymorphicDescription'),
title: i18n.t('nodes.floatPolymorphic'),
},
ImageCollection: {
color: 'purple.500',
description: 'A collection of images.',
title: 'Image Collection',
description: i18n.t('nodes.imageCollectionDescription'),
title: i18n.t('nodes.imageCollection'),
},
ImageField: {
color: 'purple.500',
description: 'Images may be passed between nodes.',
title: 'Image',
description: i18n.t('nodes.imageFieldDescription'),
title: i18n.t('nodes.imageField'),
},
ImagePolymorphic: {
color: 'purple.500',
description: 'A collection of images.',
title: 'Image Polymorphic',
description: i18n.t('nodes.imagePolymorphicDescription'),
title: i18n.t('nodes.imagePolymorphic'),
},
integer: {
color: 'red.500',
description: 'Integers are whole numbers, without a decimal point.',
title: 'Integer',
description: i18n.t('nodes.integerDescription'),
title: i18n.t('nodes.integer'),
},
IntegerCollection: {
color: 'red.500',
description: 'A collection of integers.',
title: 'Integer Collection',
description: i18n.t('nodes.integerCollectionDescription'),
title: i18n.t('nodes.integerCollection'),
},
IntegerPolymorphic: {
color: 'red.500',
description: 'A collection of integers.',
title: 'Integer Polymorphic',
description: i18n.t('nodes.integerPolymorphicDescription'),
title: i18n.t('nodes.integerPolymorphic'),
},
LatentsCollection: {
color: 'pink.500',
description: 'Latents may be passed between nodes.',
title: 'Latents Collection',
description: i18n.t('nodes.latentsCollectionDescription'),
title: i18n.t('nodes.latentsCollection'),
},
LatentsField: {
color: 'pink.500',
description: 'Latents may be passed between nodes.',
title: 'Latents',
description: i18n.t('nodes.latentsFieldDescription'),
title: i18n.t('nodes.latentsField'),
},
LatentsPolymorphic: {
color: 'pink.500',
description: 'Latents may be passed between nodes.',
title: 'Latents Polymorphic',
description: i18n.t('nodes.latentsPolymorphicDescription'),
title: i18n.t('nodes.latentsPolymorphic'),
},
LoRAModelField: {
color: 'teal.500',
description: 'TODO',
title: 'LoRA',
description: i18n.t('nodes.loRAModelFieldDescription'),
title: i18n.t('nodes.loRAModelField'),
},
MainModelField: {
color: 'teal.500',
description: 'TODO',
title: 'Model',
description: i18n.t('nodes.mainModelFieldDescription'),
title: i18n.t('nodes.mainModelField'),
},
ONNXModelField: {
color: 'teal.500',
description: 'ONNX model field.',
title: 'ONNX Model',
description: i18n.t('nodes.oNNXModelFieldDescription'),
title: i18n.t('nodes.oNNXModelField'),
},
Scheduler: {
color: 'base.500',
description: 'TODO',
title: 'Scheduler',
description: i18n.t('nodes.schedulerDescription'),
title: i18n.t('nodes.scheduler'),
},
SDXLMainModelField: {
color: 'teal.500',
description: 'SDXL model field.',
title: 'SDXL Model',
description: i18n.t('nodes.sDXLMainModelFieldDescription'),
title: i18n.t('nodes.sDXLMainModelField'),
},
SDXLRefinerModelField: {
color: 'teal.500',
description: 'TODO',
title: 'Refiner Model',
description: i18n.t('nodes.sDXLRefinerModelFieldDescription'),
title: i18n.t('nodes.sDXLRefinerModelField'),
},
string: {
color: 'yellow.500',
description: 'Strings are text.',
title: 'String',
description: i18n.t('nodes.stringDescription'),
title: i18n.t('nodes.string'),
},
StringCollection: {
color: 'yellow.500',
description: 'A collection of strings.',
title: 'String Collection',
description: i18n.t('nodes.stringCollectionDescription'),
title: i18n.t('nodes.stringCollection'),
},
StringPolymorphic: {
color: 'yellow.500',
description: 'A collection of strings.',
title: 'String Polymorphic',
description: i18n.t('nodes.stringPolymorphicDescription'),
title: i18n.t('nodes.stringPolymorphic'),
},
UNetField: {
color: 'red.500',
description: 'UNet submodel.',
title: 'UNet',
description: i18n.t('nodes.uNetFieldDescription'),
title: i18n.t('nodes.uNetField'),
},
VaeField: {
color: 'blue.500',
description: 'Vae submodel.',
title: 'Vae',
description: i18n.t('nodes.vaeFieldDescription'),
title: i18n.t('nodes.vaeField'),
},
VaeModelField: {
color: 'teal.500',
description: 'TODO',
title: 'VAE',
description: i18n.t('nodes.vaeModelFieldDescription'),
title: i18n.t('nodes.vaeModelField'),
},
};

View File

@ -20,6 +20,7 @@ import {
import { O } from 'ts-toolbelt';
import { JsonObject } from 'type-fest';
import { z } from 'zod';
import i18n from 'i18next';
export type NonNullableGraph = O.Required<Graph, 'nodes' | 'edges'>;
@ -1258,23 +1259,35 @@ export const zValidatedWorkflow = zWorkflow.transform((workflow) => {
const targetNode = keyedNodes[edge.target];
const issues: string[] = [];
if (!sourceNode) {
issues.push(`Output node ${edge.source} does not exist`);
issues.push(
`${i18n.t('nodes.outputNode')} ${edge.source} ${i18n.t(
'nodes.doesNotExist'
)}`
);
} else if (
edge.type === 'default' &&
!(edge.sourceHandle in sourceNode.data.outputs)
) {
issues.push(
`Output field "${edge.source}.${edge.sourceHandle}" does not exist`
`${i18n.t('nodes.outputField')}"${edge.source}.${
edge.sourceHandle
}" ${i18n.t('nodes.doesNotExist')}`
);
}
if (!targetNode) {
issues.push(`Input node ${edge.target} does not exist`);
issues.push(
`${i18n.t('nodes.inputNode')} ${edge.target} ${i18n.t(
'nodes.doesNotExist'
)}`
);
} else if (
edge.type === 'default' &&
!(edge.targetHandle in targetNode.data.inputs)
) {
issues.push(
`Input field "${edge.target}.${edge.targetHandle}" does not exist`
`${i18n.t('nodes.inputField')} "${edge.target}.${
edge.targetHandle
}" ${i18n.t('nodes.doesNotExist')}`
);
}
if (issues.length) {
@ -1282,7 +1295,9 @@ export const zValidatedWorkflow = zWorkflow.transform((workflow) => {
const src = edge.type === 'default' ? edge.sourceHandle : edge.source;
const tgt = edge.type === 'default' ? edge.targetHandle : edge.target;
warnings.push({
message: `Edge "${src} -> ${tgt}" skipped`,
message: `${i18n.t('nodes.edge')} "${src} -> ${tgt}" ${i18n.t(
'nodes.skipped'
)}`,
issues,
data: edge,
});

View File

@ -3,6 +3,7 @@ import { NodesState } from '../store/types';
import { Workflow, zWorkflowEdge, zWorkflowNode } from '../types/types';
import { fromZodError } from 'zod-validation-error';
import { parseify } from 'common/util/serialize';
import i18n from 'i18next';
export const buildWorkflow = (nodesState: NodesState): Workflow => {
const { workflow: workflowMeta, nodes, edges } = nodesState;
@ -20,7 +21,7 @@ export const buildWorkflow = (nodesState: NodesState): Workflow => {
const result = zWorkflowNode.safeParse(node);
if (!result.success) {
const { message } = fromZodError(result.error, {
prefix: 'Unable to parse node',
prefix: i18n.t('nodes.unableToParseNode'),
});
logger('nodes').warn({ node: parseify(node) }, message);
return;
@ -32,7 +33,7 @@ export const buildWorkflow = (nodesState: NodesState): Workflow => {
const result = zWorkflowEdge.safeParse(edge);
if (!result.success) {
const { message } = fromZodError(result.error, {
prefix: 'Unable to parse edge',
prefix: i18n.t('nodes.unableToParseEdge'),
});
logger('nodes').warn({ edge: parseify(edge) }, message);
return;

View File

@ -7,6 +7,7 @@ import {
zWorkflow,
} from 'features/nodes/types/types';
import { get } from 'lodash-es';
import i18n from 'i18next';
export const getMetadataAndWorkflowFromImageBlob = async (
image: Blob
@ -23,7 +24,7 @@ export const getMetadataAndWorkflowFromImageBlob = async (
} else {
logger('system').error(
{ error: parseify(metadataResult.error) },
'Problem reading metadata from image'
i18n.t('nodes.problemReadingMetadata')
);
}
}
@ -36,7 +37,7 @@ export const getMetadataAndWorkflowFromImageBlob = async (
} else {
logger('system').error(
{ error: parseify(workflowResult.error) },
'Problem reading workflow from image'
i18n.t('nodes.problemReadingWorkflow')
);
}
}

View File

@ -9,6 +9,7 @@ import { buildCanvasSDXLInpaintGraph } from './buildCanvasSDXLInpaintGraph';
import { buildCanvasSDXLOutpaintGraph } from './buildCanvasSDXLOutpaintGraph';
import { buildCanvasSDXLTextToImageGraph } from './buildCanvasSDXLTextToImageGraph';
import { buildCanvasTextToImageGraph } from './buildCanvasTextToImageGraph';
import i18n from 'i18next';
export const buildCanvasGraph = (
state: RootState,
@ -29,7 +30,7 @@ export const buildCanvasGraph = (
}
} else if (generationMode === 'img2img') {
if (!canvasInitImage) {
throw new Error('Missing canvas init image');
throw new Error(i18n.t('nodes.missingCanvaInitImage'));
}
if (
state.generation.model &&
@ -41,7 +42,7 @@ export const buildCanvasGraph = (
}
} else if (generationMode === 'inpaint') {
if (!canvasInitImage || !canvasMaskImage) {
throw new Error('Missing canvas init and mask images');
throw new Error(i18n.t('nodes.missingCanvaInitMaskImages'));
}
if (
state.generation.model &&
@ -57,7 +58,7 @@ export const buildCanvasGraph = (
}
} else {
if (!canvasInitImage) {
throw new Error('Missing canvas init image');
throw new Error(i18n.t('nodes.missingCanvaInitImage'));
}
if (
state.generation.model &&

View File

@ -25,6 +25,7 @@ import {
POSITIVE_CONDITIONING,
SEAMLESS,
} from './constants';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Image to Image graph.
@ -66,8 +67,8 @@ export const buildCanvasImageToImageGraph = (
);
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
let modelLoaderNodeId = MAIN_MODEL_LOADER;

View File

@ -44,6 +44,7 @@ import {
RANGE_OF_SIZE,
SEAMLESS,
} from './constants';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Inpaint graph.
@ -79,8 +80,8 @@ export const buildCanvasInpaintGraph = (
} = state.generation;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noImageFoundState'));
throw new Error(i18n.t('nodes.noImageFoundState'));
}
// The bounding box determines width and height, not the width and height params

View File

@ -46,6 +46,7 @@ import {
RANGE_OF_SIZE,
SEAMLESS,
} from './constants';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Outpaint graph.
@ -83,8 +84,8 @@ export const buildCanvasOutpaintGraph = (
} = state.generation;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noImageFoundState'));
throw new Error(i18n.t('nodes.noImageFoundState'));
}
// The bounding box determines width and height, not the width and height params

View File

@ -27,6 +27,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Image to Image graph.
@ -74,8 +75,8 @@ export const buildCanvasSDXLImageToImageGraph = (
);
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
// Model Loader ID

View File

@ -46,6 +46,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Inpaint graph.
@ -86,8 +87,8 @@ export const buildCanvasSDXLInpaintGraph = (
} = state.sdxl;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
// The bounding box determines width and height, not the width and height params

View File

@ -48,6 +48,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Outpaint graph.
@ -90,8 +91,8 @@ export const buildCanvasSDXLOutpaintGraph = (
} = state.sdxl;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
// The bounding box determines width and height, not the width and height params

View File

@ -29,6 +29,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Text to Image graph.
@ -71,8 +72,8 @@ export const buildCanvasSDXLTextToImageGraph = (
state.sdxl;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const use_cpu = shouldUseNoiseSettings

View File

@ -27,6 +27,7 @@ import {
POSITIVE_CONDITIONING,
SEAMLESS,
} from './constants';
import i18n from 'i18next';
/**
* Builds the Canvas tab's Text to Image graph.
@ -66,8 +67,8 @@ export const buildCanvasTextToImageGraph = (
);
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const use_cpu = shouldUseNoiseSettings

View File

@ -27,6 +27,7 @@ import {
RESIZE,
SEAMLESS,
} from './constants';
import i18n from 'i18next';
/**
* Builds the Image to Image tab graph.
@ -75,13 +76,13 @@ export const buildLinearImageToImageGraph = (
*/
if (!initialImage) {
log.error('No initial image found in state');
throw new Error('No initial image found in state');
log.error(i18n.t('nodes.noImageFoundState'));
throw new Error(i18n.t('nodes.noImageFoundState'));
}
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const fp32 = vaePrecision === 'fp32';

View File

@ -29,6 +29,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
/**
* Builds the Image to Image tab graph.
@ -75,13 +76,13 @@ export const buildLinearSDXLImageToImageGraph = (
*/
if (!initialImage) {
log.error('No initial image found in state');
throw new Error('No initial image found in state');
log.error(i18n.t('nodes.noImageFoundState'));
throw new Error(i18n.t('nodes.noImageFoundState'));
}
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const fp32 = vaePrecision === 'fp32';

View File

@ -23,6 +23,7 @@ import {
SEAMLESS,
} from './constants';
import { craftSDXLStylePrompt } from './helpers/craftSDXLStylePrompt';
import i18n from 'i18next';
export const buildLinearSDXLTextToImageGraph = (
state: RootState
@ -58,8 +59,8 @@ export const buildLinearSDXLTextToImageGraph = (
: initialGenerationState.shouldUseCpuNoise;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const fp32 = vaePrecision === 'fp32';

View File

@ -26,6 +26,7 @@ import {
SEAMLESS,
TEXT_TO_IMAGE_GRAPH,
} from './constants';
import i18n from 'i18next';
export const buildLinearTextToImageGraph = (
state: RootState
@ -53,8 +54,8 @@ export const buildLinearTextToImageGraph = (
: initialGenerationState.shouldUseCpuNoise;
if (!model) {
log.error('No model found in state');
throw new Error('No model found in state');
log.error(i18n.t('nodes.noModelFoundState'));
throw new Error(i18n.t('nodes.noModelFoundState'));
}
const fp32 = vaePrecision === 'fp32';

View File

@ -15,6 +15,7 @@ import {
isInvocationSchemaObject,
} from '../types/types';
import { buildInputFieldTemplate, getFieldType } from './fieldTemplateBuilders';
import i18n from 'i18next';
const RESERVED_INPUT_FIELD_NAMES = ['id', 'type', 'metadata'];
const RESERVED_OUTPUT_FIELD_NAMES = ['type'];
@ -97,7 +98,7 @@ export const parseSchema = (
if (isReservedInputField(type, propertyName)) {
logger('nodes').trace(
{ node: type, fieldName: propertyName, field: parseify(property) },
'Skipped reserved input field'
i18n.t('nodes.skippedReservedInput')
);
return inputsAccumulator;
}
@ -105,7 +106,7 @@ export const parseSchema = (
if (!isInvocationFieldSchema(property)) {
logger('nodes').warn(
{ node: type, propertyName, property: parseify(property) },
'Unhandled input property'
i18n.t('nodes.unhandledInputProperty')
);
return inputsAccumulator;
}
@ -120,7 +121,7 @@ export const parseSchema = (
fieldType,
field: parseify(property),
},
'Skipping unknown input field type'
i18n.t('nodes.skippingUnknownInputType')
);
return inputsAccumulator;
}
@ -133,7 +134,7 @@ export const parseSchema = (
fieldType,
field: parseify(property),
},
'Skipping reserved field type'
i18n.t('nodes.skippingReservedFieldType')
);
return inputsAccumulator;
}
@ -153,7 +154,7 @@ export const parseSchema = (
fieldType,
field: parseify(property),
},
'Skipping input field with no template'
i18n.t('nodes.skippingInputNoTemplate')
);
return inputsAccumulator;
}
@ -169,21 +170,24 @@ export const parseSchema = (
if (!outputSchemaName) {
logger('nodes').warn(
{ outputRefObject: parseify(schema.output) },
'No output schema name found in ref object'
i18n.t('nodes.noOutputSchemaName')
);
return invocationsAccumulator;
}
const outputSchema = openAPI.components?.schemas?.[outputSchemaName];
if (!outputSchema) {
logger('nodes').warn({ outputSchemaName }, 'Output schema not found');
logger('nodes').warn(
{ outputSchemaName },
i18n.t('nodes.outputSchemaNotFound')
);
return invocationsAccumulator;
}
if (!isInvocationOutputSchemaObject(outputSchema)) {
logger('nodes').error(
{ outputSchema: parseify(outputSchema) },
'Invalid output schema'
i18n.t('nodes.invalidOutputSchema')
);
return invocationsAccumulator;
}
@ -196,7 +200,7 @@ export const parseSchema = (
if (!isAllowedOutputField(type, propertyName)) {
logger('nodes').trace(
{ type, propertyName, property: parseify(property) },
'Skipped reserved output field'
i18n.t('nodes.skippedReservedOutput')
);
return outputsAccumulator;
}
@ -204,7 +208,7 @@ export const parseSchema = (
if (!isInvocationFieldSchema(property)) {
logger('nodes').warn(
{ type, propertyName, property: parseify(property) },
'Unhandled output property'
i18n.t('nodes.unhandledOutputProperty')
);
return outputsAccumulator;
}
@ -214,7 +218,7 @@ export const parseSchema = (
if (!isFieldType(fieldType)) {
logger('nodes').warn(
{ fieldName: propertyName, fieldType, field: parseify(property) },
'Skipping unknown output field type'
i18n.t('nodes.skippingUnknownOutputType')
);
return outputsAccumulator;
}

View File

@ -7,6 +7,7 @@ import {
isWorkflowInvocationNode,
} from '../types/types';
import { parseify } from 'common/util/serialize';
import i18n from 'i18next';
export const validateWorkflow = (
workflow: Workflow,
@ -25,8 +26,14 @@ export const validateWorkflow = (
const nodeTemplate = nodeTemplates[node.data.type];
if (!nodeTemplate) {
errors.push({
message: `Node "${node.data.type}" skipped`,
issues: [`Node type "${node.data.type}" does not exist`],
message: `${i18n.t('nodes.node')} "${node.data.type}" ${i18n.t(
'nodes.skipped'
)}`,
issues: [
`${i18n.t('nodes.nodeType')}"${node.data.type}" ${i18n.t(
'nodes.doesNotExist'
)}`,
],
data: node,
});
return;
@ -38,9 +45,13 @@ export const validateWorkflow = (
compareVersions(nodeTemplate.version, node.data.version) !== 0
) {
errors.push({
message: `Node "${node.data.type}" has mismatched version`,
message: `${i18n.t('nodes.node')} "${node.data.type}" ${i18n.t(
'nodes.mismatchedVersion'
)}`,
issues: [
`Node "${node.data.type}" v${node.data.version} may be incompatible with installed v${nodeTemplate.version}`,
`${i18n.t('nodes.node')} "${node.data.type}" v${
node.data.version
} ${i18n.t('nodes.maybeIncompatible')} v${nodeTemplate.version}`,
],
data: { node, nodeTemplate: parseify(nodeTemplate) },
});
@ -52,33 +63,49 @@ export const validateWorkflow = (
const targetNode = keyedNodes[edge.target];
const issues: string[] = [];
if (!sourceNode) {
issues.push(`Output node ${edge.source} does not exist`);
issues.push(
`${i18n.t('nodes.outputNode')} ${edge.source} ${i18n.t(
'nodes.doesNotExist'
)}`
);
} else if (
edge.type === 'default' &&
!(edge.sourceHandle in sourceNode.data.outputs)
) {
issues.push(
`Output field "${edge.source}.${edge.sourceHandle}" does not exist`
`${i18n.t('nodes.outputNodes')} "${edge.source}.${
edge.sourceHandle
}" ${i18n.t('nodes.doesNotExist')}`
);
}
if (!targetNode) {
issues.push(`Input node ${edge.target} does not exist`);
issues.push(
`${i18n.t('nodes.inputNode')} ${edge.target} ${i18n.t(
'nodes.doesNotExist'
)}`
);
} else if (
edge.type === 'default' &&
!(edge.targetHandle in targetNode.data.inputs)
) {
issues.push(
`Input field "${edge.target}.${edge.targetHandle}" does not exist`
`${i18n.t('nodes.inputFeilds')} "${edge.target}.${
edge.targetHandle
}" ${i18n.t('nodes.doesNotExist')}`
);
}
if (!nodeTemplates[sourceNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
issues.push(
`Source node "${edge.source}" missing template "${sourceNode?.data.type}"`
`${i18n.t('nodes.sourceNode')} "${edge.source}" ${i18n.t(
'nodes.missingTemplate'
)} "${sourceNode?.data.type}"`
);
}
if (!nodeTemplates[targetNode?.data.type ?? '__UNKNOWN_NODE_TYPE__']) {
issues.push(
`Source node "${edge.target}" missing template "${targetNode?.data.type}"`
`${i18n.t('nodes.sourceNode')}"${edge.target}" ${i18n.t(
'nodes.missingTemplate'
)} "${targetNode?.data.type}"`
);
}
if (issues.length) {