i18n: 翻訳対象の切り分け基準
背景
多言語対応(i18n)を実装する際、「どの文字列を翻訳し、どの文字列を英語のまま残すか」の判断に迷うことがある。特にエクスポート機能(HTML/Markdown/PlainText)のように、画面表示とファイル出力の両方にテキストが含まれる場合、基準が曖昧になりやすい。
実際のプロジェクト(差分ビューアツール)での判断過程を記録する。
一般的な切り分け基準
翻訳すべきもの
ユーザーが読む・操作するすべてのテキストが原則。
- ボタンラベル(「Copy All」→「差分をコピー」)
- 見出し・セクションタイトル
- 説明文・ヘルプテキスト
- エラーメッセージ・ステータス表示
- ツールチップ
- 動的テンプレート(「N lines hidden」等の補間テキスト)
翻訳不要なもの
- ブランド名・プロダクト名: 「BDiff」「GitHub」等はそのまま
- 技術用語・業界標準: 「diff」「HTML」「Markdown」「API」
- URL・ファイルパス
- コード内の識別子: 変数名、関数名、翻訳キー自体
- バージョン番号・ビルドID
エクスポートファイルでの判断
ここが最も迷いやすいポイント。画面表示とエクスポートファイルでは、ユーザーのコンテキストが異なる。
エクスポートでも翻訳するもの
| 文字列 | 理由 |
|---|---|
"Original" / "Modified" |
画面表示と一致させるべき主要ラベル |
"No differences to display" |
ユーザーが読むメッセージ |
"N lines hidden" |
省略ブロックのテキスト |
エクスポートで翻訳不要なもの
| 文字列 | 理由 |
|---|---|
"BDiff Comparison Report" |
ブランド名を含むレポートタイトル |
"Generated by BDiff" |
ブランドクレジット |
"Name:" / "Size:" / "Lines:" |
構造ラベル。英語でも意味が通じ、技術レポートとして国際的に通用する |
"Generated:" |
同上 |
"Statistics" / "Added" / "Removed" |
Markdown/PlainTextの構造。英語が国際標準 |
判断の考え方
エクスポートファイルの構造ラベル(Name:, Size: 等)は翻訳コストに対して効果が低い。理由:
- 技術レポートは英語が共通語: 開発者向けツールのエクスポートは英語でも違和感がない
- 翻訳すると逆に混乱: 構造ラベルまで翻訳すると、エクスポートファイルの国際的な可読性が下がる
- コスト対効果: 8言語 × 多数のラベル = 大量の翻訳エントリ。ユーザー体験の向上は限定的
Reactプロジェクトでの実装パターン
コンポーネント内(React hook使用可)
const { t } = useTranslation();
<button>{t('diff.copyDiff')}</button>
サービスクラス(hook使用不可)
サービスクラスやレンダラーは React hook を使えないため、翻訳方式を選ぶ必要がある。
方式A: i18nextを直接使用
import i18next from 'i18next';
const label = i18next.t('export.original');
方式B: 翻訳済み文字列をオプション経由で渡す
// コンポーネント側
const labels = {
original: t('diff.original'),
modified: t('diff.modified'),
};
ExportService.export(lines, { labels });
// サービス側
render(lines: DiffLine[], options: { labels: ExportLabels }) {
// options.labels.original を使用
}
方式C: locale を渡してサービス内で翻訳JSONを直接参照
方式Bが最もシンプルで、サービスをi18nextに依存させない点で推奨。
まとめ
- 原則: ユーザーが見る・操作するテキストは翻訳する
- 例外: ブランド名、技術用語、エクスポートの構造ラベル
- 判断基準: 翻訳することでユーザー体験が改善するか?翻訳コストに見合うか?
- エクスポート: 画面と共通のラベル(Original/Modified等)は翻訳、構造ラベルは英語のまま