Skip to content

Commit

Permalink
feat(t-react): add locale and translations getter functions
Browse files Browse the repository at this point in the history
  • Loading branch information
macarie committed Dec 27, 2023
1 parent a590f44 commit f06787c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 39 deletions.
83 changes: 57 additions & 26 deletions packages/t-react/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,57 @@ import delve from "dlv";
import { atom, getDefaultStore, useAtomValue } from "jotai";
import { useMemo } from "react";

import type { CreateTranslationsFactoryOptions } from "@wluwd/t";

const $locale = atom<string | undefined>(undefined);
const defaultStore = getDefaultStore();

const loadAndCacheTranslations = async (
locale: string | undefined,
{
cache,
loaders,
}: CreateTranslationsFactoryOptions<
boolean,
string,
string,
string,
string
>["resources"],
) => {
if (locale === undefined) {
throw new NoLocaleSet();
}

let translations = cache.get(locale);

if (translations !== undefined) {
return translations;
}

const loader = loaders.get(locale);

if (loader === undefined) {
throw new NoTranslationsSet({
availableLocales: Array.from(loaders.keys()),
desiredLocale: locale,
});
}

translations = await loader();

cache.set(locale, translations);

return translations;
};

export const createTranslations = createTranslationsFactory({
hasSignalLikeInterface: false,
locale: {
fn: {
factory: () => () => defaultStore.get($locale)!,
name: "getLocale",
},
hook: {
factory: () => () => useAtomValue($locale)!,
name: "useLocale",
Expand All @@ -23,35 +68,21 @@ export const createTranslations = createTranslationsFactory({
loaders: new Map(),
},
translations: {
fn: {
factory: (resources) => async (prefix) => {
const locale = defaultStore.get($locale);

// eslint-disable-next-line ts/no-unsafe-return
return delve(await loadAndCacheTranslations(locale, resources), prefix);
},
name: "getTranslations",
},
hook: {
factory: ({ cache, loaders }) => {
const $translations = atom(async (get) => {
factory: (resources) => {
const $translations = atom((get) => {
const locale = get($locale);

if (locale === undefined) {
throw new NoLocaleSet();
}

let translations = cache.get(locale);

if (translations !== undefined) {
return translations;
}

const loader = loaders.get(locale);

if (loader === undefined) {
throw new NoTranslationsSet({
availableLocales: Array.from(loaders.keys()),
desiredLocale: locale,
});
}

translations = await loader();

cache.set(locale, translations);

return translations;
return loadAndCacheTranslations(locale, resources);
});

return (prefix) => {
Expand Down
45 changes: 32 additions & 13 deletions packages/t-react/tests/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { describe, expect, it } from "vitest";

import type { ReactNode } from "react";

const nextTick = () => Promise.resolve();

const enGB = { default: { some: { deep: { string: "en-GB" } } } } as const;
const enUS = { default: { some: { deep: { string: "en-US" } } } } as const;

Expand Down Expand Up @@ -45,16 +43,14 @@ describe("throws", () => {
expect(locale.current).toBe(undefined);

const error = { current: undefined };
const { rerender } = renderHook(() => useTranslations("some.deep"), {

renderHook(() => useTranslations("some.deep"), {
wrapper: ({ children }) => (
<ErrorBoundary error={error}>{children}</ErrorBoundary>
),
});

await nextTick();
rerender();

expect(error.current).toBeInstanceOf(NoLocaleSet);
await waitFor(() => expect(error.current).toBeInstanceOf(NoLocaleSet));
});

it("`NoTranslationsSet` when trying to load translations that have no loader", async () => {
Expand All @@ -67,7 +63,8 @@ describe("throws", () => {
expect(locale.current).toBe(undefined);

const error = { current: undefined };
const { rerender } = renderHook(() => useTranslations("some.deep"), {

renderHook(() => useTranslations("some.deep"), {
wrapper: ({ children }) => (
<ErrorBoundary error={error}>{children}</ErrorBoundary>
),
Expand All @@ -76,14 +73,13 @@ describe("throws", () => {
// @ts-expect-error testing undefined translations
setLocale("en-AU");

await nextTick();
rerender();

expect(error.current).toBeInstanceOf(NoTranslationsSet);
await waitFor(() =>
expect(error.current).toBeInstanceOf(NoTranslationsSet),
);
});
});

it("creates a working `createTranslations`", async () => {
it("creates working hooks", async () => {
const { setLocale, t, useLocale, useTranslations } = createTranslations(
translations,
{ localeFrom: ["en-GB"], translator },
Expand Down Expand Up @@ -121,3 +117,26 @@ it("creates a working `createTranslations`", async () => {
expect(t(translation.current.string)).toBe(enGB.default.some.deep.string),
);
});

it("creates working functions", async () => {
const { getLocale, getTranslations, setLocale } = createTranslations(
translations,
{
localeFrom: ["en-US"],
translator,
},
);

expect(getLocale()).toBe("en-US");
expect(await getTranslations("some")).toBe(enUS.default.some);

setLocale("en-GB");

expect(getLocale()).toBe("en-GB");
expect(await getTranslations("some")).toBe(enGB.default.some);

setLocale("en-US");

expect(getLocale()).toBe("en-US");
expect(await getTranslations("some")).toBe(enUS.default.some);
});

0 comments on commit f06787c

Please sign in to comment.