@@ -9,31 +9,28 @@ import {
99import { act , renderHook , waitFor } from '@testing-library/react'
1010import { enGB , fr as frDateFns } from 'date-fns/locale'
1111import mockdate from 'mockdate'
12- import type { ReactNode } from 'react'
12+ import type { ComponentProps , ReactNode } from 'react'
1313import I18n , { useI18n , useTranslation } from '..'
14- import en from './locales/en.json '
15- import es from './locales/es.json '
16- import fr from './locales/fr.json '
14+ import en from './locales/en'
15+ import es from './locales/es'
16+ import fr from './locales/fr'
1717
1818const LOCALE_ITEM_STORAGE = 'locales'
1919
20- type Locale = {
21- test : 'Test'
22- 'with.identifier' : 'Are you sure you want to delete {identifier}?'
23- plurals : '{numPhotos, plural, =0 {You have one photo.} other {You have # photos.}}'
24- subtitle : 'Here is a subtitle'
25- 'tests.test.namespaces' : 'test'
26- title : 'Welcome on @scaelway/ui i18n hook'
27- }
28-
20+ type LocaleEN = typeof en
21+ type Locale = LocaleEN
2922type NamespaceLocale = {
3023 name : 'Name'
3124 lastName : 'Last Name'
3225 languages : 'Languages'
3326}
3427
28+ type OnTranslateError = ComponentProps < typeof I18n > [ 'onTranslateError' ]
29+
3530const defaultSupportedLocales = [ 'en' , 'fr' , 'es' ]
3631
32+ const defaultOnTranslateError : OnTranslateError = ( ) => { }
33+
3734const wrapper =
3835 ( {
3936 loadDateLocaleAsync = async ( locale : string ) => {
@@ -61,13 +58,14 @@ const wrapper =
6158 return enGB
6259 } ,
6360 defaultLoad = async ( { locale } : { locale : string } ) =>
64- import ( `./locales/${ locale } .json ` ) ,
61+ import ( `./locales/${ locale } .ts ` ) ,
6562 defaultLocale = 'en' ,
6663 defaultTranslations = { } ,
6764 enableDebugKey = false ,
6865 enableDefaultLocale = false ,
6966 localeItemStorage = LOCALE_ITEM_STORAGE ,
7067 supportedLocales = defaultSupportedLocales ,
68+ onTranslateError = defaultOnTranslateError ,
7169 } = { } ) =>
7270 ( { children } : { children : ReactNode } ) => (
7371 < I18n
@@ -80,6 +78,7 @@ const wrapper =
8078 enableDefaultLocale = { enableDefaultLocale }
8179 localeItemStorage = { localeItemStorage }
8280 supportedLocales = { supportedLocales }
81+ onTranslateError = { onTranslateError }
8382 >
8483 { children }
8584 </ I18n >
@@ -470,7 +469,7 @@ describe('i18n hook', () => {
470469 expect ( localStorage . getItem ( LOCALE_ITEM_STORAGE ) ) . toBe ( 'es' )
471470 } )
472471
473- it ( 'should translate correctly with enableDebugKey' , async ( ) => {
472+ it ( 'should translate correctly with enableDebugKey and return key ' , async ( ) => {
474473 const { result } = renderHook ( ( ) => useI18n < Locale > ( ) , {
475474 wrapper : wrapper ( {
476475 defaultLocale : 'en' ,
@@ -479,15 +478,66 @@ describe('i18n hook', () => {
479478 supportedLocales : [ 'en' , 'fr' ] ,
480479 } ) ,
481480 } )
481+
482+ // @ts -expect-error this key doesn't exist but enable debug key will return the key
482483 expect ( result . current . t ( 'test' ) ) . toEqual ( 'test' )
483484
484485 await waitFor ( ( ) => {
485486 expect ( result . current . t ( 'title' ) ) . toEqual ( 'title' )
486487 expect ( result . current . t ( 'subtitle' ) ) . toEqual ( 'subtitle' )
487- expect ( result . current . t ( 'plurals' , { numPhotos : 0 } ) ) . toEqual ( 'plurals' )
488- expect ( result . current . t ( 'plurals' , { numPhotos : 1 } ) ) . toEqual ( 'plurals' )
489- expect ( result . current . t ( 'plurals' , { numPhotos : 2 } ) ) . toEqual ( 'plurals' )
488+ expect ( result . current . t ( 'plurals' , { count : 0 } ) ) . toEqual ( 'plurals' )
489+ expect ( result . current . t ( 'plurals' , { count : 1 } ) ) . toEqual ( 'plurals' )
490+ expect ( result . current . t ( 'plurals' , { count : 2 } ) ) . toEqual ( 'plurals' )
491+ } )
492+ } )
493+
494+ it ( 'should call onTranslateError when there is a sync issue to remove/add variable in one traduction of a language' , async ( ) => {
495+ const mockOnTranslateError = jest . fn ( )
496+
497+ const { result } = renderHook ( ( ) => useI18n < Locale > ( ) , {
498+ wrapper : wrapper ( {
499+ defaultLocale : 'en' ,
500+ defaultTranslations : { en, fr } ,
501+ supportedLocales : [ 'en' , 'fr' ] ,
502+ onTranslateError : mockOnTranslateError ,
503+ } ) ,
504+ } )
505+
506+ await act ( async ( ) => {
507+ await result . current . switchLocale ( 'fr' )
508+ } )
509+
510+ waitFor ( ( ) => {
511+ expect ( result . current . currentLocale ) . toEqual ( 'fr' )
490512 } )
513+
514+ const newVariable = 'newVariable'
515+ expect (
516+ result . current . t ( 'translate.error' , { newVariable : 'newVariable' } ) ,
517+ ) . toBe (
518+ `On translate sync issue with variable between locales ${ newVariable } ` ,
519+ )
520+
521+ expect ( mockOnTranslateError ) . toHaveBeenCalledTimes ( 1 )
522+ expect ( mockOnTranslateError ) . toHaveBeenCalledWith ( {
523+ error : new Error (
524+ 'The intl string context variable "oldFrenchVariable" was not provided to the string "onTranslateError fonction sera appelé car il manque une variable en français {oldFrenchVariable}"' ,
525+ ) ,
526+ currentLocale : 'fr' ,
527+ key : 'translate.error' ,
528+ value :
529+ 'onTranslateError fonction sera appelé car il manque une variable en français {oldFrenchVariable}' ,
530+ } )
531+
532+ const oldFrenchVariable = 'cette variable fonctionne'
533+ expect (
534+ result . current . t ( 'translate.error' , {
535+ // @ts -expect-error this variable doesn't exist in english anymore but still in french locales
536+ oldFrenchVariable,
537+ } ) ,
538+ ) . toBe (
539+ `onTranslateError fonction sera appelé car il manque une variable en français ${ oldFrenchVariable } ` ,
540+ )
491541 } )
492542
493543 it ( 'should use namespaceTranslation' , async ( ) => {
0 commit comments