feat: fetch urls for language switcher from contentstack
This commit is contained in:
@@ -62,3 +62,39 @@ query GetAccountPageRefs($locale: String!, $uid: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query GetDaDeEnUrls($uid: String!) {
|
||||||
|
de: all_account_page(where: { uid: $uid }, locale: "de") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
en: all_account_page(where: { uid: $uid }, locale: "en") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
da: all_account_page(where: { uid: $uid }, locale: "da") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetFiNoSvUrls($uid: String!) {
|
||||||
|
fi: all_account_page(where: { uid: $uid }, locale: "fi") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
no: all_account_page(where: { uid: $uid }, locale: "no") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sv: all_account_page(where: { uid: $uid }, locale: "sv") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ query GetDaDeEnUrls($uid: String!) {
|
|||||||
en: current_blocks_page(uid: $uid, locale: "en") {
|
en: current_blocks_page(uid: $uid, locale: "en") {
|
||||||
url: original_url
|
url: original_url
|
||||||
}
|
}
|
||||||
|
da: current_blocks_page(uid: $uid, locale: "da") {
|
||||||
|
url: original_url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query GetFiNoSvUrls($uid: String!) {
|
query GetFiNoSvUrls($uid: String!) {
|
||||||
@@ -18,4 +21,3 @@ query GetFiNoSvUrls($uid: String!) {
|
|||||||
url: original_url
|
url: original_url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,3 +245,39 @@ query GetLoyaltyPageRefs($locale: String!, $uid: String!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query GetDaDeEnUrls($uid: String!) {
|
||||||
|
de: all_loyalty_page(where: { uid: $uid }, locale: "de") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
en: all_loyalty_page(where: { uid: $uid }, locale: "en") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
da: all_loyalty_page(where: { uid: $uid }, locale: "da") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetFiNoSvUrls($uid: String!) {
|
||||||
|
sv: all_loyalty_page(where: { uid: $uid }, locale: "sv") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
no: all_loyalty_page(where: { uid: $uid }, locale: "no") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fi: all_loyalty_page(where: { uid: $uid }, locale: "fi") {
|
||||||
|
items {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,12 +54,18 @@ export const middleware: NextMiddleware = async (request) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCurrent) {
|
if (isCurrent) {
|
||||||
|
searchParams.set("uid", uid)
|
||||||
searchParams.set("uri", pathNameWithoutLang)
|
searchParams.set("uri", pathNameWithoutLang)
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
new URL(
|
new URL(
|
||||||
`/${lang}/current-content-page?${searchParams.toString()}`,
|
`/${lang}/current-content-page?${searchParams.toString()}`,
|
||||||
nextUrl
|
nextUrl
|
||||||
)
|
),
|
||||||
|
{
|
||||||
|
request: {
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,3 +188,12 @@ export const validateAccountPageRefsSchema = z.object({
|
|||||||
export type AccountPageRefsDataRaw = z.infer<
|
export type AccountPageRefsDataRaw = z.infer<
|
||||||
typeof validateAccountPageRefsSchema
|
typeof validateAccountPageRefsSchema
|
||||||
>
|
>
|
||||||
|
|
||||||
|
export const validateLanguageSwitcherData = z.object({
|
||||||
|
en: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
da: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
de: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
fi: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
sv: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
no: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
GetAccountPage,
|
GetAccountPage,
|
||||||
GetAccountPageRefs,
|
GetAccountPageRefs,
|
||||||
|
GetDaDeEnUrls,
|
||||||
|
GetFiNoSvUrls,
|
||||||
} from "@/lib/graphql/Query/AccountPage.graphql"
|
} from "@/lib/graphql/Query/AccountPage.graphql"
|
||||||
import { request } from "@/lib/graphql/request"
|
import { request } from "@/lib/graphql/request"
|
||||||
import { internalServerError, notFound } from "@/server/errors/trpc"
|
import { internalServerError, notFound } from "@/server/errors/trpc"
|
||||||
@@ -18,11 +22,16 @@ import {
|
|||||||
AccountPageRefsDataRaw,
|
AccountPageRefsDataRaw,
|
||||||
validateAccountPageRefsSchema,
|
validateAccountPageRefsSchema,
|
||||||
validateAccountPageSchema,
|
validateAccountPageSchema,
|
||||||
|
validateLanguageSwitcherData,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import { getConnections } from "./utils"
|
import { getConnections } from "./utils"
|
||||||
|
|
||||||
import { ContentEntries } from "@/types/components/myPages/myPage/enums"
|
import { ContentEntries } from "@/types/components/myPages/myPage/enums"
|
||||||
import { Embeds } from "@/types/requests/embeds"
|
import { Embeds } from "@/types/requests/embeds"
|
||||||
|
import {
|
||||||
|
LanguageSwitcherData,
|
||||||
|
LanguageSwitcherQueryDataRaw,
|
||||||
|
} from "@/types/requests/languageSwitcher"
|
||||||
import { Edges } from "@/types/requests/utils/edges"
|
import { Edges } from "@/types/requests/utils/edges"
|
||||||
import { RTEDocument } from "@/types/rte/node"
|
import { RTEDocument } from "@/types/rte/node"
|
||||||
|
|
||||||
@@ -131,4 +140,37 @@ export const accountPageQueryRouter = router({
|
|||||||
|
|
||||||
return accountPage
|
return accountPage
|
||||||
}),
|
}),
|
||||||
|
languageSwitcher: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
|
const variables = {
|
||||||
|
uid: ctx.uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await batchRequest<LanguageSwitcherQueryDataRaw>([
|
||||||
|
{
|
||||||
|
document: GetDaDeEnUrls,
|
||||||
|
variables,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
document: GetFiNoSvUrls,
|
||||||
|
variables,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const validatedLanguageSwitcherData =
|
||||||
|
validateLanguageSwitcherData.safeParse(res.data)
|
||||||
|
|
||||||
|
if (!validatedLanguageSwitcherData.success) {
|
||||||
|
throw internalServerError(validatedLanguageSwitcherData.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const urls = Object.keys(
|
||||||
|
validatedLanguageSwitcherData.data
|
||||||
|
).reduce<LanguageSwitcherData>((acc, key) => {
|
||||||
|
const items = validatedLanguageSwitcherData.data[key as Lang].items
|
||||||
|
const url = items.length ? items[0]?.url : undefined
|
||||||
|
return { ...acc, [key]: { url } }
|
||||||
|
}, {} as LanguageSwitcherData)
|
||||||
|
|
||||||
|
return { lang: ctx.lang, urls }
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const validateHeaderConfigSchema = z.object({
|
|||||||
edges: z.array(
|
edges: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
node: z.object({
|
node: z.object({
|
||||||
description: z.string().optional(),
|
description: z.string().optional().nullable(),
|
||||||
dimension: z.object({
|
dimension: z.object({
|
||||||
height: z.number(),
|
height: z.number(),
|
||||||
width: z.number(),
|
width: z.number(),
|
||||||
@@ -79,8 +79,8 @@ export const validateHeaderConfigSchema = z.object({
|
|||||||
system: z.object({
|
system: z.object({
|
||||||
uid: z.string(),
|
uid: z.string(),
|
||||||
}),
|
}),
|
||||||
title: z.string(),
|
title: z.string().nullable(),
|
||||||
url: z.string(),
|
url: z.string().nullable(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export const configQueryRouter = router({
|
|||||||
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
return validatedContactConfigConfig.data.all_contact_config.items[0]
|
||||||
}),
|
}),
|
||||||
header: publicProcedure.query(async ({ ctx }) => {
|
header: publicProcedure.query(async ({ ctx }) => {
|
||||||
console.log({ ctx })
|
|
||||||
const response = await request<HeaderDataRaw>(
|
const response = await request<HeaderDataRaw>(
|
||||||
GetCurrentHeader,
|
GetCurrentHeader,
|
||||||
{ locale: ctx.lang },
|
{ locale: ctx.lang },
|
||||||
|
|||||||
@@ -306,3 +306,12 @@ export const validateLoyaltyPageRefsSchema = z.object({
|
|||||||
export type LoyaltyPageRefsDataRaw = z.infer<
|
export type LoyaltyPageRefsDataRaw = z.infer<
|
||||||
typeof validateLoyaltyPageRefsSchema
|
typeof validateLoyaltyPageRefsSchema
|
||||||
>
|
>
|
||||||
|
|
||||||
|
export const validateLanguageSwitcherData = z.object({
|
||||||
|
en: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
da: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
de: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
fi: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
sv: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
no: z.object({ items: z.array(z.object({ url: z.string() }).nullable()) }),
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { batchRequest } from "@/lib/graphql/batchRequest"
|
||||||
import {
|
import {
|
||||||
|
GetDaDeEnUrls,
|
||||||
|
GetFiNoSvUrls,
|
||||||
GetLoyaltyPage,
|
GetLoyaltyPage,
|
||||||
GetLoyaltyPageRefs,
|
GetLoyaltyPageRefs,
|
||||||
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
} from "@/lib/graphql/Query/LoyaltyPage.graphql"
|
||||||
@@ -17,12 +21,17 @@ import { removeEmptyObjects } from "../../utils"
|
|||||||
import {
|
import {
|
||||||
LoyaltyPage,
|
LoyaltyPage,
|
||||||
type LoyaltyPageRefsDataRaw,
|
type LoyaltyPageRefsDataRaw,
|
||||||
|
validateLanguageSwitcherData,
|
||||||
validateLoyaltyPageRefsSchema,
|
validateLoyaltyPageRefsSchema,
|
||||||
validateLoyaltyPageSchema,
|
validateLoyaltyPageSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import { getConnections } from "./utils"
|
import { getConnections } from "./utils"
|
||||||
|
|
||||||
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
|
||||||
|
import {
|
||||||
|
LanguageSwitcherData,
|
||||||
|
LanguageSwitcherQueryDataRaw,
|
||||||
|
} from "@/types/requests/languageSwitcher"
|
||||||
|
|
||||||
function makeButtonObject(button: any) {
|
function makeButtonObject(button: any) {
|
||||||
return {
|
return {
|
||||||
@@ -172,4 +181,37 @@ export const loyaltyPageQueryRouter = router({
|
|||||||
// Assert LoyaltyPage type to get correct typings for RTE fields
|
// Assert LoyaltyPage type to get correct typings for RTE fields
|
||||||
return validatedLoyaltyPage.data as LoyaltyPage
|
return validatedLoyaltyPage.data as LoyaltyPage
|
||||||
}),
|
}),
|
||||||
|
languageSwitcher: contentstackProcedure.query(async ({ ctx }) => {
|
||||||
|
const variables = {
|
||||||
|
uid: ctx.uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await batchRequest<LanguageSwitcherQueryDataRaw>([
|
||||||
|
{
|
||||||
|
document: GetDaDeEnUrls,
|
||||||
|
variables,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
document: GetFiNoSvUrls,
|
||||||
|
variables,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const validatedLanguageSwitcherData =
|
||||||
|
validateLanguageSwitcherData.safeParse(res.data)
|
||||||
|
|
||||||
|
if (!validatedLanguageSwitcherData.success) {
|
||||||
|
throw internalServerError(validatedLanguageSwitcherData.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const urls = Object.keys(
|
||||||
|
validatedLanguageSwitcherData.data
|
||||||
|
).reduce<LanguageSwitcherData>((acc, key) => {
|
||||||
|
const items = validatedLanguageSwitcherData.data[key as Lang].items
|
||||||
|
const url = items.length ? items[0]?.url : undefined
|
||||||
|
return { ...acc, [key]: { url } }
|
||||||
|
}, {} as LanguageSwitcherData)
|
||||||
|
|
||||||
|
return { lang: ctx.lang, urls }
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/new-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 2,
|
tier: 2,
|
||||||
name: "New Friend",
|
name: "Good Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -21,11 +21,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/good-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 3,
|
tier: 3,
|
||||||
name: "New Friend",
|
name: "Close Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -33,11 +33,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/close-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 4,
|
tier: 4,
|
||||||
name: "New Friend",
|
name: "Dear Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -45,11 +45,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/dear-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 5,
|
tier: 5,
|
||||||
name: "New Friend",
|
name: "Loyal Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -57,11 +57,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/loyal-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 6,
|
tier: 6,
|
||||||
name: "New Friend",
|
name: "True Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -69,11 +69,11 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/true-friend.svg",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tier: 7,
|
tier: 7,
|
||||||
name: "New Friend",
|
name: "Best Friend",
|
||||||
requiredPoints: 50000,
|
requiredPoints: 50000,
|
||||||
requiredNights: "X",
|
requiredNights: "X",
|
||||||
benefits: [
|
benefits: [
|
||||||
@@ -81,6 +81,6 @@ export const allLevels = [
|
|||||||
"Always best price",
|
"Always best price",
|
||||||
"Book reward nights with points",
|
"Book reward nights with points",
|
||||||
],
|
],
|
||||||
logo: "/_static/icons/new-friend.png",
|
logo: "/_static/icons/loyaltyLevels/best-friend.svg",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ export type MainMenuProps = {
|
|||||||
links: HeaderLink[]
|
links: HeaderLink[]
|
||||||
logo: Image
|
logo: Image
|
||||||
topMenuMobileLinks: TopMenuHeaderLink[]
|
topMenuMobileLinks: TopMenuHeaderLink[]
|
||||||
|
languageSwitcher: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ export type TopMenuProps = {
|
|||||||
frontpageLinkText: string
|
frontpageLinkText: string
|
||||||
homeHref: string
|
homeHref: string
|
||||||
links: TopMenuHeaderLink[]
|
links: TopMenuHeaderLink[]
|
||||||
|
languageSwitcher: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import type { LanguageSwitcherQueryData } from "@/types/requests/languageSwitcher"
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
|
||||||
|
|
||||||
export type LanguageSwitcherLink = {
|
export type LanguageSwitcherLink = {
|
||||||
href: string
|
href: string
|
||||||
@@ -6,6 +8,6 @@ export type LanguageSwitcherLink = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type LanguageSwitcherProps = {
|
export type LanguageSwitcherProps = {
|
||||||
currentLanguage: string
|
currentLanguage: Lang
|
||||||
urls: LanguageSwitcherQueryData
|
urls: LanguageSwitcherData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
type LanguageResult = {
|
type CurrentLanguageResult = {
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LanguageSwitcherQueryData = {
|
export type LanguageSwitcherData = {
|
||||||
|
da: CurrentLanguageResult | undefined
|
||||||
|
de: CurrentLanguageResult | undefined
|
||||||
|
en: CurrentLanguageResult | undefined
|
||||||
|
fi: CurrentLanguageResult | undefined
|
||||||
|
no: CurrentLanguageResult | undefined
|
||||||
|
sv: CurrentLanguageResult | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
type LanguageResult = {
|
||||||
|
items: { url: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LanguageSwitcherQueryDataRaw = {
|
||||||
|
da: LanguageResult | undefined
|
||||||
de: LanguageResult | undefined
|
de: LanguageResult | undefined
|
||||||
en: LanguageResult | undefined
|
en: LanguageResult | undefined
|
||||||
fi: LanguageResult | undefined
|
fi: LanguageResult | undefined
|
||||||
|
|||||||
Reference in New Issue
Block a user