React Nativeアプリへi18nextを導入したあとも、画面には7月24日(金)のような日本語の日付が残っていました。翻訳JSONを増やすだけでは、Dateを直接整形している箇所は変わりません。
文言と日付が別々の言語にならないよう、言語判定を共通化してIntl.DateTimeFormatへ渡しました。
言語設定は3種類にする
設定値は日本語、英語、端末に従う、の3種類です。
export type LanguagePreference = "system" | "ja" | "en";
ユーザーの選択はSecureStoreへ保存します。systemの場合だけ、HermesのIntlから端末ロケールを読み取ります。
function detectSystemLanguage(): "ja" | "en" {
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
return locale.toLowerCase().startsWith("ja") ? "ja" : "en";
}
この実装では、UIを変えた瞬間にi18n.changeLanguageも呼びます。
export async function setLanguagePreference(pref: LanguagePreference) {
await SecureStore.setItemAsync(LANGUAGE_KEY, pref);
const language = pref === "system" ? detectSystemLanguage() : pref;
await i18n.changeLanguage(language);
}
日付を手書きしない
以前は月や曜日を配列で持ち、文字列を組み立てていました。これをIntl.DateTimeFormatへ置き換えます。
export function formatScheduleDate(value: Date, language: string) {
const locale = language === "ja" ? "ja-JP" : "en-US";
return new Intl.DateTimeFormat(locale, {
month: "long",
day: "numeric",
weekday: "short",
}).format(value);
}
コンポーネントではi18n.resolvedLanguageを使います。
const { t, i18n } = useTranslation();
const label = formatScheduleDate(date, i18n.resolvedLanguage ?? "ja");
resolvedLanguageを見るのは、フォールバックを含めてi18nextが実際に採用した言語へ揃えるためです。
翻訳対象を段階的に増やす
全画面を一度に置き換えると、翻訳漏れと画面崩れを同時に追うことになります。実際には次の順で進めました。
- 言語保存とダッシュボード、タブ
- 出欠登録の主要画面
- 設定、通知、管理画面と日付表示
日付対応を最後に回したことで、文字列の翻訳漏れとは別に確認できました。曜日が変わったか、月日の順序が変わったか、長い英語表記でレイアウトが崩れないかを見ます。
翻訳関数を通している文字列だけを数えても、多言語対応の完了判定にはなりません。日付、時刻、数値、エラーメッセージのように、JSONの外で生成する表示も言語設定へつなぐ必要があります。