diff --git a/__tests__/useTranslation.test.js b/__tests__/useTranslation.test.js index adcd3ac..e19e093 100644 --- a/__tests__/useTranslation.test.js +++ b/__tests__/useTranslation.test.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' import { render, cleanup, fireEvent } from '@testing-library/react' import I18nProvider from '../src/I18nProvider' import useTranslation from '../src/useTranslation' @@ -1549,5 +1549,33 @@ describe('useTranslation', () => { const { container } = render() expect(container.textContent).toContain(expected) }) + + test('`t` is stable', () => { + const Inner = ({ effect }) => { + const { t } = useTranslation() + + useEffect(() => { + const text = t('ns:interpolation', { + count: 3, + }) + effect(text) + }, [effect, t]) + } + + globalThis.__NEXT_TRANSLATE__ = { + namespaces: { + ns: { + interpolation: 'There are {{count}} cats.', + }, + }, + lang: 'en', + config: {}, + } + + const effect = jest.fn() + const { rerender } = render() + rerender() + expect(effect).toBeCalledTimes(1) + }) }) }) diff --git a/package.json b/package.json index bd8e084..8c9d11d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "scripts": { "build": "yarn clean && cross-env NODE_ENV=production && yarn tsc", "clean": "yarn clean:build && yarn clean:examples", - "clean:build": "del lib appWith* Dynamic* I18n* index context loadNa* setLang* Trans* useT* withT* getP* getC* *.d.ts getT transC* wrapT* types formatElements AppDirI18nProvider* createTrans*", + "clean:build": "del lib appWith* Dynamic* I18n* index context loadNa* setLang* Trans* useT* withT* getP* getC* *.d.ts getT transC* wrapT* types formatElements isServer AppDirI18nProvider* createTrans*", "clean:examples": "del examples/**/.next examples/**/node_modules examples/**/yarn.lock", "example": "yarn example:complex", "example:basic": "yarn build && yarn --cwd examples/basic && yarn --cwd examples/basic dev", diff --git a/src/createTranslation.tsx b/src/createTranslation.tsx index 8509903..1197f57 100644 --- a/src/createTranslation.tsx +++ b/src/createTranslation.tsx @@ -1,3 +1,5 @@ +import { useMemo } from 'react' +import isServer from './isServer' import transCore from './transCore' import wrapTWithDefaultNs from './wrapTWithDefaultNs' @@ -6,12 +8,17 @@ export default function createTranslation(defaultNS?: string) { const { lang, namespaces, config } = globalThis.__NEXT_TRANSLATE__ ?? {} const localesToIgnore = config.localesToIgnore || ['default'] const ignoreLang = !lang || localesToIgnore.includes(lang) - const t = transCore({ - config, - allNamespaces: namespaces, - pluralRules: new Intl.PluralRules(ignoreLang ? undefined : lang), - lang, - }) + const getT = () => { + const t = transCore({ + config, + allNamespaces: namespaces, + pluralRules: new Intl.PluralRules(ignoreLang ? undefined : lang), + lang, + }) - return { t: wrapTWithDefaultNs(t, defaultNS), lang } + return wrapTWithDefaultNs(t, defaultNS) + } + + const t = isServer() ? getT() : useMemo(getT, [defaultNS]) + return { t, lang } } diff --git a/src/isServer.tsx b/src/isServer.tsx new file mode 100644 index 0000000..dfd4c73 --- /dev/null +++ b/src/isServer.tsx @@ -0,0 +1,3 @@ +export default function isServer() { + return typeof window === 'undefined' +}