MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

Vue I18n の肥大化した翻訳ファイルを、分割管理でスッキリ解決した話

はじめに

こんにちは、マイクロアドでサーバサイドエンジニアをしている藤田です。

Vue I18n を使った多言語対応は非常に便利ですが、プロジェクトが成長するにつれ、翻訳メッセージファイルの管理に頭を悩ませることはありませんか?

マイクロアドが提供しているプロダクトの 1 つに、 メディア事業者向けのサプライサイドプラットフォーム MicroAd COMPASS があります。 このプロダクトのリニューアルプロジェクトでは、多言語対応するためのライブラリとして Vue I18n を採用しています。

しかし、プロジェクトが進むにつれ、対応言語や翻訳対象のテキストの増加に伴って翻訳メッセージファイルの肥大化が進み、管理が煩雑になる問題が発生しました。

具体的には、以下のような問題が発生していました。

  • 行数が膨大になり、見通しが悪くなる
  • 似たような意味のメッセージが重複して定義されてしまう
  • 修正箇所を見つけるのに時間がかかる
  • コンフリクトが起きやすくなる

このため、メッセージファイルの追加・保守コストが掛かり、開発のスピード感が下がる状態となっていました。

本記事では、私たちが実際に取り組んだ Vue I18n の翻訳メッセージファイルの管理方法の改善について、具体的なファイル構成や考え方を紹介します。 この工夫が、同じような課題を抱えている方の参考になれば幸いです。

Vue I18n の機能

Vue I18n は、Vue.js 公式の国際化(Internationalization, i18n)ライブラリです。 このライブラリを使うことで、比較的簡単に Vue.js で構築されたアプリケーションを多言語対応サイトとして作成できるようになります。 これにより、世界中のさまざまな言語を使うユーザーに対して、それぞれの言語でアプリケーションを提供できるようになります。

Vue I18n が提供する主な機能には、以下のようなものがあります。

  • テキストの翻訳: キーに基づいて、対応する言語のテキストを表示する。
  • 複数形への対応: 数値に応じて単数形・複数形などを適切に表示分けする。
  • 数値フォーマット: 通貨やパーセンテージなどをロケールに合わせて表示する。
  • 日時フォーマット: 日付や時刻をロケールに合わせた形式で表示する。

この Vue I18n の中核をなすのが、翻訳メッセージファイルになります。

Vue I18n は、これらの翻訳メッセージファイルを読み込み、アプリケーションの現在のロケール設定(例:'en' や 'ja')に応じて、適切な言語のテキストをコンポーネント内に表示します。 この仕組みにより、開発者は UI コンポーネント内で翻訳キーを指定するだけで、動的に言語を切り替えることが可能になります。

メッセージファイルは、一般的に JSON や YAML といった形式で作成され、アプリケーション内で使用する翻訳対象の文言を「キー」と「翻訳済みテキスト」のペアで言語ごとに定義します。

ja.json

{
  "greeting": "こんにちは、皆さん!"
}

en.json

{
  "greeting": "Hello, everyone!"
}

以下のように、Vue I18n のインスタンス作成時に翻訳メッセージファイルを読み込ませることで、上記の翻訳ファイルによる多言語対応ができるようになります。

Vue I18n インスタンスの作成例 (main.tsi18n.ts など)

import { createI18n } from 'vue-i18n'
import jaMessages from "@/path/to/ja.json" // 正しいパスに修正
import enMessages from "@/path/to/en.json"; // 正しいパスに修正

const i18n = createI18n({
  locale: "ja",
  fallbackLocale: "en",
  messages: {
    ja: jaMessages,
    en: enMessages,
  },
});

export default i18n;

コンポーネントでの使用例

import { useI18n } from "vue-i18n";
const { t } = useI18n();

// t("greeting") で、設定された言語のテキストが表示される。
//   日本語設定なら → こんにちは、皆さん!
//   英語設定なら   → Hello, everyone!

翻訳メッセージファイル は以下の観点において非常に重要な役割を持ちます。

  • コードと翻訳テキストの分離: プログラミングロジックと表示用テキストを分離することで、コードの可読性やメンテナンス性を高める。
  • 翻訳作業の効率化: 翻訳者やローカライゼーション担当者が、アプリケーションのコードを直接触ることなく翻訳作業へ専念できる。
  • 一元管理: 各言語の翻訳情報を一箇所(または言語ごとのファイル)で管理するため、テキスト表現の統一や変更が容易になる。

従来の管理方法とその課題

改善前の私たちのプロジェクトでは、主に以下の 2 種類のファイル群で管理していました。

./src/locale
├── en
│   ├── messageEnglish.json     // 英語メッセージ全般
│   └── validationEnglish.json  // 英語のバリデーションメッセージ
├── jp
│   ├── messageJapanese.json    // 日本語メッセージ全般
│   └── validationJapanese.json // 日本語のバリデーションメッセージ
├── locale_types.ts             // 多言語対応したキーのオブジェクト
└── locale.ts                   // Vue I18nの初期化とメッセージ読込処理
  • messageEnglish.json, messageJapanese.json
    • 各画面で使用するテキストを、画面ごとのオブジェクトにまとめて格納していた。
    • 結果として、これらのファイルは非常に長大になり、開発スピードを妨げる要因の 1 つとなっていた。
    • また、異なる画面でも実質的に同じ意味の単語(例:「登録する」「キャンセル」など)が何度も記述されるケースがあった。
  • validationEnglish.json, validationJapanese.json
    • フォームのバリデーションエラーメッセージをまとめていた。
    • こちらはメッセージの性質上、ある程度文章が多くなるため messageEnglish.jsonmessageJapanese.json とは別に分離して記載していた。
      • この分離自体は良いアプローチであり、他の種類のメッセージも同様に分割管理したい、という今回の改善へ向けた大きな動機の 1 つでした。

今回の改善で目指したのは、「関心事の分離」を翻訳ファイル管理にも適用することです。

新しい翻訳ファイルの管理戦略

上記の課題を踏まえ、翻訳メッセージをその性質や用途に応じてファイル分割する戦略を導入しました。 分割後のファイル構造は以下の通りです。

./src/locale
├── en
│   ├── commonWords.json        // 英語の共通単語
│   ├── constants.json          // 英語の定数関連の表示文字列
│   ├── messageEnglish.json     // 英語の画面固有メッセージなど(旧来のファイルから共通部分を分離したもの)
│   └── validationEnglish.json  // 英語のバリデーションメッセージ
├── jp
│   ├── commonWords.json        // 日本語の共通単語
│   ├── constants.json          // 日本語の定数関連の表示文字列
│   ├── messageJapanese.json    // 日本語の画面固有メッセージなど
│   └── validationJapanese.json // 日本語のバリデーションメッセージ
├── locale_types.ts             // 多言語対応したキーのオブジェクト
└── locale.ts                   // Vue I18nの初期化とメッセージ読込処理

messageEnglish.json や messageJapanese.json は、共通単語や定数が分離されたことで、以前よりもスリムになりました。 これにより、それぞれの画面に特化したメッセージや、共通化しにくい長文のメッセージを管理する役割に特化したファイルとなりました。

以下に、今回新規で追加したファイルcommonWords.json, constants.jsonの詳細を記載します。

1. commonWords.json の導入(各言語共通)

役割

アプリケーション内で頻繁に使用される共通的な単語や短いフレーズをまとめた「単語集」として利用する。

ボタンのラベル(「保存」「更新」「削除」)、汎用的なタイトル(「設定」「一覧」)、状態を示す単語(「成功」「失敗」)など。

管理ルール

翻訳メッセージが重複しないように、キーをアルファベット順に並べ替えて記載する。

メリット

これまで各画面のメッセージファイルに散らばっていた共通単語をこのファイルで一元管理することで、メッセージの重複や表記揺れが発生しにくくなる。 例えば、ある画面のメッセージファイルで「title」というキーで翻訳を使いたい場合、まずはこの commonWords.json に適切な単語がないか確認し、あればそれを利用する。

2. constants.json の導入(各言語共通)

役割

API から返される ENUM 型 のような定数値に対応する表示文字列を管理する。

ステータス(ACTIVE → "アクティブ", STOP → "停止")、配信プラットフォーム ID(PC → "PC", SP → "スマートフォン")など。

管理ルール

TypeScript の型定義ファイル (types/constant.ts) で管理されている定数と対応付けて記載する。

メリット

バックエンドの仕様変更に伴う表示名の修正が一箇所で済むことで、アプリケーション全体での整合性を保ちやすくなる。

locale.ts での読み込みと設定

分割した複数の JSON ファイルを Vue I18n で利用可能にするには、locale.ts (または i18n.ts など、Vue I18n の設定ファイル) でこれらのファイルを messages オプションに設定します。

以下にサンプルコードを記載しました。 この例では、各言語ごとに分割された JSON ファイルをインポートし、スプレッド構文 (...) を使って 1 つのオブジェクトにマージしています。

import { createI18n } from 'vue-i18n';

// 英語のメッセージ
import enCommonWords from './en/commonWords.json';
import enConstants from './en/constants.json';
import enMessages from './en/messageEnglish.json';
import enValidation from './en/validationEnglish.json';

// 日本語のメッセージ
import jpCommonWords from './jp/commonWords.json';
import jpConstants from './jp/constants.json';
import jpMessages from './jp/messageJapanese.json';
import jpValidation from './jp/validationJapanese.json';

const jp: LocaleMessage = {
  ...jpCommonWords,
  ...jpConstants,
  ...jpMessages,
  ...jpValidation,
}
const en: LocaleMessage = {
  ...enCommonWords,
  ...enConstants,
  ...enMessages,
  ...enValidation,
}

const i18n = createI18n({
  legacy: false, // Vue 3 Composition APIモード
  locale: 'jp', // デフォルトロケール
  fallbackLocale: 'en',
  messages: { jp, en },
});

export default i18n;

新戦略によるメリット

このファイル分割戦略を導入したことで、以下のようなメリットが期待できます。

  • 可読性の向上: 各ファイルが特定の役割を持つため、どこに何が書かれているか把握しやすくなった。
  • メンテナンス性の向上: 修正対象のメッセージが特定のファイルに限定されるため、迅速な対応が可能になる。また、変更による影響範囲の予測も容易になる。
  • 再利用性の向上: commonWords.json や constants.json により、同じ意味の単語やフレーズを繰り返し定義する手間が省け、アプリケーション全体での用語の統一も図れる。
  • 行数の削減: 個々のファイルがスリムになるため、ファイルの扱いが容易になる。
  • コンフリクトの軽減: 複数人での開発時、異なる関心事のファイルを編集することになるため、コンフリクトのリスクが低減できる。

まとめ

Vue I18n の翻訳メッセージファイルの管理は、プロジェクトの規模が大きくなるほど、より重要性も増します。 今回紹介したファイル分割と分類による管理方法は、私たちのチームにとっては大きな改善となりました。

もちろん、これが唯一の正解というわけではありません。 プロジェクトの特性やチームの規模に応じて、最適な管理方法は異なります。

重要なのは、よりコード改修やメンテナンスしやすくするための方法を模索し続けることです。 我々も、より良い改善案を求めて今後も試行錯誤を進める所存です。

この記事が、あなたの Vue I18n ライフを少しでも快適にするための一助となれば幸いです。