Skip to content

Commit fb04a32

Browse files
committed
[optimize] simplify Internationalization with MobX-i18n 0.7 & other related packages
1 parent 3193dc8 commit fb04a32

File tree

8 files changed

+127
-109
lines changed

8 files changed

+127
-109
lines changed

components/LanguageMenu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const LanguageMenu: FC = observer(() => {
1010
return (
1111
<Select
1212
value={i18n.currentLanguage}
13-
onChange={key => i18n.changeLanguage(key as typeof i18n.currentLanguage)}
13+
onChange={key => i18n.loadLanguages(key as typeof i18n.currentLanguage)}
1414
>
1515
{Object.entries(LanguageName).map(([key, name]) => (
1616
<Option key={key} value={key}>

components/NotFoundCard.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { observer } from 'mobx-react';
12
import { ErrorProps } from 'next/error';
23
import { FC, useContext } from 'react';
34

45
import { I18nContext } from '../models/Translation';
56

6-
export const NotFoundCard: FC<ErrorProps> = ({ title }) => {
7+
export const NotFoundCard: FC<ErrorProps> = observer(({ title }) => {
78
const { currentLanguage } = useContext(I18nContext);
89

910
return currentLanguage.startsWith('zh') ? (
@@ -20,4 +21,4 @@ export const NotFoundCard: FC<ErrorProps> = ({ title }) => {
2021
src="https://notfound-static.fwebservices.be/en/404?key=66abb751ed312"
2122
/>
2223
);
23-
};
24+
});

models/Translation.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
import { IncomingHttpHeaders } from 'http';
21
import {
3-
parseCookie,
4-
parseLanguageHeader,
2+
loadLanguageMapFrom,
53
TranslationMap,
64
TranslationModel,
75
} from 'mobx-i18n';
86
import { createContext } from 'react';
97

108
import zhCN from '../translation/zh-CN';
119

12-
export const i18nData = {
10+
const i18nData = {
1311
'zh-CN': zhCN,
1412
'zh-TW': () => import('../translation/zh-TW'),
1513
'en-US': () => import('../translation/en-US'),
1614
};
1715
export type LanguageCode = keyof typeof i18nData;
1816

17+
export interface I18nProps {
18+
language: LanguageCode;
19+
languageMap: typeof zhCN;
20+
}
21+
1922
export const createI18nStore = <N extends LanguageCode, K extends string>(
2023
language?: N,
2124
data?: TranslationMap<K>,
22-
) =>
23-
new TranslationModel({ ...i18nData, ...(language && { [language]: data }) });
25+
) => {
26+
const store = new TranslationModel({
27+
...i18nData,
28+
...(language && { [language]: data }),
29+
});
30+
31+
if (language) store.currentLanguage = language;
32+
33+
return store;
34+
};
2435

2536
export const i18n = createI18nStore();
2637

@@ -32,16 +43,4 @@ export const LanguageName: Record<LanguageCode, string> = {
3243

3344
export const I18nContext = createContext(i18n);
3445

35-
export const loadLanguage = async ({
36-
'accept-language': acceptLanguage,
37-
cookie,
38-
}: IncomingHttpHeaders) => {
39-
const { language } = parseCookie(cookie || ''),
40-
languages = parseLanguageHeader(acceptLanguage || '');
41-
42-
const languageData = await i18n.loadLanguages(
43-
[language, ...languages].filter(Boolean),
44-
);
45-
46-
return { language: i18n.currentLanguage, languageData };
47-
};
46+
export const loadSSRLanguage = loadLanguageMapFrom.bind(null, i18nData);

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"@editorjs/list": "^2.0.8",
1616
"@editorjs/paragraph": "^2.11.7",
1717
"@editorjs/quote": "^2.7.6",
18-
"@koa/bodyparser": "^5.1.1",
1918
"@koa/router": "^13.1.0",
2019
"@mdx-js/loader": "^3.1.0",
2120
"@mdx-js/react": "^3.1.0",
@@ -33,13 +32,14 @@
3332
"lodash": "^4.17.21",
3433
"mobx": "^6.13.7",
3534
"mobx-github": "^0.3.5",
36-
"mobx-i18n": "^0.6.0",
35+
"mobx-i18n": "^0.7.0",
3736
"mobx-react": "^9.2.0",
37+
"mobx-react-helper": "^0.3.6",
3838
"mobx-restful": "^2.1.0",
39-
"mobx-restful-table": "^2.2.1",
39+
"mobx-restful-table": "^2.2.2",
4040
"next": "^15.3.1",
4141
"next-pwa": "~5.6.0",
42-
"next-ssr-middleware": "^0.10.0",
42+
"next-ssr-middleware": "^1.0.0",
4343
"next-with-less": "^3.0.1",
4444
"prismjs": "^1.30.0",
4545
"react": "^19.1.0",

pages/_app.tsx

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import '../styles/globals.less';
33
import { HTTPError } from 'koajax';
44
import { configure } from 'mobx';
55
import { enableStaticRendering, observer } from 'mobx-react';
6-
import App, { AppContext, AppProps } from 'next/app';
6+
import App, { AppContext } from 'next/app';
77
import Head from 'next/head';
88
import { Image } from 'react-bootstrap';
99

@@ -13,10 +13,9 @@ import { isServer } from '../models/configuration';
1313
import {
1414
createI18nStore,
1515
I18nContext,
16-
LanguageCode,
17-
loadLanguage,
16+
I18nProps,
17+
loadSSRLanguage,
1818
} from '../models/Translation';
19-
import zhCN from '../translation/zh-CN';
2019

2120
configure({ enforceActions: 'never' });
2221

@@ -31,20 +30,16 @@ globalThis.addEventListener?.('unhandledrejection', ({ reason }) => {
3130
if (tips) alert(tips);
3231
});
3332

34-
interface AppShellProps extends AppProps {
35-
language: LanguageCode;
36-
languageData: typeof zhCN;
37-
}
38-
3933
@observer
40-
export default class AppShell extends App<AppShellProps> {
34+
export default class AppShell extends App<I18nProps> {
4135
static async getInitialProps(context: AppContext) {
42-
const props = await App.getInitialProps(context);
43-
44-
return { ...props, ...(await loadLanguage(context.ctx.req!.headers)) };
36+
return {
37+
...(await App.getInitialProps(context)),
38+
...(await loadSSRLanguage(context.ctx.req!.headers)),
39+
};
4540
}
4641

47-
i18nStore = createI18nStore(this.props.language, this.props.languageData);
42+
i18nStore = createI18nStore(this.props.language, this.props.languageMap);
4843

4944
render() {
5045
const { Component, pageProps, router } = this.props,

pages/_error.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
import * as Sentry from '@sentry/nextjs';
2-
import type { NextPage } from 'next';
3-
import type { ErrorProps } from 'next/error';
2+
import type { NextPageContext } from 'next';
43
import Error from 'next/error';
54

65
import { NotFoundCard } from '../components/NotFoundCard';
6+
import {
7+
createI18nStore,
8+
I18nContext,
9+
I18nProps,
10+
loadSSRLanguage,
11+
} from '../models/Translation';
712

8-
const CustomErrorComponent: NextPage<ErrorProps> = props => (
9-
<>
10-
<Error {...props} />
11-
12-
<NotFoundCard {...props} />
13-
</>
14-
);
1513
const enableSentry =
1614
process.env.NODE_ENV === 'development' || !process.env.SENTRY_AUTH_TOKEN;
1715

18-
CustomErrorComponent.getInitialProps = async contextData => {
19-
if (enableSentry) await Sentry.captureUnderscoreErrorException(contextData);
16+
export default class CustomError extends Error<I18nProps> {
17+
static async getInitialProps(context: NextPageContext) {
18+
if (enableSentry) await Sentry.captureUnderscoreErrorException(context);
19+
20+
return {
21+
...(await Error.getInitialProps(context)),
22+
...(await loadSSRLanguage(context.req!.headers)),
23+
};
24+
}
25+
26+
i18nStore = createI18nStore(this.props.language, this.props.languageMap);
2027

21-
return Error.getInitialProps(contextData);
22-
};
28+
render() {
29+
const { props, i18nStore } = this;
2330

24-
export default CustomErrorComponent;
31+
return (
32+
<I18nContext.Provider value={i18nStore}>
33+
<Error {...props} />
34+
<NotFoundCard {...props} />
35+
</I18nContext.Provider>
36+
);
37+
}
38+
}

pages/pagination.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
import { text2color } from 'idea-react';
2-
import { computed, observable } from 'mobx';
2+
import { computed } from 'mobx';
33
import { GitRepository } from 'mobx-github';
44
import { observer } from 'mobx-react';
5+
import { ObservedComponent, observePropsState } from 'mobx-react-helper';
56
import { Column, RestTable } from 'mobx-restful-table';
6-
import { Component, ContextType } from 'react';
7+
import { Component } from 'react';
78
import { Badge, Container } from 'react-bootstrap';
89

910
import { PageHead } from '../components/PageHead';
1011
import { repositoryStore } from '../models/Base';
11-
import { I18nContext } from '../models/Translation';
12+
import { i18n, I18nContext } from '../models/Translation';
13+
14+
export default interface PaginationPage
15+
extends ObservedComponent<{}, typeof i18n> {}
1216

1317
@observer
18+
@observePropsState
1419
export default class PaginationPage extends Component {
1520
static contextType = I18nContext;
1621

17-
declare context: ContextType<typeof I18nContext>;
18-
19-
@observable
20-
// @ts-expect-error MobX compatibility
21-
accessor observedContext = this.context;
22-
2322
@computed
2423
get columns(): Column<GitRepository>[] {
25-
const { t } = this.observedContext;
24+
const { t } = this.observedContext!;
2625

2726
return [
2827
{
@@ -61,7 +60,7 @@ export default class PaginationPage extends Component {
6160
}
6261

6362
render() {
64-
const i18n = this.observedContext;
63+
const i18n = this.observedContext!;
6564

6665
return (
6766
<Container style={{ height: '91vh' }}>

0 commit comments

Comments
 (0)