diff --git a/examples/with-i18n-rosetta/README.md b/examples/with-i18n-rosetta/README.md
new file mode 100644
index 0000000000000..fb932e1dbdb90
--- /dev/null
+++ b/examples/with-i18n-rosetta/README.md
@@ -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 [Vercel](https://vercel.com):
+
+[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/zeit/next.js/tree/canary/examples/with-i18n-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-i18n-rosetta with-i18n-rosetta
+# or
+yarn create next-app --example with-i18n-rosetta with-i18n-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-i18n-rosetta
+cd with-i18n-rosetta
+```
+
+Install it and run:
+
+```bash
+npm install
+npm run dev
+# or
+yarn
+yarn dev
+```
+
+Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
diff --git a/examples/with-i18n-rosetta/components/title.js b/examples/with-i18n-rosetta/components/title.js
new file mode 100644
index 0000000000000..912afe370c241
--- /dev/null
+++ b/examples/with-i18n-rosetta/components/title.js
@@ -0,0 +1,6 @@
+import useI18n from '../hooks/use-i18n'
+
+export default function Title({ username }) {
+ const i18n = useI18n()
+ return
{i18n.t('intro.welcome', { username })}
+}
diff --git a/examples/with-i18n-rosetta/hooks/use-i18n.js b/examples/with-i18n-rosetta/hooks/use-i18n.js
new file mode 100644
index 0000000000000..457eb9add8d78
--- /dev/null
+++ b/examples/with-i18n-rosetta/hooks/use-i18n.js
@@ -0,0 +1,7 @@
+import { useContext } from 'react'
+import { I18nContext } from '../lib/i18n'
+
+export default function useI18n() {
+ const i18n = useContext(I18nContext)
+ return i18n
+}
diff --git a/examples/with-i18n-rosetta/lib/i18n.js b/examples/with-i18n-rosetta/lib/i18n.js
new file mode 100644
index 0000000000000..6353c0d6a4378
--- /dev/null
+++ b/examples/with-i18n-rosetta/lib/i18n.js
@@ -0,0 +1,57 @@
+import { createContext, useState, useRef, useEffect } from 'react'
+import rosetta from 'rosetta'
+// import rosetta from 'rosetta/debug';
+
+const i18n = rosetta()
+
+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, locale, lngDict }) {
+ const [activeDict, setActiveDict] = useState(() => lngDict)
+ const activeLocaleRef = useRef(locale || defaultLanguage)
+ const [, setTick] = useState(0)
+ const firstRender = useRef(true)
+
+ // for initial SSR render
+ if (locale && firstRender.current === true) {
+ firstRender.current = false
+ i18n.locale(locale)
+ i18n.set(locale, activeDict)
+ }
+
+ useEffect(() => {
+ if (locale) {
+ i18n.locale(locale)
+ i18n.set(locale, activeDict)
+ activeLocaleRef.current = locale
+ // force rerender
+ setTick(tick => tick + 1)
+ }
+ }, [locale, activeDict])
+
+ const i18nWrapper = {
+ activeLocale: activeLocaleRef.current,
+ t: (...args) => i18n.t(...args),
+ locale: (l, dict) => {
+ i18n.locale(l)
+ activeLocaleRef.current = l
+ if (dict) {
+ i18n.set(l, dict)
+ setActiveDict(dict)
+ } else {
+ setTick(tick => tick + 1)
+ }
+ },
+ }
+
+ return (
+ {children}
+ )
+}
diff --git a/examples/with-i18n-rosetta/locales/de.json b/examples/with-i18n-rosetta/locales/de.json
new file mode 100644
index 0000000000000..5bd93d0cfbf7c
--- /dev/null
+++ b/examples/with-i18n-rosetta/locales/de.json
@@ -0,0 +1,10 @@
+{
+ "intro": {
+ "welcome": "Willkommen, {{username}}!",
+ "text": "Ich hoffe, du findest das nützlich.",
+ "description": "Das Beispiel zeigt, wie man die Sprache für SSG und SSG optimierte Seiten wechselt."
+ },
+ "dashboard": {
+ "description": "Das Beispiel zeigt, wie man die Sprache nur Frontendseitig verändert. Nützlich für Dashboards wo SEO nicht relevant ist."
+ }
+}
diff --git a/examples/with-i18n-rosetta/locales/en.json b/examples/with-i18n-rosetta/locales/en.json
new file mode 100644
index 0000000000000..070362e7809f3
--- /dev/null
+++ b/examples/with-i18n-rosetta/locales/en.json
@@ -0,0 +1,10 @@
+{
+ "intro": {
+ "welcome": "Welcome, {{username}}!",
+ "text": "I hope you find this useful.",
+ "description": "This example demonstrate how to change the language for SSG and SSR optimized pages."
+ },
+ "dashboard": {
+ "description": "This example demonstrate how to change the language on client side only. Useful for dashboards because they don't require SEO."
+ }
+}
diff --git a/examples/with-i18n-rosetta/next.config.js b/examples/with-i18n-rosetta/next.config.js
new file mode 100644
index 0000000000000..3e6c176989606
--- /dev/null
+++ b/examples/with-i18n-rosetta/next.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ experimental: {
+ redirects() {
+ return [
+ {
+ source: '/',
+ permanent: true,
+ destination: '/en',
+ },
+ ]
+ },
+ },
+}
diff --git a/examples/with-i18n-rosetta/package.json b/examples/with-i18n-rosetta/package.json
new file mode 100644
index 0000000000000..06d61053b968a
--- /dev/null
+++ b/examples/with-i18n-rosetta/package.json
@@ -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"
+}
diff --git a/examples/with-i18n-rosetta/pages/[lng]/index.js b/examples/with-i18n-rosetta/pages/[lng]/index.js
new file mode 100644
index 0000000000000..0870f5055af7d
--- /dev/null
+++ b/examples/with-i18n-rosetta/pages/[lng]/index.js
@@ -0,0 +1,46 @@
+import Link from 'next/link'
+import Head from 'next/head'
+import Title from '../../components/title'
+import useI18n from '../../hooks/use-i18n'
+import { languages, contentLanguageMap } from '../../lib/i18n'
+
+const HomePage = () => {
+ const i18n = useI18n()
+
+ return (
+
+ )
+}
+
+export async function getStaticProps({ params }) {
+ const { default: lngDict = {} } = await import(
+ `../../locales/${params.lng}.json`
+ )
+
+ return {
+ props: { lng: params.lng, lngDict },
+ }
+}
+
+export async function getStaticPaths() {
+ return {
+ paths: languages.map(l => ({ params: { lng: l } })),
+ fallback: false,
+ }
+}
+
+export default HomePage
diff --git a/examples/with-i18n-rosetta/pages/_app.js b/examples/with-i18n-rosetta/pages/_app.js
new file mode 100644
index 0000000000000..f2f8d1db40a19
--- /dev/null
+++ b/examples/with-i18n-rosetta/pages/_app.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import I18n from '../lib/i18n'
+
+export default function MyApp({ Component, pageProps }) {
+ return (
+
+
+
+ )
+}
diff --git a/examples/with-i18n-rosetta/pages/dashboard.js b/examples/with-i18n-rosetta/pages/dashboard.js
new file mode 100644
index 0000000000000..7d7aeb6af365d
--- /dev/null
+++ b/examples/with-i18n-rosetta/pages/dashboard.js
@@ -0,0 +1,41 @@
+import { useEffect } from 'react'
+import Head from 'next/head'
+import useI18n from '../hooks/use-i18n'
+import Title from '../components/title'
+import { contentLanguageMap } from '../lib/i18n'
+import EN from '../locales/en.json'
+import DE from '../locales/de.json'
+
+const Dashboard = () => {
+ const i18n = useI18n()
+
+ useEffect(() => {
+ i18n.locale('en', EN)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ return (
+
+ )
+}
+
+export default Dashboard