')
68 | })
69 | })
70 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "types": ["cypress", "@types/testing-library__cypress"]
6 | },
7 | "include": ["**/*.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/docs/v8-migration.md:
--------------------------------------------------------------------------------
1 | # Migration Guide v7 to v8
2 |
3 | Please read the [full documentation](https://github.com/i18next/next-i18next/blob/master/README.md), before migrating from previous versions to v8.
4 |
5 | This is a guide which will cover most use cases to migrate from v7 to v8.
6 | We advise migrating as soon as possible, as new versions of NextJs won't be compatible with the v7 of this package `next-i18next`.
7 |
8 | ## What is new?
9 |
10 | This package, `next-i18next`, has changed a lot because it now is _not_ providing internationalised routing anymore, as [NextJs has first class support for it.](https://nextjs.org/docs/advanced-features/i18n-routing)
11 |
12 | Before the translation functionality was initialised on a global level, in `_app.js`. Now, you must use a new method, called `serverSideTranslations` on _each_ page in your `pages` directory.
13 |
14 | The object `i18n` which was imported directly from `i18n.js` in `next-i18next@<8` suppored only client-side-rendering. Now in the v8 the `i18n` object also supports server-side rendering. So you can use the `i18n.language` for server-side rendered elements.
15 |
16 | ## What is the same?
17 |
18 | 1. `appWithTranslation` still wraps the App in `_app.js`
19 | 2. `withTranslation` works the same way
20 | 3. `useTranslation` works the same way
21 | 4. The [translation content structure](https://github.com/i18next/next-i18next/blob/master/README.md#2-translation-content) remains the same
22 |
23 | ## What is different?
24 |
25 | 1. `getInitialProps` is not supported anymore, using `serverSideTranslations` is mandatory for all pages.
26 | If you require `getInitialProps` keep using the last `7.x` version.
27 |
28 | ## Step By Step Migration Guide
29 |
30 | 1. Remove `i18n.js` and add `next-i18next.config.js` as described in [the docs](https://github.com/i18next/next-i18next#3-project-setup) to your `next.config.js` file
31 | 2. Replace `import { appWithTranslation } from 'i18n'` with `import { appWithTranslation } from 'next-i18next'`
32 | 3. Replace all instances of `import { withTranslation } from 'i18n` to `import { withTranslation } from 'next-i18next'`
33 | 4. Replace all instances of `import { useTranslation } from 'i18n` to `import { useTranslation } from 'next-i18next'`
34 | 5. Add to `getServerSideProps` or `getStaticProps` in the return as props`...(await serverSideTranslations(locale, []))` in every single page where you have translations. Note that if you have a component in `_app` that needs translations, you will have to do this for _all_ pages. Follow [the docs.](https://github.com/i18next/next-i18next#serversidetranslations)
35 | 6. Remove `namespacesRequired: ['common'],` in `_app.js` (not used anymore)
36 | 7. To change language imperatively, you can now do: `router.push(router.asPath, undefined, { locale: , });`
37 |
38 | ## Optional
39 |
40 | 1. Add to the custom 404 page the `...(await serverSideTranslations(locale, [])),` as a return in props in `getStaticProps` so the 404 page works with translations as well
41 | 2. Add to the custom 500 page the `...(await serverSideTranslations(locale, [])),` as a return in props in `getStaticProps` so the 500 page works with translations as well
42 | 3. Add set cookie `NEXT_LOCALE` for language recognition. More about that in [the NextJs docs](https://nextjs.org/docs/advanced-features/i18n-routing#leveraging-the-next_locale-cookie)
43 | 4. Adjust the Jest test settings to mock `withTranslation`,`useTranslation`, and `t()` or/and `i18n` in props.
44 |
45 | More info in the [full documentation](https://github.com/i18next/next-i18next/blob/master/README.md), or in the [next.js documentation.](https://nextjs.org/docs/advanced-features/i18n-routing)
46 |
--------------------------------------------------------------------------------
/examples/auto-static-optimize/@types/i18next.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * If you want to enable locale keys typechecking and enhance IDE experience.
3 | *
4 | * Requires `resolveJsonModule:true` in your tsconfig.json.
5 | *
6 | * @link https://www.i18next.com/overview/typescript
7 | */
8 | import 'i18next'
9 |
10 | // resources.ts file is generated with `npm run toc`
11 | import resources from './resources.ts'
12 |
13 | declare module 'i18next' {
14 | interface CustomTypeOptions {
15 | defaultNS: 'common'
16 | resources: typeof resources
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/auto-static-optimize/@types/resources.ts:
--------------------------------------------------------------------------------
1 | import common from '../public/locales/en/common.json'
2 | import footer from '../public/locales/en/footer.json'
3 | import secondpage from '../public/locales/en/second-page.json'
4 | import staticpage from '../public/locales/en/staticpage.json'
5 |
6 | const resources = {
7 | common,
8 | footer,
9 | 'second-page': secondpage,
10 | staticpage,
11 | } as const
12 |
13 | export default resources
14 |
--------------------------------------------------------------------------------
/examples/auto-static-optimize/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import pkg from 'next-i18next/package.json'
2 | import { useTranslation, Trans } from 'next-i18next'
3 | import type { FC } from 'react'
4 |
5 | export const Footer: FC = () => {
6 | const { t } = useTranslation('footer')
7 |
8 | return (
9 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/examples/auto-static-optimize/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import type { FC } from 'react'
3 |
4 | type Props = {
5 | heading: string
6 | title: string
7 | }
8 |
9 | export const Header: FC = ({ heading, title }) => (
10 | <>
11 |
12 | {title}
13 |
14 |
95 |
96 |
97 |
98 | {/* alternative language change without using Link component
99 |
102 | */}
103 | {/* alternative language change without using Link component, but this will change language only on client side
104 | */}
107 |
108 |
109 |
110 |
111 |
112 |
113 |
94 |
95 |
96 |
97 | {/* alternative language change without using Link component
98 |
101 | */}
102 | {/* alternative language change without using Link component, but this will change language only on client side
103 | */}
106 |
107 |
108 |
109 |
110 |
111 |
112 | >
113 | )
114 | }
115 |
116 | // or getServerSideProps: GetServerSideProps = async ({ locale })
117 | export const getStaticProps: GetStaticProps = async ({
118 | locale,
119 | }) => ({
120 | props: {
121 | ...(await serverSideTranslations(locale ?? 'en', [
122 | 'common',
123 | 'footer',
124 | ])),
125 | },
126 | })
127 |
128 | export default Homepage
129 |
--------------------------------------------------------------------------------
/examples/simple/pages/second-page.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import type {
3 | GetServerSideProps,
4 | InferGetServerSidePropsType,
5 | } from 'next'
6 |
7 | import { useTranslation } from 'next-i18next'
8 | import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
9 |
10 | import { Header } from '../components/Header'
11 | import { Footer } from '../components/Footer'
12 |
13 | type Props = {
14 | // Add custom props here
15 | }
16 |
17 | const SecondPage = (
18 | _props: InferGetServerSidePropsType
19 | ) => {
20 | const { t } = useTranslation(['common', 'second-page'])
21 |
22 | return (
23 | <>
24 |
25 |
29 |
30 |
33 |
34 |
35 |
36 | >
37 | )
38 | }
39 |
40 | export const getServerSideProps: GetServerSideProps = async ({
41 | locale,
42 | }) => ({
43 | props: {
44 | ...(await serverSideTranslations(locale ?? 'en', [
45 | 'second-page',
46 | 'footer',
47 | ])),
48 | },
49 | })
50 |
51 | export default SecondPage
52 |
--------------------------------------------------------------------------------
/examples/simple/public/app.css:
--------------------------------------------------------------------------------
1 | #__next {
2 | font-family: 'Open Sans', sans-serif;
3 | text-align: center;
4 | background-image: linear-gradient(
5 | to left top,
6 | #ffffff,
7 | #f5f5f5,
8 | #eaeaea,
9 | #e0e0e0,
10 | #d6d6d6
11 | );
12 | display: flex;
13 | flex-direction: column;
14 | margin: 0;
15 | min-height: 100vh;
16 | min-width: 100vw;
17 | }
18 |
19 | * body {
20 | overflow-x: hidden; /* Hide x-axis overflow */
21 | }
22 |
23 | h1,
24 | h2 {
25 | font-family: 'Oswald', sans-serif;
26 | }
27 |
28 | h1 {
29 | font-size: 3rem;
30 | margin: 5rem 0;
31 | }
32 | h2 {
33 | min-width: 18rem;
34 | font-size: 2rem;
35 | opacity: 0.3;
36 | }
37 | h3 {
38 | font-size: 1.5rem;
39 | opacity: 0.5;
40 | }
41 |
42 | .mainBox {
43 | display: grid;
44 | gap: 20px;
45 | width: 90%;
46 | grid-template-columns: 1fr;
47 | grid-auto-rows: auto;
48 | }
49 |
50 | .card {
51 | background-color: #fff;
52 | border-radius: 10px;
53 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
54 | overflow: hidden;
55 | text-align: center;
56 | box-sizing: border-box;
57 | transition: box-shadow 0.3s ease; /* Smooth transition for the hover effect */
58 | display: grid;
59 | grid-template-rows: auto 1fr auto;
60 | }
61 |
62 | .card:hover {
63 | box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* Increased shadow on hover */
64 | }
65 |
66 | .card-title {
67 | padding-top: 11px;
68 | min-height: 70px;
69 | font-size: 1.5em;
70 | margin: 0 0 10px;
71 | }
72 |
73 | .card-text {
74 | font-size: 1em;
75 | color: #666;
76 | margin-bottom: 15px;
77 | padding: 0 10px; /* Add padding for text inside the card */
78 | }
79 |
80 | .card-text a {
81 | color: #007bff;
82 | text-decoration: none;
83 | }
84 |
85 | .card-text a:hover {
86 | text-decoration: underline;
87 | }
88 |
89 | .card-img {
90 | width: 100%; /* Full width of the parent div */
91 | height: auto; /* Maintain aspect ratio */
92 | border-radius: 0 0 10px 10px; /* Rounded corners on the bottom only */
93 | display: block; /* Prevent extra space below the image */
94 | transition:
95 | transform 0.5s ease,
96 | box-shadow 0.5s ease; /* Smooth transition for hover effects with longer duration */
97 | }
98 |
99 | .card-img:hover {
100 | transform: scale(1.05); /* Slightly scale up the image */
101 | box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* Add a subtle shadow */
102 | }
103 |
104 | /* Media queries for responsive design */
105 | @media (min-width: 600px) {
106 | .mainBox {
107 | grid-template-columns: repeat(
108 | 2,
109 | 1fr
110 | ); /* Show 2 cards per row on medium screens */
111 | }
112 | }
113 |
114 | @media (min-width: 900px) {
115 | .mainBox {
116 | grid-template-columns: repeat(
117 | 3,
118 | 1fr
119 | ); /* Show 3 cards per row on large screens */
120 | }
121 | }
122 |
123 | p {
124 | line-height: 1.65em;
125 | }
126 | p:nth-child(2) {
127 | font-style: italic;
128 | opacity: 0.65;
129 | margin-top: 1rem;
130 | }
131 |
132 | a.github {
133 | position: fixed;
134 | top: 0.5rem;
135 | right: 0.75rem;
136 | font-size: 4rem;
137 | color: #888;
138 | opacity: 0.8;
139 | }
140 | a.github:hover {
141 | opacity: 1;
142 | }
143 |
144 | button {
145 | display: inline-block;
146 | vertical-align: bottom;
147 | outline: 0;
148 | text-decoration: none;
149 | cursor: pointer;
150 | background-color: rgba(255, 255, 255, 0.5);
151 | box-sizing: border-box;
152 | font-size: 1em;
153 | font-family: inherit;
154 | border-radius: 3px;
155 | transition: box-shadow 0.2s ease;
156 | user-select: none;
157 | line-height: 2.5em;
158 | min-height: 40px;
159 | padding: 0 0.8em;
160 | border: 0;
161 | border-radius: 5px;
162 | color: inherit;
163 | position: relative;
164 | transform: translateZ(0);
165 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
166 | margin: 0.8rem;
167 | }
168 |
169 | button:hover,
170 | button:focus {
171 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4);
172 | }
173 |
174 | main {
175 | display: flex;
176 | flex-direction: column;
177 | flex: 1;
178 | justify-content: center;
179 | align-items: center;
180 | }
181 | footer {
182 | background-color: rgba(255, 255, 255, 0.5);
183 | width: 100vw;
184 | padding: 3rem 0;
185 | }
186 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/de/common.json:
--------------------------------------------------------------------------------
1 | {
2 | "h1": "Ein einfaches Beispiel",
3 | "change-locale": "Sprache wechseln zu \"{{changeTo}}\"",
4 | "to-second-page": "Zur zweiten Seite",
5 | "error-with-status": "Auf dem Server ist ein Fehler ({{statusCode}}) aufgetreten",
6 | "error-without-status": "Auf dem Server ist ein Fehler aufgetreten",
7 | "title": "Hauptseite | next-i18next",
8 | "blog": {
9 | "appDir": {
10 | "question": "Verwendest du das neue Next.js App Router?",
11 | "answer": "Dann schau dir <1>diesen Blogbeitrag1> an.",
12 | "link": "https://www.locize.com/blog/i18n-next-app-router"
13 | },
14 | "optimized": {
15 | "question": "Möchtest du einige Superkräfte entfesseln, um für alle Seiten optimierte Übersetzungen zu haben?",
16 | "answer": "Dann schaue dir vielleicht <1>diesen Blogbeitrag1> an.",
17 | "link": "https://locize.com/blog/next-i18next"
18 | },
19 | "ssg": {
20 | "question": "Möchtest du SSG (next export) verwenden?",
21 | "answer": "Dann schaue dir vielleicht <1>diesen Blogbeitrag1> an.",
22 | "link": "https://locize.com/blog/next-i18n-static"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/de/footer.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Dies ist eine Nicht-Seitenkomponente, die einen eigenen Namespace erfordert",
3 | "helpLocize": "Wenn Sie <1>locize1> einsetzen, unterstützen Sie direkt die Zukunft von <3>i18next3>."
4 | }
5 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/de/second-page.json:
--------------------------------------------------------------------------------
1 | {
2 | "h1": "Eine zweite Seite, um das Routing zu demonstrieren",
3 | "back-to-home": "Zurück zur Hauptseite",
4 | "title": "Zweite Seite | next-i18next"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/en/common.json:
--------------------------------------------------------------------------------
1 | {
2 | "h1": "A simple example",
3 | "change-locale": "Change locale to \"{{changeTo}}\"",
4 | "to-second-page": "To second page",
5 | "error-with-status": "A {{statusCode}} error occurred on server",
6 | "error-without-status": "An error occurred on the server",
7 | "title": "Home | next-i18next",
8 | "blog": {
9 | "appDir": {
10 | "question": "Are you using the new Next.js App Router?",
11 | "answer": "Then check out <1>this blog post1>.",
12 | "link": "https://www.locize.com/blog/i18n-next-app-router"
13 | },
14 | "optimized": {
15 | "question": "Do you like to unleash some super powers to have all side optimized translations?",
16 | "answer": "Then you may have a look at <1>this blog post1>.",
17 | "link": "https://locize.com/blog/next-i18next"
18 | },
19 | "ssg": {
20 | "question": "Do you want to use SSG (next export)?",
21 | "answer": "Then you may have a look at <1>this blog post1>.",
22 | "link": "https://locize.com/blog/next-i18n-static"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/en/footer.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "This is a non-page component that requires its own namespace",
3 | "helpLocize": "With using <1>locize1> you directly support the future of <3>i18next3>."
4 | }
5 |
--------------------------------------------------------------------------------
/examples/simple/public/locales/en/second-page.json:
--------------------------------------------------------------------------------
1 | {
2 | "h1": "A second page, to demonstrate routing",
3 | "back-to-home": "Back to home",
4 | "title": "Second page | next-i18next"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/simple/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "target": "esnext",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "baseUrl": ".",
7 | "allowJs": true,
8 | "skipLibCheck": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true
19 | },
20 | "include": [
21 | "next-env.d.ts",
22 | "**/*.ts",
23 | "**/*.tsx",
24 | "**/*.js",
25 | "**/*.jsx",
26 | "**/*.cjs",
27 | "**/*.mjs"
28 | ],
29 | "exclude": ["**/node_modules", "**/.*/"]
30 | }
31 |
--------------------------------------------------------------------------------
/examples/simple/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "github": {
3 | "silent": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/ssg/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | .next
4 | node_modules
5 | .env.local
6 | public/sitemap.xml
7 | public/feed.xml
8 | .vercel
9 | out
10 |
--------------------------------------------------------------------------------
/examples/ssg/README.md:
--------------------------------------------------------------------------------
1 | # Static internationalized (i18n) next.js website with the help of [next-i18next](https://github.com/i18next/next-i18next) and [next-language-detector](https://github.com/i18next/next-language-detector)
2 |
3 | ## What is this?
4 |
5 | This is a simple example of how to use [next-i18next](https://github.com/i18next/next-i18next) with [Next.js](https://github.com/zeit/next.js) for a complete static website and optional use of [locize](https://locize.com) to get translations up and running quickly and easily.
6 |
7 | ## For more info...
8 |
9 | You may have arrived here from the [Next.js](https://github.com/zeit/next.js) repository, from [next-i18next](https://github.com/i18next/next-i18next) repository or the [react-i18next](https://github.com/i18next/react-i18next/) repository. Either way, for more documentation on:
10 |
11 | - **next-i18next**, please visit the [main README](https://github.com/i18next/next-i18next)
12 | - **Next.js**, please visit the [website](https://nextjs.org/)
13 | - **locize** in combination with Next.js, please visit the [main README](https://github.com/locize/next-i18next-locize)
14 |
15 | **A step by step guide can be found in [this tutorial](https://locize.com/blog/next-i18n-static/).**
16 |
17 | ## optional [locize](https://locize.com) usage (not mandatory)
18 |
19 | Before "deploying" your app, you can run the [downloadLocales script](https://github.com/i18next/next-language-detector/blob/main/examples/basic/package.json#L15) (or similar), which will use the [cli](https://github.com/locize/locize-cli) to download the translations from locize into the appropriate folder next-i18next is looking in to (i.e. ./public/locales).
20 |
21 | ## Verify yourself
22 |
23 | To prove that this works, pull this example to your local machine and run:
24 |
25 | ```sh
26 | npm i
27 | npm run build
28 | npm run serve
29 | ```
30 |
31 | and open your browser at http://localhost:8080
32 |
33 | You can now deploy the out folder to any static webserver, like [GitHub pages](https://pages.github.com).
34 |
--------------------------------------------------------------------------------
/examples/ssg/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'next-i18next'
2 | import { useRouter } from 'next/router'
3 | import LanguageSwitchLink from './LanguageSwitchLink'
4 | import pkg from 'next-i18next/package.json'
5 | import pkgLD from 'next-language-detector/package.json'
6 |
7 | import i18nextConfig from '../next-i18next.config'
8 |
9 | export const Footer = () => {
10 | const router = useRouter()
11 | const { t } = useTranslation('footer')
12 | const currentLocale =
13 | router.query.locale || i18nextConfig.i18n.defaultLocale
14 |
15 | return (
16 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/examples/ssg/components/Header.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 |
3 | export const Header = ({ heading, title }) => (
4 | <>
5 |
6 | {title}
7 |
8 |