Feat/i18n restructure (#2529)
This commit is contained in:
parent
91ea6fe4ee
commit
9574730050
140 changed files with 448 additions and 659 deletions
175
web/i18n/README.md
Normal file
175
web/i18n/README.md
Normal 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`.
|
||||
|
|
@ -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 授权登录后,是否直接设置当前浏览器语言为界面语言
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
135
web/i18n/language.ts
Normal 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: '#',
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue