feat: init

This commit is contained in:
Joel
2023-04-14 20:35:08 +08:00
parent 8acd8f6fbd
commit 97d3a6277d
77 changed files with 5299 additions and 0 deletions

18
i18n/client.ts Normal file
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
}