-
Notifications
You must be signed in to change notification settings - Fork 27.3k
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
Rosetta i18n example #11841
Merged
Merged
Rosetta i18n example #11841
Changes from 6 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
d71286f
add rosetta example
StarpTech 2600725
add comment
StarpTech 2f84d99
add example for ssr
StarpTech 92aecbd
add debug example, rerender when i18n keys are added
StarpTech 3bd87ac
output active locale, fix interpolation, fix cases for SSR and CSR
StarpTech 4dfbaea
add useful comments
StarpTech c6ba6bf
address pr issues
StarpTech cac5161
update name in readme
StarpTech bf17db9
improve wording
StarpTech 28589d8
rename folder
StarpTech 88b03e3
fix prop typo, add redirects
StarpTech 3e58526
load specific i18n json file in getStatisProps
StarpTech 556ba81
use ext
StarpTech b727a26
improve example
StarpTech 9832eb2
Merge branch 'canary' of github.com:zeit/next.js into rosetta_i18n_ex…
2b71c68
Updated example
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# rosetta example | ||
|
||
This example uses [rosetta](https://github.com/lukeed/rosetta), react hooks and context to provide a SSR, SSG, CSR compatible i18n solution. | ||
|
||
In `next.config.js` you can configure the fallback language. | ||
|
||
## Deploy your own | ||
|
||
Deploy the example using [ZEIT Now](https://zeit.co/now): | ||
|
||
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/next.js/tree/canary/examples/with-rosetta) | ||
|
||
## How to use | ||
|
||
### Using `create-next-app` | ||
|
||
Execute [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: | ||
|
||
```bash | ||
npm init next-app --example with-rosetta with-rosetta | ||
# or | ||
yarn create next-app --example with-rosetta with-rosetta | ||
``` | ||
|
||
### Download manually | ||
|
||
Download the example: | ||
|
||
```bash | ||
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-rosetta | ||
cd with-rosetta | ||
``` | ||
|
||
Install it and run: | ||
|
||
```bash | ||
npm install | ||
npm run dev | ||
# or | ||
yarn | ||
yarn dev | ||
``` | ||
|
||
Deploy it to the cloud with [ZEIT Now](https://zeit.co/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { useContext, useState, useRef, useEffect } from 'react' | ||
import { I18nContext, defaultLanguage } from '../lib/i18n' | ||
|
||
export default function useI18n(loc) { | ||
// OR detect language based on navigator.language OR user setting (cookie) | ||
const activeLocaleRef = useRef(loc || defaultLanguage) | ||
const [, setTick] = useState(0) | ||
|
||
const i18n = useContext(I18nContext) | ||
if (loc) { | ||
i18n.locale(loc) | ||
} | ||
|
||
useEffect(() => { | ||
if (loc) { | ||
i18n.locale(loc) | ||
activeLocaleRef.current = loc | ||
// force rerender | ||
setTick(tick => tick + 1) | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [loc]) | ||
|
||
return { | ||
activeLocale: activeLocaleRef.current, | ||
set: (...args) => { | ||
i18n.set(...args) | ||
// force rerender | ||
setTick(tick => tick + 1) | ||
}, | ||
t: (...args) => i18n.t(...args), | ||
locale: l => { | ||
i18n.locale(l) | ||
activeLocaleRef.current = l | ||
// force rerender | ||
setTick(tick => tick + 1) | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { createContext } from 'react' | ||
import rosetta from 'rosetta' | ||
|
||
// import rosetta from 'rosetta/debug'; | ||
|
||
const i18n = rosetta({ | ||
en: { | ||
contact: { | ||
email: '[email protected]', | ||
}, | ||
intro: { | ||
welcome: 'Welcome, {{username}}!', | ||
text: 'I hope you find this useful.', | ||
}, | ||
}, | ||
de: { | ||
contact: { | ||
email: '[email protected]', | ||
}, | ||
intro: { | ||
welcome: 'Willkommen, {{username}}!', | ||
text: 'Ich hoffe, du findest das nützlich.', | ||
}, | ||
}, | ||
}) | ||
|
||
export const defaultLanguage = 'en' | ||
export const languages = ['de', 'en'] | ||
export const contentLanguageMap = { de: 'de-DE', en: 'en-US' } | ||
|
||
export const I18nContext = createContext() | ||
|
||
// default language | ||
i18n.locale(defaultLanguage) | ||
|
||
export default function I18n({ children }) { | ||
return <I18nContext.Provider value={i18n}>{children}</I18nContext.Provider> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module.exports = { | ||
experimental: { | ||
pages404: true, | ||
polyfillsOptimization: true, | ||
redirects() { | ||
return [ | ||
{ | ||
source: '/', | ||
permanent: true, | ||
destination: '/en', | ||
}, | ||
] | ||
}, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "with-rosetta", | ||
"version": "1.0.0", | ||
"scripts": { | ||
"dev": "next", | ||
"build": "next build", | ||
"start": "next start" | ||
}, | ||
"dependencies": { | ||
"next": "latest", | ||
"react": "^16.13.1", | ||
"react-dom": "^16.13.1", | ||
"rosetta": "1.0.0" | ||
}, | ||
"license": "ISC" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Link from 'next/link' | ||
import Head from 'next/head' | ||
import useI18n from '../../hooks/use-i18n' | ||
import { languages, contentLanguageMap } from '../../lib/i18n' | ||
|
||
const HomePage = ({ lng }) => { | ||
const i18n = useI18n(lng) | ||
|
||
console.log(lng) | ||
|
||
return ( | ||
<div> | ||
<Head> | ||
<meta | ||
http-equiv="content-language" | ||
content={contentLanguageMap[i18n.activeLocale]} | ||
/> | ||
</Head> | ||
<h1>{i18n.t('intro.welcome', { username: 'Peter' })}</h1> | ||
<h3>{i18n.t('intro.text')}</h3> | ||
<div>Current locale: {i18n.activeLocale}</div> | ||
<Link href="/de"> | ||
<a>Change language SSG to 'de'</a> | ||
</Link> | ||
</div> | ||
) | ||
} | ||
|
||
export async function getStaticProps({ params }) { | ||
return { | ||
props: { lng: params.lng }, | ||
} | ||
} | ||
|
||
export async function getStaticPaths() { | ||
return { | ||
paths: languages.map(l => ({ params: { lng: l } })), | ||
fallback: true, | ||
} | ||
} | ||
|
||
export default HomePage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React from 'react' | ||
import I18n from '../lib/i18n' | ||
|
||
export default function MyApp({ Component, pageProps, store }) { | ||
return ( | ||
<I18n> | ||
<Component {...pageProps} /> | ||
</I18n> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import Link from 'next/link' | ||
import Head from 'next/head' | ||
import useI18n from '../hooks/use-i18n' | ||
import { contentLanguageMap } from '../lib/i18n' | ||
|
||
const Contact = ({ lng }) => { | ||
const i18n = useI18n(lng) | ||
|
||
return ( | ||
<div> | ||
<Head> | ||
<meta http-equiv="content-language" content={contentLanguageMap[lng]} /> | ||
</Head> | ||
<h1>{i18n.t('contact.email')}</h1> | ||
<div>Current locale: {i18n.activeLocale}</div> | ||
<Link href={{ pathname: '/contact', query: { lng: 'de' } }}> | ||
<a>Change language SSR to 'de'</a> | ||
</Link> | ||
</div> | ||
) | ||
} | ||
|
||
export async function getServerSideProps({ query }) { | ||
return { | ||
props: { | ||
lng: query.lng || 'en', // OR detect default language based on header OR user setting | ||
}, // will be passed to the page component as props | ||
} | ||
} | ||
|
||
export default Contact | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import useI18n from '../hooks/use-i18n' | ||
|
||
const Dashboard = () => { | ||
const i18n = useI18n() | ||
|
||
return ( | ||
<div> | ||
<h1>{i18n.t('intro.welcome', { username: 'Peter' })}</h1> | ||
<h3>Client side only.</h3> | ||
<div>Current locale: {i18n.activeLocale}</div> | ||
<a href="#" onClick={() => i18n.locale('de')}> | ||
Change language client-side to 'de' | ||
</a> | ||
</div> | ||
) | ||
} | ||
|
||
export default Dashboard | ||
StarpTech marked this conversation as resolved.
Show resolved
Hide resolved
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Let's move this page to
pages/[lng]/contact.js
and usegetStaticProps
, there's no good use case for having a serverless function decide the language of the page if you can do that at build time 👍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.
You're right but it must not be a good use case. I want to demonstrate the different modes.
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.
We prefer to demonstrate good use cases in examples always, rather than show the usage of some method, we have docs and learning lessons for that.
For this case
getStaticProps
is better.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.
Did you get my point? I'd like to demonstrate that the i18n solution works in all modes CSR, SSG, SSR.
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.
Yes, and it surely does, but you don't have to demonstrate it, instead you have to show the community the best way of using it 👍
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.
Would it be worth having 3 separate examples? Whichever you decide should be default behavior can be
with-i18n
and then you can have the two other mode bewith-i18n-(csr|ssg)
.While I understand and relate to both points, I do think as an official example, something should be as concise and as close to prod-ready as possible instead of forcing the user to figure out what's actually core/necessary.
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.
@lfades I updated it. @lukeed that was my first idea too but there are other i18n solutions here. I renamed it to
with-rosetta-i18n
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.
Ah, then
with-i18n-rosetta
would be better for grouping 😉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.
Done.