井出草平の研究ノート

ChatGPT Translate翻訳環境構築

ChatGPT TranslateをWindows環境においてDeepLのアプリのように使うための設定書である。グローバルホットキー(Ctrl+Alt+T)で翻訳できるように設定してある。DeepLに対する優位性と課題はある。

本マニュアルで構築する「ChatGPT Translate」の自動翻訳環境は、定番の翻訳ツールであるDeepLと比較して明確な一長一短がある。

【利点】DeepLより優れている点

  • 圧倒的な翻訳精度と文脈理解(最大の利点): 単なる直訳ではなく、前後の文脈、細かなニュアンス、高度な専門用語を深く汲み取り、人間が書いたような極めて自然な文章に翻訳できる。
  • ノイズや乱れに対する強力な補正力: コピー時に混ざった不自然な改行や、OCR(画像文字認識)の誤字脱字、砕けたスラングなどでも、AIが文脈から自動推測して綺麗に補正しながら翻訳してくれる。

【欠点】DeepLに劣っている点

  • 一度に翻訳できる文章量が圧倒的に少ない: DeepLが長文やドキュメント丸ごとの翻訳を得意とするのに対し、ChatGPTは一度に入力・出力できるテキスト量(トークン数)に厳しい制限がある。長文は分割して処理する必要がある。
  • 翻訳スピードの遅さ: DeepLが瞬時に結果を返すのに対し、ChatGPTはAIが文字を順番に生成(推論)する仕組み上、結果が出そろうまでに数秒の待機時間が発生する。
  • 元の書式(レイアウト)の保持が不可: DeepLのようにWordやPDFのレイアウトや文字装飾を維持したまま翻訳する機能はなく、すべてプレーンテキストでの出力となる。

Step 1: ChromeでのPWA(プログレッシブウェブアプリ)化

PWA(プログレッシブウェブアプリ)はWebサイトをデスクトップアプリのように独立したウィンドウで実行できる仕組みである。ブラウザのタブやアドレスバーが非表示になるため、ChatGPT Translateをネイティブの専用翻訳アプリのように扱うことができる。

  1. Google ChromeでChatGPT TranslateのURL(https://chatgpt.com/ja-JP/translate/)を開く。
  2. ブラウザ右上の「︙(設定メニュー)」を開き、「保存して共有」>「ページをアプリとしてインストール」の順に選択する。
  3. 任意の名前(例:ChatGPT Translate)をつけてインストールを完了させる。これにより、独立したウィンドウとして翻訳画面が起動するようになる。

Step 2: StylusによるUI最適化 (CSS)

Stylusは特定のWebサイトに対して独自のCSS(デザインルール)を適用し、見た目を自由に変更できるブラウザ拡張機能である。本環境では、不要なヘッダーやフッター、案内文などを非表示にし、翻訳の入力・出力エリアだけを画面いっぱいに広げるために使用している。Stylusを使用し、翻訳作業に不要なヘッダーやフッター、余白を非表示にして入力・出力エリアを最大化する。

  1. Stylusの管理画面を開き、新しくスタイルを作成する。
  2. 適用先(URL)を「次で始まる」などに設定し、https://chatgpt.com/ja-JP/translate/ を指定する。
  3. 以下のCSSコードを貼り付けて保存する。
@-moz-document url("https://chatgpt.com/ja-JP/translate/") {
  /* 入力欄(textarea / role=textbox)を縦に伸ばす */
  textarea,
  div[role="textbox"]{
    min-height: 80vh !important;
    height: 80vh !important;
  }

  /* 右側の出力表示(よくある “prose” 表示)を縦に伸ばす */
  .prose,
  [class*="prose"]{
    max-height: 80vh !important;
    overflow: auto !important;
  }
      
  /* ===== translate ページの上下余白を詰める ===== */

  /* 1) ヘッダー本体を消す */
  #contentful-header{
    display: none !important;
  }

  /* 2) “ヘッダー高さ前提”のオフセットをゼロにする */
  :root{
    --mkt-header-height: 0px !important;
  }

  /* 3) ヘッダー分のpadding/scroll-marginを付けているクラスを潰す */
  .pt-mkt-header-height{
    padding-top: 0 !important;
  }
  .scroll-mt-mkt-header-height{
    scroll-margin-top: 0 !important;
  }

  /* 4) スマホ用の固定メニューを潰す */
  div[style*="background-image:linear-gradient"][class*="fixed"][class*="inset-0"][class*="lg:hidden"]{
    display: none !important;
  }
      
  /* 「ChatGPT を使用して翻訳」の上下余白を消す(h1だけ) */
  @media (min-width: 48rem) {
    h1.text-mkt-h3.py-5.text-center.md\:py-20 {
      padding-block: 0 !important;
      padding-top: 0 !important;
      padding-bottom: 0 !important;
    }
  }

  /* 翻訳ページの「翻訳に ChatGPT を使う理由」以降の大きいブロックを丸ごと消す */
  div.mt-16.flex.flex-col.overflow-hidden {
    display: none !important;
  }
      
  /* 「ChatGPT で翻訳を開始する」ブロックだけ消す */
  section.bg-token-bg-secondary\/30.dark\:bg-token-bg-secondary.rounded-2xl.py-16.w-\[calc\(100\%-2\*1rem\)\].sm\:w-\[calc\(100\%-2\*2rem\)\].mt-22.sm\:mt-32{
    display: none !important;
  }
      
  /* 「OpenAI / 規約とポリシー」等のフッターブロックを消す */
  footer.bg-token-bg-primary
    > div.max-w-9xl.border-token-border-primary.mx-auto.mb-8.border-t
    > div.px-4.py-8.md\:py-10 {
    display: none !important;
  }
}

Stylusで設定した翻訳窓の高さ調整に関するメモを追加した。これもマニュアルの末尾やStylus設定の直後に配置すると、後からのメンテナンスが容易になる。


補足:翻訳窓(入力・出力欄)の高さ調整メモ

StylusのCSSコード内で指定している 80vh という数値は、「ブラウザ画面の高さの80%」を意味している。 使用しているモニターの解像度やウィンドウサイズに合わせて、この数値を変更することで翻訳窓の高さを自由に調整可能である。

もし画面の下部が見切れてしまったり、逆にもっと広く使いたい場合は、CSSコードの先頭付近にある以下の該当箇所の数値を 70vh や 90vh などに書き換えて保存すればよい。

@-moz-document url("https://chatgpt.com/ja-JP/translate/") {
  /* 入力欄(textarea / role=textbox)を縦に伸ばす */
  textarea,
  div[role="textbox"]{
    min-height: 80vh !important; /* ← ここを調整(例:70vh) */
    height: 80vh !important;     /* ← ここを調整(例:70vh) */
  }

  /* 右側の出力表示(よくある “prose” 表示)を縦に伸ばす */
  .prose,
  [class*="prose"]{
    max-height: 80vh !important; /* ← ここを調整(例:70vh) */
    overflow: auto !important;
  }

Step 3: Tampermonkeyの設定とスクリプト登録 (JS)

Tampermonkeyは特定のWebサイト上で独自のJavaScriptを自動実行できるブラウザ拡張機能である。本環境では、ChatGPTの裏側のシステム(React)に干渉して翻訳先を常に「日本語」へ強制固定し、AHKが確実にテキストを貼り付けられるよう下準備(入力欄のフォーカスと全選択)を行う裏方として機能している。

Reactの内部状態を監視して言語設定を強制的に固定し、AHKからの合図で入力欄を準備するスクリプトを導入する。

  1. Chromeの拡張機能管理画面(chrome://extensions/)を開き、右上の「デベロッパーモード」をONにする。
  2. Tampermonkeyの「詳細」を開き、「ファイルの URL へのアクセスを許可する」をONにする。
  3. Tampermonkeyのダッシュボードから新規スクリプトを追加し、以下のコードを貼り付けて保存する。
// ==UserScript==
// @name         ChatGPT Translate - Select Fix & Focus
// @match        https://chatgpt.com/*/translate*
// @run-at       document-idle
// ==/UserScript==

(() => {
  "use strict";

  const AHK_FOCUS = (e) => e.ctrlKey && e.altKey && (e.key === "f" || e.key === "F");

  // Reactの内部状態を確実に書き換えるための特殊な関数
  function setReactSelect(selectEl, newValue) {
    if (selectEl.value === newValue) return;
    const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, "value").set;
    nativeSetter.call(selectEl, newValue);
    selectEl.dispatchEvent(new Event('change', { bubbles: true }));
  }

  function fixLanguages() {
    const selects = document.querySelectorAll('select');
    if (selects.length < 2) return;

    // value="detect" の選択肢を持っている方が「翻訳元」
    const srcSel = Array.from(selects).find(s => s.querySelector('option[value="detect"]'));
    const dstSel = Array.from(selects).find(s => !s.querySelector('option[value="detect"]'));

    if (srcSel) setReactSelect(srcSel, 'detect');
    if (dstSel) setReactSelect(dstSel, 'ja');
  }

  function findSourceInput() {
    const textareas = Array.from(document.querySelectorAll("textarea"))
      .filter(el => el.getBoundingClientRect().width > 5 && !el.disabled && !el.readOnly);
    if (textareas.length) return textareas[0];

    const edits = Array.from(document.querySelectorAll('[contenteditable="true"]'))
      .filter(el => el.getBoundingClientRect().width > 5);
    if (edits.length) return edits[0];

    return null;
  }

  document.addEventListener("keydown", async (e) => {
    if (!AHK_FOCUS(e)) return;
    e.preventDefault();
    e.stopPropagation();

    fixLanguages();

    let input = null;
    for(let i=0; i<30; i++) {
        input = findSourceInput();
        if(input) break;
        await new Promise(r => setTimeout(r, 50));
    }

    if (input) {
        input.focus();
        if (input.tagName.toLowerCase() === "textarea" || input.tagName.toLowerCase() === "input") {
            input.select();
        } else {
            const range = document.createRange();
            range.selectNodeContents(input);
            const sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
  }, true);

  // SPAの再描画による「スペイン語化」を完封するため、1秒ごとに言語をチェック
  setInterval(fixLanguages, 1000);

})();

Step 4: AutoHotkey (AHK v2) の設定

AutoHotkeyは、Windowsの操作(キーボード入力、マウス操作、ウィンドウ管理など)を高度に自動化できるスクリプト言語である。独自のショートカットキー(Ctrl+Alt+T)を起点として、コピー、PWAの前面化、ペースト、翻訳開始(Enterキー送信)までの一連の動作を1秒足らずで完遂させる役割を担っている。テキストのコピーからPWAの呼び出し、確実なペースト処理とエンターキー送信までを自動化する。

  1. 新規の .ahk ファイルを作成する。
  2. 以下のコードを貼り付けて保存し、スクリプトを実行する。
#Requires AutoHotkey v2.0
#SingleInstance Force
SendMode "Input"
SetTitleMatchMode 2

; ====== 設定 ======
hk := "^!t"       ; ユーザーが押すトリガー
focusHk := "^!f"  ; TMへ送る合図

targetTitle := "ChatGPT Translate"
targetClass := "Chrome_WidgetWin_1"
targetExe   := "chrome.exe"
autoLaunchIfMissing := true
translateUrl := "https://chatgpt.com/ja-JP/translate/" 
chromePath := "" 

Hotkey(hk, DoTranslate)

DoTranslate(*) {
    ; 1) 選択範囲をコピー
    A_Clipboard := ""
    Send "^c"
    if !ClipWait(1.0) {
        ToolTip "コピー失敗(選択範囲なし?)"
        SetTimer () => ToolTip(), -1200
        return
    }

    ; 2) PWAを前面化
    winQuery := targetTitle " ahk_class " targetClass " ahk_exe " targetExe
    if !WinExist(winQuery) {
        if autoLaunchIfMissing {
            if (chromePath != "")
                Run Format('"{1}" --app="{2}"', chromePath, translateUrl)
            else
                Run Format('chrome.exe --app="{1}"', translateUrl)
            WinWait(winQuery, , 8)
        } else {
            return
        }
    }

    WinActivate(winQuery)
    WinWaitActive(winQuery, , 3)

    ; 起動・描画待ち
    Sleep 600

    ; 3) リトライループ(初回空振り対策)
    Loop 2 {
        Send focusHk      ; TMにフォーカス&全選択を指示
        Sleep 200

        Send "^v"         ; ネイティブ貼り付け
        Sleep 150
        
        Send "{Enter}"    ; 翻訳を開始させるエンターキー
        Sleep 200
    }
}