seo
This commit is contained in:
@@ -1,10 +1,30 @@
|
|||||||
import { setRequestLocale } from "next-intl/server";
|
import type { Metadata } from "next";
|
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { createPageMetadata } from "@/lib/seo";
|
||||||
import AboutHeroSection from "@/components/AboutHeroSection";
|
import AboutHeroSection from "@/components/AboutHeroSection";
|
||||||
import ParentCompanySection from "@/components/ParentCompanySection";
|
import ParentCompanySection from "@/components/ParentCompanySection";
|
||||||
import TeamSection from "@/components/TeamSection";
|
import TeamSection from "@/components/TeamSection";
|
||||||
import MilestonesSection from "@/components/MilestonesSection";
|
import MilestonesSection from "@/components/MilestonesSection";
|
||||||
import RecruitSection from "@/components/RecruitSection";
|
import RecruitSection from "@/components/RecruitSection";
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { locale } = await params;
|
||||||
|
const t = await getTranslations({ locale, namespace: "metadata" });
|
||||||
|
|
||||||
|
return createPageMetadata({
|
||||||
|
locale: locale as Locale,
|
||||||
|
pathname: "/about",
|
||||||
|
title: t("aboutTitle"),
|
||||||
|
description: t("aboutDescription"),
|
||||||
|
siteName: t("title"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default async function AboutPage({
|
export default async function AboutPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { NextIntlClientProvider, useMessages } from "next-intl";
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { routing } from "@/i18n/routing";
|
import { routing } from "@/i18n/routing";
|
||||||
import { Locale } from "@/i18n/config";
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { getBaseUrl, ogImage } from "@/lib/seo";
|
||||||
import "../globals.css";
|
import "../globals.css";
|
||||||
import Header from "@/components/Header";
|
import Header from "@/components/Header";
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
@@ -25,10 +26,35 @@ export async function generateMetadata({
|
|||||||
}): Promise<Metadata> {
|
}): Promise<Metadata> {
|
||||||
const { locale } = await params;
|
const { locale } = await params;
|
||||||
const t = await getTranslations({ locale, namespace: "metadata" });
|
const t = await getTranslations({ locale, namespace: "metadata" });
|
||||||
|
const baseUrl = getBaseUrl();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
metadataBase: baseUrl,
|
||||||
|
title: {
|
||||||
|
default: t("title"),
|
||||||
|
template: `%s | ${t("title")}`,
|
||||||
|
},
|
||||||
|
description: t("description"),
|
||||||
|
keywords: t("keywords"),
|
||||||
|
applicationName: t("title"),
|
||||||
|
icons: { icon: "/LOGO_header.png" },
|
||||||
|
openGraph: {
|
||||||
title: t("title"),
|
title: t("title"),
|
||||||
description: t("description"),
|
description: t("description"),
|
||||||
icons: { icon: "/LOGO_header.png" },
|
siteName: t("title"),
|
||||||
|
type: "website",
|
||||||
|
images: [{ url: ogImage, alt: t("title") }],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: t("title"),
|
||||||
|
description: t("description"),
|
||||||
|
images: [ogImage],
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: true,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,29 @@
|
|||||||
import { setRequestLocale } from "next-intl/server";
|
import type { Metadata } from "next";
|
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { createPageMetadata } from "@/lib/seo";
|
||||||
import HeroSection from "@/components/HeroSection";
|
import HeroSection from "@/components/HeroSection";
|
||||||
import FlywheelSection from "@/components/FlywheelSection";
|
import FlywheelSection from "@/components/FlywheelSection";
|
||||||
import MarketSection from "@/components/MarketSection";
|
import MarketSection from "@/components/MarketSection";
|
||||||
import CTASection from "@/components/CTASection";
|
import CTASection from "@/components/CTASection";
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { locale } = await params;
|
||||||
|
const t = await getTranslations({ locale, namespace: "metadata" });
|
||||||
|
|
||||||
|
return createPageMetadata({
|
||||||
|
locale: locale as Locale,
|
||||||
|
pathname: "/",
|
||||||
|
title: t("homeTitle"),
|
||||||
|
description: t("homeDescription"),
|
||||||
|
siteName: t("title"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Home({
|
export default async function Home({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,8 +1,28 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
import { setRequestLocale, getTranslations } from "next-intl/server";
|
import { setRequestLocale, getTranslations } from "next-intl/server";
|
||||||
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { createPageMetadata } from "@/lib/seo";
|
||||||
import SolutionsHeroSection from "@/components/SolutionsHeroSection";
|
import SolutionsHeroSection from "@/components/SolutionsHeroSection";
|
||||||
import ScenarioSection from "@/components/ScenarioSection";
|
import ScenarioSection from "@/components/ScenarioSection";
|
||||||
import SolutionsCTASection from "@/components/SolutionsCTASection";
|
import SolutionsCTASection from "@/components/SolutionsCTASection";
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { locale } = await params;
|
||||||
|
const t = await getTranslations({ locale, namespace: "metadata" });
|
||||||
|
|
||||||
|
return createPageMetadata({
|
||||||
|
locale: locale as Locale,
|
||||||
|
pathname: "/solutions",
|
||||||
|
title: t("solutionsTitle"),
|
||||||
|
description: t("solutionsDescription"),
|
||||||
|
siteName: t("title"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default async function SolutionsPage({
|
export default async function SolutionsPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
import { setRequestLocale } from "next-intl/server";
|
import type { Metadata } from "next";
|
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||||
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { createPageMetadata } from "@/lib/seo";
|
||||||
import TechHeroSection from "@/components/TechHeroSection";
|
import TechHeroSection from "@/components/TechHeroSection";
|
||||||
import ArchitectureSection from "@/components/ArchitectureSection";
|
import ArchitectureSection from "@/components/ArchitectureSection";
|
||||||
import AICapabilitiesSection from "@/components/AICapabilitiesSection";
|
import AICapabilitiesSection from "@/components/AICapabilitiesSection";
|
||||||
import Web3CapabilitiesSection from "@/components/Web3CapabilitiesSection";
|
import Web3CapabilitiesSection from "@/components/Web3CapabilitiesSection";
|
||||||
import TechCTASection from "@/components/TechCTASection";
|
import TechCTASection from "@/components/TechCTASection";
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { locale } = await params;
|
||||||
|
const t = await getTranslations({ locale, namespace: "metadata" });
|
||||||
|
|
||||||
|
return createPageMetadata({
|
||||||
|
locale: locale as Locale,
|
||||||
|
pathname: "/tech",
|
||||||
|
title: t("techTitle"),
|
||||||
|
description: t("techDescription"),
|
||||||
|
siteName: t("title"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default async function TechPage({
|
export default async function TechPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
15
src/app/robots.ts
Normal file
15
src/app/robots.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { MetadataRoute } from "next";
|
||||||
|
import { siteUrl } from "@/lib/seo";
|
||||||
|
|
||||||
|
export default function robots(): MetadataRoute.Robots {
|
||||||
|
return {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
userAgent: "*",
|
||||||
|
allow: "/",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sitemap: `${siteUrl}/sitemap.xml`,
|
||||||
|
host: siteUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
19
src/app/sitemap.ts
Normal file
19
src/app/sitemap.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { MetadataRoute } from "next";
|
||||||
|
import { routing } from "@/i18n/routing";
|
||||||
|
import { Locale } from "@/i18n/config";
|
||||||
|
import { buildUrl, getLocalePath } from "@/lib/seo";
|
||||||
|
|
||||||
|
const staticRoutes = ["/", "/tech", "/solutions", "/about"] as const;
|
||||||
|
|
||||||
|
export default function sitemap(): MetadataRoute.Sitemap {
|
||||||
|
const lastModified = new Date();
|
||||||
|
|
||||||
|
return staticRoutes.flatMap((pathname) =>
|
||||||
|
routing.locales.map((locale) => ({
|
||||||
|
url: buildUrl(getLocalePath(locale as Locale, pathname)),
|
||||||
|
lastModified,
|
||||||
|
changeFrequency: pathname === "/" ? "weekly" : "monthly",
|
||||||
|
priority: pathname === "/" ? 1 : 0.7,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
86
src/lib/seo.ts
Normal file
86
src/lib/seo.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { routing } from "@/i18n/routing";
|
||||||
|
import { defaultLocale, Locale } from "@/i18n/config";
|
||||||
|
|
||||||
|
const rawSiteUrl =
|
||||||
|
process.env.NEXT_PUBLIC_SITE_URL ||
|
||||||
|
process.env.SITE_URL ||
|
||||||
|
"http://localhost:3000";
|
||||||
|
|
||||||
|
const normalizedSiteUrl = rawSiteUrl.match(/^https?:\/\//)
|
||||||
|
? rawSiteUrl
|
||||||
|
: `https://${rawSiteUrl}`;
|
||||||
|
|
||||||
|
export const siteUrl = normalizedSiteUrl.replace(/\/+$/, "");
|
||||||
|
export const ogImage = "/LOGO_header.png";
|
||||||
|
|
||||||
|
const trimSlashes = (value: string) => value.replace(/^\/+|\/+$/g, "");
|
||||||
|
|
||||||
|
export const getBaseUrl = () => {
|
||||||
|
try {
|
||||||
|
return new URL(siteUrl);
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLocalePath = (locale: Locale, pathname: string) => {
|
||||||
|
const cleaned = trimSlashes(pathname);
|
||||||
|
const suffix = cleaned ? `/${cleaned}` : "";
|
||||||
|
|
||||||
|
if (locale === defaultLocale) {
|
||||||
|
return suffix || "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/${locale}${suffix}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildUrl = (pathname: string) => `${siteUrl}${pathname}`;
|
||||||
|
|
||||||
|
export const getAlternates = (pathname: string) =>
|
||||||
|
Object.fromEntries(
|
||||||
|
routing.locales.map((locale) => [
|
||||||
|
locale,
|
||||||
|
buildUrl(getLocalePath(locale as Locale, pathname)),
|
||||||
|
]),
|
||||||
|
) as Record<Locale, string>;
|
||||||
|
|
||||||
|
export const createPageMetadata = ({
|
||||||
|
locale,
|
||||||
|
pathname,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
siteName,
|
||||||
|
}: {
|
||||||
|
locale: Locale;
|
||||||
|
pathname: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
siteName: string;
|
||||||
|
}): Metadata => {
|
||||||
|
const canonicalPath = getLocalePath(locale, pathname);
|
||||||
|
const canonicalUrl = buildUrl(canonicalPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
alternates: {
|
||||||
|
canonical: canonicalUrl,
|
||||||
|
languages: getAlternates(pathname),
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
url: canonicalUrl,
|
||||||
|
siteName,
|
||||||
|
type: "website",
|
||||||
|
images: [{ url: ogImage, alt: siteName }],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
images: [ogImage],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"title": "DESUN SINGULARITY",
|
"title": "DESUN SINGULARITY",
|
||||||
"description": "AI + Web3 Powered Full-Chain Asset Technology Platform"
|
"description": "AI + Web3 Powered Full-Chain Asset Technology Platform",
|
||||||
|
"homeTitle": "Reshaping Global Asset Value Networks",
|
||||||
|
"homeDescription": "AI + Web3 Powered Full-Chain Asset Technology Platform",
|
||||||
|
"techTitle": "Technology Stack",
|
||||||
|
"techDescription": "Full-stack architecture combining AI, Web3, and asset operations for scalable real-world asset digitization.",
|
||||||
|
"solutionsTitle": "Solutions",
|
||||||
|
"solutionsDescription": "DeSpace smart space management, RWA services, and AI-driven quantitative management for asset value creation.",
|
||||||
|
"aboutTitle": "About DESUN Singularity",
|
||||||
|
"aboutDescription": "Technology-driven alternative asset management platform integrating AI, Web3, finance, and real estate operations.",
|
||||||
|
"keywords": "AI, Web3, RWA, asset digitization, tokenization, quantitative management, smart space"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"title": "DESUN SINGULARITY",
|
"title": "DESUN SINGULARITY",
|
||||||
"description": "AI + Web3 驅動的全鏈路資產科技平台"
|
"description": "AI + Web3 驅動的全鏈路資產科技平台",
|
||||||
|
"homeTitle": "重構全球資產價值網絡",
|
||||||
|
"homeDescription": "AI + Web3 驅動的全鏈路資產科技平台",
|
||||||
|
"techTitle": "技術體系",
|
||||||
|
"techDescription": "AI 與 Web3 貫通的全棧技術架構,支撐實體資產數字化與規模化營運。",
|
||||||
|
"solutionsTitle": "場景解決方案",
|
||||||
|
"solutionsDescription": "DeSpace 空間管理、RWA 資產代幣化、量化資管等核心場景解決方案。",
|
||||||
|
"aboutTitle": "關於德商奇點",
|
||||||
|
"aboutDescription": "科技驅動的另類資產管理平台,融合 AI、Web3、金融與實體營運能力。",
|
||||||
|
"keywords": "AI, Web3, RWA, 資產數字化, 資產代幣化, 量化資管, 空間管理"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"home": "首頁",
|
"home": "首頁",
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"title": "DESUN SINGULARITY",
|
"title": "DESUN SINGULARITY",
|
||||||
"description": "AI + Web3 驱动的全链路资产科技平台"
|
"description": "AI + Web3 驱动的全链路资产科技平台",
|
||||||
|
"homeTitle": "重构全球资产价值网络",
|
||||||
|
"homeDescription": "AI + Web3 驱动的全链路资产科技平台",
|
||||||
|
"techTitle": "技术体系",
|
||||||
|
"techDescription": "AI 与 Web3 贯通的全栈技术架构,支撑实体资产数字化与规模化运营。",
|
||||||
|
"solutionsTitle": "场景解决方案",
|
||||||
|
"solutionsDescription": "DeSpace 空间管理、RWA 资产代币化、量化资管等核心场景解决方案。",
|
||||||
|
"aboutTitle": "关于德商奇点",
|
||||||
|
"aboutDescription": "科技驱动的另类资产管理平台,融合 AI、Web3、金融与实体运营能力。",
|
||||||
|
"keywords": "AI, Web3, RWA, 资产数字化, 资产代币化, 量化资管, 空间管理"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"home": "首页",
|
"home": "首页",
|
||||||
|
|||||||
Reference in New Issue
Block a user