-
-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Next.js 13 RSC integration #139
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,14 @@ | ||
require('eslint-config-molindo/setupPlugins'); | ||
|
||
module.exports = { | ||
extends: ['molindo/typescript', 'molindo/react', 'plugin:@next/next/recommended'], | ||
extends: [ | ||
'molindo/typescript', | ||
'molindo/react', | ||
'plugin:@next/next/recommended' | ||
], | ||
rules: { | ||
'react/react-in-jsx-scope': 'off', | ||
'jsx-a11y/anchor-is-valid': 'off', | ||
'react/display-name': 'off' | ||
} | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
/.next/ | ||
.DS_Store | ||
tsconfig.tsbuildinfo | ||
.vscode |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,3 @@ | ||
module.exports = { | ||
i18n: { | ||
locales: ['en', 'de'], | ||
defaultLocale: 'en' | ||
} | ||
} | ||
experimental: {appDir: true} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use client'; | ||
import {NextIntlProvider} from 'next-intl'; | ||
import {ReactNode} from 'react'; | ||
|
||
type Props = { | ||
children: ReactNode; | ||
locale: string; | ||
}; | ||
|
||
export default function Provider({children, locale}: Props) { | ||
console.log('Provider'); | ||
|
||
// return children; | ||
return <NextIntlProvider locale={locale}>{children}</NextIntlProvider>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import 'server-only'; | ||
import {createServerContext} from 'react'; | ||
|
||
// This is always passed to the client, regardless of if it's read from a client component! | ||
// Interestingly the module code is not there, but the context value. | ||
const ServerOnlyContext = createServerContext('serverOnly', 'initialValue'); | ||
|
||
export default ServerOnlyContext; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
export function generateStaticParams() { | ||
return ['de', 'en'].map((locale) => ({locale})); | ||
} | ||
|
||
type Props = { | ||
params: { | ||
locale: string; | ||
}; | ||
}; | ||
|
||
export default function Index({params: {locale}}: Props) { | ||
// TODO: Validate locale or redirect to default locale | ||
|
||
return <p>Hello {locale}</p>; | ||
} | ||
|
||
// import {useTranslations} from 'next-intl'; | ||
// import LocaleSwitcher from 'components/LocaleSwitcher'; | ||
// import PageLayout from 'components/PageLayout'; | ||
|
||
// import {useContext} from 'react'; | ||
// import ServerOnlyContext from './ServerOnlyContext'; | ||
|
||
// export default function Index() { | ||
// // TODO: Use middleware to redirect to a specific locale | ||
|
||
// const serverOnly = useContext(ServerOnlyContext); | ||
// // const t = useTranslations('Index'); | ||
|
||
// console.log('Index'); | ||
|
||
// // return <p>{t('title')}</p>; | ||
|
||
// return <p>Hello {serverOnly.only.for.server + 10}</p>; | ||
|
||
// // return ( | ||
// // <PageLayout title={t('title')}> | ||
// // <p>{t('description')}</p> | ||
// // <LocaleSwitcher /> | ||
// // </PageLayout> | ||
// // ); | ||
// } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {ReactNode} from 'react'; | ||
// import Provider from './Provider'; | ||
import ServerOnlyContext from './ServerOnlyContext'; | ||
|
||
type Props = { | ||
children: ReactNode; | ||
}; | ||
|
||
export default function RootLayout({children, ...rest}: Props) { | ||
console.log(rest); | ||
|
||
// How to get this from the URL? | ||
const locale = 'en'; | ||
|
||
return ( | ||
<html lang={locale}> | ||
<head> | ||
<title>next-intl example</title> | ||
</head> | ||
<body> | ||
<ServerOnlyContext.Provider value={{only: {for: {server: 42}}}}> | ||
{/* <Provider locale={locale}> */} | ||
{children} | ||
{/* </Provider> */} | ||
</ServerOnlyContext.Provider> | ||
</body> | ||
</html> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export default { | ||
locales: ['en', 'de'], | ||
defaultLocale: 'en' | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import acceptLanguageParser from 'accept-language-parser'; | ||
import {headers} from 'next/headers'; | ||
import {NextResponse} from 'next/server'; | ||
import type {NextRequest} from 'next/server'; | ||
import i18n from './i18n'; | ||
|
||
// Somehow not invoked? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a quick glance at the example WIP, the middleware doesn't seem to fire unless there is a "pages" folder as well. After that, it fires on both "pages" and "app" folders. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh cool, thanks for figuring this out! Seems like a bug on the Next.js side. |
||
|
||
function resolveLocale(requestHeaders: Headers) { | ||
const locale = | ||
acceptLanguageParser.pick( | ||
i18n.locales, | ||
requestHeaders.get('accept-language') || i18n.defaultLocale | ||
) || i18n.defaultLocale; | ||
|
||
return locale; | ||
} | ||
|
||
export function middleware(request: NextRequest) { | ||
const locale = resolveLocale(headers()); | ||
return NextResponse.redirect(new URL('/' + locale, request.url)); | ||
} | ||
|
||
export const config = { | ||
matcher: '/test' | ||
}; |
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe wrap all pages under optional
[[locale]]
then get usingparams
https://beta.nextjs.org/docs/routing/defining-routes#example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey and thank you for your interest! Yep, that seems to work, I've experimented with it here:
next-intl/packages/example/src/app/[locale]/page.tsx
Lines 11 to 15 in ccb9c8a
I think that part is doable, the part where I currently don't see an ergonomic solution is how global configuration like messages can be passed to the library (see the topic about context in the PR description).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried some workarounds,
1, Using singleton — Failed because for RSC, initializing on root layout requires 2 render to assign the
messages
object.2. Using third-party state manager like zustand — Failed because of the same reason as number 1.
Probably the solution is gonna be around client component. To use in a server component we could provide utility component like
<T key='dashboard.hi' />
. Performance-wise, next will also server-render client components so the downside is on developer experience. But won't be much I think.