mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-05-23 07:32:28 -04:00
Handle fallback when creating new translations
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { t } from "elysia";
|
||||
import { ExternalId, Genre, Image, Language, SeedImage } from "./utils";
|
||||
import { ExternalId, Genre, Image, Language, SeedImage, TranslationRecord } from "./utils";
|
||||
import { bubble, registerExamples } from "./examples";
|
||||
import { bubbleImages } from "./examples/bubble";
|
||||
|
||||
@@ -50,8 +50,7 @@ export type Movie = typeof Movie.static;
|
||||
export const SeedMovie = t.Intersect([
|
||||
t.Omit(BaseMovie, ["id", "createdAt", "nextRefresh"]),
|
||||
t.Object({
|
||||
translations: t.Record(
|
||||
Language(),
|
||||
translations: TranslationRecord(
|
||||
t.Intersect([
|
||||
t.Omit(MovieTranslation, ["poster", "thumbnail", "banner", "logo"]),
|
||||
t.Object({
|
||||
@@ -61,9 +60,6 @@ export const SeedMovie = t.Intersect([
|
||||
logo: t.Nullable(SeedImage),
|
||||
}),
|
||||
]),
|
||||
{
|
||||
minProperties: 1,
|
||||
},
|
||||
),
|
||||
videos: t.Optional(t.Array(t.String({ format: "uuid" }))),
|
||||
}),
|
||||
|
||||
@@ -1,53 +1,69 @@
|
||||
import { FormatRegistry } from "@sinclair/typebox";
|
||||
import {
|
||||
FormatRegistry,
|
||||
StaticDecode,
|
||||
TSchema,
|
||||
TString,
|
||||
} from "@sinclair/typebox";
|
||||
import { t } from "elysia";
|
||||
import { comment } from "../../utils";
|
||||
import type { KError } from "../error";
|
||||
import { KErrorT } from "../error";
|
||||
|
||||
export const validateTranslations = <T extends object>(
|
||||
translations: Record<string, T>,
|
||||
): KError | null => {
|
||||
for (const lang of Object.keys(translations)) {
|
||||
try {
|
||||
const valid = new Intl.Locale(lang).baseName;
|
||||
if (lang !== valid) {
|
||||
translations[valid] = translations[lang];
|
||||
delete translations[lang];
|
||||
// this is just for the doc
|
||||
FormatRegistry.Set("language", () => true);
|
||||
|
||||
export const Language = (props?: NonNullable<Parameters<typeof t.String>[0]>) =>
|
||||
t
|
||||
.Transform(
|
||||
t.String({
|
||||
format: "language",
|
||||
description: comment`
|
||||
${props?.description ?? ""}
|
||||
This is a BCP 47 language code (the IETF Best Current Practices on Tags for Identifying Languages).
|
||||
BCP 47 is also known as RFC 5646. It subsumes ISO 639 and is backward compatible with it.
|
||||
`,
|
||||
error: "Expected a valid (and NORMALIZED) bcp-47 language code.",
|
||||
...props,
|
||||
}),
|
||||
)
|
||||
.Decode((lang) => {
|
||||
try {
|
||||
return new Intl.Locale(lang).baseName;
|
||||
} catch {
|
||||
throw new KErrorT(`Invalid language name: '${lang}'`);
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
status: 400,
|
||||
message: `Invalid translation name: '${lang}'.`,
|
||||
details: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
})
|
||||
.Encode((x) => x);
|
||||
|
||||
FormatRegistry.Set("language", (lang) => {
|
||||
try {
|
||||
const normalized = new Intl.Locale(lang).baseName;
|
||||
// TODO: we should actually replace the locale with normalized if we managed to parse it but transforms aren't working
|
||||
return lang === normalized;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
export const TranslationRecord = <T extends TSchema>(
|
||||
values: Parameters<typeof t.Record<TString, T>>[1],
|
||||
props?: Parameters<typeof t.Record<TString, T>>[2],
|
||||
) =>
|
||||
t
|
||||
.Transform(t.Record(t.String(), values, { minPropreties: 1, ...props }))
|
||||
// @ts-expect-error idk why the translations type can't get resolved so it's a pain to work
|
||||
// with without casting it
|
||||
.Decode((translations: Record<string, StaticDecode<T>>) => {
|
||||
for (const lang of Object.keys(translations)) {
|
||||
try {
|
||||
const locale = new Intl.Locale(lang);
|
||||
|
||||
type StringProps = NonNullable<Parameters<typeof t.String>[0]>;
|
||||
|
||||
// TODO: format validation doesn't work in record's key. We should have a proper way to check that.
|
||||
export const Language = (props?: StringProps) =>
|
||||
t.String({
|
||||
format: "language",
|
||||
description: comment`
|
||||
${props?.description ?? ""}
|
||||
This is a BCP 47 language code (the IETF Best Current Practices on Tags for Identifying Languages).
|
||||
BCP 47 is also known as RFC 5646. It subsumes ISO 639 and is backward compatible with it.
|
||||
`,
|
||||
error: "Expected a valid (and NORMALIZED) bcp-47 language code.",
|
||||
...props,
|
||||
});
|
||||
// fallback (ex add `en` if we only have `en-us`)
|
||||
if (!(locale.language in translations))
|
||||
translations[locale.language] = translations[lang];
|
||||
// normalize locale names (caps, old values etc)
|
||||
// we need to do this here because the record's key (Language)'s transform is not runned.
|
||||
// this is a limitation of typebox
|
||||
if (lang !== locale.baseName) {
|
||||
translations[locale.baseName] = translations[lang];
|
||||
delete translations[lang];
|
||||
}
|
||||
} catch (e) {
|
||||
throw new KErrorT(`Invalid translation name: '${lang}'.`);
|
||||
}
|
||||
}
|
||||
return translations;
|
||||
})
|
||||
.Encode((x) => x);
|
||||
|
||||
export const processLanguages = (languages?: string) => {
|
||||
if (!languages) return ["*"];
|
||||
|
||||
Reference in New Issue
Block a user