# パワポを自動翻訳する | LittleVoice-g-string

LittleVoice-g-string

苦しむために生きないであなた自身を愛してくれ

パワポを自動翻訳する

課題紹介

業務でよく報告書を翻訳するので、いつもDeepLを使っていますが、DeepLは最近翻訳したパワポをダウンロードしたら、修復エラー出てしまうので、Google translate apiを呼び出して、パワポの自動翻訳を実現したいと思います。

参考文献

  1. Pythonでパワポスライドを自動翻訳する
  2. 3 分で作る無料の翻訳 API with Google Apps Script
  3. deep-translator_github
  4. python-pptx

① Google Apps Scriptによる翻訳APIの利用

  1. 簡単にapiを作成

こちらの記事3 分で作る無料の翻訳 API with Google Apps Scriptを参考し、無料の翻訳apiを作成しました。
下記のコードをGoogle Apps Scriptにて入力し、ウェブアプリケーションとして導入を選択したら
https://script.google.com/macros/s/[api_key]/execのようなURLが生成してくれる。こちらを使って翻訳を行います。
https://script.google.com/macros/s/[api_key]/exec?text=こんにちは&source=ja&target=enで結果があったら、apiの作成が成功できたといえます。

1
2
3
4
5
function doGet(e) {
var p = e.parameter;
var translatedText = LanguageApp.translate(p.text, p.source, p.target);
return ContentService.createTextOutput(translatedText);
}
  1. PythonからAPIにアクセス
    requestsを使ってAPIのURLにアクセスし、英訳を試してみました。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def translate(str_in, source='ja', target= 'en'):
    url="https://script.google.com/macros/s/"
    url += "[api_key]"
    url += "/exec?text=" + str_in
    url += "&source=" + source
    url += "&target=" + target
    rr = requests.get(url)

    return rr.text

② deep-translatorを使って複数の翻訳ツールを利用する

  1. deep-translatorはgoogle_translator,Linguee translator,DeepL等複数翻訳ツールを対応してくれる複合型モジュールです。いくつかの翻訳ツールを比較したい場合、かなり便利なモジュールですね。本記事では、google translate、Linguee translator、DeepLを使いたいと思います。
  • DeepL apiは現在アクセスできないため、とりあえずやめました。
  • google_translatorを使用する場合、5000文字の制限があります。
    • text must be a valid text with maximum 5000 character,otherwise it cannot be translated
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      #google translate
      def Google_Translator(str_in):
      from_language = json_inputs['source_Google']
      to_language = json_inputs['target_Google']
      translated = GoogleTranslator(source = from_language,target = to_language).translate(text=str_in)
      return translated
      #Linguee translator
      def Linguee_Translator(str_in):
      from_language = json_inputs['source_Linguee']
      to_language = json_inputs['target_Linguee']
      translated = LingueeTranslator(source = from_language,target = to_language).translate(str_in)
      return translated
      #DeepL
      #DeepLは今有料ユーザーにしかapiを提供しないため、auth_keyは必要です。
      with open(os.getcwd() + '/config.json') as j:
      config = json.load(j)
      def DeepL_Translator(str_in):
      from_language = json_inputs['source_deepl']
      to_language = json_inputs['target_deepl']
      translated = DeepL(config['auth_token']).translate(source=from_language,target=to_language,text=str_in)
      return translated
  1. 各翻訳ツール対応している言語、及び言語の省略をdeep-translatorでそのまま調べます。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    langs_list = GoogleTranslator.get_supported_languages(as_dict=True) 
    google_lang = pd.DataFrame(langs_list.keys(),langs_list.values()
    ).reset_index().rename(columns= {'index':'言語省略',0:'言語'})
    print(google_lang[google_lang['言語']=='english'])
    print(google_lang[google_lang['言語']=='japanese'])
    print(google_lang[google_lang['言語']=='chinese (simplified)'])
    print(google_lang[google_lang['言語']=='chinese (traditional)'])
    # 言語省略 言語
    21 en english
    言語省略 言語
    45 ja japanese
    言語省略 言語
    14 zh-cn chinese (simplified)
    言語省略 言語
    15 zh-tw chinese (traditional)

③ パワポを翻訳

  1. パワポの解析はslide→shape→paragraph→runに基づいて行いました。
  • 本記事では最小単位であるrun.textごとのテキスト翻訳とrun一個上のparagraph.textごとのテキスト翻訳を行いました。
  • notesの部分はslide.notes_slide.notes_text_frame.textでテキスト本文を指定できます。
  • テーブルの部分はshape.shape_type == MSO_SHAPE_TYPE.TABLEを使ってテーブルを絞って、row→cellという構造に基づいてcell.text_frame.textを用いて本文を指定できます。
  • Google_Translatorは空白とNoneTypeに対応しないため、if paragraph.text.isspace() != True and paragraph.text != ''を使ってそれらを除外しました。
  • #が入ってしまうと、通信エラーが生じてしまうらしいので、str_in = title.text.replace('#','')を使って翻訳に投げるテキストに入っている#を除外しました。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    json_inputs = {
    'file_path':ファイルのパスをここに記入,
    'Google': False, #deep-translator
    'translate': True, #api with Google Apps Script
    'Linguee':False, #deep-translator
    'DeepL':False, #deep-translator
    'notes':True,
    'source_Google':'ja',
    'target_Google':'en',
    'source_translate':'ja',
    'target_translate':'en',
    'source_Linguee':'ja',
    'target_Linguee':'en',
    'source_deepl':'JA',
    'target_deepl':'EN'
    }

    path_to_presentation = json_inputs['file_path']
    prs = Presentation(path_to_presentation)

    def ppt_translate(prs):
    for ns,slide in enumerate(prs.slides): #slide
    title = slide.shapes.title
    if title:
    if json_inputs['Google']:
    str_in = title.text.replace('#','')
    title_out = Google_Translator(str_in)
    sleep(1.5)
    title.text = title_out
    elif json_inputs['translate']:
    str_in = title.text.replace('#','')
    title_out = translate(str_in)
    sleep(1.5)
    title.text = title_out
    elif json_inputs['deepL']:
    str_in = title.text.replace('#','')
    title_out = deepl_translate(str_in)
    sleep(1.5)
    title.text = title_out
    if json_inputs['notes']: #notes
    if slide.has_notes_slide:
    if json_inputs['Google']:
    str_in = slide.notes_slide.notes_text_frame.text.replace('#','')
    notes_out = Google_Translator(str_in)
    sleep(1.5)
    slide.notes_slide.notes_text_frame.text = notes_out
    elif json_inputs['translate']:
    str_in = slide.notes_slide.notes_text_frame.text.replace('#','')
    notes_out = translate(str_in)
    sleep(1.5)
    slide.notes_slide.notes_text_frame.text = notes_out
    elif json_inputs['deepL']:
    str_in = slide.notes_slide.notes_text_frame.text.replace('#','')
    notes_out = deepl_translate(str_in)
    sleep(1.5)
    slide.notes_slide.notes_text_frame.text = notes_out
    for nsh,shape in enumerate(slide.shapes):
    if shape.shape_type == MSO_SHAPE_TYPE.TABLE: #table
    for row in shape.table.rows:
    for cell in row.cells:
    if json_inputs['Google']:
    str_in = cell.text_frame.text.replace('#','')
    cell_out = Google_Translator(str_in)
    sleep(1.5)
    cell.text_frame.text = cell_out
    elif json_inputs['translate']:
    str_in = cell.text_frame.text.replace('#','')
    cell_out = translate(str_in)
    sleep(1.5)
    cell.text_frame.text = cell_out
    elif json_inputs['deepL']:
    str_in = cell.text_frame.text.replace('#','')
    cell_out = deepl_translate(str_in)
    sleep(1.5)
    cell.text_frame.text = cell_out
    #paragraph.textごとのテキスト翻訳
    if shape.has_text_frame:
    for np,paragraph in enumerate(shape.text_frame.paragraphs):
    if json_inputs['Google']:
    # Google_Translatorは空白とNoneTypeに対応しないため、それらを除外する
    if paragraph.text.isspace() != True and paragraph.text != '':
    str_in = paragraph.text.replace('#','')
    str_out = Google_Translator(str_in)
    paragraph.text = str_out
    elif json_inputs['translate']:
    str_in = paragraph.text.replace('#','')
    str_out = translate(str_in)
    paragraph.text = str_out
    elif json_inputs['deepL']:
    str_in = paragraph.text.replace('#','')
    str_out = deepl_translate(str_in)
    paragraph.text = str_out
    #run.textごとのテキスト翻訳
    # for rs,run in enumerate(paragraph.runs):
    # if json_inputs['Google']:
    # str_in = run.text.replace('#','')
    # str_out = Google_Translator(str_in)
    # run.text = str_out
    # elif json_inputs['translate']:
    # str_in = run.text.replace('#','')
    # str_out = translate(str_in)
    # sleep(1.5)
    # run.text = str_out
    # elif json_inputs['deepL']:
    # str_in = run.text.replace('#','')
    # str_out = deepl_translate(str_in)
    # sleep(1.5)
    # run.text = str_out
    prs.save(os.path.split(path_to_presentation)[1].split('.pptx')[0]+'_en.pptx')
    print(f'第{ns}スライドは完了')
  • run.textを単位とparagraph.textを単位に翻訳した結果は以下の通りです。
  • paragraph.textのほうが若干ナチュラルと気がしますが、run.textの場合はちゃんとフォントサイトや、太字等を原本のままで保持できるので、場合によって取捨選択できたらと思います。
run.text paragraph.text

④ Excel,csv,txtファイルを翻訳

  • Excel,csv,txtの場合だとずっと簡単ですが、一応練習するため、以下の関数を書いてみました。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def excel_excel_txt(path):
    if os.path.splitext(path)[1] == '.csv':
    df = pd.read_csv(path,index_col=0)
    elif os.path.splitext(path)[1] == '.xlsx':
    df = pd.read_excel(path,header=None,index_col=0)
    elif os.path.splitext(path)[1] == '.txt':
    df = pd.read_table(path,header=None)
    for i in df.columns:
    if json_inputs['Google']:
    i_en = [Google_Translator(str(m)) for m in df[i]]
    sleep(1.5)
    df[f'{i}_en'] = i_en
    elif json_inputs['translate']:
    i_en = [translate(str(m)) for m in df[i]]
    sleep(1.5)
    df[f'{i}_en'] = i_en
    elif json_inputs['DeepL']:
    i_en = [DeepL_Translator(str(m)) for m in df[i]]
    sleep(1.5)
    df[f'{i}_en'] = i_en
    df.to_csv(os.path.splitext(json_inputs['file_path'])[0]+'.csv',encoding='utf-8-sig')
    return df

    ⑤ Next Action

  • 上記コードで実現できたことをWeb ツールにて実現できたら良いかと思います。
  • 現状パワポ内の画像テキストの翻訳はまだできていないので、今後画像の部分もクリアしたいです。
  • word、pdf系のファイルもこちらに取り入れたいと思います。

Appendix

  1. 中国繁體語⇔簡体語
  • deep-translatorを利用する場合
    1
    2
    3
    4
    5
    text = '统计学入门'

    result = GoogleTranslator(source = 'auto',target = 'zh-tw').translate(text=text)
    result
    #'統計學入門'
  • openccを利用する場合
    1
    2
    3
    4
    5
    6
    7
    pip install opencc-python-reimplemented
    from opencc import OpenCC
    cc = OpenCC('S2t')
    to_convert = '统计学入门'
    converted = cc.convert(to_convert)
    converted
    #'統計學入門'
  1. 単語の同義語を取得
  • deep-translatorのLingueeTranslatorを使って、単語の同義語を取得できる
  • LingueeTranslator内のreturn_all = Trueにすると、該当単語の翻訳した言語の同義語がいくつか出てくる
    1
    2
    3
    4
    word='面白い'
    translated_linguee = LingueeTranslator(source = 'ja',target = 'en').translate(word,return_all = True)
    translated_linguee
    #['interesting', 'interesting', 'amusing']

Welcome to my other publishing channels