Skip to content

Commit

Permalink
feat: t.render
Browse files Browse the repository at this point in the history
  • Loading branch information
schummar committed Aug 25, 2021
1 parent 1c6b9bd commit 5b46b39
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 97 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ function t.format(template: string, values: V): ReactNode;
`t.format` can be used to format something using ICU. E.g. `t.format('{d, date, short}', { d: new Date() })`.
### t.render
```ts
function f.render(renderFn: (locale: string) => ReactNode): ReactNode;
```
`t.render` can be used to pass the current locale to some other component and will abviously update automatically when the locale changes.
A common example will be to use the Intl api like: `t.render(locale => new Intl.DateTimeFormat(locale).format(new Date()))`.
### useTranslator
```ts
Expand All @@ -165,6 +174,7 @@ type HookTranslator = {
(id: K, values: V, options?: Options): string | string[];
unknow: (id: string, values?: Record<string, unknown>, options?: Options): string | string[];
format: (template: string, values: V): string;
locale: string;
}

type Options = {
Expand All @@ -186,6 +196,7 @@ type Translator = {
(id: K, values: V, options?: Options): string | string[];
unknow: (id: string, values?: Record<string, unknown>, options?: Options): string | string[];
format: (template: string, values: V): string;
locale: string;
}

type Options = {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"lint:eslint": "eslint src",
"lint:tsc": "tsc --noEmit",
"prepublishOnly": "run-s build",
"test": "cd test && ava",
"test": "cd test && ava -v",
"test-watch": "cd test && ava --watch",
"coverage": "nyc --include src --all npm run test"
},
Expand Down
56 changes: 37 additions & 19 deletions src/react/translator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, Fragment, useContext, useMemo } from 'react';
import React, { createContext, Fragment, ReactNode, useContext, useMemo } from 'react';
import { DictStore } from '../dictStore';
import { format, translate } from '../translate';
import { getTranslator } from '../translator';
Expand Down Expand Up @@ -40,20 +40,23 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
const localeFallbackOrder = [locale, ...fallbackLocale];
const dicts = useFuture(() => store.load(...new Set(localeFallbackOrder)), [locale]);

const t: TranslateUnknown<UseTranslatorOptions, string> = (id, ...[values, options]) => {
const fallback = options?.fallback ?? fallbackDefault;
const placeholder = options?.placeholder ?? placeholderDefault;
return translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, placeholder, locale, warn });
};

const f: Format<string> = (template, ...[values]) => {
return format(template, values as any, locale);
};

return Object.assign(t as unknown as TranslateKnown<FlattenDict<D>, UseTranslatorOptions, string, readonly string[]>, {
unknown: t,
format: f,
});
return useMemo(() => {
const t: TranslateUnknown<UseTranslatorOptions, string> = (id, ...[values, options]) => {
const fallback = options?.fallback ?? fallbackDefault;
const placeholder = options?.placeholder ?? placeholderDefault;
return translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, placeholder, locale, warn });
};

const f: Format<string> = (template, ...[values]) => {
return format(template, values as any, locale);
};

return Object.assign((t as unknown) as TranslateKnown<FlattenDict<D>, UseTranslatorOptions, string, readonly string[]>, {
unknown: t,
format: f,
locale,
});
}, [fallbackDefault, placeholderDefault, dicts, store.sourceDict, locale, warn]);
};

const TranslatorComponent = ({
Expand All @@ -70,11 +73,19 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
const localeFallbackOrder = [locale, ...fallbackLocale];
const dicts = useFuture(() => store.load(...new Set(localeFallbackOrder)), [locale]);

if (id === 'flatKey') console.log(id, options, contextLocale, localeFallbackOrder, dicts);

const fallback = options?.fallback ?? fallbackElement ?? fallbackDefault;
const placeholder = options?.placeholder ?? placeholderElement ?? placeholderDefault;
const text = translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, placeholder, locale, warn });

const text = useMemo(() => translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, placeholder, locale, warn }), [
dicts,
store.sourceDict,
id,
values,
fallback,
placeholder,
locale,
warn,
]);
const textArray = text instanceof Array ? text : [text];
const Component = options?.component ?? Fragment;

Expand All @@ -94,7 +105,7 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
const FormatComponent = ({ template, values }: { template: string; values?: Record<string, unknown> }) => {
const contextLocale = useContext(TranslationContext).locale;
const locale = contextLocale ?? sourceLocale;
const text = format(template, values, locale);
const text = useMemo(() => format(template, values, locale), [template, values, locale]);

return <>{text}</>;
};
Expand All @@ -103,11 +114,18 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
return <FormatComponent {...{ template, values: values as any }} />;
};

const RenderComponent = ({ render }: { render: (locale: string) => ReactNode }) => {
const contextLocale = useContext(TranslationContext).locale;
const locale = contextLocale ?? sourceLocale;
return <>{render(locale)}</>;
};

const t: ReactTranslator<FlattenDict<D>> = Object.assign(
createTranslatorComponent as TranslateKnown<FlattenDict<D>, ReactTranslatorOptions, React.ReactNode, React.ReactNode>,
{
unknown: createTranslatorComponent,
format: createFormatComponent,
render: (render: (locale: string) => ReactNode) => <RenderComponent render={render} />,
},
);

Expand Down
22 changes: 13 additions & 9 deletions src/react/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import React, { ReactNode } from 'react';
import { CreateTranslatorOptions, Dict, FlatDict, Format, GetTranslator, TranslateKnown, TranslateUnknown } from '../types';

export type ReactCreateTranslatorOptions<D extends Dict> = CreateTranslatorOptions<D> & {
fallbackElement?: React.ReactNode | ((id: string, sourceTranslation: string) => React.ReactNode);
fallbackElement?: ReactNode | ((id: string, sourceTranslation: string) => ReactNode);
placeholder?: string | ((id: string, sourceTranslation: string) => string);
placeholderElement?: React.ReactNode | ((id: string, sourceTranslation: string) => React.ReactNode);
placeholderElement?: ReactNode | ((id: string, sourceTranslation: string) => ReactNode);
};

export type ReactCreateTranslatorResult<D extends FlatDict> = {
Expand All @@ -18,19 +18,23 @@ export type UseTranslatorOptions = {
placeholder?: string;
};

export type UseTranslator<D extends FlatDict> = (locale?: string) => TranslateKnown<D, UseTranslatorOptions, string, readonly string[]> & {
export type UseTranslator<D extends FlatDict> = (
locale?: string,
) => TranslateKnown<D, UseTranslatorOptions, string, readonly string[]> & {
unknown: TranslateUnknown<UseTranslatorOptions, string>;
format: Format<string>;
locale: string;
};

export type ReactTranslatorOptions = {
locale?: string;
fallback?: React.ReactNode;
placeholder?: React.ReactNode;
fallback?: ReactNode;
placeholder?: ReactNode;
component?: React.ElementType;
};

export type ReactTranslator<D extends FlatDict> = TranslateKnown<D, ReactTranslatorOptions, React.ReactNode, React.ReactNode> & {
unknown: TranslateUnknown<ReactTranslatorOptions, React.ReactNode>;
format: Format<React.ReactNode>;
export type ReactTranslator<D extends FlatDict> = TranslateKnown<D, ReactTranslatorOptions, ReactNode, ReactNode> & {
unknown: TranslateUnknown<ReactTranslatorOptions, ReactNode>;
format: Format<ReactNode>;
render: (render: (locale: string) => ReactNode) => ReactNode;
};
39 changes: 19 additions & 20 deletions src/translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,29 @@ import {
TranslateUnknown,
} from './types';

export const getTranslator =
<D extends Dict>(
store: DictStore<D>,
{ fallbackLocale = [], fallback: globalFallback, warn }: CreateTranslatorOptions<D>,
): GetTranslator<FlattenDict<D>> =>
async (locale: string) => {
const localeFallbackOrder = [locale, ...fallbackLocale];
const dicts = await store.load(...new Set(localeFallbackOrder));
export const getTranslator = <D extends Dict>(
store: DictStore<D>,
{ fallbackLocale = [], fallback: globalFallback, warn }: CreateTranslatorOptions<D>,
): GetTranslator<FlattenDict<D>> => async (locale: string) => {
const localeFallbackOrder = [locale, ...fallbackLocale];
const dicts = await store.load(...new Set(localeFallbackOrder));

const t: TranslateUnknown<GetTranslatorOptions, string> = (id, ...[values, options]) => {
const fallback = options?.fallback ?? globalFallback;
return translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, locale, warn });
};

const f: Format<string> = (template, ...[values]) => {
return format(template, values as any, locale);
};
const t: TranslateUnknown<GetTranslatorOptions, string> = (id, ...[values, options]) => {
const fallback = options?.fallback ?? globalFallback;
return translate({ dicts, sourceDict: store.sourceDict, id, values, fallback, locale, warn });
};

return Object.assign(t as unknown as TranslateKnown<FlattenDict<D>, GetTranslatorOptions, string, readonly string[]>, {
unknown: t,
format: f,
});
const f: Format<string> = (template, ...[values]) => {
return format(template, values as any, locale);
};

return Object.assign((t as unknown) as TranslateKnown<FlattenDict<D>, GetTranslatorOptions, string, readonly string[]>, {
unknown: t,
format: f,
locale,
});
};

export function createTranslator<D extends Dict>(options: CreateTranslatorOptions<D>): CreateTranslatorResult<FlattenDict<D>> {
const store = new DictStore(options);

Expand Down
5 changes: 4 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,12 @@ export type GetTranslatorOptions = {
fallback?: string;
};

export type GetTranslator<D extends FlatDict> = (locale: string) => Promise<
export type GetTranslator<D extends FlatDict> = (
locale: string,
) => Promise<
TranslateKnown<D, GetTranslatorOptions, string, readonly string[]> & {
unknown: TranslateUnknown<GetTranslatorOptions, string>;
format: Format<string>;
locale: string;
}
>;
4 changes: 0 additions & 4 deletions test/_setup.ts

This file was deleted.

3 changes: 1 addition & 2 deletions test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
],
"require": [
"ts-node/register",
"global-jsdom/register",
"./_setup.ts"
"global-jsdom/register"
]
}
}
Loading

0 comments on commit 5b46b39

Please sign in to comment.