├── .all-contributorsrc ├── .circleci └── config.yml ├── .editorconfig ├── .eslintrc.json ├── .github ├── stale.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .npmignore ├── .size-limit.cjs ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TROUBLESHOOT.md ├── UPGRADING.md ├── babel.config.json ├── cypress.config.ts ├── cypress ├── e2e │ └── spec.cy.ts └── tsconfig.json ├── docs └── v8-migration.md ├── examples ├── auto-static-optimize │ ├── @types │ │ ├── i18next.d.ts │ │ └── resources.ts │ ├── components │ │ ├── Footer.tsx │ │ └── Header.tsx │ ├── next-env.d.ts │ ├── next-i18next.config.js │ ├── next-utils.config.js │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── auto-static.tsx │ │ ├── index.tsx │ │ └── second-page.tsx │ ├── public │ │ ├── app.css │ │ └── locales │ │ │ ├── de │ │ │ ├── common.json │ │ │ ├── footer.json │ │ │ ├── second-page.json │ │ │ └── staticpage.json │ │ │ └── en │ │ │ ├── common.json │ │ │ ├── footer.json │ │ │ ├── second-page.json │ │ │ └── staticpage.json │ ├── tsconfig.json │ └── vercel.json ├── simple │ ├── @types │ │ ├── i18next.d.ts │ │ └── resources.ts │ ├── components │ │ ├── Footer.tsx │ │ └── Header.tsx │ ├── next-env.d.ts │ ├── next-i18next.config.js │ ├── next-utils.config.js │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── index.tsx │ │ └── second-page.tsx │ ├── public │ │ ├── app.css │ │ └── locales │ │ │ ├── de │ │ │ ├── common.json │ │ │ ├── footer.json │ │ │ └── second-page.json │ │ │ └── en │ │ │ ├── common.json │ │ │ ├── footer.json │ │ │ └── second-page.json │ ├── tsconfig.json │ └── vercel.json └── ssg │ ├── .gitignore │ ├── README.md │ ├── components │ ├── Footer.js │ ├── Header.js │ ├── LanguageSwitchLink.js │ └── Link.js │ ├── lib │ ├── getStatic.js │ ├── languageDetector.js │ └── redirect.js │ ├── next-i18next.config.js │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── 404.js │ ├── [locale] │ │ ├── 404.js │ │ ├── index.js │ │ └── second-page.js │ ├── _app.js │ ├── _document.js │ ├── index.js │ └── second-page.js │ └── public │ ├── app.css │ └── locales │ ├── de │ ├── 404.json │ ├── common.json │ ├── footer.json │ └── second-page.json │ ├── en │ ├── 404.json │ ├── common.json │ ├── footer.json │ └── second-page.json │ └── it │ ├── 404.json │ ├── common.json │ ├── footer.json │ └── second-page.json ├── jest.config.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── serverSideTranslations.d.ts ├── serverSideTranslations.js ├── src ├── appWithTranslation.client.test.tsx ├── appWithTranslation.server.test.tsx ├── appWithTranslation.tsx ├── config │ ├── createConfig.test.ts │ ├── createConfig.ts │ └── defaultConfig.ts ├── createClient │ ├── browser.test.ts │ ├── browser.ts │ ├── node.test.ts │ ├── node.ts │ └── package.json ├── index.tsx ├── serverSideTranslations.test.tsx ├── serverSideTranslations.ts ├── types.ts └── utils.ts └── tsconfig.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "next-i18next", 3 | "projectOwner": "i18next", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "contributors": [ 12 | { 13 | "login": "capellini", 14 | "name": "Rob Capellini", 15 | "avatar_url": "https://avatars3.githubusercontent.com/u/75311?v=4", 16 | "profile": "https://github.com/capellini", 17 | "contributions": [ 18 | "code", 19 | "test" 20 | ] 21 | }, 22 | { 23 | "login": "kachkaev", 24 | "name": "Alexander Kachkaev", 25 | "avatar_url": "https://avatars3.githubusercontent.com/u/608862?v=4", 26 | "profile": "https://en.kachkaev.ru", 27 | "contributions": [ 28 | "talk", 29 | "question", 30 | "ideas", 31 | "code", 32 | "test" 33 | ] 34 | }, 35 | { 36 | "login": "MathiasKandelborg", 37 | "name": "Mathias Wøbbe", 38 | "avatar_url": "https://avatars1.githubusercontent.com/u/33042011?v=4", 39 | "profile": "https://kandelborg.dk", 40 | "contributions": [ 41 | "code", 42 | "ideas", 43 | "test" 44 | ] 45 | }, 46 | { 47 | "login": "lucasfeliciano", 48 | "name": "Lucas Feliciano", 49 | "avatar_url": "https://avatars3.githubusercontent.com/u/968014?v=4", 50 | "profile": "http://lucasfeliciano.com", 51 | "contributions": [ 52 | "ideas", 53 | "review" 54 | ] 55 | }, 56 | { 57 | "login": "minocys", 58 | "name": "Ryan Leung", 59 | "avatar_url": "https://avatars2.githubusercontent.com/u/6932550?v=4", 60 | "profile": "http://www.fifteenprospects.com", 61 | "contributions": [ 62 | "code" 63 | ] 64 | }, 65 | { 66 | "login": "nathanfriemel", 67 | "name": "Nathan Friemel", 68 | "avatar_url": "https://avatars3.githubusercontent.com/u/1325835?v=4", 69 | "profile": "http://nathanfriemel.com", 70 | "contributions": [ 71 | "code", 72 | "doc", 73 | "example", 74 | "ideas" 75 | ] 76 | }, 77 | { 78 | "login": "isaachinman", 79 | "name": "Isaac Hinman", 80 | "avatar_url": "https://avatars.githubusercontent.com/u/10575782?v=4", 81 | "profile": "https://isaachinman.com/", 82 | "contributions": [ 83 | "a11y", 84 | "question", 85 | "audio", 86 | "blog", 87 | "bug", 88 | "business", 89 | "code", 90 | "content", 91 | "data", 92 | "design", 93 | "doc", 94 | "eventOrganizing", 95 | "example", 96 | "financial", 97 | "fundingFinding", 98 | "ideas", 99 | "infra", 100 | "maintenance", 101 | "mentoring", 102 | "platform", 103 | "plugin", 104 | "projectManagement", 105 | "research", 106 | "review", 107 | "security", 108 | "talk", 109 | "test", 110 | "tool", 111 | "translation", 112 | "tutorial", 113 | "userTesting", 114 | "video" 115 | ] 116 | }, 117 | { 118 | "login": "adrai", 119 | "name": "Adriano Raiano", 120 | "avatar_url": "https://avatars.githubusercontent.com/u/1086194?v=4", 121 | "profile": "https://locize.com/", 122 | "contributions": [ 123 | "a11y", 124 | "question", 125 | "audio", 126 | "blog", 127 | "bug", 128 | "business", 129 | "code", 130 | "content", 131 | "data", 132 | "design", 133 | "doc", 134 | "eventOrganizing", 135 | "example", 136 | "financial", 137 | "fundingFinding", 138 | "ideas", 139 | "infra", 140 | "maintenance", 141 | "mentoring", 142 | "platform", 143 | "plugin", 144 | "projectManagement", 145 | "research", 146 | "review", 147 | "security", 148 | "talk", 149 | "test", 150 | "tool", 151 | "translation", 152 | "tutorial", 153 | "userTesting", 154 | "video" 155 | ] 156 | }, 157 | { 158 | "login": "felixmosh", 159 | "name": "Felix Mosheev", 160 | "avatar_url": "https://avatars.githubusercontent.com/u/9304194?v=4", 161 | "profile": "https://github.com/felixmosh", 162 | "contributions": [ 163 | "question", 164 | "code", 165 | "talk", 166 | "test" 167 | ] 168 | }, 169 | { 170 | "login": "belgattitude", 171 | "name": "Sébastien Vanvelthem", 172 | "avatar_url": "https://avatars.githubusercontent.com/u/259798?v=4", 173 | "profile": "https://soluble.io/pro", 174 | "contributions": [ 175 | "code", 176 | "doc", 177 | "example", 178 | "maintenance", 179 | "userTesting" 180 | ] 181 | } 182 | ], 183 | "commitConvention": "none" 184 | } 185 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | commands: 3 | build-and-test: 4 | steps: 5 | - checkout 6 | - run: 7 | name: Install dependencies (main) 8 | command: npm install 9 | - run: 10 | name: Build next-i18next 11 | command: npm run build 12 | - run: 13 | name: Install examples 14 | command: npm run install:examples 15 | - run: 16 | name: Replace examples install with repo build 17 | command: npm run move-build-to-examples 18 | - run: 19 | name: Build examples 20 | command: npm run build:examples 21 | - run: 22 | name: Lint 23 | command: npm run lint 24 | - run: 25 | name: Typecheck 26 | command: npm run typecheck 27 | - run: 28 | name: Test 29 | command: npm test 30 | - run: 31 | name: e2e 32 | command: npm run test:e2e 33 | - store_artifacts: 34 | path: cypress/screenshots 35 | - store_artifacts: 36 | path: cypress/videos 37 | 38 | jobs: 39 | # node-v12: 40 | # docker: 41 | # - image: circleci/node:12-browsers 42 | # steps: 43 | # - build-and-test 44 | # node-v14: 45 | # docker: 46 | # - image: circleci/node:14-browsers 47 | # steps: 48 | # - build-and-test 49 | node-v16: 50 | docker: 51 | - image: circleci/node:16-browsers 52 | steps: 53 | - build-and-test 54 | # node-v18: 55 | # docker: 56 | # - image: cimg/node:18.4 57 | # steps: 58 | # - build-and-test 59 | 60 | workflows: 61 | node-multi-build: 62 | jobs: 63 | # - node-v12 64 | # - node-v14 65 | # - node-v16 66 | - node-v18 67 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | 12 | # see docs: https://editorconfig.org/ 13 | # see example from a project on GitHub: https://github.com/tailwindlabs/tailwindcss/blob/master/.editorconfig 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": [ 4 | "@typescript-eslint", 5 | "eslint-plugin-import", 6 | "typescript-sort-keys", 7 | "prefer-arrow" 8 | ], 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:react/recommended", 12 | "plugin:@typescript-eslint/recommended", 13 | "plugin:jest/recommended", 14 | "plugin:jest/style", 15 | "plugin:typescript-sort-keys/recommended", 16 | "prettier" 17 | ], 18 | "ignorePatterns": [ 19 | "**/node_modules/*", 20 | "**/out/*", 21 | "**/.next/*", 22 | "**/*package.json" 23 | ], 24 | "rules": { 25 | "jest/no-conditional-expect": 0, 26 | "no-console": 2, 27 | "multiline-comment-style": ["error", "separate-lines"], 28 | "no-return-await": 2, 29 | "arrow-body-style": ["error", "as-needed"], 30 | "import/no-amd": 2, 31 | "import/no-commonjs": 2, 32 | // "import/no-default-export": 2, 33 | "import/no-namespace": 2, 34 | "import/no-nodejs-modules": 0, 35 | "react/prefer-stateless-function": 2, 36 | "react/prop-types": 0, 37 | "sort-keys": 1, 38 | "@typescript-eslint/explicit-function-return-type": 0, 39 | "@typescript-eslint/explicit-module-boundary-types": 0, 40 | "@typescript-eslint/no-explicit-any": 0, 41 | "@typescript-eslint/no-unused-vars": [ 42 | 2, 43 | { 44 | "argsIgnorePattern": "^_" 45 | } 46 | ], 47 | "@typescript-eslint/no-var-requires": 0, 48 | "prefer-arrow/prefer-arrow-functions": [ 49 | "error", 50 | { 51 | "disallowPrototype": true, 52 | "singleReturnOnly": false, 53 | "classPropertiesAllowed": false 54 | } 55 | ], 56 | "prefer-destructuring": [ 57 | "error", 58 | { "object": true, "array": true } 59 | ] 60 | }, 61 | "overrides": [ 62 | { 63 | "files": ["./src/createClient"], 64 | "rules": { 65 | "import/no-default-export": 0 66 | } 67 | }, 68 | { 69 | "files": ["cypress/**/*"], 70 | "plugins": ["cypress"], 71 | "env": { 72 | "cypress/globals": true 73 | }, 74 | "rules": { 75 | "import/no-default-export": 0, 76 | "jest/expect-expect": 0 77 | } 78 | }, 79 | { 80 | "files": ["examples/**/*"], 81 | "rules": { 82 | "react/react-in-jsx-scope": 0 83 | } 84 | }, 85 | { 86 | "files": ["examples/**/pages/**/*"], 87 | "rules": { 88 | "import/no-default-export": 0 89 | } 90 | }, 91 | { 92 | "files": ["**/*.config.js"], 93 | "rules": { 94 | "import/no-commonjs": 0, 95 | "no-undef": 0 96 | } 97 | } 98 | ], 99 | "settings": { 100 | "react": { 101 | "pragma": "React", 102 | "version": "detect" 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 7 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - 'discussion' 8 | - 'feature request' 9 | - 'bug' 10 | - 'breaking change' 11 | - 'doc' 12 | - 'issue' 13 | - 'help wanted' 14 | - 'good first issue' 15 | - 'pr hold' 16 | # Label to use when marking an issue as stale 17 | staleLabel: stale 18 | # Comment to post when marking an issue as stale. Set to `false` to disable 19 | markComment: > 20 | This issue has been automatically marked as stale because it has not had 21 | recent activity. It will be closed if no further activity occurs. Thank you 22 | for your contributions. 23 | # Comment to post when closing a stale issue. Set to `false` to disable 24 | closeComment: false 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened, ready_for_review] 9 | branches: 10 | - '**' 11 | paths-ignore: 12 | - 'docs/**' 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | test: 19 | runs-on: ubuntu-latest 20 | timeout-minutes: 10 21 | strategy: 22 | matrix: 23 | node-version: [20.x, 18.x] 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - name: Use Node.js ${{ matrix.node-version }} 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: ${{ matrix.node-version }} 31 | 32 | - name: 📥 Install 33 | run: npm install 34 | 35 | - name: Install Examples 36 | run: npm run install:examples 37 | 38 | - name: ⓘ Debug next-i18next installation 39 | shell: bash 40 | run: npx envinfo --binaries --npmPackages 41 | 42 | - name: Typecheck 43 | run: npm run typecheck 44 | 45 | - name: Prettier 46 | run: npm run prettier 47 | 48 | - name: Unit tests 49 | run: npm test 50 | 51 | - name: ESLint checks 52 | run: npm run lint 53 | 54 | - name: Build next-i18next 55 | run: npm run build 56 | 57 | - name: Check dist for ecmascript compliance 58 | run: npm run check-dist 59 | 60 | # Temporary hack that replace the ./node_modules/next-i18next content 61 | # by the current build. This is needed cause next-i18next does not work (yet) when the 62 | # build is below current dir. There's few issues left notably that webpack does not play 63 | # well with dynamic import of next-i18next config (and there might not be solution to this). 64 | # @link https://github.com/i18next/next-i18next/pull/2012 65 | # (see also the npm file: in package.json) 66 | - name: Replace examples install with repo build 67 | run: npm run move-build-to-examples 68 | 69 | - name: ⓘ Debug example/simple installation 70 | shell: bash 71 | run: npx envinfo --binaries --npmPackages 72 | 73 | - name: Build examples 74 | run: npm run build:examples 75 | 76 | - name: Check size limits 77 | run: npm run check-size 78 | 79 | - name: E2E tests 80 | run: npm run test:e2e 81 | 82 | - name: Build example with experimental.esmExternals 83 | run: npm run build:example:simple 84 | env: 85 | NEXTJS_ESM_EXTERNALS: true 86 | 87 | - name: Typecheck simple example 88 | run: npm run typecheck:example:simple 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules 3 | 4 | # Dist 5 | dist 6 | .next 7 | out 8 | 9 | # OS 10 | .DS_Store 11 | 12 | # Development Environment 13 | .vscode 14 | .idea 15 | 16 | # Logs 17 | *.log 18 | 19 | # Testing 20 | coverage/ 21 | /coverage 22 | /cypress/videos 23 | /cypress/screenshots 24 | /cypress/plugins 25 | /cypress/support 26 | /cypress/fixtures/example.json 27 | cypress.json 28 | !/cypress/cypress.json 29 | 30 | # npm (all lock files except the root one) 31 | package-lock.json 32 | !/package-lock.json 33 | 34 | # typescript 35 | tsconfig.tsbuildinfo -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Untranspiled source and examples 2 | examples 3 | src 4 | 5 | # Dependencies 6 | node_modules 7 | yarn.lock 8 | 9 | # NextJs 10 | .next 11 | out 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Development Environment 17 | .vscode 18 | 19 | # Logs 20 | runtime.log 21 | 22 | # Testing 23 | cypress 24 | cypress.json 25 | coverage 26 | jest.* 27 | .e2e 28 | *.test.* 29 | 30 | # Build tools 31 | tsconfig.json 32 | .eslintrc.json 33 | babel.config.json 34 | 35 | # Misc 36 | .circleci 37 | .all-contributorsrc 38 | greenkeeper.json 39 | .github 40 | Procfile 41 | *.md 42 | scripts 43 | docs 44 | cypress 45 | cypress.config.ts 46 | jest.config.js 47 | .eslintrc 48 | .gitignore 49 | examples 50 | src 51 | .husky 52 | coverage 53 | -------------------------------------------------------------------------------- /.size-limit.cjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const fullEsmMaxSize = '5KB' 4 | // Recent Nextjs version have some optimizations for commonjs that 5 | // aren't available on bare-bone webpack. don't worry about threshold 6 | // difference here for cjs. We can keep this till cjs is supported. 7 | const fullCjsMaxSize = '29KB' 8 | 9 | const getSimpleExamplePageLimits = () => { 10 | const dir = './examples/simple/.next' 11 | let manifest 12 | try { 13 | manifest = require(`${dir}/build-manifest.json`) 14 | } catch (e) { 15 | throw new Error( 16 | 'Cannot find a NextJs build folder, did you forget to run build:examples ?' 17 | ) 18 | } 19 | const limitCfg = { 20 | defaultSize: '90kb', 21 | pages: { 22 | '/': '97kb', 23 | '/404': '90kb', 24 | '/_app': '114kb', 25 | '/_error': '94Kb', 26 | '/second-page': '97Kb', 27 | }, 28 | } 29 | let pageLimits = [] 30 | for (const [uri, paths] of Object.entries(manifest.pages)) { 31 | pageLimits.push({ 32 | name: `Example app: page '${uri}'`, 33 | limit: limitCfg.pages?.[uri] ?? limitCfg.defaultSize, 34 | webpack: false, 35 | path: paths.map(p => `${dir}/${p}`), 36 | }) 37 | } 38 | return pageLimits 39 | } 40 | 41 | const modifyWebpackConfig = config => { 42 | config.resolve = {} 43 | config.resolve.fallback = { path: false, fs: false } 44 | } 45 | 46 | /** 47 | * Will ensure esm tree-shakeability and total size are within expectations. 48 | * 49 | * @link https://github.com/ai/size-limit/ 50 | * @type {{name: string, path: string[], limit: string, import?: string, webpack?: boolean, modifyWebpackConfig: any}[]} 51 | */ 52 | module.exports = [ 53 | // ################################################### 54 | // Dist ESM full bundle 55 | // ################################################### 56 | { 57 | name: 'ESM (import everything *)', 58 | path: ['dist/esm/index.js'], 59 | import: '*', 60 | limit: fullEsmMaxSize, 61 | modifyWebpackConfig, 62 | }, 63 | // ################################################### 64 | // Fist commonjs full bundle 65 | // Tip: older versions of nextjs will not tree-shake 66 | // cjs very well. This explains threshold differences 67 | // ################################################### 68 | { 69 | name: 'CJS (require everything *)', 70 | path: ['dist/commonjs/index.js'], 71 | import: '*', 72 | webpack: true, 73 | limit: fullCjsMaxSize, 74 | modifyWebpackConfig, 75 | }, 76 | // ################################################### 77 | // Example apps 78 | // ################################################### 79 | ...getSimpleExamplePageLimits(), 80 | ] 81 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 15.4.2 2 | 3 | - types: re-add @types/hoist-non-react-statics to dependencies [#2316](https://github.com/i18next/next-i18next/pull/2316) 4 | 5 | ## 15.4.1 6 | 7 | - Move @types/hoist-non-react-statics to devDependencies [#2310](https://github.com/i18next/next-i18next/pull/2310) 8 | 9 | ## 15.4.0 10 | 11 | - support i18next v24 12 | 13 | ## 15.3.1 14 | 15 | - update some i18next dependencies to address [#2288](https://github.com/i18next/next-i18next/issues/2288) 16 | 17 | ## 15.3.0 18 | 19 | - Only overwrite ns config if it provided [#2270](https://github.com/i18next/next-i18next/pull/2270) 20 | 21 | ## 15.2.0 22 | 23 | - add possibility to pass resources directly via config and set localePath to null 24 | 25 | ## 15.1.2 26 | 27 | - fix: Install error with react-i18next v14 [#2248](https://github.com/i18next/next-i18next/issues/2248) 28 | 29 | ## 15.1.1 30 | 31 | - optimize/fix last change for turbo 32 | 33 | ## 15.1.0 34 | 35 | - try to fix for turbo [#2222](https://github.com/i18next/next-i18next/issues/2222) 36 | 37 | ## 15.0.0 38 | 39 | - refactor: reuse existing i18next instance [#2226](https://github.com/i18next/next-i18next/pull/2226) 40 | -> If you use client side pages (not lazy loading translations), like described [here](https://github.com/i18next/i18next-http-backend/tree/master/example/next#4-setup-your-client-rendered-pagescomponents), make sure you set the `partialBundledLanguages` option to true, like [here](https://github.com/i18next/i18next-http-backend/tree/master/example/next#4-setup-your-client-rendered-pagescomponents). 41 | 42 | ## 14.0.3 43 | 44 | - fix: correct namespacesRequired type in serverSideTranslations [#2203](https://github.com/i18next/next-i18next/pull/2201) 45 | 46 | ## 14.0.2 47 | 48 | - try to fix namespacesRequired in serverSideTranslations is not type-safe anymore [#2201](https://github.com/i18next/next-i18next/issues/2201) 49 | 50 | ## 14.0.0 51 | 52 | - requires i18next >= v23.0.1 53 | - requires react-i18next >= v13.0.0 54 | 55 | ### Breaking changes 56 | 57 | [i18next 23.0.0](https://github.com/i18next/i18next/releases/tag/v23.0.0) dropped support for older browsers. 58 | From nextjs 13, you can use the [transpilePackages](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages) 59 | to avoid issues. 60 | 61 | ```js 62 | /** @type {import('next').NextConfig} */ 63 | const nextConfig = { 64 | transpilePackages: ['i18next'], 65 | } 66 | module.exports = nextConfig 67 | ``` 68 | 69 | ## 13.3.0 70 | 71 | - using a custom backend on server side should also lazy load the passed namespaces 72 | 73 | ## 13.2.2 74 | 75 | - pageProps may be undefined on strange setups [#2109](https://github.com/i18next/next-i18next/issues/2109)" 76 | 77 | ## 13.2.1 78 | 79 | - types: fix serverSideTranslations args [#2104](https://github.com/i18next/next-i18next/pull/2104)" 80 | 81 | ## 13.2.0 82 | 83 | - types: Update serverSideTranslation args type [#2097](https://github.com/i18next/next-i18next/pull/2097)" 84 | 85 | ## 13.1.6 86 | 87 | - fix: allow user provided affixes to be used without providing localeStructure [#2100](https://github.com/i18next/next-i18next/pull/2100)" 88 | 89 | ## 13.1.5 90 | 91 | - [#2089](https://github.com/i18next/next-i18next/pull/2089), more stable impelementation of "feat(server-side): custom default config path [#2084](https://github.com/i18next/next-i18next/pull/2084)" 92 | 93 | ## 13.1.4 94 | 95 | - revert #2084 96 | 97 | ## 13.1.3 98 | 99 | - another code snippet for #2084 to try to fix for dynamic pages 100 | 101 | ## 13.1.2 102 | 103 | - different code snippet for #2084 to try to fix for dynamic pages 104 | 105 | ## 13.1.1 106 | 107 | - log config path if no config found 108 | 109 | ## 13.1.0 110 | 111 | - feat(server-side): custom default config path [#2084](https://github.com/i18next/next-i18next/pull/2084) 112 | 113 | ## 13.0.3 114 | 115 | - Error if custom localeStructure provided, but no ns option defined. 116 | 117 | ## 13.0.2 118 | 119 | ### Fix 120 | 121 | - Types: DefaultNamespace import, see [#2061](https://github.com/i18next/next-i18next/pull/2061). 122 | 123 | ## 13.0.1 124 | 125 | ### Fix 126 | 127 | - Fix missing `i18n.localeDetection` in UserConfig, see [#2057](https://github.com/i18next/next-i18next/pull/2057). 128 | - Update examples to latest i18next in [#2058](https://github.com/i18next/next-i18next/pull/2058) 129 | 130 | **Caution** 131 | 132 | If you're experiencing typecheck errors regarding keys: ensure `i18next` is at least `^22.4.3` in your 133 | package.json (then run install), see [#2058](https://github.com/i18next/next-i18next/pull/2058). 134 | 135 | ## 13.0.0 136 | 137 | The v13.0.0 release is a major version to improve stability and general experience. 138 | It comes with 2 easy changes related to installation. Existing code shouldn't be impacted. 139 | Details can be found in the [UPGRADING.md](https://github.com/i18next/next-i18next/blob/master/UPGRADING.md#version-1300) document. 140 | 141 | ### Breaking changes 142 | 143 | - [react-i18next](https://github.com/i18next/react-i18next) and [i18next](https://github.com/i18next/i18next) 144 | have been moved to peer-dependencies. They must be installed 145 | in your app ([#1966](https://github.com/i18next/next-i18next/pull/1966)) 146 | 147 | ```bash 148 | # Add react-i18next > 12.0.0 and i18next > 22.0.4 to your app dependencies 149 | npm install react-i18next i18next --save # NPM 150 | yarn add react-i18next i18next # Yarn 151 | pnpm add react-i18next i18next --save # PNPM 152 | ``` 153 | 154 | This might solve issues with duplicates and multiple i18n context instances. 155 | If you encounter any issue, please read the [Troubleshoot](https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md) doc 156 | before posting an issue. 157 | 158 | - Types augmentations are now handled by i18next instead of react-i18next ([#1997](https://github.com/i18next/next-i18next/pull/1997)). 159 | See the upgrade [document here](https://github.com/i18next/next-i18next/blob/master/UPGRADING.md#keys-typings). 160 | 161 | ### New 162 | 163 | - Support for NextJs 13 (excluding new experimental layout / rsc) 164 | - Upgrade to [i18next v22](https://github.com/i18next/i18next/releases) and react-i18next v12, see [#1966](https://github.com/i18next/next-i18next/pull/1966) 165 | - Support for node 18 lts [#2017](https://github.com/i18next/next-i18next/pull/2017) 166 | 167 | ### Fix 168 | 169 | - Fix types for appWithTranslation [#1987](https://github.com/i18next/next-i18next/pull/1987) 170 | 171 | ### New minimum versions 172 | 173 | We've dropped support for nextjs < 12.0.0 / react < 17.0.2 ([#1983](https://github.com/i18next/next-i18next/pull/1983)) 174 | and node < 14 ([#1974](https://github.com/i18next/next-i18next/pull/1974)). 175 | 176 | ## 12.1.0 177 | 178 | - fix: appWithTranslation re-renders \_app when the locale is changed (#1954) 179 | - feat: introduce onPreInitI18next option (#1960) 180 | 181 | ## 12.0.1 182 | 183 | - fix: fallbackLng if namespaces are undefined (#1943 closes #1941) 184 | 185 | ## 12.0.0 186 | 187 | **Why a major version?** 188 | The following changes could lead to more languages being loaded, which could increase the page size. 189 | 190 | - feat: improve fallback language handling (#1927) 191 | - feat: add support for nonExplicitSupportedLngs (#1930) 192 | 193 | ## 11.3.0 194 | 195 | - feat: introduce extraLocales (#1916) 196 | 197 | ## 11.2.2 198 | 199 | - fix: pass namespaces to the client also for custom backends (#1913) 200 | 201 | ## 11.2.1 202 | 203 | - fix: pass namespaces to the client (#1912 closes #1839) 204 | 205 | ## 11.2.0 206 | 207 | - feat: support nested namespace structure (#1911) 208 | 209 | ## 11.1.1 210 | 211 | - fix: remove postinstall script 212 | 213 | ## 11.1.0 214 | 215 | - first release with new project ownership 216 | - update most dependencies 217 | - update docs and example 218 | - feat: support default locale by ignoring it (#1679) 219 | 220 | ## 11.0.0 221 | 222 | **Features:** 223 | 224 | - Allow client side translation loading (8132efd) 225 | 226 | **Documentation:** 227 | 228 | - Link to `react-i18next` config options (422a0f3) 229 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Setup local dev environment 4 | 5 | 1. Clone the repo 6 | 2. Ensure you have a node version >=14, npm > 8 7 | 8 | ## Working on the code 9 | 10 | Run npm install 11 | 12 | ```bash 13 | npm install 14 | ``` 15 | 16 | Useful commands: 17 | 18 | | Name | Description | 19 | | ------------------------ | ---------------------------------------------------------------------- | 20 | | `npm run lint` | Run eslint over the codebase. | 21 | | `npm run typecheck` | Run typescript typechecking. | 22 | | `npm run test` | Run unit tests. | 23 | | `npm run build` | Run a build (creates dist files in ./dist folder) | 24 | | `npm run check-dist` | Check dist files for ecmascript compliance (requires build). | 25 | | `npm run check-size` | Ensure dist files are under a certain size threshold (requires build). | 26 | | `npm run check-size:why` | Open treemap of dist files (requires build) | 27 | | `npm run clean` | Clean all dist files. | 28 | 29 | ## E2E testing 30 | 31 | First ensure the `examples/simple` app is installed. 32 | 33 | ```bash 34 | npm run install:example:simple 35 | ``` 36 | 37 | To run the e2e test suite 38 | 39 | ```bash 40 | npm run build && npm run move-build-to-examples 41 | npm run build:examples 42 | npm run test:e2e 43 | ``` 44 | 45 | ## Working on examples 46 | 47 | Examples nextjs apps live in the `examples/` folder. 48 | 49 | ```bash 50 | npm run install:examples 51 | npm run build:examples 52 | ``` 53 | 54 | ## PRs 55 | 56 | All PRs must pass all checks before they will be considered for review. 57 | 58 | ## Notes 59 | 60 | Please be sure to comply with our [Developer's Certificate of Origin](https://github.com/i18next/i18next/blob/master/CONTRIBUTING.md) 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 i18next 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TROUBLESHOOT.md: -------------------------------------------------------------------------------- 1 | ## Troubleshoot 2 | 3 | ### Issues 4 | 5 | #### Need to pass in an i18next instance 6 | 7 | The `next-i18next` / `react-i18next` packages are relying on a react context to 8 | share data. Due to the nature of nextjs/ssr/react, the variety of package managers, 9 | their configurations and versions, you might encounter the following warning: 10 | 11 | ``` 12 | You will need to pass in an i18next instance by using initReactI18next 13 | ``` 14 | 15 | To fix: 16 | 17 | 1. Ensure no duplicate versions of `ì18next` and/or `react-i18next` co-exists. See [debug installation](#how-to-debug-installation). 18 | 2. Optionally adapt your setup to [explicitly pass the config](#how-to-explicitly-pass-the-config) recommendations. 19 | 20 | #### Can't find next-i18next.config.js 21 | 22 | In monorepo or package managers creating symlinks in node_modules (ie: pnpm) you 23 | might experience. 24 | 25 | ``` 26 | (...)next-i18next/dist/commonjs/serverSideTranslations.js 27 | Critical dependency: the request of a dependency is an expression 28 | Cannot find module '(...)app/next-i18next.config.js' 29 | ``` 30 | 31 | You can solve it by [explicitly passing the config](#how-to-explicitly-pass-the-config). 32 | 33 | ### Recipes 34 | 35 | #### How to explicitly pass the config 36 | 37 | By default, next-i18next will automatically load a `next-i18n.config.js` located in the project root 38 | directory. In monorepos / workspace enabled projects the detection of the current project directory 39 | might fail (depending on where the package manager actually hoist the next-i18next package). If you're impacted 40 | a good way to circumvent the issue is to pass the config explicitly. It also allows 41 | to choose a different extension (.mjs, .cjs). 42 | 43 | This generally fix [can't find next-i18next.config.js](#cant-find-next-i18nextconfigjs) (and eventual [need to pass in an i18next instance](#need-to-pass-in-an-i18next-instance)). 44 | 45 | 1. in _app.tsx 46 | 47 | ```tsx 48 | // _app.tsx 49 | import type { AppProps } from 'next/app' 50 | import { appWithTranslation } from 'next-i18next' 51 | import nextI18NextConfig from '../next-i18next.config' 52 | const MyApp = ({ Component, pageProps }: AppProps) => ( 53 | 54 | ) 55 | export default appWithTranslation(MyApp, nextI18NextConfig) 56 | ``` 57 | 58 | 2. For `getServerSideProps()` and `getStaticProps()` the recommended approach is 59 | to wrap our [serverSideTranslations()](https://github.com/i18next/next-i18next/blob/master/src/serverSideTranslations.ts) in a separate file where you can inject the 60 | configuration. 61 | 62 | ```typescript 63 | // ie: ./lib/i18n/getServerTranslations.ts 64 | import type { Namespace } from 'i18next'; 65 | import type { SSRConfig, UserConfig } from 'next-i18next' 66 | import { serverSideTranslations } from 'next-i18next/serverSideTranslations' 67 | import nextI18nextConfig from '../../next-i18next.config' 68 | 69 | type ArrayElementOrSelf = T extends Array ? U[] : T[]; 70 | 71 | export const getServerTranslations = async ( 72 | locale: string, 73 | namespacesRequired?: ArrayElementOrSelf | undefined, 74 | configOverride?: UserConfig, 75 | extraLocales?: string[] | false 76 | ): Promise => { 77 | const config = configOverride ?? nextI18nextConfig 78 | return serverSideTranslations(locale, namespacesRequired, config, extraLocales) 79 | } 80 | ``` 81 | 82 | And use it instead of `serverSideTranslations`: 83 | 84 | ```typescript 85 | import { getServerTranslations } from '@/lib/i18n'; 86 | const i18nNamespaces = ['demo']; 87 | 88 | export default function DemoRoute( 89 | props: InferGetStaticPropsType 90 | ) { 91 | // ... 92 | } 93 | 94 | export const getStaticProps: GetStaticProps = async (context) => { 95 | const { locale } = context; 96 | return { 97 | props: { 98 | ...(await getServerTranslations(locale, i18nNamespaces)), 99 | }, 100 | }; 101 | }; 102 | ``` 103 | 104 | #### How to debug installation 105 | 106 | 1. Since v13.0.0, [i18next](https://github.com/i18next/i18next) and [react-i18next](https://github.com/i18next/react-i18next) 107 | must be explicitly installed in your app dependencies as well (peer-deps of next-i8next). Check first they are well present 108 | in your package.json (`"i18next": "^22.0.6"` and `"react-i18next": "^12.0.0"`) 109 | 110 | 111 | 2. Recent versions of pnpm (>=7.29) / yarn (>=3) / npm (>=8) should be able to dedupe installed versions for you. 112 | In case of issue, try to dedupe manually 113 | 114 | | PM | Check | Fix (only on semver) | 115 | |----------------|----------------------------------|---------------------------| 116 | | yarn 1 | `npx -y yarn-deduplicate --list` | `npx -y yarn-deduplicate` | 117 | | yarn 2+ | `yarn dedupe --list` | `yarn dedupe` | 118 | | pnpm < 7.23.0 | `npx -y pnpm-deduplicate --list` | `npx -y pnpm-deduplicate` | 119 | | pnpm >= 7.23.0 | _not available_ | `pnpm dedupe` | 120 | | npm 8 | _not available_ | `npm dedupe` | 121 | 122 | > You can also list duplicate with `npm why -r next-i18next i18next`, `pnpm why -r next-i18next i18next` 123 | > or `yarn why -R next-i18next && yarn why -R i18next`. 124 | 125 | 3. PNPM < 7.29.0 might have issues with peer deduplication in monorepo. This will be addressed in v8, but 126 | starting with 7.29.0 you can already set these settings in `.npmrc`. 127 | 128 | ``` 129 | # https://pnpm.io/next/npmrc 130 | use-lockfile-v6=true 131 | # https://github.com/pnpm/pnpm/releases/tag/v7.29.0 132 | dedupe-peer-dependents=true 133 | resolve-peers-from-workspace-root=true 134 | ``` 135 | 136 | After fixing potential duplicates, run an installation (or update). A new lock file should be generated. 137 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | ## Version 13.0.0 2 | 3 | Version 13.0.0 comes with 2 breaking changes in order to improve developer experience and 4 | stability. 5 | 6 | ### New installation requirements 7 | 8 | Both `i18next` and `react-i18next` have been moved to [peerDependencies](https://github.com/npm/rfcs/blob/main/implemented/0030-no-install-optional-peer-deps.md) 9 | and must be installed part of `next-i18next`. When upgrading don't forget to add them to your dependencies: 10 | 11 | ```bash 12 | npm install react-i18next i18next --save # NPM 13 | yarn add react-i18next i18next # Yarn 14 | pnpm add react-i18next i18next --save # PNPM 15 | ``` 16 | 17 | Minimum versions supported are `i18next@^22.0.6` and `react-i18next@^12.0.0`. 18 | 19 | This move was done in the hope to avoid issues regarding duplicates. In case of issue when 20 | uĝrading, see the [TROUBLESHOOT](https://github.com/i18next/next-i18next/blob/master/TROUBLESHOOT.md#need-to-pass-in-an-i18next-instance) 21 | to ensure peer-dependencies are properly installed. 22 | 23 | ### Keys typings 24 | 25 | If you're using typescript type augmentation for your locale keys, they've been moved from `react-i18next` to [i18next](https://www.i18next.com/overview/typescript). 26 | Rename `@types/react-i18next.d.ts` to `@types/i18next.d.ts` and be sure to update the imports: 27 | 28 | ```typescript 29 | import 'i18next' // before v13.0.0 -> import 'react-i18next'; 30 | import type common from '../public/locales/en/common.json' 31 | import type other from '../public/locales/en/other.json' 32 | 33 | interface I18nNamespaces { 34 | common: typeof common 35 | other: typeof other 36 | } 37 | // before v13.0.0 -> declare module 'react-i18next' 38 | declare module 'i18next' { 39 | interface CustomTypeOptions { 40 | defaultNS: 'common' 41 | resources: I18nNamespaces 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es": { 4 | "presets": [ 5 | "@babel/preset-typescript", 6 | [ 7 | "@babel/env", 8 | { 9 | "modules": false, 10 | "targets": { 11 | "node": "current" 12 | } 13 | } 14 | ], 15 | ["@babel/preset-react"] 16 | ] 17 | }, 18 | "cjs": { 19 | "presets": [ 20 | "@babel/preset-typescript", 21 | [ 22 | "@babel/preset-env", 23 | { 24 | "useBuiltIns": "usage", 25 | "corejs": { 26 | "version": 3 27 | } 28 | } 29 | ], 30 | [ 31 | "next/babel", 32 | { 33 | "transform-runtime": { 34 | "corejs": false, 35 | "helpers": true, 36 | "regenerator": true, 37 | "useESModules": false 38 | } 39 | } 40 | ] 41 | ], 42 | "plugins": [ 43 | "@babel/proposal-class-properties", 44 | [ 45 | "add-module-exports", 46 | { 47 | "addDefaultProperty": true 48 | } 49 | ] 50 | ] 51 | }, 52 | "esm": { 53 | "presets": [ 54 | "@babel/preset-typescript", 55 | [ 56 | "@babel/preset-env", 57 | { 58 | "modules": false, 59 | "targets": { 60 | "browsers": ["last 2 versions", "ie >= 11"] 61 | } 62 | } 63 | ], 64 | [ 65 | "next/babel", 66 | { 67 | "transform-runtime": { 68 | "corejs": false, 69 | "helpers": true, 70 | "regenerator": true, 71 | "useESModules": false 72 | }, 73 | "preset-env": { 74 | "modules": false 75 | } 76 | } 77 | ] 78 | ], 79 | "plugins": [ 80 | "@babel/proposal-class-properties", 81 | [ 82 | "add-module-exports", 83 | { 84 | "addDefaultProperty": true 85 | } 86 | ] 87 | ] 88 | }, 89 | "test": { 90 | "presets": [ 91 | "@babel/preset-typescript", 92 | [ 93 | "@babel/env", 94 | { 95 | "targets": { 96 | "node": "current" 97 | }, 98 | "modules": "commonjs" 99 | } 100 | ], 101 | ["@babel/preset-react"] 102 | ], 103 | "plugins": [ 104 | "@babel/proposal-class-properties", 105 | [ 106 | "add-module-exports", 107 | { 108 | "addDefaultProperty": true 109 | } 110 | ] 111 | ] 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress' 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:3000', 6 | supportFile: false, 7 | // 8 | // setupNodeEvents(on, config) { 9 | // // implement node event listeners here 10 | // }, 11 | // 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('basic e2e test run', () => { 2 | it('should display translated content across routes', () => { 3 | cy.visit('/') 4 | 5 | // Test English content 6 | cy.contains('A simple example') 7 | cy.contains('To second page').click() 8 | cy.location('pathname', { timeout: 10000 }).should( 9 | 'equal', 10 | '/second-page' 11 | ) 12 | cy.contains('A second page') 13 | cy.contains('Back to home').click() 14 | cy.location('pathname', { timeout: 10000 }).should('equal', '/') 15 | cy.contains('A simple example') 16 | // cy.contains('To auto static page (en)').click() 17 | // cy.location('pathname', { timeout: 10000 }).should( 18 | // 'equal', 19 | // '/auto-static' 20 | // ) 21 | // cy.contains('hello_en') 22 | // cy.contains('Back to home').click() 23 | // cy.location('pathname', { timeout: 10000 }).should('equal', '/') 24 | // cy.contains('A simple example') 25 | 26 | // Test German content 27 | cy.contains('Change locale').click() 28 | cy.location('pathname', { timeout: 10000 }).should( 29 | 'equal', 30 | '/de' 31 | ) 32 | cy.contains('Ein einfaches Beispiel') 33 | cy.contains('Zur zweiten Seite').click() 34 | cy.location('pathname', { timeout: 10000 }).should( 35 | 'equal', 36 | '/de/second-page' 37 | ) 38 | cy.contains('Eine zweite Seite') 39 | cy.contains('Zurück zur Hauptseite').click() 40 | cy.location('pathname', { timeout: 10000 }).should( 41 | 'equal', 42 | '/de' 43 | ) 44 | cy.contains('Ein einfaches Beispiel') 45 | // cy.contains('To auto static page (de)').click() 46 | // cy.location('pathname', { timeout: 10000 }).should( 47 | // 'equal', 48 | // '/de/auto-static' 49 | // ) 50 | // cy.contains('hello_de') 51 | // cy.contains('Zurück zur Hauptseite').click() 52 | // cy.location('pathname', { timeout: 10000 }).should( 53 | // 'equal', 54 | // '/de' 55 | // ) 56 | // cy.contains('Ein einfaches Beispiel') 57 | cy.contains('Sprache wechseln zu').click() 58 | cy.location('pathname', { timeout: 10000 }).should('equal', '/') 59 | cy.contains('A simple example') 60 | 61 | // Test generated version of auto static 62 | // cy.request('/auto-static') 63 | // .its('body') 64 | // .should('include', '

hello_en

') 65 | // cy.request('/de/auto-static') 66 | // .its('body') 67 | // .should('include', '

hello_de

') 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 |
10 |

{t('description')}

11 |

next-i18next v{pkg.version}

12 |

19 | 20 | With using 21 | 22 | locize 23 | 24 | you directly support the future of 25 | 26 | i18next 27 | 28 | . 29 | 30 |

31 |
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 |

15 | next-i18next 16 |
17 |

18 |

{heading}

19 | 20 | 21 | 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/next-i18next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const HttpBackend = require('i18next-http-backend/cjs') 4 | const ChainedBackend = require('i18next-chained-backend').default 5 | const LocalStorageBackend = 6 | require('i18next-localstorage-backend').default 7 | 8 | const isBrowser = typeof window !== 'undefined' 9 | const isDev = process.env.NODE_ENV === 'development' 10 | 11 | /** 12 | * @type {import('next-i18next').UserConfig} 13 | */ 14 | module.exports = { 15 | // It should config backend, use, partialBundledLanguages in case you want translate for auto static page 16 | backend: { 17 | backendOptions: [ 18 | { expirationTime: isDev ? 60 * 1000 : 60 * 60 * 1000 }, 19 | {}, 20 | ], // 1 hour 21 | backends: isBrowser ? [LocalStorageBackend, HttpBackend] : [], 22 | }, 23 | // https://www.i18next.com/overview/configuration-options#logging 24 | debug: isDev, 25 | i18n: { 26 | defaultLocale: 'en', 27 | locales: ['en', 'de'], 28 | }, 29 | initAsync: false, 30 | /** To avoid issues when deploying to some paas (vercel...) */ 31 | localePath: 32 | typeof window === 'undefined' 33 | ? require('path').resolve('./public/locales') 34 | : '/locales', 35 | ns: ['common', 'footer', 'second-page', 'staticpage'], 36 | partialBundledLanguages: isBrowser, 37 | reloadOnPrerender: process.env.NODE_ENV === 'development', 38 | use: isBrowser ? [ChainedBackend] : [], 39 | /** 40 | * @link https://github.com/i18next/next-i18next#6-advanced-configuration 41 | */ 42 | // saveMissing: false, 43 | // strictMode: true, 44 | // serializeConfig: false, 45 | // react: { useSuspense: false } 46 | } 47 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/next-utils.config.js: -------------------------------------------------------------------------------- 1 | const pc = require('picocolors') 2 | 3 | const nextUtilsConfig = () => { 4 | const trueEnv = ['true', '1', 'yes'] 5 | const esmExternals = trueEnv.includes( 6 | process.env?.NEXTJS_ESM_EXTERNALS ?? 'false' 7 | ) 8 | const tsconfigPath = process.env.NEXTJS_TSCONFIG_PATH 9 | ? process.env.NEXTJS_TSCONFIG_PATH 10 | : './tsconfig.json' 11 | 12 | // eslint-disable-next-line no-console 13 | console.warn( 14 | `${pc.green('warn -')} experimental.esmExternals is ${ 15 | esmExternals ? 'enabled' : 'disabled' 16 | }` 17 | ) 18 | return { 19 | esmExternals, 20 | tsconfigPath, 21 | } 22 | } 23 | 24 | module.exports = { 25 | loadCustomBuildParams: nextUtilsConfig, 26 | } 27 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { i18n } = require('./next-i18next.config.js') 3 | 4 | // You can remove the following 2 lines when integrating our example. 5 | const { loadCustomBuildParams } = require('./next-utils.config') 6 | const { esmExternals = false, tsconfigPath } = 7 | loadCustomBuildParams() 8 | 9 | /** @type {import('next').NextConfig} */ 10 | const nextConfig = { 11 | experimental: { 12 | esmExternals, // https://nextjs.org/blog/next-11-1#es-modules-support 13 | }, 14 | i18n, 15 | reactStrictMode: true, 16 | typescript: { 17 | tsconfigPath, 18 | }, 19 | } 20 | 21 | module.exports = nextConfig 22 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-i18next-example-simple", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "next", 8 | "build": "next build", 9 | "start": "next start -p ${PORT:=3000}", 10 | "typecheck": "tsc --project ./tsconfig.json --noEmit", 11 | "clean": "rimraf .next", 12 | "nuke:install": "rimraf ./node_modules ./package-lock.json", 13 | "toc": "i18next-resources-for-ts toc -i ./public/locales/en -o ./@types/resources.ts", 14 | "merge": "i18next-resources-for-ts merge -i ./public/locales/en -o ./@types/resources.json", 15 | "interface": "i18next-resources-for-ts interface -i ./public/locales/en -o ./@types/resources.d.ts" 16 | }, 17 | "dependencies": { 18 | "i18next": "24.2.2", 19 | "i18next-chained-backend": "^4.6.2", 20 | "i18next-http-backend": "^3.0.2", 21 | "i18next-localstorage-backend": "^4.2.0", 22 | "next": "^15.1.6", 23 | "next-i18next": "^15.4.2", 24 | "react": "^19.0.0", 25 | "react-dom": "^19.0.0", 26 | "react-i18next": "^15.4.0" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^22.13.1", 30 | "@types/react": "^19.0.8", 31 | "@types/react-dom": "^19.0.3", 32 | "eslint-config-next": "^15.1.6", 33 | "i18next-resources-for-ts": "1.5.0", 34 | "picocolors": "^1.1.1", 35 | "rimraf": "^6.0.1", 36 | "typescript": "^5.7.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppProps } from 'next/app' 2 | import { appWithTranslation } from 'next-i18next' 3 | import nextI18NextConfig from '../next-i18next.config' 4 | 5 | const MyApp = ({ Component, pageProps }: AppProps) => ( 6 | 7 | ) 8 | 9 | // https://github.com/i18next/next-i18next#unserializable-configs 10 | export default (appWithTranslation as any)(MyApp, nextI18NextConfig) 11 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { 2 | Html, 3 | Head, 4 | Main, 5 | NextScript, 6 | } from 'next/document' 7 | import type { DocumentProps } from 'next/document' 8 | import i18nextConfig from '../next-i18next.config' 9 | 10 | type Props = DocumentProps & { 11 | // add custom document props 12 | } 13 | 14 | class MyDocument extends Document { 15 | render() { 16 | const currentLocale = 17 | this.props.__NEXT_DATA__.locale ?? 18 | i18nextConfig.i18n.defaultLocale 19 | return ( 20 | 21 | 22 | 23 | 27 | 28 | 32 | 36 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | ) 48 | } 49 | } 50 | 51 | export default MyDocument 52 | -------------------------------------------------------------------------------- /examples/auto-static-optimize/pages/auto-static.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | import { useTranslation } from 'next-i18next' 4 | 5 | import { Header } from '../components/Header' 6 | import { Footer } from '../components/Footer' 7 | 8 | const StaticPage = () => { 9 | const { t } = useTranslation([ 10 | 'common', 11 | 'second-page', 12 | 'staticpage', 13 | ]) 14 | 15 | return ( 16 | <> 17 |
18 |
22 |

{t('h1')}

23 |

{t('staticpage:hi')}

24 | 25 | 28 | 29 |
30 |