Merge pull request #32235 from huy-nguyen/add-fluent-project

Add type definitions for project fluent's constituent packages (fluent, fluent-react and fluent-langneg)
This commit is contained in:
Benjamin Lichtman
2019-01-24 14:16:00 -08:00
committed by GitHub
13 changed files with 342 additions and 0 deletions
@@ -0,0 +1,7 @@
import { negotiateLanguages } from 'fluent-langneg';
const supportedLocales = negotiateLanguages(
['en-US', 'pl'], // requested locales
['de', 'en-US', 'pl'], // available locales
{ defaultLocale: 'en-US' }
);
+11
View File
@@ -0,0 +1,11 @@
// Type definitions for fluent-langneg 0.1
// Project: http://projectfluent.io
// Definitions by: Huy Nguyen <https://github.com/huy-nguyen>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
export interface LanguageNegotiationOptions {
strategy?: 'filtering' | 'matching' | 'lookup';
defaultLocale?: string;
}
export function negotiateLanguages(requestedLocales: ReadonlyArray<string>, availableLocales: ReadonlyArray<string>, options?: LanguageNegotiationOptions): string[];
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"fluent-langneg-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }
+60
View File
@@ -0,0 +1,60 @@
import { Localized, LocalizationProvider, withLocalization, GetString } from 'fluent-react';
import { FluentBundle, ftl } from 'fluent';
import * as ReactDOM from 'react-dom';
// Localized examples:
const Test = () => (
<Localized id="hello-world">
<p>Hello, world!</p>
</Localized>
);
// LocalizationProvider examples:
function* generateBundles(currentLocales: string[]) {
for (const locale of currentLocales) {
const bundle = new FluentBundle(locale);
bundle.addMessages(ftl`some-message = Hello`);
yield bundle;
}
}
ReactDOM.render(
<LocalizationProvider bundles={generateBundles(['en-US'])}>
Content
</LocalizationProvider>,
document.getElementById('root')
);
// withLocalization examples:
interface Props {
getString: GetString;
otherProp: number;
someOtherProp: string;
}
function HelloButton(props: Props) {
const { getString } = props;
return (
<button onClick={() => alert(getString('hello'))}>
👋
</button>
);
}
const LocalizedHelloButton = withLocalization(HelloButton);
// Remove `getString` from list of required props:
const Test2 = () => (
<LocalizedHelloButton otherProp={2} someOtherProp='abc'/>
);
// Should not allow `getString` prop:
const Test3 = () => (
// $ExpectError
<LocalizedHelloButton otherProp={2} someOtherProp='abc' getString={() => {}}/>
);
// Should not allow any other props to be omitted:
const Test4 = () => (
// $ExpectError
<LocalizedHelloButton otherProp={2}/>
);
+101
View File
@@ -0,0 +1,101 @@
// Type definitions for fluent-react 0.8
// Project: http://projectfluent.org
// Definitions by: Huy Nguyen <https://github.com/huy-nguyen>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
import * as React from 'react';
import {
FluentBundle,
} from 'fluent';
export interface Node {
TEXT_NODE: 3;
nodeType: number;
localName?: string;
textContext: string;
}
export type MarkupParser = (str: string) => Node[];
export interface Context {
l10n: ReactLocalization;
parseMarkup: MarkupParser;
}
export interface LocalizationProviderProps {
bundles: IterableIterator<FluentBundle>;
parseMarkup?: MarkupParser;
}
export class LocalizationProvider extends React.Component<LocalizationProviderProps> {
}
export class ReactLocalization {
constructor(bundles: IterableIterator<FluentBundle>);
getString(id: string, args?: object, fallback?: string): string;
}
export interface LocalizedProps {
id: string;
attrs?: object;
[key: string]: any;
}
export class Localized extends React.Component<LocalizedProps> {
}
// Inspired by react-redux's type definition:
/**
* A property P will be present if:
* - it is present in DecorationTargetProps
*
* Its value will be dependent on the following conditions
* - if property P is present in InjectedProps and its definition extends the definition
* in DecorationTargetProps, then its definition will be that of DecorationTargetProps[P]
* - if property P is not present in InjectedProps then its definition will be that of
* DecorationTargetProps[P]
* - if property P is present in InjectedProps but does not extend the
* DecorationTargetProps[P] definition, its definition will be that of InjectedProps[P]
*/
export type Matching<InjectedProps, DecorationTargetProps> = {
[P in keyof DecorationTargetProps]: P extends keyof InjectedProps
? InjectedProps[P] extends DecorationTargetProps[P]
? DecorationTargetProps[P]
: InjectedProps[P]
: DecorationTargetProps[P];
};
/**
* a property P will be present if :
* - it is present in both DecorationTargetProps and InjectedProps
* - InjectedProps[P] can satisfy DecorationTargetProps[P]
* ie: decorated component can accept more types than decorator is injecting
*
* For decoration, inject props or ownProps are all optionally
* required by the decorated (right hand side) component.
* But any property required by the decorated component must be satisfied by the injected property.
*/
export type Shared<
InjectedProps,
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
> = {
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]?: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never;
};
// Infers prop type from component C
export type GetProps<C> = C extends React.ComponentType<infer P> ? P : never;
export type GetString = (id: string, args?: object) => string;
export interface InjectedProps {
getString: GetString;
}
// Taken from
// https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#predefined-conditional-types
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
// Injects `getString` and removes it from the prop requirements. Will not pass
// through `getString` if it's passed in during render.
export function withLocalization<C extends React.ComponentType<Matching<InjectedProps, GetProps<C>>>>(
component: C
): React.ComponentType<Omit< GetProps<C>, keyof Shared<InjectedProps, GetProps<C>>>>;
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"dependencies": {
"csstype": "^2.2.0"
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"jsx": "preserve",
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"fluent-react-tests.tsx"
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "dtslint/dt.json"
}
+44
View File
@@ -0,0 +1,44 @@
import { FluentBundle, ftl, FluentResource } from 'fluent';
const bundle1 = new FluentBundle('en-US');
const errors1 = bundle1.addMessages(ftl`
-brand-name = Foo 3000
welcome = Welcome, { $name }, to { -brand-name }!
`);
if (errors1.length) {
// syntax errors are per-message and don't break the whole resource
}
const welcome = bundle1.getMessage('welcome');
bundle1.format(welcome, { name: 'Anna' });
// FluentBundle constructor examples:
const bundle2 = new FluentBundle(['en-US']);
const bundle3 = new FluentBundle(['en-US'], { useIsolating: false });
const bundle4 = new FluentBundle(['en-US'], {
useIsolating: true,
functions: {
NODE_ENV: () => 'production'
}
});
// FluentBundle addMessages examples:
bundle1.addMessages('foo = Foo');
bundle2.getMessage('foo');
// FluentBundle addResource examples:
const a = FluentResource.fromString('foo');
bundle1.addResource(a);
bundle2.getMessage('foo');
// FluentBundle format examples:
const errors2: any[] = [];
bundle1.addMessages('hello = Hello, { $name }!');
const hello = bundle2.getMessage('hello');
bundle3.format(hello, { name: 'Jane' }, errors2);
bundle3.format(hello, undefined, errors2);
+37
View File
@@ -0,0 +1,37 @@
// Type definitions for fluent 0.10
// Project: http://projectfluent.org
// Definitions by: Huy Nguyen <https://github.com/huy-nguyen>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.2
export interface FluentBundleContructorOptions {
functions?: object;
useIsolating?: boolean;
transform?: (...args: any[]) => any;
}
export class FluentType {
constructor(value: any, opts: object);
toString(bundle: FluentBundle): string;
valueOf(): any;
}
export class FluentNone extends FluentType {}
export class FluentNumber extends FluentType {}
export class FluentDateTime extends FluentType {}
export type FluentNode = FluentType | string;
export class FluentResource extends Map {
static fromString(source: string): FluentResource;
}
export class FluentBundle {
constructor(locales: string | string[], options?: FluentBundleContructorOptions);
addMessages(source: string): string[];
getMessage(id: string): FluentNode[];
format(message: FluentNode[], args?: object, errors?: string[]): string;
addResource(res: FluentResource): string[];
}
export function ftl(strings: TemplateStringsArray): string;
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"fluent-tests.ts"
]
}
+1
View File
@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }