177 lines
5.0 KiB
TypeScript
177 lines
5.0 KiB
TypeScript
import { ApolloServer, gql, IResolvers, ApolloError, UserInputError } from "apollo-server-micro";
|
|
import { NextApiHandler } from "next";
|
|
import NextCors from "nextjs-cors";
|
|
import {
|
|
getTranslationInfo,
|
|
getTranslationText,
|
|
getAudio,
|
|
replaceExceptedCode,
|
|
isValidCode,
|
|
LanguageType,
|
|
languageList,
|
|
LangCode
|
|
} from "lingva-scraper";
|
|
|
|
export const typeDefs = gql`
|
|
enum LangType {
|
|
SOURCE,
|
|
TARGET
|
|
}
|
|
type Query {
|
|
translation(source: String="auto" target: String="en" query: String!): Translation!
|
|
audio(lang: String! query: String!): AudioEntry!
|
|
languages(type: LangType): [Language]!
|
|
}
|
|
type Translation {
|
|
source: SourceEntry!
|
|
target: TargetEntry!
|
|
}
|
|
type SourceEntry {
|
|
lang: Language!
|
|
text: String!
|
|
audio: [Int]!
|
|
detected: Language
|
|
typo: String
|
|
pronunciation: String
|
|
definitions: [DefinitionsGroup]
|
|
examples: [String]
|
|
similar: [String]
|
|
}
|
|
type TargetEntry {
|
|
lang: Language!
|
|
text: String!
|
|
audio: [Int]!
|
|
pronunciation: String
|
|
extraTranslations: [ExtraTranslationsGroup]
|
|
}
|
|
type AudioEntry {
|
|
lang: Language!
|
|
text: String!
|
|
audio: [Int]!
|
|
}
|
|
type Language {
|
|
code: String!
|
|
name: String!
|
|
}
|
|
type DefinitionsGroup {
|
|
type: String!
|
|
list: [DefinitionList]!
|
|
}
|
|
type DefinitionList {
|
|
definition: String!
|
|
example: String!
|
|
field: String
|
|
synonyms: [String]
|
|
}
|
|
type ExtraTranslationsGroup {
|
|
type: String!
|
|
list: [ExtraTranslationList]!
|
|
}
|
|
type ExtraTranslationList {
|
|
word: String!
|
|
article: String
|
|
frequency: Int!
|
|
meanings: [String]
|
|
}
|
|
`;
|
|
|
|
export const resolvers: IResolvers = {
|
|
Query: {
|
|
async translation(_, args) {
|
|
const { source, target, query } = args;
|
|
|
|
if (!isValidCode(source, LanguageType.SOURCE) || !isValidCode(target, LanguageType.TARGET))
|
|
throw new UserInputError("Invalid language code");
|
|
|
|
const translation = await getTranslationText(source, target, query);
|
|
if (!translation)
|
|
throw new ApolloError("An error occurred while retrieving the translation");
|
|
|
|
const info = await getTranslationInfo(source, target, query);
|
|
return {
|
|
source: {
|
|
lang: {
|
|
code: source
|
|
},
|
|
text: query,
|
|
detected: info?.detectedSource && {
|
|
code: info.detectedSource
|
|
},
|
|
typo: info?.typo,
|
|
pronunciation: info?.pronunciation.query,
|
|
definitions: info?.definitions,
|
|
examples: info?.examples,
|
|
similar: info?.similar
|
|
},
|
|
target: {
|
|
lang: {
|
|
code: target
|
|
},
|
|
text: translation,
|
|
pronunciation: info?.pronunciation.translation,
|
|
extraTranslations: info?.extraTranslations
|
|
}
|
|
};
|
|
},
|
|
audio(_, args) {
|
|
const { lang, query } = args;
|
|
|
|
if (!isValidCode(lang))
|
|
throw new UserInputError("Invalid language code");
|
|
|
|
return {
|
|
lang: {
|
|
code: lang
|
|
},
|
|
text: query
|
|
};
|
|
},
|
|
languages(_, args) {
|
|
const { type } = args;
|
|
const lowerType = type?.toLocaleLowerCase() as typeof LanguageType[keyof typeof LanguageType] | undefined;
|
|
const langEntries = Object.entries(languageList[lowerType ?? "all"]);
|
|
return langEntries.map(([code, name]) => ({ code, name }));
|
|
}
|
|
},
|
|
...(["SourceEntry", "TargetEntry", "AudioEntry"].reduce((acc, key) => ({
|
|
...acc,
|
|
[key]: {
|
|
async audio(parent) {
|
|
const { lang, text } = parent;
|
|
const parsedLang = replaceExceptedCode(LanguageType.TARGET, lang.code);
|
|
const audio = await getAudio(parsedLang, text);
|
|
if (!audio)
|
|
throw new ApolloError("An error occurred while retrieving the audio");
|
|
return audio;
|
|
}
|
|
}
|
|
}), {} as IResolvers)),
|
|
Language: {
|
|
name(parent) {
|
|
const { code, name } = parent;
|
|
return name || languageList.all[code as LangCode];
|
|
}
|
|
}
|
|
};
|
|
|
|
export const config = {
|
|
api: {
|
|
bodyParser: false
|
|
}
|
|
};
|
|
|
|
const apolloHandler = new ApolloServer({ typeDefs, resolvers }).createHandler({ path: "/api/graphql" });
|
|
|
|
const handler: NextApiHandler = async (req, res) => {
|
|
await NextCors(req, res, {
|
|
methods: ["GET", "POST"],
|
|
origin: "*"
|
|
});
|
|
|
|
if (req.method !== "OPTIONS")
|
|
return apolloHandler(req, res);
|
|
res.end();
|
|
};
|
|
|
|
export default handler;
|