From 78c2b845a64d1d655677b04fb00d37b9986942f2 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 14 Dec 2017 09:15:57 +0100 Subject: [PATCH 1/3] Backup v4 --- types/react-i18next/v4/index.d.ts | 37 ++++++++ .../react-i18next/v4/src/I18nextProvider.d.ts | 12 +++ types/react-i18next/v4/src/interpolate.d.ts | 23 +++++ .../react-i18next/v4/src/loadNamespaces.d.ts | 9 ++ types/react-i18next/v4/src/trans.d.ts | 8 ++ types/react-i18next/v4/src/translate.d.ts | 15 +++ .../v4/test/react-i18next-tests.tsx | 92 +++++++++++++++++++ types/react-i18next/v4/tsconfig.json | 33 +++++++ types/react-i18next/v4/tslint.json | 1 + 9 files changed, 230 insertions(+) create mode 100644 types/react-i18next/v4/index.d.ts create mode 100644 types/react-i18next/v4/src/I18nextProvider.d.ts create mode 100644 types/react-i18next/v4/src/interpolate.d.ts create mode 100644 types/react-i18next/v4/src/loadNamespaces.d.ts create mode 100644 types/react-i18next/v4/src/trans.d.ts create mode 100644 types/react-i18next/v4/src/translate.d.ts create mode 100644 types/react-i18next/v4/test/react-i18next-tests.tsx create mode 100644 types/react-i18next/v4/tsconfig.json create mode 100644 types/react-i18next/v4/tslint.json diff --git a/types/react-i18next/v4/index.d.ts b/types/react-i18next/v4/index.d.ts new file mode 100644 index 0000000000..d7bb05a30f --- /dev/null +++ b/types/react-i18next/v4/index.d.ts @@ -0,0 +1,37 @@ +// Type definitions for react-i18next 4.6 +// Project: https://github.com/i18next/react-i18next +// Definitions by: Giedrius Grabauskas +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +import { TranslationFunction } from "i18next"; + +import I18nextProvider from "./src/I18nextProvider"; +import Interpolate from "./src/interpolate"; +import loadNamespaces from "./src/loadNamespaces"; +import Trans from "./src/trans"; +import translate from "./src/translate"; + +export { + I18nextProvider, + Interpolate, + loadNamespaces, + Trans, + translate, + // Exports for TypeScript only + TranslationFunction +}; + +/** + * Extend your component's Prop interface with this one to get access to `this.props.t` + * + * Please note that if you use the `translateFuncName` option, you should create + * your own interface just like this one, but with your name of the translation function. + * + * interface MyComponentProps extends ReactI18next.InjectedTranslateProps {} + */ +export interface InjectedTranslateProps { + t?: TranslationFunction; +} + +export as namespace reactI18Next; diff --git a/types/react-i18next/v4/src/I18nextProvider.d.ts b/types/react-i18next/v4/src/I18nextProvider.d.ts new file mode 100644 index 0000000000..2ecb87f4a1 --- /dev/null +++ b/types/react-i18next/v4/src/I18nextProvider.d.ts @@ -0,0 +1,12 @@ +import * as React from "react"; +import { i18n } from "i18next"; + +// tslint:disable-next-line:interface-name +export interface I18nextProviderProps { + i18n: i18n; + children: React.ReactElement; + initialI18nStore?: any; + initialLanguage?: string; +} + +export default class I18nextProvider extends React.Component { } diff --git a/types/react-i18next/v4/src/interpolate.d.ts b/types/react-i18next/v4/src/interpolate.d.ts new file mode 100644 index 0000000000..e6f07893ed --- /dev/null +++ b/types/react-i18next/v4/src/interpolate.d.ts @@ -0,0 +1,23 @@ +import * as React from "react"; +import { InterpolationOptions } from "i18next"; + +export type InterpolateValue = string | JSX.Element; + +export interface InterpolatePropsBase { + parent?: string; + regexp?: RegExp; + useDangerouslySetInnerHTML?: boolean; + dangerouslySetInnerHTMLPartElement?: string; + options?: InterpolationOptions; + i18nKey?: string; + className?: string; + style?: React.CSSProperties; +} + +export interface OtherInterpolateProps { + [regexKey: string]: InterpolateValue | RegExp | InterpolationOptions | boolean | undefined; +} + +export type InterpolateProps = InterpolatePropsBase & OtherInterpolateProps; + +export default class Interpolate extends React.Component { } diff --git a/types/react-i18next/v4/src/loadNamespaces.d.ts b/types/react-i18next/v4/src/loadNamespaces.d.ts new file mode 100644 index 0000000000..d0e0b372f5 --- /dev/null +++ b/types/react-i18next/v4/src/loadNamespaces.d.ts @@ -0,0 +1,9 @@ +import * as React from "react"; +import { i18n } from "i18next"; + +export interface LoadNamespacesArguments { + components: Array | React.StatelessComponent>; + i18n: i18n; +} + +export default function loadNamespaces(args: LoadNamespacesArguments): Promise; diff --git a/types/react-i18next/v4/src/trans.d.ts b/types/react-i18next/v4/src/trans.d.ts new file mode 100644 index 0000000000..384e1605f9 --- /dev/null +++ b/types/react-i18next/v4/src/trans.d.ts @@ -0,0 +1,8 @@ +import * as React from "react"; + +export interface TransProps { + i18nKey?: string; + [name: string]: any; +} + +export default class Trans extends React.Component { } diff --git a/types/react-i18next/v4/src/translate.d.ts b/types/react-i18next/v4/src/translate.d.ts new file mode 100644 index 0000000000..d41b4dc00a --- /dev/null +++ b/types/react-i18next/v4/src/translate.d.ts @@ -0,0 +1,15 @@ +import * as React from "react"; +import { i18n } from "i18next"; + +export interface TranslateOptions { + withRef?: boolean; + bindI18n?: string; + bindStore?: string; + translateFuncName?: string; + wait?: boolean; + nsMode?: string; + i18n?: i18n; +} + +// tslint:disable-next-line:ban-types +export default function translate(namespaces?: TKey[] | TKey, options?: TranslateOptions): (WrappedComponent: C) => C; diff --git a/types/react-i18next/v4/test/react-i18next-tests.tsx b/types/react-i18next/v4/test/react-i18next-tests.tsx new file mode 100644 index 0000000000..6105ccf921 --- /dev/null +++ b/types/react-i18next/v4/test/react-i18next-tests.tsx @@ -0,0 +1,92 @@ +import * as React from 'react'; +import * as i18n from 'i18next'; +import { translate, I18nextProvider, Interpolate, InjectedTranslateProps, TranslationFunction, loadNamespaces, Trans } from 'react-i18next'; + +interface InnerAnotherComponentProps { + _?: TranslationFunction; +} + +class InnerAnotherComponent extends React.Component { + render() { + const _ = this.props._!; + return

{_('content.text', { /* options t options */ })}

; + } +} + +const AnotherComponent = translate('view', { wait: true, translateFuncName: '_' })(InnerAnotherComponent); + +class InnerYetAnotherComponent extends React.Component { + render() { + const t = this.props.t!; + return

{t('usingDefaultNS', { /* options t options */ })}

; + } +} + +const YetAnotherComponent = translate()(InnerYetAnotherComponent); + +@translate(['view', 'nav'], { wait: true }) +class TranslatableView extends React.Component { + render() { + const t = this.props.t!; + const interpolateComponent = "a interpolated component"; + + return ( +
+

{t('common:appName')}

+ + + + + {t('nav:link1')} +
+ ); + } +} + +class App extends React.Component { + render() { + return ( +
+
+ +
+
+ ); + } +} + + + +; + +loadNamespaces({ components: [App], i18n }).then(() => { }).catch(error => { }); + +; +; + + +; + +type Key = "view" | "nav"; + +@translate(['view', 'nav']) +class GenericsTest extends React.Component { + render() { return null; } +} + +@translate('view') +class GenericsTest2 extends React.Component { + render() { return null; } +} diff --git a/types/react-i18next/v4/tsconfig.json b/types/react-i18next/v4/tsconfig.json new file mode 100644 index 0000000000..a0f7445e79 --- /dev/null +++ b/types/react-i18next/v4/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "paths": { + "react-i18next": [ "react-i18next/v4" ] + }, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react", + "experimentalDecorators": true + }, + "files": [ + "index.d.ts", + "test/react-i18next-tests.tsx", + "src/I18nextProvider.d.ts", + "src/interpolate.d.ts", + "src/loadNamespaces.d.ts", + "src/trans.d.ts", + "src/translate.d.ts" + ] +} diff --git a/types/react-i18next/v4/tslint.json b/types/react-i18next/v4/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/react-i18next/v4/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } From c46598893cdd00eeb70d797ff2f2556e772898cc Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 30 Nov 2017 21:15:13 +0100 Subject: [PATCH 2/3] React-i18next Generic translate Fixes #16395 --- types/react-i18next/index.d.ts | 18 ++---- types/react-i18next/src/context.d.ts | 14 +++++ types/react-i18next/src/props.d.ts | 24 +++++++ types/react-i18next/src/translate.d.ts | 61 ++++++++++++++++-- .../test/react-i18next-tests.tsx | 63 ++++++++++++++++--- 5 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 types/react-i18next/src/context.d.ts create mode 100644 types/react-i18next/src/props.d.ts diff --git a/types/react-i18next/index.d.ts b/types/react-i18next/index.d.ts index d7bb05a30f..9e731fe5e6 100644 --- a/types/react-i18next/index.d.ts +++ b/types/react-i18next/index.d.ts @@ -1,10 +1,10 @@ -// Type definitions for react-i18next 4.6 +// Type definitions for react-i18next 7.0 // Project: https://github.com/i18next/react-i18next // Definitions by: Giedrius Grabauskas // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 2.4 -import { TranslationFunction } from "i18next"; +import { i18n as I18n, TranslationFunction } from "i18next"; import I18nextProvider from "./src/I18nextProvider"; import Interpolate from "./src/interpolate"; @@ -22,16 +22,6 @@ export { TranslationFunction }; -/** - * Extend your component's Prop interface with this one to get access to `this.props.t` - * - * Please note that if you use the `translateFuncName` option, you should create - * your own interface just like this one, but with your name of the translation function. - * - * interface MyComponentProps extends ReactI18next.InjectedTranslateProps {} - */ -export interface InjectedTranslateProps { - t?: TranslationFunction; -} +export { InjectedI18nProps, InjectedTranslateProps } from "./src/props"; export as namespace reactI18Next; diff --git a/types/react-i18next/src/context.d.ts b/types/react-i18next/src/context.d.ts new file mode 100644 index 0000000000..8e11a4d7a3 --- /dev/null +++ b/types/react-i18next/src/context.d.ts @@ -0,0 +1,14 @@ +import { i18n } from "i18next"; + +export interface ReactI18NextOptions { + wait?: boolean; + withRef?: boolean; + bindI18n?: string; + bindStore?: string; + translateFuncName?: string; + nsMode?: string; +} + +export function setDefaults(options: ReactI18NextOptions): void; + +export function setI18n(instance: i18n): void; diff --git a/types/react-i18next/src/props.d.ts b/types/react-i18next/src/props.d.ts new file mode 100644 index 0000000000..62a201049a --- /dev/null +++ b/types/react-i18next/src/props.d.ts @@ -0,0 +1,24 @@ +import {i18n as I18n, TranslationFunction} from "i18next"; + +/** + * Extend your component's Prop interface with this one to get access to `this.props.t` + * + * Please note that if you use the `translateFuncName` option, you should create + * your own interface just like this one, but with your name of the translation function. + * + * interface MyComponentProps extends ReactI18next.InjectedTranslateProps {} + * + * Then specify the name of the translate function as generic argument + * + * const translated = translate("view", { translateFuncName: "_" })(YourComponent); + */ +export interface InjectedTranslateProps { + t: TranslationFunction; +} + +/** + * Extend your component's Prop interface with this one to get access to `this.props.i18n` + */ +export interface InjectedI18nProps { + i18n: I18n +} diff --git a/types/react-i18next/src/translate.d.ts b/types/react-i18next/src/translate.d.ts index d41b4dc00a..0711d3895d 100644 --- a/types/react-i18next/src/translate.d.ts +++ b/types/react-i18next/src/translate.d.ts @@ -1,15 +1,64 @@ import * as React from "react"; -import { i18n } from "i18next"; +import { i18n as I18n, TranslationFunction } from "i18next"; +import { InjectedTranslateProps, InjectedI18nProps } from "./props"; +import { setDefaults, setI18n } from "./context"; -export interface TranslateOptions { +export interface TranslateOptions { withRef?: boolean; bindI18n?: string; bindStore?: string; - translateFuncName?: string; + translateFuncName?: TTranslateFuncName; wait?: boolean; nsMode?: string; - i18n?: i18n; + i18n?: I18n; } -// tslint:disable-next-line:ban-types -export default function translate(namespaces?: TKey[] | TKey, options?: TranslateOptions): (WrappedComponent: C) => C; +export interface TranslateHocProps { + i18n?: I18n; + initialI18nStore?: object; + initialLanguage?: string; +} + +// Diff / Omit taken from https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766 +type Diff = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T]; +type Omit = Pick>; + +type InjectedProps = InjectedI18nProps & InjectedTranslateProps; + +export interface WrapperComponentClass

extends React.ComponentClass

{ + new (props: P, context?: any): React.Component

& { getWrappedInstance(): React.Component }; +} + +// Injects props and removes them from the prop requirements. +// Adds the new properties t (or whatever the translation function is called) and i18n if needed. +export type InferableComponentEnhancerWithProps = +

(component: React.ComponentClass

| React.StatelessComponent

) => + React.ComponentClass & TranslateHocProps>; + +export type InferableComponentEnhancerWithPropsAndRef = +

(component: React.ComponentClass

| React.StatelessComponent

) => + WrapperComponentClass, P>; + +export interface Translate { + + (namespaces?: TNamespace | TNamespace[], options?: Omit & { withRef?: false }): + InferableComponentEnhancerWithProps<"t">; + + + (namespaces?: TNamespace | TNamespace[], options?: Omit & { withRef: true }): + InferableComponentEnhancerWithPropsAndRef<"t">; + + + (namespaces?: TNamespace | TNamespace[], options?: TranslateOptions & { withRef?: false }): + InferableComponentEnhancerWithProps; + + + (namespaces?: TNamespace | TNamespace[], options?: TranslateOptions & { withRef: true }): + InferableComponentEnhancerWithPropsAndRef; + + setDefaults: typeof setDefaults; + setI18n: typeof setI18n; +} + +declare const translate: Translate; +export default translate; diff --git a/types/react-i18next/test/react-i18next-tests.tsx b/types/react-i18next/test/react-i18next-tests.tsx index 6105ccf921..5ddb17c10c 100644 --- a/types/react-i18next/test/react-i18next-tests.tsx +++ b/types/react-i18next/test/react-i18next-tests.tsx @@ -1,33 +1,54 @@ import * as React from 'react'; import * as i18n from 'i18next'; import { translate, I18nextProvider, Interpolate, InjectedTranslateProps, TranslationFunction, loadNamespaces, Trans } from 'react-i18next'; +import { InjectedI18nProps } from 'react-i18next/src/props'; interface InnerAnotherComponentProps { - _?: TranslationFunction; + _: TranslationFunction; } class InnerAnotherComponent extends React.Component { render() { - const _ = this.props._!; + const _ = this.props._; return

{_('content.text', { /* options t options */ })}

; } } -const AnotherComponent = translate('view', { wait: true, translateFuncName: '_' })(InnerAnotherComponent); +const AnotherComponent = translate('view', { wait: true, translateFuncName: '_' })(InnerAnotherComponent); +const instanceWithoutRef = new AnotherComponent({}); +instanceWithoutRef.componentWillReceiveProps!({ + i18n, + initialI18nStore: { context: { text: "a message" } }, + initialLanguage: "en" +}, {}); + +translate.setDefaults({ wait: false }); +translate.setI18n(i18n); + +const AnotherComponentWithRef = translate("view" as Key, { translateFuncName: "_", withRef: true })(InnerAnotherComponent); +const instanceWithRef = new AnotherComponentWithRef({}); +const ref = instanceWithRef.getWrappedInstance(); +instanceWithRef.componentWillReceiveProps!({ + i18n, + initialI18nStore: { context: { text: "a message" } }, + initialLanguage: "en" +}, {}); class InnerYetAnotherComponent extends React.Component { render() { - const t = this.props.t!; + const t = this.props.t; return

{t('usingDefaultNS', { /* options t options */ })}

; } } const YetAnotherComponent = translate()(InnerYetAnotherComponent); -@translate(['view', 'nav'], { wait: true }) +const YetAnotherComponentWithRef = translate(undefined, { withRef: true })(InnerYetAnotherComponent); +new YetAnotherComponentWithRef({}).getWrappedInstance(); + class TranslatableView extends React.Component { render() { - const t = this.props.t!; + const t = this.props.t; const interpolateComponent = "a interpolated component"; return ( @@ -55,12 +76,14 @@ class TranslatableView extends React.Component { } } +const TranslatedView = translate(["view", "nav"] as Key[], { wait: true })(TranslatableView); + class App extends React.Component { render() { return (
- +
); @@ -81,12 +104,34 @@ loadNamespaces({ components: [App], i18n }).then(() => { }).catch(error => { }); type Key = "view" | "nav"; -@translate(['view', 'nav']) class GenericsTest extends React.Component { render() { return null; } } -@translate('view') +const TranslatedGenericsTest = translate(["view", "nav"] as Key[])(GenericsTest); +; + class GenericsTest2 extends React.Component { render() { return null; } } + +const TranslatedGenericsTest2 = translate("view" as Key)(GenericsTest2); +; + +class ComponentWithInjectedI18n extends React.Component { + render() { return null; } +} + +const TranslatedComponentWithInjectedI18n = translate()(ComponentWithInjectedI18n); +; + +function StatlessComponent(props: InjectedTranslateProps) { + return

{props.t("hy")}

; +} + +const TranslatedStatlessComponent = translate()(StatlessComponent); +; + +interface CustomTranslateFunctionProps { + _: TranslationFunction; +} From 0357d79df8c94a16368bf627beaf488cbc877797 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 14 Dec 2017 12:30:33 +0100 Subject: [PATCH 3/3] Enable Strict Function Types --- types/react-i18next/tsconfig.json | 1 + types/react-i18next/v4/tsconfig.json | 1 + 2 files changed, 2 insertions(+) diff --git a/types/react-i18next/tsconfig.json b/types/react-i18next/tsconfig.json index cbb9a8b45f..c38c15b504 100644 --- a/types/react-i18next/tsconfig.json +++ b/types/react-i18next/tsconfig.json @@ -16,6 +16,7 @@ "types": [], "noEmit": true, "forceConsistentCasingInFileNames": true, + "strictFunctionTypes": true, "jsx": "react", "experimentalDecorators": true }, diff --git a/types/react-i18next/v4/tsconfig.json b/types/react-i18next/v4/tsconfig.json index a0f7445e79..bd85233525 100644 --- a/types/react-i18next/v4/tsconfig.json +++ b/types/react-i18next/v4/tsconfig.json @@ -18,6 +18,7 @@ "types": [], "noEmit": true, "forceConsistentCasingInFileNames": true, + "strictFunctionTypes": true, "jsx": "react", "experimentalDecorators": true },