feat(i18n): Add i18next and lobe-i18n internationalization configuration files and update dependencies

This commit introduces new configuration files for internationalization,
deletes one file, modifies existing files, and adds new files. The changes
also involve importing dependencies and updating app and document pages.
Additional libraries and modules are used in a React application. The Header
component is modified, the index page is updated for server-side translations,
and the tsconfig.json file is modified.

Description:
- Added configuration files for internationalization
- Deleted one file
- Modified existing files
- Added new files
- Imported dependencies
- Updated app and document pages
- Used additional libraries and modules in a React application
- Modified the Header component
- Updated the index page for server-side translations
- Modified the tsconfig.json file.
This commit is contained in:
canisminor1990
2023-07-15 18:22:47 +08:00
parent 4e522a6e2b
commit 53cd87c8ba
19 changed files with 181 additions and 14 deletions

12
.i18nrc.js Normal file
View File

@@ -0,0 +1,12 @@
const { description } = require('./package.json');
module.exports = {
reference: description,
entry: 'public/locales/zh_CN',
entryLocale: 'zh_CN',
output: 'public/locales',
outputLocales: ['zh_HK', 'en_US', 'ja_JP', 'ko_KR'],
splitToken: 2500,
temperature: 0,
modelName: 'gpt-3.5-turbo',
};

View File

@@ -1,3 +0,0 @@
module.exports = {
extends: ['gitmoji'],
};

13
next-i18next.config.js Normal file
View File

@@ -0,0 +1,13 @@
const i18n = require('./.i18nrc');
/** @type {import('next-i18next').UserConfig} */
module.exports = {
debug: process.env.NODE_ENV === 'development',
i18n: {
defaultLocale: i18n.entryLocale,
locales: [i18n.entryLocale, i18n.outputLocales],
},
localePath:
typeof window === 'undefined' ? require('node:path').resolve('./', i18n.output) : '/locales',
reloadOnPrerender: process.env.NODE_ENV === 'development',
};

22
next-utils.config.js Normal file
View File

@@ -0,0 +1,22 @@
const pc = require('picocolors');
const nextUtilsConfig = () => {
const trueEnv = ['true', '1', 'yes'];
const esmExternals = trueEnv.includes(process.env?.NEXTJS_ESM_EXTERNALS ?? 'false');
const tsconfigPath = process.env.NEXTJS_TSCONFIG_PATH
? process.env.NEXTJS_TSCONFIG_PATH
: './tsconfig.json';
// eslint-disable-next-line no-console
console.warn(
`${pc.green('warn -')} experimental.esmExternals is ${esmExternals ? 'enabled' : 'disabled'}`,
);
return {
esmExternals,
tsconfigPath,
};
};
module.exports = {
loadCustomBuildParams: nextUtilsConfig,
};

View File

@@ -1,11 +1,22 @@
// @ts-check
import i18nConfig from './next-i18next.config.js';
import utilsConfig from './next-utils.config.js';
const API_END_PORT_URL = process.env.API_END_PORT_URL || '';
const { esmExternals = false, tsconfigPath } = utilsConfig.loadCustomBuildParams();
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
esmExternals, // https://nextjs.org/blog/next-11-1#es-modules-support
},
i18n: i18nConfig.i18n,
reactStrictMode: true,
pageExtensions: ['page.tsx', 'api.ts'],
transpilePackages: ['@lobehub/ui', 'antd-style'],
typescript: {
tsconfigPath,
},
webpack(config) {
config.experiments = {
asyncWebAssembly: true,

View File

@@ -23,6 +23,7 @@
"scripts": {
"build": "next build",
"dev": "next dev -p 3010",
"i18n": "lobe-i18n",
"lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
"lint:md": "remark . --quiet --frail --output",
"lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
@@ -34,6 +35,7 @@
"test": "vitest --passWithNoTests",
"test:coverage": "vitest run --coverage --passWithNoTests",
"test:update": "vitest -u",
"toc": "i18next-resources-for-ts toc -i ./public/locales/zh_CN -o ./src/types/resources.ts",
"type-check": "tsc --noEmit"
},
"lint-staged": {
@@ -75,6 +77,7 @@
"lucide-react": "latest",
"nanoid": "^4",
"next": "13.4.7",
"next-i18next": "^14",
"polished": "^4",
"react": "^18",
"react-dom": "^18",
@@ -88,6 +91,7 @@
},
"devDependencies": {
"@commitlint/cli": "^17",
"@lobehub/i18n-cli": "latest",
"@lobehub/lint": "latest",
"@next/eslint-plugin-next": "^13",
"@testing-library/jest-dom": "^5",
@@ -104,10 +108,12 @@
"commitlint": "^17",
"eslint": "^8",
"husky": "^8",
"i18next-resources-for-ts": "^1",
"jsdom": "^22",
"lint-staged": "^13",
"next-pwa": "^5",
"node-fetch": "^3",
"picocolors": "^1",
"postcss-styled-syntax": "^0.4",
"prettier": "^2",
"remark": "^14",

View File

@@ -0,0 +1,4 @@
{
"setting": "Setting",
"share": "Share"
}

View File

@@ -0,0 +1,4 @@
{
"setting": "設定",
"share": "共有する"
}

View File

@@ -0,0 +1,4 @@
{
"setting": "설정",
"share": "공유"
}

View File

@@ -0,0 +1,4 @@
{
"setting": "设置",
"share": "分享"
}

View File

@@ -0,0 +1,4 @@
{
"setting": "設置",
"share": "分享"
}

18
src/i18n/index.ts Normal file
View File

@@ -0,0 +1,18 @@
import type { Namespace } from 'i18next';
import type { SSRConfig, UserConfig } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import nextI18nextConfig from '@/../next-i18next.config';
type ArrayElementOrSelf<T> = T extends Array<infer U> ? U[] : T[];
export const getServerTranslations = async (
locale: string,
namespacesRequired?: ArrayElementOrSelf<Namespace> | undefined,
configOverride?: UserConfig,
extraLocales?: string[] | false,
): Promise<SSRConfig> => {
const config = configOverride ?? nextI18nextConfig;
// @ts-ignore
return serverSideTranslations(locale, namespacesRequired, config, extraLocales);
};

View File

@@ -1,15 +1,19 @@
import { Analytics } from '@vercel/analytics/react';
import { appWithTranslation } from 'next-i18next';
import type { AppProps } from 'next/app';
import { Suspense } from 'react';
import Layout from '@/layout';
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<Component {...pageProps} />
<Analytics />
</Layout>
<Suspense fallback="loading">
<Layout>
<Component {...pageProps} />
<Analytics />
</Layout>
</Suspense>
);
}
export default MyApp;
export default appWithTranslation(MyApp);

View File

@@ -1,6 +1,8 @@
import { StyleProvider, extractStaticStyle } from 'antd-style';
import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';
import i18nextConfig from '../../next-i18next.config.js';
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const page = await ctx.renderPage({
@@ -28,9 +30,37 @@ class MyDocument extends Document {
}
render() {
const currentLocale = this.props.__NEXT_DATA__.locale ?? i18nextConfig.i18n.defaultLocale;
return (
<Html lang="en">
<Head></Head>
<Html lang={currentLocale}>
<Head>
<link
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/apple-touch-icon.png"
rel="apple-touch-icon"
sizes="180x180"
/>
<link
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-32x32.png"
rel="icon"
sizes="32x32"
type="image/png"
/>
<link
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/favicon-16x16.png"
rel="icon"
sizes="16x16"
type="image/png"
/>
<link
color="#000000"
href="https://npm.elemecdn.com/@lobehub/assets-favicons/assets/safari-pinned-tab.svg"
rel="mask-icon"
/>
<meta content="LobeHub" name="apple-mobile-web-app-title" />
<meta content="LobeHub" name="application-name" />
<meta content="#000000" name="msapplication-TileColor" />
<meta content="#000000" name="theme-color" />
</Head>
<body>
<Main />
<NextScript />

View File

@@ -1,6 +1,7 @@
import { ActionIcon, Avatar } from '@lobehub/ui';
import { createStyles, useTheme } from 'antd-style';
import { ArchiveIcon, MoreVerticalIcon, Share2Icon } from 'lucide-react';
import { useTranslation } from 'next-i18next';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
@@ -19,6 +20,7 @@ const useStyles = createStyles(({ css, token }) => ({
}));
const Header = memo(() => {
const theme = useTheme();
const { t } = useTranslation('common');
const meta = useChatStore((s) => {
const chat = sessionSelectors.currentSession(s);
return chat?.meta;
@@ -62,7 +64,7 @@ const Header = memo(() => {
onClick={() => {
// genShareUrl();
}}
title={'分享'}
title={t('share')}
/>
<ActionIcon icon={ArchiveIcon} title={'归档'} />
<ActionIcon icon={MoreVerticalIcon} onClick={toggleConfig} />

View File

@@ -1 +1,17 @@
export { default } from './chat/index.page';
import type { GetStaticProps, InferGetStaticPropsType } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Chat from './chat/index.page';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Index = (_props: InferGetStaticPropsType<typeof getStaticProps>) => {
return <Chat />;
};
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale ?? 'zh_CN', ['common'])),
},
});
export default Index;

8
src/types/i18next.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
import resources from '@/types/resources';
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: typeof resources;
}
}

7
src/types/resources.ts Normal file
View File

@@ -0,0 +1,7 @@
import common from '@/../public/locales/zh_CN/common.json';
const resources = {
common,
} as const;
export default resources;

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
@@ -21,5 +22,5 @@
}
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
"include": ["next-env.d.ts", "**/*.ts", "**/*.d.ts", "**/*.tsx"]
}