From f1a5071811f6afbde06c9adc48273fc5f840cf0d Mon Sep 17 00:00:00 2001 From: Alexandre Philibeaux Date: Fri, 5 Jan 2024 15:43:45 +0100 Subject: [PATCH] fix(use-i18n): add loadDateLocal non async possibility to avoid re-render --- .changeset/nice-seahorses-protect.md | 5 ++ packages/use-i18n/src/__tests__/usei18n.tsx | 70 ++++++++++++--------- packages/use-i18n/src/usei18n.tsx | 16 +++-- 3 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 .changeset/nice-seahorses-protect.md diff --git a/.changeset/nice-seahorses-protect.md b/.changeset/nice-seahorses-protect.md new file mode 100644 index 000000000..2726fed7c --- /dev/null +++ b/.changeset/nice-seahorses-protect.md @@ -0,0 +1,5 @@ +--- +"@scaleway/use-i18n": minor +--- + +Add a non async loadDateLocal possibility diff --git a/packages/use-i18n/src/__tests__/usei18n.tsx b/packages/use-i18n/src/__tests__/usei18n.tsx index 831ddc458..bf5e852a4 100644 --- a/packages/use-i18n/src/__tests__/usei18n.tsx +++ b/packages/use-i18n/src/__tests__/usei18n.tsx @@ -7,6 +7,7 @@ import { jest, } from '@jest/globals' import { act, renderHook, waitFor } from '@testing-library/react' +import { enGB, fr as frDateFns } from 'date-fns/locale' import mockdate from 'mockdate' import type { ReactNode } from 'react' import I18n, { useI18n, useTranslation } from '..' @@ -35,7 +36,7 @@ const defaultSupportedLocales = ['en', 'fr', 'es'] const wrapper = ({ - loadDateLocale = async (locale: string) => { + loadDateLocaleAsync = async (locale: string) => { if (locale === 'en') { return (await import('date-fns/locale/en-GB')).enGB } @@ -49,6 +50,16 @@ const wrapper = return (await import(`date-fns/locale/en-GB`)).enGB }, + loadDateLocale = (locale: string) => { + if (locale === 'en') { + return enGB + } + if (locale === 'fr') { + return frDateFns + } + + return enGB + }, defaultLoad = async ({ locale }: { locale: string }) => import(`./locales/${locale}.json`), defaultLocale = 'en', @@ -61,6 +72,7 @@ const wrapper = ({ children }: { children: ReactNode }) => ( { }) }) + it('should work with a component', async () => { + const { result } = renderHook( + () => useTranslation<{ 'with.identifier': 'Hello {identifier}' }>([]), + { + wrapper: wrapper({ defaultLocale: 'en' }), + }, + ) + const CustomComponent = ({ children }: { children: ReactNode }) => ( +

{children}

+ ) + + await waitFor(() => { + expect( + result.current.t('with.identifier', { identifier: My resource }), + ).toEqual(['Are you sure you want to delete ', My resource, '?']) + expect( + result.current.t('with.identifier', { + identifier: My resource, + }), + ).toEqual([ + 'Are you sure you want to delete ', + My resource, + '?', + ]) + }) + }) + describe('getCurrentLocale', () => { it('should set current locale from localStorage', async () => { jest.spyOn(global, 'navigator', 'get').mockReturnValueOnce({ @@ -765,7 +804,7 @@ describe('i18n hook', () => { await waitFor(() => { expect(result.current.currentLocale).toEqual('fr') - expect(mockGetItem).toHaveBeenCalledTimes(2) + expect(mockGetItem).toHaveBeenCalledTimes(1) expect(mockGetItem).toHaveBeenCalledWith(LOCALE_ITEM_STORAGE) }) @@ -777,31 +816,4 @@ describe('i18n hook', () => { localStorageMock.mockRestore() }) }) - - it('should work with a component', async () => { - const { result } = renderHook( - () => useTranslation<{ 'with.identifier': 'Hello {identifier}' }>([]), - { - wrapper: wrapper({ defaultLocale: 'en' }), - }, - ) - const CustomComponent = ({ children }: { children: ReactNode }) => ( -

{children}

- ) - - await waitFor(() => { - expect( - result.current.t('with.identifier', { identifier: My resource }), - ).toEqual(['Are you sure you want to delete ', My resource, '?']) - expect( - result.current.t('with.identifier', { - identifier: My resource, - }), - ).toEqual([ - 'Are you sure you want to delete ', - My resource, - '?', - ]) - }) - }) }) diff --git a/packages/use-i18n/src/usei18n.tsx b/packages/use-i18n/src/usei18n.tsx index f73d799e5..f80cdd292 100644 --- a/packages/use-i18n/src/usei18n.tsx +++ b/packages/use-i18n/src/usei18n.tsx @@ -182,7 +182,8 @@ type LoadTranslationsFn = ({ locale: string }) => Promise<{ default: BaseLocale }> -type LoadLocaleFn = (locale: string) => Promise +type LoadLocaleFn = (locale: string) => DateFnsLocale +type LoadLocaleFnAsync = (locale: string) => Promise type LoadDateLocaleError = (error: Error) => void const initialDefaultTranslations = {} @@ -195,13 +196,15 @@ const I18nContextProvider = ({ enableDebugKey = false, enableDefaultLocale = false, loadDateLocale, + loadDateLocaleAsync, localeItemStorage = LOCALE_ITEM_STORAGE, onLoadDateLocaleError, supportedLocales, }: { children: ReactNode defaultLoad: LoadTranslationsFn - loadDateLocale: LoadLocaleFn + loadDateLocale?: LoadLocaleFn + loadDateLocaleAsync: LoadLocaleFnAsync onLoadDateLocaleError?: LoadDateLocaleError defaultLocale: string defaultTranslations: TranslationsByLocales @@ -216,14 +219,17 @@ const I18nContextProvider = ({ const [translations, setTranslations] = useState(defaultTranslations) const [namespaces, setNamespaces] = useState([]) + const [dateFnsLocale, setDateFnsLocale] = useState( - undefined, + loadDateLocale?.(currentLocale) ?? undefined, ) + const loadDateFNS = loadDateLocale ?? loadDateLocaleAsync + const setDateFns = useCallback( async (locale: string) => { try { - const dateFns = await loadDateLocale(locale) + const dateFns = await loadDateFNS(locale) setDateFnsLocale(dateFns) } catch (err) { if (err instanceof Error && onLoadDateLocaleError) { @@ -233,7 +239,7 @@ const I18nContextProvider = ({ setDateFnsLocale(dateFnsLocale) } }, - [loadDateLocale, setDateFnsLocale, onLoadDateLocaleError, dateFnsLocale], + [loadDateFNS, setDateFnsLocale, onLoadDateLocaleError, dateFnsLocale], ) /**