Invalid Date

Next.jsアプリケーションの多言語化とパフォーマンス改善

導入

このブログ記事では、Next.jsアプリケーションの開発中に直面した多言語化とパフォーマンスに関する課題、そしてそれらをどのように解決したかについて詳しく説明します。特に、翻訳の遅延や開発環境でのエラーに焦点を当て、その解決策を共有します。

問題点1: Next.jsのparams.localeに関するエラーと修正

Next.jsのApp Routerにおいて、[locale]/layout.tsx[locale]/page.tsxparams.localeに直接アクセスするとエラーが発生しました。これは、paramsが非同期で解決されるため、使用する前にawaitする必要があるというNext.jsの新しい挙動によるものです。

解決策: layout.tsxpage.tsxの関数シグネチャでparamsPromise<{ locale: string }>として受け取り、関数内でawait paramsとしてからlocaleを使用するように修正しました。

// 例: layout.tsx
export default async function RootLayout({
  children,
  params,
}: Readonly<{
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
}>) {
  const awaitedParams = await params;
  const locale = awaitedParams.locale;
  // ...
}

問題点2: 求人情報の翻訳パフォーマンス改善(Prismaへの翻訳結果キャッシュ)

アプリケーションの初期段階では、求人情報(JobCard)の翻訳をユーザーがアクセスするたびにリアルタイムで行っていました。これにより、特に初回アクセス時にパフォーマンスのボトルネックが生じていました。この問題を解決するため、翻訳結果をデータベースにキャッシュする仕組みを導入しました。

解決策:

  1. Prismaスキーマの変更: JobTranslationという新しいモデルをprisma/schema.prismaに追加しました。このモデルは、jobIdlanguagetitlecompanylocationdescriptionなどの翻訳済みフィールドを保持します。
    model JobTranslation {
      id          String   @id @default(cuid())
      jobId       String
      job         Job      @relation(fields: [jobId], references: [id], onDelete: Cascade)
      language    String
      title       String?
      company     String?
      location    String?
      description String?
    
      @@unique([jobId, language])
      @@map("job_translations")
    }
    
  2. マイグレーションの課題と解決:
    • ObjectIdauto()関数の非サポート: SQLiteデータベースを使用しているため、@db.ObjectId@default(auto())がサポートされていないというエラーに直面しました。これらはMongoDBのようなドキュメントデータベース向けの機能です。 解決策: idフィールドをString @id @default(cuid())に変更しました。
    • リレーションの欠落: JobモデルとUserモデル、JobTranslationモデルとJobモデル間の双方向リレーションが定義されていなかったため、エラーが発生しました。 解決策: Userモデルにjobs Job[] @relation("UserJobs")を、Jobモデルにtranslations JobTranslation[]を追加しました。
    • 非対話モードでのマイグレーション: prisma migrate devコマンドが非対話環境で実行できないという問題に直面しました。 解決策: 最終的に、prisma migrate reset --forceでデータベースをリセットし、すべてのマイグレーションを再適用することで問題を解決しました。これにより開発環境のデータは失われましたが、スキーマの整合性が確保されました。
  3. 求人情報の投入スクリプトの作成: データベースがリセットされたため、求人情報が空になりました。sample-hellowork-data.xmlからデータを読み込み、データベースに投入するスクリプトscripts/populateJobsFromXml.tsを作成しました。
    • fast-xml-parserを使用してXMLデータをパースし、prisma.job.upsertでデータを投入しました。
    • ts-nodeでの実行時にUnknown file extension ".ts"エラーが発生したため、tscでJavaScriptにコンパイルしてからnodeで実行するようにしました。

問題点3: i18nextの翻訳ファイルロードエラーと修正

アプリケーションの起動時に、i18nextが翻訳ファイルをロードできないというエラーが発生しました。Failed to parse URL from /locales/vi/common.jsonのようなメッセージが表示され、翻訳キーがそのまま表示される問題につながりました。

解決策:

  1. NEXT_PUBLIC_BASE_URLの設定: next.config.tsNEXT_PUBLIC_BASE_URL環境変数を設定し、翻訳ファイルのロードパスを絶対URLにしました。
    // next.config.ts
    const nextConfig: NextConfig = {
      env: {
        NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000',
      },
    };
    
  2. resourcesToBackendの導入: i18next-http-backendがサーバーサイドで相対パスを解決できない問題を回避するため、i18next-resources-to-backendを導入しました。これにより、翻訳ファイルがビルド時にバンドルに含まれるようになります。
    • src/app/i18n/settings.tssrc/app/i18n/client.tsxを修正し、resourcesToBackendを使用するように変更しました。
    • i18nextの初期化オプションからbackendプロパティを完全に削除し、resourcesToBackendが正しく機能するようにしました。

問題点4: 翻訳キーがそのまま表示される問題と修正

翻訳ファイルがロードされているにもかかわらず、ヘッダー部分などの翻訳キーがそのまま表示される問題が残りました。求人情報は正しく翻訳されていたため、i18nextの機能自体は動作していました。

解決策:

  1. RootLayoutHomeコンポーネントからのuseTranslation削除: Next.jsのApp Routerでは、サーバーコンポーネントでuseTranslationのようなクライアントサイドのフックを直接使用することはできません。RootLayoutHomeコンポーネントからuseTranslationの呼び出しを削除しました。翻訳は、クライアントコンポーネントであるHeaderJobListで行うようにしました。
  2. HeaderコンポーネントでのuseTranslationreadyフラグ使用: react-i18nextuseTranslationフックは非同期であり、i18nインスタンスが完全に初期化される前にt()が呼び出されるとキーがそのまま表示されることがあります。HeaderコンポーネントでuseTranslationから返されるreadyフラグを使用し、readytrueになるまでレンダリングを遅らせるようにしました。

問題点5: tsconfig.jsonとコンパイルエラーの修正

開発中に様々なコンパイルエラーが発生しました。

解決策:

  1. tsconfig.jsonnoEmitoutDirの調整: スクリプトのコンパイルのためにtsconfig.jsonnoEmit: trueを一時的にfalseに変更し、outDirを設定しました。コンパイル後、設定を元に戻しました。
  2. Userモデルのnameフィールド削除による波及: Userモデルからnameフィールドを削除したことで、api/register/route.tslib/session.tsなど、nameを参照している他のファイルでエラーが発生しました。 解決策: これらのファイルからnameフィールドへの参照を削除しました。
  3. paramsの型不一致: layout.tsxpage.tsxparamsの型がPromise<any>stringの間で不一致を起こすエラーが発生しました。 解決策: paramsPromise<{ locale: string }>として受け取り、awaitしてからlocaleを使用するように修正しました。
  4. cityMasterDataの重複宣言: api/forum/posts/route.tscityMasterDataが二重に宣言されているエラーが発生しました。 解決策: 重複する宣言を削除しました。

結論

これらの修正を通じて、Next.jsアプリケーションの多言語化機能は大幅に改善され、翻訳結果のデータベースキャッシュによりパフォーマンスも向上しました。開発中に発生した様々なエラーも解決し、アプリケーションは安定して動作するようになりました。

今後の展望としては、より多くの言語への対応や、翻訳の精度向上、さらなるパフォーマンス最適化などが挙げられます。

← Previous Entry: Next Entry:
← Back to Blog List