私が歌川です

@utgwkk が書いている

ここ最近部屋が乾燥しまくっている。たまにハンドクリームを塗ってみているものの一瞬で手がカサカサになってしまう。エアコンの風が直撃しているのがよくない気もする。

旅するルルルンの企画で買ったパックがなくなりつつあるので次のパックを探さなければならない。なんかいいのないですか。

ブルーアーカイブのエデン条約編のストーリーを全部読み切った。いろいろ言うとネタバレになるので詳細は伏せるけど、ハッピーエンドを求める普通の少女が主人公であることの意味が出ていてよかった。全員プレイしてください。こちらの記事を読んだらどういうゲームなのか分かると思います。

cemetrygates1919.hatenablog.com

Next.js Static HTML Export 国際化 最速 無料

はじめに

みなさまはNext.jsで静的に生成するHTMLファイルを国際化したくありませんか? Next.jsに組み込みのi18n routingStatic HTML Exportに対応していないので、別の手を考える必要があります。

結論から言うと、以下の記事にやり方が書いてあります。

dev.to

この記事では、Next.jsでStatic HTML Exportで言語ごとのHTMLファイルを生成する方法と、ハマったところを簡単にまとめます。

手順

翻訳データを用意する

public/locales/(言語)/common.json に翻訳データを用意します。ラベル→文言というJSONを置けばよいです。

{
  "hello": "こんにちは"
}

next-i18nextをインストールする

Next.jsでi18next (react-i18next) を使えるようにするライブラリです。i18next, react-i18nextのインストールは不要でnext-i18nextだけ入れれば動きます。useTranslation 関数もnext-i18nextからimportします。

$ yarn add next-i18next

next-i18nextの設定を書く

リポジトリルートに next-i18next.config.js というファイルを書きます。設定ファイルの書き方はREADMEを参照してください。ここでは、デフォルトで日本語を使う・対応言語は日本語とアメリカ英語、という設定をしています。

module.exports = {
  i18n: {
    defaultLocale: 'ja-JP',
    locales: [
      'ja-JP',
      'en-US',
    ],
  },
}

言語ごとのページを生成できるようなファイル構成にする

pages ディレクトリを以下のように、[lang] ディレクトリにページを突っ込むような構成にします。簡単のため index.tsx だけ用意します。

pages
├── [lang]
│   └── index.tsx
└── _app.tsx

getStaticProps で言語ごとの情報を読み込む

react-i18nextの serverSideTranslations 関数で翻訳データを読み込んで、それを getStaticProps で呼び出して、propsとして渡します。

import nextI18nextConfig from '../../next-i18next.config';
import {serverSideTranslations} from 'next-i18next/serverSideTranslations';

const getI18nProps = async (ctx) => {
  let locale = ctx.params?.lang ?? nextI18nextConfig.i18n.defaultLocale;
  if (locale instanceof Array) {
    locale = locale[0];
  }

  const props = await serverSideTranslations(locale);
  return props;
};

export const getStaticProps = async (ctx) => ({
  props: await getI18nProps(ctx),
});

getStaticPaths で生成対象の言語を指定する

next-i18next.config.js に対応言語の一覧があるので、それを使ってHTMLを生成する対象の言語を指定します。

const getI18nPaths = () =>
  nextI18nextConfig.i18n.locales.map(lang => ({
    params: {
      lang,
    },
  }));

export const getStaticPaths = () => ({
  fallback: false,
  paths: getI18nPaths(),
});

翻訳する

ここまでできたら useTranslation フックを使って翻訳していけばよいです。

const { t } = useTranslation();
t('hello');

next build して next export したら、言語ごとにHTMLファイルが生成されていることが確認できます。

注意点

生成されたHTMLに翻訳データが埋め込まれる

生成されたHTMLに翻訳データが埋め込まれることに注意しましょう。文言が多いとそれだけファイルサイズも大きくなるし、公開したくない文言がある場合は含めてはいけません。翻訳データの名前空間を絞っておくのが安全です。

クライアントサイドのJavaScriptで翻訳文言を表示する必要がない場合は更に最適化する余地があるかもしれません。

next dev で起動する際にも正しく翻訳させる

defaultNS (デフォルトで使う翻訳データの名前空間) をいじっている場合などでは、 serverSideTranslations 関数の第3引数に next-i18next.config.js の内容を渡す必要があります。

const props = await serverSideTranslations(locale, undefined, nextI18nextConfig);

こうした上で next dev で開発用サーバーを起動して、 /ja-JP などのパスにアクセスすると動作確認できます。

next export で生成した静的ファイルに対して動作確認するには

out ディレクトリ以下で適当なHTTPサーバーを立ち上げればよいです。Pythonなどで適当なワンライナーを書いてもよいし、http-serverなどのライブラリを使うとNode.js上で完結できてよさそうです。

おわりに

Next.jsで国際化しつつStatic HTML Exportできましたね。ファイルパスに [] といった記号を使うのは初見だとギョッとするけど思ったよりも簡単に実現できたのでよかったです。こうして生成したファイルを何らかの方法で言語ごとに出し分ければよさそうですね。

ひきつづき原神をプレイしている。ストーリーが進んでエリアが開放された。いつの時代も地図が広がる喜びは変わらないのであろう。またランクを上げてメインストーリーの解禁を待つところまで進んだのでしばらく散策が続きそう。

マルチプレイをちょっとやってみて、海外の人と英語でチャットしていた。きみ強いね~って書いたら、ずっとやってたらこうなるって言われた。素朴なコミュニケーションを体験している。オンラインゲームでコミュニケーションを取るのが怖くて手を出していなかったけどこれぐらいならできそう。

www.youtube.com

次に欲しいのは胡桃です。ソシャゲ部分の仕組みはよく分かっていない。

3連休

久しぶりに原神をプレイして、冒険ランクを上げたらついにストーリーが動きはじめて感動的だった。またしばらくランクを上げてたら次のストーリーが解禁されたので今日はここまで。

いつの間にか璃月のマップを全部開放していた。璃月は高い山が多いので飛び甲斐がある。

www.youtube.com

www.youtube.com

このあたりの動画に出てくるマップがだいたい分かって感動の体験がある。稲妻にはまだ到着していない。