5日目のゴールと今日やること
5日目のテーマは
「翻訳アプリを“実用レベル”に引き上げるために、UI の改善・入力補助・翻訳精度の向上・エラーハンドリングの強化を行う」
ことです。
技術的な柱はいつも通りこの 3 つです。
- fetch
- Promise / async-await
- エラーハンドリング
ただし今日は、これらを “ユーザー体験(UX)” に結びつけます。
具体的には次の機能を追加します。
- 入力補助(文字数カウント・入力チェック)
- 翻訳の精度を上げるための「文法モード」
- ローディング表示の改善(アニメーション風)
- エラーハンドリングの強化(原因別メッセージ)
- 翻訳結果のコピー機能
- UI の整理(読みやすいレイアウト)
どれも fetch / async-await の型をそのまま使い回す ことで実現できます。
入力補助を追加して「翻訳前のミス」を防ぐ
入力文字数をリアルタイムで表示する
翻訳アプリでは、
「入力が長すぎて翻訳に時間がかかる」
「入力が短すぎて翻訳にならない」
といった問題が起きます。
そこで、入力欄の下に文字数を表示します。
const countDiv = document.getElementById("count");
inputText.addEventListener("input", () => {
const length = inputText.value.length;
countDiv.textContent = `${length} 文字`;
});
JavaScript深掘りポイント
文字数表示は単純ですが、
ユーザーが「今どれくらい入力しているか」を把握できる
という大きな UX 改善になります。
翻訳精度を上げるための「文法モード」を追加する
LibreTranslate の format パラメータ
LibreTranslate には format というパラメータがあります。
"text"→ 通常のテキスト"html"→ HTML を含むテキスト
これを利用して、
「文法モード」 を追加します。
文法モードの UI
<select id="formatMode">
<option value="text">通常モード</option>
<option value="html">HTML モード</option>
</select>
翻訳リクエストに追加
body: JSON.stringify({
q: text,
source: source,
target: target,
format: formatMode.value
})
JavaScript深掘りポイント
HTML モードは、
タグを壊さずに翻訳したいときに便利
という特徴があります。
例えば:
<p>Hello <strong>world</strong></p>
これを HTML モードで送ると、
タグを保持したまま翻訳されます。
ローディング表示を「アニメーション風」に改善する
シンプルなアニメーション
ローディング中に「…」が増えていくような表示を作ります。
let loadingInterval = null;
function startLoading(message) {
let dots = "";
statusDiv.textContent = message;
loadingInterval = setInterval(() => {
dots = dots.length < 3 ? dots + "." : "";
statusDiv.textContent = message + dots;
}, 300);
translateButton.disabled = true;
}
function endLoading() {
clearInterval(loadingInterval);
translateButton.disabled = false;
}
JavaScript深掘りポイント
ローディング表示は、
「アプリが固まっていない」ことを伝える重要な UI
です。
アニメーションがあるだけで、
ユーザーのストレスが大きく減ります。
エラーハンドリングを「原因別」に強化する
翻訳専用の共通関数を改良する
async function requestTranslate(text, source, target, format) {
try {
const response = await fetch("https://libretranslate.com/translate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
q: text,
source: source,
target: target,
format: format
})
});
if (!response.ok) {
if (response.status === 429) {
throw new Error("リクエストが多すぎます。少し待ってから再試行してください。");
}
throw new Error(`HTTPエラー(${response.status})`);
}
const data = await response.json();
if (!data.translatedText) {
throw new Error("翻訳結果が取得できませんでした。");
}
return data.translatedText;
} catch (error) {
throw new Error(error.message);
}
}
JavaScript深掘りポイント
特に重要なのは 429(Too Many Requests) の扱いです。
翻訳 API は連続で叩くと制限に引っかかることがあります。
このとき、
「しばらく待ってください」
と伝えるのが正しい UX です。
翻訳結果を「ワンクリックでコピー」できるようにする
コピー用ボタンを追加
<button id="copyButton">コピー</button>
コピー処理
copyButton.addEventListener("click", () => {
const text = resultDiv.textContent;
navigator.clipboard.writeText(text)
.then(() => {
statusDiv.textContent = "コピーしました。";
})
.catch(() => {
statusDiv.textContent = "コピーに失敗しました。";
});
});
JavaScript深掘りポイント
コピー機能は地味ですが、
翻訳アプリでは必須級の便利機能 です。
翻訳処理の完成形(5日目版)
async function translateText() {
const text = inputText.value.trim();
const source = sourceLang.value;
const target = targetLang.value;
const format = formatMode.value;
if (!text) {
statusDiv.textContent = "翻訳する文章を入力してください。";
resultDiv.textContent = "";
return;
}
if (source === target) {
statusDiv.textContent = "翻訳元と翻訳先が同じです。別の言語を選んでください。";
return;
}
startLoading("翻訳中です");
try {
const translated = await requestTranslate(text, source, target, format);
statusDiv.textContent = "翻訳に成功しました。";
resultDiv.textContent = translated;
addHistory(text, translated, source, target);
} catch (error) {
statusDiv.textContent = `翻訳中にエラーが発生しました:${error.message}`;
console.error(error);
} finally {
endLoading();
}
}
JavaScript5日目のまとめ
今日やったことを整理すると、こうなります。
- 入力補助(文字数カウント)
- 文法モード(format=text / html)
- ローディング表示のアニメーション化
- エラーハンドリングを原因別に強化
- 翻訳結果のコピー機能
- UI の改善(読みやすいレイアウト)
どれも新しい文法ではなく、
「fetch / async-await / エラーハンドリングの型をどう応用するか」
という話です。
今日いちばん深く理解してほしいこと
5日目の本質は、
「API 通信の型を理解すれば、アプリの使い勝手をどんどん改善できる」
ということです。
翻訳
履歴
お気に入り
コピー
ローディング
エラー表示
これらはすべて、
fetch / async-await / try-catch の型の上に乗っています。
6日目では、
UI の最適化、翻訳の自動化、状態管理の整理などを行い、
さらに完成度の高い翻訳アプリに育てていきます。

