Skip to content

Commit 405cf9f

Browse files
committed
Add i18n and 10n with lingui and babel
1 parent 18bdb89 commit 405cf9f

File tree

16 files changed

+9511
-5501
lines changed

16 files changed

+9511
-5501
lines changed

.babelrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"presets": [
3+
"next/babel"
4+
],
5+
"plugins": ["@lingui/babel-plugin-lingui-macro"]
6+
}

lingui.config.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const nextConfig = require('./next.config')
2+
3+
console.log('nextConfig', nextConfig)
4+
5+
/** @type {import('@lingui/conf').LinguiConfig} */
6+
module.exports = {
7+
locales: nextConfig.i18n.locales,
8+
pseudoLocale: 'pseudo',
9+
sourceLocale: nextConfig.i18n.defaultLocale,
10+
fallbackLocales: {
11+
default: 'en',
12+
},
13+
catalogs: [
14+
{
15+
path: 'src/locales/{locale}/messages',
16+
// path: 'src/locales/{locale}',
17+
include: ['src/'],
18+
},
19+
],
20+
}

next.config.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,10 @@ export default withMDX({
1818
images: {
1919
domains: ["raw.githubusercontent.com", "numpy.org", "dask.org", "chainer.org", ],
2020
},
21+
i18n: {
22+
// These are all the locales you want to support in
23+
// your application
24+
locales: ["en", "es", "pt"],
25+
defaultLocale: "en",
26+
},
2127
})

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
"private": true,
55
"scripts": {
66
"build-cards": "node build-cards.js",
7+
"extract": "lingui extract",
8+
"compile": "lingui compile",
79
"dev": "next dev",
8-
"build": "next build",
10+
"build": "yarn extract && next build",
911
"start": "next start",
1012
"lint": "next lint"
1113
},
@@ -24,6 +26,8 @@
2426
"@emotion/styled": "^11.13.0",
2527
"@fontsource-variable/inter": "^5.2.5",
2628
"@giscus/react": "^3.0.0",
29+
"@lingui/core": "^5.0.0-next.2",
30+
"@lingui/react": "^5.0.0-next.2",
2731
"@mdx-js/loader": "^3.0.1",
2832
"@mdx-js/react": "^3.0.1",
2933
"@next/mdx": "^14.2.14",
@@ -46,6 +50,9 @@
4650
"swr": "^2.2.5"
4751
},
4852
"devDependencies": {
53+
"@lingui/babel-plugin-lingui-macro": "^5.0.0-next.2",
54+
"@lingui/cli": "^5.0.0-next.2",
55+
"@lingui/loader": "^5.0.0-next.2",
4956
"@types/react": "^18.3.11",
5057
"eslint": "^9.12.0",
5158
"eslint-config-next": "14.2.14",

src/components/layout.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Header } from '@/components/header'
44
import { Link } from '@/components/mdx'
55
import { Box, Flex } from '@chakra-ui/react'
66
import Head from 'next/head'
7+
import { Trans, useLingui } from '@lingui/react/macro'
8+
import { useRouter } from 'next/router'
79

810
export const Layout = ({
911
title,
@@ -13,7 +15,15 @@ export const Layout = ({
1315
url = 'https://xarray.dev',
1416
enableBanner = false,
1517
}) => {
16-
const bannerTitle = 'Check out the new blog post on DataTree!'
18+
/**
19+
* This macro hook is needed to get `t` which
20+
* is bound to i18n from React.Context
21+
*/
22+
const { t } = useLingui()
23+
const router = useRouter()
24+
const { pathname, asPath, query } = router
25+
26+
const bannerTitle = t`Check out the new blog post on DataTree!`
1727
const bannerDescription = ''
1828
const bannerChildren = (
1929
<Link href='/blog/datatree'>

src/i18n.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { i18n, Messages } from '@lingui/core'
2+
import { useRouter } from 'next/router'
3+
import { useEffect } from 'react'
4+
5+
/**
6+
* Load messages for requested locale and activate it.
7+
* This function isn't part of the LinguiJS library because there are
8+
* many ways how to load messages — from REST API, from file, from cache, etc.
9+
*/
10+
export async function loadCatalog(locale: string) {
11+
const catalog = await import(`@lingui/loader!./locales/${locale}/messages.po`)
12+
return catalog.messages
13+
}
14+
15+
export function useLinguiInit(messages: Messages) {
16+
const router = useRouter()
17+
const locale = router.locale || router.defaultLocale!
18+
const isClient = typeof window !== 'undefined'
19+
20+
if (!isClient && locale !== i18n.locale) {
21+
// there is single instance of i18n on the server
22+
// note: on the server, we could have an instance of i18n per supported locale
23+
// to avoid calling loadAndActivate for (worst case) each request, but right now that's what we do
24+
i18n.loadAndActivate({ locale, messages })
25+
}
26+
if (isClient && !i18n.locale) {
27+
// first client render
28+
i18n.loadAndActivate({ locale, messages })
29+
}
30+
31+
useEffect(() => {
32+
const localeDidChange = locale !== i18n.locale
33+
if (localeDidChange) {
34+
i18n.loadAndActivate({ locale, messages })
35+
}
36+
}, [locale])
37+
38+
return i18n
39+
}

src/locales/en/messages.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/en/messages.po

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
msgid ""
2+
msgstr ""
3+
"POT-Creation-Date: 2025-05-01 08:03-0500\n"
4+
"MIME-Version: 1.0\n"
5+
"Content-Type: text/plain; charset=utf-8\n"
6+
"Content-Transfer-Encoding: 8bit\n"
7+
"X-Generator: @lingui/cli\n"
8+
"Language: en\n"
9+
"Project-Id-Version: \n"
10+
"Report-Msgid-Bugs-To: \n"
11+
"PO-Revision-Date: \n"
12+
"Last-Translator: \n"
13+
"Language-Team: \n"
14+
"Plural-Forms: \n"
15+
16+
#: src/components/layout.js:26
17+
msgid "Check out the new blog post on DataTree!"
18+
msgstr "Check out the new blog post on DataTree!"

src/locales/es/messages.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/es/messages.mo

420 Bytes
Binary file not shown.

src/locales/es/messages.po

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
msgid ""
2+
msgstr ""
3+
"Project-Id-Version: \n"
4+
"Report-Msgid-Bugs-To: \n"
5+
"POT-Creation-Date: 2025-05-01 08:03-0500\n"
6+
"PO-Revision-Date: \n"
7+
"Last-Translator: \n"
8+
"Language-Team: \n"
9+
"Language: es\n"
10+
"MIME-Version: 1.0\n"
11+
"Content-Type: text/plain; charset=utf-8\n"
12+
"Content-Transfer-Encoding: 8bit\n"
13+
"Plural-Forms: \n"
14+
"X-Generator: Poedit 3.6\n"
15+
16+
#: src/components/layout.js:26
17+
msgid "Check out the new blog post on DataTree!"
18+
msgstr "¡Consulta la nueva publicación del blog sobre DataTree!"

src/locales/pt/messages.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/pt/messages.po

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
msgid ""
2+
msgstr ""
3+
"POT-Creation-Date: 2025-05-01 08:04-0500\n"
4+
"MIME-Version: 1.0\n"
5+
"Content-Type: text/plain; charset=utf-8\n"
6+
"Content-Transfer-Encoding: 8bit\n"
7+
"X-Generator: @lingui/cli\n"
8+
"Language: pt\n"
9+
10+
#: src/components/layout.js:26
11+
msgid "Check out the new blog post on DataTree!"
12+
msgstr ""

src/pages/_app.js

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
import { I18nProvider } from '@lingui/react'
2+
import { i18n } from '@lingui/core'
3+
14
import * as gtag from '@/lib/ga'
25
import { customTheme } from '@/theme'
36
import { ChakraProvider } from '@chakra-ui/react'
47
import { useRouter } from 'next/router'
8+
import { useLinguiInit } from '../i18n'
59
import Script from 'next/script'
610
import { useEffect } from 'react'
711

812
function MyApp({ Component, pageProps }) {
13+
useLinguiInit(pageProps.translation)
914
const router = useRouter()
1015
useEffect(() => {
1116
const handleRouteChange = (url) => {
@@ -20,29 +25,32 @@ function MyApp({ Component, pageProps }) {
2025
}, [router.events])
2126

2227
return (
23-
<ChakraProvider resetCSS theme={customTheme}>
24-
{/* Google Tag Manager - Global base code */}
25-
<Script
26-
strategy='afterInteractive'
27-
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
28-
/>
29-
<Script
30-
id='gtag-init'
31-
strategy='afterInteractive'
32-
dangerouslySetInnerHTML={{
33-
__html: `
34-
window.dataLayer = window.dataLayer || [];
35-
function gtag(){dataLayer.push(arguments);}
36-
gtag('js', new Date());
37-
gtag('config', '${gtag.GA_TRACKING_ID}', {
38-
page_path: window.location.pathname,
39-
});
40-
`,
41-
}}
42-
/>
43-
44-
<Component {...pageProps} />
45-
</ChakraProvider>
28+
<>
29+
<I18nProvider i18n={i18n}>
30+
<ChakraProvider resetCSS theme={customTheme}>
31+
{/* Google Tag Manager - Global base code */}
32+
<Script
33+
strategy='afterInteractive'
34+
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
35+
/>
36+
<Script
37+
id='gtag-init'
38+
strategy='afterInteractive'
39+
dangerouslySetInnerHTML={{
40+
__html: `
41+
window.dataLayer = window.dataLayer || [];
42+
function gtag(){dataLayer.push(arguments);}
43+
gtag('js', new Date());
44+
gtag('config', '${gtag.GA_TRACKING_ID}', {
45+
page_path: window.location.pathname,
46+
});
47+
`,
48+
}}
49+
/>
50+
<Component {...pageProps} />
51+
</ChakraProvider>
52+
</I18nProvider>
53+
</>
4654
)
4755
}
4856

src/pages/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@ import {
77
Sponsors,
88
} from '@/components'
99

10+
import { useLingui } from '@lingui/react/macro'
11+
1012
import { Layout } from '@/components/layout'
1113

14+
import { loadCatalog } from '../i18n'
15+
16+
export const getStaticProps = async (ctx) => {
17+
const translation = await loadCatalog(ctx.locale)
18+
return {
19+
props: {
20+
translation,
21+
},
22+
}
23+
}
1224
export default function IndexPage() {
25+
const { t } = useLingui()
1326
return (
1427
<Layout
1528
url={`/`}

0 commit comments

Comments
 (0)