非エンジニアのための多言語コンテンツでの動的プレースホルダー管理ガイド

翻訳が必要な文章に含まれるURLや日付、時刻などの変数の取り扱いは、ローカライゼーションにおいてよくある課題です。これらの位置やフォーマットは言語によって大きく異なるため、多言語対応を同時に進めるような場面では、文字列の管理が非常に複雑になる場合もあります。
こんにちは、KINTOテクノロジーズでローカライゼーションを担当しているMayaです。本記事は、社内で多言語コンテンツを扱いつつも開発バックグラウンドはなく、それでもエンジニアと連携しながら効率的な仕組みをつくりたい!と思い悩んでいる方にぜひお役に立てば良いなという思いで書きました。
コーディング経験がなくても大丈夫です。実装のサポートをいただける同僚がいると理想的ですが、最近はソース言語と翻訳データをエンジニアチームと一緒に整えるための情報もオンラインに多く公開されています。
ちなみに私たちのチームでは、TMS はLokalise を翻訳データのSingle Source of Truthとして使用しており、GitHub に連携しています。
なぜこの仕組みの理解が重要か
ローカライゼーション担当者がURLや日付のような動的プレースホルダーの扱い方を理解することで、作業効率が上がるだけでなく、開発チームとの認識ギャップも縮まる期待が見込めます。わかりやすく説明してくれた開発チームのWenさんに感謝です!
このアプローチを導入してから(特に利用規約や日付・時間などプレースホルダーを多く含む文の)翻訳対応箇所の修正工数が月に5件ほどからほぼゼロに削減されたのです。時間の節約だけでなく、QAとのやり取りも減り、ローカライゼーションと開発の間の信頼関係も向上しました。
課題:言語ごとに異なるURLの位置
では本題の課題を見てみましょう。以下のような文章があるとします。
"本サービスを利用することにより、利用規約 および プライバシーポリシー に同意したものとみなされます。"
表現は一旦目を瞑って頂きたいのですが、英語版が以下の形だったとして、URLの位置は英語とは異なります。
"By using our service, you agree to our Terms and Conditions and Privacy Policy."
さらに3つ目の言語が加われば、その配置はまた異なるかもしれません。多言語を同時に扱う場合、これらの構文差を手動で管理するのはとても手間がかかり、ミスも起こりやすくなります。
解決策:formatString() を使おう
ここで登場するのが、この関数です。言語ごとにURLの配置を柔軟に変えられるテンプレートを作成できるため、非常に役立ちます。文全体を各言語ごとに書き換える必要がなく、URLの位置だけを調整できます。
多くのチームが直面している面倒な代替手段は、各言語の文字列にURLを直接ハードコーディングしてしまうことです。これにより、ほとんど同じ文のバリエーションを手作業で複数作成することになり、文字列ファイルが煩雑になり、タイプミスやリンク切れのリスクが高まり、URL変更時のメンテナンスが非常に大変になります。
この記事では、JavaScriptを例にこの課題への解決策を紹介しますが、同様の考え方はPython、C#、PHPなど他の言語でも応用可能です。
ステップ 1:文中の動的要素を特定する
まず、英語(ベース言語)の例文を見てみましょう:
“By using our service, you agree to our {terms and conditions} and {privacy policy}.”
ここで {terms and conditions} と {privacy policy} はURLに置き換えるプレースホルダです。これらの内容と配置は言語ごとに異なりますが、リンクとして機能する必要があります。言語ごとに文構造が異なるため、プレースホルダは各言語の構文に合わせて動的に扱う必要があります。
ステップ 2:プレースホルダ付きの翻訳対象文字列を作成する
i18nファイル内のキーと値の形式で、プレースホルダ付きの文を定義します。記号ではなく名前付きプレースホルダを使うことで、より明確でミスを防ぎやすくなります。
json
CopyEdit
{
"terms_and_privacy": "By using our service, you agree to our {terms} and {privacy}."
}
ここでは、コード上の混乱を避けるため、{terms} と {privacy} に簡略化しています。ベストプラクティスとしては、snake_case や camelCase の簡潔で説明的なキーを使うのが良いでしょう。
ステップ3:言語ごとにURLとリンクテキストを定義する
次に、URLだけでなくリンクテキストも翻訳して定義します。言語ごとに個別に処理する必要があります。
英語の場合:
js
CopyEdit
const en = {
terms: '<a href="https://example.com/terms_en">Terms and Conditions</a>',
privacy: '<a href="https://example.com/privacy_en">Privacy Policy</a>'
};
日本語の場合:
js
CopyEdit
const ja = {
terms: '<a href="https://example.com/terms_ja">利用規約</a>',
privacy: '<a href="https://example.com/privacy_ja">プライバシーポリシー</a>'
};
この方法により、リンクテキストとURLの両方を翻訳・再利用可能な形で柔軟に扱えます。
ステップ4:format()関数で動的にプレースホルダを置換する
ここがポイントです。format() メソッドを使って、プレースホルダを実際のURLに置き換えます。
JavaScriptにはPythonのような組み込みの format() メソッドはありませんが、以下のように自作することで同様の機能が実現できます:
js
CopyEdit
String.prototype.format = function (replacements) {
return this.replace(/{(\w+)}/g, (match, key) =>
typeof replacements[key] !== 'undefined' ? replacements[key] : match
);
};
例:
js
CopyEdit
// i18nファイルから取得した翻訳済み文字列
const localizedString = i18n.t('terms_and_privacy');
// 英語版
const formattedStringEn = localizedString.format(en);
console.log(formattedStringEn);
// 出力:By using our service, you agree to our <a href="https://example.com/terms_en">Terms and Conditions</a> and <a href="https://example.com/privacy_en">Privacy Policy</a>.
// 日本語版
const formattedStringJa = localizedString.format(ja);
console.log(formattedStringJa);
// 出力:本サービスを利用することにより<a href="https://example.com/terms_ja">利用規約</a>および<a href="https://example.com/privacy_ja">プライバシーポリシー</a>に同意したものとみなされます。
この format() 関数は {terms} や {privacy} といった中括弧付きのプレースホルダを検索し、指定されたオブジェクトの値で置換します。
開発者向けメモ:ここでは視覚的にわかりやすくするために生のHTMLを挿入していますが、本番環境では必ずサニタイズ処理を行い、インジェクション対策をしてください。
最後に
プレースホルダベースの翻訳文字列と動的フォーマットを組み合わせることで、柔軟性と制御性を確保できます。ソースコードをクリーンに保てるだけでなく、技術的な問題や人的ミスの回避にもつながります。
プレースホルダの形式は言語やフレームワーク、引数の種類によって異なります。Microsoftの国際化ガイドラインでは、翻訳者がプレースホルダ名を制御できるようにすべきとされていますが、最も重要なのは「開発者と翻訳者が、それぞれのプレースホルダが何を意味するのかを正しく理解し合うこと」だと思います。
例として、以下の2つの書き方を比べると、最初のものの方が理解しやすいです:
css
CopyEdit
Available {firstWeekDay} to {lastWeekDay}, from {startTime} to {endTime} {timeZone}.
よりも:
perl
CopyEdit
Available %s to %s, from %s to %s %s.
Microsoftの引用より
ソフトウェアプロジェクトを進める際は、最初の段階から国際化を意識することが重要です。最初のバージョンやリリースでは翻訳が不要かもしれませんが、初期の設計での判断が将来的に他市場向けに展開できるかどうかを左右します。…すべての機能を「ワールド・レディ(世界対応)」にしておくことが大切です。
参考リンク
関連記事 | Related Posts
We are hiring!
【フロントエンドエンジニア(リードクラス)】プロジェクト推進G/東京
配属グループについて▶新サービス開発部 プロジェクト推進グループ 中古車サブスク開発チームTOYOTAのクルマのサブスクリプションサービスである『 KINTO ONE 中古車 』のWebサイトの開発、運用を中心に、その他サービスの開発、運用も行っています。“とりあえずやってみる”から始まる開発文化。
【UI/UXデザイナー】クリエイティブ室/東京・大阪
クリエイティブ室についてKINTOやトヨタが抱えている課題やサービスの状況に応じて、色々なプロジェクトが発生しそれにクリエイティブ力で応えるグループです。所属しているメンバーはそれぞれ異なる技術や経験を持っているので、クリエイティブの側面からサービスの改善案を出し、周りを巻き込みながらプロジェクトを進めています。