mirror of
https://github.com/langgenius/dify.git
synced 2026-01-14 06:07:33 +08:00
refactor(i18n): about locales (#30336)
Some checks are pending
autofix.ci / autofix (push) Waiting to run
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/amd64, build-api-amd64) (push) Waiting to run
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/arm64, build-api-arm64) (push) Waiting to run
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/amd64, build-web-amd64) (push) Waiting to run
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/arm64, build-web-arm64) (push) Waiting to run
Build and Push API & Web / create-manifest (api, DIFY_API_IMAGE_NAME, merge-api-images) (push) Blocked by required conditions
Build and Push API & Web / create-manifest (web, DIFY_WEB_IMAGE_NAME, merge-web-images) (push) Blocked by required conditions
Main CI Pipeline / Check Changed Files (push) Waiting to run
Main CI Pipeline / API Tests (push) Blocked by required conditions
Main CI Pipeline / Web Tests (push) Blocked by required conditions
Main CI Pipeline / Style Check (push) Waiting to run
Main CI Pipeline / VDB Tests (push) Blocked by required conditions
Main CI Pipeline / DB Migration Test (push) Blocked by required conditions
Some checks are pending
autofix.ci / autofix (push) Waiting to run
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/amd64, build-api-amd64) (push) Waiting to run
Build and Push API & Web / build (api, DIFY_API_IMAGE_NAME, linux/arm64, build-api-arm64) (push) Waiting to run
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/amd64, build-web-amd64) (push) Waiting to run
Build and Push API & Web / build (web, DIFY_WEB_IMAGE_NAME, linux/arm64, build-web-arm64) (push) Waiting to run
Build and Push API & Web / create-manifest (api, DIFY_API_IMAGE_NAME, merge-api-images) (push) Blocked by required conditions
Build and Push API & Web / create-manifest (web, DIFY_WEB_IMAGE_NAME, merge-web-images) (push) Blocked by required conditions
Main CI Pipeline / Check Changed Files (push) Waiting to run
Main CI Pipeline / API Tests (push) Blocked by required conditions
Main CI Pipeline / Web Tests (push) Blocked by required conditions
Main CI Pipeline / Style Check (push) Waiting to run
Main CI Pipeline / VDB Tests (push) Blocked by required conditions
Main CI Pipeline / DB Migration Test (push) Blocked by required conditions
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
parent
3505516e8e
commit
2399d00d86
@ -8,7 +8,7 @@ import { noop } from 'es-toolkit/compat'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import Picker from '@/app/components/base/date-and-time-picker/date-picker'
|
import Picker from '@/app/components/base/date-and-time-picker/date-picker'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
import { formatToLocalTime } from '@/utils/format'
|
import { formatToLocalTime } from '@/utils/format'
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ const DatePicker: FC<Props> = ({
|
|||||||
onStartChange,
|
onStartChange,
|
||||||
onEndChange,
|
onEndChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
|
|
||||||
const renderDate = useCallback(({ value, handleClickTrigger, isOpen }: TriggerProps) => {
|
const renderDate = useCallback(({ value, handleClickTrigger, isOpen }: TriggerProps) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import dayjs from 'dayjs'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { HourglassShape } from '@/app/components/base/icons/src/vender/other'
|
import { HourglassShape } from '@/app/components/base/icons/src/vender/other'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { formatToLocalTime } from '@/utils/format'
|
import { formatToLocalTime } from '@/utils/format'
|
||||||
import DatePicker from './date-picker'
|
import DatePicker from './date-picker'
|
||||||
import RangeSelector from './range-selector'
|
import RangeSelector from './range-selector'
|
||||||
@ -27,7 +27,7 @@ const TimeRangePicker: FC<Props> = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
queryDateFormat,
|
queryDateFormat,
|
||||||
}) => {
|
}) => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
|
|
||||||
const [isCustomRange, setIsCustomRange] = useState(false)
|
const [isCustomRange, setIsCustomRange] = useState(false)
|
||||||
const [start, setStart] = useState<Dayjs>(today)
|
const [start, setStart] = useState<Dayjs>(today)
|
||||||
|
|||||||
@ -3,12 +3,12 @@ import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Countdown from '@/app/components/signin/countdown'
|
import Countdown from '@/app/components/signin/countdown'
|
||||||
import I18NContext from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import { sendWebAppResetPasswordCode, verifyWebAppResetPasswordCode } from '@/service/common'
|
import { sendWebAppResetPasswordCode, verifyWebAppResetPasswordCode } from '@/service/common'
|
||||||
|
|
||||||
export default function CheckCode() {
|
export default function CheckCode() {
|
||||||
@ -19,7 +19,7 @@ export default function CheckCode() {
|
|||||||
const token = decodeURIComponent(searchParams.get('token') as string)
|
const token = decodeURIComponent(searchParams.get('token') as string)
|
||||||
const [code, setVerifyCode] = useState('')
|
const [code, setVerifyCode] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const verify = async () => {
|
const verify = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -5,13 +5,13 @@ import Link from 'next/link'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import useDocumentTitle from '@/hooks/use-document-title'
|
import useDocumentTitle from '@/hooks/use-document-title'
|
||||||
import { sendResetPasswordCode } from '@/service/common'
|
import { sendResetPasswordCode } from '@/service/common'
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export default function CheckCode() {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const handleGetEMailVerificationCode = async () => {
|
const handleGetEMailVerificationCode = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Countdown from '@/app/components/signin/countdown'
|
import Countdown from '@/app/components/signin/countdown'
|
||||||
import I18NContext from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useWebAppStore } from '@/context/web-app-context'
|
import { useWebAppStore } from '@/context/web-app-context'
|
||||||
import { sendWebAppEMailLoginCode, webAppEmailLoginWithCode } from '@/service/common'
|
import { sendWebAppEMailLoginCode, webAppEmailLoginWithCode } from '@/service/common'
|
||||||
import { fetchAccessToken } from '@/service/share'
|
import { fetchAccessToken } from '@/service/share'
|
||||||
@ -23,7 +23,7 @@ export default function CheckCode() {
|
|||||||
const token = decodeURIComponent(searchParams.get('token') as string)
|
const token = decodeURIComponent(searchParams.get('token') as string)
|
||||||
const [code, setVerifyCode] = useState('')
|
const [code, setVerifyCode] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
const codeInputRef = useRef<HTMLInputElement>(null)
|
const codeInputRef = useRef<HTMLInputElement>(null)
|
||||||
const redirectUrl = searchParams.get('redirect_url')
|
const redirectUrl = searchParams.get('redirect_url')
|
||||||
const embeddedUserId = useWebAppStore(s => s.embeddedUserId)
|
const embeddedUserId = useWebAppStore(s => s.embeddedUserId)
|
||||||
|
|||||||
@ -2,13 +2,12 @@ import { noop } from 'es-toolkit/compat'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { sendWebAppEMailLoginCode } from '@/service/common'
|
import { sendWebAppEMailLoginCode } from '@/service/common'
|
||||||
|
|
||||||
export default function MailAndCodeAuth() {
|
export default function MailAndCodeAuth() {
|
||||||
@ -18,7 +17,7 @@ export default function MailAndCodeAuth() {
|
|||||||
const emailFromLink = decodeURIComponent(searchParams.get('email') || '')
|
const emailFromLink = decodeURIComponent(searchParams.get('email') || '')
|
||||||
const [email, setEmail] = useState(emailFromLink)
|
const [email, setEmail] = useState(emailFromLink)
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const handleGetEMailVerificationCode = async () => {
|
const handleGetEMailVerificationCode = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -4,12 +4,11 @@ import Link from 'next/link'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useWebAppStore } from '@/context/web-app-context'
|
import { useWebAppStore } from '@/context/web-app-context'
|
||||||
import { webAppLogin } from '@/service/common'
|
import { webAppLogin } from '@/service/common'
|
||||||
import { fetchAccessToken } from '@/service/share'
|
import { fetchAccessToken } from '@/service/share'
|
||||||
@ -21,7 +20,7 @@ type MailAndPasswordAuthProps = {
|
|||||||
|
|
||||||
export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAuthProps) {
|
export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAuthProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
import type { Mock } from 'vitest'
|
||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config'
|
||||||
import { render, screen } from '@testing-library/react'
|
import { render, screen } from '@testing-library/react'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import I18nContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import CSVDownload from './csv-downloader'
|
import CSVDownload from './csv-downloader'
|
||||||
|
|
||||||
@ -17,17 +18,13 @@ vi.mock('react-papaparse', () => ({
|
|||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/i18n', () => ({
|
||||||
|
useLocale: vi.fn(() => 'en-US'),
|
||||||
|
}))
|
||||||
|
|
||||||
const renderWithLocale = (locale: Locale) => {
|
const renderWithLocale = (locale: Locale) => {
|
||||||
return render(
|
;(useLocale as Mock).mockReturnValue(locale)
|
||||||
<I18nContext.Provider value={{
|
return render(<CSVDownload />)
|
||||||
locale,
|
|
||||||
i18n: {},
|
|
||||||
setLocaleOnClient: vi.fn().mockResolvedValue(undefined),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CSVDownload />
|
|
||||||
</I18nContext.Provider>,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('CSVDownload', () => {
|
describe('CSVDownload', () => {
|
||||||
|
|||||||
@ -5,9 +5,9 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import {
|
import {
|
||||||
useCSVDownloader,
|
useCSVDownloader,
|
||||||
} from 'react-papaparse'
|
} from 'react-papaparse'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { Download02 as DownloadIcon } from '@/app/components/base/icons/src/vender/solid/general'
|
import { Download02 as DownloadIcon } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
|
|
||||||
const CSV_TEMPLATE_QA_EN = [
|
const CSV_TEMPLATE_QA_EN = [
|
||||||
@ -24,7 +24,7 @@ const CSV_TEMPLATE_QA_CN = [
|
|||||||
const CSVDownload: FC = () => {
|
const CSVDownload: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { CSVDownloader, Type } = useCSVDownloader()
|
const { CSVDownloader, Type } = useCSVDownloader()
|
||||||
|
|
||||||
const getTemplate = () => {
|
const getTemplate = () => {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import type { ComponentProps } from 'react'
|
import type { ComponentProps } from 'react'
|
||||||
|
import type { Mock } from 'vitest'
|
||||||
import type { AnnotationItemBasic } from '../type'
|
import type { AnnotationItemBasic } from '../type'
|
||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config'
|
||||||
import { render, screen, waitFor } from '@testing-library/react'
|
import { render, screen, waitFor } from '@testing-library/react'
|
||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { clearAllAnnotations, fetchExportAnnotationList } from '@/service/annotation'
|
import { clearAllAnnotations, fetchExportAnnotationList } from '@/service/annotation'
|
||||||
import HeaderOptions from './index'
|
import HeaderOptions from './index'
|
||||||
@ -163,12 +164,18 @@ vi.mock('@/app/components/billing/annotation-full', () => ({
|
|||||||
default: () => <div data-testid="annotation-full" />,
|
default: () => <div data-testid="annotation-full" />,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/i18n', () => ({
|
||||||
|
useLocale: vi.fn(() => LanguagesSupported[0]),
|
||||||
|
}))
|
||||||
|
|
||||||
type HeaderOptionsProps = ComponentProps<typeof HeaderOptions>
|
type HeaderOptionsProps = ComponentProps<typeof HeaderOptions>
|
||||||
|
|
||||||
const renderComponent = (
|
const renderComponent = (
|
||||||
props: Partial<HeaderOptionsProps> = {},
|
props: Partial<HeaderOptionsProps> = {},
|
||||||
locale: Locale = LanguagesSupported[0],
|
locale: Locale = LanguagesSupported[0],
|
||||||
) => {
|
) => {
|
||||||
|
;(useLocale as Mock).mockReturnValue(locale)
|
||||||
|
|
||||||
const defaultProps: HeaderOptionsProps = {
|
const defaultProps: HeaderOptionsProps = {
|
||||||
appId: 'test-app-id',
|
appId: 'test-app-id',
|
||||||
onAdd: vi.fn(),
|
onAdd: vi.fn(),
|
||||||
@ -177,17 +184,7 @@ const renderComponent = (
|
|||||||
...props,
|
...props,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(
|
return render(<HeaderOptions {...defaultProps} />)
|
||||||
<I18NContext.Provider
|
|
||||||
value={{
|
|
||||||
locale,
|
|
||||||
i18n: {},
|
|
||||||
setLocaleOnClient: vi.fn(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HeaderOptions {...defaultProps} />
|
|
||||||
</I18NContext.Provider>,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const openOperationsPopover = async (user: ReturnType<typeof userEvent.setup>) => {
|
const openOperationsPopover = async (user: ReturnType<typeof userEvent.setup>) => {
|
||||||
@ -440,20 +437,12 @@ describe('HeaderOptions', () => {
|
|||||||
await waitFor(() => expect(mockedFetchAnnotations).toHaveBeenCalledTimes(1))
|
await waitFor(() => expect(mockedFetchAnnotations).toHaveBeenCalledTimes(1))
|
||||||
|
|
||||||
view.rerender(
|
view.rerender(
|
||||||
<I18NContext.Provider
|
<HeaderOptions
|
||||||
value={{
|
appId="test-app-id"
|
||||||
locale: LanguagesSupported[0],
|
onAdd={vi.fn()}
|
||||||
i18n: {},
|
onAdded={vi.fn()}
|
||||||
setLocaleOnClient: vi.fn(),
|
controlUpdateList={1}
|
||||||
}}
|
/>,
|
||||||
>
|
|
||||||
<HeaderOptions
|
|
||||||
appId="test-app-id"
|
|
||||||
onAdd={vi.fn()}
|
|
||||||
onAdded={vi.fn()}
|
|
||||||
controlUpdateList={1}
|
|
||||||
/>
|
|
||||||
</I18NContext.Provider>,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await waitFor(() => expect(mockedFetchAnnotations).toHaveBeenCalledTimes(2))
|
await waitFor(() => expect(mockedFetchAnnotations).toHaveBeenCalledTimes(2))
|
||||||
|
|||||||
@ -13,15 +13,14 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import {
|
import {
|
||||||
useCSVDownloader,
|
useCSVDownloader,
|
||||||
} from 'react-papaparse'
|
} from 'react-papaparse'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||||
import { FileDownload02, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
|
import { FileDownload02, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
|
||||||
import CustomPopover from '@/app/components/base/popover'
|
import CustomPopover from '@/app/components/base/popover'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { clearAllAnnotations, fetchExportAnnotationList } from '@/service/annotation'
|
import { clearAllAnnotations, fetchExportAnnotationList } from '@/service/annotation'
|
||||||
import { cn } from '@/utils/classnames'
|
|
||||||
|
|
||||||
|
import { cn } from '@/utils/classnames'
|
||||||
import Button from '../../../base/button'
|
import Button from '../../../base/button'
|
||||||
import AddAnnotationModal from '../add-annotation-modal'
|
import AddAnnotationModal from '../add-annotation-modal'
|
||||||
import BatchAddModal from '../batch-add-annotation-modal'
|
import BatchAddModal from '../batch-add-annotation-modal'
|
||||||
@ -44,7 +43,7 @@ const HeaderOptions: FC<Props> = ({
|
|||||||
controlUpdateList,
|
controlUpdateList,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { CSVDownloader, Type } = useCSVDownloader()
|
const { CSVDownloader, Type } = useCSVDownloader()
|
||||||
const [list, setList] = useState<AnnotationItemBasic[]>([])
|
const [list, setList] = useState<AnnotationItemBasic[]>([])
|
||||||
const annotationUnavailable = list.length === 0
|
const annotationUnavailable = list.length === 0
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { render, screen, waitFor } from '@testing-library/react'
|
|||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { CollectionType } from '@/app/components/tools/types'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
import SettingBuiltInTool from './setting-built-in-tool'
|
import SettingBuiltInTool from './setting-built-in-tool'
|
||||||
|
|
||||||
const fetchModelToolList = vi.fn()
|
const fetchModelToolList = vi.fn()
|
||||||
@ -56,6 +55,10 @@ vi.mock('@/app/components/plugins/readme-panel/entrance', () => ({
|
|||||||
ReadmeEntrance: ({ className }: { className?: string }) => <div className={className}>readme</div>,
|
ReadmeEntrance: ({ className }: { className?: string }) => <div className={className}>readme</div>,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/i18n', () => ({
|
||||||
|
useLocale: vi.fn(() => 'en-US'),
|
||||||
|
}))
|
||||||
|
|
||||||
const createParameter = (overrides?: Partial<ToolParameter>): ToolParameter => ({
|
const createParameter = (overrides?: Partial<ToolParameter>): ToolParameter => ({
|
||||||
name: 'settingParam',
|
name: 'settingParam',
|
||||||
label: {
|
label: {
|
||||||
@ -129,18 +132,16 @@ const renderComponent = (props?: Partial<React.ComponentProps<typeof SettingBuil
|
|||||||
const onSave = vi.fn()
|
const onSave = vi.fn()
|
||||||
const onAuthorizationItemClick = vi.fn()
|
const onAuthorizationItemClick = vi.fn()
|
||||||
const utils = render(
|
const utils = render(
|
||||||
<I18n.Provider value={{ locale: 'en-US', i18n: {}, setLocaleOnClient: vi.fn() as any }}>
|
<SettingBuiltInTool
|
||||||
<SettingBuiltInTool
|
collection={baseCollection as any}
|
||||||
collection={baseCollection as any}
|
toolName="search"
|
||||||
toolName="search"
|
isModel
|
||||||
isModel
|
setting={{ settingParam: 'value' }}
|
||||||
setting={{ settingParam: 'value' }}
|
onHide={onHide}
|
||||||
onHide={onHide}
|
onSave={onSave}
|
||||||
onSave={onSave}
|
onAuthorizationItemClick={onAuthorizationItemClick}
|
||||||
onAuthorizationItemClick={onAuthorizationItemClick}
|
{...props}
|
||||||
{...props}
|
/>,
|
||||||
/>
|
|
||||||
</I18n.Provider>,
|
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
...utils,
|
...utils,
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import {
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import ActionButton from '@/app/components/base/action-button'
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Drawer from '@/app/components/base/drawer'
|
import Drawer from '@/app/components/base/drawer'
|
||||||
@ -26,7 +25,7 @@ import {
|
|||||||
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
|
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
|
||||||
import { CollectionType } from '@/app/components/tools/types'
|
import { CollectionType } from '@/app/components/tools/types'
|
||||||
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
|
import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
@ -58,7 +57,7 @@ const SettingBuiltInTool: FC<Props> = ({
|
|||||||
credentialId,
|
credentialId,
|
||||||
onAuthorizationItemClick,
|
onAuthorizationItemClick,
|
||||||
}) => {
|
}) => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const passedTools = (collection as ToolWithProvider).tools
|
const passedTools = (collection as ToolWithProvider).tools
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import type {
|
|||||||
import { noop } from 'es-toolkit/compat'
|
import { noop } from 'es-toolkit/compat'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||||
@ -16,7 +15,7 @@ import Modal from '@/app/components/base/modal'
|
|||||||
import { SimpleSelect } from '@/app/components/base/select'
|
import { SimpleSelect } from '@/app/components/base/select'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
||||||
import I18n, { useDocLink } from '@/context/i18n'
|
import { useDocLink, useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { useCodeBasedExtensions } from '@/service/use-common'
|
import { useCodeBasedExtensions } from '@/service/use-common'
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ const ExternalDataToolModal: FC<ExternalDataToolModalProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const docLink = useDocLink()
|
const docLink = useDocLink()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const [localeData, setLocaleData] = useState(data.type ? data : { ...data, type: 'api' })
|
const [localeData, setLocaleData] = useState(data.type ? data : { ...data, type: 'api' })
|
||||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||||
const { data: codeBasedExtensionList } = useCodeBasedExtensions('external_data_tool')
|
const { data: codeBasedExtensionList } = useCodeBasedExtensions('external_data_tool')
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import {
|
|||||||
RiErrorWarningLine,
|
RiErrorWarningLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||||
import BlockIcon from '@/app/components/workflow/block-icon'
|
import BlockIcon from '@/app/components/workflow/block-icon'
|
||||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -26,7 +26,7 @@ type Props = {
|
|||||||
|
|
||||||
const ToolCallItem: FC<Props> = ({ toolCall, isLLM = false, isFinal, tokens, observation, finalAnswer }) => {
|
const ToolCallItem: FC<Props> = ({ toolCall, isLLM = false, isFinal, tokens, observation, finalAnswer }) => {
|
||||||
const [collapseState, setCollapseState] = useState<boolean>(true)
|
const [collapseState, setCollapseState] = useState<boolean>(true)
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const toolName = isLLM ? 'LLM' : (toolCall.tool_label[locale] || toolCall.tool_label[locale.replaceAll('-', '_')])
|
const toolName = isLLM ? 'LLM' : (toolCall.tool_label[locale] || toolCall.tool_label[locale.replaceAll('-', '_')])
|
||||||
|
|
||||||
const getTime = (time: number) => {
|
const getTime = (time: number) => {
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import type { CodeBasedExtensionForm } from '@/models/common'
|
import type { CodeBasedExtensionForm } from '@/models/common'
|
||||||
import type { ModerationConfig } from '@/models/debug'
|
import type { ModerationConfig } from '@/models/debug'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { PortalSelect } from '@/app/components/base/select'
|
import { PortalSelect } from '@/app/components/base/select'
|
||||||
import Textarea from '@/app/components/base/textarea'
|
import Textarea from '@/app/components/base/textarea'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
|
|
||||||
type FormGenerationProps = {
|
type FormGenerationProps = {
|
||||||
forms: CodeBasedExtensionForm[]
|
forms: CodeBasedExtensionForm[]
|
||||||
@ -16,7 +15,7 @@ const FormGeneration: FC<FormGenerationProps> = ({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
|
|
||||||
const handleFormChange = (type: string, v: string) => {
|
const handleFormChange = (type: string, v: string) => {
|
||||||
onChange({ ...value, [type]: v })
|
onChange({ ...value, [type]: v })
|
||||||
|
|||||||
@ -1,16 +1,14 @@
|
|||||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||||
import { RiEqualizer2Line } from '@remixicon/react'
|
import { RiEqualizer2Line } from '@remixicon/react'
|
||||||
import { produce } from 'immer'
|
import { produce } from 'immer'
|
||||||
import * as React from 'react'
|
|
||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||||
import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card'
|
import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card'
|
||||||
import { FeatureEnum } from '@/app/components/base/features/types'
|
import { FeatureEnum } from '@/app/components/base/features/types'
|
||||||
import { ContentModeration } from '@/app/components/base/icons/src/vender/features'
|
import { ContentModeration } from '@/app/components/base/icons/src/vender/features'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
import { useCodeBasedExtensions } from '@/service/use-common'
|
import { useCodeBasedExtensions } from '@/service/use-common'
|
||||||
|
|
||||||
@ -25,7 +23,7 @@ const Moderation = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { setShowModerationSettingModal } = useModalContext()
|
const { setShowModerationSettingModal } = useModalContext()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const featuresStore = useFeaturesStore()
|
const featuresStore = useFeaturesStore()
|
||||||
const moderation = useFeatures(s => s.features.moderation)
|
const moderation = useFeatures(s => s.features.moderation)
|
||||||
const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
|
const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { RiCloseLine } from '@remixicon/react'
|
|||||||
import { noop } from 'es-toolkit/compat'
|
import { noop } from 'es-toolkit/compat'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
||||||
@ -15,7 +14,7 @@ import { useToastContext } from '@/app/components/base/toast'
|
|||||||
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
||||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||||
import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||||
import I18n, { useDocLink } from '@/context/i18n'
|
import { useDocLink, useLocale } from '@/context/i18n'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { useCodeBasedExtensions, useModelProviders } from '@/service/use-common'
|
import { useCodeBasedExtensions, useModelProviders } from '@/service/use-common'
|
||||||
@ -45,7 +44,7 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const docLink = useDocLink()
|
const docLink = useDocLink()
|
||||||
const { notify } = useToastContext()
|
const { notify } = useToastContext()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { data: modelProviders, isPending: isLoading, refetch: refetchModelProviders } = useModelProviders()
|
const { data: modelProviders, isPending: isLoading, refetch: refetchModelProviders } = useModelProviders()
|
||||||
const [localeData, setLocaleData] = useState<ModerationConfig>(data)
|
const [localeData, setLocaleData] = useState<ModerationConfig>(data)
|
||||||
const { setShowAccountSettingModal } = useModalContext()
|
const { setShowAccountSettingModal } = useModalContext()
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { usePipelineTemplateList } from '@/service/use-pipeline'
|
import { usePipelineTemplateList } from '@/service/use-pipeline'
|
||||||
import CreateCard from './create-card'
|
import CreateCard from './create-card'
|
||||||
import TemplateCard from './template-card'
|
import TemplateCard from './template-card'
|
||||||
|
|
||||||
const BuiltInPipelineList = () => {
|
const BuiltInPipelineList = () => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
const language = useMemo(() => {
|
const language = useMemo(() => {
|
||||||
if (['zh-Hans', 'ja-JP'].includes(locale))
|
if (['zh-Hans', 'ja-JP'].includes(locale))
|
||||||
return locale
|
return locale
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import SimplePieChart from '@/app/components/base/simple-pie-chart'
|
|||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
|
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { upload } from '@/service/base'
|
import { upload } from '@/service/base'
|
||||||
@ -40,7 +40,7 @@ const FileUploader = ({
|
|||||||
}: IFileUploaderProps) => {
|
}: IFileUploaderProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const [dragging, setDragging] = useState(false)
|
const [dragging, setDragging] = useState(false)
|
||||||
const dropRef = useRef<HTMLDivElement>(null)
|
const dropRef = useRef<HTMLDivElement>(null)
|
||||||
const dragRef = useRef<HTMLDivElement>(null)
|
const dragRef = useRef<HTMLDivElement>(null)
|
||||||
|
|||||||
@ -12,10 +12,8 @@ import {
|
|||||||
import { noop } from 'es-toolkit/compat'
|
import { noop } from 'es-toolkit/compat'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import * as React from 'react'
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { trackEvent } from '@/app/components/base/amplitude'
|
import { trackEvent } from '@/app/components/base/amplitude'
|
||||||
import Badge from '@/app/components/base/badge'
|
import Badge from '@/app/components/base/badge'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
@ -38,7 +36,7 @@ import { useDefaultModel, useModelList, useModelListAndDefaultModelAndCurrentPro
|
|||||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||||
import { FULL_DOC_PREVIEW_LENGTH, IS_CE_EDITION } from '@/config'
|
import { FULL_DOC_PREVIEW_LENGTH, IS_CE_EDITION } from '@/config'
|
||||||
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
|
||||||
import I18n, { useDocLink } from '@/context/i18n'
|
import { useDocLink, useLocale } from '@/context/i18n'
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { DataSourceProvider } from '@/models/common'
|
import { DataSourceProvider } from '@/models/common'
|
||||||
@ -151,7 +149,7 @@ const StepTwo = ({
|
|||||||
}: StepTwoProps) => {
|
}: StepTwoProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const docLink = useDocLink()
|
const docLink = useDocLink()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { getFileUploadErrorMessage } from '@/app/components/base/file-uploader/u
|
|||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import DocumentFileIcon from '@/app/components/datasets/common/document-file-icon'
|
import DocumentFileIcon from '@/app/components/datasets/common/document-file-icon'
|
||||||
import { IS_CE_EDITION } from '@/config'
|
import { IS_CE_EDITION } from '@/config'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { upload } from '@/service/base'
|
import { upload } from '@/service/base'
|
||||||
@ -33,7 +33,7 @@ const LocalFile = ({
|
|||||||
}: LocalFileProps) => {
|
}: LocalFileProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const localFileList = useDataSourceStoreWithSelector(state => state.localFileList)
|
const localFileList = useDataSourceStoreWithSelector(state => state.localFileList)
|
||||||
const dataSourceStore = useDataSourceStore()
|
const dataSourceStore = useDataSourceStore()
|
||||||
const [dragging, setDragging] = useState(false)
|
const [dragging, setDragging] = useState(false)
|
||||||
|
|||||||
@ -5,9 +5,8 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import {
|
import {
|
||||||
useCSVDownloader,
|
useCSVDownloader,
|
||||||
} from 'react-papaparse'
|
} from 'react-papaparse'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { Download02 as DownloadIcon } from '@/app/components/base/icons/src/vender/solid/general'
|
import { Download02 as DownloadIcon } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { ChunkingMode } from '@/models/datasets'
|
import { ChunkingMode } from '@/models/datasets'
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ const CSV_TEMPLATE_CN = [
|
|||||||
|
|
||||||
const CSVDownload: FC<{ docForm: ChunkingMode }> = ({ docForm }) => {
|
const CSVDownload: FC<{ docForm: ChunkingMode }> = ({ docForm }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { CSVDownloader, Type } = useCSVDownloader()
|
const { CSVDownloader, Type } = useCSVDownloader()
|
||||||
|
|
||||||
const getTemplate = () => {
|
const getTemplate = () => {
|
||||||
|
|||||||
@ -2,8 +2,7 @@
|
|||||||
import { RiCloseLine, RiListUnordered } from '@remixicon/react'
|
import { RiCloseLine, RiListUnordered } from '@remixicon/react'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useLocale } from '@/context/i18n'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { AppModeEnum, Theme } from '@/types/app'
|
import { AppModeEnum, Theme } from '@/types/app'
|
||||||
@ -26,7 +25,7 @@ type IDocProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Doc = ({ appDetail }: IDocProps) => {
|
const Doc = ({ appDetail }: IDocProps) => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [toc, setToc] = useState<Array<{ href: string, text: string }>>([])
|
const [toc, setToc] = useState<Array<{ href: string, text: string }>>([])
|
||||||
const [isTocExpanded, setIsTocExpanded] = useState(false)
|
const [isTocExpanded, setIsTocExpanded] = useState(false)
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import { useContext } from 'use-context-selector'
|
|||||||
import { SimpleSelect } from '@/app/components/base/select'
|
import { SimpleSelect } from '@/app/components/base/select'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
|
import { setLocaleOnClient } from '@/i18n-config'
|
||||||
import { languages } from '@/i18n-config/language'
|
import { languages } from '@/i18n-config/language'
|
||||||
import { updateUserProfile } from '@/service/common'
|
import { updateUserProfile } from '@/service/common'
|
||||||
import { timezones } from '@/utils/timezone'
|
import { timezones } from '@/utils/timezone'
|
||||||
@ -18,7 +20,7 @@ const titleClassName = `
|
|||||||
`
|
`
|
||||||
|
|
||||||
export default function LanguagePage() {
|
export default function LanguagePage() {
|
||||||
const { locale, setLocaleOnClient } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { userProfile, mutateUserProfile } = useAppContext()
|
const { userProfile, mutateUserProfile } = useAppContext()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import type { InvitationResult } from '@/models/common'
|
|||||||
import { RiPencilLine, RiUserAddLine } from '@remixicon/react'
|
import { RiPencilLine, RiUserAddLine } from '@remixicon/react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Avatar from '@/app/components/base/avatar'
|
import Avatar from '@/app/components/base/avatar'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
@ -12,7 +11,7 @@ import { Plan } from '@/app/components/billing/type'
|
|||||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
@ -34,7 +33,7 @@ const MembersPage = () => {
|
|||||||
dataset_operator: t('members.datasetOperator', { ns: 'common' }),
|
dataset_operator: t('members.datasetOperator', { ns: 'common' }),
|
||||||
normal: t('members.normal', { ns: 'common' }),
|
normal: t('members.normal', { ns: 'common' }),
|
||||||
}
|
}
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
|
|
||||||
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
||||||
const { data, refetch } = useMembers()
|
const { data, refetch } = useMembers()
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import Button from '@/app/components/base/button'
|
|||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useProviderContextSelector } from '@/context/provider-context'
|
import { useProviderContextSelector } from '@/context/provider-context'
|
||||||
import { inviteMember } from '@/service/common'
|
import { inviteMember } from '@/service/common'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
@ -47,7 +47,7 @@ const InviteModal = ({
|
|||||||
setIsLimitExceeded(limited && (used > licenseLimit.workspace_members.limit))
|
setIsLimitExceeded(limited && (used > licenseLimit.workspace_members.limit))
|
||||||
}, [licenseLimit, emails])
|
}, [licenseLimit, emails])
|
||||||
|
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const [role, setRole] = useState<RoleKey>('normal')
|
const [role, setRole] = useState<RoleKey>('normal')
|
||||||
|
|
||||||
const [isSubmitting, {
|
const [isSubmitting, {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { Mock } from 'vitest'
|
import type { Mock } from 'vitest'
|
||||||
import { renderHook } from '@testing-library/react'
|
import { renderHook } from '@testing-library/react'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useLanguage } from './hooks'
|
import { useLanguage } from './hooks'
|
||||||
|
|
||||||
vi.mock('@tanstack/react-query', () => ({
|
vi.mock('@tanstack/react-query', () => ({
|
||||||
@ -36,8 +36,7 @@ vi.mock('@/service/use-common', () => ({
|
|||||||
|
|
||||||
// mock context hooks
|
// mock context hooks
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
__esModule: true,
|
useLocale: vi.fn(() => 'en-US'),
|
||||||
default: vi.fn(),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/provider-context', () => ({
|
vi.mock('@/context/provider-context', () => ({
|
||||||
@ -72,27 +71,20 @@ afterAll(() => {
|
|||||||
|
|
||||||
describe('useLanguage', () => {
|
describe('useLanguage', () => {
|
||||||
it('should replace hyphen with underscore in locale', () => {
|
it('should replace hyphen with underscore in locale', () => {
|
||||||
(useContext as Mock).mockReturnValue({
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
locale: 'en-US',
|
|
||||||
})
|
|
||||||
const { result } = renderHook(() => useLanguage())
|
const { result } = renderHook(() => useLanguage())
|
||||||
expect(result.current).toBe('en_US')
|
expect(result.current).toBe('en_US')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return locale as is if no hyphen exists', () => {
|
it('should return locale as is if no hyphen exists', () => {
|
||||||
(useContext as Mock).mockReturnValue({
|
;(useLocale as Mock).mockReturnValue('enUS')
|
||||||
locale: 'enUS',
|
|
||||||
})
|
|
||||||
|
|
||||||
const { result } = renderHook(() => useLanguage())
|
const { result } = renderHook(() => useLanguage())
|
||||||
expect(result.current).toBe('enUS')
|
expect(result.current).toBe('enUS')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle multiple hyphens', () => {
|
it('should handle multiple hyphens', () => {
|
||||||
// Mock the I18n context return value
|
;(useLocale as Mock).mockReturnValue('zh-Hans-CN')
|
||||||
(useContext as Mock).mockReturnValue({
|
|
||||||
locale: 'zh-Hans-CN',
|
|
||||||
})
|
|
||||||
|
|
||||||
const { result } = renderHook(() => useLanguage())
|
const { result } = renderHook(() => useLanguage())
|
||||||
expect(result.current).toBe('zh_Hans-CN')
|
expect(result.current).toBe('zh_Hans-CN')
|
||||||
|
|||||||
@ -16,14 +16,13 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import {
|
import {
|
||||||
useMarketplacePlugins,
|
useMarketplacePlugins,
|
||||||
useMarketplacePluginsByCollectionId,
|
useMarketplacePluginsByCollectionId,
|
||||||
} from '@/app/components/plugins/marketplace/hooks'
|
} from '@/app/components/plugins/marketplace/hooks'
|
||||||
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
||||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useModalContextSelector } from '@/context/modal-context'
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import {
|
import {
|
||||||
@ -70,7 +69,7 @@ export const useSystemDefaultModelAndModelList: UseDefaultModelAndModelList = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useLanguage = () => {
|
export const useLanguage = () => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
return locale.replace('-', '_')
|
return locale.replace('-', '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config'
|
||||||
import { usePrefetchQuery } from '@tanstack/react-query'
|
import { usePrefetchQuery } from '@tanstack/react-query'
|
||||||
|
import { useHydrateAtoms } from 'jotai/utils'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import I18NContext from '@/context/i18n'
|
import { localeAtom } from '@/context/i18n'
|
||||||
import { setLocaleOnClient } from '@/i18n-config'
|
import { setLocaleOnClient } from '@/i18n-config'
|
||||||
import { getSystemFeatures } from '@/service/common'
|
import { getSystemFeatures } from '@/service/common'
|
||||||
import Loading from './base/loading'
|
import Loading from './base/loading'
|
||||||
@ -18,6 +19,7 @@ const I18n: FC<II18nProps> = ({
|
|||||||
locale,
|
locale,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
useHydrateAtoms([[localeAtom, locale]])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
usePrefetchQuery({
|
usePrefetchQuery({
|
||||||
@ -35,14 +37,9 @@ const I18n: FC<II18nProps> = ({
|
|||||||
return <div className="flex h-screen w-screen items-center justify-center"><Loading type="app" /></div>
|
return <div className="flex h-screen w-screen items-center justify-center"><Loading type="app" /></div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18NContext.Provider value={{
|
<>
|
||||||
locale,
|
|
||||||
i18n: {},
|
|
||||||
setLocaleOnClient,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</I18NContext.Provider>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default React.memo(I18n)
|
export default React.memo(I18n)
|
||||||
|
|||||||
@ -46,7 +46,6 @@ vi.mock('../marketplace/hooks', () => ({
|
|||||||
// Mock useGetLanguage context
|
// Mock useGetLanguage context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useGetLanguage: () => 'en-US',
|
useGetLanguage: () => 'en-US',
|
||||||
useI18N: () => ({ locale: 'en-US' }),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock useTheme hook
|
// Mock useTheme hook
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
/* eslint-disable dify-i18n/require-ns-option */
|
/* eslint-disable dify-i18n/require-ns-option */
|
||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config'
|
||||||
import {
|
import { getLocaleOnServer, getTranslation } from '@/i18n-config/server'
|
||||||
getLocaleOnServer,
|
|
||||||
getTranslation as translate,
|
|
||||||
} from '@/i18n-config/server'
|
|
||||||
|
|
||||||
type DescriptionProps = {
|
type DescriptionProps = {
|
||||||
locale?: Locale
|
locale?: Locale
|
||||||
@ -12,8 +9,8 @@ const Description = async ({
|
|||||||
locale: localeFromProps,
|
locale: localeFromProps,
|
||||||
}: DescriptionProps) => {
|
}: DescriptionProps) => {
|
||||||
const localeDefault = await getLocaleOnServer()
|
const localeDefault = await getLocaleOnServer()
|
||||||
const { t } = await translate(localeFromProps || localeDefault, 'plugin')
|
const { t } = await getTranslation(localeFromProps || localeDefault, 'plugin')
|
||||||
const { t: tCommon } = await translate(localeFromProps || localeDefault, 'common')
|
const { t: tCommon } = await getTranslation(localeFromProps || localeDefault, 'common')
|
||||||
const isZhHans = localeFromProps === 'zh-Hans'
|
const isZhHans = localeFromProps === 'zh-Hans'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -191,11 +191,9 @@ vi.mock('next-themes', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock useI18N context
|
// Mock useLocale context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useI18N: () => ({
|
useLocale: () => 'en-US',
|
||||||
locale: 'en-US',
|
|
||||||
}),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock i18n-config/language
|
// Mock i18n-config/language
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
|
|||||||
import { useTags } from '@/app/components/plugins/hooks'
|
import { useTags } from '@/app/components/plugins/hooks'
|
||||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||||
import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
|
import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getPluginDetailLinkInMarketplace, getPluginLinkInMarketplace } from '../utils'
|
import { getPluginDetailLinkInMarketplace, getPluginLinkInMarketplace } from '../utils'
|
||||||
|
|
||||||
type CardWrapperProps = {
|
type CardWrapperProps = {
|
||||||
@ -31,7 +31,7 @@ const CardWrapperComponent = ({
|
|||||||
setTrue: showInstallFromMarketplace,
|
setTrue: showInstallFromMarketplace,
|
||||||
setFalse: hideInstallFromMarketplace,
|
setFalse: hideInstallFromMarketplace,
|
||||||
}] = useBoolean(false)
|
}] = useBoolean(false)
|
||||||
const { locale: localeFromLocale } = useI18N()
|
const localeFromLocale = useLocale()
|
||||||
const { getTagLabel } = useTags(t)
|
const { getTagLabel } = useTags(t)
|
||||||
|
|
||||||
// Memoize marketplace link params to prevent unnecessary re-renders
|
// Memoize marketplace link params to prevent unnecessary re-renders
|
||||||
|
|||||||
@ -49,11 +49,9 @@ vi.mock('../context', () => ({
|
|||||||
useMarketplaceContext: (selector: (v: typeof mockContextValues) => unknown) => selector(mockContextValues),
|
useMarketplaceContext: (selector: (v: typeof mockContextValues) => unknown) => selector(mockContextValues),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock useI18N context
|
// Mock useLocale context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useI18N: () => ({
|
useLocale: () => 'en-US',
|
||||||
locale: 'en-US',
|
|
||||||
}),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock next-themes
|
// Mock next-themes
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-v
|
|||||||
import { API_PREFIX } from '@/config'
|
import { API_PREFIX } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import { useGetLanguage, useI18N } from '@/context/i18n'
|
import { useGetLanguage, useLocale } from '@/context/i18n'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import useTheme from '@/hooks/use-theme'
|
import useTheme from '@/hooks/use-theme'
|
||||||
@ -67,7 +67,7 @@ const DetailHeader = ({
|
|||||||
|
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const locale = useGetLanguage()
|
const locale = useGetLanguage()
|
||||||
const { locale: currentLocale } = useI18N()
|
const currentLocale = useLocale()
|
||||||
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
||||||
const { setShowUpdatePluginModal } = useModalContext()
|
const { setShowUpdatePluginModal } = useModalContext()
|
||||||
const { refreshModelProviders } = useProviderContext()
|
const { refreshModelProviders } = useProviderContext()
|
||||||
|
|||||||
@ -29,7 +29,6 @@ vi.mock('../marketplace/hooks', () => ({
|
|||||||
// Mock useGetLanguage context
|
// Mock useGetLanguage context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useGetLanguage: () => 'en-US',
|
useGetLanguage: () => 'en-US',
|
||||||
useI18N: () => ({ locale: 'en-US' }),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock useTheme hook
|
// Mock useTheme hook
|
||||||
|
|||||||
@ -6,11 +6,10 @@ import {
|
|||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import { getDocsUrl } from '@/app/components/plugins/utils'
|
import { getDocsUrl } from '@/app/components/plugins/utils'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useDebugKey } from '@/service/use-plugins'
|
import { useDebugKey } from '@/service/use-plugins'
|
||||||
import KeyValueItem from '../base/key-value-item'
|
import KeyValueItem from '../base/key-value-item'
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ const i18nPrefix = 'debugInfo'
|
|||||||
|
|
||||||
const DebugInfo: FC = () => {
|
const DebugInfo: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { data: info, isLoading } = useDebugKey()
|
const { data: info, isLoading } = useDebugKey()
|
||||||
|
|
||||||
// info.key likes 4580bdb7-b878-471c-a8a4-bfd760263a53 mask the middle part using *.
|
// info.key likes 4580bdb7-b878-471c-a8a4-bfd760263a53 mask the middle part using *.
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import { noop } from 'es-toolkit/compat'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import TabSlider from '@/app/components/base/tab-slider'
|
import TabSlider from '@/app/components/base/tab-slider'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
@ -19,7 +18,7 @@ import ReferenceSettingModal from '@/app/components/plugins/reference-setting-mo
|
|||||||
import { getDocsUrl } from '@/app/components/plugins/utils'
|
import { getDocsUrl } from '@/app/components/plugins/utils'
|
||||||
import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
|
import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import useDocumentTitle from '@/hooks/use-document-title'
|
import useDocumentTitle from '@/hooks/use-document-title'
|
||||||
import { usePluginInstallation } from '@/hooks/use-query-params'
|
import { usePluginInstallation } from '@/hooks/use-query-params'
|
||||||
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
|
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
|
||||||
@ -48,7 +47,7 @@ const PluginPage = ({
|
|||||||
marketplace,
|
marketplace,
|
||||||
}: PluginPageProps) => {
|
}: PluginPageProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
useDocumentTitle(t('metadata.title', { ns: 'plugin' }))
|
useDocumentTitle(t('metadata.title', { ns: 'plugin' }))
|
||||||
|
|
||||||
// Use nuqs hook for installation state
|
// Use nuqs hook for installation state
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||||
import { getPluginLinkInMarketplace } from '@/app/components/plugins/marketplace/utils'
|
import { getPluginLinkInMarketplace } from '@/app/components/plugins/marketplace/utils'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
import Badge from '../base/badge'
|
import Badge from '../base/badge'
|
||||||
@ -36,7 +36,7 @@ const ProviderCardComponent: FC<Props> = ({
|
|||||||
setFalse: hideInstallFromMarketplace,
|
setFalse: hideInstallFromMarketplace,
|
||||||
}] = useBoolean(false)
|
}] = useBoolean(false)
|
||||||
const { org, label } = payload
|
const { org, label } = payload
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
|
|
||||||
// Memoize the marketplace link params to prevent unnecessary re-renders
|
// Memoize the marketplace link params to prevent unnecessary re-renders
|
||||||
const marketplaceLinkParams = useMemo(() => ({ language: locale, theme }), [locale, theme])
|
const marketplaceLinkParams = useMemo(() => ({ language: locale, theme }), [locale, theme])
|
||||||
|
|||||||
@ -51,7 +51,6 @@ vi.mock('react-i18next', async (importOriginal) => {
|
|||||||
// Mock useGetLanguage context
|
// Mock useGetLanguage context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useGetLanguage: () => 'en-US',
|
useGetLanguage: () => 'en-US',
|
||||||
useI18N: () => ({ locale: 'en-US' }),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Mock app context for useGetIcon
|
// Mock app context for useGetIcon
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
import type { CustomCollectionBackend, CustomParamSchema } from '@/app/components/tools/types'
|
import type { CustomCollectionBackend, CustomParamSchema } from '@/app/components/tools/types'
|
||||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||||
import { AuthType } from '@/app/components/tools/types'
|
import { AuthType } from '@/app/components/tools/types'
|
||||||
import I18n from '@/context/i18n'
|
|
||||||
import { testAPIAvailable } from '@/service/tools'
|
import { testAPIAvailable } from '@/service/tools'
|
||||||
import TestApi from './test-api'
|
import TestApi from './test-api'
|
||||||
|
|
||||||
vi.mock('@/service/tools', () => ({
|
vi.mock('@/service/tools', () => ({
|
||||||
testAPIAvailable: vi.fn(),
|
testAPIAvailable: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/context/i18n', () => ({
|
||||||
|
useLocale: vi.fn(() => 'en-US'),
|
||||||
|
}))
|
||||||
|
|
||||||
const testAPIAvailableMock = vi.mocked(testAPIAvailable)
|
const testAPIAvailableMock = vi.mocked(testAPIAvailable)
|
||||||
|
|
||||||
describe('TestApi', () => {
|
describe('TestApi', () => {
|
||||||
@ -40,19 +44,12 @@ describe('TestApi', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const renderTestApi = () => {
|
const renderTestApi = () => {
|
||||||
const providerValue = {
|
|
||||||
locale: 'en-US',
|
|
||||||
i18n: {},
|
|
||||||
setLocaleOnClient: vi.fn(),
|
|
||||||
}
|
|
||||||
return render(
|
return render(
|
||||||
<I18n.Provider value={providerValue as any}>
|
<TestApi
|
||||||
<TestApi
|
customCollection={customCollection}
|
||||||
customCollection={customCollection}
|
tool={tool}
|
||||||
tool={tool}
|
onHide={vi.fn()}
|
||||||
onHide={vi.fn()}
|
/>,
|
||||||
/>
|
|
||||||
</I18n.Provider>,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,11 @@ import { RiSettings2Line } from '@remixicon/react'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Drawer from '@/app/components/base/drawer-plus'
|
import Drawer from '@/app/components/base/drawer-plus'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import { AuthType } from '@/app/components/tools/types'
|
import { AuthType } from '@/app/components/tools/types'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { testAPIAvailable } from '@/service/tools'
|
import { testAPIAvailable } from '@/service/tools'
|
||||||
import ConfigCredentials from './config-credentials'
|
import ConfigCredentials from './config-credentials'
|
||||||
@ -29,7 +28,7 @@ const TestApi: FC<Props> = ({
|
|||||||
onHide,
|
onHide,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const [credentialsModalShow, setCredentialsModalShow] = useState(false)
|
const [credentialsModalShow, setCredentialsModalShow] = useState(false)
|
||||||
const [tempCredential, setTempCredential] = React.useState<Credential>(customCollection.credentials)
|
const [tempCredential, setTempCredential] = React.useState<Credential>(customCollection.credentials)
|
||||||
|
|||||||
@ -7,9 +7,8 @@ import {
|
|||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { useCreateMCP } from '@/service/use-tools'
|
import { useCreateMCP } from '@/service/use-tools'
|
||||||
import MCPModal from './modal'
|
import MCPModal from './modal'
|
||||||
@ -20,7 +19,7 @@ type Props = {
|
|||||||
|
|
||||||
const NewMCPCard = ({ handleCreate }: Props) => {
|
const NewMCPCard = ({ handleCreate }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,8 @@
|
|||||||
import type { Tool } from '@/app/components/tools/types'
|
import type { Tool } from '@/app/components/tools/types'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ type Props = {
|
|||||||
const MCPToolItem = ({
|
const MCPToolItem = ({
|
||||||
tool,
|
tool,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,10 @@ import {
|
|||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import I18n, { useDocLink } from '@/context/i18n'
|
import { useDocLink, useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { createCustomCollection } from '@/service/tools'
|
import { createCustomCollection } from '@/service/tools'
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ type Props = {
|
|||||||
|
|
||||||
const Contribute = ({ onRefreshData }: Props) => {
|
const Contribute = ({ onRefreshData }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const { isCurrentWorkspaceManager } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import ActionButton from '@/app/components/base/action-button'
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
@ -24,7 +23,7 @@ import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-m
|
|||||||
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
|
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
|
||||||
import WorkflowToolModal from '@/app/components/tools/workflow-tool'
|
import WorkflowToolModal from '@/app/components/tools/workflow-tool'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
|
||||||
@ -60,7 +59,7 @@ const ProviderDetail = ({
|
|||||||
onRefreshData,
|
onRefreshData,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
|
|
||||||
const needAuth = collection.allow_delete || collection.type === CollectionType.model
|
const needAuth = collection.allow_delete || collection.type === CollectionType.model
|
||||||
|
|||||||
@ -2,9 +2,8 @@
|
|||||||
import type { Collection, Tool } from '../types'
|
import type { Collection, Tool } from '../types'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool'
|
import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { getLanguage } from '@/i18n-config/language'
|
import { getLanguage } from '@/i18n-config/language'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ const ToolItem = ({
|
|||||||
isBuiltIn,
|
isBuiltIn,
|
||||||
isModel,
|
isModel,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const language = getLanguage(locale)
|
const language = getLanguage(locale)
|
||||||
const [showDetail, setShowDetail] = useState(false)
|
const [showDetail, setShowDetail] = useState(false)
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
'use client'
|
|
||||||
|
|
||||||
import type { ReactNode } from 'react'
|
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import I18NContext from '@/context/i18n'
|
|
||||||
|
|
||||||
export type II18NHocProps = {
|
|
||||||
children: ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
const withI18N = (Component: any) => {
|
|
||||||
return (props: any) => {
|
|
||||||
const { i18n } = useContext(I18NContext)
|
|
||||||
return (
|
|
||||||
<Component {...props} i18n={i18n} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withI18N
|
|
||||||
@ -4,9 +4,8 @@ import type { Plugin } from '@/app/components/plugins/types.ts'
|
|||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
import { formatNumber } from '@/utils/format'
|
import { formatNumber } from '@/utils/format'
|
||||||
@ -27,7 +26,7 @@ const Item: FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [open, setOpen] = React.useState(false)
|
const [open, setOpen] = React.useState(false)
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||||
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
||||||
const [isShowInstallModal, {
|
const [isShowInstallModal, {
|
||||||
|
|||||||
@ -3,9 +3,8 @@ import type { Plugin } from '@/app/components/plugins/types'
|
|||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import BlockIcon from '../../block-icon'
|
import BlockIcon from '../../block-icon'
|
||||||
import { BlockEnum } from '../../types'
|
import { BlockEnum } from '../../types'
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ const UninstalledItem = ({
|
|||||||
payload,
|
payload,
|
||||||
}: UninstalledItemProps) => {
|
}: UninstalledItemProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
|
|
||||||
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
const getLocalizedText = (obj: Record<string, string> | undefined) =>
|
||||||
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
obj?.[locale] || obj?.['en-US'] || obj?.en_US || ''
|
||||||
|
|||||||
@ -3,10 +3,9 @@ import type { DocExtractorNodeType } from './types'
|
|||||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||||
import { BlockEnum } from '@/app/components/workflow/types'
|
import { BlockEnum } from '@/app/components/workflow/types'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { LanguagesSupported } from '@/i18n-config/language'
|
import { LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { useFileSupportTypes } from '@/service/use-common'
|
import { useFileSupportTypes } from '@/service/use-common'
|
||||||
import OutputVars, { VarItem } from '../_base/components/output-vars'
|
import OutputVars, { VarItem } from '../_base/components/output-vars'
|
||||||
@ -22,7 +21,7 @@ const Panel: FC<NodePanelProps<DocExtractorNodeType>> = ({
|
|||||||
data,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const link = useNodeHelpLink(BlockEnum.DocExtractor)
|
const link = useNodeHelpLink(BlockEnum.DocExtractor)
|
||||||
const { data: supportFileTypesResponse } = useFileSupportTypes()
|
const { data: supportFileTypesResponse } = useFileSupportTypes()
|
||||||
const supportTypes = supportFileTypesResponse?.allowed_extensions || []
|
const supportTypes = supportFileTypesResponse?.allowed_extensions || []
|
||||||
|
|||||||
@ -3,12 +3,11 @@ import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Countdown from '@/app/components/signin/countdown'
|
import Countdown from '@/app/components/signin/countdown'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { sendResetPasswordCode, verifyResetPasswordCode } from '@/service/common'
|
import { sendResetPasswordCode, verifyResetPasswordCode } from '@/service/common'
|
||||||
|
|
||||||
export default function CheckCode() {
|
export default function CheckCode() {
|
||||||
@ -19,7 +18,7 @@ export default function CheckCode() {
|
|||||||
const token = decodeURIComponent(searchParams.get('token') as string)
|
const token = decodeURIComponent(searchParams.get('token') as string)
|
||||||
const [code, setVerifyCode] = useState('')
|
const [code, setVerifyCode] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const verify = async () => {
|
const verify = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -5,12 +5,11 @@ import Link from 'next/link'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import useDocumentTitle from '@/hooks/use-document-title'
|
import useDocumentTitle from '@/hooks/use-document-title'
|
||||||
import { sendResetPasswordCode } from '@/service/common'
|
import { sendResetPasswordCode } from '@/service/common'
|
||||||
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../components/signin/countdown'
|
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../components/signin/countdown'
|
||||||
@ -22,7 +21,7 @@ export default function CheckCode() {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const handleGetEMailVerificationCode = async () => {
|
const handleGetEMailVerificationCode = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import * as React from 'react'
|
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import LocaleSigninSelect from '@/app/components/base/select/locale-signin'
|
import LocaleSigninSelect from '@/app/components/base/select/locale-signin'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
|
import { setLocaleOnClient } from '@/i18n-config'
|
||||||
import { languages } from '@/i18n-config/language'
|
import { languages } from '@/i18n-config/language'
|
||||||
|
|
||||||
// Avoid rendering the logo and theme selector on the server
|
// Avoid rendering the logo and theme selector on the server
|
||||||
@ -20,7 +19,7 @@ const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector
|
|||||||
})
|
})
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { locale, setLocaleOnClient } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -4,13 +4,13 @@ import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { trackEvent } from '@/app/components/base/amplitude'
|
import { trackEvent } from '@/app/components/base/amplitude'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Countdown from '@/app/components/signin/countdown'
|
import Countdown from '@/app/components/signin/countdown'
|
||||||
import I18NContext from '@/context/i18n'
|
|
||||||
|
import { useLocale } from '@/context/i18n'
|
||||||
import { emailLoginWithCode, sendEMailLoginCode } from '@/service/common'
|
import { emailLoginWithCode, sendEMailLoginCode } from '@/service/common'
|
||||||
import { encryptVerificationCode } from '@/utils/encryption'
|
import { encryptVerificationCode } from '@/utils/encryption'
|
||||||
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
||||||
@ -25,7 +25,7 @@ export default function CheckCode() {
|
|||||||
const language = i18n.language
|
const language = i18n.language
|
||||||
const [code, setVerifyCode] = useState('')
|
const [code, setVerifyCode] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
const codeInputRef = useRef<HTMLInputElement>(null)
|
const codeInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
const verify = async () => {
|
const verify = async () => {
|
||||||
|
|||||||
@ -2,13 +2,12 @@ import type { FormEvent } from 'react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { sendEMailLoginCode } from '@/service/common'
|
import { sendEMailLoginCode } from '@/service/common'
|
||||||
|
|
||||||
type MailAndCodeAuthProps = {
|
type MailAndCodeAuthProps = {
|
||||||
@ -22,7 +21,7 @@ export default function MailAndCodeAuth({ isInvite }: MailAndCodeAuthProps) {
|
|||||||
const emailFromLink = decodeURIComponent(searchParams.get('email') || '')
|
const emailFromLink = decodeURIComponent(searchParams.get('email') || '')
|
||||||
const [email, setEmail] = useState(emailFromLink)
|
const [email, setEmail] = useState(emailFromLink)
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
|
|
||||||
const handleGetEMailVerificationCode = async () => {
|
const handleGetEMailVerificationCode = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -4,13 +4,12 @@ import Link from 'next/link'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import { trackEvent } from '@/app/components/base/amplitude'
|
import { trackEvent } from '@/app/components/base/amplitude'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { login } from '@/service/common'
|
import { login } from '@/service/common'
|
||||||
import { encryptPassword } from '@/utils/encryption'
|
import { encryptPassword } from '@/utils/encryption'
|
||||||
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
||||||
@ -23,7 +22,7 @@ type MailAndPasswordAuthProps = {
|
|||||||
|
|
||||||
export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegistration: _allowRegistration }: MailAndPasswordAuthProps) {
|
export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegistration: _allowRegistration }: MailAndPasswordAuthProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
|
|||||||
@ -6,14 +6,14 @@ import Link from 'next/link'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { SimpleSelect } from '@/app/components/base/select'
|
import { SimpleSelect } from '@/app/components/base/select'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import I18n, { useDocLink } from '@/context/i18n'
|
import { useDocLink } from '@/context/i18n'
|
||||||
|
import { setLocaleOnClient } from '@/i18n-config'
|
||||||
import { languages, LanguagesSupported } from '@/i18n-config/language'
|
import { languages, LanguagesSupported } from '@/i18n-config/language'
|
||||||
import { activateMember } from '@/service/common'
|
import { activateMember } from '@/service/common'
|
||||||
import { useInvitationCheck } from '@/service/use-common'
|
import { useInvitationCheck } from '@/service/use-common'
|
||||||
@ -27,7 +27,6 @@ export default function InviteSettingsPage() {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
const token = decodeURIComponent(searchParams.get('invite_token') as string)
|
const token = decodeURIComponent(searchParams.get('invite_token') as string)
|
||||||
const { setLocaleOnClient } = useContext(I18n)
|
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
const [language, setLanguage] = useState(LanguagesSupported[0])
|
const [language, setLanguage] = useState(LanguagesSupported[0])
|
||||||
const [timezone, setTimezone] = useState(() => Intl.DateTimeFormat().resolvedOptions().timeZone || 'America/Los_Angeles')
|
const [timezone, setTimezone] = useState(() => Intl.DateTimeFormat().resolvedOptions().timeZone || 'America/Los_Angeles')
|
||||||
@ -65,7 +64,7 @@ export default function InviteSettingsPage() {
|
|||||||
catch {
|
catch {
|
||||||
recheck()
|
recheck()
|
||||||
}
|
}
|
||||||
}, [language, name, recheck, setLocaleOnClient, timezone, token, router, t])
|
}, [language, name, recheck, timezone, token, router, t])
|
||||||
|
|
||||||
if (!checkRes)
|
if (!checkRes)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|||||||
@ -4,12 +4,11 @@ import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
|
|||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Countdown from '@/app/components/signin/countdown'
|
import Countdown from '@/app/components/signin/countdown'
|
||||||
import I18NContext from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useMailValidity, useSendMail } from '@/service/use-common'
|
import { useMailValidity, useSendMail } from '@/service/use-common'
|
||||||
|
|
||||||
export default function CheckCode() {
|
export default function CheckCode() {
|
||||||
@ -20,7 +19,7 @@ export default function CheckCode() {
|
|||||||
const [token, setToken] = useState(decodeURIComponent(searchParams.get('token') as string))
|
const [token, setToken] = useState(decodeURIComponent(searchParams.get('token') as string))
|
||||||
const [code, setVerifyCode] = useState('')
|
const [code, setVerifyCode] = useState('')
|
||||||
const [loading, setIsLoading] = useState(false)
|
const [loading, setIsLoading] = useState(false)
|
||||||
const { locale } = useContext(I18NContext)
|
const locale = useLocale()
|
||||||
const { mutateAsync: submitMail } = useSendMail()
|
const { mutateAsync: submitMail } = useSendMail()
|
||||||
const { mutateAsync: verifyCode } = useMailValidity()
|
const { mutateAsync: verifyCode } = useMailValidity()
|
||||||
|
|
||||||
|
|||||||
@ -4,14 +4,13 @@ import { noop } from 'es-toolkit/compat'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useContext } from 'use-context-selector'
|
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Input from '@/app/components/base/input'
|
import Input from '@/app/components/base/input'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import Split from '@/app/signin/split'
|
import Split from '@/app/signin/split'
|
||||||
import { emailRegex } from '@/config'
|
import { emailRegex } from '@/config'
|
||||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||||
import I18n from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { useSendMail } from '@/service/use-common'
|
import { useSendMail } from '@/service/use-common'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -22,7 +21,7 @@ export default function Form({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
const { locale } = useContext(I18n)
|
const locale = useLocale()
|
||||||
const { systemFeatures } = useGlobalPublicStore()
|
const { systemFeatures } = useGlobalPublicStore()
|
||||||
|
|
||||||
const { mutateAsync: submitMail, isPending } = useSendMail()
|
const { mutateAsync: submitMail, isPending } = useSendMail()
|
||||||
|
|||||||
@ -1,33 +1,19 @@
|
|||||||
import type { Locale } from '@/i18n-config'
|
import type { Locale } from '@/i18n-config/language'
|
||||||
import { noop } from 'es-toolkit/compat'
|
import { atom, useAtomValue } from 'jotai'
|
||||||
import {
|
|
||||||
createContext,
|
|
||||||
useContext,
|
|
||||||
} from 'use-context-selector'
|
|
||||||
import { getDocLanguage, getLanguage, getPricingPageLanguage } from '@/i18n-config/language'
|
import { getDocLanguage, getLanguage, getPricingPageLanguage } from '@/i18n-config/language'
|
||||||
|
|
||||||
type II18NContext = {
|
export const localeAtom = atom<Locale>('en-US')
|
||||||
locale: Locale
|
export const useLocale = () => {
|
||||||
i18n: Record<string, any>
|
return useAtomValue(localeAtom)
|
||||||
setLocaleOnClient: (_lang: Locale, _reloadPage?: boolean) => Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const I18NContext = createContext<II18NContext>({
|
|
||||||
locale: 'en-US',
|
|
||||||
i18n: {},
|
|
||||||
setLocaleOnClient: async (_lang: Locale, _reloadPage?: boolean) => {
|
|
||||||
noop()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export const useI18N = () => useContext(I18NContext)
|
|
||||||
export const useGetLanguage = () => {
|
export const useGetLanguage = () => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
|
|
||||||
return getLanguage(locale)
|
return getLanguage(locale)
|
||||||
}
|
}
|
||||||
export const useGetPricingPageLanguage = () => {
|
export const useGetPricingPageLanguage = () => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
|
|
||||||
return getPricingPageLanguage(locale)
|
return getPricingPageLanguage(locale)
|
||||||
}
|
}
|
||||||
@ -36,7 +22,7 @@ export const defaultDocBaseUrl = 'https://docs.dify.ai'
|
|||||||
export const useDocLink = (baseUrl?: string): ((path?: string, pathMap?: { [index: string]: string }) => string) => {
|
export const useDocLink = (baseUrl?: string): ((path?: string, pathMap?: { [index: string]: string }) => string) => {
|
||||||
let baseDocUrl = baseUrl || defaultDocBaseUrl
|
let baseDocUrl = baseUrl || defaultDocBaseUrl
|
||||||
baseDocUrl = (baseDocUrl.endsWith('/')) ? baseDocUrl.slice(0, -1) : baseDocUrl
|
baseDocUrl = (baseDocUrl.endsWith('/')) ? baseDocUrl.slice(0, -1) : baseDocUrl
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
const docLanguage = getDocLanguage(locale)
|
const docLanguage = getDocLanguage(locale)
|
||||||
return (path?: string, pathMap?: { [index: string]: string }): string => {
|
return (path?: string, pathMap?: { [index: string]: string }): string => {
|
||||||
const pathUrl = path || ''
|
const pathUrl = path || ''
|
||||||
@ -45,4 +31,3 @@ export const useDocLink = (baseUrl?: string): ((path?: string, pathMap?: { [inde
|
|||||||
return `${baseDocUrl}/${docLanguage}/${targetPath}`
|
return `${baseDocUrl}/${docLanguage}/${targetPath}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default I18NContext
|
|
||||||
|
|||||||
@ -14,15 +14,13 @@ import type { Mock } from 'vitest'
|
|||||||
*/
|
*/
|
||||||
import { renderHook } from '@testing-library/react'
|
import { renderHook } from '@testing-library/react'
|
||||||
// Import after mock to get the mocked version
|
// Import after mock to get the mocked version
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
|
|
||||||
import { useFormatTimeFromNow } from './use-format-time-from-now'
|
import { useFormatTimeFromNow } from './use-format-time-from-now'
|
||||||
|
|
||||||
// Mock the i18n context
|
// Mock the i18n context
|
||||||
vi.mock('@/context/i18n', () => ({
|
vi.mock('@/context/i18n', () => ({
|
||||||
useI18N: vi.fn(() => ({
|
useLocale: vi.fn(() => 'en-US'),
|
||||||
locale: 'en-US',
|
|
||||||
})),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe('useFormatTimeFromNow', () => {
|
describe('useFormatTimeFromNow', () => {
|
||||||
@ -47,7 +45,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should return human-readable relative time strings
|
* Should return human-readable relative time strings
|
||||||
*/
|
*/
|
||||||
it('should format time from now in English', () => {
|
it('should format time from now in English', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -65,7 +63,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Very recent timestamps should show seconds
|
* Very recent timestamps should show seconds
|
||||||
*/
|
*/
|
||||||
it('should format very recent times', () => {
|
it('should format very recent times', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -81,7 +79,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should handle day-level granularity
|
* Should handle day-level granularity
|
||||||
*/
|
*/
|
||||||
it('should format times from days ago', () => {
|
it('should format times from days ago', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -98,7 +96,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* dayjs fromNow also supports future times (e.g., "in 2 hours")
|
* dayjs fromNow also supports future times (e.g., "in 2 hours")
|
||||||
*/
|
*/
|
||||||
it('should format future times', () => {
|
it('should format future times', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -117,7 +115,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should use Chinese characters for time units
|
* Should use Chinese characters for time units
|
||||||
*/
|
*/
|
||||||
it('should format time in Chinese (Simplified)', () => {
|
it('should format time in Chinese (Simplified)', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'zh-Hans' })
|
;(useLocale as Mock).mockReturnValue('zh-Hans')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -134,7 +132,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should use Spanish words for relative time
|
* Should use Spanish words for relative time
|
||||||
*/
|
*/
|
||||||
it('should format time in Spanish', () => {
|
it('should format time in Spanish', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'es-ES' })
|
;(useLocale as Mock).mockReturnValue('es-ES')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -151,7 +149,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should use French words for relative time
|
* Should use French words for relative time
|
||||||
*/
|
*/
|
||||||
it('should format time in French', () => {
|
it('should format time in French', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'fr-FR' })
|
;(useLocale as Mock).mockReturnValue('fr-FR')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -168,7 +166,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should use Japanese characters
|
* Should use Japanese characters
|
||||||
*/
|
*/
|
||||||
it('should format time in Japanese', () => {
|
it('should format time in Japanese', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'ja-JP' })
|
;(useLocale as Mock).mockReturnValue('ja-JP')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -185,7 +183,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should use pt-br locale mapping
|
* Should use pt-br locale mapping
|
||||||
*/
|
*/
|
||||||
it('should format time in Portuguese (Brazil)', () => {
|
it('should format time in Portuguese (Brazil)', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'pt-BR' })
|
;(useLocale as Mock).mockReturnValue('pt-BR')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -202,7 +200,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Unknown locales should default to English
|
* Unknown locales should default to English
|
||||||
*/
|
*/
|
||||||
it('should fallback to English for unsupported locale', () => {
|
it('should fallback to English for unsupported locale', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'xx-XX' as any })
|
;(useLocale as Mock).mockReturnValue('xx-XX' as any)
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -222,7 +220,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should format as a very old date
|
* Should format as a very old date
|
||||||
*/
|
*/
|
||||||
it('should handle timestamp 0', () => {
|
it('should handle timestamp 0', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -238,7 +236,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* Should handle dates far in the future
|
* Should handle dates far in the future
|
||||||
*/
|
*/
|
||||||
it('should handle very large timestamps', () => {
|
it('should handle very large timestamps', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -260,12 +258,12 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
const oneHourAgo = now - (60 * 60 * 1000)
|
const oneHourAgo = now - (60 * 60 * 1000)
|
||||||
|
|
||||||
// First render with English
|
// First render with English
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
rerender()
|
rerender()
|
||||||
const englishResult = result.current.formatTimeFromNow(oneHourAgo)
|
const englishResult = result.current.formatTimeFromNow(oneHourAgo)
|
||||||
|
|
||||||
// Second render with Spanish
|
// Second render with Spanish
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'es-ES' })
|
;(useLocale as Mock).mockReturnValue('es-ES')
|
||||||
rerender()
|
rerender()
|
||||||
const spanishResult = result.current.formatTimeFromNow(oneHourAgo)
|
const spanishResult = result.current.formatTimeFromNow(oneHourAgo)
|
||||||
|
|
||||||
@ -280,7 +278,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* dayjs should automatically choose the appropriate unit
|
* dayjs should automatically choose the appropriate unit
|
||||||
*/
|
*/
|
||||||
it('should use appropriate time units for different durations', () => {
|
it('should use appropriate time units for different durations', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -342,7 +340,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
const oneHourAgo = now - (60 * 60 * 1000)
|
const oneHourAgo = now - (60 * 60 * 1000)
|
||||||
|
|
||||||
locales.forEach((locale) => {
|
locales.forEach((locale) => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale })
|
;(useLocale as Mock).mockReturnValue(locale)
|
||||||
|
|
||||||
const { result } = renderHook(() => useFormatTimeFromNow())
|
const { result } = renderHook(() => useFormatTimeFromNow())
|
||||||
const formatted = result.current.formatTimeFromNow(oneHourAgo)
|
const formatted = result.current.formatTimeFromNow(oneHourAgo)
|
||||||
@ -360,7 +358,7 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
* The formatTimeFromNow function should be memoized with useCallback
|
* The formatTimeFromNow function should be memoized with useCallback
|
||||||
*/
|
*/
|
||||||
it('should memoize formatTimeFromNow function', () => {
|
it('should memoize formatTimeFromNow function', () => {
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
|
|
||||||
const { result, rerender } = renderHook(() => useFormatTimeFromNow())
|
const { result, rerender } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
@ -379,11 +377,11 @@ describe('useFormatTimeFromNow', () => {
|
|||||||
it('should create new function when locale changes', () => {
|
it('should create new function when locale changes', () => {
|
||||||
const { result, rerender } = renderHook(() => useFormatTimeFromNow())
|
const { result, rerender } = renderHook(() => useFormatTimeFromNow())
|
||||||
|
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'en-US' })
|
;(useLocale as Mock).mockReturnValue('en-US')
|
||||||
rerender()
|
rerender()
|
||||||
const englishFunction = result.current.formatTimeFromNow
|
const englishFunction = result.current.formatTimeFromNow
|
||||||
|
|
||||||
;(useI18N as Mock).mockReturnValue({ locale: 'es-ES' })
|
;(useLocale as Mock).mockReturnValue('es-ES')
|
||||||
rerender()
|
rerender()
|
||||||
const spanishFunction = result.current.formatTimeFromNow
|
const spanishFunction = result.current.formatTimeFromNow
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useI18N } from '@/context/i18n'
|
import { useLocale } from '@/context/i18n'
|
||||||
import { localeMap } from '@/i18n-config/language'
|
import { localeMap } from '@/i18n-config/language'
|
||||||
import 'dayjs/locale/de'
|
import 'dayjs/locale/de'
|
||||||
import 'dayjs/locale/es'
|
import 'dayjs/locale/es'
|
||||||
@ -27,7 +27,7 @@ import 'dayjs/locale/zh-tw'
|
|||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
export const useFormatTimeFromNow = () => {
|
export const useFormatTimeFromNow = () => {
|
||||||
const { locale } = useI18N()
|
const locale = useLocale()
|
||||||
const formatTimeFromNow = useCallback((time: number) => {
|
const formatTimeFromNow = useCallback((time: number) => {
|
||||||
const dayjsLocale = localeMap[locale] ?? 'en'
|
const dayjsLocale = localeMap[locale] ?? 'en'
|
||||||
return dayjs(time).locale(dayjsLocale).fromNow()
|
return dayjs(time).locale(dayjsLocale).fromNow()
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
- useTranslation
|
- useTranslation
|
||||||
- useGetLanguage
|
- useGetLanguage
|
||||||
- useI18N
|
- useLocale
|
||||||
- useRenderI18nObject
|
- useRenderI18nObject
|
||||||
|
|
||||||
## impl
|
## impl
|
||||||
@ -46,6 +46,6 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] ts docs for useGetLanguage
|
- [ ] ts docs for useGetLanguage
|
||||||
- [ ] ts docs for useI18N
|
- [ ] ts docs for useLocale
|
||||||
- [ ] client docs for i18n
|
- [ ] client docs for i18n
|
||||||
- [ ] server docs for i18n
|
- [ ] server docs for i18n
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
import type { Locale } from '.'
|
import type { Locale } from '.'
|
||||||
import { camelCase, kebabCase } from 'es-toolkit/compat'
|
import { camelCase, kebabCase } from 'es-toolkit/compat'
|
||||||
import i18n from 'i18next'
|
import i18n from 'i18next'
|
||||||
|
|
||||||
import { initReactI18next } from 'react-i18next'
|
import { initReactI18next } from 'react-i18next'
|
||||||
import appAnnotation from '../i18n/en-US/app-annotation.json'
|
import appAnnotation from '../i18n/en-US/app-annotation.json'
|
||||||
import appApi from '../i18n/en-US/app-api.json'
|
import appApi from '../i18n/en-US/app-api.json'
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { i18n as I18nInstance } from 'i18next'
|
||||||
import type { Locale } from '.'
|
import type { Locale } from '.'
|
||||||
import type { NamespaceCamelCase, NamespaceKebabCase } from './i18next-config'
|
import type { NamespaceCamelCase, NamespaceKebabCase } from './i18next-config'
|
||||||
import { match } from '@formatjs/intl-localematcher'
|
import { match } from '@formatjs/intl-localematcher'
|
||||||
@ -7,29 +8,39 @@ import resourcesToBackend from 'i18next-resources-to-backend'
|
|||||||
import Negotiator from 'negotiator'
|
import Negotiator from 'negotiator'
|
||||||
import { cookies, headers } from 'next/headers'
|
import { cookies, headers } from 'next/headers'
|
||||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
import { initReactI18next } from 'react-i18next/initReactI18next'
|
||||||
|
import serverOnlyContext from '@/utils/server-only-context'
|
||||||
import { i18n } from '.'
|
import { i18n } from '.'
|
||||||
|
|
||||||
// https://locize.com/blog/next-13-app-dir-i18n/
|
const [getLocaleCache, setLocaleCache] = serverOnlyContext<Locale | null>(null)
|
||||||
const initI18next = async (lng: Locale, ns: NamespaceKebabCase) => {
|
const [getI18nInstance, setI18nInstance] = serverOnlyContext<I18nInstance | null>(null)
|
||||||
const i18nInstance = createInstance()
|
|
||||||
await i18nInstance
|
const getOrCreateI18next = async (lng: Locale) => {
|
||||||
|
let instance = getI18nInstance()
|
||||||
|
if (instance)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
instance = createInstance()
|
||||||
|
await instance
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.use(resourcesToBackend((language: Locale, namespace: NamespaceKebabCase) => {
|
.use(resourcesToBackend((language: Locale, namespace: NamespaceKebabCase) => {
|
||||||
return import(`../i18n/${language}/${namespace}.json`)
|
return import(`../i18n/${language}/${namespace}.json`)
|
||||||
}))
|
}))
|
||||||
.init({
|
.init({
|
||||||
lng: lng === 'zh-Hans' ? 'zh-Hans' : lng,
|
lng,
|
||||||
ns,
|
|
||||||
defaultNS: ns,
|
|
||||||
fallbackLng: 'en-US',
|
fallbackLng: 'en-US',
|
||||||
keySeparator: false,
|
keySeparator: false,
|
||||||
})
|
})
|
||||||
return i18nInstance
|
setI18nInstance(instance)
|
||||||
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTranslation(lng: Locale, ns: NamespaceKebabCase) {
|
export async function getTranslation(lng: Locale, ns: NamespaceKebabCase) {
|
||||||
const camelNs = camelCase(ns) as NamespaceCamelCase
|
const camelNs = camelCase(ns) as NamespaceCamelCase
|
||||||
const i18nextInstance = await initI18next(lng, ns)
|
const i18nextInstance = await getOrCreateI18next(lng)
|
||||||
|
|
||||||
|
if (!i18nextInstance.hasLoadedNamespace(camelNs))
|
||||||
|
await i18nextInstance.loadNamespaces(camelNs)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
t: i18nextInstance.getFixedT(lng, camelNs),
|
t: i18nextInstance.getFixedT(lng, camelNs),
|
||||||
i18n: i18nextInstance,
|
i18n: i18nextInstance,
|
||||||
@ -37,6 +48,10 @@ export async function getTranslation(lng: Locale, ns: NamespaceKebabCase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getLocaleOnServer = async (): Promise<Locale> => {
|
export const getLocaleOnServer = async (): Promise<Locale> => {
|
||||||
|
const cached = getLocaleCache()
|
||||||
|
if (cached)
|
||||||
|
return cached
|
||||||
|
|
||||||
const locales: string[] = i18n.locales
|
const locales: string[] = i18n.locales
|
||||||
|
|
||||||
let languages: string[] | undefined
|
let languages: string[] | undefined
|
||||||
@ -58,5 +73,6 @@ export const getLocaleOnServer = async (): Promise<Locale> => {
|
|||||||
|
|
||||||
// match locale
|
// match locale
|
||||||
const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale
|
const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale
|
||||||
|
setLocaleCache(matchedLocale)
|
||||||
return matchedLocale
|
return matchedLocale
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,6 +92,7 @@
|
|||||||
"i18next": "^25.7.3",
|
"i18next": "^25.7.3",
|
||||||
"i18next-resources-to-backend": "^1.2.1",
|
"i18next-resources-to-backend": "^1.2.1",
|
||||||
"immer": "^11.1.0",
|
"immer": "^11.1.0",
|
||||||
|
"jotai": "^2.16.1",
|
||||||
"js-audio-recorder": "^1.0.7",
|
"js-audio-recorder": "^1.0.7",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
|||||||
28
web/pnpm-lock.yaml
generated
28
web/pnpm-lock.yaml
generated
@ -192,6 +192,9 @@ importers:
|
|||||||
immer:
|
immer:
|
||||||
specifier: ^11.1.0
|
specifier: ^11.1.0
|
||||||
version: 11.1.0
|
version: 11.1.0
|
||||||
|
jotai:
|
||||||
|
specifier: ^2.16.1
|
||||||
|
version: 2.16.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.7)(react@19.2.3)
|
||||||
js-audio-recorder:
|
js-audio-recorder:
|
||||||
specifier: ^1.0.7
|
specifier: ^1.0.7
|
||||||
version: 1.0.7
|
version: 1.0.7
|
||||||
@ -6207,6 +6210,24 @@ packages:
|
|||||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jotai@2.16.1:
|
||||||
|
resolution: {integrity: sha512-vrHcAbo3P7Br37C8Bv6JshMtlKMPqqmx0DDREtTjT4nf3QChDrYdbH+4ik/9V0cXA57dK28RkJ5dctYvavcIlg==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/core': '>=7.0.0'
|
||||||
|
'@babel/template': '>=7.0.0'
|
||||||
|
'@types/react': ~19.2.7
|
||||||
|
react: '>=17.0.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@babel/core':
|
||||||
|
optional: true
|
||||||
|
'@babel/template':
|
||||||
|
optional: true
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
|
||||||
js-audio-recorder@1.0.7:
|
js-audio-recorder@1.0.7:
|
||||||
resolution: {integrity: sha512-JiDODCElVHGrFyjGYwYyNi7zCbKk9va9C77w+zCPMmi4C6ix7zsX2h3ddHugmo4dOTOTCym9++b/wVW9nC0IaA==}
|
resolution: {integrity: sha512-JiDODCElVHGrFyjGYwYyNi7zCbKk9va9C77w+zCPMmi4C6ix7zsX2h3ddHugmo4dOTOTCym9++b/wVW9nC0IaA==}
|
||||||
|
|
||||||
@ -15331,6 +15352,13 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.6.1: {}
|
jiti@2.6.1: {}
|
||||||
|
|
||||||
|
jotai@2.16.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.7)(react@19.2.3):
|
||||||
|
optionalDependencies:
|
||||||
|
'@babel/core': 7.28.5
|
||||||
|
'@babel/template': 7.27.2
|
||||||
|
'@types/react': 19.2.7
|
||||||
|
react: 19.2.3
|
||||||
|
|
||||||
js-audio-recorder@1.0.7: {}
|
js-audio-recorder@1.0.7: {}
|
||||||
|
|
||||||
js-base64@3.7.8: {}
|
js-base64@3.7.8: {}
|
||||||
|
|||||||
15
web/utils/server-only-context.ts
Normal file
15
web/utils/server-only-context.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// credit: https://github.com/manvalls/server-only-context/blob/main/src/index.ts
|
||||||
|
|
||||||
|
import { cache } from 'react'
|
||||||
|
|
||||||
|
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
|
||||||
|
const getRef = cache(() => ({ current: defaultValue }))
|
||||||
|
|
||||||
|
const getValue = (): T => getRef().current
|
||||||
|
|
||||||
|
const setValue = (value: T) => {
|
||||||
|
getRef().current = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return [getValue, setValue]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user