Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lovely-knives-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scaleway/use-i18n": major
---

Add a onError function for load date-fns locale, fix a bug when a local is removed
5 changes: 5 additions & 0 deletions .changeset/renovate-2327f3b.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@scaleway/use-i18n': patch
---

Updated dependency `date-fns` to `2.x || 3.x`.
6 changes: 3 additions & 3 deletions packages/use-i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "A small hook to handle i18n",
"type": "module",
"engines": {
"node": ">=14.x"
"node": ">=18.x"
},
"sideEffects": false,
"exports": {
Expand All @@ -31,13 +31,13 @@
"dependencies": {
"@formatjs/ecma402-abstract": "1.18.0",
"@formatjs/fast-memoize": "2.2.0",
"date-fns": "2.30.0",
"date-fns": "3.0.6",
"filesize": "10.1.0",
"international-types": "0.8.1",
"intl-messageformat": "10.5.8"
},
"peerDependencies": {
"date-fns": "2.x",
"date-fns": "3.x",
"react": "18.x || 18",
"react-dom": "18.x || 18"
}
Expand Down
174 changes: 132 additions & 42 deletions packages/use-i18n/src/__tests__/usei18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,32 @@ type NamespaceLocale = {
languages: 'Languages'
}

const defaultSupportedLocales = ['en', 'fr', 'es']

const wrapper =
({
loadDateLocale = async (locale: string) =>
import(`date-fns/locale/${locale}/index`),
loadDateLocale = async (locale: string) => {
if (locale === 'en') {
return (await import('date-fns/locale/en-GB')).enGB
}
if (locale === 'fr') {
return (await import('date-fns/locale/fr')).fr
}

if (locale === 'es') {
return (await import('date-fns/locale/es')).es
}

return (await import(`date-fns/locale/en-GB`)).enGB
},
defaultLoad = async ({ locale }: { locale: string }) =>
import(`./locales/${locale}.json`),
defaultLocale = 'en',
defaultTranslations = {},
enableDebugKey = false,
enableDefaultLocale = false,
localeItemStorage = LOCALE_ITEM_STORAGE,
supportedLocales = ['en', 'fr', 'es'],
supportedLocales = defaultSupportedLocales,
} = {}) =>
({ children }: { children: ReactNode }) => (
<I18n
Expand Down Expand Up @@ -126,16 +140,16 @@ describe('i18n hook', () => {
expect(result.current.t('title')).toEqual(en.title)
})

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
expect(result.current.t('title')).toEqual(fr.title)
})

act(() => {
result.current.switchLocale('es')
await act(async () => {
await result.current.switchLocale('es')
})

await waitFor(() => {
Expand Down Expand Up @@ -176,8 +190,8 @@ describe('i18n hook', () => {
expect(result.current.t('lastName')).toEqual('Last Name')
expect(result.current.t('languages')).toEqual('Languages')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand Down Expand Up @@ -221,8 +235,8 @@ describe('i18n hook', () => {

// current local will be 'en' based on navigator
// await load of locales
act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand Down Expand Up @@ -264,11 +278,13 @@ describe('i18n hook', () => {
} as unknown as Navigator)
const mockGetItem = jest.fn().mockImplementation(() => 'en')
const mockSetItem = jest.fn()
const mockRemoveItem = jest.fn()
const localStorageMock = jest
.spyOn(global, 'localStorage', 'get')
.mockReturnValue({
getItem: mockGetItem,
setItem: mockSetItem,
removeItem: mockRemoveItem,
clear: jest.fn(),
} as unknown as Storage)

Expand All @@ -281,7 +297,38 @@ describe('i18n hook', () => {

await waitFor(() => {
expect(result.current.currentLocale).toEqual('en')
expect(mockGetItem).toHaveBeenCalledTimes(2)
expect(mockGetItem).toHaveBeenCalledTimes(1)
expect(mockGetItem).toHaveBeenCalledWith(LOCALE_ITEM_STORAGE)
})
localStorageMock.mockRestore()
})

it('should not set current locale from localStorage when this value is not supported', async () => {
jest.spyOn(global, 'navigator', 'get').mockReturnValueOnce({
languages: ['bz'],
} as unknown as Navigator)
const mockGetItem = jest.fn().mockImplementation(() => 're')
const mockSetItem = jest.fn()
const mockRemoveItem = jest.fn()
const localStorageMock = jest
.spyOn(global, 'localStorage', 'get')
.mockReturnValue({
getItem: mockGetItem,
setItem: mockSetItem,
removeItem: mockRemoveItem,
clear: jest.fn(),
} as unknown as Storage)

const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'en',
supportedLocales: ['en'],
}),
})

await waitFor(() => {
expect(result.current.currentLocale).toEqual('en')
expect(mockGetItem).toHaveBeenCalledTimes(1)
expect(mockGetItem).toHaveBeenCalledWith(LOCALE_ITEM_STORAGE)
})
localStorageMock.mockRestore()
Expand All @@ -293,11 +340,13 @@ describe('i18n hook', () => {
} as unknown as Navigator)
const mockGetItem = jest.fn()
const mockSetItem = jest.fn()
const mockRemoveItem = jest.fn()
const localStorageMock = jest
.spyOn(global, 'localStorage', 'get')
.mockReturnValueOnce({
getItem: mockGetItem,
setItem: mockSetItem,
removeItem: mockRemoveItem,
clear: jest.fn(),
} as unknown as Storage)

Expand All @@ -320,11 +369,13 @@ describe('i18n hook', () => {
} as unknown as Navigator)
const mockGetItem = jest.fn()
const mockSetItem = jest.fn()
const mockRemoveItem = jest.fn()
const localStorageMock = jest
.spyOn(global, 'localStorage', 'get')
.mockReturnValueOnce({
getItem: mockGetItem,
setItem: mockSetItem,
removeItem: mockRemoveItem,
clear: jest.fn(),
} as unknown as Storage)

Expand All @@ -350,28 +401,28 @@ describe('i18n hook', () => {
}),
})
expect(result.current.currentLocale).toEqual('en')
expect(localStorage.getItem(LOCALE_ITEM_STORAGE)).toBe(null)
expect(localStorage.getItem(LOCALE_ITEM_STORAGE)).toBe('en')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
expect(result.current.currentLocale).toEqual('fr')
})
expect(localStorage.getItem(LOCALE_ITEM_STORAGE)).toBe('fr')

act(() => {
result.current.switchLocale('es')
await act(async () => {
await result.current.switchLocale('es')
})

await waitFor(() => {
expect(result.current.currentLocale).toEqual('es')
})
expect(localStorage.getItem(LOCALE_ITEM_STORAGE)).toBe('es')

act(() => {
result.current.switchLocale('test')
await act(async () => {
await result.current.switchLocale('test')
})

await waitFor(() => {
Expand Down Expand Up @@ -441,8 +492,8 @@ describe('i18n hook', () => {
}),
).toEqual('$2.00')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

// https://stackoverflow.com/questions/58769806/identical-strings-not-matching-in-jest
Expand Down Expand Up @@ -491,8 +542,8 @@ describe('i18n hook', () => {
}),
).toEqual('Motorcycle Bus Car')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand Down Expand Up @@ -565,8 +616,8 @@ describe('i18n hook', () => {
}),
).toEqual('12/17/1995')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand Down Expand Up @@ -602,11 +653,12 @@ describe('i18n hook', () => {

expect(result.current.relativeTime(date)).toEqual('over 20 years ago')

act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
expect(result.current.dateFnsLocale?.code).toBe('fr')
expect(result.current.relativeTime(date)).toEqual('il y a plus de 20 ans')
})
})
Expand All @@ -621,8 +673,8 @@ describe('i18n hook', () => {
const date = new Date('September 13, 2011 15:15:00')

expect(result.current.relativeTimeStrict(date)).toEqual('3499 days ago')
act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand All @@ -642,8 +694,8 @@ describe('i18n hook', () => {
expect(
result.current.formatUnit(12, { short: false, unit: 'byte' }),
).toEqual('12 bytes')
act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand All @@ -663,8 +715,8 @@ describe('i18n hook', () => {
expect(
result.current.formatDate(new Date(2020, 1, 13, 16, 28), 'numericHour'),
).toEqual('2020-02-13 4:28 PM')
act(() => {
result.current.switchLocale('fr')
await act(async () => {
await result.current.switchLocale('fr')
})

await waitFor(() => {
Expand All @@ -674,17 +726,55 @@ describe('i18n hook', () => {
})
})

it('should load default datefns locales', async () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'test',
supportedLocales: ['test'],
}),
describe('date-fns', () => {
it('should load default date-fns locales', async () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'test',
supportedLocales: ['test'],
}),
})

await waitFor(() => {
expect(result.current.dateFnsLocale?.code).toEqual('en-GB')
})
})
expect(result.current.dateFnsLocale).toBe(undefined)

await waitFor(() => {
expect(result.current.dateFnsLocale?.code).toEqual('en-GB')
it('should load correct date-fns based on current local', async () => {
jest.spyOn(global, 'navigator', 'get').mockReturnValueOnce({
languages: ['fr'],
} as unknown as Navigator)
const mockGetItem = jest.fn().mockImplementation(() => 'fr')
const mockSetItem = jest.fn()
const mockRemoveItem = jest.fn()
const localStorageMock = jest
.spyOn(global, 'localStorage', 'get')
.mockReturnValue({
getItem: mockGetItem,
setItem: mockSetItem,
removeItem: mockRemoveItem,
clear: jest.fn(),
} as unknown as Storage)

const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'es',
supportedLocales: ['en', 'fr', 'es'],
}),
})

await waitFor(() => {
expect(result.current.currentLocale).toEqual('fr')
expect(mockGetItem).toHaveBeenCalledTimes(2)
expect(mockGetItem).toHaveBeenCalledWith(LOCALE_ITEM_STORAGE)
})

await waitFor(() => {
expect(result.current.dateFnsLocale?.code).toEqual('fr')
expect(result.current.dateFnsLocale).toMatchObject({ code: 'fr' })
})

localStorageMock.mockRestore()
})
})

Expand Down
Loading