Compare commits

..

28 Commits

Author SHA1 Message Date
8c6302d1fc docs: add more description to env 2024-11-22 11:33:10 +08:00
291e9a067b Merge pull request #105 from langgenius/fix/not-support-num-input
fix: not support num input
2024-09-04 18:04:16 +08:00
ac0e3e807d chore: paragrpah form type 2024-09-04 17:55:45 +08:00
b7f703852e fix: not support num input 2024-09-04 17:51:22 +08:00
ef15747e4a Merge pull request #104 from langgenius/fix/i18n-files-problem
fix: i18n problems
2024-09-03 14:54:40 +08:00
f9bd745bb0 fix: i18n problems 2024-09-03 14:52:11 +08:00
e2b37c1a9c Merge pull request #100 from bcat95/patch-5
Update i18next-config.ts
2024-09-03 14:45:52 +08:00
0f490de7ff Merge branch 'main' into patch-5 2024-09-03 14:44:35 +08:00
aaeb440210 Merge pull request #99 from bcat95/patch-1
Create app.vi.ts
2024-09-03 14:40:15 +08:00
b45262add9 Merge pull request #97 from bcat95/patch-3
Create tools.vi.ts
2024-09-03 14:39:53 +08:00
368c6b3dae Merge pull request #94 from yusuke-ten/feat/add-japanese
Add Japanese language settings to i18n
2024-09-03 14:39:28 +08:00
f6fb9c7cea Merge pull request #98 from bcat95/patch-2
Create common.vi.ts
2024-09-03 14:38:07 +08:00
69044eb8a3 Merge pull request #103 from langgenius/fix/not-show-opening-statement
fix: not show opening statement
2024-09-03 14:32:46 +08:00
cafd643c00 fix: not show opening statement 2024-09-03 14:31:05 +08:00
1c12b1dce3 Update i18next-config.ts 2024-08-18 22:43:22 +07:00
94d09ed23b Create tools.vi.ts 2024-08-18 22:41:51 +07:00
5d313f7463 Create common.vi.ts 2024-08-18 22:41:11 +07:00
97203f5ac6 Create app.vi.ts 2024-08-18 22:40:29 +07:00
7f24387eef Add Japanese language settings to i18n” 2024-08-12 18:44:49 +09:00
8d21cbc2da Merge pull request #71 from eltociear/patch-1
docs: update README.md
2024-08-07 18:29:39 +08:00
7bb19ed8ec Merge pull request #90 from langgenius/chore/hide-workflow-run-detail
chore: hide workflow run detail
2024-08-07 18:04:19 +08:00
5a85f0d427 chore: hide workflow run detail 2024-08-07 18:01:00 +08:00
96bd12af44 Merge pull request #38 from Saul-BT/feat/spanish-language
feat: add spanish language
2024-08-07 17:14:47 +08:00
484a5dc102 Merge pull request #81 from yoyocircle/main
fix: typos
2024-08-07 17:08:50 +08:00
10eb176f72 Merge pull request #84 from langgenius/fix/optional-i18n
fix: optional copywriting i18n
2024-07-31 11:56:15 +08:00
f6b4b4a361 fix: typos 2024-07-17 03:59:08 +00:00
df0ae34be1 docs: update README.md
trucated -> truncated
2024-05-09 15:15:24 +09:00
f7ff288ff1 feat: add spanish language 2023-12-07 21:20:21 +01:00
20 changed files with 525 additions and 107 deletions

View File

@ -4,11 +4,15 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next
## Config App
Create a file named `.env.local` in the current directory and copy the contents from `.env.example`. Setting the following content:
```
# APP ID
# APP ID: This is the unique identifier for your app. You can find it in the app's detail page URL.
# For example, in the URL `https://cloud.dify.ai/app/xxx/workflow`, the value `xxx` is your APP ID.
NEXT_PUBLIC_APP_ID=
# APP API key
# APP API Key: This is the key used to authenticate your app's API requests.
# You can generate it on the app's "API Access" page by clicking the "API Key" button in the top-right corner.
NEXT_PUBLIC_APP_KEY=
# APP URL
# APP URL: This is the API's base URL. If you're using the Dify cloud service, set it to: https://api.dify.ai/v1.
NEXT_PUBLIC_API_URL=
```
@ -68,7 +72,7 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
## Deploy on Vercel
> ⚠️ If you are using [Vercel Hobby](https://vercel.com/pricing), your message will be trucated due to the limitation of vercel.
> ⚠️ If you are using [Vercel Hobby](https://vercel.com/pricing), your message will be truncated due to the limitation of vercel.
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

View File

@ -4,25 +4,25 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
type IAppUnavailableProps = {
isUnknwonReason: boolean
isUnknownReason: boolean
errMessage?: string
}
const AppUnavailable: FC<IAppUnavailableProps> = ({
isUnknwonReason,
isUnknownReason,
errMessage,
}) => {
const { t } = useTranslation()
let message = errMessage
if (!errMessage)
message = (isUnknwonReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string
message = (isUnknownReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string
return (
<div className='flex items-center justify-center w-screen h-screen'>
<h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
style={{
borderRight: '1px solid rgba(0,0,0,.3)',
}}>{(errMessage || isUnknwonReason) ? 500 : 404}</h1>
}}>{(errMessage || isUnknownReason) ? 500 : 404}</h1>
<div className='text-sm'>{message}</div>
</div>
)

View File

@ -58,7 +58,7 @@ type IAnswerProps = {
item: ChatItem
feedbackDisabled: boolean
onFeedback?: FeedbackFunc
isResponsing?: boolean
isResponding?: boolean
allToolIcons?: Record<string, string | Emoji>
}
@ -67,7 +67,7 @@ const Answer: FC<IAnswerProps> = ({
item,
feedbackDisabled = false,
onFeedback,
isResponsing,
isResponding,
allToolIcons,
}) => {
const { id, content, feedback, agent_thoughts, workflowProcess } = item
@ -153,7 +153,7 @@ const Answer: FC<IAnswerProps> = ({
<Thought
thought={item}
allToolIcons={allToolIcons || {}}
isFinished={!!item.observation || !isResponsing}
isFinished={!!item.observation || !isResponding}
/>
)}
@ -169,7 +169,7 @@ const Answer: FC<IAnswerProps> = ({
<div key={id}>
<div className='flex items-start'>
<div className={`${s.answerIcon} w-10 h-10 shrink-0`}>
{isResponsing
{isResponding
&& <div className={s.typeingIcon}>
<LoadingAnim type='avatar' />
</div>
@ -181,7 +181,7 @@ const Answer: FC<IAnswerProps> = ({
{workflowProcess && (
<WorkflowProcess data={workflowProcess} hideInfo />
)}
{(isResponsing && (isAgentMode ? (!content && (agent_thoughts || []).filter(item => !!item.thought || !!item.tool).length === 0) : !content))
{(isResponding && (isAgentMode ? (!content && (agent_thoughts || []).filter(item => !!item.thought || !!item.tool).length === 0) : !content))
? (
<div className='flex items-center justify-center w-6 h-5'>
<LoadingAnim type='text' />

View File

@ -30,7 +30,7 @@ export type IChatProps = {
checkCanSend?: () => boolean
onSend?: (message: string, files: VisionFile[]) => void
useCurrentUserAvatar?: boolean
isResponsing?: boolean
isResponding?: boolean
controlClearQuery?: number
visionConfig?: VisionSettings
}
@ -43,7 +43,7 @@ const Chat: FC<IChatProps> = ({
checkCanSend,
onSend = () => { },
useCurrentUserAvatar,
isResponsing,
isResponding,
controlClearQuery,
visionConfig,
}) => {
@ -95,7 +95,7 @@ const Chat: FC<IChatProps> = ({
if (!files.find(item => item.type === TransferMethod.local_file && !item.fileId)) {
if (files.length)
onClear()
if (!isResponsing)
if (!isResponding)
setQuery('')
}
}
@ -129,7 +129,7 @@ const Chat: FC<IChatProps> = ({
item={item}
feedbackDisabled={feedbackDisabled}
onFeedback={onFeedback}
isResponsing={isResponsing && isLast}
isResponding={isResponding && isLast}
/>
}
return (

View File

@ -33,7 +33,7 @@ const Main: FC = () => {
* app info
*/
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
const [isUnknwonReason, setIsUnknwonReason] = useState<boolean>(false)
const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
const [inited, setInited] = useState<boolean>(false)
// in mobile, show sidebar by click button
@ -86,7 +86,7 @@ const Main: FC = () => {
setCurrInputs(inputs)
setChatStarted()
// parse variables in introduction
setChatList(generateNewChatListWithOpenstatement('', inputs))
setChatList(generateNewChatListWithOpenStatement('', inputs))
}
const hasSetInputs = (() => {
if (!isNewConversation)
@ -121,10 +121,10 @@ const Main: FC = () => {
}
// update chat list of current conversation
if (!isNewConversation && !conversationIdChangeBecauseOfNew && !isResponsing) {
if (!isNewConversation && !conversationIdChangeBecauseOfNew && !isResponding) {
fetchChatList(currConversationId).then((res: any) => {
const { data } = res
const newChatList: ChatItem[] = generateNewChatListWithOpenstatement(notSyncToStateIntroduction, notSyncToStateInputs)
const newChatList: ChatItem[] = generateNewChatListWithOpenStatement(notSyncToStateIntroduction, notSyncToStateInputs)
data.forEach((item: any) => {
newChatList.push({
@ -148,7 +148,7 @@ const Main: FC = () => {
}
if (isNewConversation && isChatStarted)
setChatList(generateNewChatListWithOpenstatement())
setChatList(generateNewChatListWithOpenStatement())
}
useEffect(handleConversationSwitch, [currConversationId, inited])
@ -176,7 +176,7 @@ const Main: FC = () => {
chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight
}, [chatList, currConversationId])
// user can not edit inputs if user had send message
const canEditInpus = !chatList.some(item => item.isAnswer === false) && isNewConversation
const canEditInputs = !chatList.some(item => item.isAnswer === false) && isNewConversation
const createNewChat = () => {
// if new chat is already exist, do not create new chat
if (conversationList.some(item => item.id === '-1'))
@ -193,21 +193,21 @@ const Main: FC = () => {
}
// sometime introduction is not applied to state
const generateNewChatListWithOpenstatement = (introduction?: string, inputs?: Record<string, any> | null) => {
let caculatedIntroduction = introduction || conversationIntroduction || ''
const caculatedPromptVariables = inputs || currInputs || null
if (caculatedIntroduction && caculatedPromptVariables)
caculatedIntroduction = replaceVarWithValues(caculatedIntroduction, promptConfig?.prompt_variables || [], caculatedPromptVariables)
const generateNewChatListWithOpenStatement = (introduction?: string, inputs?: Record<string, any> | null) => {
let calculatedIntroduction = introduction || conversationIntroduction || ''
const calculatedPromptVariables = inputs || currInputs || null
if (calculatedIntroduction && calculatedPromptVariables)
calculatedIntroduction = replaceVarWithValues(calculatedIntroduction, promptConfig?.prompt_variables || [], calculatedPromptVariables)
const openstatement = {
const openStatement = {
id: `${Date.now()}`,
content: caculatedIntroduction,
content: calculatedIntroduction,
isAnswer: true,
feedbackDisabled: true,
isOpeningStatement: isShowPrompt,
}
if (caculatedIntroduction)
return [openstatement]
if (calculatedIntroduction)
return [openStatement]
return []
}
@ -255,14 +255,14 @@ const Main: FC = () => {
setAppUnavailable(true)
}
else {
setIsUnknwonReason(true)
setIsUnknownReason(true)
setAppUnavailable(true)
}
}
})()
}, [])
const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
const [isResponding, { setTrue: setRespondingTrue, setFalse: setRespondingFalse }] = useBoolean(false)
const [abortController, setAbortController] = useState<AbortController | null>(null)
const { notify } = Toast
const logError = (message: string) => {
@ -279,8 +279,8 @@ const Main: FC = () => {
const inputLens = Object.values(currInputs).length
const promptVariablesLens = promptConfig.prompt_variables.length
const emytyInput = inputLens < promptVariablesLens || Object.values(currInputs).find(v => !v)
if (emytyInput) {
const emptyInput = inputLens < promptVariablesLens || Object.values(currInputs).find(v => !v)
if (emptyInput) {
logError(t('app.errorMessage.valueOfVarRequired'))
return false
}
@ -291,7 +291,7 @@ const Main: FC = () => {
const [openingSuggestedQuestions, setOpeningSuggestedQuestions] = useState<string[]>([])
const [messageTaskId, setMessageTaskId] = useState('')
const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
const [isResponsingConIsCurrCon, setIsResponsingConCurrCon, getIsResponsingConIsCurrCon] = useGetState(true)
const [isRespondingConIsCurrCon, setIsRespondingConCurrCon, getIsRespondingConIsCurrCon] = useGetState(true)
const [userQuery, setUserQuery] = useState('')
const updateCurrentQA = ({
@ -318,7 +318,7 @@ const Main: FC = () => {
}
const handleSend = async (message: string, files?: VisionFile[]) => {
if (isResponsing) {
if (isResponding) {
notify({ type: 'info', message: t('app.errorMessage.waitForResponse') })
return
}
@ -340,7 +340,7 @@ const Main: FC = () => {
})
}
// qustion
// question
const questionId = `question-${Date.now()}`
const questionItem = {
id: questionId,
@ -374,7 +374,7 @@ const Main: FC = () => {
const prevTempNewConversationId = getCurrConversationId() || '-1'
let tempNewConversationId = ''
setResponsingTrue()
setRespondingTrue()
sendChatMessage(data, {
getAbortController: (abortController) => {
setAbortController(abortController)
@ -399,7 +399,7 @@ const Main: FC = () => {
setMessageTaskId(taskId)
// has switched to other conversation
if (prevTempNewConversationId !== getCurrConversationId()) {
setIsResponsingConCurrCon(false)
setIsRespondingConCurrCon(false)
return
}
updateCurrentQA({
@ -426,7 +426,7 @@ const Main: FC = () => {
resetNewConversationInputs()
setChatNotStarted()
setCurrConversationId(tempNewConversationId, APP_ID, true)
setResponsingFalse()
setRespondingFalse()
},
onFile(file) {
const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1]
@ -465,7 +465,7 @@ const Main: FC = () => {
}
// has switched to other conversation
if (prevTempNewConversationId !== getCurrConversationId()) {
setIsResponsingConCurrCon(false)
setIsRespondingConCurrCon(false)
return false
}
@ -520,7 +520,7 @@ const Main: FC = () => {
))
},
onError() {
setResponsingFalse()
setRespondingFalse()
// role back placeholder answer
setChatList(produce(getChatList(), (draft) => {
draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
@ -604,7 +604,7 @@ const Main: FC = () => {
}
if (appUnavailable)
return <AppUnavailable isUnknwonReason={isUnknwonReason} errMessage={!hasSetAppConfig ? 'Please set APP_ID and API_KEY in config/index.tsx' : ''} />
return <AppUnavailable isUnknownReason={isUnknownReason} errMessage={!hasSetAppConfig ? 'Please set APP_ID and API_KEY in config/index.tsx' : ''} />
if (!APP_ID || !APP_INFO || !promptConfig)
return <Loading type='app' />
@ -639,7 +639,7 @@ const Main: FC = () => {
siteInfo={APP_INFO}
promptConfig={promptConfig}
onStartChat={handleStartChat}
canEidtInpus={canEditInpus}
canEditInputs={canEditInputs}
savedInputs={currInputs as Record<string, any>}
onInputsChange={setCurrInputs}
></ConfigSence>
@ -652,7 +652,7 @@ const Main: FC = () => {
chatList={chatList}
onSend={handleSend}
onFeedback={handleFeedback}
isResponsing={isResponsing}
isResponding={isResponding}
checkCanSend={checkCanSend}
visionConfig={visionConfig}
/>

View File

@ -20,7 +20,7 @@ export type IWelcomeProps = {
siteInfo: AppInfo
promptConfig: PromptConfig
onStartChat: (inputs: Record<string, any>) => void
canEidtInpus: boolean
canEditInputs: boolean
savedInputs: Record<string, any>
onInputsChange: (inputs: Record<string, any>) => void
}
@ -32,7 +32,7 @@ const Welcome: FC<IWelcomeProps> = ({
siteInfo,
promptConfig,
onStartChat,
canEidtInpus,
canEditInputs,
savedInputs,
onInputsChange,
}) => {
@ -122,6 +122,15 @@ const Welcome: FC<IWelcomeProps> = ({
onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
/>
)}
{item.type === 'number' && (
<input
type="number"
className="block w-full p-2 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-xs focus:ring-blue-500 focus:border-blue-500 "
placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
value={inputs[item.key]}
onChange={(e) => { onInputsChange({ ...inputs, [item.key]: e.target.value }) }}
/>
)}
</div>
))}
</div>
@ -131,8 +140,8 @@ const Welcome: FC<IWelcomeProps> = ({
const canChat = () => {
const inputLens = Object.values(inputs).length
const promptVariablesLens = promptConfig.prompt_variables.length
const emytyInput = inputLens < promptVariablesLens || Object.values(inputs).filter(v => v === '').length > 0
if (emytyInput) {
const emptyInput = inputLens < promptVariablesLens || Object.values(inputs).filter(v => v === '').length > 0
if (emptyInput) {
logError(t('app.errorMessage.valueOfVarRequired'))
return false
}
@ -217,7 +226,7 @@ const Welcome: FC<IWelcomeProps> = ({
}
const renderHasSetInputsPublic = () => {
if (!canEidtInpus) {
if (!canEditInputs) {
return (
<TemplateVarPanel
isFold={false}
@ -260,7 +269,7 @@ const Welcome: FC<IWelcomeProps> = ({
}
const renderHasSetInputsPrivate = () => {
if (!canEidtInpus || !hasVar)
if (!canEditInputs || !hasVar)
return null
return (
@ -284,7 +293,7 @@ const Welcome: FC<IWelcomeProps> = ({
}
const renderHasSetInputs = () => {
if ((!isPublicVersion && !canEidtInpus) || !hasVar)
if ((!isPublicVersion && !canEditInputs) || !hasVar)
return null
return (

View File

@ -3,13 +3,10 @@ import type { FC } from 'react'
import { useEffect, useState } from 'react'
import cn from 'classnames'
import BlockIcon from './block-icon'
import CodeEditor from './code-editor'
import { CodeLanguage } from '@/types/app'
import AlertCircle from '@/app/components/base/icons/line/alert-circle'
import AlertTriangle from '@/app/components/base/icons/line/alert-triangle'
import Loading02 from '@/app/components/base/icons/line/loading-02'
import CheckCircle from '@/app/components/base/icons/line/check-circle'
import ChevronRight from '@/app/components/base/icons/line/chevron-right'
import type { NodeTracing } from '@/types/app'
type Props = {
@ -52,12 +49,6 @@ const NodePanel: FC<Props> = ({ nodeInfo, hideInfo = false }) => {
)}
onClick={() => setCollapseState(!collapseState)}
>
<ChevronRight
className={cn(
'shrink-0 w-3 h-3 mr-1 text-gray-400 transition-all group-hover:text-gray-500',
!collapseState && 'rotate-90',
)}
/>
<BlockIcon size={hideInfo ? 'xs' : 'sm'} className={cn('shrink-0 mr-2', hideInfo && '!mr-1')} type={nodeInfo.node_type} toolIcon={nodeInfo.extras?.icon || nodeInfo.extras} />
<div className={cn(
'grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate',
@ -82,48 +73,6 @@ const NodePanel: FC<Props> = ({ nodeInfo, hideInfo = false }) => {
</div>
)}
</div>
{!collapseState && (
<div className='pb-2'>
<div className={cn('px-[10px] py-1', hideInfo && '!px-2 !py-0.5')}>
{nodeInfo.status === 'failed' && (
<div className='px-3 py-[10px] bg-[#fef3f2] rounded-lg border-[0.5px] border-[rbga(0,0,0,0.05)] text-xs leading-[18px] text-[#d92d20] shadow-xs'>{nodeInfo.error}</div>
)}
</div>
{nodeInfo.inputs && (
<div className={cn('px-[10px] py-1', hideInfo && '!px-2 !py-0.5')}>
<CodeEditor
readOnly
title={<div>INPUT</div>}
language={CodeLanguage.json}
value={nodeInfo.inputs}
isJSONStringifyBeauty
/>
</div>
)}
{nodeInfo.process_data && (
<div className={cn('px-[10px] py-1', hideInfo && '!px-2 !py-0.5')}>
<CodeEditor
readOnly
title={<div>PROCESS DATA</div>}
language={CodeLanguage.json}
value={nodeInfo.process_data}
isJSONStringifyBeauty
/>
</div>
)}
{nodeInfo.outputs && (
<div className={cn('px-[10px] py-1', hideInfo && '!px-2 !py-0.5')}>
<CodeEditor
readOnly
title={<div>OUTPUT</div>}
language={CodeLanguage.json}
value={nodeInfo.outputs}
isJSONStringifyBeauty
/>
</div>
)}
</div>
)}
</div>
</div>
)

View File

@ -7,7 +7,7 @@ export const APP_INFO: AppInfo = {
description: '',
copyright: '',
privacy_policy: '',
default_language: 'zh-Hans',
default_language: 'en',
}
export const isShowPrompt = false

View File

@ -2,11 +2,20 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import commonEn from './lang/common.en'
import commonEs from './lang/common.es'
import commonZh from './lang/common.zh'
import commonVi from './lang/common.vi'
import commonJa from './lang/common.ja'
import appEn from './lang/app.en'
import appEs from './lang/app.es'
import appZh from './lang/app.zh'
import appVi from './lang/app.vi'
import appJa from './lang/app.ja'
import toolsEn from './lang/tools.en'
import toolsZh from './lang/tools.zh'
import toolsVi from './lang/tools.vi'
import toolsJa from './lang/tools.ja'
import type { Locale } from '.'
const resources = {
@ -18,6 +27,12 @@ const resources = {
tools: toolsEn,
},
},
'es': {
translation: {
common: commonEs,
app: appEs,
},
},
'zh-Hans': {
translation: {
common: commonZh,
@ -26,6 +41,22 @@ const resources = {
tools: toolsZh,
},
},
'vi': {
translation: {
common: commonVi,
app: appVi,
// tools
tools: toolsVi,
},
},
'ja': {
translation: {
common: commonJa,
app: appJa,
// tools
tools: toolsJa,
},
},
}
i18n.use(initReactI18next)

View File

@ -1,6 +1,6 @@
export const i18n = {
defaultLocale: 'en',
locales: ['en', 'zh-Hans'],
locales: ['en', 'es', 'zh-Hans', 'ja'],
} as const
export type Locale = typeof i18n['locales'][number]

33
i18n/lang/app.es.ts Normal file
View File

@ -0,0 +1,33 @@
const translation = {
common: {
welcome: 'Bienvenido a usar',
appUnavailable: 'App es inaccesible',
appUnkonwError: 'App es inaccesible',
},
chat: {
newChat: 'Nuevo chat',
newChatDefaultName: 'Nueva conversación',
openingStatementTitle: 'Frase de apertura',
powerBy: 'Desarrollado por',
prompt: 'Prompt',
privatePromptConfigTitle: 'Ajustes de conversación',
publicPromptConfigTitle: 'Prompt inicial',
configStatusDes: 'Antes de comenzar, puede modificar la configuración de la conversación',
configDisabled:
'La configuración de la sesión anterior se ha utilizado para esta sesión.',
startChat: 'Comenzar chat',
privacyPolicyLeft:
'Por favor lea la ',
privacyPolicyMiddle:
'política de privacidad',
privacyPolicyRight:
' proporcionada por el desarrollador de la aplicación.',
},
errorMessage: {
valueOfVarRequired: 'El valor de las variables no puede estar vacío',
waitForResponse:
'Por favor espere a que la respuesta al mensaje anterior se complete.',
},
}
export default translation

36
i18n/lang/app.ja.ts Normal file
View File

@ -0,0 +1,36 @@
const translation = {
common: {
welcome: 'ご利用いただきありがとうございます',
appUnavailable: 'アプリは利用できません',
appUnkonwError: 'アプリは利用できません',
},
chat: {
newChat: '新しいチャット',
newChatDefaultName: '新しい会話',
openingStatementTitle: 'オープニングステートメント',
powerBy: '提供元',
prompt: 'プロンプト',
privatePromptConfigTitle: '会話設定',
publicPromptConfigTitle: '初期プロンプト',
configStatusDes: '開始前に、会話設定を変更できます',
configDisabled:
'前回のセッション設定がこのセッションで使用されています。',
startChat: '開始',
privacyPolicyLeft:
'ご利用前に、',
privacyPolicyMiddle:
'プライバシーポリシー',
privacyPolicyRight:
' をお読みください。',
},
errorMessage: {
valueOfVarRequired: '変数の値は空にできません',
waitForResponse:
'前のメッセージの応答が完了するまでお待ちください。',
},
variableTable: {
optional: '任意',
},
}
export default translation

36
i18n/lang/app.vi.ts Normal file
View File

@ -0,0 +1,36 @@
const translation = {
common: {
welcome: 'Chào mừng bạn sử dụng',
appUnavailable: 'Ứng dụng không khả dụng',
appUnkonwError: 'Ứng dụng không khả dụng',
},
chat: {
newChat: 'Cuộc trò chuyện mới',
newChatDefaultName: 'Cuộc trò chuyện mới',
openingStatementTitle: 'Lời mở đầu',
powerBy: 'Được hỗ trợ bởi',
prompt: 'Nhắc nhở',
privatePromptConfigTitle: 'Cài đặt cuộc trò chuyện',
publicPromptConfigTitle: 'Nhắc nhở ban đầu',
configStatusDes: 'Trước khi bắt đầu, bạn có thể chỉnh sửa cài đặt cuộc trò chuyện',
configDisabled:
'Cài đặt của phiên trước đã được sử dụng cho phiên này.',
startChat: 'Bắt đầu trò chuyện',
privacyPolicyLeft:
'Vui lòng đọc ',
privacyPolicyMiddle:
'chính sách bảo mật',
privacyPolicyRight:
' được cung cấp bởi nhà phát triển ứng dụng.',
},
errorMessage: {
valueOfVarRequired: 'Giá trị của biến không thể để trống',
waitForResponse:
'Vui lòng đợi phản hồi từ tin nhắn trước khi gửi tin nhắn mới.',
},
variableTable: {
optional: 'Tùy chọn',
},
}
export default translation;

33
i18n/lang/common.es.ts Normal file
View File

@ -0,0 +1,33 @@
const translation = {
api: {
success: 'Éxito',
saved: 'Guardado',
create: 'Creado',
},
operation: {
confirm: 'Confirmar',
cancel: 'Cancelar',
clear: 'Limpiar',
save: 'Guardar',
edit: 'Editar',
refresh: 'Reiniciar',
search: 'Buscar',
send: 'Enviar',
lineBreak: 'Salto de línea',
like: 'Me gusta',
dislike: 'No me gusta',
ok: 'OK',
},
imageUploader: {
uploadFromComputer: 'Subir desde el ordenador',
uploadFromComputerReadError: 'La lectura de la imagen falló, por favor inténtelo de nuevo.',
uploadFromComputerUploadError: 'Error al subir la imagen, por favor inténtelo de nuevo.',
uploadFromComputerLimit: 'Las imágenes subidas no pueden superar los {{size}} MB',
pasteImageLink: 'Pegar enlace de imagen',
pasteImageLinkInputPlaceholder: 'Pegar enlace de imagen aquí',
pasteImageLinkInvalid: 'Enlace de imagen no válido',
imageUpload: 'Subir imagen',
},
}
export default translation

33
i18n/lang/common.ja.ts Normal file
View File

@ -0,0 +1,33 @@
const translation = {
api: {
success: '成功',
saved: '保存しました',
create: '作成しました',
},
operation: {
confirm: '確認',
cancel: 'キャンセル',
clear: 'クリア',
save: '保存',
edit: '編集',
refresh: '再起動',
search: '検索',
send: '送信',
lineBreak: '改行',
like: 'いいね',
dislike: 'よくないね',
ok: 'OK',
},
imageUploader: {
uploadFromComputer: 'コンピューターからアップロード',
uploadFromComputerReadError: '画像の読み込みに失敗しました。もう一度お試しください。',
uploadFromComputerUploadError: '画像のアップロードに失敗しました。もう一度アップロードしてください。',
uploadFromComputerLimit: 'アップロードする画像は{{size}} MBを超えてはいけません',
pasteImageLink: '画像リンクを貼り付け',
pasteImageLinkInputPlaceholder: 'ここに画像リンクを貼り付けてください',
pasteImageLinkInvalid: '無効な画像リンクです',
imageUpload: '画像アップロード',
},
}
export default translation

33
i18n/lang/common.vi.ts Normal file
View File

@ -0,0 +1,33 @@
const translation = {
api: {
success: 'Thành công',
saved: 'Đã lưu',
create: 'Đã tạo',
},
operation: {
confirm: 'Xác nhận',
cancel: 'Hủy',
clear: 'Xóa',
save: 'Lưu',
edit: 'Chỉnh sửa',
refresh: 'Khởi động lại',
search: 'Tìm kiếm',
send: 'Gửi',
lineBreak: 'Xuống dòng',
like: 'thích',
dislike: 'không thích',
ok: 'OK',
},
imageUploader: {
uploadFromComputer: 'Tải lên từ máy tính',
uploadFromComputerReadError: 'Đọc ảnh thất bại, vui lòng thử lại.',
uploadFromComputerUploadError: 'Tải ảnh lên thất bại, vui lòng tải lại.',
uploadFromComputerLimit: 'Ảnh tải lên không được vượt quá {{size}} MB',
pasteImageLink: 'Dán liên kết ảnh',
pasteImageLinkInputPlaceholder: 'Dán liên kết ảnh vào đây',
pasteImageLinkInvalid: 'Liên kết ảnh không hợp lệ',
imageUpload: 'Tải ảnh lên',
},
}
export default translation;

103
i18n/lang/tools.ja.ts Normal file
View File

@ -0,0 +1,103 @@
const translation = {
title: 'ツール',
createCustomTool: 'カスタムツールの作成',
type: {
all: 'すべて',
builtIn: '内蔵',
custom: 'カスタム',
},
contribute: {
line1: '興味があります ',
line2: 'Difyにツールを貢献すること。',
viewGuide: 'ガイドを見る',
},
author: '作成者',
auth: {
unauthorized: '認証が必要',
authorized: '認証済み',
setup: '使用するために認証を設定',
setupModalTitle: '認証設定',
setupModalTitleDescription: '資格情報を設定すると、ワークスペース内のすべてのメンバーがアプリケーションを編成する際にこのツールを使用できるようになります。',
},
includeToolNum: '{{num}} のツールが含まれています',
addTool: 'ツールを追加',
createTool: {
title: 'カスタムツールの作成',
editAction: '設定',
editTitle: 'カスタムツールの編集',
name: '名前',
toolNamePlaceHolder: 'ツール名を入力してください',
schema: 'スキーマ',
schemaPlaceHolder: 'ここにOpenAPIスキーマを入力してください',
viewSchemaSpec: 'OpenAPI-Swagger仕様を見る',
importFromUrl: 'URLからインポート',
importFromUrlPlaceHolder: 'https://...',
urlError: '有効なURLを入力してください',
examples: '例',
exampleOptions: {
json: '天気予報JSON',
yaml: 'ペットストアYAML',
blankTemplate: '空のテンプレート',
},
availableTools: {
title: '利用可能なツール',
name: '名前',
description: '説明',
method: 'メソッド',
path: 'パス',
action: 'アクション',
test: 'テスト',
},
authMethod: {
title: '認証方法',
type: '認証タイプ',
types: {
none: 'なし',
api_key: 'APIキー',
},
key: 'キー',
value: '値',
},
privacyPolicy: 'プライバシーポリシー',
privacyPolicyPlaceholder: 'プライバシーポリシーを入力してください',
},
test: {
title: 'テスト',
parametersValue: 'パラメータと値',
parameters: 'パラメータ',
value: '値',
testResult: 'テスト結果',
testResultPlaceholder: 'テスト結果はここに表示されます',
},
thought: {
using: '使用中',
used: '使用済み',
requestTitle: 'リクエスト先',
responseTitle: 'レスポンス元',
},
setBuiltInTools: {
info: '情報',
setting: '設定',
toolDescription: 'ツールの説明',
parameters: 'パラメータ',
string: '文字列',
number: '数値',
required: '必須',
infoAndSetting: '情報と設定',
},
noCustomTool: {
title: 'カスタムツールがありません!',
content: 'ここでカスタムツールを追加および管理して、AIアプリを構築します。',
createTool: 'ツールの作成',
},
noSearchRes: {
title: '申し訳ありません、結果が見つかりません!',
content: '検索条件に一致するツールは見つかりませんでした。',
reset: '検索をリセット',
},
builtInPromptTitle: 'プロンプト',
toolRemoved: 'ツールが削除されました',
notAuthorized: 'ツールが認証されていません',
}
export default translation

103
i18n/lang/tools.vi.ts Normal file
View File

@ -0,0 +1,103 @@
const translation = {
title: 'Công cụ',
createCustomTool: 'Tạo công cụ tùy chỉnh',
type: {
all: 'Tất cả',
builtIn: 'Có sẵn',
custom: 'Tùy chỉnh',
},
contribute: {
line1: 'Tôi quan tâm đến việc ',
line2: 'đóng góp công cụ cho Dify.',
viewGuide: 'Xem hướng dẫn',
},
author: 'Bởi',
auth: {
unauthorized: 'Chưa ủy quyền',
authorized: 'Đã ủy quyền',
setup: 'Thiết lập ủy quyền để sử dụng',
setupModalTitle: 'Thiết lập ủy quyền',
setupModalTitleDescription: 'Sau khi cấu hình thông tin xác thực, tất cả các thành viên trong không gian làm việc đều có thể sử dụng công cụ này khi sắp xếp các ứng dụng.',
},
includeToolNum: 'Bao gồm {{num}} công cụ',
addTool: 'Thêm công cụ',
createTool: {
title: 'Tạo công cụ tùy chỉnh',
editAction: 'Cấu hình',
editTitle: 'Chỉnh sửa công cụ tùy chỉnh',
name: 'Tên',
toolNamePlaceHolder: 'Nhập tên công cụ',
schema: 'Schema',
schemaPlaceHolder: 'Nhập schema OpenAPI của bạn tại đây',
viewSchemaSpec: 'Xem đặc tả OpenAPI-Swagger',
importFromUrl: 'Nhập từ URL',
importFromUrlPlaceHolder: 'https://...',
urlError: 'Vui lòng nhập URL hợp lệ',
examples: 'Ví dụ',
exampleOptions: {
json: 'Thời tiết(JSON)',
yaml: 'Cửa hàng thú cưng(YAML)',
blankTemplate: 'Mẫu trống',
},
availableTools: {
title: 'Công cụ có sẵn',
name: 'Tên',
description: 'Mô tả',
method: 'Phương thức',
path: 'Đường dẫn',
action: 'Hành động',
test: 'Kiểm tra',
},
authMethod: {
title: 'Phương thức ủy quyền',
type: 'Loại ủy quyền',
types: {
none: 'Không có',
api_key: 'API Key',
},
key: 'Khóa',
value: 'Giá trị',
},
privacyPolicy: 'Chính sách bảo mật',
privacyPolicyPlaceholder: 'Vui lòng nhập chính sách bảo mật',
},
test: {
title: 'Kiểm tra',
parametersValue: 'Tham số & Giá trị',
parameters: 'Tham số',
value: 'Giá trị',
testResult: 'Kết quả kiểm tra',
testResultPlaceholder: 'Kết quả kiểm tra sẽ hiển thị ở đây',
},
thought: {
using: 'Đang sử dụng',
used: 'Đã sử dụng',
requestTitle: 'Yêu cầu đến',
responseTitle: 'Phản hồi từ',
},
setBuiltInTools: {
info: 'Thông tin',
setting: 'Cài đặt',
toolDescription: 'Mô tả công cụ',
parameters: 'tham số',
string: 'chuỗi',
number: 'số',
required: 'Bắt buộc',
infoAndSetting: 'Thông tin & Cài đặt',
},
noCustomTool: {
title: 'Không có công cụ tùy chỉnh!',
content: 'Thêm và quản lý các công cụ tùy chỉnh của bạn tại đây để xây dựng các ứng dụng AI.',
createTool: 'Tạo công cụ',
},
noSearchRes: {
title: 'Xin lỗi, không tìm thấy kết quả!',
content: 'Chúng tôi không thể tìm thấy bất kỳ công cụ nào phù hợp với tìm kiếm của bạn.',
reset: 'Đặt lại tìm kiếm',
},
builtInPromptTitle: 'Nhắc nhở',
toolRemoved: 'Công cụ đã được xóa',
notAuthorized: 'Công cụ chưa được ủy quyền',
}
export default translation;

View File

@ -37,6 +37,8 @@ export type UserInputFormItem = {
'text-input': TextTypeFormItem
} | {
'select': SelectTypeFormItem
} | {
'paragraph': TextTypeFormItem
}
export const MessageRatings = ['like', 'dislike', null] as const

View File

@ -24,8 +24,12 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
if (item['text-input'])
return ['string', item['text-input']]
if (item.number)
return ['number', item.number]
return ['select', item.select]
})()
if (type === 'string' || type === 'paragraph') {
promptVariables.push({
key: content.variable,
@ -36,6 +40,15 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
options: [],
})
}
else if (type === 'number') {
promptVariables.push({
key: content.variable,
name: content.label,
required: content.required,
type,
options: [],
})
}
else {
promptVariables.push({
key: content.variable,