mirror of
https://github.com/langgenius/webapp-conversation.git
synced 2025-12-08 17:32:27 +08:00
feat: init
This commit is contained in:
18
i18n/client.ts
Normal file
18
i18n/client.ts
Normal file
@ -0,0 +1,18 @@
|
||||
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, notReload?: boolean) => {
|
||||
Cookies.set(LOCALE_COOKIE_NAME, locale)
|
||||
changeLanguage(locale)
|
||||
if (!notReload) {
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
38
i18n/i18next-config.ts
Normal file
38
i18n/i18next-config.ts
Normal file
@ -0,0 +1,38 @@
|
||||
'use client'
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import commonEn from './lang/common.en'
|
||||
import commonZh from './lang/common.zh'
|
||||
import appEn from './lang/app.en'
|
||||
import appZh from './lang/app.zh'
|
||||
import { Locale } from '.'
|
||||
|
||||
const resources = {
|
||||
'en': {
|
||||
translation: {
|
||||
common: commonEn,
|
||||
app: appEn,
|
||||
},
|
||||
},
|
||||
'zh-Hans': {
|
||||
translation: {
|
||||
common: commonZh,
|
||||
app: appZh,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
i18n.use(initReactI18next)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
// debug: true,
|
||||
resources,
|
||||
})
|
||||
|
||||
export const changeLanguage = (lan: Locale) => {
|
||||
i18n.changeLanguage(lan)
|
||||
}
|
||||
export default i18n
|
||||
26
i18n/i18next-serverside-config.ts
Normal file
26
i18n/i18next-serverside-config.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { createInstance } from 'i18next'
|
||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
||||
import { 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
|
||||
}
|
||||
}
|
||||
6
i18n/index.ts
Normal file
6
i18n/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const i18n = {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'zh-Hans'],
|
||||
} as const
|
||||
|
||||
export type Locale = typeof i18n['locales'][number]
|
||||
33
i18n/lang/app.en.ts
Normal file
33
i18n/lang/app.en.ts
Normal file
@ -0,0 +1,33 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "Welcome to use",
|
||||
appUnavailable: "App is unavailable",
|
||||
appUnkonwError: "App is unavailable"
|
||||
},
|
||||
chat: {
|
||||
newChat: "New chat",
|
||||
newChatDefaultName: "New conversation",
|
||||
openingStatementTitle: "Opening statement",
|
||||
powerBy: "Powered by",
|
||||
prompt: "Prompt",
|
||||
privatePromptConfigTitle: "Conversation settings",
|
||||
publicPromptConfigTitle: "Initial Prompt",
|
||||
configStatusDes: "Before start, you can modify conversation settings",
|
||||
configDisabled:
|
||||
"Previous session settings have been used for this session.",
|
||||
startChat: "Start Chat",
|
||||
privacyPolicyLeft:
|
||||
"Please read the ",
|
||||
privacyPolicyMiddle:
|
||||
"privacy policy",
|
||||
privacyPolicyRight:
|
||||
" provided by the app developer.",
|
||||
},
|
||||
errorMessage: {
|
||||
valueOfVarRequired: "Variables value can not be empty",
|
||||
waitForResponse:
|
||||
"Please wait for the response to the previous message to complete.",
|
||||
},
|
||||
};
|
||||
|
||||
export default translation;
|
||||
28
i18n/lang/app.zh.ts
Normal file
28
i18n/lang/app.zh.ts
Normal file
@ -0,0 +1,28 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "欢迎使用",
|
||||
appUnavailable: "应用不可用",
|
||||
appUnkonwError: "应用不可用",
|
||||
},
|
||||
chat: {
|
||||
newChat: "新对话",
|
||||
newChatDefaultName: "新的对话",
|
||||
openingStatementTitle: "对话开场白",
|
||||
powerBy: "Powered by",
|
||||
prompt: "提示词",
|
||||
privatePromptConfigTitle: "对话设置",
|
||||
publicPromptConfigTitle: "对话前提示词",
|
||||
configStatusDes: "开始前,您可以修改对话设置",
|
||||
configDisabled: "此次会话已使用上次会话表单",
|
||||
startChat: "开始对话",
|
||||
privacyPolicyLeft: "请阅读由该应用开发者提供的",
|
||||
privacyPolicyMiddle: "隐私政策",
|
||||
privacyPolicyRight: "。",
|
||||
},
|
||||
errorMessage: {
|
||||
valueOfVarRequired: "变量值必填",
|
||||
waitForResponse: "请等待上条信息响应完成",
|
||||
},
|
||||
};
|
||||
|
||||
export default translation;
|
||||
22
i18n/lang/common.en.ts
Normal file
22
i18n/lang/common.en.ts
Normal file
@ -0,0 +1,22 @@
|
||||
const translation = {
|
||||
api: {
|
||||
success: 'Success',
|
||||
saved: 'Saved',
|
||||
create: 'Created',
|
||||
},
|
||||
operation: {
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
clear: 'Clear',
|
||||
save: 'Save',
|
||||
edit: 'Edit',
|
||||
refresh: 'Restart',
|
||||
search: 'Search',
|
||||
send: 'Send',
|
||||
lineBreak: 'Line break',
|
||||
like: 'like',
|
||||
dislike: 'dislike',
|
||||
}
|
||||
}
|
||||
|
||||
export default translation
|
||||
22
i18n/lang/common.zh.ts
Normal file
22
i18n/lang/common.zh.ts
Normal file
@ -0,0 +1,22 @@
|
||||
const translation = {
|
||||
api: {
|
||||
success: '成功',
|
||||
saved: '已保存',
|
||||
create: '已创建',
|
||||
},
|
||||
operation: {
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
clear: '清空',
|
||||
save: '保存',
|
||||
edit: '编辑',
|
||||
refresh: '重新开始',
|
||||
search: '搜索',
|
||||
send: '发送',
|
||||
lineBreak: '换行',
|
||||
like: '赞同',
|
||||
dislike: '反对',
|
||||
}
|
||||
}
|
||||
|
||||
export default translation
|
||||
29
i18n/server.ts
Normal file
29
i18n/server.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import 'server-only'
|
||||
|
||||
import { cookies, headers } from 'next/headers'
|
||||
import Negotiator from 'negotiator'
|
||||
import { match } from '@formatjs/intl-localematcher'
|
||||
import type { Locale } from '.'
|
||||
import { i18n } from '.'
|
||||
|
||||
export const getLocaleOnServer = (): Locale => {
|
||||
// @ts-expect-error locales are readonly
|
||||
const locales: string[] = i18n.locales
|
||||
|
||||
let languages: string[] | undefined
|
||||
// get locale from cookie
|
||||
const localeCookie = cookies().get('locale')
|
||||
languages = localeCookie?.value ? [localeCookie.value] : []
|
||||
|
||||
if (!languages.length) {
|
||||
// Negotiator expects plain object so we need to transform headers
|
||||
const negotiatorHeaders: Record<string, string> = {}
|
||||
headers().forEach((value, key) => (negotiatorHeaders[key] = value))
|
||||
// Use negotiator and intl-localematcher to get best locale
|
||||
languages = new Negotiator({ headers: negotiatorHeaders }).languages()
|
||||
}
|
||||
|
||||
// match locale
|
||||
const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale
|
||||
return matchedLocale
|
||||
}
|
||||
Reference in New Issue
Block a user