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

APP JavaScript
スポンサーリンク

6日目のゴールと今日やること

6日目のテーマは
「翻訳アプリの内部構造を整理し、fetch・async/await・エラーハンドリングを“設計として理解する”」
ことです。

ここまでであなたはすでに、

  • 翻訳
  • 自動翻訳(デバウンス)
  • 履歴
  • お気に入り
  • コピー機能
  • ローディング表示
  • 言語一覧の取得

といった機能を実装してきました。

6日目は、これらを 「読みやすく・直しやすく・拡張しやすいコード」 に整理していきます。

つまり今日は、
“中級者としての設計力”を身につける日
です。


状態管理(state)を「アプリの中心」に置く

状態をひとまとめにする理由

昨日までのコードでは、
お気に入り、履歴、ローディング状態などが
バラバラの変数として存在していました。

これをひとつの state にまとめると、

  • どこに何があるか迷わない
  • UI 更新がしやすい
  • 保存(localStorage)と連動しやすい
  • デバッグがしやすい

というメリットがあります。

state の基本形

const state = {
  isLoading: false,
  history: [],
  favorites: [],
  languages: []
};
JavaScript

状態を更新する関数

function updateState(updates) {
  Object.assign(state, updates);
}
JavaScript

この関数があるだけで、
アプリ全体のコードがスッキリします。


fetch を「共通関数」にまとめて再利用する

なぜ共通化が必要なのか

翻訳アプリでは、

  • 言語一覧取得
  • 翻訳
  • 自動翻訳
  • 履歴から再翻訳
  • お気に入りから再翻訳

など、fetch を使う場面が多いです。

そこで、
fetch のエラーハンドリングを共通化した関数
を作ります。

共通 fetch 関数(重要)

async function requestJson(url, options = {}) {
  try {
    const response = await fetch(url, options);

    if (!response.ok) {
      throw new Error(`HTTPエラー(${response.status})`);
    }

    const data = await response.json();
    return data;

  } catch (error) {
    throw new Error(error.message);
  }
}
JavaScript

深掘りポイント

この関数は、

  • fetch の失敗
  • HTTP エラー
  • JSON パースエラー

すべて「Error」として上に投げる 役割を持っています。

これにより、
上位の関数は「何をしたいか」だけに集中できます。


翻訳専用の関数を「読みやすい形」にする

翻訳リクエストを共通化

async function requestTranslate(text, source, target, format) {
  const body = {
    q: text,
    source: source,
    target: target,
    format: format
  };

  const data = await requestJson("https://libretranslate.com/translate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });

  if (!data.translatedText) {
    throw new Error("翻訳結果が取得できませんでした。");
  }

  return data.translatedText;
}
JavaScript

深掘りポイント

requestTranslate
「翻訳 API の仕様を知っている唯一の関数」
です。

他の関数は、
翻訳 API の細かい仕様を知らなくてよくなります。


ローディング表示を「状態と連動」させる

ローディング開始・終了を関数化

function startLoading(message) {
  updateState({ isLoading: true });
  statusDiv.textContent = message || "処理中です…";
  translateButton.disabled = true;
}

function endLoading() {
  updateState({ isLoading: false });
  translateButton.disabled = false;
}
JavaScript

深掘りポイント

ローディング表示は、
「ユーザーに安心感を与える UI」
です。

状態と UI を連動させることで、
アプリ全体の動きが統一されます。


翻訳処理を「ストーリーとして読める」形にする

translateText の完成形(6日目版)

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("翻訳中です…");
  resultDiv.textContent = "";

  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

深掘りポイント

この関数は、
「翻訳の流れをそのまま読める」
という設計になっています。

  • 入力チェック
  • ローディング開始
  • 翻訳
  • 成功時の処理
  • 失敗時の処理
  • ローディング終了

fetch の細かい処理は
requestTranslate に隠れているため、
とても読みやすくなっています。


通信失敗時の分岐を「ユーザー目線」で整理する

エラーの種類を分類する

翻訳アプリでは、次のようなエラーが起きます。

  • ネットワークエラー
  • HTTP エラー
  • JSON パースエラー
  • API の中身が想定外
  • 入力が空
  • 言語が同じ

これらをすべて
「ユーザーが理解できる言葉」
に変換します。

例:429(リクエスト過多)

if (response.status === 429) {
  throw new Error("リクエストが多すぎます。少し待ってから再試行してください。");
}
JavaScript

深掘りポイント

エラーメッセージは、
「技術的に正しい」より「ユーザーに伝わる」ことが大事
です。


UI を「使いやすい形」に整える

翻訳結果のコピー機能

copyButton.addEventListener("click", () => {
  navigator.clipboard.writeText(resultDiv.textContent)
    .then(() => statusDiv.textContent = "コピーしました。")
    .catch(() => statusDiv.textContent = "コピーに失敗しました。");
});
JavaScript

深掘りポイント

コピー機能は地味ですが、
翻訳アプリでは必須級の便利機能
です。


6日目のまとめ

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

  • state にアプリの状態を集約
  • fetch を requestJson に共通化
  • 翻訳処理を requestTranslate に分離
  • translateText を「読みやすい流れ」に整理
  • ローディング表示を状態と連動
  • エラーハンドリングを原因別に整理
  • UI の改善(コピー機能など)

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


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

6日目の本質は、

「コードは“動けばいい”から、“読めて直せて拡張できる”に育てていくもの」
ということです。

fetch
async/await
try-catch
state
localStorage
UI 更新

これらがひとつの“型”としてまとまり始めています。

7日目では、この翻訳アプリを
中級編の完成形としてまとめる
回にします。

あなたのアプリが「本当に使えるツール」になる瞬間です。

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