11import type { NumberFormatOptions } from '@formatjs/ecma402-abstract'
22import {
3- Locale ,
3+ Locale as DateFnsLocale ,
44 formatDistanceToNow ,
55 formatDistanceToNowStrict ,
66} from 'date-fns'
7+ import type { BaseLocale , LocaleValue } from 'international-types'
78import PropTypes from 'prop-types'
89import {
910 ReactElement ,
@@ -19,17 +20,20 @@ import ReactDOM from 'react-dom'
1920import dateFormat , { FormatDateOptions } from './formatDate'
2021import unitFormat , { FormatUnitOptions } from './formatUnit'
2122import formatters , { IntlListFormatOptions } from './formatters'
23+ import type { ScopedTranslateFn , TranslateFn } from './types'
2224
2325const LOCALE_ITEM_STORAGE = 'locale'
2426
25- type PrimitiveType = string | number | boolean | null | undefined | Date
27+ type TranslationsByLocales = Record < string , BaseLocale >
2628
27- type Translations = Record < string , string > & { prefix ?: string }
28- type TranslationsByLocales = Record < string , Translations >
29- type TranslateFn = (
29+ export type InitialTranslateFn = (
3030 key : string ,
31- context ?: Record < string , PrimitiveType > ,
31+ context ?: Record < string , LocaleValue > ,
3232) => string
33+ export type InitialScopedTranslateFn = (
34+ namespace : string ,
35+ t ?: InitialTranslateFn ,
36+ ) => InitialTranslateFn
3337
3438const prefixKeys = ( prefix : string ) => ( obj : { [ key : string ] : string } ) =>
3539 Object . keys ( obj ) . reduce ( ( acc : { [ key : string ] : string } , key ) => {
@@ -65,9 +69,9 @@ const getCurrentLocale = ({
6569 )
6670}
6771
68- interface Context {
72+ interface Context < Locale extends BaseLocale | undefined = undefined > {
6973 currentLocale : string
70- dateFnsLocale ?: Locale
74+ dateFnsLocale ?: DateFnsLocale
7175 datetime : (
7276 date : Date | number ,
7377 options ?: Intl . DateTimeFormatOptions ,
@@ -85,7 +89,9 @@ interface Context {
8589 ) => Promise < string >
8690 locales : string [ ]
8791 namespaces : string [ ]
88- namespaceTranslation : ( namespace : string , t ?: TranslateFn ) => TranslateFn
92+ namespaceTranslation : Locale extends BaseLocale
93+ ? ScopedTranslateFn < Locale >
94+ : InitialScopedTranslateFn
8995 relativeTime : (
9096 date : Date | number ,
9197 options ?: {
@@ -103,19 +109,21 @@ interface Context {
103109 ) => string
104110 setTranslations : React . Dispatch < React . SetStateAction < TranslationsByLocales > >
105111 switchLocale : ( locale : string ) => void
106- t : TranslateFn
112+ t : Locale extends BaseLocale ? TranslateFn < Locale > : InitialTranslateFn
107113 translations : TranslationsByLocales
108114}
109115
110116const I18nContext = createContext < Context | undefined > ( undefined )
111117
112- export const useI18n = ( ) : Context => {
118+ export function useI18n <
119+ Locale extends BaseLocale | undefined = undefined ,
120+ > ( ) : Context < Locale > {
113121 const context = useContext ( I18nContext )
114122 if ( context === undefined ) {
115123 throw new Error ( 'useI18n must be used within a I18nProvider' )
116124 }
117125
118- return context
126+ return context as unknown as Context < Locale >
119127}
120128
121129export const useTranslation = (
@@ -149,7 +157,7 @@ type LoadTranslationsFn = ({
149157} : {
150158 namespace : string
151159 locale : string
152- } ) => Promise < { default : Translations } >
160+ } ) => Promise < { default : BaseLocale } >
153161type LoadLocaleFn = ( locale : string ) => Promise < Locale >
154162
155163const I18nContextProvider = ( {
@@ -209,7 +217,7 @@ const I18nContextProvider = ({
209217 namespace,
210218 } )
211219
212- const trad : Translations = {
220+ const trad : Record < string , string > = {
213221 ...result . defaultLocale . default ,
214222 ...result [ currentLocale ] . default ,
215223 }
@@ -321,9 +329,9 @@ const I18nContextProvider = ({
321329 [ dateFnsLocale ] ,
322330 )
323331
324- const translate = useCallback < TranslateFn > (
325- ( key : string , context ?: Record < string , PrimitiveType > ) => {
326- const value = translations [ currentLocale ] ?. [ key ]
332+ const translate = useCallback < InitialTranslateFn > (
333+ ( key , context ) => {
334+ const value = translations [ currentLocale ] ?. [ key ] as string
327335 if ( ! value ) {
328336 if ( enableDebugKey ) {
329337 return key
@@ -342,9 +350,9 @@ const I18nContextProvider = ({
342350 [ currentLocale , translations , enableDebugKey ] ,
343351 )
344352
345- const namespaceTranslation = useCallback (
346- ( namespace : string , t : TranslateFn = translate ) =>
347- ( identifier : string , context ?: Record < string , PrimitiveType > ) =>
353+ const namespaceTranslation = useCallback < InitialScopedTranslateFn > (
354+ ( namespace , t = translate ) =>
355+ ( identifier , context ) =>
348356 t ( `${ namespace } .${ identifier } ` , context ) || t ( identifier , context ) ,
349357 [ translate ] ,
350358 )
0 commit comments