mirror of
https://github.com/langgenius/dify.git
synced 2026-02-24 09:55:09 +00:00
fix: use localized doc pathMap links for multilingual anchors
Some checks failed
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/amd64, build-api-amd64) (push) Has been cancelled
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/arm64, build-api-arm64) (push) Has been cancelled
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/amd64, build-web-amd64) (push) Has been cancelled
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/arm64, build-web-arm64) (push) Has been cancelled
Build and Push API & Web / create-manifest (api, DIFY_API_IMAGE_NAME, merge-api-images) (push) Has been cancelled
Build and Push API & Web / create-manifest (web, DIFY_WEB_IMAGE_NAME, merge-web-images) (push) Has been cancelled
Some checks failed
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/amd64, build-api-amd64) (push) Has been cancelled
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/arm64, build-api-arm64) (push) Has been cancelled
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/amd64, build-web-amd64) (push) Has been cancelled
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/arm64, build-web-arm64) (push) Has been cancelled
Build and Push API & Web / create-manifest (api, DIFY_API_IMAGE_NAME, merge-api-images) (push) Has been cancelled
Build and Push API & Web / create-manifest (web, DIFY_WEB_IMAGE_NAME, merge-web-images) (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { useDebounceFn, useKeyPress } from 'ahooks'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { useRouter } from 'next/navigation'
|
||||
@@ -15,7 +16,6 @@ import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
||||
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { appManagementAnchorMap } from '@/context/doc-anchors'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import {
|
||||
@@ -47,6 +47,13 @@ export enum CreateFromDSLModalTab {
|
||||
FROM_URL = 'from-url',
|
||||
}
|
||||
|
||||
const appManagementLocalizedPathMap = {
|
||||
'zh-Hans': '/use-dify/workspace/app-management#应用导出和导入' as DocPathWithoutLang,
|
||||
'zh_Hans': '/use-dify/workspace/app-management#应用导出和导入' as DocPathWithoutLang,
|
||||
'ja-JP': '/use-dify/workspace/app-management#アプリのエクスポートとインポート' as DocPathWithoutLang,
|
||||
'ja_JP': '/use-dify/workspace/app-management#アプリのエクスポートとインポート' as DocPathWithoutLang,
|
||||
}
|
||||
|
||||
const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDSLModalTab.FROM_FILE, dslUrl = '', droppedFile }: CreateFromDSLModalProps) => {
|
||||
const { push } = useRouter()
|
||||
const { t } = useTranslation()
|
||||
@@ -313,7 +320,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
<div className="flex items-center justify-between px-6 pb-6 pt-5">
|
||||
<a
|
||||
className="flex items-center gap-1 text-text-accent system-xs-regular"
|
||||
href={docLink('/use-dify/workspace/app-management#app-export-and-import', { anchorMap: appManagementAnchorMap })}
|
||||
href={docLink('/use-dify/workspace/app-management#app-export-and-import', appManagementLocalizedPathMap)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { FileDownload01 } from '@/app/components/base/icons/src/vender/line/file
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import ArtifactsTree from '@/app/components/workflow/skill/file-tree/artifacts/artifacts-tree'
|
||||
import ReadOnlyFilePreview from '@/app/components/workflow/skill/viewer/read-only-file-preview'
|
||||
import { fileSystemArtifactsAnchorMap } from '@/context/doc-anchors'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useDownloadSandboxFile, useSandboxFileDownloadUrl, useSandboxFilesTree } from '@/service/use-sandbox-file'
|
||||
import { cn } from '@/utils/classnames'
|
||||
@@ -19,6 +18,13 @@ import { WorkflowRunningStatus } from '../types'
|
||||
import InspectLayout from './inspect-layout'
|
||||
import SplitPanel from './split-panel'
|
||||
|
||||
const fileSystemArtifactsLocalizedPathMap = {
|
||||
'zh-Hans': '/use-dify/build/file-system#产物' as DocPathWithoutLang,
|
||||
'zh_Hans': '/use-dify/build/file-system#产物' as DocPathWithoutLang,
|
||||
'ja-JP': '/use-dify/build/file-system#アーティファクト' as DocPathWithoutLang,
|
||||
'ja_JP': '/use-dify/build/file-system#アーティファクト' as DocPathWithoutLang,
|
||||
}
|
||||
|
||||
const ArtifactsEmpty = ({ description }: { description: string }) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const docLink = useDocLink()
|
||||
@@ -33,7 +39,7 @@ const ArtifactsEmpty = ({ description }: { description: string }) => {
|
||||
<div className="text-text-tertiary system-xs-regular">{description}</div>
|
||||
<a
|
||||
className="cursor-pointer text-text-accent system-xs-regular"
|
||||
href={docLink('/use-dify/build/file-system#artifacts' as DocPathWithoutLang, { anchorMap: fileSystemArtifactsAnchorMap })}
|
||||
href={docLink('/use-dify/build/file-system#artifacts' as DocPathWithoutLang, fileSystemArtifactsLocalizedPathMap)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { DocAnchorMap } from './i18n'
|
||||
|
||||
export const appManagementAnchorMap = {
|
||||
'zh-Hans': '应用导出和导入',
|
||||
'zh_Hans': '应用导出和导入',
|
||||
'ja-JP': 'アプリのエクスポートとインポート',
|
||||
'ja_JP': 'アプリのエクスポートとインポート',
|
||||
} satisfies DocAnchorMap
|
||||
|
||||
export const fileSystemArtifactsAnchorMap = {
|
||||
'zh-Hans': '产物',
|
||||
'zh_Hans': '产物',
|
||||
'ja-JP': 'アーティファクト',
|
||||
'ja_JP': 'アーティファクト',
|
||||
} satisfies DocAnchorMap
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DocAnchorMap, DocPathMap } from './i18n'
|
||||
import type { DocPathMap } from './i18n'
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { useTranslation } from '#i18n'
|
||||
import { renderHook } from '@testing-library/react'
|
||||
@@ -103,7 +103,7 @@ describe('useDocLink', () => {
|
||||
}
|
||||
|
||||
const { result } = renderHook(() => useDocLink())
|
||||
const url = result.current('/use-dify/getting-started/quick-start' as DocPathWithoutLang, { pathMap })
|
||||
const url = result.current('/use-dify/getting-started/quick-start' as DocPathWithoutLang, pathMap)
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/zh/use-dify/getting-started/introduction`)
|
||||
})
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('useDocLink', () => {
|
||||
}
|
||||
|
||||
const { result } = renderHook(() => useDocLink())
|
||||
const url = result.current('/use-dify/getting-started/quick-start' as DocPathWithoutLang, { pathMap })
|
||||
const url = result.current('/use-dify/getting-started/quick-start' as DocPathWithoutLang, pathMap)
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/ja/use-dify/getting-started/quick-start`)
|
||||
})
|
||||
|
||||
@@ -234,17 +234,6 @@ describe('useDocLink', () => {
|
||||
const url = result.current('/use-dify/getting-started/introduction')
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/zh/use-dify/getting-started/introduction`)
|
||||
})
|
||||
|
||||
it('should preserve anchor while translating API reference path', () => {
|
||||
vi.mocked(useTranslation).mockReturnValue({
|
||||
i18n: { language: 'zh-Hans' },
|
||||
} as ReturnType<typeof useTranslation>)
|
||||
vi.mocked(getDocLanguage).mockReturnValue('zh')
|
||||
|
||||
const { result } = renderHook(() => useDocLink())
|
||||
const url = result.current('/api-reference/annotations/create-annotation#request-body')
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/api-reference/标注管理/创建标注#request-body`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
@@ -254,20 +243,22 @@ describe('useDocLink', () => {
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/en/use-dify/getting-started/introduction#overview`)
|
||||
})
|
||||
|
||||
it('should support locale-specific anchors via anchorMap', () => {
|
||||
it('should support locale-specific anchors via pathMap', () => {
|
||||
vi.mocked(useTranslation).mockReturnValue({
|
||||
i18n: { language: 'zh-Hans' },
|
||||
} as ReturnType<typeof useTranslation>)
|
||||
vi.mocked(getDocLanguage).mockReturnValue('zh')
|
||||
|
||||
const anchorMap: DocAnchorMap = {
|
||||
'zh-Hans': '应用导出和导入',
|
||||
'ja-JP': 'アプリのエクスポートとインポート',
|
||||
const pathMap: DocPathMap = {
|
||||
'zh-Hans': '/use-dify/workspace/app-management#应用导出和导入' as DocPathWithoutLang,
|
||||
'zh_Hans': '/use-dify/workspace/app-management#应用导出和导入' as DocPathWithoutLang,
|
||||
'ja-JP': '/use-dify/workspace/app-management#アプリのエクスポートとインポート' as DocPathWithoutLang,
|
||||
'ja_JP': '/use-dify/workspace/app-management#アプリのエクスポートとインポート' as DocPathWithoutLang,
|
||||
}
|
||||
|
||||
const { result } = renderHook(() => useDocLink())
|
||||
const url = result.current('/use-dify/workspace/app-management#app-export-and-import', { anchorMap })
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/zh/use-dify/workspace/app-management#${encodeURIComponent('应用导出和导入')}`)
|
||||
const url = result.current('/use-dify/workspace/app-management#app-export-and-import', pathMap)
|
||||
expect(url).toBe(`${defaultDocBaseUrl}/zh/use-dify/workspace/app-management#应用导出和导入`)
|
||||
})
|
||||
|
||||
it('should handle multiple calls with same hook instance', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Locale } from '@/i18n-config/language'
|
||||
import type { DocPathWithoutLang } from '@/types/doc-paths'
|
||||
import { useTranslation } from '#i18n'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { getDocLanguage, getLanguage, getPricingPageLanguage } from '@/i18n-config/language'
|
||||
import { apiReferencePathTranslations } from '@/types/doc-paths'
|
||||
|
||||
@@ -23,63 +23,29 @@ export const useGetPricingPageLanguage = () => {
|
||||
|
||||
export const defaultDocBaseUrl = 'https://docs.bash-is-all-you-need.dify.dev'
|
||||
export type DocPathMap = Partial<Record<Locale, DocPathWithoutLang>>
|
||||
export type DocAnchorMap = Partial<Record<Locale, string>>
|
||||
export type DocLinkOptions = {
|
||||
pathMap?: DocPathMap
|
||||
anchorMap?: DocAnchorMap
|
||||
}
|
||||
export type BuildDocLink = (path?: DocPathWithoutLang, options?: DocLinkOptions) => string
|
||||
|
||||
const splitPathWithHash = (path: string) => {
|
||||
const [pathname, ...hashParts] = path.split('#')
|
||||
return {
|
||||
pathname,
|
||||
hash: hashParts.join('#'),
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeAnchor = (anchor: string) => {
|
||||
const normalizedAnchor = anchor.startsWith('#') ? anchor.slice(1) : anchor
|
||||
if (!normalizedAnchor)
|
||||
return ''
|
||||
|
||||
const isAsciiOnly = Array.from(normalizedAnchor).every(char => char.codePointAt(0)! <= 0x7F)
|
||||
if (isAsciiOnly)
|
||||
return normalizedAnchor
|
||||
|
||||
return encodeURIComponent(normalizedAnchor)
|
||||
}
|
||||
|
||||
export const useDocLink = (baseUrl?: string): BuildDocLink => {
|
||||
const baseDocUrl = useMemo(() => {
|
||||
const resolvedBaseUrl = baseUrl || defaultDocBaseUrl
|
||||
return resolvedBaseUrl.endsWith('/') ? resolvedBaseUrl.slice(0, -1) : resolvedBaseUrl
|
||||
}, [baseUrl])
|
||||
export const useDocLink = (baseUrl?: string): ((path?: DocPathWithoutLang, pathMap?: DocPathMap) => string) => {
|
||||
let baseDocUrl = baseUrl || defaultDocBaseUrl
|
||||
baseDocUrl = (baseDocUrl.endsWith('/')) ? baseDocUrl.slice(0, -1) : baseDocUrl
|
||||
const locale = useLocale()
|
||||
return useCallback(
|
||||
(path?: DocPathWithoutLang, options?: DocLinkOptions): string => {
|
||||
(path?: DocPathWithoutLang, pathMap?: DocPathMap): string => {
|
||||
const docLanguage = getDocLanguage(locale)
|
||||
const pathUrl = path || ''
|
||||
const { pathMap, anchorMap } = options || {}
|
||||
const targetPath = (pathMap) ? pathMap[locale] || pathUrl : pathUrl
|
||||
const { pathname: pathWithoutHash, hash: pathAnchor } = splitPathWithHash(targetPath)
|
||||
let targetPathWithoutHash = pathWithoutHash
|
||||
let targetPath = (pathMap) ? pathMap[locale] || pathUrl : pathUrl
|
||||
let languagePrefix = `/${docLanguage}`
|
||||
|
||||
if (targetPathWithoutHash.startsWith('/api-reference/')) {
|
||||
if (targetPath.startsWith('/api-reference/')) {
|
||||
languagePrefix = ''
|
||||
if (docLanguage !== 'en') {
|
||||
const translatedPath = apiReferencePathTranslations[targetPathWithoutHash]?.[docLanguage]
|
||||
const translatedPath = apiReferencePathTranslations[targetPath]?.[docLanguage]
|
||||
if (translatedPath) {
|
||||
targetPathWithoutHash = translatedPath
|
||||
targetPath = translatedPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const anchor = normalizeAnchor(anchorMap?.[locale] || pathAnchor)
|
||||
const anchorSuffix = anchor ? `#${anchor}` : ''
|
||||
|
||||
return `${baseDocUrl}${languagePrefix}${targetPathWithoutHash}${anchorSuffix}`
|
||||
return `${baseDocUrl}${languagePrefix}${targetPath}`
|
||||
},
|
||||
[baseDocUrl, locale],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user