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/fuzzy-emus-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scaleway/use-i18n": major
---

Add typing on locales and remove auto mapping from browser locale to simple locale
64 changes: 35 additions & 29 deletions packages/use-i18n/src/__tests__/usei18n.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import fr from './locales/fr'

const LOCALE_ITEM_STORAGE = 'locales'

type LocaleEN = typeof en
type Locale = LocaleEN
const ListLocales = ['es', 'en', 'fr', 'fr-FR', 'en-GB'] as const
type Locales = (typeof ListLocales)[number]

type Locale = typeof en
type NamespaceLocale = {
name: 'Name'
lastName: 'Last Name'
Expand All @@ -20,7 +22,8 @@ type NamespaceLocale = {

type OnTranslateError = ComponentProps<typeof I18n>['onTranslateError']

const defaultSupportedLocales = ['en', 'fr', 'es']
const isDefaultLocalesSupported = (locale: string): locale is Locales =>
ListLocales.includes(locale as Locales)

const defaultOnTranslateError: OnTranslateError = () => {}

Expand Down Expand Up @@ -57,7 +60,7 @@ const wrapper =
enableDebugKey = false,
enableDefaultLocale = false,
localeItemStorage = LOCALE_ITEM_STORAGE,
supportedLocales = defaultSupportedLocales,
isLocaleSupported = isDefaultLocalesSupported,
onTranslateError = defaultOnTranslateError,
} = {}) =>
({ children }: { children: ReactNode }) => (
Expand All @@ -70,7 +73,7 @@ const wrapper =
enableDebugKey={enableDebugKey}
enableDefaultLocale={enableDefaultLocale}
localeItemStorage={localeItemStorage}
supportedLocales={supportedLocales}
isLocaleSupported={isLocaleSupported}
onTranslateError={onTranslateError}
>
{children}
Expand Down Expand Up @@ -114,7 +117,7 @@ describe('i18n hook', () => {
})

it('should use defaultLoad, useTranslation, switch local and translate', async () => {
const { result } = renderHook(() => useTranslation<Locale>([]), {
const { result } = renderHook(() => useTranslation<Locale, Locales>([]), {
wrapper: wrapper({ defaultLocale: 'en' }),
})
// first render there is no load
Expand Down Expand Up @@ -152,11 +155,11 @@ describe('i18n hook', () => {
}) => import(`./locales/namespaces/${locale}/${namespace}.json`)

const { result } = renderHook(
() => useTranslation<NamespaceLocale>(['user', 'profile'], load),
() => useTranslation<NamespaceLocale, Locales>(['user', 'profile'], load),
{
wrapper: wrapper({
defaultLocale: 'en',
supportedLocales: ['en', 'fr'],
isLocaleSupported: isDefaultLocalesSupported,
}),
},
)
Expand Down Expand Up @@ -208,12 +211,12 @@ describe('i18n hook', () => {
}) => import(`./locales/namespaces/${locale}/${namespace}.json`)

const { result } = renderHook(
() => useTranslation<NamespaceLocale>(['user'], load),
() => useTranslation<NamespaceLocale, Locales>(['user'], load),
{
wrapper: wrapper({
defaultLocale: 'fr',
enableDefaultLocale: true,
supportedLocales: ['en', 'fr'],
isLocaleSupported: isDefaultLocalesSupported,
}),
},
)
Expand Down Expand Up @@ -247,7 +250,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'fr',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down Expand Up @@ -303,7 +306,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'es',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down Expand Up @@ -334,7 +337,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'en',
supportedLocales: ['en'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down Expand Up @@ -365,7 +368,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'es',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down Expand Up @@ -394,7 +397,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'es',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand All @@ -406,10 +409,10 @@ describe('i18n hook', () => {
})

it('should switch locale', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})
expect(result.current.currentLocale).toEqual('en')
Expand All @@ -434,6 +437,9 @@ describe('i18n hook', () => {
expect(localStorage.getItem(LOCALE_ITEM_STORAGE)).toBe('es')

await act(async () => {
// we test even if an incorrect typescript value is being passed to the function
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
await result.current.switchLocale('test')
})

Expand All @@ -449,7 +455,7 @@ describe('i18n hook', () => {
defaultLocale: 'en',
defaultTranslations: { en },
enableDebugKey: true,
supportedLocales: ['en', 'fr'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand All @@ -468,11 +474,11 @@ describe('i18n hook', () => {
it('should call onTranslateError when there is a sync issue to remove/add variable in one traduction of a language', async () => {
const mockOnTranslateError = vi.fn()

const { result } = renderHook(() => useI18n<Locale>(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
defaultTranslations: { en, fr },
supportedLocales: ['en', 'fr'],
isLocaleSupported: isDefaultLocalesSupported,
onTranslateError: mockOnTranslateError,
}),
})
Expand Down Expand Up @@ -534,7 +540,7 @@ describe('i18n hook', () => {
})

it('should use formatNumber', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand Down Expand Up @@ -578,7 +584,7 @@ describe('i18n hook', () => {
})

it('should use formatList', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand Down Expand Up @@ -635,7 +641,7 @@ describe('i18n hook', () => {
})

it('should use datetime', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand Down Expand Up @@ -707,7 +713,7 @@ describe('i18n hook', () => {
})

it('should relativeTime', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand All @@ -728,7 +734,7 @@ describe('i18n hook', () => {
})

it('should relativeTimeStrict', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand All @@ -749,7 +755,7 @@ describe('i18n hook', () => {
})

it('should formatUnit', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand All @@ -770,7 +776,7 @@ describe('i18n hook', () => {
})

it('should formatDate', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale, Locales>(), {
wrapper: wrapper({
defaultLocale: 'en',
}),
Expand All @@ -795,7 +801,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'test',
supportedLocales: ['test'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down Expand Up @@ -823,7 +829,7 @@ describe('i18n hook', () => {
const { result } = renderHook(() => useI18n(), {
wrapper: wrapper({
defaultLocale: 'es',
supportedLocales: ['en', 'fr', 'es'],
isLocaleSupported: isDefaultLocalesSupported,
}),
})

Expand Down
20 changes: 12 additions & 8 deletions packages/use-i18n/src/__typetests__/namespaceTranslation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
import { expect, test } from 'tstyche'
import { useI18n } from '../usei18n'

const ListLocales = ['es', 'en', 'fr', 'fr-FR', 'en-GB'] as const
type Locales = (typeof ListLocales)[number]
type Locale = {
hello: 'world'
'doe.john': 'John Doe'
'doe.jane': 'Jane Doe'
'doe.child': 'Child is {name}'
'describe.john': '{name} is {age} years old'
}

test('i18n - namespaceTranslation', () => {
const { namespaceTranslation } = useI18n<{
hello: 'world'
'doe.john': 'John Doe'
'doe.jane': 'Jane Doe'
'doe.child': 'Child is {name}'
'describe.john': '{name} is {age} years old'
}>()
const { namespaceTranslation } = useI18n<Locale, Locales>()

// Single key
expect(namespaceTranslation('hello')).type.toRaiseError(
Expand Down Expand Up @@ -62,7 +66,7 @@ test('i18n - namespaceTranslation', () => {
expect(namespaceTranslation('describe')('john')).type.toRaiseError()

// Required generic
const { namespaceTranslation: namespaceTranslation2 } = useI18n()
const { namespaceTranslation: namespaceTranslation2 } = useI18n<Locale>()
expect(namespaceTranslation2('test')).type.toRaiseError(
`Argument of type '"test"' is not assignable to parameter of type`,
)
Expand Down
Loading