From 1f129d7aba101c8c834fbe2d4e624b8053238a64 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 5 Mar 2020 18:46:32 +0800 Subject: [PATCH] pack languages into static htmls --- frontend/components/Content.tsx | 2 ++ frontend/next.config.js | 17 +++++++++++++ frontend/pages/_app.tsx | 44 +++++++++++++++++++++++++++++++-- frontend/pages/_document.tsx | 8 +++--- frontend/pages/index.tsx | 2 +- frontend/utils/i18n.ts | 19 +++++++++----- 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/frontend/components/Content.tsx b/frontend/components/Content.tsx index 8f2b413b..08db0170 100644 --- a/frontend/components/Content.tsx +++ b/frontend/components/Content.tsx @@ -26,6 +26,8 @@ const Aside = styled.aside` position: fixed; top: ${headerHeight}; right: 0; + overflow-x: hidden; + overflow-y: auto; `; type ContentProps = { diff --git a/frontend/next.config.js b/frontend/next.config.js index 10c2db3d..33efac60 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -14,6 +14,11 @@ const APP = { keywords: pkg.keywords.join(',') }; +const DEFAULT_LANGUAGE = 'en'; +const LOCALE_PATH = 'public/locales'; +const LANGUAGES = ['en', 'zh']; +const otherLanguages = LANGUAGES.filter(lang => lang !== DEFAULT_LANGUAGE); + module.exports = { target: 'serverless', assetPrefix: publicPath, @@ -21,9 +26,21 @@ module.exports = { poweredByHeader: false, env: { ...APP, + DEFAULT_LANGUAGE, + LOCALE_PATH, + LANGUAGES, PUBLIC_PATH: publicPath, API_URL: apiUrl }, + exportPathMap: defaultPathMap => { + return { + ...defaultPathMap, + ...Object.entries(defaultPathMap).reduce((prev, [path, router]) => { + otherLanguages.forEach(lang => (prev[`/${lang}${path}`] = router)); + return prev; + }, {}) + }; + }, webpack: config => { config.resolve = config.resolve || {}; config.resolve.alias = config.resolve.alias || {}; diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 902c83f6..eaf78325 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,19 +1,59 @@ import '~/public/style/vdl-icon.css'; import React from 'react'; -import App from 'next/app'; +import {NextPageContext} from 'next'; +import App, {AppContext} from 'next/app'; import Head from 'next/head'; import NProgress from 'nprogress'; import {SWRConfig} from 'swr'; -import {Router, appWithTranslation} from '~/utils/i18n'; +import {i18n, Router, appWithTranslation} from '~/utils/i18n'; import {fetcher} from '~/utils/fetch'; import {GlobalStyle} from '~/utils/style'; import Layout from '~/components/Layout'; +import {I18n} from 'next-i18next'; Router.events.on('routeChangeStart', () => NProgress.start()); Router.events.on('routeChangeComplete', () => NProgress.done()); Router.events.on('routeChangeError', () => NProgress.done()); +type I18nReq = { + i18n?: I18n; + locale?: string; + lng?: string; + language?: string; +}; + +type I18nRes = { + locals?: { + language?: string; + languageDir?: string; + }; +}; + class VDLApp extends App { + static async getInitialProps(appContext: AppContext) { + const req = appContext.ctx.req as (NextPageContext['req'] & I18nReq) | undefined; + if (req && !req.i18n) { + const {router} = appContext; + const result = router.asPath.match(/^\/(.*?)\//); + const lng = result ? result[1] : process.env.DEFAULT_LANGUAGE; + req.i18n = i18n.cloneInstance({initImmediate: false, lng}); + const res = appContext.ctx.res as (NextPageContext['res'] & I18nRes) | undefined; + const setContextLocale = (lng?: string) => { + // SEE: i18n-express-middleware + req.language = req.locale = req.lng = lng; + if (res) { + res.locals = res.locals || {}; + res.locals.language = lng; + res.locals.languageDir = i18n.dir(lng); + } + }; + setContextLocale(lng); + i18n.on('languageChanged', setContextLocale); + } + const appProps = await App.getInitialProps(appContext); + return {...appProps}; + } + render() { const {Component, pageProps} = this.props; diff --git a/frontend/pages/_document.tsx b/frontend/pages/_document.tsx index 15089c7e..02b99c29 100644 --- a/frontend/pages/_document.tsx +++ b/frontend/pages/_document.tsx @@ -3,6 +3,7 @@ import {ServerStyleSheet} from '~/utils/style'; interface VDLDocumentProps extends DocumentProps { language: string; + languageDir: string; } export default class VDLDocument extends Document { @@ -18,12 +19,11 @@ export default class VDLDocument extends Document { }); const initialProps = await Document.getInitialProps(ctx); - // stealed from https://github.com/isaachinman/next-i18next/issues/20#issuecomment-558799264 // FIXME: https://github.com/i18next/i18next-express-middleware/blob/master/src/index.js#L23-L26 const additionalProps = { // eslint-disable-next-line @typescript-eslint/no-explicit-any - language: (ctx.res as any)?.locals?.language ?? 'en' + ...((ctx.res as any)?.locals || {}) }; return { @@ -42,9 +42,9 @@ export default class VDLDocument extends Document { } render() { - const {language} = this.props; + const {language, languageDir} = this.props; return ( - +
diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index c9241f73..314b26e3 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -11,7 +11,7 @@ const Index: NextI18NextPage = () => { Index.getInitialProps = () => { return { - namespacesRequired: [] + namespacesRequired: ['common'] }; }; diff --git a/frontend/utils/i18n.ts b/frontend/utils/i18n.ts index 06bb77f1..062f25f7 100644 --- a/frontend/utils/i18n.ts +++ b/frontend/utils/i18n.ts @@ -1,17 +1,24 @@ import {NextComponentType, NextPageContext} from 'next'; import NextI18Next from 'next-i18next'; +import {env} from '../next.config'; + +const defaultLanguage = env.DEFAULT_LANGUAGE; +const allLanguages = env.LANGUAGES; +const otherLanguages = allLanguages.filter(lang => lang !== defaultLanguage); const isDev = process.env.NODE_ENV === 'development'; const nextI18Next = new NextI18Next({ - localePath: 'public/locales', + localePath: env.LOCALE_PATH, browserLanguageDetection: !isDev, serverLanguageDetection: !isDev, - defaultLanguage: 'en', - otherLanguages: ['zh'], - localeSubpaths: { - zh: 'zh' - } + cleanCode: true, + defaultLanguage, + otherLanguages, + localeSubpaths: otherLanguages.reduce((prev, curr) => { + prev[curr] = curr; + return prev; + }, {} as Record) }); // from ~/node_modules/next/types/index.d.ts -- GitLab