Skip to content

Commit

Permalink
feat: render passes hook translator
Browse files Browse the repository at this point in the history
  • Loading branch information
schummar committed Oct 1, 2021
1 parent 7da3b36 commit 5515664
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 22 deletions.
36 changes: 19 additions & 17 deletions src/react/translator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
const contextLocale = useContext(TranslationContext).locale;
const locale = overrideLocale ?? contextLocale ?? sourceLocale;
const dicts = useStore(store, locale, ...castArray(fallbackLocale));
const [sourceDict] = useStore(store, sourceLocale) ?? [];
const [sourceDict] = useStore(store, sourceLocale);

return useMemo(() => {
const t: TranslatorFn<FD, HookTranslatorOptions, string> = (id, ...[values, options]) => {
Expand Down Expand Up @@ -104,7 +104,7 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
const contextLocale = useContext(TranslationContext).locale;
const locale = options?.locale ?? contextLocale ?? sourceLocale;
const dicts = useStore(store, locale, ...castArray(fallbackLocale));
const [sourceDict] = useStore(store, sourceLocale) ?? [];
const [sourceDict] = useStore(store, sourceLocale);

const fallback = options?.fallback ?? defaultFallback;
const placeholder = options?.placeholder ?? defaultPlaceholder;
Expand All @@ -129,10 +129,15 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
return <TranslatorComponent id={id} values={values} options={options} />;
};

const RenderComponent = ({ renderFn, dependecies = [renderFn] }: { renderFn: (locale: string) => ReactNode; dependecies?: any[] }) => {
const contextLocale = useContext(TranslationContext).locale;
const locale = contextLocale ?? sourceLocale;
const value = useMemo(() => renderFn(locale), [locale, ...dependecies]);
const RenderComponent = ({
renderFn,
dependecies = [renderFn],
}: {
renderFn: (t: HookTranslator<FD>) => ReactNode;
dependecies?: any[];
}) => {
const t = useTranslator();
const value = useMemo(() => renderFn(t), [t, ...dependecies]);
return <>{value}</>;
};

Expand All @@ -144,46 +149,43 @@ export function createTranslator<D extends Dict>(options: ReactCreateTranslatorO
TranslatorFn<FD, InlineTranslatorOptions, ReactNode>,
Omit<InlineTranslator<FD>, keyof TranslatorFn<FD, InlineTranslatorOptions, ReactNode>>
>(createTranslatorComponent, {
locale: render((locale) => locale, []),
locale: render((t) => t.locale, []),

unknown: createTranslatorComponent as InlineTranslator<FD>['unknown'],

format(template, ...[values]) {
return render((locale) => format({ template, values: values as any, locale, cache: store.cache }), [template, hash(values)]);
return render((t) => format({ template, values: values as any, locale: t.locale, cache: store.cache }), [template, hash(values)]);
},

render,

dateTimeFormat(date, options = dateTimeFormatOptions) {
return render((locale) => store.cache.get(Intl.DateTimeFormat, locale, options).format(toDate(date)), [date, hash(options)]);
return render((t) => store.cache.get(Intl.DateTimeFormat, t.locale, options).format(toDate(date)), [date, hash(options)]);
},

displayNames(code, options = displayNamesOptions) {
// TODO remove cast when DisplayNames is included in standard lib
return render((locale) => store.cache.get((Intl as any).DisplayNames, locale, options).of(code), [code, hash(options)]);
return render((t) => store.cache.get((Intl as any).DisplayNames, t.locale, options).of(code), [code, hash(options)]);
},

listFormat(list, options = listFormatOptions) {
// TODO remove cast when DisplayNames is included in standard lib
return render(
(locale) => store.cache.get((Intl as any).ListFormat, locale, options).format(list),
(t) => store.cache.get((Intl as any).ListFormat, t.locale, options).format(list),
[list && hash([...list]), hash(options)],
);
},

numberFormat(number, options = numberFormatOptions) {
return render((locale) => store.cache.get(Intl.NumberFormat, locale, options).format(number), [number, hash(options)]);
return render((t) => store.cache.get(Intl.NumberFormat, t.locale, options).format(number), [number, hash(options)]);
},

pluralRules(number, options = pluralRulesOptions) {
return render((locale) => store.cache.get(Intl.PluralRules, locale, options).select(number), [number, hash(options)]);
return render((t) => store.cache.get(Intl.PluralRules, t.locale, options).select(number), [number, hash(options)]);
},

relativeTimeFormat(value, unit, options = relativeTimeFormatOptions) {
return render(
(locale) => store.cache.get(Intl.RelativeTimeFormat, locale, options).format(value, unit),
[value, unit, hash(options)],
);
return render((t) => store.cache.get(Intl.RelativeTimeFormat, t.locale, options).format(value, unit), [value, unit, hash(options)]);
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/react/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ export interface InlineTranslator<D extends FlatDict> extends HookTranslator<D,
* @param renderFn your custom render function
* @param dependencies if provided, will memoize the result of renderFn as long as dependencies stay the same (shallow compare)
*/
render(renderFn: (locale: string) => ReactNode, dependencies?: any[]): ReactNode;
render(renderFn: (t: HookTranslator<D>) => ReactNode, dependencies?: any[]): ReactNode;
}
11 changes: 9 additions & 2 deletions src/react/useStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { FlatDict, MaybePromise } from '..';
import { hash } from '../cache';
import { arrEquals } from '../helpers';
import { Store } from '../store';

export function useStore(store: Store, ...locales: string[]): MaybePromise<FlatDict>[] {
Expand All @@ -12,5 +13,11 @@ export function useStore(store: Store, ...locales: string[]): MaybePromise<FlatD
});
}, [store, hash(locales)]);

return store.getAll(...locales);
const dicts = store.getAll(...locales);
const ref = useRef(dicts);
if (ref.current !== dicts && !arrEquals(ref.current, dicts)) {
ref.current = dicts;
}

return ref.current;
}
4 changes: 2 additions & 2 deletions test/react.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ test('render', async (t) => {
return (
<div onClick={() => setI((i) => i + 1)}>
<App id={t.title} locales={['en', 'de', 'de']}>
{t.context.t.render((locale) => {
{t.context.t.render((t) => {
renderCount++;
return new Intl.DateTimeFormat(locale, { dateStyle: 'full' }).format(date);
return new Intl.DateTimeFormat(t.locale, { dateStyle: 'full' }).format(date);
}, [])}
</App>
,
Expand Down

0 comments on commit 5515664

Please sign in to comment.