Feat: Translate the fields of the parsing operator #9869 (#10079)

### What problem does this PR solve?

Feat: Translate the fields of the parsing operator #9869

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-09-15 11:24:19 +08:00
committed by GitHub
parent d8ef22db68
commit 2b50de3186
8 changed files with 72 additions and 24 deletions

View File

@ -18,7 +18,7 @@ export function LLMFormField({ options, name }: LLMFormFieldProps) {
]);
return (
<RAGFlowFormItem name={name || 'llm_id'} label={t('model')}>
<RAGFlowFormItem name={name || 'llm_id'} label={t('chat.model')}>
<SelectWithSearch options={options || modelOptions}></SelectWithSearch>
</RAGFlowFormItem>
);

View File

@ -1636,6 +1636,11 @@ This delimiter is used to split the input text into several text pieces echo of
chunkerDescription: 'Chunker',
tokenizer: 'Tokenizer',
tokenizerDescription: 'Tokenizer',
outputFormat: 'Output format',
lang: 'Language',
fileFormats: 'File formats',
fields: 'Fields',
addParser: 'Add Parser',
},
},
};

View File

@ -1544,6 +1544,11 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
chunkerDescription: '分块器',
tokenizer: '分词器',
tokenizerDescription: '分词器',
outputFormat: '输出格式',
lang: '语言',
fileFormats: '文件格式',
fields: '字段',
addParser: '增加解析器',
},
},
};

View File

@ -388,7 +388,7 @@ export const initialStringTransformValues = {
},
};
export const initialParserValues = { outputs: {} };
export const initialParserValues = { outputs: {}, parser: [] };
export const CategorizeAnchorPointPositions = [
{ top: 1, right: 34 },

View File

@ -6,6 +6,7 @@ import {
} from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { useTranslation } from 'react-i18next';
import { FileType } from '../../constant';
import { OutputFormatMap } from './constant';
import { CommonProps } from './interface';
@ -28,10 +29,11 @@ export function OutputFormatFormField({
prefix,
fileType,
}: OutputFormatFormFieldProps) {
const { t } = useTranslation();
return (
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`output_format`, prefix)}
label="output_format"
label={t('dataflow.outputFormat')}
>
<SelectWithSearch
options={buildOutputOptionsFormatMap()[fileType]}

View File

@ -1,6 +1,7 @@
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { buildOptions } from '@/utils/form';
import { useTranslation } from 'react-i18next';
import { FileType } from '../../constant';
import { OutputFormatFormField } from './common-form-fields';
import { CommonProps } from './interface';
@ -18,11 +19,12 @@ const options = buildOptions([
]);
export function EmailFormFields({ prefix }: CommonProps) {
const { t } = useTranslation();
return (
<>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fields`, prefix)}
label="fields"
label={t('dataflow.fields')}
>
<SelectWithSearch options={options}></SelectWithSearch>
</RAGFlowFormItem>

View File

@ -8,13 +8,14 @@ import { buildOptions } from '@/utils/form';
import { zodResolver } from '@hookform/resolvers/zod';
import { useHover } from 'ahooks';
import { Trash2 } from 'lucide-react';
import { memo, useCallback, useRef } from 'react';
import { memo, useCallback, useMemo, useRef } from 'react';
import {
UseFieldArrayRemove,
useFieldArray,
useForm,
useFormContext,
} from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { FileType, initialParserValues } from '../../constant';
import { useFormValues } from '../../hooks/use-form-values';
@ -48,26 +49,57 @@ type ParserItemProps = {
remove: UseFieldArrayRemove;
};
export const FormSchema = z.object({
parser: z.array(
z.object({
fileFormat: z.string().nullish(),
output_format: z.string().optional(),
parse_method: z.string().optional(),
llm_id: z.string().optional(),
lang: z.string().optional(),
fields: z.array(z.string()).optional(),
}),
),
});
export type FormSchemaType = z.infer<typeof FormSchema>;
function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
const form = useFormContext();
const { t } = useTranslation();
const form = useFormContext<FormSchemaType>();
const ref = useRef(null);
const isHovering = useHover(ref);
const prefix = `${name}.${index}`;
const fileFormat = form.getValues(`${name}.${index}.fileFormat`);
const fileFormat = form.getValues(`parser.${index}.fileFormat`);
const values = form.getValues();
const parserList = values.parser.slice(); // Adding, deleting, or modifying the parser array will not change the reference.
const filteredFileFormatOptions = useMemo(() => {
const otherFileFormatList = parserList
.filter((_, idx) => idx !== index)
.map((x) => x.fileFormat);
return FileFormatOptions.filter((x) => {
return !otherFileFormatList.includes(x.value);
});
}, [index, parserList]);
const Widget =
fileFormat && fileFormat in FileFormatWidgetMap
typeof fileFormat === 'string' && fileFormat in FileFormatWidgetMap
? FileFormatWidgetMap[fileFormat as keyof typeof FileFormatWidgetMap]
: OutputFormatFormField;
return (
<section
className={cn('space-y-5 p-5 rounded-md', {
className={cn('space-y-5 px-5 py-2.5 rounded-md', {
'bg-state-error-5': isHovering,
})}
>
<div className="flex justify-between items-center">
<span className="text-text-primary text-sm">Parser {index}</span>
<span className="text-text-primary text-sm font-medium">
Parser {index}
</span>
{index > 0 && (
<Button variant={'ghost'} onClick={() => remove(index)} ref={ref}>
<Trash2 />
@ -76,9 +108,11 @@ function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
</div>
<RAGFlowFormItem
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
label="File Formats"
label={t('dataflow.fileFormats')}
>
<SelectWithSearch options={FileFormatOptions}></SelectWithSearch>
<SelectWithSearch
options={filteredFileFormatOptions}
></SelectWithSearch>
</RAGFlowFormItem>
<Widget prefix={prefix} fileType={fileFormat as FileType}></Widget>
{index < fieldLength - 1 && <Separator />}
@ -86,15 +120,8 @@ function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
);
}
export const FormSchema = z.object({
parser: z.array(
z.object({
fileFormat: z.string().optional(),
}),
),
});
const ParserForm = ({ node }: INextOperatorForm) => {
const { t } = useTranslation();
const defaultValues = useFormValues(initialParserValues, node);
const form = useForm<z.infer<typeof FormSchema>>({
@ -111,7 +138,12 @@ const ParserForm = ({ node }: INextOperatorForm) => {
const add = useCallback(() => {
append({
fileFormat: undefined,
fileFormat: null,
output_format: '',
parse_method: '',
llm_id: '',
lang: '',
fields: [],
});
}, [append]);
@ -131,8 +163,8 @@ const ParserForm = ({ node }: INextOperatorForm) => {
></ParserItem>
);
})}
<BlockButton onClick={add} type="button">
Add Parser
<BlockButton onClick={add} type="button" className="mt-2.5">
{t('dataflow.addParser')}
</BlockButton>
</form>
<div className="p-5">

View File

@ -1,4 +1,5 @@
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
import { useTranslation } from 'react-i18next';
import { FileType } from '../../constant';
import {
LargeModelFormField,
@ -9,6 +10,7 @@ import { CommonProps } from './interface';
import { buildFieldNameWithPrefix } from './utils';
export function PdfFormFields({ prefix }: CommonProps) {
const { t } = useTranslation();
return (
<>
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
@ -16,7 +18,7 @@ export function PdfFormFields({ prefix }: CommonProps) {
<LargeModelFormField prefix={prefix}></LargeModelFormField>
<CrossLanguageFormField
name={buildFieldNameWithPrefix(`lang`, prefix)}
label="lang"
label={t('dataflow.lang')}
></CrossLanguageFormField>
<OutputFormatFormField
prefix={prefix}