Cloud Translation API で Word をレイアウトを保ったまま翻訳する

Cloud Translation API で Word をレイアウトを保ったまま翻訳する

Cloud Translation API のドキュメント翻訳機能を使って、Word(DOCX)をレイアウトを保ったまま英語に翻訳できるか、実際に試してみました。テキストボックスの扱いや用語集での固有名詞の固定方法も含めて検証します。
2026.06.03

こんにちは、けーまです。

Word で作った提案書やマニュアルを、レイアウトを保ったまま別の言語に翻訳したい場面はよくあります。
表や図、テキストボックスを使った資料を、見た目を崩さずに英語化したい、といった要件です。
ただ、Word からテキストだけを抜いて翻訳すると、表や段組みが崩れて元の見た目が再現できません。
さらに、製品名やキャラクター名のような独自の固有名詞は、毎回決まった訳に揃えたいことも多いです。

以前、Google Cloud の Cloud Translation API が持つ Document Translation 機能で PDF を翻訳する記事を書きました。
そこで本記事では、同じ機能を使って Word(DOCX)をファイルのまま翻訳し、レイアウトがどこまで保たれるのか、テキストボックスや表はどう扱われるのか、用語集で独自用語を固定できるのかを、実際に動かして確かめました。

本記事は、ドキュメント翻訳を形式別に検証するシリーズの Word 編です。
仕様や挙動は Google Cloud の公式ドキュメントで確認し、該当箇所を引用しています。
そのうえで、公式の記載どおりになるかを実際に動かして検証しました。

シリーズ記事

形式 記事
PDF 編 Cloud Translation API で PDF をレイアウトを保ったまま翻訳する
Word 編(本記事)

対象読者:Word 文書の翻訳自動化を検討している方

1. 結論:公式ドキュメントの記載と実証結果

急いで結論だけ知りたい方向けに、Word(DOCX)の翻訳について、Google Cloud 公式ドキュメントの記載と、実際に翻訳して確認した結果を先にまとめます。
詳しい手順や翻訳前後の画像は §3 以降にあります。

観点 公式ドキュメントの記載 実証結果(本記事で確認)
本文・書式・レイアウト フォーマットとレイアウトを保持して翻訳する 見出し・本文・表・多段組・色つき文字・ヘッダ/フッタが保持された
テキストボックス内のテキスト 翻訳されず原文のまま残る 記載どおり、日本語のまま残った(図形・角丸・楕円すべて)
画像内の文字 (記載なし) 翻訳されなかった(テキストデータではないため)
用語集(独自用語の固定) 用語集で訳語を固定できる 本文・表ともにほぼ統一された
用語集を作れるリージョン カスタムリソースは us-central1 のみ us-central1 で作成(東京リージョン不可)

公式の記載は、各セクションで該当箇所を引用しています。
一番のポイントは、テキストボックスの中身が翻訳されない点です。
公式に明記された仕様で、実際にそのとおりでした。
急ぎの方は、この表と §3 の画像を見れば概要がつかめます。

2. Cloud Translation API のドキュメント翻訳とは

Cloud Translation API には、テキストを翻訳する機能とは別に、ファイルをそのまま翻訳する Document Translation という機能があります。
PDF や DOCX をファイルごと渡すと、書式やレイアウトを保ったまま翻訳して返してくれます。
この機能の概要、Basic(v2)と Advanced(v3)の違い、同期とバッチの使い分け、認証(API キーは使えず ADC かサービスアカウント)については、前回の PDF 編で詳しく扱いました。

引用元: DevelopersIO: Cloud Translation API で PDF をレイアウトを保ったまま翻訳する

対応している入力形式は、PDF だけでなく Word・PowerPoint・Excel の各形式(旧形式も含む)です。
本記事で扱う Word は、DOCX の1つです。

入力形式 MIME タイプ 出力形式
DOCX application/vnd.openxmlformats-officedocument.wordprocessingml.document DOCX
DOC application/msword DOC または DOCX

引用元: 公式ドキュメント: Translate documents | Google Cloud

Word については、公式ドキュメントに重要な注意点が1つ明記されています。
テキストボックス内のテキストは翻訳されず、原文のまま残る、というものです。

Content inside text boxes aren't translated and remain in the source language.

引用元: 公式ドキュメント: Translate documents | Google Cloud

この挙動は本記事の検証でもそのまま再現しました(§4 で確認します)。

3. 検証の準備

3.1 前提環境

本記事の検証は、次の環境で動かしました。

項目 今回の環境
OS macOS
Python 3.12 系(この中に venv を作成)
Google Cloud SDK(gcloud) 565.0.0
google-cloud-translate 3.26.0
GCP プロジェクト 個人プロジェクト(課金を有効化済み)

加えて、次の準備ができている前提で進めます。

  • 課金を有効にした Google Cloud プロジェクトがあること
  • gcloud コマンドが使えること
  • 翻訳や用語集を操作できる IAM 権限があること(roles/cloudtranslate.editor 相当。用語集を使う場合は対象バケットへの書き込み権限も)

3.2 サンプル文書(オリジナルの架空アニメ)

検証用に、架空アニメ「星霊物語 ルミナ・クロニクル」の公式設定資料という体で、Word 文書を Claude に作ってもらいました。
実在の作品・人物・団体とは一切関係のない、完全オリジナルの内容です。
PDF 編と同じ世界観で、固有名詞(造語)も揃えています。

この資料に、崩れやすい要素と翻訳で確かめたい要素を意図的に詰め込んでいます。

  • 見出し、本文、複数の表(星霊図鑑・用語解説・放送情報)、2段組(多段組)
  • 色つき文字、ハイパーリンク、ヘッダ/フッタ
  • 角丸・四角・楕円のテキストボックス(図形)に入れた文章(テキストボックスの翻訳挙動の確認用)
  • 星霊共鳴進化雷狼ボルテ のような造語(用語集の確認用)

今回翻訳した Word は、全4ページの構成です。
翻訳前の全ページを載せておきます。

翻訳前のWord 1ページ目(日本語)
翻訳前のWord 1ページ目:タイトル、キービジュアル画像、オレンジ色の枠のテキストボックス(「みどころ」)、2段組の世界設定、星霊図鑑の表

翻訳前のWord 2ページ目(日本語)
翻訳前のWord 2ページ目:星霊図鑑の表の続き、用語解説の表、あらすじ

翻訳前のWord 3ページ目(日本語)
翻訳前のWord 3ページ目:人気ランキングのグラフ画像、制作メモのテキストボックス、四角・角丸・楕円の図形

翻訳前のWord 4ページ目(日本語)
翻訳前のWord 4ページ目:放送情報の表、注釈、フッタ

3.3 環境のセットアップ

準備の流れは PDF 編と同じです。

まず、Cloud Translation API を有効化します。

gcloud services enable translate.googleapis.com --project <YOUR_PROJECT_ID>

次に認証です。
Document Translation(v3 / Advanced)は API キーに対応していないため、ADC(Application Default Credentials)を使います。

gcloud auth application-default login
gcloud auth application-default set-quota-project <YOUR_PROJECT_ID>

仮想環境(venv)を作って、その中にライブラリを入れます。

python3 -m venv .venv
source .venv/bin/activate
pip install google-cloud-translate

4. Word(DOCX)を翻訳してみる

ファイルのバイト列をそのままリクエストに載せる、同期翻訳で試します。
翻訳に使うスクリプトは、拡張子から MIME タイプを判定して PDF・DOCX・XLSX・PPTX を同じコードで扱えるようにしたものです。
PDF 専用のオプション(ネイティブ判定や傾き補正)は、PDF のときだけ付与し、Office 形式では付けません。

translate_document_handson.py の全文(クリックすると展開します)
from __future__ import annotations

import argparse
import time
from pathlib import Path

from google.cloud import translate_v3 as translate

# 用語集・カスタムモデルは us-central1 に置く必要がある。
DEFAULT_LOCATION = "us-central1"

# 拡張子 → MIME タイプ(Document Translation 対応形式)
MIME_BY_SUFFIX = {
    ".pdf": "application/pdf",
    ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
    ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}

def mime_for(path: str) -> str:
    """入力ファイルの拡張子から MIME タイプを返す。未対応拡張子は ValueError。"""
    suffix = Path(path).suffix.lower()
    if suffix not in MIME_BY_SUFFIX:
        raise ValueError(f"未対応の拡張子: {suffix}(対応: {', '.join(MIME_BY_SUFFIX)})")
    return MIME_BY_SUFFIX[suffix]

def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Cloud Translation API ドキュメント翻訳(同期)")
    p.add_argument("--project", required=True, help="GCP プロジェクト ID")
    p.add_argument("--input", required=True, help="入力ファイルパス")
    p.add_argument("--output", required=True, help="出力ファイルパス(用語集なしの結果)")
    p.add_argument("--source", default="ja", help="原文言語コード(既定: ja)")
    p.add_argument("--target", default="en", help="訳文言語コード(既定: en)")
    p.add_argument("--location", default=DEFAULT_LOCATION, help=f"ロケーション(既定: {DEFAULT_LOCATION})")
    p.add_argument("--glossary-id", default=None, help="用語集 ID(指定時は用語集あり/なし両方を出力)")
    return p.parse_args()

def build_request(args: argparse.Namespace, content: bytes) -> dict:
    """translate_document のリクエスト辞書を組み立てる。

    用語集を指定する場合は原文言語の指定が必須(公式仕様)。
    """
    parent = f"projects/{args.project}/locations/{args.location}"
    mime_type = mime_for(args.input)
    request: dict = {
        "parent": parent,
        "source_language_code": args.source,
        "target_language_code": args.target,
        "document_input_config": {"content": content, "mime_type": mime_type},
    }
    if args.glossary_id:
        glossary_path = (
            f"projects/{args.project}/locations/{args.location}"
            f"/glossaries/{args.glossary_id}"
        )
        request["glossary_config"] = translate.TranslateTextGlossaryConfig(glossary=glossary_path)
    return request

def write_bytes(path: str, data: bytes) -> None:
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    Path(path).write_bytes(data)

def main() -> None:
    args = parse_args()
    content = Path(args.input).read_bytes()
    print(f"入力: {args.input}{len(content):,} bytes, {args.source}{args.target})")

    client = translate.TranslationServiceClient()
    request = build_request(args, content)

    started = time.perf_counter()
    response = client.translate_document(request=request)
    elapsed = time.perf_counter() - started

    base = response.document_translation
    write_bytes(args.output, base.byte_stream_outputs[0])
    print(f"処理時間: {elapsed:.2f} 秒")
    print(f"出力(用語集なし): {args.output}{len(base.byte_stream_outputs[0]):,} bytes)")

    # 用語集あり: 1回の呼び出しで glossary_document_translation に別出力が返る
    if args.glossary_id and response.glossary_document_translation.byte_stream_outputs:
        out = Path(args.output)
        glossary_out = str(out.with_name(f"{out.stem}_glossary{out.suffix}"))
        write_bytes(glossary_out, response.glossary_document_translation.byte_stream_outputs[0])
        print(f"出力(用語集あり): {glossary_out}")

if __name__ == "__main__":
    main()

まずは用語集なしで、Word を日本語から英語に翻訳します。

python translate_document_handson.py \
    --project <YOUR_PROJECT_ID> \
    --input hoshirei_ja.docx \
    --output hoshirei_en.docx
# 出力例
入力: hoshirei_ja.docx(125,305 bytes, ja→en)
処理時間: 1.30
出力(用語集なし): hoshirei_en.docx(119,172 bytes)

数ページの DOCX が、1秒台で翻訳されました。
翻訳前と翻訳後(用語集なし)を、全4ページぶん並べてみます。

翻訳前後の比較(Word・1ページ目)
1ページ目:左が翻訳前(日本語)、右が翻訳後(用語集なし)。本文・見出し・色つき文字・ヘッダ/フッタは英訳されるが、テキストボックス「みどころ」は日本語のまま残る

翻訳前後の比較(Word・2ページ目)
2ページ目:左が翻訳前、右が翻訳後。星霊図鑑・用語解説の表とあらすじが、レイアウトを保って英訳される

翻訳前後の比較(Word・3ページ目)
3ページ目:左が翻訳前、右が翻訳後。本文は英訳されるが、制作メモや四角・角丸・楕円の図形の中身は翻訳されず日本語のまま残る

翻訳前後の比較(Word・4ページ目)
4ページ目:左が翻訳前、右が翻訳後。放送情報の表と注釈、フッタが英訳される

見出し・本文・色つき文字・2段組・表、そしてヘッダ/フッタまで、レイアウトがほぼそのまま保たれています。

書式の保持で特に目を引いたのは、背景色や文字色の扱いです。
原文で文章の途中から文字の色が変わっている箇所や、背景色が付いた箇所は、翻訳後もその色がそのまま引き継がれていました。
太字で強調されていた部分も、翻訳後にそのまま太字が維持されています。
単にテキストを置き換えるだけでなく、文字単位の書式情報まで保持して翻訳していることが分かります。

一方で、翻訳されなかった要素もはっきりと分かれました。

まず、テキストボックスです。
1ページ目のオレンジ色の枠(「みどころ」)、3ページ目の制作メモのテキストボックスと四角・角丸・楕円の図形は、いずれも中の文章が日本語のまま残っていました。
これは §2 で引用した「テキストボックス内のテキストは翻訳されず原文のまま残る」という公式の記載どおりの挙動です。
なお、PDF 編ではフォントサイズを小さくして翻訳前と同じレイアウトに寄せる挙動でしたが、Word の場合はフォントサイズは変わらず、改ページの位置が多少ずれていました。
ただし、自然な位置で改ページされており、内容上の問題はありませんでした。

もう1つは、画像として貼り付けた棒グラフです。
3ページ目の人気ランキングのグラフは画像として挿入しているため、翻訳の対象にはなりませんでした。
画像内の文字はテキストデータではないので、Document Translation では扱えません。

5. 用語集で独自用語を固定する

固有名詞や独自用語を、決まった訳に固定できるかを確かめます。
用語集を使うには、(1) 原文と訳語の対応を書いた TSV を用意し、(2) それを Cloud Storage に置いて、(3) 用語集リソースを作成します。
作成した用語集を翻訳時に指定すると、登録した語が固定訳に揃います。

5.1 用語集の TSV を用意する

用語集は、原文(日本語)と訳語(英語)をタブ区切りで1行ずつ並べた TSV ファイルとして用意します。
ヘッダー行は不要で、左が原文の造語、右が固定したい訳語です。
今回は、サンプルに散りばめた造語20語を glossary_ja_en.tsv として用意しました。

星霊	Hoshirei
共鳴進化	Reso-Evolution
輝光石	Lumina Shard
雷狼ボルテ	Voltefang
焔狐ココ	Pyrofox Coco
水亀アクオ	Aquortle
草鹿リーフィ	Leafawn
輝竜ルミナ	Lumidragon
月読の祠	Moonread Shrine
守護者	Warden
星導士	Starwright
星霊守護協会	Hoshirei Warden Guild
共鳴値	Reso-Value
共鳴の灯	Resonance Flame
絆ゲージ	Bond Gauge
星霊酔い	Hoshirei-sickness
星霊図鑑	Hoshirei Codex
ルミナ群島	Lumina Archipelago
七つの祠	Seven Shrines
共鳴結界	Reso-Barrier

5.2 TSV を Cloud Storage に置いて用語集リソースを作成する

用語集リソースは TSV を直接アップロードするのではなく、いったん Cloud Storage に置き、その GCS の URI を指定して作成します。
バケットは用語集と同じ us-central1 に作っておきます。

# バケットを作成(既にあれば不要)
gcloud storage buckets create gs://<YOUR_BUCKET> \
    --project <YOUR_PROJECT_ID> --location us-central1

# TSV をアップロード
gcloud storage cp glossary_ja_en.tsv \
    gs://<YOUR_BUCKET>/glossaries/glossary_ja_en.tsv

次に、アップロードした TSV から用語集リソースを作成します。
作成は長時間オペレーション(LRO)なので、クライアントライブラリから実行して完了を待ちます。
次のコードを setup_glossary.py として保存し、§3.3 の venv のまま実行します。

setup_glossary.py の全文(クリックすると展開します)
from google.cloud import translate_v3 as translate

PROJECT_ID = "<YOUR_PROJECT_ID>"
LOCATION = "us-central1"   # 用語集は us-central1 のみ
GLOSSARY_ID = "hoshirei-ja-en"
INPUT_URI = "gs://<YOUR_BUCKET>/glossaries/glossary_ja_en.tsv"

client = translate.TranslationServiceClient()
name = client.glossary_path(PROJECT_ID, LOCATION, GLOSSARY_ID)
glossary = translate.Glossary(
    name=name,
    # 単方向(ja→en)の用語集
    language_pair=translate.Glossary.LanguageCodePair(
        source_language_code="ja", target_language_code="en"
    ),
    input_config=translate.GlossaryInputConfig(
        gcs_source=translate.GcsSource(input_uri=INPUT_URI)
    ),
)
parent = f"projects/{PROJECT_ID}/locations/{LOCATION}"
operation = client.create_glossary(parent=parent, glossary=glossary)
result = operation.result(180)   # 完了を最大180秒待つ
print(f"作成完了: {result.name}(エントリ数: {result.entry_count})")
python setup_glossary.py
# 出力例
作成完了: projects/.../locations/us-central1/glossaries/hoshirei-ja-en(エントリ数: 20)

公式ドキュメントにも、カスタムリソースは us-central1 を使う必要があると明記されています。

Note: All of your resources in a single request to Cloud Translation - Advanced must have the same location. Currently, only global and us-central1 locations are supported. For all custom resources—AutoML models, glossaries, long-running-operations—you must use us-central1.

引用元: 公式ドキュメント: Migrate to Cloud Translation - Advanced (v3) | Google Cloud

5.3 用語集あり/なしを比べる

作成した用語集を指定して翻訳します。
--glossary-id に 5.2 で作成した用語集 ID を渡すと、1回の応答に「用語集なし」と「用語集あり」の両方の結果が返ります。

python translate_document_handson.py \
    --project <YOUR_PROJECT_ID> \
    --input hoshirei_ja.docx \
    --output hoshirei_en.docx \
    --glossary-id <YOUR_GLOSSARY_ID>

まず用語集なしの結果を見ると、固有名詞の訳がかなりバラついていました(Claude調べ)。
原文に26回出てくる 星霊 を、用語集なしの英訳で数えると、主な訳語の内訳は次のとおりでした。

星霊 の訳(用語集なし) 出現回数
Star Spirit 8
star spirits 6
Celestial Spirit 3
star spirit 3
その他の表記 数件

同じ単語なのに、大文字小文字や単複も含めて何通りにも分かれてしまいました。
機械翻訳は文脈ごとに最適な訳を選ぶので、固有名詞をそろえる仕組みがないとこうなります。

用語集ありに切り替えると、これがきれいに統一されました。
訳が実際に変わったのは、固有名詞が多く出るタイトル(1ページ目)と用語解説の表(2ページ目)です。

用語集なし/ありの比較(Word・1ページ目)
1ページ目:左が用語集なし(タイトルが「Star Spirit Story」、星霊 が Star Spirit と訳が揺れる)、右が用語集あり(「Hoshirei Story」に固定され 星霊 が Hoshirei に統一される)

用語集なし/ありの比較(Word・2ページ目)
2ページ目:左が用語集なし、右が用語集あり。用語解説の表で、共鳴進化 が Reso-Evolution、星導士 が Starwright、共鳴結界 が Reso-Barrier と、登録した造語がまとめて固定訳になる

タイトルが「Star Spirit Story」から「Hoshirei Story」に変わり、4通りに割れていた 星霊Hoshirei に統一されました。
他の造語も、守護者Warden共鳴進化Reso-Evolution輝光石Lumina Shard雷狼ボルテVoltefang と、登録したとおりの訳に固定されています。
用語集は本文だけでなく表のセルにも適用されました(テキストボックスは元から翻訳対象外なので、用語集の対象にもなりません)。

ただ、用語集ありの出力を全文で調べてみると、完全に100%ではありませんでした(Claude調べ)。
星霊 のうち1か所だけ、Hoshirei に固定されず star spirits のまま残っていました。
本文の「Hoshirei become Warden who form bonds with these star spirits」では、同じ文の中で 星霊Hoshirei に、守護者Warden に固定されているのに、もう1つの 星霊 だけ star spirits のまま残っています。
用語集は出現箇所ごとに一致を見て適用するため、前後の文脈によって、同じ語でもまれに取りこぼすことがあるようです。
とはいえ大半は固定できているので、「ほぼ統一されるが、取りこぼしがゼロではない」というのが実際のところでした。

6. 料金と処理時間

ドキュメント翻訳の料金は、使う翻訳モデルによって単価が変わります。
標準の NMT モデルの場合、ドキュメント翻訳はページ単位です。

項目 単価
NMT ドキュメント翻訳 0.08 ドル / ページ

引用元: 料金ページ: Pricing | Google Cloud

今回のサンプル(数ページの DOCX)は、用語集あり/なしを合わせても 1 ドルに満たない規模でした。
処理時間は実測で約 1〜2 秒でした。

7. 帰属表示(Machine Translated by Google)について

PDF 編では、翻訳後の PDF の左上に「Machine Translated by Google」という帰属表示が入りました。
今回の Word(DOCX)では、同じく帰属表示を指定していないにもかかわらず、翻訳後のファイルにこの表示は見当たりませんでした。
PDF と Office で同じ条件なのに表示の有無が違ったので、仕様を確認しました。

帰属表示の文言は、API のリクエストで customizedAttribution として指定でき、指定しない場合のデフォルトが「Machine Translated by Google」です。
このフィールドは PDF 専用ではなく、translateDocument(ドキュメント翻訳)全体に共通のものです。

customizedAttribution string

Optional. This flag is to support user customized attribution. If not provided, the default is Machine Translated by Google. Customized attribution should follow rules in https://cloud.google.com/translate/attribution#attribution_and_logos

引用元: API リファレンス: Method: projects.locations.translateDocument | Google Cloud

ここで注意したいのは、customizedAttribution が説明しているのは「帰属表示の文言」だけで、その文言がどの形式の出力に、どう反映されるか(ファイルに焼き込まれるか)は公式ドキュメントに明記が見当たらなかった点です。
形式ごとに帰属表示の有無が変わる理由は、公式には確認できませんでした。
実際に動かした範囲では、帰属表示は PDF の出力に焼き込まれ、Office 形式(DOCX/XLSX/PPTX)の出力には入りませんでした。
PDF はレイアウトを保つために訳文をページ上に重ねて再構成する形式で、編集可能な Office 形式とは出力の作られ方が違うため、と推測されますが、公式の記載で裏づけられたものではありません。

もう1点、別の軸として押さえておきたいのが、ブランドガイドライン上の明示義務です。
これは「出力ファイルに焼き込まれるか」とは別で、翻訳結果を利用者に見せるときは、形式にかかわらず機械翻訳であることを明示するよう求めています。

Whenever you display translation results from Google Translate directly to users, you must make it clear to users that they are viewing automatic translations from Google Translate using the appropriate text or brand elements.

引用元: ブランドガイドライン: Attribution requirements | Google Cloud

つまり、Office 形式の出力に帰属表示が焼き込まれないこと自体は問題ありませんが、翻訳結果を公開・配布する場面では、形式にかかわらず機械翻訳であることを明示する責任が利用者側にあります。

8. まとめ

Cloud Translation API の Document Translation は、Word(DOCX)をファイルのまま渡すだけで、本文・表・多段組・色・ヘッダ/フッタのレイアウトを保って翻訳でき、用語集を使えば固有名詞の訳もほぼ固定できました。
公式ドキュメントに明記されているとおり、テキストボックスや図形の中身は翻訳されず日本語のまま残ります。
翻訳が必要な文章はテキストボックスではなく本文や表に入れておくことを考慮した方が良さそうです。

なお、このテキストボックスの扱いは形式によって変わります。
今後、 Excel 編・PowerPoint 編についてもブログを書きますのでご覧ください。

本記事が、Word 文書の翻訳自動化を考えている方の参考になればうれしいです。

参考

この記事をシェアする

関連記事