fix: Optimize internationalization configuration #3221 (#9924)

### What problem does this PR solve?

fix: Optimize internationalization configuration

- Update multi-language options, adding general translations for
functions like Select All and Clear
- Add internationalization support for modules like Chat, Search, and
Datasets

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-09-05 09:57:15 +08:00
committed by GitHub
parent 41cb94324a
commit 3b1ee769eb
36 changed files with 175 additions and 51 deletions

View File

@ -6,6 +6,8 @@ import {
} from '@/components/ui/form'; } from '@/components/ui/form';
import { MultiSelect } from '@/components/ui/multi-select'; import { MultiSelect } from '@/components/ui/multi-select';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { toLower } from 'lodash';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -20,7 +22,10 @@ const Languages = [
'Vietnamese', 'Vietnamese',
]; ];
const options = Languages.map((x) => ({ label: x, value: x })); const options = Languages.map((x) => ({
label: t('language.' + toLower(x)),
value: x,
}));
type CrossLanguageItemProps = { type CrossLanguageItemProps = {
name?: string; name?: string;

View File

@ -52,6 +52,7 @@ export function DelimiterFormField() {
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FormLabel <FormLabel
required
tooltip={t('knowledgeDetails.delimiterTip')} tooltip={t('knowledgeDetails.delimiterTip')}
className="text-sm text-muted-foreground whitespace-break-spaces w-1/4" className="text-sm text-muted-foreground whitespace-break-spaces w-1/4"
> >

View File

@ -18,6 +18,7 @@ import { TreeData } from '@antv/g6/lib/types';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import React, { useCallback, useEffect, useRef } from 'react'; import React, { useCallback, useEffect, useRef } from 'react';
import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
import { useIsDarkTheme } from '../theme-provider';
const rootId = 'root'; const rootId = 'root';
@ -322,7 +323,7 @@ const IndentedTree = ({ data, show, style = {} }: IProps) => {
node.children.forEach((child, idx) => assignIds(child, node.id, idx)); node.children.forEach((child, idx) => assignIds(child, node.id, idx));
} }
}, []); }, []);
const isDark = useIsDarkTheme();
const render = useCallback( const render = useCallback(
async (data: TreeData) => { async (data: TreeData) => {
const graph: Graph = new Graph({ const graph: Graph = new Graph({
@ -335,7 +336,8 @@ const IndentedTree = ({ data, show, style = {} }: IProps) => {
labelBackground: (datum) => datum.id === rootId, labelBackground: (datum) => datum.id === rootId,
labelBackgroundRadius: 0, labelBackgroundRadius: 0,
labelBackgroundFill: '#576286', labelBackgroundFill: '#576286',
labelFill: (datum) => (datum.id === rootId ? '#fff' : '#666'), labelFill: isDark ? '#fff' : '#333',
// labelFill: (datum) => (datum.id === rootId ? '#fff' : '#666'),
labelText: (d) => d.style?.labelText || d.id, labelText: (d) => d.style?.labelText || d.id,
labelTextAlign: (datum) => labelTextAlign: (datum) =>
datum.id === rootId ? 'center' : 'left', datum.id === rootId ? 'center' : 'left',

View File

@ -134,7 +134,9 @@ export function KnowledgeBaseFormField({
name="kb_ids" name="kb_ids"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('chat.knowledgeBases')}</FormLabel> <FormLabel tooltip={t('chat.knowledgeBasesTip')}>
{t('chat.knowledgeBases')}
</FormLabel>
<FormControl> <FormControl>
<MultiSelect <MultiSelect
options={options} options={options}

View File

@ -18,6 +18,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { t } from 'i18next';
import { FilterChange, FilterCollection, FilterValue } from './interface'; import { FilterChange, FilterCollection, FilterValue } from './interface';
export type CheckboxFormMultipleProps = { export type CheckboxFormMultipleProps = {
@ -134,10 +135,10 @@ function CheckboxFormMultiple({
size={'sm'} size={'sm'}
onClick={onReset} onClick={onReset}
> >
Clear {t('common.clear')}
</Button> </Button>
<Button type="submit" size={'sm'}> <Button type="submit" size={'sm'}>
Submit {t('common.submit')}
</Button> </Button>
</div> </div>
</form> </form>

View File

@ -14,6 +14,7 @@ export function MaxTokenNumberFormField({ max = 2048, initialValue }: IProps) {
<SliderInputFormField <SliderInputFormField
name={'parser_config.chunk_token_num'} name={'parser_config.chunk_token_num'}
label={t('chunkTokenNumber')} label={t('chunkTokenNumber')}
tooltip={t('chunkTokenNumberTip')}
max={max} max={max}
defaultValue={initialValue ?? 0} defaultValue={initialValue ?? 0}
layout={FormLayout.Horizontal} layout={FormLayout.Horizontal}

View File

@ -15,6 +15,7 @@ import {
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { CircleStop, Paperclip, Send, Upload, X } from 'lucide-react'; import { CircleStop, Paperclip, Send, Upload, X } from 'lucide-react';
import * as React from 'react'; import * as React from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@ -141,7 +142,7 @@ export function NextMessageInput({
<Textarea <Textarea
value={value} value={value}
onChange={onInputChange} onChange={onInputChange}
placeholder="Type your message here..." placeholder={t('chat.messagePlaceholder')}
className="field-sizing-content min-h-10 w-full resize-none border-0 bg-transparent p-0 shadow-none focus-visible:ring-0 dark:bg-transparent" className="field-sizing-content min-h-10 w-full resize-none border-0 bg-transparent p-0 shadow-none focus-visible:ring-0 dark:bg-transparent"
disabled={isUploading || disabled || sendLoading} disabled={isUploading || disabled || sendLoading}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}

View File

@ -95,7 +95,9 @@ const RaptorFormFields = () => {
tooltip={t('useRaptorTip')} tooltip={t('useRaptorTip')}
className="text-sm text-muted-foreground w-1/4 whitespace-break-spaces" className="text-sm text-muted-foreground w-1/4 whitespace-break-spaces"
> >
{t('useRaptor')} <div className="w-auto xl:w-20 2xl:w-24 3xl:w-28 4xl:w-auto ">
{t('useRaptor')}
</div>
</FormLabel> </FormLabel>
<div className="w-3/4"> <div className="w-3/4">
<FormControl> <FormControl>

View File

@ -94,8 +94,9 @@ const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>, React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & { React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & {
tooltip?: React.ReactNode; tooltip?: React.ReactNode;
required?: boolean;
} }
>(({ className, tooltip, ...props }, ref) => { >(({ className, tooltip, required = false, ...props }, ref) => {
const { error, formItemId } = useFormField(); const { error, formItemId } = useFormField();
return ( return (
@ -105,6 +106,7 @@ const FormLabel = React.forwardRef<
htmlFor={formItemId} htmlFor={formItemId}
{...props} {...props}
> >
{required && <span className="text-destructive">*</span>}
{props.children} {props.children}
{tooltip && <FormTooltip tooltip={tooltip}></FormTooltip>} {tooltip && <FormTooltip tooltip={tooltip}></FormTooltip>}
</Label> </Label>

View File

@ -29,6 +29,7 @@ import {
} from '@/components/ui/popover'; } from '@/components/ui/popover';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
export type MultiSelectOptionType = { export type MultiSelectOptionType = {
@ -193,7 +194,7 @@ export const MultiSelect = React.forwardRef<
onValueChange, onValueChange,
variant, variant,
defaultValue = [], defaultValue = [],
placeholder = 'Select options', placeholder = t('common.selectPlaceholder'),
animation = 0, animation = 0,
maxCount = 3, maxCount = 3,
modalPopover = false, modalPopover = false,
@ -379,7 +380,7 @@ export const MultiSelect = React.forwardRef<
> >
<Command> <Command>
<CommandInput <CommandInput
placeholder="Search..." placeholder={t('common.search') + '...'}
onKeyDown={handleInputKeyDown} onKeyDown={handleInputKeyDown}
/> />
<CommandList> <CommandList>
@ -401,7 +402,7 @@ export const MultiSelect = React.forwardRef<
> >
<CheckIcon className="h-4 w-4" /> <CheckIcon className="h-4 w-4" />
</div> </div>
<span>(Select All)</span> <span>({t('common.selectAll')})</span>
</CommandItem> </CommandItem>
)} )}
{!options.some((x) => 'options' in x) && {!options.some((x) => 'options' in x) &&
@ -457,7 +458,7 @@ export const MultiSelect = React.forwardRef<
onSelect={() => setIsPopoverOpen(false)} onSelect={() => setIsPopoverOpen(false)}
className="flex-1 justify-center cursor-pointer max-w-full" className="flex-1 justify-center cursor-pointer max-w-full"
> >
Close {t('common.close')}
</CommandItem> </CommandItem>
</div> </div>
</CommandGroup> </CommandGroup>

View File

@ -3,6 +3,7 @@ export default {
common: { common: {
noResults: 'No results.', noResults: 'No results.',
selectPlaceholder: 'select value', selectPlaceholder: 'select value',
selectAll: 'Select All',
delete: 'Delete', delete: 'Delete',
deleteModalTitle: 'Are you sure to delete this item?', deleteModalTitle: 'Are you sure to delete this item?',
ok: 'Yes', ok: 'Yes',
@ -37,6 +38,7 @@ export default {
pleaseSelect: 'Please select', pleaseSelect: 'Please select',
pleaseInput: 'Please input', pleaseInput: 'Please input',
submit: 'Submit', submit: 'Submit',
clear: 'Clear',
embedIntoSite: 'Embed into webpage', embedIntoSite: 'Embed into webpage',
previousPage: 'Previous', previousPage: 'Previous',
nextPage: 'Next', nextPage: 'Next',
@ -145,7 +147,8 @@ export default {
vectorSimilarityWeightTip: vectorSimilarityWeightTip:
'This sets the weight of keyword similarity in the combined similarity score, either used with vector cosine similarity or with reranking score. The total of the two weights must equal 1.0.', 'This sets the weight of keyword similarity in the combined similarity score, either used with vector cosine similarity or with reranking score. The total of the two weights must equal 1.0.',
keywordSimilarityWeight: 'Keyword similarity weight', keywordSimilarityWeight: 'Keyword similarity weight',
keywordSimilarityWeightTip: '', keywordSimilarityWeightTip:
'This sets the weight of keyword similarity in the combined similarity score, either used with vector cosine similarity or with reranking score. The total of the two weights must equal 1.0.',
testText: 'Test text', testText: 'Test text',
testTextPlaceholder: 'Input your question here!', testTextPlaceholder: 'Input your question here!',
testingLabel: 'Testing', testingLabel: 'Testing',
@ -441,6 +444,12 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
delete: 'Delete', delete: 'Delete',
}, },
chat: { chat: {
messagePlaceholder: 'Type your message here...',
exit: 'Exit',
multipleModels: 'Multiple Models',
applyModelConfigs: 'Apply model configs',
conversations: 'Conversations',
chatApps: 'Chat Apps',
newConversation: 'New conversation', newConversation: 'New conversation',
createAssistant: 'Create an Assistant', createAssistant: 'Create an Assistant',
assistantSetting: 'Assistant settings', assistantSetting: 'Assistant settings',
@ -839,6 +848,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
hint: 'hint', hint: 'hint',
}, },
fileManager: { fileManager: {
files: 'Files',
name: 'Name', name: 'Name',
uploadDate: 'Upload Date', uploadDate: 'Upload Date',
knowledgeBase: 'Dataset', knowledgeBase: 'Dataset',
@ -864,6 +874,12 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
pleaseUploadAtLeastOneFile: 'Please upload at least one file', pleaseUploadAtLeastOneFile: 'Please upload at least one file',
}, },
flow: { flow: {
recommended: 'Recommended',
customerSupport: 'Customer Support',
marketing: 'Marketing',
consumerApp: 'Consumer App',
other: 'Other',
agents: 'Agents',
days: 'Days', days: 'Days',
beginInput: 'Begin Input', beginInput: 'Begin Input',
ref: 'Variable', ref: 'Variable',
@ -1527,6 +1543,7 @@ This delimiter is used to split the input text into several text pieces echo of
editMCP: 'Edit MCP', editMCP: 'Edit MCP',
}, },
search: { search: {
searchApps: 'Search Apps',
createSearch: 'Create Search', createSearch: 'Create Search',
searchGreeting: 'How can I help you today ', searchGreeting: 'How can I help you today ',
profile: 'Hide Profile', profile: 'Hide Profile',
@ -1551,5 +1568,15 @@ This delimiter is used to split the input text into several text pieces echo of
okText: 'Save', okText: 'Save',
cancelText: 'Cancel', cancelText: 'Cancel',
}, },
language: {
english: 'English',
chinese: 'Chinese',
spanish: 'Spanish',
french: 'French',
german: 'German',
japanese: 'Japanese',
korean: 'Korean',
vietnamese: 'Vietnamese',
},
}, },
}; };

View File

@ -3,6 +3,7 @@ export default {
common: { common: {
noResults: '无结果。', noResults: '无结果。',
selectPlaceholder: '请选择', selectPlaceholder: '请选择',
selectAll: '全选',
delete: '删除', delete: '删除',
deleteModalTitle: '确定删除吗?', deleteModalTitle: '确定删除吗?',
ok: '是', ok: '是',
@ -36,6 +37,7 @@ export default {
pleaseSelect: '请选择', pleaseSelect: '请选择',
pleaseInput: '请输入', pleaseInput: '请输入',
submit: '提交', submit: '提交',
clear: '清空',
embedIntoSite: '嵌入网站', embedIntoSite: '嵌入网站',
previousPage: '上一页', previousPage: '上一页',
nextPage: '下一页', nextPage: '下一页',
@ -105,7 +107,7 @@ export default {
testing: '检索测试', testing: '检索测试',
configuration: '配置', configuration: '配置',
knowledgeGraph: '知识图谱', knowledgeGraph: '知识图谱',
files: '文件', files: '文件',
name: '名称', name: '名称',
namePlaceholder: '请输入名称', namePlaceholder: '请输入名称',
doc: '文档', doc: '文档',
@ -136,7 +138,8 @@ export default {
vectorSimilarityWeightTip: vectorSimilarityWeightTip:
'我们使用混合相似性评分来评估两行文本之间的距离。它是加权关键字相似性和矢量余弦相似性或rerank得分0〜1。两个权重的总和为1.0。', '我们使用混合相似性评分来评估两行文本之间的距离。它是加权关键字相似性和矢量余弦相似性或rerank得分0〜1。两个权重的总和为1.0。',
keywordSimilarityWeight: '关键词相似度权重', keywordSimilarityWeight: '关键词相似度权重',
keywordSimilarityWeightTip: '', keywordSimilarityWeightTip:
'我们使用混合相似性评分来评估两行文本之间的距离。它是加权关键字相似性和矢量余弦相似性或rerank得分0〜1。两个权重的总和为1.0。',
testText: '测试文本', testText: '测试文本',
testTextPlaceholder: '请输入您的问题!', testTextPlaceholder: '请输入您的问题!',
testingLabel: '测试', testingLabel: '测试',
@ -440,6 +443,12 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
delete: '删除', delete: '删除',
}, },
chat: { chat: {
messagePlaceholder: '请输入消息...',
exit: '退出',
multipleModels: '多模型',
applyModelConfigs: '应用模型配置',
conversations: '会话',
chatApps: '聊天',
createChat: '创建聊天', createChat: '创建聊天',
newConversation: '新会话', newConversation: '新会话',
createAssistant: '新建助理', createAssistant: '新建助理',
@ -798,6 +807,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
hint: '提示', hint: '提示',
}, },
fileManager: { fileManager: {
files: '文件',
name: '名称', name: '名称',
uploadDate: '上传日期', uploadDate: '上传日期',
knowledgeBase: '知识库', knowledgeBase: '知识库',
@ -822,6 +832,12 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
pleaseUploadAtLeastOneFile: '请上传至少一个文件', pleaseUploadAtLeastOneFile: '请上传至少一个文件',
}, },
flow: { flow: {
recommended: '推荐',
customerSupport: '客户支持',
marketing: '营销',
consumerApp: '消费者应用',
other: '其他',
agents: '智能体',
beginInput: '开始输入', beginInput: '开始输入',
seconds: '秒', seconds: '秒',
ref: '引用变量', ref: '引用变量',
@ -1441,6 +1457,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
cancelText: '取消', cancelText: '取消',
}, },
search: { search: {
searchApps: '搜索',
createSearch: '创建查询', createSearch: '创建查询',
searchGreeting: '今天我能为你做些什么?', searchGreeting: '今天我能为你做些什么?',
profile: '隐藏个人资料', profile: '隐藏个人资料',
@ -1465,5 +1482,15 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
okText: '保存', okText: '保存',
cancelText: '返回', cancelText: '返回',
}, },
language: {
english: '英语',
chinese: '中文',
spanish: '西班牙语',
french: '法语',
german: '德语',
japanese: '日语',
korean: '韩语',
vietnamese: '越南语',
},
}, },
}; };

View File

@ -114,7 +114,7 @@ export default function Agent() {
<BreadcrumbList> <BreadcrumbList>
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbLink onClick={navigateToAgents}> <BreadcrumbLink onClick={navigateToAgents}>
Agent {t('header.flow')}
</BreadcrumbLink> </BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> <BreadcrumbSeparator />

View File

@ -36,7 +36,7 @@ export default function Agents() {
<section className="flex flex-col w-full flex-1"> <section className="flex flex-col w-full flex-1">
<div className="px-8 pt-8 "> <div className="px-8 pt-8 ">
<ListFilterBar <ListFilterBar
title="Agents" title={t('flow.agents')}
searchString={searchString} searchString={searchString}
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
icon="agent" icon="agent"

View File

@ -1,5 +1,7 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { lowerFirst } from 'lodash';
import { import {
Box, Box,
ChartPie, ChartPie,
@ -23,26 +25,38 @@ const menuItems = [
items: [ items: [
{ {
icon: Sparkle, icon: Sparkle,
label: MenuItemKey.Recommended, label: t('flow.' + lowerFirst(MenuItemKey.Recommended)),
key: MenuItemKey.Recommended, key: MenuItemKey.Recommended,
}, },
{ icon: Box, label: MenuItemKey.Agent, key: MenuItemKey.Agent }, {
icon: Box,
label: t('flow.' + lowerFirst(MenuItemKey.Agent)),
key: MenuItemKey.Agent,
},
{ {
icon: MessageCircleCode, icon: MessageCircleCode,
label: MenuItemKey.CustomerSupport, label: t(
'flow.' + lowerFirst(MenuItemKey.CustomerSupport).replace(' ', ''),
),
key: MenuItemKey.CustomerSupport, key: MenuItemKey.CustomerSupport,
}, },
{ {
icon: ChartPie, icon: ChartPie,
label: MenuItemKey.Marketing, label: t('flow.' + lowerFirst(MenuItemKey.Marketing)),
key: MenuItemKey.Marketing, key: MenuItemKey.Marketing,
}, },
{ {
icon: Component, icon: Component,
label: MenuItemKey.ConsumerApp, label: t(
'flow.' + lowerFirst(MenuItemKey.ConsumerApp.replace(' ', '')),
),
key: MenuItemKey.ConsumerApp, key: MenuItemKey.ConsumerApp,
}, },
{ icon: PencilRuler, label: MenuItemKey.Other, key: MenuItemKey.Other }, {
icon: PencilRuler,
label: t('flow.' + lowerFirst(MenuItemKey.Other)),
key: MenuItemKey.Other,
},
], ],
}, },
]; ];

View File

@ -140,11 +140,11 @@ const ChunkCreatingModal: React.FC<IModalProps<any> & kFProps> = ({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="flex justify-start items-start"> <FormLabel className="flex justify-start items-start">
<div className="flex items-center gap-0"> <div className="flex items-center gap-1">
<span>{t('chunk.question')}</span> <span>{t('chunk.question')}</span>
<HoverCard> <HoverCard>
<HoverCardTrigger asChild> <HoverCardTrigger asChild>
<span className="text-xs mt-[-3px] text-center scale-[90%] font-thin text-primary cursor-pointer rounded-full w-[16px] h-[16px] border-muted-foreground/50 border"> <span className="text-xs mt-[0px] text-center scale-[90%] text-text-secondary cursor-pointer rounded-full w-[17px] h-[17px] border-text-secondary border-2">
? ?
</span> </span>
</HoverCardTrigger> </HoverCardTrigger>

View File

@ -95,7 +95,10 @@ export default ({
</PopoverContent> </PopoverContent>
</Popover> </Popover>
<div className="w-[20px]"></div> <div className="w-[20px]"></div>
<Button onClick={() => createChunk()} className="bg-bg-card text-primary"> <Button
onClick={() => createChunk()}
className="bg-bg-card text-primary hover:bg-card"
>
<Plus size={44} /> <Plus size={44} />
</Button> </Button>
</div> </div>

View File

@ -28,6 +28,7 @@ export function ChunkMethodItem() {
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
required
tooltip={t('chunkMethodTip')} tooltip={t('chunkMethodTip')}
className="text-sm text-muted-foreground whitespace-wrap w-1/4" className="text-sm text-muted-foreground whitespace-wrap w-1/4"
> >
@ -68,6 +69,7 @@ export function EmbeddingModelItem() {
<FormItem className=" items-center space-y-0 "> <FormItem className=" items-center space-y-0 ">
<div className="flex items-center"> <div className="flex items-center">
<FormLabel <FormLabel
required
tooltip={t('embeddingModelTip')} tooltip={t('embeddingModelTip')}
className="text-sm text-muted-foreground whitespace-wrap w-1/4" className="text-sm text-muted-foreground whitespace-wrap w-1/4"
> >

View File

@ -16,4 +16,5 @@ export const ImageMap = {
table: getImageName('table', 2), table: getImageName('table', 2),
one: getImageName('one', 2), one: getImageName('one', 2),
knowledge_graph: getImageName('knowledge-graph', 2), knowledge_graph: getImageName('knowledge-graph', 2),
tag: getImageName('tag', 2),
}; };

View File

@ -83,7 +83,7 @@ export default function TestingForm({
<FormContainer className="p-10"> <FormContainer className="p-10">
<SimilaritySliderFormField <SimilaritySliderFormField
vectorSimilarityWeightName="keywords_similarity_weight" vectorSimilarityWeightName="keywords_similarity_weight"
isTooltipShown isTooltipShown={true}
></SimilaritySliderFormField> ></SimilaritySliderFormField>
<RerankFormFields></RerankFormFields> <RerankFormFields></RerankFormFields>
<UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField> <UseKnowledgeGraphFormField name="use_kg"></UseKnowledgeGraphFormField>

View File

@ -4,6 +4,7 @@ import { SharedBadge } from '@/components/shared-badge';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IKnowledge } from '@/interfaces/database/knowledge'; import { IKnowledge } from '@/interfaces/database/knowledge';
import { t } from 'i18next';
import { ChevronRight } from 'lucide-react'; import { ChevronRight } from 'lucide-react';
import { DatasetDropdown } from './dataset-dropdown'; import { DatasetDropdown } from './dataset-dropdown';
import { useRenameDataset } from './use-rename-dataset'; import { useRenameDataset } from './use-rename-dataset';
@ -20,7 +21,10 @@ export function DatasetCard({
return ( return (
<HomeCard <HomeCard
data={{ ...dataset, description: `${dataset.doc_num} files` }} data={{
...dataset,
description: `${dataset.doc_num} ${t('knowledgeDetails.files')}`,
}}
moreDropdown={ moreDropdown={
<DatasetDropdown <DatasetDropdown
showDatasetRenameModal={showDatasetRenameModal} showDatasetRenameModal={showDatasetRenameModal}

View File

@ -55,7 +55,7 @@ export default function Datasets() {
return ( return (
<section className="py-4 flex-1 flex flex-col"> <section className="py-4 flex-1 flex flex-col">
<ListFilterBar <ListFilterBar
title={t('header.knowledgeBase')} title={t('header.dataset')}
searchString={searchString} searchString={searchString}
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
value={filterValue} value={filterValue}

View File

@ -78,7 +78,11 @@ export default function Files() {
const leftPanel = ( const leftPanel = (
<div> <div>
{breadcrumbItems.length > 0 ? <FileBreadcrumb></FileBreadcrumb> : 'File'} {breadcrumbItems.length > 0 ? (
<FileBreadcrumb></FileBreadcrumb>
) : (
t('fileManager.files')
)}
</div> </div>
); );

View File

@ -26,8 +26,8 @@ export function Applications() {
const options = useMemo( const options = useMemo(
() => [ () => [
{ value: Routes.Chats, label: t('header.chat') }, { value: Routes.Chats, label: t('chat.chatApps') },
{ value: Routes.Searches, label: t('header.search') }, { value: Routes.Searches, label: t('search.searchApps') },
{ value: Routes.Agents, label: t('header.flow') }, { value: Routes.Agents, label: t('header.flow') },
], ],
[t], [t],

View File

@ -22,7 +22,7 @@ export function Datasets() {
<section> <section>
<h2 className="text-2xl font-bold mb-6 flex gap-2.5 items-center"> <h2 className="text-2xl font-bold mb-6 flex gap-2.5 items-center">
<IconFont name="data" className="size-8"></IconFont> <IconFont name="data" className="size-8"></IconFont>
{t('header.knowledgeBase')} {t('header.dataset')}
</h2> </h2>
<div className="flex gap-6"> <div className="flex gap-6">
{loading ? ( {loading ? (

View File

@ -43,7 +43,7 @@ export default function ChatBasicSetting() {
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('assistantName')}</FormLabel> <FormLabel required>{t('assistantName')}</FormLabel>
<FormControl> <FormControl>
<Input {...field}></Input> <Input {...field}></Input>
</FormControl> </FormControl>
@ -69,7 +69,9 @@ export default function ChatBasicSetting() {
name={'prompt_config.empty_response'} name={'prompt_config.empty_response'}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('emptyResponse')}</FormLabel> <FormLabel tooltip={t('emptyResponseTip')}>
{t('emptyResponse')}
</FormLabel>
<FormControl> <FormControl>
<Textarea {...field}></Textarea> <Textarea {...field}></Textarea>
</FormControl> </FormControl>
@ -82,7 +84,9 @@ export default function ChatBasicSetting() {
name={'prompt_config.prologue'} name={'prompt_config.prologue'}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t('setAnOpener')}</FormLabel> <FormLabel tooltip={t('setAnOpenerTip')}>
{t('setAnOpener')}
</FormLabel>
<FormControl> <FormControl>
<Textarea {...field}></Textarea> <Textarea {...field}></Textarea>
</FormControl> </FormControl>
@ -93,14 +97,17 @@ export default function ChatBasicSetting() {
<SwitchFormField <SwitchFormField
name={'prompt_config.quote'} name={'prompt_config.quote'}
label={t('quote')} label={t('quote')}
tooltip={t('quoteTip')}
></SwitchFormField> ></SwitchFormField>
<SwitchFormField <SwitchFormField
name={'prompt_config.keyword'} name={'prompt_config.keyword'}
label={t('keyword')} label={t('keyword')}
tooltip={t('keywordTip')}
></SwitchFormField> ></SwitchFormField>
<SwitchFormField <SwitchFormField
name={'prompt_config.tts'} name={'prompt_config.tts'}
label={t('tts')} label={t('tts')}
tooltip={t('ttsTip')}
></SwitchFormField> ></SwitchFormField>
<TavilyFormField></TavilyFormField> <TavilyFormField></TavilyFormField>
<KnowledgeBaseFormField></KnowledgeBaseFormField> <KnowledgeBaseFormField></KnowledgeBaseFormField>

View File

@ -37,11 +37,12 @@ export function ChatPromptEngine() {
</FormItem> </FormItem>
)} )}
/> />
<SimilaritySliderFormField></SimilaritySliderFormField> <SimilaritySliderFormField isTooltipShown></SimilaritySliderFormField>
<TopNFormField></TopNFormField> <TopNFormField></TopNFormField>
<SwitchFormField <SwitchFormField
name={'prompt_config.refine_multiturn'} name={'prompt_config.refine_multiturn'}
label={t('multiTurn')} label={t('multiTurn')}
tooltip={t('multiTurnTip')}
></SwitchFormField> ></SwitchFormField>
<UseKnowledgeGraphFormField name="prompt_config.use_kg"></UseKnowledgeGraphFormField> <UseKnowledgeGraphFormField name="prompt_config.use_kg"></UseKnowledgeGraphFormField>
<SwitchFormField <SwitchFormField

View File

@ -23,6 +23,7 @@ import {
import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { buildMessageUuidWithRole } from '@/utils/chat'; import { buildMessageUuidWithRole } from '@/utils/chat';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { t } from 'i18next';
import { isEmpty, omit } from 'lodash'; import { isEmpty, omit } from 'lodash';
import { ListCheck, Plus, Trash2 } from 'lucide-react'; import { ListCheck, Plus, Trash2 } from 'lucide-react';
import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
@ -139,7 +140,7 @@ const ChatCard = forwardRef(function ChatCard(
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>Apply model configs</p> <p>{t('chat.applyModelConfigs')}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
{!isLatestChat || chatBoxIds.length === 3 ? ( {!isLatestChat || chatBoxIds.length === 3 ? (

View File

@ -63,10 +63,10 @@ export default function Chat() {
<section className="pt-14 h-[100vh] pb-24"> <section className="pt-14 h-[100vh] pb-24">
<div className="flex items-center justify-between px-10 pb-5"> <div className="flex items-center justify-between px-10 pb-5">
<span className="text-2xl"> <span className="text-2xl">
Multiple Models ({chatBoxIds.length}/3) {t('chat.multipleModels')} ({chatBoxIds.length}/3)
</span> </span>
<Button variant={'ghost'} onClick={switchDebugMode}> <Button variant={'ghost'} onClick={switchDebugMode}>
Exit <LogOut /> {t('chat.exit')} <LogOut />
</Button> </Button>
</div> </div>
<MultipleChatBox <MultipleChatBox
@ -124,7 +124,7 @@ export default function Chat() {
isNew === 'true' isNew === 'true'
} }
> >
<ArrowUpRight /> Multiple Models <ArrowUpRight /> {t('chat.multipleModels')}
</Button> </Button>
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>

View File

@ -71,7 +71,7 @@ export function Sessions({
</section> </section>
<div className="flex justify-between items-center mb-4 pt-10"> <div className="flex justify-between items-center mb-4 pt-10">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-base font-bold">Conversations</span> <span className="text-base font-bold">{t('chat.conversations')}</span>
<span className="text-text-secondary text-xs"> <span className="text-text-secondary text-xs">
{conversationList.length} {conversationList.length}
</span> </span>

View File

@ -38,7 +38,7 @@ export default function ChatList() {
<section className="flex flex-col w-full flex-1"> <section className="flex flex-col w-full flex-1">
<div className="px-8 pt-8"> <div className="px-8 pt-8">
<ListFilterBar <ListFilterBar
title="Chat apps" title={t('chat.chatApps')}
icon="chat" icon="chat"
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
searchString={searchString} searchString={searchString}

View File

@ -62,7 +62,7 @@ export default function SearchPage() {
<BreadcrumbList> <BreadcrumbList>
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbLink onClick={navigateToSearchList}> <BreadcrumbLink onClick={navigateToSearchList}>
Search {t('header.search')}
</BreadcrumbLink> </BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> <BreadcrumbSeparator />

View File

@ -397,7 +397,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
name="search_config.similarity_threshold" name="search_config.similarity_threshold"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Similarity Threshold</FormLabel> <FormLabel
tooltip={t('knowledgeDetails.similarityThresholdTip')}
>
{t('knowledgeDetails.similarityThreshold')}
</FormLabel>
<div <div
className={cn( className={cn(
'flex items-center gap-4 justify-between', 'flex items-center gap-4 justify-between',
@ -433,9 +437,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
name="search_config.vector_similarity_weight" name="search_config.vector_similarity_weight"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel
<span className="text-destructive mr-1"> *</span>Vector tooltip={t('knowledgeDetails.vectorSimilarityWeightTip')}
Similarity Weight >
<span className="text-destructive mr-1"> *</span>
{t('knowledgeDetails.vectorSimilarityWeight')}
</FormLabel> </FormLabel>
<div <div
className={cn( className={cn(

View File

@ -239,7 +239,7 @@ export default function SearchingView({
searchData.search_config.related_search && ( searchData.search_config.related_search && (
<div className="mt-14 w-full overflow-hidden opacity-100 max-h-96"> <div className="mt-14 w-full overflow-hidden opacity-100 max-h-96">
<p className="text-text-primary mb-2 text-xl"> <p className="text-text-primary mb-2 text-xl">
{t('relatedSearch')} {t('search.relatedSearch')}
</p> </p>
<div className="mt-2 flex flex-wrap justify-start gap-2"> <div className="mt-2 flex flex-wrap justify-start gap-2">
{relatedQuestions?.map((x, idx) => ( {relatedQuestions?.map((x, idx) => (

View File

@ -49,7 +49,7 @@ export default function SearchList() {
<div className="px-8 pt-8"> <div className="px-8 pt-8">
<ListFilterBar <ListFilterBar
icon="search" icon="search"
title="Search apps" title={t('searchApps')}
showFilter={false} showFilter={false}
onSearchChange={(e) => handleSearchChange(e.target.value)} onSearchChange={(e) => handleSearchChange(e.target.value)}
> >

View File

@ -17,6 +17,15 @@ module.exports = {
'2xl': '1400px', '2xl': '1400px',
}, },
}, },
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1400px',
'3xl': '1780px',
'4xl': '1980px',
},
extend: { extend: {
colors: { colors: {
border: 'var(--colors-outline-neutral-strong)', border: 'var(--colors-outline-neutral-strong)',