提交 3210f089 编写于 作者: P Peter Pan

we can use next-i18next in serverless mode now

上级 0eca8a87
import React, {FunctionComponent} from 'react'; import React, {FunctionComponent} from 'react';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import {NodeType, TypedNode} from '~/resource/graph'; import {NodeType, TypedNode} from '~/resource/graph';
import styled from 'styled-components'; import styled from 'styled-components';
import {WithStyled} from '~/utils/style'; import {WithStyled} from '~/utils/style';
......
import React, {FunctionComponent, useEffect, useState, useRef} from 'react'; import React, {FunctionComponent, useEffect, useState, useRef} from 'react';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import fetch from 'isomorphic-unfetch'; import fetch from 'isomorphic-unfetch';
type ImageProps = { type ImageProps = {
......
import React, {FunctionComponent} from 'react'; import React, {FunctionComponent} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import Link from 'next/link'; import {useTranslation, Link} from '~/utils/i18n';
import {useTranslation} from 'react-i18next';
import {rem, headerColor, duration, easing, lighten, transitions} from '~/utils/style'; import {rem, headerColor, duration, easing, lighten, transitions} from '~/utils/style';
const navItems = ['scalars', 'samples', 'graphs', 'high-dimensional']; const navItems = ['scalars', 'samples', 'graphs', 'high-dimensional'];
......
import React, {FunctionComponent} from 'react'; import React, {FunctionComponent} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import Select, {SelectValueType} from '~/components/Select'; import Select, {SelectValueType} from '~/components/Select';
const Title = styled.div` const Title = styled.div`
......
import React, {FunctionComponent, useState, useCallback} from 'react'; import React, {FunctionComponent, useState, useCallback} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import Button from '~/components/Button'; import Button from '~/components/Button';
const StyledButton = styled(Button)` const StyledButton = styled(Button)`
......
import React, {FunctionComponent, useState} from 'react'; import React, {FunctionComponent, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import useSWR from 'swr'; import useSWR from 'swr';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import {em, size, ellipsis, textLightColor} from '~/utils/style'; import {em, size, ellipsis, textLightColor} from '~/utils/style';
import StepSlider from '~/components/StepSlider'; import StepSlider from '~/components/StepSlider';
import Image from '~/components/Image'; import Image from '~/components/Image';
......
...@@ -7,7 +7,7 @@ import maxBy from 'lodash/maxBy'; ...@@ -7,7 +7,7 @@ import maxBy from 'lodash/maxBy';
import sortBy from 'lodash/sortBy'; import sortBy from 'lodash/sortBy';
import {EChartOption} from 'echarts'; import {EChartOption} from 'echarts';
import {em, size} from '~/utils/style'; import {em, size} from '~/utils/style';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import {cycleFetcher} from '~/utils/fetch'; import {cycleFetcher} from '~/utils/fetch';
import {transform, range, tooltip, TooltipData} from '~/utils/scalars'; import {transform, range, tooltip, TooltipData} from '~/utils/scalars';
import * as chart from '~/utils/chart'; import * as chart from '~/utils/chart';
......
import React, {FunctionComponent, useState, useCallback, useEffect} from 'react'; import React, {FunctionComponent, useState, useCallback, useEffect} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import without from 'lodash/without'; import without from 'lodash/without';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import useClickOutside from '~/hooks/useClickOutside'; import useClickOutside from '~/hooks/useClickOutside';
import { import {
WithStyled, WithStyled,
......
import React, {FunctionComponent, useState} from 'react'; import React, {FunctionComponent, useState} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import Field from '~/components/Field'; import Field from '~/components/Field';
import RangeSlider from '~/components/RangeSlider'; import RangeSlider from '~/components/RangeSlider';
......
import React, {FunctionComponent, useState, useEffect} from 'react'; import React, {FunctionComponent, useState, useEffect} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import {em, textLightColor} from '~/utils/style'; import {em, textLightColor} from '~/utils/style';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import RangeSlider from '~/components/RangeSlider'; import RangeSlider from '~/components/RangeSlider';
const Label = styled.div` const Label = styled.div`
......
...@@ -2,7 +2,7 @@ import React, {FunctionComponent, useState, useCallback, useEffect} from 'react' ...@@ -2,7 +2,7 @@ import React, {FunctionComponent, useState, useCallback, useEffect} from 'react'
import styled from 'styled-components'; import styled from 'styled-components';
import groupBy from 'lodash/groupBy'; import groupBy from 'lodash/groupBy';
import sortBy from 'lodash/sortBy'; import sortBy from 'lodash/sortBy';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
import {rem, math, ellipsis} from '~/utils/style'; import {rem, math, ellipsis} from '~/utils/style';
import SearchInput from '~/components/SearchInput'; import SearchInput from '~/components/SearchInput';
import Tag from '~/components/Tag'; import Tag from '~/components/Tag';
......
import {useRef, useEffect, useCallback, MutableRefObject} from 'react'; import {useRef, useEffect, useCallback, MutableRefObject} from 'react';
import echarts, {ECharts} from 'echarts'; import echarts, {ECharts} from 'echarts';
import {useTranslation} from 'react-i18next'; import {useTranslation} from '~/utils/i18n';
const useECharts = <T extends HTMLElement>(options: { const useECharts = <T extends HTMLElement>(options: {
loading: boolean; loading: boolean;
......
...@@ -15,6 +15,7 @@ const APP = { ...@@ -15,6 +15,7 @@ const APP = {
}; };
module.exports = { module.exports = {
target: 'serverless',
assetPrefix: publicPath, assetPrefix: publicPath,
distDir: 'dist', distDir: 'dist',
poweredByHeader: false, poweredByHeader: false,
......
...@@ -39,20 +39,15 @@ ...@@ -39,20 +39,15 @@
"echarts": "4.6.0", "echarts": "4.6.0",
"echarts-gl": "1.1.1", "echarts-gl": "1.1.1",
"express": "4.17.1", "express": "4.17.1",
"i18next": "19.3.2",
"i18next-browser-languagedetector": "4.0.2",
"i18next-chained-backend": "2.0.1",
"i18next-localstorage-backend": "3.1.1",
"i18next-xhr-backend": "3.2.2",
"isomorphic-unfetch": "3.0.0", "isomorphic-unfetch": "3.0.0",
"lodash": "4.17.15", "lodash": "4.17.15",
"moment": "2.24.0", "moment": "2.24.0",
"next": "9.2.2", "next": "9.2.2",
"next-i18next": "4.2.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"polished": "3.4.4", "polished": "3.4.4",
"react": "16.13.0", "react": "16.13.0",
"react-dom": "16.13.0", "react-dom": "16.13.0",
"react-i18next": "11.3.3",
"react-input-range": "1.3.0", "react-input-range": "1.3.0",
"react-is": "16.13.0", "react-is": "16.13.0",
"save-svg-as-png": "1.4.17", "save-svg-as-png": "1.4.17",
......
import '~/public/style/vdl-icon.css'; import '~/public/style/vdl-icon.css';
import '~/utils/i18n';
import React from 'react'; import React from 'react';
import Router from 'next/router';
import App from 'next/app'; import App from 'next/app';
import Head from 'next/head'; import Head from 'next/head';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import {SWRConfig} from 'swr'; import {SWRConfig} from 'swr';
import {Router, appWithTranslation} from '~/utils/i18n';
import {fetcher} from '~/utils/fetch'; import {fetcher} from '~/utils/fetch';
import {GlobalStyle} from '~/utils/style'; import {GlobalStyle} from '~/utils/style';
import Layout from '~/components/Layout'; import Layout from '~/components/Layout';
...@@ -48,4 +47,4 @@ class VDLApp extends App { ...@@ -48,4 +47,4 @@ class VDLApp extends App {
} }
} }
export default VDLApp; export default appWithTranslation(VDLApp);
import Document, {Head, Main, NextScript, DocumentContext} from 'next/document'; import Document, {Head, Main, NextScript, DocumentContext, DocumentProps} from 'next/document';
import {ServerStyleSheet} from '~/utils/style'; import {ServerStyleSheet} from '~/utils/style';
export default class VDLDocument extends Document { interface VDLDocumentProps extends DocumentProps {
language: string;
}
export default class VDLDocument extends Document<VDLDocumentProps> {
static async getInitialProps(ctx: DocumentContext) { static async getInitialProps(ctx: DocumentContext) {
// https://github.com/zeit/next.js/blob/canary/examples/with-typescript-styled-components/pages/_document.tsx // https://github.com/zeit/next.js/blob/canary/examples/with-typescript-styled-components/pages/_document.tsx
const sheet = new ServerStyleSheet(); const sheet = new ServerStyleSheet();
...@@ -15,8 +19,16 @@ export default class VDLDocument extends Document { ...@@ -15,8 +19,16 @@ export default class VDLDocument extends Document {
const initialProps = await Document.getInitialProps(ctx); 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'
};
return { return {
...initialProps, ...initialProps,
...additionalProps,
styles: ( styles: (
<> <>
{initialProps.styles} {initialProps.styles}
...@@ -30,8 +42,9 @@ export default class VDLDocument extends Document { ...@@ -30,8 +42,9 @@ export default class VDLDocument extends Document {
} }
render() { render() {
const {language} = this.props;
return ( return (
<html> <html lang={language}>
<Head /> <Head />
<body> <body>
<Main /> <Main />
......
import React from 'react'; import React from 'react';
import {useTranslation} from 'react-i18next'; import {useTranslation, NextI18NextPage} from '~/utils/i18n';
import {NextPage} from 'next';
interface ErrorProps { interface ErrorProps {
statusCode?: number | null; statusCode?: number | null;
} }
const Error: NextPage<ErrorProps> = ({statusCode}) => { const Error: NextI18NextPage<ErrorProps> = ({statusCode}) => {
const {t} = useTranslation('errors'); const {t} = useTranslation('errors');
return <p>{statusCode ? t('error-with-status', {statusCode}) : t('error-without-status')}</p>; return <p>{statusCode ? t('error-with-status', {statusCode}) : t('error-without-status')}</p>;
...@@ -20,6 +19,7 @@ Error.getInitialProps = ({res, err}) => { ...@@ -20,6 +19,7 @@ Error.getInitialProps = ({res, err}) => {
({statusCode} = err); ({statusCode} = err);
} }
return { return {
namespacesRequired: ['errors'],
statusCode statusCode
}; };
}; };
......
import React, {useState, useEffect, useMemo} from 'react'; import React, {useState, useEffect, useMemo} from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import styled from 'styled-components'; import styled from 'styled-components';
import {NextPage} from 'next';
import RawButton from '~/components/Button'; import RawButton from '~/components/Button';
import RawRangeSlider from '~/components/RangeSlider'; import RawRangeSlider from '~/components/RangeSlider';
import Content from '~/components/Content'; import Content from '~/components/Content';
import Title from '~/components/Title'; import Title from '~/components/Title';
import Field from '~/components/Field'; import Field from '~/components/Field';
import {useTranslation} from 'react-i18next'; import {useTranslation, NextI18NextPage} from '~/utils/i18n';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import {fetcher} from '~/utils/fetch'; import {fetcher} from '~/utils/fetch';
import NodeInfo, {NodeInfoProps} from '~/components/GraphPage/NodeInfo'; import NodeInfo, {NodeInfoProps} from '~/components/GraphPage/NodeInfo';
...@@ -240,7 +239,7 @@ const useDagreD3 = (graph: Graph | undefined) => { ...@@ -240,7 +239,7 @@ const useDagreD3 = (graph: Graph | undefined) => {
return {currentNode, displaySwitch, setDisplaySwitch, downloadImage, fitScreen, scale, setScale}; return {currentNode, displaySwitch, setDisplaySwitch, downloadImage, fitScreen, scale, setScale};
}; };
const Graphs: NextPage = () => { const Graphs: NextI18NextPage = () => {
const {t} = useTranslation(['graphs', 'common']); const {t} = useTranslation(['graphs', 'common']);
const {data: graph} = useSWR<{data: Graph}>('/graphs/graph', fetcher); const {data: graph} = useSWR<{data: Graph}>('/graphs/graph', fetcher);
const {currentNode, downloadImage, fitScreen, scale, setScale} = useDagreD3(graph ? graph.data : undefined); const {currentNode, downloadImage, fitScreen, scale, setScale} = useDagreD3(graph ? graph.data : undefined);
...@@ -282,4 +281,8 @@ const Graphs: NextPage = () => { ...@@ -282,4 +281,8 @@ const Graphs: NextPage = () => {
); );
}; };
Graphs.getInitialProps = () => ({
namespacesRequired: ['graphs', 'common']
});
export default Graphs; export default Graphs;
import React, {useState, useEffect, useMemo} from 'react'; import React, {useState, useEffect, useMemo} from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import useSWR from 'swr'; import useSWR from 'swr';
import {NextPage} from 'next';
import {useRouter} from 'next/router'; import {useRouter} from 'next/router';
import {rem, em} from '~/utils/style'; import {rem, em} from '~/utils/style';
import {useTranslation} from 'react-i18next'; import {useTranslation, NextI18NextPage} from '~/utils/i18n';
import Title from '~/components/Title'; import Title from '~/components/Title';
import Content from '~/components/Content'; import Content from '~/components/Content';
import SearchInput from '~/components/SearchInput'; import SearchInput from '~/components/SearchInput';
...@@ -43,7 +42,7 @@ type Data = { ...@@ -43,7 +42,7 @@ type Data = {
labels: string[]; labels: string[];
}; };
const HighDimensional: NextPage = () => { const HighDimensional: NextI18NextPage = () => {
const {t} = useTranslation(['high-dimensional', 'common']); const {t} = useTranslation(['high-dimensional', 'common']);
const {query} = useRouter(); const {query} = useRouter();
...@@ -143,4 +142,8 @@ const HighDimensional: NextPage = () => { ...@@ -143,4 +142,8 @@ const HighDimensional: NextPage = () => {
); );
}; };
HighDimensional.getInitialProps = () => ({
namespacesRequired: ['high-dimensional', 'common']
});
export default HighDimensional; export default HighDimensional;
import {useEffect} from 'react'; import {useEffect} from 'react';
import {NextPage} from 'next'; import {NextI18NextPage, Router} from '~/utils/i18n';
import Router from 'next/router';
const Index: NextPage = () => { const Index: NextI18NextPage = () => {
useEffect(() => { useEffect(() => {
Router.replace('/scalars'); Router.replace('/scalars');
}, []); }, []);
...@@ -10,4 +9,10 @@ const Index: NextPage = () => { ...@@ -10,4 +9,10 @@ const Index: NextPage = () => {
return null; return null;
}; };
Index.getInitialProps = () => {
return {
namespacesRequired: []
};
};
export default Index; export default Index;
import React, {useState, useCallback, useMemo} from 'react'; import React, {useState, useCallback, useMemo} from 'react';
import {useTranslation} from 'react-i18next';
import styled from 'styled-components'; import styled from 'styled-components';
import {NextPage} from 'next'; import {useTranslation, NextI18NextPage} from '~/utils/i18n';
import {rem, em} from '~/utils/style'; import {rem, em} from '~/utils/style';
import useTagFilter from '~/hooks/useTagFilter'; import useTagFilter from '~/hooks/useTagFilter';
import Title from '~/components/Title'; import Title from '~/components/Title';
...@@ -38,7 +37,7 @@ type Item = { ...@@ -38,7 +37,7 @@ type Item = {
label: string; label: string;
}; };
const Samples: NextPage = () => { const Samples: NextI18NextPage = () => {
const {t} = useTranslation(['samples', 'common']); const {t} = useTranslation(['samples', 'common']);
const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter('images'); const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter('images');
...@@ -113,4 +112,8 @@ const Samples: NextPage = () => { ...@@ -113,4 +112,8 @@ const Samples: NextPage = () => {
); );
}; };
Samples.getInitialProps = () => ({
namespacesRequired: ['samples', 'common']
});
export default Samples; export default Samples;
import React, {useState, useCallback} from 'react'; import React, {useState, useCallback} from 'react';
import {useTranslation} from 'react-i18next'; import {useTranslation, NextI18NextPage} from '~/utils/i18n';
import {NextPage} from 'next';
import useTagFilter from '~/hooks/useTagFilter'; import useTagFilter from '~/hooks/useTagFilter';
import Title from '~/components/Title'; import Title from '~/components/Title';
import Content from '~/components/Content'; import Content from '~/components/Content';
...@@ -21,7 +20,7 @@ const xAxisValues = ['step', 'relative', 'wall']; ...@@ -21,7 +20,7 @@ const xAxisValues = ['step', 'relative', 'wall'];
type TooltiopSorting = keyof typeof sortingMethodMap; type TooltiopSorting = keyof typeof sortingMethodMap;
const toolTipSortingValues = ['default', 'descending', 'ascending', 'nearest']; const toolTipSortingValues = ['default', 'descending', 'ascending', 'nearest'];
const Scalars: NextPage = () => { const Scalars: NextI18NextPage = () => {
const {t} = useTranslation(['scalars', 'common']); const {t} = useTranslation(['scalars', 'common']);
const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter('scalars'); const {runs, tags, selectedRuns, selectedTags, onChangeRuns, onFilterTags} = useTagFilter('scalars');
...@@ -93,4 +92,8 @@ const Scalars: NextPage = () => { ...@@ -93,4 +92,8 @@ const Scalars: NextPage = () => {
); );
}; };
Scalars.getInitialProps = () => ({
namespacesRequired: ['scalars', 'common']
});
export default Scalars; export default Scalars;
...@@ -2,6 +2,8 @@ import path from 'path'; ...@@ -2,6 +2,8 @@ import path from 'path';
import express from 'express'; import express from 'express';
import next from 'next'; import next from 'next';
import {setConfig} from 'next/config'; import {setConfig} from 'next/config';
import nextI18NextMiddleware from 'next-i18next/middleware';
import nextI18next from '../utils/i18n';
import config from '../next.config'; import config from '../next.config';
const isDev = process.env.NODE_ENV !== 'production'; const isDev = process.env.NODE_ENV !== 'production';
...@@ -21,6 +23,9 @@ const handle = app.getRequestHandler(); ...@@ -21,6 +23,9 @@ const handle = app.getRequestHandler();
server.use(config.env.API_URL, mock({path: path.resolve(__dirname, '../mock')})); server.use(config.env.API_URL, mock({path: path.resolve(__dirname, '../mock')}));
} }
await nextI18next.initPromise;
server.use(nextI18NextMiddleware(nextI18next));
server.get('*', (req, res) => handle(req, res)); server.get('*', (req, res) => handle(req, res));
server.listen(port); server.listen(port);
......
import i18n, {InitOptions} from 'i18next'; import {NextComponentType, NextPageContext} from 'next';
import {initReactI18next} from 'react-i18next'; import NextI18Next from 'next-i18next';
import Backend from 'i18next-chained-backend';
import LocalStorageBackend from 'i18next-localstorage-backend';
import XHR from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
const options: InitOptions = { const isDev = process.env.NODE_ENV === 'development';
react: {
useSuspense: false
},
load: 'languageOnly', const nextI18Next = new NextI18Next({
fallbackLng: 'en', localePath: 'public/locales',
defaultNS: 'common', browserLanguageDetection: !isDev,
ns: ['common'], serverLanguageDetection: !isDev,
defaultLanguage: 'en',
interpolation: { otherLanguages: ['zh'],
escapeValue: false // not needed for react as it escapes by default localeSubpaths: {
zh: 'zh'
} }
}; });
if (process.browser) {
i18n
// load translation using xhr -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-xhr-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
backend: {
backends: [LocalStorageBackend, XHR],
backendOptions: [
{
defaultVersion: '1' // TODO: use build id
},
{
loadPath: `${process.env.PUBLIC_PATH}/locales/{{lng}}/{{ns}}.json`,
allowMultiLoading: false,
crossDomain: true,
overrideMimeType: true
}
]
},
detection: {}, // from ~/node_modules/next/types/index.d.ts
// https://gitlab.com/kachkaev/website-frontend/-/blob/master/src/i18n.ts#L64-68
export type NextI18NextPage<P = {}, IP = P> = NextComponentType<
NextPageContext,
IP & {namespacesRequired: string[]},
P & {namespacesRequired: string[]}
>;
...options export default nextI18Next;
});
} else {
i18n.use(initReactI18next).init(options);
}
export default i18n; export const {i18n, appWithTranslation, withTranslation, useTranslation, Router, Link, Trans} = nextI18Next;
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册