Next.js 15で多言語計算機プラットフォームを構築する
Next.js 15、React 19、next-intlを使用して高性能な多言語計算機プラットフォームを構築する方法の詳細分析。国際化の実装、SEO最適化、パフォーマンス最適化を含む。
こんにちは、開発者の皆さん!👋
世界中のユーザーが利用するウェブサイトを構築しようとしたことがあるなら、それがどれほど挑戦的なことかご存知でしょう。私は最近、Free Calculatorsを作成する旅に出ました——12の異なる言語でユーザーにサービスを提供するプラットフォームです。正直に言って、それは本当に冒険でした!
この記事では、この多言語計算機プラットフォームの構築の背後にある実話を共有したいと思います。「これが私たちがやったこと」のバージョンだけでなく、「これが実際に起こったこと、何がうまくいかなかったか、そして私たちが何を学んだか」のバージョンです。なぜなら、正直なところ、グローバルなオーディエンス向けに構築することは公園の散歩ではないからです。
なぜこれを作ったのか(そしてなぜそれが重要なのか)
これを想像してみてください:あなたは計算機アプリを構築しています。かなり簡単ですよね?しかし、日本の誰かが日本語版を必要とするかもしれない、ブラジルの誰かがポルトガル語を好むかもしれない、そしてドイツのユーザーは絶対に数字をドイツ式でフォーマットしたい(1,234.56ではなく1.234,56)ことに気づきます。突然、あなたのシンプルな計算機は複雑な国際プロジェクトになります。
それがまさに私たちに起こったことです。私たちは基本的な計算機から始めて、最終的に異なる文化、言語、数字フォーマットのユーザーにサービスを提供するプラットフォームを構築することになりました。そして、知っていますか?それは私が取り組んだ最もやりがいのあるプロジェクトの一つになりました。
テックスタックの決定:なぜこれらのツールを選んだのか
なぜNext.js 15なのか?
正直に言うと、Next.js 15が最初にリリースされたとき、私たちは少し躊躇していました。結局のところ、「壊れていないなら、修理するな」ですよね?しかし、新しいApp RouterとReact 19の統合を見たとき、私たちは飛びつくことにしました。それは賢い選択だったことがわかりました。
// これが私たちがルーティングを設定した方法です
export const routing = defineRouting({
locales: [
'en',
'zh',
'ja',
'ko',
'fr',
'de',
'es',
'pt',
'ru',
'ar',
'hi',
'it',
],
defaultLocale: 'en',
localePrefix: 'as-needed', // この設定が多くの頭痛を救ってくれました
localeDetection: true,
});
React 19:アップグレードの価値はあるか?
短い答え:絶対に!長い答え:いくつかの互換性の問題に遭遇しましたが、新機能のパフォーマンス向上は顕著です。特にSuspenseとコンカレント機能の改善により、私たちのユーザーエクスペリエンスが大幅に向上しました。
TypeScript:私たちの救世主
TypeScriptなしで12言語の翻訳ファイルを管理するなんて想像できません。型安全性により、リファクタリング時に自信を持つことができました。特に複雑なネストされた翻訳オブジェクトを扱う際に。
// これが私たちが翻訳タイプを定義する方法です
interface TranslationKeys {
nav: {
siteName: string;
home: string;
calculators: string;
};
common: {
calculate: string;
clear: string;
result: string;
};
}
国際化の実装:本当の挑戦と解決策
挑戦1:言語検出の複雑さ
「言語検出?簡単!」と思うかもしれません。しかし、そうではありません。ユーザーは異なるデバイスからアクセスしたり、VPNを使用したり、特定の言語設定を持っているかもしれません。私たちは言語検出ロジックを完璧にするために多くの時間を費やしました。
// 私たちのミドルウェア設定
import createMiddleware from 'next-intl/middleware';
import { routing } from '@/i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)',
};
挑戦2:翻訳ファイルの管理
12言語の翻訳ファイルを管理するのは、12の異なるプロジェクトを管理するようなものです。私たちは自動化スクリプトを開発して支援しました:
// 私たちの翻訳管理スクリプト
const addMissingTranslations = async () => {
const baseTranslations = await loadTranslations('en');
const allLocales = await getAllLocales();
for (const locale of allLocales) {
const existingTranslations = await loadTranslations(locale);
const missingKeys = findMissingKeys(baseTranslations, existingTranslations);
if (missingKeys.length > 0) {
console.log(`Missing keys in ${locale}:`, missingKeys);
await addMissingKeys(locale, missingKeys);
}
}
};
挑戦3:数字フォーマットの罠
これは最も興味深い挑戦の一つでした。異なる国が異なる数字フォーマットを持っていることをご存知ですか?ドイツは小数点区切り文字としてコンマを使用し、アメリカはピリオドを使用します。通貨計算を扱う際に多くの問題に遭遇しましたが、適切なローカライズされたフォーマットを実装するまで。
SEO最適化:検索エンジンに愛されるようにする
静的サイト生成の力
静的サイト生成(SSG)を使用することにしました。これは私たちがこれまでに下した最高の決定かもしれません。ページが超高速なだけでなく、SEO結果も優秀です。
// 各言語の静的ページを生成
export const dynamic = 'force-static';
export function generateStaticParams() {
return routing.locales
.filter(locale => locale !== routing.defaultLocale)
.map(locale => ({ locale }));
}
メタデータ生成の技術
各言語の正しいメタデータを生成するには、ある程度の技術が必要です。私たちはSEOフレンドリーなメタデータを自動生成するシステムを開発しました:
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Home' });
return {
title: t('title'),
description: t('description'),
openGraph: {
title: t('title'),
description: t('description'),
locale: locale,
},
};
}
パフォーマンス最適化:ユーザーエクスペリエンスをスムーズにする
画像最適化の重要性
画像の読み込み最適化に多くの時間を費やしました。AVIFとWebPフォーマットの採用により、ページ読み込み速度が40%向上しました。
// next.config.tsでの画像設定
images: {
formats: ['image/avif', 'image/webp'],
domains: ['freecalculators.app'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
}
キャッシュ戦略:私たちの秘密兵器
正しいキャッシュ戦略を実装するには、試行錯誤が必要でした。最終的に階層化されたキャッシュアプローチを採用しました:
async headers() {
return [
// 静的アセットには強キャッシュ
{
source: '/(.*).(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)',
headers: [{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
}],
},
// ページには交渉キャッシュ
{
source: '/(.*)',
headers: [{
key: 'Cache-Control',
value: 'public, max-age=0, must-revalidate',
}],
},
];
}
ユーザーエクスペリエンスデザイン:すべての人を歓迎する
レスポンシブデザインの挑戦
私たちの計算機がすべてのデバイスで完璧に動作することを確保するのは、継続的な挑戦です。モバイルファーストアプローチを採用しました:
// モバイルファーストレスポンシブレイアウト
<div className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-1/2">
{/* 計算機インターフェース */}
</div>
<div className="w-full md:w-1/2">
{/* 結果表示 */}
</div>
</div>
アクセシビリティ:単なるコンプライアンス以上のもの
アクセシビリティは法的要件以上のものであることを認識しました——それは正しいことです。すべてのインタラクティブ要素に適切なARIAラベルを追加しました:
<button
aria-label={t('common.calculate')}
aria-describedby="calculator-help"
className="bg-blue-600 text-white px-4 py-2 rounded"
>
{t('common.calculate')}
</button>
開発ツールチェーン:開発をより効率的にする
エラーモニタリング:私たちの安全網
エラーモニタリングのためにSentryを統合しました。これは私たちが予期しなかった多くの問題を発見するのに役立ちました:
export default withSentryConfig(withNextIntl(nextConfig), {
org: 'my-projects-pd',
project: 'free-calculators',
widenClientFileUpload: true,
disableLogger: true,
automaticVercelMonitors: true,
});
自動化された翻訳管理
翻訳ファイルを管理するためのスクリプトを開発し、これらのスクリプトは今や私たちのワークフローの不可欠な部分です。
私たちが学んだ教訓
1. 最初から国際化を考える
多言語サポートが必要になるかもしれないと思うなら、最初から設計してください。後から国際化を追加するのは、最初から設計するよりもはるかに困難です。
2. ユーザーエクスペリエンスは技術的な見せびらかしよりも重要
私たちはパフォーマンスの最適化に多くの時間を費やしましたが、最終的にユーザーが最も気にするのは、インターフェースが直感的で計算が正確かどうかです。
3. 自動化が鍵
12言語の翻訳を手動で管理するのは現実的ではありません。自動化スクリプトは私たちのプロジェクトの生命線です。
4. テスト、テスト、そして再びテスト
異なる言語は異なるテキスト長、異なる数字フォーマット、異なる文化的期待を持っています。徹底的なテストは必須です。
将来の計画
より多くの言語を追加し、翻訳管理プロセスを改善し、AI支援翻訳の可能性を探ることを検討しています。しかし、最も重要なのは、ユーザーに最高の計算機エクスペリエンスを提供し続けることです。
結論
多言語ウェブアプリケーションの構築は挑戦ですが、非常にやりがいのある経験でもあります。賢明な技術選択と多くのテストを通じて、私たちは本当にグローバルユーザーにサービスを提供するプラットフォームを成功裏に作成しました。
多言語アプリケーションの構築を検討しているなら、この記事があなたにいくらかのインスピレーションを与えることを願っています。覚えておいてください、すべての挑戦は学習の機会であり、すべての間違いは成功への踏み石です。
プロジェクトリンク: https://www.freecalculators.app テックスタック: Next.js 15, React 19, TypeScript, Tailwind CSS, next-intl