react-i18next
Version:
Internationalization for react done right. Using the i18next i18n ecosystem.
435 lines (400 loc) • 13.9 kB
TypeScript
import type { $Subtract, $Tuple } from './helpers.js';
import type {
ReactOptions,
i18n,
Resource,
FlatNamespace,
Namespace,
TypeOptions,
TFunction,
KeyPrefix,
KeyPrefixSelector,
NsResource,
ParseKeys,
TOptions,
InterpolationMap,
TFunctionReturn,
TOptionsBase,
} from 'i18next';
import * as React from 'react';
import {
Trans,
TransProps,
TransSelectorProps,
ErrorCode,
ErrorArgs,
} from './TransWithoutContext.js';
export { initReactI18next } from './initReactI18next.js';
export function nodesToString(
children: React.ReactNode,
i18nOptions?: ReactOptions,
i18n?: i18n,
i18nKey?: string,
): string;
export const TransWithoutContext: typeof Trans;
export { Trans, TransProps, TransSelectorProps, ErrorArgs, ErrorCode };
/**
* Content declaration for IcuTrans component
* Describes React components as type + props blueprints that will be rendered
*/
export interface IcuTransContentDeclaration {
type: string | React.ComponentType<any>;
props?: {
children?: IcuTransContentDeclaration | IcuTransContentDeclaration[];
[key: string]: any;
};
}
/**
* Props for IcuTransWithoutContext component (no React context)
*/
export type IcuTransWithoutContextProps<
Key extends ParseKeys<Ns, TOpt, KPrefix>,
Ns extends Namespace = _DefaultNamespace,
KPrefix = undefined,
TContext extends string | undefined = undefined,
TOpt extends TOptions & { context?: TContext } = { context: TContext },
> = {
/** The i18n key to look up the translation */
i18nKey: Key;
/** The default translation in ICU format with numbered tags (e.g., "<0>Click here</0>") */
defaultTranslation: string;
/** Declaration tree describing React components and their props */
content: IcuTransContentDeclaration[];
/** Optional namespace(s) for the translation */
// see TransProps.ns: keep single-namespace values assignable when `t` fixes Ns to an array
ns?: Ns | (Ns extends readonly (infer S extends string)[] ? S : never);
/**
* Values for ICU variable interpolation. Under `parseInterpolation: 'icu'`
* (or `'mf2'`) they are typed from the message.
*/
values?: [TypeOptions['parseInterpolation']] extends ['icu'] | ['mf2']
? InterpolationMap<
TFunctionReturn<
Ns,
KPrefix extends string ? `${KPrefix}${TypeOptions['keySeparator']}${Key & string}` : Key,
TOpt
>
>
: Record<string, any>;
/** i18next instance. If not provided, uses global instance */
i18n?: i18n;
/** Custom translation function */
t?: TFunction<Ns, KPrefix>;
};
/**
* Props for IcuTrans component (with React context support)
*/
export type IcuTransProps<
Key extends ParseKeys<Ns, TOpt, KPrefix>,
Ns extends Namespace = _DefaultNamespace,
KPrefix = undefined,
TContext extends string | undefined = undefined,
TOpt extends TOptions & { context?: TContext } = { context: TContext },
> = IcuTransWithoutContextProps<Key, Ns, KPrefix, TContext, TOpt>;
/**
* IcuTrans component for rendering ICU MessageFormat translations
* This is the context-aware version that works with I18nextProvider
*
* @example
* ```tsx
* // Type-safe usage with namespace
* <IcuTrans<'welcome.message', 'common'>
* i18nKey="welcome.message"
* defaultTranslation="Welcome <0>friend</0>!"
* content={[{ type: 'strong', props: {} }]}
* />
* ```
*/
export interface IcuTransComponent {
<
const Key extends ParseKeys<Ns, TOpt, KPrefix>,
Ns extends Namespace = _DefaultNamespace,
KPrefix = undefined,
TContext extends string | undefined = undefined,
TOpt extends TOptions & { context?: TContext } = { context: TContext },
>(
props: IcuTransProps<Key, Ns, KPrefix, TContext, TOpt>,
): React.ReactElement;
}
/**
* IcuTransWithoutContext component for rendering ICU MessageFormat translations
* This version does not use React context and requires explicit i18n instance
*
* @example
* ```tsx
* // Type-safe usage with namespace
* <IcuTransWithoutContext<'welcome.message', 'common'>
* i18nKey="welcome.message"
* defaultTranslation="Welcome <0>back</0>!"
* content={[{ type: 'strong', props: {} }]}
* i18n={i18nInstance}
* />
* ```
*/
export interface IcuTransWithoutContextComponent {
<
const Key extends ParseKeys<Ns, TOpt, KPrefix>,
Ns extends Namespace = _DefaultNamespace,
KPrefix = undefined,
TContext extends string | undefined = undefined,
TOpt extends TOptions & { context?: TContext } = { context: TContext },
>(
props: IcuTransWithoutContextProps<Key, Ns, KPrefix, TContext, TOpt>,
): React.ReactElement;
}
export const IcuTrans: IcuTransComponent;
export const IcuTransWithoutContext: IcuTransWithoutContextComponent;
export function setDefaults(options: ReactOptions): void;
export function getDefaults(): ReactOptions;
export function setI18n(instance: i18n): void;
export function getI18n(): i18n;
export function composeInitialProps(ForComponent: any): (ctx: unknown) => Promise<any>;
export function getInitialProps(): {
initialI18nStore: {
[ns: string]: {};
};
initialLanguage: string;
};
export interface ReportNamespaces {
addUsedNamespaces(namespaces: Namespace): void;
getUsedNamespaces(): string[];
}
declare module 'i18next' {
// interface i18n {
// reportNamespaces?: ReportNamespaces;
// }
interface CustomInstanceExtensions {
reportNamespaces?: ReportNamespaces;
}
// i18next 27 declares `ReactOptions` empty and leaves its shape to react-i18next. These are
// exactly i18next 26's property types: TypeScript accepts the repeated declarations on i18next 26
// and they fill in the empty interface on i18next 27.
interface ReactOptions {
/**
* Set it to fallback to let passed namespaces to translated hoc act as fallbacks
* @default 'default'
*/
nsMode?: 'default' | 'fallback';
/**
* Set it to the default parent element created by the Trans component.
* @default 'div'
*/
defaultTransParent?: string;
/**
* Set which events trigger a re-render, can be set to false or string of events
* @default 'languageChanged'
*/
bindI18n?: string | false;
/**
* Set which events on store trigger a re-render, can be set to false or string of events
* @default ''
*/
bindI18nStore?: string | false;
/**
* Set fallback value for Trans components without children
* @default undefined
*/
transEmptyNodeValue?: string;
/**
* Set it to false if you do not want to use Suspense
* @default true
*/
useSuspense?: boolean;
/**
* Function to generate an i18nKey from the defaultValue (or Trans children)
* when no key is provided.
* By default, the defaultValue (Trans text) itself is used as the key.
* If you want to require keys for all translations, supply a function
* that always throws an error.
* @default undefined
*/
hashTransKey?(defaultValue: TOptionsBase['defaultValue']): TOptionsBase['defaultValue'];
/**
* Convert eg. <br/> found in translations to a react component of type br
* @default true
*/
transSupportBasicHtmlNodes?: boolean;
/**
* Which nodes not to convert in defaultValue generation in the Trans component.
* @default ['br', 'strong', 'i', 'p']
*/
transKeepBasicHtmlNodesFor?: readonly string[];
/**
* Wrap text nodes in a user-specified element.
* @default ''
*/
transWrapTextNodes?: string;
/**
* Default props to apply to all Trans components.
* Component-level props will override these defaults.
*/
transDefaultProps?: {
tOptions?: TOptions;
values?: object;
shouldUnescape?: boolean;
components?: readonly unknown[] | { readonly [tagName: string]: unknown }; // Use `unknown` (or `any`) to be permissive without importing React.
};
/**
* Optional keyPrefix that will be automatically applied to returned t function in useTranslation for example.
* Accepts a string or a selector function (e.g. `$ => $.deeply.nested`).
* @default undefined
*/
keyPrefix?: string | (($: any) => any);
/**
* Unescape function
* by default it unescapes some basic html entities
*/
unescape?(str: string): string;
}
}
type ObjectOrNever = TypeOptions['allowObjectInHTMLChildren'] extends true
? Record<string, unknown>
: never;
type ReactI18NextChildren = React.ReactNode | ObjectOrNever;
declare module 'react' {
namespace JSX {
interface IntrinsicAttributes {
i18nIsDynamicList?: boolean;
}
}
interface HTMLAttributes<T> {
// Without allowObjectInHTMLChildren this is React's own children type, so it follows @types/react (#1928).
// The union is inspired by the typings for React.ReactNode. We do this to fix "This JSX tag's 'children' prop
// expects a single child of type 'ReactI18NextChildren', but multiple children were provided":
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5a1e9f91ed0143adede394adb3f540e650455f71/types/react/index.d.ts#L268
children?: TypeOptions['allowObjectInHTMLChildren'] extends true
? ReactI18NextChildren | Iterable<ReactI18NextChildren>
: DOMAttributes<T>['children'];
}
}
type _DefaultNamespace = TypeOptions['defaultNS'];
export function useSSR(initialI18nStore: Resource, initialLanguage: string): void;
// If the version is earlier than i18next v25.4.0, enableSelector does not exist in TypeOptions, so a conditional type is used to maintain type compatibility.
type _EnableSelector = TypeOptions extends { enableSelector: infer U } ? U : false;
export type UseTranslationOptions<KPrefix> = {
i18n?: i18n;
useSuspense?: boolean;
keyPrefix?: KPrefix;
bindI18n?: string | false;
nsMode?: 'fallback' | 'default';
lng?: string;
// other of these options might also work: https://github.com/i18next/i18next/blob/master/index.d.ts#L127
};
export type UseTranslationResponse<Ns extends Namespace, KPrefix> = [
t: TFunction<Ns, KPrefix>,
i18n: i18n,
ready: boolean,
] & {
t: TFunction<Ns, KPrefix>;
i18n: i18n;
ready: boolean;
};
// Workaround to make code completion to work when suggesting namespaces.
// This is a typescript limitation when using generics with default values,
// it'll be addressed in this issue: https://github.com/microsoft/TypeScript/issues/52516
export type FallbackNs<Ns> = Ns extends undefined
? _DefaultNamespace
: Ns extends Namespace
? Ns
: _DefaultNamespace;
export const useTranslation: _EnableSelector extends true | 'optimize' | 'strict'
? UseTranslationSelector
: UseTranslationLegacy;
interface UseTranslationLegacy {
<
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
const KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
>(
ns?: Ns,
options?: UseTranslationOptions<KPrefix>,
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
}
interface UseTranslationSelector {
// Overload: selector function as keyPrefix — provides type-safe key narrowing
<
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined,
const KPrefix extends KeyPrefixSelector<FallbackNs<Ns>>,
>(
ns: Ns,
options: UseTranslationOptions<KPrefix> & { keyPrefix: KPrefix },
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
// Overload: string or undefined keyPrefix (original behavior)
<
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
const KPrefix = undefined,
>(
ns?: Ns,
options?: UseTranslationOptions<KPrefix>,
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
}
// Need to see usage to improve this
export function withSSR(): <Props>(WrappedComponent: React.ComponentType<Props>) => {
({
initialI18nStore,
initialLanguage,
...rest
}: {
initialI18nStore: Resource;
initialLanguage: string;
} & Props): React.FunctionComponentElement<Props>;
getInitialProps: (ctx: unknown) => Promise<any>;
};
export interface WithTranslation<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
> {
t: TFunction<FallbackNs<Ns>, KPrefix>;
i18n: i18n;
tReady: boolean;
}
export interface WithTranslationProps {
i18n?: i18n;
useSuspense?: boolean;
}
export function withTranslation<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
>(
ns?: Ns,
options?: {
withRef?: boolean;
keyPrefix?: KPrefix;
},
): <
C extends React.ComponentType<React.ComponentProps<any> & WithTranslationProps>,
ResolvedProps = React.JSX.LibraryManagedAttributes<
C,
$Subtract<React.ComponentProps<C>, WithTranslationProps>
>,
>(
component: C,
) => React.ComponentType<Omit<ResolvedProps, keyof WithTranslation<Ns>> & WithTranslationProps>;
export interface I18nextProviderProps {
children?: React.ReactNode;
i18n: i18n;
defaultNS?: string | string[];
}
export const I18nextProvider: React.FunctionComponent<I18nextProviderProps>;
export const I18nContext: React.Context<{ i18n: i18n }>;
export interface TranslationProps<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
> {
children: (
t: TFunction<FallbackNs<Ns>, KPrefix>,
options: {
i18n: i18n;
lng: string;
},
ready: boolean,
) => React.ReactNode;
ns?: Ns;
i18n?: i18n;
useSuspense?: boolean;
keyPrefix?: KPrefix;
nsMode?: 'fallback' | 'default';
}
export function Translation<
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
>(props: TranslationProps<Ns, KPrefix>): any;