Feat: Added hreflang link for better SEO. (#1167)
* Update Language type to Readonly * Add withHrefLang utility function for generating hreflang metadata * feat: Support for specified language through locale search parameter. (...?locale=zh) * feat: Added hreflang link for better search engine optimization.
This commit is contained in:
parent
6e327f04fd
commit
55bb1229aa
|
|
@ -1,11 +1,10 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Comments Activities - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function FollowingActivities() {
|
||||
return <HomeFeed type="comments" />
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Following Activities - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function FollowingActivities() {
|
||||
return <HomeFeed type="following" />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Hottest Activities - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function HottestActivities() {
|
||||
const queryClient = getQueryClient()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Latest Activities - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function LatestActivities() {
|
||||
const queryClient = getQueryClient()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { HomeActivitiesTabs } from "~/components/home/HomeActivitiesTabs"
|
||||
import { HomeSidebar } from "~/components/home/HomeSidebar"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Activities - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function ActivitiesLayout({
|
||||
children,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -8,11 +7,12 @@ import ShortPreviewList from "~/components/site/ShortPreviewList"
|
|||
import { APP_NAME, APP_SLOGAN } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { getPreviewShort, prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `${APP_NAME} - ${APP_SLOGAN}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function HomeActivities() {
|
||||
const queryClient = getQueryClient()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -8,19 +7,12 @@ import { HomeFeed } from "~/components/home/HomeFeed"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export function generateMetadata({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: {
|
||||
[key: string]: string | string[] | undefined
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Search: ${searchParams.q} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
export const generateMetadata = withHrefLang(async ({ searchParams }) => ({
|
||||
title: `Search: ${searchParams?.q} - ${APP_NAME}`,
|
||||
}))
|
||||
|
||||
export default async function Search({
|
||||
searchParams,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `Shorts - ${APP_NAME}`,
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function LatestActivities() {
|
||||
const queryClient = getQueryClient()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -8,19 +7,16 @@ import ParticipateButton from "~/components/home/ParticipateButton"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
export function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
tag: string
|
||||
}
|
||||
}): Metadata {
|
||||
return {
|
||||
title: `Tag: ${params.tag} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
}>(async ({ params }) => ({
|
||||
title: `Tag: ${params.tag} - ${APP_NAME}`,
|
||||
}))
|
||||
|
||||
export default async function Tag({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -8,22 +7,21 @@ import ParticipateButton from "~/components/home/ParticipateButton"
|
|||
import { APP_NAME } from "~/lib/env"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
import topics from "../../../../../../../data/topics.json"
|
||||
|
||||
export function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
topic: string
|
||||
}
|
||||
}): Metadata {
|
||||
}>(async ({ params }) => {
|
||||
params.topic = decodeURIComponent(params.topic)
|
||||
return {
|
||||
title: `Topic: ${params.topic} - ${APP_NAME}`,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function Topic({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { NextIntlClientProvider } from "next-intl"
|
||||
import { getMessages, unstable_setRequestLocale } from "next-intl/server"
|
||||
import { headers } from "next/headers"
|
||||
|
|
@ -29,7 +28,9 @@ import "remark-github-alerts/styles/github-colors-dark-class.css"
|
|||
import "remark-github-alerts/styles/github-base.css"
|
||||
import "~/css/main.css"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
|
||||
export const generateMetadata = withHrefLang(async () => ({
|
||||
title: `${APP_NAME} - ${APP_SLOGAN}`,
|
||||
description: APP_DESCRIPTION,
|
||||
applicationName: APP_NAME,
|
||||
|
|
@ -94,7 +95,7 @@ export const metadata: Metadata = {
|
|||
site: "@_xLog",
|
||||
creator: "@_xLog",
|
||||
},
|
||||
}
|
||||
}))
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
import { notFound } from "next/navigation"
|
||||
import serialize from "serialize-javascript"
|
||||
|
|
@ -18,17 +17,16 @@ import { isInRN } from "~/lib/is-in-rn"
|
|||
import { isOnlyContent } from "~/lib/is-only-content"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { Language } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { fetchGetPage, getSummary } from "~/queries/page.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
slug: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
const locale = (await getLocale()) as Language
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -76,7 +74,7 @@ export async function generateMetadata({
|
|||
creator: twitterCreator ? `@${twitterCreator}` : undefined,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SitePagePage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SiteArchives } from "~/components/site/SiteArchives"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetPagesBySite } from "~/queries/page.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -24,7 +21,7 @@ export async function generateMetadata({
|
|||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SiteArchivesPage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { Metadata } from "next"
|
||||
import { headers } from "next/headers"
|
||||
import { notFound, redirect } from "next/navigation"
|
||||
|
||||
|
|
@ -18,6 +17,7 @@ import { isOnlyContent, searchParser } from "~/lib/is-only-content"
|
|||
import getQueryClient from "~/lib/query-client"
|
||||
import { ExpandedNote } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { fetchGetPage } from "~/queries/page.server"
|
||||
import {
|
||||
fetchGetSite,
|
||||
|
|
@ -26,13 +26,11 @@ import {
|
|||
prefetchGetSiteToSubscriptions,
|
||||
} from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -80,7 +78,7 @@ export async function generateMetadata({
|
|||
creator: twitterCreator,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SiteLayout({
|
||||
children,
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
import { Metadata } from "next"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { fetchGetSite, getNFTs } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -22,7 +20,7 @@ export async function generateMetadata({
|
|||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SiteNFTPage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import SiteHome from "~/components/site/SiteHome"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetPagesBySite } from "~/queries/page.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -24,7 +21,7 @@ export async function generateMetadata({
|
|||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function SitePortfoliosPage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,29 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { SearchInput } from "~/components/common/SearchInput"
|
||||
import { SiteSearch } from "~/components/site/SiteSearch"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
searchParams,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
searchParams: {
|
||||
[key: string]: string | string[] | undefined
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params, searchParams }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
const site = await fetchGetSite(params?.site, queryClient)
|
||||
|
||||
const title = `Search: ${searchParams.q} - ${
|
||||
const title = `Search: ${searchParams?.q} - ${
|
||||
site?.metadata?.content?.name || site?.handle
|
||||
}`
|
||||
|
||||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SiteSearchPage() {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import SiteHome from "~/components/site/SiteHome"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetPagesBySite } from "~/queries/page.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -24,7 +21,7 @@ export async function generateMetadata({
|
|||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function SiteShortsPage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
import { Metadata } from "next"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { SiteArchives } from "~/components/site/SiteArchives"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import { withHrefLang } from "~/lib/with-hreflang"
|
||||
import { prefetchGetPagesBySite } from "~/queries/page.server"
|
||||
import { fetchGetSite } from "~/queries/site.server"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
export const generateMetadata = withHrefLang<{
|
||||
params: {
|
||||
site: string
|
||||
tag: string
|
||||
}
|
||||
}): Promise<Metadata> {
|
||||
}>(async ({ params }) => {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
|
|
@ -28,7 +25,7 @@ export async function generateMetadata({
|
|||
return {
|
||||
title,
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default async function SiteTagPage({
|
||||
params,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { getRequestConfig } from "next-intl/server"
|
||||
|
||||
export const locales = ["en", "ja", "zh", "zh-TW"] as const
|
||||
import { Language } from "./lib/types"
|
||||
|
||||
export const locales: Language[] = ["en", "ja", "zh", "zh-TW"]
|
||||
export const languageNames: Record<(typeof locales)[number], string> = {
|
||||
en: "English",
|
||||
ja: "Japanese",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { CharacterEntity, NoteEntity } from "crossbell"
|
||||
|
||||
export type Language = "en" | "zh" | "zh-TW" | "ja"
|
||||
export type Language = Readonly<"en" | "zh" | "zh-TW" | "ja">
|
||||
|
||||
export type Site = {
|
||||
id: string
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import { Metadata, ResolvingMetadata } from "next"
|
||||
import { headers } from "next/headers"
|
||||
|
||||
import { defaultLocale, locales } from "~/i18n"
|
||||
|
||||
type BaseProps = {
|
||||
params?: Record<string, any>
|
||||
searchParams?: Record<string, string | string[] | undefined>
|
||||
}
|
||||
|
||||
type GenerateMetadata<T = BaseProps> = (
|
||||
props: T,
|
||||
parent: ResolvingMetadata,
|
||||
) => Promise<Metadata>
|
||||
|
||||
export function withHrefLang<T extends BaseProps>(
|
||||
_generateMetadata: GenerateMetadata<T>,
|
||||
): GenerateMetadata<T> {
|
||||
return async function generateMetadata(
|
||||
props: T,
|
||||
parent: ResolvingMetadata,
|
||||
): Promise<Metadata> {
|
||||
const metadata: Metadata = await _generateMetadata(props, parent)
|
||||
|
||||
if (metadata.alternates) {
|
||||
const { locale } = props?.params || {}
|
||||
const protocol = headers().get("x-forwarded-proto") || ""
|
||||
const host = headers().get("x-forwarded-host") || ""
|
||||
const metadataBase = new URL(`${protocol}://${host}`)
|
||||
const path = headers().get("x-xlog-pathname") || ""
|
||||
|
||||
const redirectedPathName = (locale: string) => {
|
||||
const search = headers().get("x-xlog-search") || ""
|
||||
const searchParams = new URLSearchParams(search)
|
||||
searchParams.set("locale", locale)
|
||||
|
||||
return `${path}?${searchParams.toString()}`
|
||||
}
|
||||
|
||||
metadata.metadataBase = metadataBase
|
||||
/**
|
||||
* Generate hreflang.
|
||||
* e.g. In the home page, the hreflang should be:
|
||||
* <head>
|
||||
* ...
|
||||
* <link rel="canonical" href="<URL>?locale=zh"/>
|
||||
* <link rel="alternate" hrefLang="en" href="<URL>?locale=en"/>
|
||||
* ...
|
||||
* <link rel="alternate" hrefLang="x-default" href="<URL>?locale=en"/>
|
||||
* </head>
|
||||
* */
|
||||
metadata.alternates["canonical"] = redirectedPathName(
|
||||
locale ?? defaultLocale,
|
||||
)
|
||||
metadata.alternates["languages"] = {
|
||||
...locales.reduce(
|
||||
(acc, locale) => {
|
||||
const hreflang: string = locale
|
||||
acc[hreflang] = redirectedPathName(locale)
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
"x-default": redirectedPathName("en"),
|
||||
}
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import { getClientIp } from "@supercharge/request-ip"
|
|||
import { IS_PROD } from "~/lib/constants"
|
||||
|
||||
import { defaultLocale, locales } from "./i18n"
|
||||
import { Language } from "./lib/types"
|
||||
|
||||
// HTTPWhiteListPaths: White list of path for plain http request, no HTTPS redirect
|
||||
const HTTPWhitelistPaths = ["/api/healthcheck"]
|
||||
|
|
@ -17,7 +18,7 @@ export const config = {
|
|||
}
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
const handleI18nRouting = createMiddleware({
|
||||
const handleI18nRouting = createMiddleware<Language[]>({
|
||||
locales,
|
||||
defaultLocale,
|
||||
localePrefix: "never",
|
||||
|
|
@ -25,6 +26,13 @@ export async function middleware(req: NextRequest) {
|
|||
|
||||
const { pathname } = req.nextUrl
|
||||
|
||||
const searchParams = new URLSearchParams(req.nextUrl.search)
|
||||
const specifiedLocale = searchParams.get("locale") as Language | undefined
|
||||
|
||||
if (specifiedLocale && locales.includes(specifiedLocale)) {
|
||||
req.cookies.set("NEXT_LOCALE", specifiedLocale)
|
||||
}
|
||||
|
||||
if (
|
||||
IS_PROD &&
|
||||
req.headers.get("x-forwarded-proto") !== "https" &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue