From 07364953f597498006b66577231ea377edba8c0d Mon Sep 17 00:00:00 2001 From: Nam PHAM Date: Mon, 15 Apr 2024 17:42:16 +0200 Subject: [PATCH] Only overwrite `ns` config if it provided (#2270) * draft: test fail when process.browser eq false * fix: remove deprecated process.browser and fix test fail * feat: only overwrite `ns` config if it provided * test: add example for auto-static-optimize and e2e test * fix: use ns and initImmediate: false to fix mismatch ssr --- cypress/e2e/spec.cy.ts | 30 +++- .../auto-static-optimize/@types/i18next.d.ts | 18 +++ .../auto-static-optimize/@types/resources.ts | 13 ++ .../components/Footer.tsx | 33 +++++ .../components/Header.tsx | 23 +++ examples/auto-static-optimize/next-env.d.ts | 5 + .../next-i18next.config.js | 46 ++++++ .../auto-static-optimize/next-utils.config.js | 26 ++++ examples/auto-static-optimize/next.config.js | 21 +++ examples/auto-static-optimize/package.json | 38 +++++ examples/auto-static-optimize/pages/_app.tsx | 10 ++ .../auto-static-optimize/pages/_document.tsx | 51 +++++++ .../pages/auto-static.tsx | 39 ++++++ examples/auto-static-optimize/pages/index.tsx | 132 ++++++++++++++++++ .../pages/second-page.tsx | 51 +++++++ examples/auto-static-optimize/public/app.css | 99 +++++++++++++ .../public/locales/de/common.json | 26 ++++ .../public/locales/de/footer.json | 4 + .../public/locales/de/second-page.json | 5 + .../public/locales/de/staticpage.json | 1 + .../public/locales/en/common.json | 26 ++++ .../public/locales/en/footer.json | 4 + .../public/locales/en/second-page.json | 5 + .../public/locales/en/staticpage.json | 1 + examples/auto-static-optimize/tsconfig.json | 30 ++++ examples/auto-static-optimize/vercel.json | 5 + package.json | 6 +- src/appWithTranslation.client.test.tsx | 6 +- src/appWithTranslation.tsx | 2 +- src/config/defaultConfig.ts | 2 +- 30 files changed, 750 insertions(+), 8 deletions(-) create mode 100644 examples/auto-static-optimize/@types/i18next.d.ts create mode 100644 examples/auto-static-optimize/@types/resources.ts create mode 100644 examples/auto-static-optimize/components/Footer.tsx create mode 100644 examples/auto-static-optimize/components/Header.tsx create mode 100644 examples/auto-static-optimize/next-env.d.ts create mode 100644 examples/auto-static-optimize/next-i18next.config.js create mode 100644 examples/auto-static-optimize/next-utils.config.js create mode 100644 examples/auto-static-optimize/next.config.js create mode 100644 examples/auto-static-optimize/package.json create mode 100644 examples/auto-static-optimize/pages/_app.tsx create mode 100644 examples/auto-static-optimize/pages/_document.tsx create mode 100644 examples/auto-static-optimize/pages/auto-static.tsx create mode 100644 examples/auto-static-optimize/pages/index.tsx create mode 100644 examples/auto-static-optimize/pages/second-page.tsx create mode 100644 examples/auto-static-optimize/public/app.css create mode 100644 examples/auto-static-optimize/public/locales/de/common.json create mode 100644 examples/auto-static-optimize/public/locales/de/footer.json create mode 100644 examples/auto-static-optimize/public/locales/de/second-page.json create mode 100644 examples/auto-static-optimize/public/locales/de/staticpage.json create mode 100644 examples/auto-static-optimize/public/locales/en/common.json create mode 100644 examples/auto-static-optimize/public/locales/en/footer.json create mode 100644 examples/auto-static-optimize/public/locales/en/second-page.json create mode 100644 examples/auto-static-optimize/public/locales/en/staticpage.json create mode 100644 examples/auto-static-optimize/tsconfig.json create mode 100644 examples/auto-static-optimize/vercel.json diff --git a/cypress/e2e/spec.cy.ts b/cypress/e2e/spec.cy.ts index 94f811ad6..0b7bb9faf 100644 --- a/cypress/e2e/spec.cy.ts +++ b/cypress/e2e/spec.cy.ts @@ -13,6 +13,15 @@ describe('basic e2e test run', () => { cy.contains('Back to home').click() cy.location('pathname', { timeout: 10000 }).should('equal', '/') cy.contains('A simple example') + cy.contains('To auto static page (en)').click() + cy.location('pathname', { timeout: 10000 }).should( + 'equal', + '/auto-static' + ) + cy.contains('hello_en') + cy.contains('Back to home').click() + cy.location('pathname', { timeout: 10000 }).should('equal', '/') + cy.contains('A simple example') // Test German content cy.contains('Change locale').click() @@ -33,9 +42,28 @@ describe('basic e2e test run', () => { '/de' ) cy.contains('Ein einfaches Beispiel') - + cy.contains('To auto static page (de)').click() + cy.location('pathname', { timeout: 10000 }).should( + 'equal', + '/de/auto-static' + ) + cy.contains('hello_de') + cy.contains('Zurück zur Hauptseite').click() + cy.location('pathname', { timeout: 10000 }).should( + 'equal', + '/de' + ) + cy.contains('Ein einfaches Beispiel') cy.contains('Sprache wechseln zu').click() cy.location('pathname', { timeout: 10000 }).should('equal', '/') cy.contains('A simple example') + + // Test generated version of auto static + cy.request('/auto-static') + .its('body') + .should('include', '

hello_en

') + cy.request('/de/auto-static') + .its('body') + .should('include', '

hello_de

') }) }) diff --git a/examples/auto-static-optimize/@types/i18next.d.ts b/examples/auto-static-optimize/@types/i18next.d.ts new file mode 100644 index 000000000..4bb27ff4d --- /dev/null +++ b/examples/auto-static-optimize/@types/i18next.d.ts @@ -0,0 +1,18 @@ +/** + * If you want to enable locale keys typechecking and enhance IDE experience. + * + * Requires `resolveJsonModule:true` in your tsconfig.json. + * + * @link https://www.i18next.com/overview/typescript + */ +import 'i18next' + +// resources.ts file is generated with `npm run toc` +import resources from './resources.ts' + +declare module 'i18next' { + interface CustomTypeOptions { + defaultNS: 'common' + resources: typeof resources + } +} diff --git a/examples/auto-static-optimize/@types/resources.ts b/examples/auto-static-optimize/@types/resources.ts new file mode 100644 index 000000000..acd395781 --- /dev/null +++ b/examples/auto-static-optimize/@types/resources.ts @@ -0,0 +1,13 @@ +import common from '../public/locales/en/common.json' +import footer from '../public/locales/en/footer.json' +import secondpage from '../public/locales/en/second-page.json' +import staticpage from '../public/locales/en/staticpage.json' + +const resources = { + common, + footer, + 'second-page': secondpage, + staticpage, +} as const + +export default resources diff --git a/examples/auto-static-optimize/components/Footer.tsx b/examples/auto-static-optimize/components/Footer.tsx new file mode 100644 index 000000000..ae7a09a40 --- /dev/null +++ b/examples/auto-static-optimize/components/Footer.tsx @@ -0,0 +1,33 @@ +import pkg from 'next-i18next/package.json' +import { useTranslation, Trans } from 'next-i18next' +import type { FC } from 'react' + +export const Footer: FC = () => { + const { t } = useTranslation('footer') + + return ( + + ) +} diff --git a/examples/auto-static-optimize/components/Header.tsx b/examples/auto-static-optimize/components/Header.tsx new file mode 100644 index 000000000..455330890 --- /dev/null +++ b/examples/auto-static-optimize/components/Header.tsx @@ -0,0 +1,23 @@ +import Head from 'next/head' +import type { FC } from 'react' + +type Props = { + heading: string + title: string +} + +export const Header: FC = ({ heading, title }) => ( + <> + + {title} + +

+ next-i18next +
+

+

{heading}

+ + + + +) diff --git a/examples/auto-static-optimize/next-env.d.ts b/examples/auto-static-optimize/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/examples/auto-static-optimize/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/auto-static-optimize/next-i18next.config.js b/examples/auto-static-optimize/next-i18next.config.js new file mode 100644 index 000000000..f1d2439c9 --- /dev/null +++ b/examples/auto-static-optimize/next-i18next.config.js @@ -0,0 +1,46 @@ +// @ts-check + +const HttpBackend = require('i18next-http-backend/cjs') +const ChainedBackend = require('i18next-chained-backend').default +const LocalStorageBackend = + require('i18next-localstorage-backend').default + +const isBrowser = typeof window !== 'undefined' +const isDev = process.env.NODE_ENV === 'development' + +/** + * @type {import('next-i18next').UserConfig} + */ +module.exports = { + // It should config backend, use, partialBundledLanguages in case you want translate for auto static page + backend: { + backendOptions: [ + { expirationTime: isDev ? 60 * 1000 : 60 * 60 * 1000 }, + {}, + ], // 1 hour + backends: isBrowser ? [LocalStorageBackend, HttpBackend] : [], + }, + // https://www.i18next.com/overview/configuration-options#logging + debug: isDev, + i18n: { + defaultLocale: 'en', + locales: ['en', 'de'], + }, + initImmediate: false, + /** To avoid issues when deploying to some paas (vercel...) */ + localePath: + typeof window === 'undefined' + ? require('path').resolve('./public/locales') + : '/locales', + ns: ['common', 'footer', 'second-page', 'staticpage'], + partialBundledLanguages: isBrowser, + reloadOnPrerender: process.env.NODE_ENV === 'development', + use: isBrowser ? [ChainedBackend] : [], + /** + * @link https://github.com/i18next/next-i18next#6-advanced-configuration + */ + // saveMissing: false, + // strictMode: true, + // serializeConfig: false, + // react: { useSuspense: false } +} diff --git a/examples/auto-static-optimize/next-utils.config.js b/examples/auto-static-optimize/next-utils.config.js new file mode 100644 index 000000000..3a056e23b --- /dev/null +++ b/examples/auto-static-optimize/next-utils.config.js @@ -0,0 +1,26 @@ +const pc = require('picocolors') + +const nextUtilsConfig = () => { + const trueEnv = ['true', '1', 'yes'] + const esmExternals = trueEnv.includes( + process.env?.NEXTJS_ESM_EXTERNALS ?? 'false' + ) + const tsconfigPath = process.env.NEXTJS_TSCONFIG_PATH + ? process.env.NEXTJS_TSCONFIG_PATH + : './tsconfig.json' + + // eslint-disable-next-line no-console + console.warn( + `${pc.green('warn -')} experimental.esmExternals is ${ + esmExternals ? 'enabled' : 'disabled' + }` + ) + return { + esmExternals, + tsconfigPath, + } +} + +module.exports = { + loadCustomBuildParams: nextUtilsConfig, +} diff --git a/examples/auto-static-optimize/next.config.js b/examples/auto-static-optimize/next.config.js new file mode 100644 index 000000000..bbef00632 --- /dev/null +++ b/examples/auto-static-optimize/next.config.js @@ -0,0 +1,21 @@ +// @ts-check +const { i18n } = require('./next-i18next.config.js') + +// You can remove the following 2 lines when integrating our example. +const { loadCustomBuildParams } = require('./next-utils.config') +const { esmExternals = false, tsconfigPath } = + loadCustomBuildParams() + +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + esmExternals, // https://nextjs.org/blog/next-11-1#es-modules-support + }, + i18n, + reactStrictMode: true, + typescript: { + tsconfigPath, + }, +} + +module.exports = nextConfig diff --git a/examples/auto-static-optimize/package.json b/examples/auto-static-optimize/package.json new file mode 100644 index 000000000..d78c7f5f5 --- /dev/null +++ b/examples/auto-static-optimize/package.json @@ -0,0 +1,38 @@ +{ + "name": "next-i18next-example-simple", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "dev": "next", + "build": "next build", + "start": "next start -p ${PORT:=3000}", + "typecheck": "tsc --project ./tsconfig.json --noEmit", + "clean": "rimraf .next", + "nuke:install": "rimraf ./node_modules ./package-lock.json", + "toc": "i18next-resources-for-ts toc -i ./public/locales/en -o ./@types/resources.ts", + "merge": "i18next-resources-for-ts merge -i ./public/locales/en -o ./@types/resources.json", + "interface": "i18next-resources-for-ts interface -i ./public/locales/en -o ./@types/resources.d.ts" + }, + "dependencies": { + "i18next": "23.7.13", + "i18next-chained-backend": "^4.6.2", + "i18next-http-backend": "^2.5.0", + "i18next-localstorage-backend": "^4.2.0", + "next": "^14.0.4", + "next-i18next": "^15.1.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^14.0.0" + }, + "devDependencies": { + "@types/node": "^20.10.6", + "@types/react": "^18.2.46", + "@types/react-dom": "^18.2.18", + "eslint-config-next": "^14.0.4", + "i18next-resources-for-ts": "1.4.0", + "picocolors": "^1.0.0", + "rimraf": "^5.0.5", + "typescript": "^5.3.3" + } +} diff --git a/examples/auto-static-optimize/pages/_app.tsx b/examples/auto-static-optimize/pages/_app.tsx new file mode 100644 index 000000000..546b681e2 --- /dev/null +++ b/examples/auto-static-optimize/pages/_app.tsx @@ -0,0 +1,10 @@ +import type { AppProps } from 'next/app' +import { appWithTranslation } from 'next-i18next' +import nextI18NextConfig from '../next-i18next.config' + +const MyApp = ({ Component, pageProps }: AppProps) => ( + +) + +// https://github.com/i18next/next-i18next#unserializable-configs +export default (appWithTranslation as any)(MyApp, nextI18NextConfig) diff --git a/examples/auto-static-optimize/pages/_document.tsx b/examples/auto-static-optimize/pages/_document.tsx new file mode 100644 index 000000000..0ad972287 --- /dev/null +++ b/examples/auto-static-optimize/pages/_document.tsx @@ -0,0 +1,51 @@ +import Document, { + Html, + Head, + Main, + NextScript, +} from 'next/document' +import type { DocumentProps } from 'next/document' +import i18nextConfig from '../next-i18next.config' + +type Props = DocumentProps & { + // add custom document props +} + +class MyDocument extends Document { + render() { + const currentLocale = + this.props.__NEXT_DATA__.locale ?? + i18nextConfig.i18n.defaultLocale + return ( + + + + + + + + + + +
+ + + + ) + } +} + +export default MyDocument diff --git a/examples/auto-static-optimize/pages/auto-static.tsx b/examples/auto-static-optimize/pages/auto-static.tsx new file mode 100644 index 000000000..8430a911b --- /dev/null +++ b/examples/auto-static-optimize/pages/auto-static.tsx @@ -0,0 +1,39 @@ +import Link from 'next/link' + +import { useTranslation } from 'next-i18next' + +import { Header } from '../components/Header' +import { Footer } from '../components/Footer' + +const StaticPage = () => { + const { t } = useTranslation([ + 'common', + 'second-page', + 'staticpage', + ]) + + return ( + <> +
+
+

{t('h1')}

+

{t('staticpage:hi')}

+ + + +
+