Feat/i18n restructure (#2529)

This commit is contained in:
crazywoola 2024-02-23 14:31:06 +08:00 • committed by GitHub
commit 9574730050
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
140 changed files with 448 additions and 659 deletions

175
web/i18n/README.md Normal file
View file

@ -0,0 +1,175 @@
# Internationalization (i18n)
## Introduction
This directory contains the internationalization (i18n) files for this project.
## File Structure
```
├── [ 24] README.md
├── [ 0] README_CN.md
├── [ 704] en-US
│   ├── [2.4K] app-annotation.ts
│   ├── [5.2K] app-api.ts
│   ├── [ 16K] app-debug.ts
│   ├── [2.1K] app-log.ts
│   ├── [5.3K] app-overview.ts
│   ├── [1.9K] app.ts
│   ├── [4.1K] billing.ts
│   ├── [ 17K] common.ts
│   ├── [ 859] custom.ts
│   ├── [5.7K] dataset-creation.ts
│   ├── [ 10K] dataset-documents.ts
│   ├── [ 761] dataset-hit-testing.ts
│   ├── [1.7K] dataset-settings.ts
│   ├── [2.0K] dataset.ts
│   ├── [ 941] explore.ts
│   ├── [ 52] layout.ts
│   ├── [2.3K] login.ts
│   ├── [ 52] register.ts
│   ├── [2.5K] share-app.ts
│   └── [2.8K] tools.ts
├── [1.6K] i18next-config.ts
├── [ 634] index.ts
├── [4.4K] language.ts
```
We use English as the default language. The i18n files are organized by language and then by module. For example, the English translation for the `app` module is in `en-US/app.ts`.
If you want to add a new language or modify an existing translation, you can create a new file for the language or modify the existing file. The file name should be the language code (e.g., `zh-CN` for Chinese) and the file extension should be `.ts`.
For example, if you want to add french translation, you can create a new folder `fr-FR` and add the translation files in it.
By default we will use `LanguagesSupported` to determine which languages are supported. For example, in login page and settings page, we will use `LanguagesSupported` to determine which languages are supported and display them in the language selection dropdown.
## Example
1. Create a new folder for the new language.
```
cp -r en-US fr-FR
```
2. Modify the translation files in the new folder.
3. Add type to new language in the `language.ts` file.
```typescript
export type I18nText = {
'en-US': string
'zh-Hans': string
'pt-BR': string
'es-ES': string
'fr-FR': string
'de-DE': string
'ja-JP': string
'ko-KR': string
'ru-RU': string
'it-IT': string
'uk-UA': string
'YOUR_LANGUAGE_CODE': string
}
```
4. Add the new language to the `language.ts` file.
```typescript
export const languages = [
{
value: 'en-US',
name: 'English(United States)',
example: 'Hello, Dify!',
supported: true,
},
{
value: 'zh-Hans',
name: '简体中文',
example: '你好,Dify!',
supported: true,
},
{
value: 'pt-BR',
name: 'Português(Brasil)',
example: 'Olá, Dify!',
supported: true,
},
{
value: 'es-ES',
name: 'Español(España)',
example: 'Saluton, Dify!',
supported: false,
},
{
value: 'fr-FR',
name: 'Français(France)',
example: 'Bonjour, Dify!',
supported: false,
},
{
value: 'de-DE',
name: 'Deutsch(Deutschland)',
example: 'Hallo, Dify!',
supported: false,
},
{
value: 'ja-JP',
name: '日本語(日本)',
example: 'こんにちは、Dify!',
supported: false,
},
{
value: 'ko-KR',
name: '한국어(대한민국)',
example: '안녕, Dify!',
supported: false,
},
{
value: 'ru-RU',
name: 'Русский(Россия)',
example: ' Привет, Dify!',
supported: false,
},
{
value: 'it-IT',
name: 'Italiano(Italia)',
example: 'Ciao, Dify!',
supported: false,
},
{
value: 'th-TH',
name: 'ไทย(ประเทศไทย)',
example: 'สวัสดี Dify!',
supported: false,
},
{
value: 'id-ID',
name: 'Bahasa Indonesia',
example: 'Saluto, Dify!',
supported: false,
},
{
value: 'uk-UA',
name: 'Українська(Україна)',
example: 'Привет, Dify!',
supported: true,
},
// Add your language here 👇
...
// Add your language here 👆
]
```
5. Don't forget to mark the supported field as `true` if the language is supported.
6. Sometime you might need to do some changes in the server side. Please change this file as well. 👇
https://github.com/langgenius/dify/blob/61e4bbabaf2758354db4073cbea09fdd21a5bec1/api/constants/languages.py#L5
## Clean Up
That's it! You have successfully added a new language to the project. If you want to remove a language, you can simply delete the folder and remove the language from the `language.ts` file.
We have a list of languages that we support in the `language.ts` file. But some of them are not supported yet. So, they are marked as `false`. If you want to support a language, you can follow the steps above and mark the supported field as `true`.

View file

@ -1,82 +0,0 @@
# 前端 i18n 修改
## 后端多语言支持
`api/libs/helper.py:117` 中添加对应的语言支持。如:
```python
def supported_language(lang):
if lang in ['en-US', 'zh-Hans', 'de', 'de-AT']:
return lang
```
## 添加多语言文件
在 `web/i18n/lang` 下添加不同模块的多语言文件。文件命令为 模块名.{LANG}.ts。详细参考[LANG](https://www.venea.net/web/culture_code)
## 引入新添加的多语言文件
在 `web/i18n/i18next-config.ts` 中 resources 对象中中引入新添加的多语言文件。如:
```javascript
const resources = {
'en': {...},
'zh-Hans': {...},
// 引入新添加的语言
'new LANG': {
translation: {
common: commonNewLan,
layout: layoutNewLan,
...
}
}
}
```
## 翻译过程中的改动
### 日期格式化的多语言处理
目前日期做多语言格式化的文件涉及到如下 2 个:
```javascript
1. web/app/components/header/account-setting/members-page/index.tsx
// Line: 78
{dayjs(Number((account.last_login_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
2. web/app/components/develop/secret-key/secret-key-modal.tsx
// Line:82
const formatDate = (timestamp: any) => {
if (locale === 'en') {
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000)
} else {
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
}
}
```
看需求做对应的改动。
### 翻译中带变量的内容的处理
翻译中会存在带变量的情况,变量的值会在运行时被替换。翻译中的变量会用{{ 和 }} 包裹。
翻译带变量的内容时:
1. 不能改变量的名称。即:变量的名称不需要做翻译。
2. 确保变量填充后,语句仍保持通顺。
查找所有翻译中带变量的方式:在 ./web/i18n/lang 下搜索:{{。
### 翻译内容太长破坏 UI
如果某个翻译的内容比其他语言的长很多,检查下是否会破坏 UI。
## 帮助文档
目前的帮助文档的调整逻辑是:中文跳转中文,其他语言跳英文。如果帮助文档也做了多语言。需要做这块的改动。
## 验证
新增语言包建议通过本地部署最新代码来验证,可参考:https://docs.dify.ai/getting-started/install-self-hosted/local-source-code
验证点:
1. 首次初始化安装是否存在新语言下拉选项,以及是否可以用新语言进行初始化
2. 个人设置中是否存在新语言下拉选项,以及是否可以选择并保存新语言
3. 界面各处文案是否使用新语言来展示,以及文案是否破坏 UI
4. 从模板创建应用内容是否均为新语言
5. (CLOUD 版)通过 OAuth 授权登录后,是否直接设置当前浏览器语言为界面语言

View file

@ -1,81 +0,0 @@
# Frontend i18n modification
## Backend i18n modification
`api/libs/helper.py:117` Add corresponding language support. Such as:
```python
def supported_language(lang):
if lang in ['en-US', 'zh-Hans', 'de', 'de-AT']:
return lang
```
## Adding multiple language files
Add multilingual files for different modules under web/i18n/lang. The file name is Module name.{LANG}.ts. Please refer [LANG](https://www.venea.net/web/culture_code) for details.
## Introducing a newly added multilingual file
Introduce the newly added multilingual file in the resources object in web/i18n/i18next-config.ts. For example:
```javascript
const resources = {
'en': {...},
'zh-Hans': {...},
_// Introduce the newly added language_
'new LANG': {
translation: {
common: commonNewLan,
layout: layoutNewLan,
...
}
}
}
```
## Changes in the translation process
### Multi-language processing of date formatting
Currently, two files are involved in date formatting in multiple languages:
```javascript
1. web/app/components/header/account-setting/members-page/index.tsx
_// Line 78_
{dayjs(Number((account.last_login_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
2. web/app/components/develop/secret-key/secret-key-modal.tsx
_// Line 82_
const formatDate = (timestamp: any) => {
if (locale === 'en') {
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000)
} else {
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
}
}
```
Make corresponding changes based on requirements.
### Handling translation content with variables
There will be variables in the translation, and the value of the variables will be replaced at runtime. Variables in translation will be wrapped in {{ and }}.
When translating content with variables:
1. Do not change the variable name. That is: the variable name does not need to be translated.
2. Ensure that the statement remains smooth after the variable is filled.
Find all translations with variables: search for {{ under ./web/i18n/lang.
### Translation content is too long to destroy UI
If a certain translation content is much longer than other languages, check if it will destroy the UI.
## Help documentation
The current logic for adjusting the help documentation is: Chinese jumps to Chinese, other languages jump to English. If the help documentation is also multilingual, changes need to be made in this area.
## Verification
It is recommended to verify the newly added language pack through local deployment of the latest code. For reference: https://docs.dify.ai/getting-started/install-self-hosted/local-source-code
Verification points:
1. Whether the initial installation has new language drop-down options, and whether the new language can be used for initialization
2. Whether there is a new language drop-down option in personal settings, and whether the new language can be selected and saved
3. Whether the text in the interface is displayed in the new language, and whether the text destroys the UI
4. Whether the content created from the template is all in the new language
5. (CLOUD version) After logging in through OAuth authorization, whether the current browser language is set directly as the interface language

View file

@ -1,16 +0,0 @@
import Cookies from 'js-cookie'
import type { Locale } from '.'
import { i18n } from '.'
import { LOCALE_COOKIE_NAME } from '@/config'
import { changeLanguage } from '@/i18n/i18next-config'
// same logic as server
export const getLocaleOnClient = (): Locale => {
return Cookies.get(LOCALE_COOKIE_NAME) as Locale || i18n.defaultLocale
}
export const setLocaleOnClient = (locale: Locale, reloadPage = true) => {
Cookies.set(LOCALE_COOKIE_NAME, locale)
changeLanguage(locale)
reloadPage && location.reload()
}

View file

@ -1,202 +1,44 @@
'use client'
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import commonEn from './lang/common.en'
import commonZh from './lang/common.zh'
import commonUk from './lang/common.uk' // Ukrainian import
import commonPt from './lang/common.pt' // Portuguese import
import loginEn from './lang/login.en'
import loginZh from './lang/login.zh'
import loginPt from './lang/login.pt' // Portuguese import
import loginUk from './lang/login.uk' // Ukrainian import
import registerEn from './lang/register.en'
import registerZh from './lang/register.zh'
import registerPt from './lang/register.pt' // Portuguese import
import registerUk from './lang/register.uk' // Ukrainian import
import layoutEn from './lang/layout.en'
import layoutZh from './lang/layout.zh'
import layoutPt from './lang/layout.pt' // Portuguese import
import layoutUk from './lang/layout.uk' // Ukrainian import
import appEn from './lang/app.en'
import appZh from './lang/app.zh'
import appPt from './lang/app.pt' // Portuguese import
import appUk from './lang/app.uk' // Ukrainian import
import appOverviewEn from './lang/app-overview.en'
import appOverviewZh from './lang/app-overview.zh'
import appOverviewPt from './lang/app-overview.pt' // Portuguese import
import appOverviewUk from './lang/app-overview.uk' // Ukrainian import
import appDebugEn from './lang/app-debug.en'
import appDebugZh from './lang/app-debug.zh'
import appDebugPt from './lang/app-debug.pt' // Portuguese import
import appDebugUk from './lang/app-debug.uk' // Ukrainian import
import appApiEn from './lang/app-api.en'
import appApiZh from './lang/app-api.zh'
import appApiPt from './lang/app-api.pt' // Portuguese import
import appApiUk from './lang/app-api.uk' // Ukrainian import
import appLogEn from './lang/app-log.en'
import appLogZh from './lang/app-log.zh'
import appLogPt from './lang/app-log.pt' // Portuguese import
import appLogUk from './lang/app-log.uk' // Ukrainian import
import appAnnotationEn from './lang/app-annotation.en'
import appAnnotationZh from './lang/app-annotation.zh'
import appAnnotationPt from './lang/app-annotation.pt' // Portuguese import
import appAnnotationUk from './lang/app-annotation.uk' // Ukrainian import
import shareEn from './lang/share-app.en'
import shareZh from './lang/share-app.zh'
import sharePt from './lang/share-app.pt' // Portuguese import
import shareUk from './lang/share-app.uk' // Ukrainian import
import datasetEn from './lang/dataset.en'
import datasetZh from './lang/dataset.zh'
import datasetPt from './lang/dataset.pt' // Portuguese import
import datasetUk from './lang/dataset.uk' // Ukrainian import
import datasetDocumentsEn from './lang/dataset-documents.en'
import datasetDocumentsZh from './lang/dataset-documents.zh'
import datasetDocumentsPt from './lang/dataset-documents.pt' // Portuguese import
import datasetDocumentsUk from './lang/dataset-documents.uk' // Ukrainian import
import datasetHitTestingEn from './lang/dataset-hit-testing.en'
import datasetHitTestingZh from './lang/dataset-hit-testing.zh'
import datasetHitTestingPt from './lang/dataset-hit-testing.pt' // Portuguese import
import datasetHitTestingUk from './lang/dataset-hit-testing.uk' // Ukrainian import
import datasetSettingsEn from './lang/dataset-settings.en'
import datasetSettingsZh from './lang/dataset-settings.zh'
import datasetSettingsPt from './lang/dataset-settings.pt' // Portuguese import
import datasetSettingsUk from './lang/dataset-settings.uk' // Ukrainian import
import datasetCreationEn from './lang/dataset-creation.en'
import datasetCreationZh from './lang/dataset-creation.zh'
import datasetCreationPt from './lang/dataset-creation.pt' // Portuguese import
import datasetCreationUk from './lang/dataset-creation.uk' // Ukrainian import
import exploreEn from './lang/explore.en'
import exploreZh from './lang/explore.zh'
import explorePt from './lang/explore.pt' // Portuguese import
import exploreUk from './lang/explore.uk' // Ukrainian import
import billingEn from './lang/billing.en'
import billingZh from './lang/billing.zh'
import billingPt from './lang/billing.pt' // Portuguese import
import billingUk from './lang/billing.uk' // Ukrainian import
import customEn from './lang/custom.en'
import customZh from './lang/custom.zh'
import customPt from './lang/custom.pt' // Portuguese import
import customUk from './lang/custom.uk' // Ukrainian import
import toolsEn from './lang/tools.en'
import toolsZh from './lang/tools.zh'
import toolsPt from './lang/tools.pt' // Portuguese import
import toolsUk from './lang/tools.uk' // Ukrainian import
const resources = {
'en-US': {
translation: {
common: commonEn,
layout: layoutEn, // page layout
login: loginEn,
register: registerEn,
// app
app: appEn,
appOverview: appOverviewEn,
appDebug: appDebugEn,
appApi: appApiEn,
appLog: appLogEn,
appAnnotation: appAnnotationEn,
// share
share: shareEn,
dataset: datasetEn,
datasetDocuments: datasetDocumentsEn,
datasetHitTesting: datasetHitTestingEn,
datasetSettings: datasetSettingsEn,
datasetCreation: datasetCreationEn,
explore: exploreEn,
// billing
billing: billingEn,
custom: customEn,
// tools
tools: toolsEn,
},
import { LanguagesSupported } from '@/i18n/language'
const loadLangResources = (lang: string) => ({
translation: {
common: require(`./${lang}/common`).default,
layout: require(`./${lang}/layout`).default,
login: require(`./${lang}/login`).default,
register: require(`./${lang}/register`).default,
app: require(`./${lang}/app`).default,
appOverview: require(`./${lang}/app-overview`).default,
appDebug: require(`./${lang}/app-debug`).default,
appApi: require(`./${lang}/app-api`).default,
appLog: require(`./${lang}/app-log`).default,
appAnnotation: require(`./${lang}/app-annotation`).default,
share: require(`./${lang}/share-app`).default,
dataset: require(`./${lang}/dataset`).default,
datasetDocuments: require(`./${lang}/dataset-documents`).default,
datasetHitTesting: require(`./${lang}/dataset-hit-testing`).default,
datasetSettings: require(`./${lang}/dataset-settings`).default,
datasetCreation: require(`./${lang}/dataset-creation`).default,
explore: require(`./${lang}/explore`).default,
billing: require(`./${lang}/billing`).default,
custom: require(`./${lang}/custom`).default,
tools: require(`./${lang}/tools`).default,
},
'zh-Hans': {
translation: {
common: commonZh,
layout: layoutZh,
login: loginZh,
register: registerZh,
// app
app: appZh,
appOverview: appOverviewZh,
appDebug: appDebugZh,
appApi: appApiZh,
appLog: appLogZh,
appAnnotation: appAnnotationZh,
// share
share: shareZh,
dataset: datasetZh,
datasetDocuments: datasetDocumentsZh,
datasetHitTesting: datasetHitTestingZh,
datasetSettings: datasetSettingsZh,
datasetCreation: datasetCreationZh,
explore: exploreZh,
billing: billingZh,
custom: customZh,
// tools
tools: toolsZh,
},
},
'pt-BR': {
translation: {
common: commonPt,
layout: layoutPt,
login: loginPt,
register: registerPt,
// app
app: appPt,
appOverview: appOverviewPt,
appDebug: appDebugPt,
appApi: appApiPt,
appLog: appLogPt,
appAnnotation: appAnnotationPt,
// share
share: sharePt,
dataset: datasetPt,
datasetDocuments: datasetDocumentsPt,
datasetHitTesting: datasetHitTestingPt,
datasetSettings: datasetSettingsPt,
datasetCreation: datasetCreationPt,
explore: explorePt,
billing: billingPt,
custom: customPt,
tools: toolsPt,
},
},
'uk-UA': {
translation: {
common: commonUk,
layout: layoutUk,
login: loginUk,
register: registerUk,
app: appUk,
appOverview: appOverviewUk,
appDebug: appDebugUk,
appApi: appApiUk,
appLog: appLogUk,
appAnnotation: appAnnotationUk,
share: shareUk,
dataset: datasetUk,
datasetDocuments: datasetDocumentsUk,
datasetHitTesting: datasetHitTestingUk,
datasetSettings: datasetSettingsUk,
datasetCreation: datasetCreationUk,
explore: exploreUk,
billing: billingUk,
custom: customUk,
tools: toolsUk,
},
},
}
})
// Automatically generate the resources object
const resources = LanguagesSupported.reduce((acc: any, lang: string) => {
acc[lang] = loadLangResources(lang)
return acc
}, {})
i18n.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
lng: undefined,
fallbackLng: 'en-US',
// debug: true,
resources,
})

View file

@ -1,26 +0,0 @@
import { createInstance } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next'
import type { Locale } from '.'
// https://locize.com/blog/next-13-app-dir-i18n/
const initI18next = async (lng: Locale, ns: string) => {
const i18nInstance = createInstance()
await i18nInstance
.use(initReactI18next)
.use(resourcesToBackend((language: string, namespace: string) => import(`./lang/${namespace}.${language}.ts`)))
.init({
lng: lng === 'zh-Hans' ? 'zh' : lng,
ns,
fallbackLng: 'en',
})
return i18nInstance
}
export async function useTranslation(lng: Locale, ns = '', options: Record<string, any> = {}) {
const i18nextInstance = await initI18next(lng, ns)
return {
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
i18n: i18nextInstance,
}
}

View file

@ -1,8 +1,22 @@
import { LanguagesSupported } from '@/utils/language'
import Cookies from 'js-cookie'
import { changeLanguage } from '@/i18n/i18next-config'
import { LOCALE_COOKIE_NAME } from '@/config'
import { LanguagesSupported } from '@/i18n/language'
export const i18n = {
defaultLocale: 'en',
defaultLocale: 'en-US',
locales: LanguagesSupported,
} as const
export type Locale = typeof i18n['locales'][number]
export const getLocaleOnClient = (): Locale => {
return Cookies.get(LOCALE_COOKIE_NAME) as Locale || i18n.defaultLocale
}
export const setLocaleOnClient = (locale: Locale, reloadPage = true) => {
Cookies.set(LOCALE_COOKIE_NAME, locale)
changeLanguage(locale)
reloadPage && location.reload()
}

135
web/i18n/language.ts Normal file
View file

@ -0,0 +1,135 @@
export type Item = {
value: number | string
name: string
example: string
}
export type I18nText = {
'en-US': string
'zh-Hans': string
'pt-BR': string
'es-ES': string
'fr-FR': string
'de-DE': string
'ja-JP': string
'ko-KR': string
'ru-RU': string
'it-IT': string
'uk-UA': string
}
export const languages = [
{
value: 'en-US',
name: 'English(United States)',
example: 'Hello, Dify!',
supported: true,
},
{
value: 'zh-Hans',
name: '简体中文',
example: '你好,Dify!',
supported: true,
},
{
value: 'pt-BR',
name: 'Português(Brasil)',
example: 'Olá, Dify!',
supported: true,
},
{
value: 'es-ES',
name: 'Español(España)',
example: 'Saluton, Dify!',
supported: false,
},
{
value: 'fr-FR',
name: 'Français(France)',
example: 'Bonjour, Dify!',
supported: false,
},
{
value: 'de-DE',
name: 'Deutsch(Deutschland)',
example: 'Hallo, Dify!',
supported: false,
},
{
value: 'ja-JP',
name: '日本語(日本)',
example: 'こんにちは、Dify!',
supported: false,
},
{
value: 'ko-KR',
name: '한국어(대한민국)',
example: '안녕, Dify!',
supported: false,
},
{
value: 'ru-RU',
name: 'Русский(Россия)',
example: ' Привет, Dify!',
supported: false,
},
{
value: 'it-IT',
name: 'Italiano(Italia)',
example: 'Ciao, Dify!',
supported: false,
},
{
value: 'th-TH',
name: 'ไทย(ประเทศไทย)',
example: 'สวัสดี Dify!',
supported: false,
},
{
value: 'id-ID',
name: 'Bahasa Indonesia',
example: 'Saluto, Dify!',
supported: false,
},
{
value: 'uk-UA',
name: 'Українська(Україна)',
example: 'Привет, Dify!',
supported: true,
},
]
export const LanguagesSupported = languages.filter(item => item.supported).map(item => item.value)
export const getLanguage = (locale: string) => {
if (locale === 'zh-Hans')
return locale.replace('-', '_')
return LanguagesSupported[0].replace('-', '_')
}
export const NOTICE_I18N = {
title: {
en_US: 'Important Notice',
zh_Hans: '重要公告',
pt_BR: 'Aviso Importante',
es_ES: 'Aviso Importante',
fr_FR: 'Avis important',
de_DE: 'Wichtiger Hinweis',
ja_JP: '重要なお知らせ',
ko_KR: '중요 공지',
uk_UA: 'Важливе повідомлення',
},
desc: {
en_US: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
zh_Hans: '为了有效提升数据检索能力及稳定性,Dify 将于 2023 年 8 月 29 日 03:00 至 08:00 期间进行服务升级,届时 Dify 云端版及应用将无法访问。感谢您的耐心与支持。',
pt_BR: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
es_ES: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
fr_FR: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
de_DE: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
ja_JP: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
ko_KR: 'Our system will be unavailable from 19:00 to 24:00 UTC on August 28 for an upgrade. For questions, kindly contact our support team (support@dify.ai). We value your patience.',
uk_UA: 'Наша система буде недоступна з 19:00 до 24:00 UTC 28 серпня для оновлення. Якщо у вас виникнуть запитання, будь ласка, зв’яжіться з нашою службою підтримки (support@dify.ai). Дякуємо за терпіння.',
},
href: '#',
}

View file

@ -1,13 +1,36 @@
import 'server-only'
import { cookies, headers } from 'next/headers'
import Negotiator from 'negotiator'
import { match } from '@formatjs/intl-localematcher'
import type { Locale } from '.'
import { createInstance } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next'
import { i18n } from '.'
import type { Locale } from '.'
// https://locize.com/blog/next-13-app-dir-i18n/
const initI18next = async (lng: Locale, ns: string) => {
const i18nInstance = createInstance()
await i18nInstance
.use(initReactI18next)
.use(resourcesToBackend((language: string, namespace: string) => import(`./${language}/${namespace}.ts`)))
.init({
lng: lng === 'zh-Hans' ? 'zh-Hans' : lng,
ns,
fallbackLng: 'en-US',
})
return i18nInstance
}
export async function useTranslation(lng: Locale, ns = '', options: Record<string, any> = {}) {
const i18nextInstance = await initI18next(lng, ns)
return {
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
i18n: i18nextInstance,
}
}
export const getLocaleOnServer = (): Locale => {
// @ts-expect-error locales are readonly
const locales: string[] = i18n.locales
let languages: string[] | undefined