From 2c08b2972eb506feefc1d0cee6fe6919600b1aca Mon Sep 17 00:00:00 2001 From: David Porter Date: Wed, 1 Sep 2021 02:47:17 -0700 Subject: [PATCH] Add `reloadOnPrerender` option to reload resources in serverSideTranslations so that developers don't have to restart their server when making changes to their translation JSON files (#1359) * feat: add reloadOnPrerender option to reload resources * tests: add tests for reloadOnPrerender * linter: remove semicolons Co-authored-by: Isaac Hinman --- README.md | 9 +++ src/config/defaultConfig.ts | 1 + ...est.ts => serverSideTranslations.test.tsx} | 65 +++++++++++++++++++ src/serverSideTranslations.ts | 7 ++ src/types.ts | 1 + 5 files changed, 83 insertions(+) rename src/{serverSideTranslations.test.ts => serverSideTranslations.test.tsx} (77%) diff --git a/README.md b/README.md index f5d88451..1667a1a7 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,14 @@ export const getStaticProps = async ({ locale }) => ({ }); ``` +#### Reloading Resources in Development + +Because resources are loaded once when the server is started, any changes made to your translation JSON files in development will not be loaded until the server is restarted. + +In production this does not tend to be an issue, but in development you may want to see updates to your translation JSON files without having to restart your development server each time. To do this, set the `reloadOnPrerender` config option to `true`. + +This option will reload your translations whenever `serverSideTranslations` is called (in `getStaticProps` or `getServerSideProps`). If you are using `serverSideTranslations` in `getServerSideProps`, it is recommended to disable `reloadOnPrerender` in production environments as to avoid reloading resources on each server call. + #### Options | Key | Default value | @@ -221,6 +229,7 @@ export const getStaticProps = async ({ locale }) => ({ | `localeExtension` | `'json'` | | `localePath` | `'./public/locales'` | | `localeStructure` | `'{{lng}}/{{ns}}'` | +| `reloadOnPrerender` | `false` | | `serializeConfig` | `true` | | `strictMode` | `true` | | `use` (for plugins) | `[]` | diff --git a/src/config/defaultConfig.ts b/src/config/defaultConfig.ts index 17b88909..2162a4a7 100644 --- a/src/config/defaultConfig.ts +++ b/src/config/defaultConfig.ts @@ -27,6 +27,7 @@ export const defaultConfig = { react: { useSuspense: true, }, + reloadOnPrerender: false, serializeConfig: true, strictMode: true, use: [], diff --git a/src/serverSideTranslations.test.ts b/src/serverSideTranslations.test.tsx similarity index 77% rename from src/serverSideTranslations.test.ts rename to src/serverSideTranslations.test.tsx index 16991cf0..73696610 100644 --- a/src/serverSideTranslations.test.ts +++ b/src/serverSideTranslations.test.tsx @@ -1,12 +1,41 @@ +import React from 'react' import fs from 'fs' import { UserConfig } from './types' import { serverSideTranslations } from './serverSideTranslations' +import { globalI18n } from './appWithTranslation' +import { renderToString } from 'react-dom/server' +import { appWithTranslation } from './appWithTranslation' jest.mock('fs', () => ({ existsSync: jest.fn(), readdirSync: jest.fn(), })) +const DummyApp = appWithTranslation(() => ( +
Hello world
+)) + +const props = { + pageProps: { + _nextI18Next: { + initialLocale: 'en', + userConfig: { + i18n: { + defaultLocale: 'en', + locales: ['en', 'fr'], + }, + }, + }, + } as any, +} as any + +const renderDummyComponent = () => + renderToString( + , + ) + describe('serverSideTranslations', () => { beforeEach(() => { (fs.existsSync as jest.Mock).mockReturnValueOnce(false); @@ -163,4 +192,40 @@ describe('serverSideTranslations', () => { }, }) }) + + it('calls reloadResources when reloadOnPrerender option is true', async () => { + renderDummyComponent() + + if (globalI18n) { + globalI18n.reloadResources = jest.fn() + } + + await serverSideTranslations('en-US', [], { + i18n: { + defaultLocale: 'en-US', + locales: ['en-US', 'fr-CA'], + }, + reloadOnPrerender: true, + } as UserConfig) + + expect(globalI18n?.reloadResources).toHaveBeenCalledTimes(1) + }) + + it('does not call reloadResources when reloadOnPrerender option is false', async () => { + renderDummyComponent() + + if (globalI18n) { + globalI18n.reloadResources = jest.fn() + } + + await serverSideTranslations('en-US', [], { + i18n: { + defaultLocale: 'en-US', + locales: ['en-US', 'fr-CA'], + }, + reloadOnPrerender: false, + } as UserConfig) + + expect(globalI18n?.reloadResources).toHaveBeenCalledTimes(0) + }) }) diff --git a/src/serverSideTranslations.ts b/src/serverSideTranslations.ts index 38ffebb9..12978fbb 100644 --- a/src/serverSideTranslations.ts +++ b/src/serverSideTranslations.ts @@ -4,6 +4,8 @@ import path from 'path' import { createConfig } from './config/createConfig' import createClient from './createClient' +import { globalI18n } from './appWithTranslation' + import { UserConfig, SSRConfig } from './types' import { FallbackLng } from 'i18next' @@ -60,8 +62,13 @@ export const serverSideTranslations = async ( localeExtension, localePath, fallbackLng, + reloadOnPrerender, } = config + if (reloadOnPrerender) { + await globalI18n?.reloadResources() + } + const { i18n, initPromise } = createClient({ ...config, lng: initialLocale, diff --git a/src/types.ts b/src/types.ts index 620448d3..c9d013a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,7 @@ export type UserConfig = { localeExtension?: string localePath?: string localeStructure?: string + reloadOnPrerender?: boolean serializeConfig?: boolean strictMode?: boolean use?: any[]