GA4が教えてくれた。海外の人がアクセスしている
シミュレーションでも実機でもなく、GA4のリアルタイムデータを眺めるところから始まった。
💡 Key Takeaway
「ブラウザ翻訳で記事は読める。足りないのはチャットの英語対応だった。」GA4の北米流入をきっかけに、AIエージェントのEN/JA切り替えを実装した。記事コンテンツに手をつけず、チャットUI1本で海外ユーザーへの導線を整えた。
やったこと | 起きたこと | 判断 |
|---|---|---|
GA4のリアルタイムを確認した | 北米から28ユーザー、first_visit 27件という流入を発見 | 英語対応を今日やる理由ができた |
DifyにEN/JA変数を追加した | language変数をワークフローの開始ノードに追加 | プロンプト1行の変更だけで対応できた |
チャットUIにENボタンを実装した | 押すと挨拶・サジェスト・プレースホルダーが英語に切り替わる | Dify変数よりクエリプレフィックスの方が確実だった |
TOPページの案内テキストを英語化した | 「Chat with Hakobu — EN / JA available」に変更 | 日本語ページのまま、導線だけ英語にする判断 |
北米から、人が来ていた
— ログを眺めていたら、想定外のデータが出てきた。
GA4のリアルタイム画面を開いたら、過去30分のアクティブユーザーが28人。地図を見ると、北米に大きな青い点が集まっていた。first_visitが27件。ほぼ全員が初見の訪問者だ。
上位ページは「Record | 記録 シミュレーション」「Record | 記録 グリッパー」——技術ログを直接読みに来ている。おそらく海外のエンジニアか研究者だ。
「ブラウザ翻訳で読んでいるはず」と思った。自分もそうしているから、その判断は正しいと思う。記事コンテンツを英語化するコストをかけなくていい。でも、チャットが日本語のまま返してくるのは問題だ。
以前、チャットUIに言語切り替えボタンを実装しようとして、うまく動かなかったことがあった。Difyからの返答が日本語のまま帰ってくる問題だった。改めてその仕組みを作り直した。
Dify側の変更——変数を1つ追加するだけ
ワークフローの開始ノードに language という変数を追加した。
変数名: language
タイプ: String
デフォルト値: 日本語
非表示: ON(ユーザーには見せない)LLMノードのシステムプロンプトに1行追加した。
- 必ず {{language}} で回答することそして、フォローアップ提案のマーカーが言語で変わってしまう問題を防ぐために:
- フォローアップ提案のマーカーは言語に関わらず必ず [次の質問] を使うことDifyを公開して、次はHTML側の実装に入った。
チャットUI側の実装
最初の試み——Dify変数に渡す方式
最初は、APIコールの inputs にlanguage変数を渡す方式を試みた。
inputs: { language: ptmLang },でも、Difyのチャットフローは会話開始時にしかinputsを参照しない。会話途中で言語を切り替えてもうまく動かなかった。
解決策——クエリプレフィックス方式
より確実な方法に切り替えた。クエリ自体に言語指示を埋め込む。
query: ptmLang === 'English' ? '[Respond in English] ' + input : input,英語モードのとき、ユーザーの質問の先頭に [Respond in English] を自動付与する。Difyの変数設定に依存しないので、毎回確実に動く。
ENボタンの実装
ヘッダーに切り替えボタンを追加した。
<div id="ptm-header-right">
<span id="ptm-header-sub">Pixels to Motors AI</span>
<button id="ptm-lang-btn" onclick="ptmToggleLang()">EN</button>
</div>ボタンのスタイルは白背景・黒テキスト。ホバーでブランドカラーのイエローに変わる。
#ptm-lang-btn {
background: #ffffff;
border: 1px solid #ffffff;
color: #0a0a0a;
font-family: 'Syne', sans-serif;
font-size: 10px;
font-weight: 700;
letter-spacing: .1em;
padding: 4px 10px;
cursor: pointer;
border-radius: 2px;
transition: all .2s;
}
#ptm-lang-btn:hover { background: #e8ff00; border-color: #e8ff00; color: #0a0a0a; }切り替え関数——チャットごとリセットする
ボタンを押したとき、単に言語フラグを変えるだけでは不十分だった。それまでの会話が日本語で表示されたまま残るから、画面が混在してしまう。
切り替えのタイミングでチャット全体をリセットし、言語に合った挨拶とサジェストを表示し直す設計にした。
function ptmToggleLang() {
ptmLang = ptmLang === '日本語' ? 'English' : '日本語';
const btn = document.getElementById('ptm-lang-btn');
btn.textContent = ptmLang === '日本語' ? 'EN' : 'JA';
document.getElementById('ptm-input').placeholder =
ptmLang === '日本語' ? 'ロボットのことを聞いてみよう' : 'Ask me anything about the project';
ptmConvId = '';
// チャットをリセットして言語に合った挨拶を表示
const box = document.getElementById('ptm-messages');
box.innerHTML = '';
if (ptmLang === '日本語') {
ptmAppendMsg('ai', 'ハコブくんにまかせて!\nロボット開発の記録、全部頭に入ってるよ。\nなんでも聞いてね!');
ptmAppendSuggestions([
'このプロジェクトって何をやってるの?',
'一番苦労したのはどこ?',
'デジタルツインって何?'
]);
} else {
ptmAppendMsg('ai', "Hi! I'm Hakobu, your guide to the Pixels to Motors project.\nAsk me anything about the build log, the robot arm, or the R2R2R pipeline!");
ptmAppendSuggestions([
'What is this project about?',
'How does the simulation connect to the real robot?',
'Why use an iPhone for robotics?'
]);
}
}ptmConvId = '' でDify側の会話IDもリセットする。次のメッセージは新しい会話として送られるので、inputsも新鮮な状態で渡せる。
動作確認
ENを押すと:
ハコブくんの挨拶が英語に切り替わる
サジェストボタンが英語3択に変わる
プレースホルダーが「Ask me anything about the project」になる
以降の返答がすべて英語で返ってくる
JAに戻すと、チャットがリセットされて日本語の挨拶に戻る。
TOPページの導線も英語化
チャットだけ英語対応しても、そこへ誘導する導線が日本語のままでは気づいてもらえない。
TOPページのキャプションを変更した。
変更前: Pixels to MotorsのAIエージェントはこちら!
変更後: Chat with Hakobu, the AI guide to this project — EN / JA available →
ハコブくんの吹き出しも英語に変更。シンプルな英語なので、日本語話者にも読める。
判断したこと
記事コンテンツを英語に翻訳することはやらなかった。
海外ユーザーはブラウザ翻訳で読んでいる。それで実際に読めているなら、そこにコストをかける必要はない。足りていなかったのはチャットの英語対応だけだった。
チャット1本を直す。TOPの導線テキストを変える。それだけで、今日のGA4の28人に対して「ここで英語で話せる」という導線を作れた。
精度より体験の成立を優先する——という設計哲学は、ロボット開発だけじゃなく、こういうところにも出てくる。
