refactor(i18n): migrate server routes to next-intl messages

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
david_bai
2026-03-27 14:04:28 +08:00
parent 6c93b1d995
commit b6193b662f
12 changed files with 105 additions and 74 deletions
+8 -6
View File
@@ -1,21 +1,23 @@
import { getDictionary } from "@/lib/dictionary";
import AboutContent from "./AboutContent";
import { Metadata } from "next";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.about.title,
description: messages.meta.about.description,
metadataBase: new URL("https://www.privydrop.app"),
alternates: {
canonical: `/${params.lang}/about`,
canonical: `/${lang}/about`,
languages: Object.fromEntries(
supportedLocales.map((lang) => [lang, `/${lang}/about`])
),
@@ -23,9 +25,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.about.title,
description: messages.meta.about.description,
url: `https://www.privydrop.app/${params.lang}/about`,
url: `https://www.privydrop.app/${lang}/about`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
+10 -7
View File
@@ -1,24 +1,27 @@
// app/[lang]/blog/[slug]/metadata.ts
import { Metadata } from "next";
import { getMessages } from "next-intl/server";
import { getPostBySlug } from "@/lib/blog";
import { generateMetadata as generateBlogMetadata } from "../metadata";
import { getDictionary } from "@/lib/dictionary";
import { supportedLocales } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
import type { Locale } from "@/constants/i18n-config";
export async function generateMetadata({
params,
}: {
params: { slug: string; lang: string };
}): Promise<Metadata> {
const post = await getPostBySlug(params.slug, params.lang);
const lang = params.lang as Locale;
const post = await getPostBySlug(params.slug, lang);
if (!post) {
//blog not found
// Call the generateMetadata function of the blog homepage and pass in the parameters
return generateBlogMetadata({ params: { lang: params.lang } });
return generateBlogMetadata({ params: { lang } });
}
const messages = await getDictionary(params.lang);
const messages = (await getMessages({ locale: lang })) as Messages;
const blogWord = messages.text.Header.blogLabel;
const blogCap = blogWord.charAt(0).toUpperCase() + blogWord.slice(1);
@@ -30,7 +33,7 @@ export async function generateMetadata({
)}, secure file sharing, p2p transfer, privacy`,
metadataBase: new URL("https://www.privydrop.app"),
alternates: {
canonical: `/${params.lang}/blog/${params.slug}`,
canonical: `/${lang}/blog/${params.slug}`,
languages: Object.fromEntries(
supportedLocales.map((l) => [l, `/${l}/blog/${params.slug}`])
),
@@ -38,9 +41,9 @@ export async function generateMetadata({
openGraph: {
title: post.frontmatter.title,
description: post.frontmatter.description,
url: `https://www.privydrop.app/${params.lang}/blog/${params.slug}`,
url: `https://www.privydrop.app/${lang}/blog/${params.slug}`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "article",
publishedTime: post.frontmatter.date,
modifiedTime: post.frontmatter.date,
+10 -7
View File
@@ -1,5 +1,6 @@
//Article detail page
import { MDXRemote } from "next-mdx-remote/rsc";
import { getMessages } from "next-intl/server";
import { getPostBySlug } from "@/lib/blog";
import * as React from "react";
import { mdxOptions } from "@/lib/mdx-config";
@@ -13,7 +14,8 @@ import {
buildBreadcrumbJsonLd,
getSiteUrl,
} from "@/lib/seo/jsonld";
import { getDictionary } from "@/lib/dictionary";
import type { Messages } from "@/types/messages";
import type { Locale } from "@/constants/i18n-config";
export { generateMetadata };
@@ -22,8 +24,9 @@ export default async function BlogPost({
}: {
params: { slug: string; lang: string };
}) {
const post = await getPostBySlug(params.slug, params.lang);
const messages = await getDictionary(params.lang);
const locale = params.lang as Locale;
const post = await getPostBySlug(params.slug, locale);
const messages = (await getMessages({ locale })) as Messages;
if (!post) {
return <div>{messages.text.blog.postNotFound}</div>;
@@ -41,12 +44,12 @@ export default async function BlogPost({
dateModified: post.frontmatter.date,
authorName: post.frontmatter.author,
imageUrl,
inLanguage: params.lang,
inLanguage: locale,
});
const breadcrumbsLd = buildBreadcrumbJsonLd({
items: [
{ name: messages.text.Header.homeLabel, item: `${siteUrl}/${params.lang}` },
{ name: messages.text.Header.blogLabel, item: `${siteUrl}/${params.lang}/blog` },
{ name: messages.text.Header.homeLabel, item: `${siteUrl}/${locale}` },
{ name: messages.text.Header.blogLabel, item: `${siteUrl}/${locale}/blog` },
{ name: post.frontmatter.title, item: postUrl },
],
});
@@ -64,7 +67,7 @@ export default async function BlogPost({
</h1>
<div className="flex flex-wrap items-center text-muted-foreground gap-2 sm:gap-4">
<time className="text-sm">
{new Date(post.frontmatter.date).toLocaleDateString(params.lang, {
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
year: "numeric",
month: "long",
day: "numeric",
+8 -5
View File
@@ -1,13 +1,16 @@
import { supportedLocales } from "@/constants/i18n-config";
import { Metadata } from "next";
import { getDictionary } from "@/lib/dictionary";
import { getMessages } from "next-intl/server";
import type { Messages } from "@/types/messages";
import type { Locale } from "@/constants/i18n-config";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.blog.title,
@@ -15,7 +18,7 @@ export async function generateMetadata({
keywords: messages.meta.blog.keywords,
metadataBase: new URL("https://www.privydrop.app"),
alternates: {
canonical: `/${params.lang}/blog`,
canonical: `/${lang}/blog`,
languages: Object.fromEntries(
supportedLocales.map((l) => [l, `/${l}/blog`])
),
@@ -23,9 +26,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.blog.title,
description: messages.meta.blog.description,
url: `https://www.privydrop.app/${params.lang}/blog`,
url: `https://www.privydrop.app/${lang}/blog`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
+6 -3
View File
@@ -1,9 +1,11 @@
import { getAllPosts } from "@/lib/blog";
import { ArticleListItem } from "@/components/blog/ArticleListItem";
import Link from "next/link";
import { getMessages } from "next-intl/server";
import { slugifyTag } from "@/utils/tagUtils";
import { generateMetadata } from "./metadata";
import { getDictionary } from "@/lib/dictionary";
import type { Messages } from "@/types/messages";
import type { Locale } from "@/constants/i18n-config";
export { generateMetadata };
@@ -12,8 +14,9 @@ export default async function BlogPage({
}: {
params: { lang: string };
}) {
const posts = await getAllPosts(lang);
const messages = await getDictionary(lang);
const locale = lang as Locale;
const posts = await getAllPosts(locale);
const messages = (await getMessages({ locale })) as Messages;
return (
<div className="max-w-[1400px] mx-auto px-4 sm:px-6 lg:px-8 py-12">
+13 -10
View File
@@ -1,17 +1,19 @@
import { Metadata } from "next";
import { getMessages } from "next-intl/server";
import { getPostsByTag } from "@/lib/blog";
import { ArticleListItem } from "@/components/blog/ArticleListItem";
import { supportedLocales } from "@/constants/i18n-config";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import { unslugifyTag } from "@/utils/tagUtils";
import { getDictionary } from "@/lib/dictionary";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params: { tag, lang },
}: {
params: { tag: string; lang: string };
}): Promise<Metadata> {
const locale = lang as Locale;
const decodedTag = unslugifyTag(tag);
const messages = await getDictionary(lang);
const messages = (await getMessages({ locale })) as Messages;
// Note: metadata text kept concise and localized
return {
@@ -20,7 +22,7 @@ export async function generateMetadata({
keywords: `${decodedTag}, blog, privydrop`,
metadataBase: new URL("https://www.privydrop.app"),
alternates: {
canonical: `/${lang}/blog/tag/${encodeURIComponent(tag)}`,
canonical: `/${locale}/blog/tag/${encodeURIComponent(tag)}`,
languages: Object.fromEntries(
supportedLocales.map((l) => [l, `/${l}/blog/tag/${encodeURIComponent(tag)}`])
),
@@ -28,9 +30,9 @@ export async function generateMetadata({
openGraph: {
title: `${decodedTag} - PrivyDrop`,
description: `Articles tagged: ${decodedTag}`,
url: `https://www.privydrop.app/${lang}/blog/tag/${encodeURIComponent(tag)}`,
url: `https://www.privydrop.app/${locale}/blog/tag/${encodeURIComponent(tag)}`,
siteName: "PrivyDrop",
locale: lang,
locale,
type: "website",
},
};
@@ -40,9 +42,10 @@ export default async function TagPage({
}: {
params: { tag: string; lang: string };
}) {
const locale = lang as Locale;
const decodedTag = unslugifyTag(tag);
const posts = await getPostsByTag(decodedTag, lang);
const messages = await getDictionary(lang);
const posts = await getPostsByTag(decodedTag, locale);
const messages = (await getMessages({ locale })) as Messages;
return (
<div className="max-w-[1400px] mx-auto px-4 sm:px-6 lg:px-8 py-12">
@@ -64,8 +67,8 @@ export default async function TagPage({
))
) : (
<p>{messages.text.blog.tagEmpty}</p>
)}
</div>
)}
</div>
</main>
</div>
</div>
+11 -8
View File
@@ -1,7 +1,8 @@
import FAQSection from "@/components/web/FAQSection";
import type { Metadata } from "next";
import { getDictionary } from "@/lib/dictionary";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import type { Messages } from "@/types/messages";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import JsonLd from "@/components/seo/JsonLd";
import { buildFaqJsonLd } from "@/lib/seo/jsonld";
@@ -10,7 +11,8 @@ export async function generateMetadata({
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.faq.title,
@@ -26,9 +28,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.faq.title,
description: messages.meta.faq.description,
url: `https://www.privydrop.app/${params.lang}/faq`,
url: `https://www.privydrop.app/${lang}/faq`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
@@ -39,8 +41,9 @@ export default async function FAQ({
}: {
params: { lang: string };
}) {
const messages = await getDictionary(lang);
const faqsData = (messages as any).text.faqs as Record<string, string>;
const locale = lang as Locale;
const messages = (await getMessages({ locale })) as Messages;
const faqsData = messages.text.faqs as Record<string, string>;
const questionKeys = Object.keys(faqsData).filter((k) => k.startsWith("question_"));
const faqs = questionKeys
.map((qKey) => {
@@ -53,7 +56,7 @@ export default async function FAQ({
})
.filter(Boolean) as { question: string; answer: string }[];
const faqLd = buildFaqJsonLd({ inLanguage: lang, faqs });
const faqLd = buildFaqJsonLd({ inLanguage: locale, faqs });
return (
<>
+7 -5
View File
@@ -1,14 +1,16 @@
import KeyFeatures from "@/components/web/KeyFeatures";
import type { Metadata } from "next";
import { getDictionary } from "@/lib/dictionary";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.features.title,
@@ -24,9 +26,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.features.title,
description: messages.meta.features.description,
url: `https://www.privydrop.app/${params.lang}/features`,
url: `https://www.privydrop.app/${lang}/features`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
+7 -5
View File
@@ -1,14 +1,16 @@
import { getDictionary } from "@/lib/dictionary";
import HelpContent from "./HelpContent";
import { Metadata } from "next";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.help.title,
@@ -23,9 +25,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.help.title,
description: messages.meta.help.description,
url: `https://www.privydrop.app/${params.lang}/help`,
url: `https://www.privydrop.app/${lang}/help`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
+11 -8
View File
@@ -1,7 +1,8 @@
import HomeClient from "./HomeClient";
import { getDictionary } from "@/lib/dictionary";
import { Metadata } from "next";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
import JsonLd from "@/components/seo/JsonLd";
import { buildWebAppJsonLd, getSiteUrl, absoluteUrl } from "@/lib/seo/jsonld";
@@ -10,7 +11,8 @@ export async function generateMetadata({
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.home.title,
@@ -18,7 +20,7 @@ export async function generateMetadata({
keywords: messages.meta.home.keywords,
metadataBase: new URL("https://www.privydrop.app"),
alternates: {
canonical: `/${params.lang}`,
canonical: `/${lang}`,
languages: Object.fromEntries(
supportedLocales.map((lang) => [lang, `/${lang}`])
),
@@ -27,9 +29,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.home.title,
description: messages.meta.home.description,
url: `https://www.privydrop.app/${params.lang}`,
url: `https://www.privydrop.app/${lang}`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
@@ -40,7 +42,8 @@ export default async function Home({
}: {
params: { lang: string };
}) {
const messages = await getDictionary(lang);
const locale = lang as Locale;
const messages = (await getMessages({ locale })) as Messages;
const siteUrl = getSiteUrl();
const webAppLd = buildWebAppJsonLd({
siteUrl,
@@ -52,7 +55,7 @@ export default async function Home({
"Open-source web-based AirDrop alternative",
],
description: messages.meta.home.description,
inLanguage: lang,
inLanguage: locale,
imageUrl: absoluteUrl("/logo.png", siteUrl),
applicationCategory: "UtilityApplication",
operatingSystem: "Web Browser",
+7 -5
View File
@@ -1,14 +1,16 @@
import type { Metadata } from "next";
import { getDictionary } from "@/lib/dictionary";
import { getMessages } from "next-intl/server";
import PrivacyContent from "./PrivacyContent";
import { supportedLocales } from "@/constants/i18n-config";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.privacy.title,
@@ -23,9 +25,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.privacy.title,
description: messages.meta.privacy.description,
url: `https://www.privydrop.app/${params.lang}/privacy`,
url: `https://www.privydrop.app/${lang}/privacy`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};
+7 -5
View File
@@ -1,14 +1,16 @@
import { getDictionary } from "@/lib/dictionary";
import TermsContent from "./TermsContent";
import { Metadata } from "next";
import { supportedLocales } from "@/constants/i18n-config";
import { getMessages } from "next-intl/server";
import { supportedLocales, type Locale } from "@/constants/i18n-config";
import type { Messages } from "@/types/messages";
export async function generateMetadata({
params,
}: {
params: { lang: string };
}): Promise<Metadata> {
const messages = await getDictionary(params.lang);
const lang = params.lang as Locale;
const messages = (await getMessages({ locale: lang })) as Messages;
return {
title: messages.meta.terms.title,
@@ -23,9 +25,9 @@ export async function generateMetadata({
openGraph: {
title: messages.meta.terms.title,
description: messages.meta.terms.description,
url: `https://www.privydrop.app/${params.lang}/terms`,
url: `https://www.privydrop.app/${lang}/terms`,
siteName: "PrivyDrop",
locale: params.lang,
locale: lang,
type: "website",
},
};