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
24 changes: 15 additions & 9 deletions packages/use-i18n/src/__tests__/usei18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import fr from './locales/fr.json'

const LOCALE_ITEM_STORAGE = 'locales'

type Locale = {
test: 'Test'
'with.identifier': 'Are you sure you want to delete {identifier}?'
plurals: '{numPhotos, plural, =0 {You have one photo.} other {You have # photos.}}'
subtitle: 'Here is a subtitle'
'tests.test.namespaces': 'test'
title: 'Welcome on @scaelway/ui i18n hook'
}

const wrapper =
({
loadDateLocale = async (locale: string) =>
Expand Down Expand Up @@ -285,7 +294,7 @@ describe('i18n hook', () => {
})

it('should translate correctly with enableDebugKey', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale>(), {
wrapper: wrapper({
defaultLocale: 'en',
defaultTranslations: { en },
Expand All @@ -311,24 +320,21 @@ describe('i18n hook', () => {
})

it('should use namespaceTranslation', async () => {
const { result } = renderHook(() => useI18n(), {
const { result } = renderHook(() => useI18n<Locale>(), {
wrapper: wrapper({
defaultLocale: 'en',
defaultTranslations: { en },
}),
})
await waitFor(() => {
const identiqueTranslate = result.current.namespaceTranslation('')
expect(identiqueTranslate('title')).toEqual(result.current.t('title'))
const identiqueTranslate = result.current.namespaceTranslation('tests')
expect(identiqueTranslate('test.namespaces')).toEqual(
result.current.t('tests.test.namespaces'),
)
})

const translate = result.current.namespaceTranslation('tests.test')
expect(translate('namespaces')).toEqual('test')

// inception
const translate1 = result.current.namespaceTranslation('tests')
const translate2 = result.current.namespaceTranslation('test', translate1)
expect(translate2('namespaces')).toEqual(translate1('test.namespaces'))
})

it('should use formatNumber', async () => {
Expand Down
44 changes: 15 additions & 29 deletions packages/use-i18n/src/usei18n.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
formatDistanceToNow,
formatDistanceToNowStrict,
} from 'date-fns'
import type { BaseLocale, LocaleValue } from 'international-types'
import type { BaseLocale } from 'international-types'
import PropTypes from 'prop-types'
import {
ReactElement,
Expand All @@ -20,21 +20,12 @@ import ReactDOM from 'react-dom'
import dateFormat, { FormatDateOptions } from './formatDate'
import unitFormat, { FormatUnitOptions } from './formatUnit'
import formatters, { IntlListFormatOptions } from './formatters'
import type { ScopedTranslateFn, TranslateFn } from './types'
import type { ReactParamsObject, ScopedTranslateFn, TranslateFn } from './types'

const LOCALE_ITEM_STORAGE = 'locale'

type TranslationsByLocales = Record<string, BaseLocale>

export type InitialTranslateFn = (
key: string,
context?: Record<string, LocaleValue | JSX.Element>,
) => string
export type InitialScopedTranslateFn = (
namespace: string,
t?: InitialTranslateFn,
) => InitialTranslateFn

const areNamespacesLoaded = (
namespaces: string[],
loadedNamespaces: string[] = [],
Expand Down Expand Up @@ -62,7 +53,7 @@ const getCurrentLocale = ({
)
}

interface Context<Locale extends BaseLocale | undefined = undefined> {
interface Context<Locale extends BaseLocale> {
currentLocale: string
dateFnsLocale?: DateFnsLocale
datetime: (
Expand All @@ -82,9 +73,7 @@ interface Context<Locale extends BaseLocale | undefined = undefined> {
) => Promise<string>
locales: string[]
namespaces: string[]
namespaceTranslation: Locale extends BaseLocale
? ScopedTranslateFn<Locale>
: InitialScopedTranslateFn
namespaceTranslation: ScopedTranslateFn<Locale>
relativeTime: (
date: Date | number,
options?: {
Expand All @@ -102,15 +91,15 @@ interface Context<Locale extends BaseLocale | undefined = undefined> {
) => string
setTranslations: React.Dispatch<React.SetStateAction<TranslationsByLocales>>
switchLocale: (locale: string) => void
t: Locale extends BaseLocale ? TranslateFn<Locale> : InitialTranslateFn
t: TranslateFn<Locale>
translations: TranslationsByLocales
}

const I18nContext = createContext<Context | undefined>(undefined)
// It's safe to use any here because the Locale can be anything at this point:
// useI18n / useTranslation requires to explicitely give a Locale to use.
const I18nContext = createContext<Context<any> | undefined>(undefined)

export function useI18n<
Locale extends BaseLocale | undefined = undefined,
>(): Context<Locale> {
export function useI18n<Locale extends BaseLocale>(): Context<Locale> {
const context = useContext(I18nContext)
if (context === undefined) {
throw new Error('useI18n must be used within a I18nProvider')
Expand All @@ -119,9 +108,7 @@ export function useI18n<
return context as unknown as Context<Locale>
}

export function useTranslation<
Locale extends BaseLocale | undefined = undefined,
>(
export function useTranslation<Locale extends BaseLocale>(
namespaces: string[] = [],
load: LoadTranslationsFn | undefined = undefined,
): Context<Locale> & { isLoaded: boolean } {
Expand Down Expand Up @@ -323,8 +310,8 @@ const I18nContextProvider = ({
[dateFnsLocale],
)

const translate = useCallback<InitialTranslateFn>(
(key, context) => {
const translate = useCallback(
(key: string, context?: ReactParamsObject<any>) => {
const value = translations[currentLocale]?.[key] as string
if (!value) {
if (enableDebugKey) {
Expand All @@ -344,10 +331,9 @@ const I18nContextProvider = ({
[currentLocale, translations, enableDebugKey],
)

const namespaceTranslation = useCallback<InitialScopedTranslateFn>(
(namespace, t = translate) =>
(identifier, context) =>
t(`${namespace}.${identifier}`, context) || t(identifier, context),
const namespaceTranslation = useCallback(
(scope: string) => (key: string, context?: ReactParamsObject<any>) =>
translate(`${scope}.${key}`, context) || translate(key, context),
[translate],
)

Expand Down