JavaScript | 1 日 120 分 × 7 日アプリ学習:API通信アプリ(LibreTranslate API)

APP JavaScript
スポンサーリンク

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();
  }
}
JavaScript

5日目のまとめ

今日やったことを整理すると、こうなります。

  • 入力補助(文字数カウント)
  • 文法モード(format=text / html)
  • ローディング表示のアニメーション化
  • エラーハンドリングを原因別に強化
  • 翻訳結果のコピー機能
  • UI の改善(読みやすいレイアウト)

どれも新しい文法ではなく、
「fetch / async-await / エラーハンドリングの型をどう応用するか」
という話です。


今日いちばん深く理解してほしいこと

5日目の本質は、

「API 通信の型を理解すれば、アプリの使い勝手をどんどん改善できる」
ということです。

翻訳
履歴
お気に入り
コピー
ローディング
エラー表示

これらはすべて、
fetch / async-await / try-catch の型の上に乗っています。

6日目では、
UI の最適化、翻訳の自動化、状態管理の整理などを行い、
さらに完成度の高い翻訳アプリに育てていきます。

タイトルとURLをコピーしました