This article will provide practical implementation methods for optimizing SEO in internationalized (i18n) web applications using Next.js 14's App Router and Next-intl. We'll focus on metadata configuration for dynamically generated pages and multilingual support, using an e-commerce site as a concrete example.
Target Audience
This guide is for developers who:
- Have basic understanding of Next.js App Router
- Know TypeScript fundamentals
- Have basic SEO knowledge
Development Environment
{
"next": "^14.2.22",
"next-intl": "^3.26.3",
"react": "^18",
"typescript": "^5"
}
1. Project Structure
1.1 Directory Layout
src/
├── app/
│ └── [locale]/
│ └── product/
│ └── [id]/
│ ├── page.tsx
│ └── layout.tsx
├── lib/
│ └── metadata/
│ └── productMetadata.ts
├── api/
│ └── model/
│ └── Product.ts
└── i18n/
│ ├── routing.ts
│
└── messages/
├── en.json
└── ja.json
1.2 Implementing Internationalized Routing
// src/i18n/routing.ts
import { defineRouting } from "next-intl/routing";
import { createNavigation } from "next-intl/navigation";
export const routing = defineRouting({
locales: ["en", "ja"],
defaultLocale: "en",
});
export const { Link, redirect, usePathname, useRouter } = createNavigation(routing);
1.3 Product Data Type Definition
// src/api/model/Product.ts
export interface Product {
id: string;
name: string;
code: string;
category: {
id: string;
name: string;
};
manufacturer?: string;
description: string;
images: {
small: string;
large: string;
};
price: number;
stock: number;
}
2. Dynamic Metadata Generation
2.1 Implementing Metadata Generation Logic
// src/lib/metadata/productMetadata.ts
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import type { Product } from "@/api/model/Product";
import { routing } from "@/i18n/routing";
export async function generateProductMetadata(
productDataPromise: Promise<Product>,
locale: string
): Promise<Metadata> {
const [productData, t] = await Promise.all([
productDataPromise,
getTranslations({ locale, namespace: "metadataProduct" }),
]);
// SEO optimization: Information structuring
const descriptionParts = [
productData.name,
`${productData.category.name} #${productData.code}`,
productData.manufacturer ? `${t("manufacturedBy")}${productData.manufacturer}` : "",
t("checkPrice")
].filter(Boolean);
const description = descriptionParts.join(" | ");
// Dynamic keyword generation
const productSpecificKeywords = [
productData.name,
productData.category.name,
productData.manufacturer,
`${productData.name} ${t("price")}`,
`${productData.name} ${t("stock")}`,
`${productData.category.name} ${t("products")}`
].filter(Boolean);
const baseKeywords = t("keywords").split(", ");
const allKeywords = [...new Set([...productSpecificKeywords, ...baseKeywords])];
return {
title: `${productData.name} | ${productData.category.name} | ${t("siteTitle")}`,
description,
keywords: allKeywords.join(", "),
metadataBase: new URL("https://example.com"),
alternates: {
canonical: `/${locale}/product/${productData.id}`,
languages: {
"x-default": `/en/product/${productData.id}`,
...Object.fromEntries(
routing.locales.map((l) => [l, `/${l}/product/${productData.id}`])
),
},
},
openGraph: {
type: "website",
siteName: t("siteTitle"),
title: `${productData.name} | ${productData.category.name}`,
description,
locale,
alternateLocale: routing.locales.filter((l) => l !== locale),
images: productData.images.large ? [
{
url: productData.images.large,
width: 1200,
height: 630,
alt: productData.name,
},
] : undefined,
},
robots: {
index: true,
follow: true,
},
};
}
2.2 Translation File Setup
// messages/en.json
{
"metadataProduct": {
"siteTitle": "Example Store",
"manufacturedBy": "Manufactured by",
"checkPrice": "Check price and availability",
"price": "price",
"stock": "stock",
"products": "products",
"keywords": "online store, ecommerce, shopping, price comparison, stock check, product reviews, online shopping"
}
}
// messages/ja.json
{
"metadataProduct": {
"siteTitle": "Example Store",
"manufacturedBy": "製造元",
"checkPrice": "価格と在庫状況をチェック",
"price": "価格",
"stock": "在庫",
"products": "商品",
"keywords": "オンラインストア, eコマース, 通販, 価格比較, 在庫確認, 商品レビュー, オンラインショッピング"
}
}
2.3 Page Component Implementation
// app/[locale]/product/[id]/page.tsx
import { generateProductMetadata } from "@/lib/metadata/productMetadata";
import { cache } from 'react';
import { notFound } from 'next/navigation';
const getProductDataCached = cache(async (id: string) => {
const response = await fetch(`/api/products/${id}`);
if (!response.ok) {
notFound();
}
return response.json();
});
export async function generateMetadata({
params
}: {
params: { id: string; locale: string }
}) {
const productDataPromise = getProductDataCached(params.id);
return generateProductMetadata(productDataPromise, params.locale);
}
export default async function ProductPage({
params
}: {
params: { id: string }
}) {
const productData = await getProductDataCached(params.id);
// Page content implementation
}
3. SEO Optimization Key Points
3.1 Metadata Structuring
When structuring metadata, consider:
-
Title Optimization
- Order information by importance
- Use pipes (|) for proper separation
- Include site name for brand recognition
-
Description Structuring
- Clear information segmentation
- Include only necessary information
- Match search intent
-
Keyword Optimization
- Combine dynamic and static keywords
- Consider search patterns
- Natural keyword placement
3.2 Multilingual Considerations
-
URL Structure Design
- Include language codes
- Set proper canonical URLs
- Configure x-default
-
Content Optimization
- Language-appropriate descriptions
- Cultural considerations
- Proper keyword translation
3.3 OpenGraph Configuration
For OpenGraph settings:
-
Basic Configuration
- Choose appropriate type settings
- Maintain siteName and title consistency
- Optimize descriptions
-
Image Handling
- Set appropriate sizes and ratios
- Configure alt attributes
- Handle missing images gracefully
4. Performance Optimization
4.1 Data Fetching Optimization
- Utilizing Cache
const getProductDataCached = cache(async (id: string) => {
// Data fetching logic
});
- Prevent duplicate requests
- Improve response time
- Ensure consistent data
- Error Handling
- Display 404 pages appropriately
- Maintain user experience
- Ensure crawlability
4.2 Rendering Optimization
-
Separate Metadata Generation
- Clear logic separation
- Improve reusability
- Enhance maintainability
-
Optimize Async Processing
- Parallel processing for speed
- Efficient resource usage
Summary
We've covered key implementation points for SEO and internationalization in Next.js App Router:
- Dynamic metadata generation implementation
- Multilingual support considerations and optimization
- SEO best practices
- Performance optimization techniques
These implementations enable building SEO-optimized multilingual web applications effectively.
Top comments (1)
SEO Optimization in Next.js is crucial but unfortunately it is often overlooked. Thanks, Great Tips!