cagov / ui-claim-tracker

This repo contains the Claim Status Tracker app, which helps Californians better understand what’s happening with their unemployment claim and benefits.
9 stars 4 forks source link

Choose and integrate an internationalization library #44

Closed lomky closed 3 years ago

lomky commented 3 years ago

Description

Integrate a translation library into the UI Claim Tracker application. By default we are leaning towards i18next-react like our previous project, but we should do a bit of research if there is some NextJS bonus or other best practice that has emerged.

Acceptance Criteria

lomky commented 3 years ago

We ended up using next-i18next which is the extension of i18next-react for NextJS.

See also: Example app

lomky commented 3 years ago
Debugging NextJS/Jest/next-i18next ## Integrating Next-i18next with Jest, React Testing Library, and React Test Renderer ### First: mocking the next/router Jest test error: ```bash ● Exemplar react-test-renderer Snapshot test › renders homepage unchanged TypeError: Cannot read property 'locale' of null 34 |
35 | > 36 | | ^ 37 | 38 | 39 | at Home (pages/index.tsx:36:39) at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6016:18) at mountIndeterminateComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8744:13) at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9966:16) at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13800:12) at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13728:5) at renderRootSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13691:7) at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13404:18) at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2537:26 at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:646:12) at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2486:10) at flushSyncCallbackQueueImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2532:9) at flushSyncCallbackQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2519:3) at scheduleUpdateOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13004:9) at updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16454:3) at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:17149:3) at Object. (tests/index.test.tsx:13:27) ``` looks promising: https://stackoverflow.com/a/66213883/784829 but where do the mocks go? https://jestjs.io/docs/mock-functions Okay! implemented inside the test file, the router works. Just working on Typescript now... https://klzns.github.io/how-to-use-type-script-and-jest-mocks - no dice - we aren't calling the function in our test https://www.salto.io/post/typescript-unit-testing-pitfalls-with-jest-and-how-to-work-around-them nah... ooooooo https://github.com/vercel/next.js/issues/7479 finally! this works with our Typescript code https://github.com/vercel/next.js/discussions/23034#discussioncomment-478462 Okay, now debugging the i18n issues ### i18n in tests jest test error: ```bash ● Example react testing-library Test › has our placeholder app expect(received).toBeInTheDocument() received value must be an HTMLElement or an SVGElement. Received has value: null 25 | it('has our placeholder app', () => { 26 | render() > 27 | expect(screen.queryByText('Welcome to the Placeholder Claim Tracker App!')).toBeInTheDocument() | ^ 28 | }) 29 | }) 30 | at __EXTERNAL_MATCHER_TRAP__ (node_modules/expect/build/index.js:342:30) at Object.toBeInTheDocument (node_modules/expect/build/index.js:343:15) at Object. (tests/index.test.tsx:27:81) console.warn react-i18next:: You will need to pass in an i18next instance by using initReactI18next 9 | 10 | export default function Home(): ReactElement { > 11 | const { t } = useTranslation('common') | ^ 12 | const router = useRouter() 13 | 14 | return ( at warn (node_modules/react-i18next/dist/commonjs/utils.js:22:31) at warnOnce (node_modules/react-i18next/dist/commonjs/utils.js:35:8) at useTranslation (node_modules/react-i18next/dist/commonjs/useTranslation.js:36:25) at Home (pages/index.tsx:11:17) at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6016:18) at mountIndeterminateComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8744:13) at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9966:16) ``` Looking at https://dev.to/mehmetnyarar/how-to-test-next-i18next-1nh4 I only needed the react-i18next mock, and to import my resources - it works!! Ignoring a lint rule for now... ```typescript import i18n from 'i18next' import { initReactI18next } from 'react-i18next' import enCommon from './public/locales/en/common.json' /* eslint-disable @typescript-eslint/no-floating-promises */ // Disabling this rule due to hitting a half hour of debugging, // and considering this is a specialized file used only for tests // Mock react-i18next for tests i18n.use(initReactI18next).init({ lng: 'en', fallbackLng: 'en', ns: ['common'], defaultNS: 'common', resources: { en: { common: enCommon }, }, }) /* eslint-enable @typescript-eslint/no-floating-promises */ ```