2023-02-22 06:23:18 +01:00

103 lines
3.4 KiB
TypeScript

import { IntlMessageFormat } from "intl-messageformat";
import { useCallback } from "react";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { LibraryItemMetadataDynamicZone } from "graphql/generated";
import { ICUParams } from "graphql/icuParams";
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { getLogger } from "helpers/logger";
const logger = getLogger("🗺️ [I18n]");
type WordingKey = keyof ICUParams;
type LibraryItemType = Exclude<LibraryItemMetadataDynamicZone["__typename"], undefined>;
export type ContentStatus = "Done" | "Draft" | "Incomplete" | "Review";
const componentMetadataToWording: Record<LibraryItemType, WordingKey> = {
ComponentMetadataAudio: "audio",
ComponentMetadataBooks: "textual",
ComponentMetadataGame: "game",
ComponentMetadataGroup: "group",
ComponentMetadataVideo: "video",
ComponentMetadataOther: "other",
Error: "item",
};
const componentSetsTextsetStatusToWording: Record<
ContentStatus,
{ label: WordingKey; description: WordingKey }
> = {
Draft: { label: "draft", description: "status_draft" },
Incomplete: { label: "incomplete", description: "status_incomplete" },
Review: { label: "review", description: "status_review" },
Done: { label: "done", description: "status_done" },
};
export const useFormat = (): {
format: <K extends WordingKey>(
key: K,
...values: ICUParams[K] extends never ? [undefined?] : [ICUParams[K]]
) => string;
formatLibraryItemType: (metadata: { __typename: LibraryItemType }) => string;
formatStatusLabel: (status: ContentStatus) => string;
formatStatusDescription: (status: ContentStatus) => string;
} => {
const langui = useAtomGetter(atoms.localData.langui);
const fallbackLangui = useAtomGetter(atoms.localData.fallbackLangui);
const format = useCallback(
(
key: WordingKey,
values?: Record<string, Date | boolean | number | string | null | undefined>
): string => {
const processedValues = Object.fromEntries(
Object.entries(values ?? {}).map(([oKey, value]) => [
oKey,
isDefined(value) ? value : "undefined",
])
);
const result = new IntlMessageFormat(langui[key] ?? "").format(processedValues).toString();
if (isDefinedAndNotEmpty(result)) {
return result;
}
logger.warn(
`Missing ${langui.ui_language?.data?.attributes?.code} translation for ${key}. \
Falling back to en translation.`
);
const fallback = new IntlMessageFormat(fallbackLangui[key] ?? "")
.format(processedValues)
.toString();
if (isDefinedAndNotEmpty(fallback)) {
return fallback;
}
logger.warn(`Missing fallback translation for ${key}. The key seems unvalid`);
return key;
},
[langui, fallbackLangui]
);
const formatLibraryItemType = useCallback(
(metadata: { __typename: LibraryItemType }): string =>
format(componentMetadataToWording[metadata.__typename]),
[format]
);
const formatStatusLabel = useCallback(
(status: ContentStatus): string => format(componentSetsTextsetStatusToWording[status].label),
[format]
);
const formatStatusDescription = useCallback(
(status: ContentStatus): string =>
format(componentSetsTextsetStatusToWording[status].description),
[format]
);
return {
format,
formatLibraryItemType,
formatStatusLabel,
formatStatusDescription,
};
};