外国の方とのZoomリモート会議に参加したよ、多国語会議で共有メモの翻訳自動化で改善してみる：（Web翻訳サービスとPowerShell Seleniumを使ってConfluenceミーティング議事録上でやってみます）

先日、以下の記事で紹介させてもらいましたが、
ベトナムの方と、私たちのチーム(日本人)とで、日本語やベトナム語が入れ替わり話される会議に参加しています。

https://qiita.com/k-hideo/items/b3c0ba1beaad56e9099e


前回は、Zoomのホワイトボードを活用して議事トピックを日本語で共有しつつ、RPA SikulixとWeb翻訳を活用して、英語等に翻訳することをやってみました。

ちょっと動作や共有が遅いことや、使い勝手がもう一つということで、
今度は、Zoomのホワイトボードの代わりに、Confluenceのページ編集画面を使って挑戦してみます。
（まだ一部だけ作成して、簡単なテストで動作確認しただけです。これから実際に活用して改善しようかと思っています）

Confluenceのミーティング議事録に議論の内容をテキストで列挙していき、
PowerShellとSeleniumを使って、議論のテキストをWeb翻訳かけて、
ベトナム語や英語にして、ミーティング議事録に列挙してみました。


以下のgifイメージのように動作します。（左側が自動翻訳で、右側に自分で会議内容を手入力しています）

![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943678/4082eccd-90d9-65f9-d612-dc33c28cbc16.gif)

最終的に以下のイメージのようになりました。
![Confluence_Translate_text_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943678/716c90d5-d029-102f-8380-3201df4e2312.jpeg)


大まかな構成は以下のようになっています。

![Confluence_Translate_arch_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/943678/a14a705b-44f8-68fe-7393-964ccb54d988.jpeg)




今回もネット上の情報を拝見させてもらいました。以下のページを参考にさせてもらいました。
（情報提供されている皆さん、いつもありがとうございます。
　色々と検索して参照しましたが、載せれていないページが多々あります。すみませんM(_)M）

* PowerShell Selenium
   * [【PowerShell】Seleniumを導入してみる](https://kojimanotech.com/2020/05/19/222/)
   * [GitHub adamdriscoll/selenium-powershell](https://github.com/adamdriscoll/selenium-powershell)
* Confluence ミーティング議事録
   * [Confluenceのテンプレート紹介② ミーティング議事録](https://blog.innoov.io/confluence-template-2)
* Selenium IDE
   * [Selenium IDEとは](https://qiita.com/jun2014/items/bf6b87699b7622c91326)


今回の内容の前提として、以下がありますので、ご注意ください。

 * 構成
  * PC
     * OS: Windows 10 64bit
     * SW
         * PowerShell 5.1.18362.1801
         * PowerShell Selenium
         * [Google Chrome](https://www.google.com/intl/ja_jp/chrome/) 96.0.4664.45
         * [Google Chrome拡張機能Selenium IDE](https://chrome.google.com/webstore/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd) 3.17.2
         * [Google Chrome Driver](https://chromedriver.chromium.org/downloads) 96.0.4664.45
 * 2021年12月に調べたり動作確認した内容です。


では、以下の各章の流れで、活用ツールとやったこと等を、順に紹介させてもらいます。



#1. Confluenceのミーティング議事録テンプレートについて

Confluenceに用意されているミーティング議事録という、議事録用のテンプレートを活用しています。

このテンプレートの中に、「Discussion topics」という議論用の表があるので、
そこのNotes列に、英語や日本語、ベトナム語で、話した内容を記載して、みんなの認識を合わせるために活用しようと思います。


Confluenceの議事録ページは、編集モードにすると、複数のユーザで同時編集が可能で、他の人の編集内容も参照できます。
また、画像イメージも張り付けることもできるので、Zoomのホワイトボードで出来なかったネット上のイメージを張り付けて共有する、といったことも可能です。

Atlassian社のJira SoftwareやBitBucketと合わせて、Confluenceも活用することになったので、今回の構成でやってみようと思いました。


あと、気になったことがありましたので、Atlassian社の方に相談の確認をしました。

規約に以下のような記述があります。
「リバースエンジニアリング」がNGということで、Selenium等のツールを活用して翻訳を自動化してよいか、わかりませんでしたので、
問い合わせを行ったところ、OKでしたので、進めることにしました。

https://www.atlassian.com/ja/legal/cloud-terms-of-service

> ３. お客様のクラウド製品サブスクリプションの内容および制限
> 3.3.制限: 本規約において別途明示的に許可されている場合を除き、お客様は、
> ...(中略)
> (f) 適用法で明示的に認められている場合かつ当社に事前に通知をしている場合を除き、クラウド製品をリバース エンジニアリング、逆アセンブル、逆コンパイル、変換またはその他の方法でソースコード、内在するアイデア、アルゴリズム、ファイル形式およびクラウド製品向けの非公開 API を取得および導出しようとせず、
> ...(略)


「Discussion topics」の表のセルのxpathを調べるのにChromeの拡張機能のSelenium IDEを活用しました。
Selenium IDEで、ブラウザ操作をレコーディングし、表のセルをクリックして、xpath等を把握しました。




#2. 社内Web翻訳サービス

前回と同様に、社内のWeb翻訳サービスを活用しました。

詳細については省略とさせてください。
一般的なWeb翻訳等と似たような機能になります。



#3. PowerShell SeleniumによるConfluenceとWeb翻訳の連携

スクリプト言語のPowerShellを活用して、以下の処理を実装しました。

* Confluenceの日本語を取得
* 取得した日本語をWeb翻訳に入力し、翻訳（英語やベトナム語等）し、Web翻訳から翻訳結果を取得
* 取得した翻訳結果をConfluenceに追記


実装コードは以下になります。

コード自体は、まだまだ入門者であること、お試しなので最低限動くことを優先、実験的コードやTODOも記載したまま、といった内容です。
なので、まだまだ改善の余地がありますが(例：長文だと時間がかかる点等)、ひとまず動くレベル、といった状況です。
あと、社内Web翻訳のデータや、Confluenceのミーティング議事録のURL等、別の文字に変換してあります。
上記のように色々と不都合ありますが、ご了承ください。


<div>
<pre><code>
###############
# pre setting #
###############

# PowerShell Seleniumのインストール
#   ex: 管理者権限でPowerShellを起動
#       > Install-Module Selenium
#   参考：https://kojimanotech.com/2020/05/19/222/
# Chromeのインストール
# ChromeWebドライバーのインストール(Chromeとバージョンを合わすことを推奨します)
#   ダウンロード先：https://chromedriver.chromium.org/downloads
# パラメーターの設定
#   chromeExePath：Chromeの実行ファイルパスを指定
#   chromeWebDriverDirPath：ChromeWebドライバーのフォルダパスを指定(注：実行ファイルパスではありません)
#   sleepMiliSecond：操作の検出処理の実行間隔をミリ秒で指定
#   target_url_share：Confluenceミーティング議事録のURL
#   target_url_translate：Web翻訳のURL
#   CountWaitMax：翻訳待ちの回数
#     CountWaitMax * waitSleepMiliSeondTranslateが最大翻訳待ち時間となる
#       例： 7 * 300 = 2100ミリ秒
#   waitSleepMiliSeondTranslate：翻訳待ちのインターバル時間(ミリ秒)
#   translate_id：翻訳実施識別子(この文字列があると翻訳が実行される)
# 実行例
#   本スクリプトの内容をPowerShellにコピー＆ペーストして実行


#############
# parameter #
#############

$chromeExePath = "C:\Program Files\Google\Chrome\Application\chrome.exe"
# 2021/11/29 96.0.4664.45
$chromeWebDriverDirPath = "D:\WINDOWS\soft\ChromeDriver\96.0.4664.45\chromedriver_win32"

# loop interval sleep mili second
#$sleepMiliSecond = 2000
$sleepMiliSecond = 1000
#$sleepMiliSecond = 500
#$sleepMiliSecond = 300
#$sleepMiliSecond = 200
#$sleepMiliSecond = 100

$waitTimeoutGetElement = 1.0

$CountWaitMax = 7
$waitSleepMiliSeondTranslate = 300

# target html url
#   ★Personal Data
$target_url_share = "secret_share_url"
$target_url_translate = "secret_translate_url"

$translate_id = "〓〓"
# PowerShellにコピーペーストすると記号(★●×▲▼■★◆◎□◇▽△等)は消えるので、自分で打ちなおすか別の記号にする(〇〓)

$str_crlf = "`r`n"


############
# function #
############

function open_driver($url) {
  #$driver = Start-SeChrome
  # chrome driver download url: https://chromedriver.chromium.org/downloads
  $driver = Start-SeChrome -BinaryPath $chromeExePath -WebDriverDirectory $chromeWebDriverDirPath

  Enter-SeUrl $url -Driver $driver

  return $driver
}

function close_driver($driver) {
  Stop-SeDriver -Driver $driver
}

function get_element_xpath_wait($driver, $xpath, $timeout) {
  $targetElement = Find-SeElement -Driver $driver -Wait -Timeout $timeout -XPath $xpath
  return $targetElement
}

function get_element_xpath($driver, $xpath) {
  $targetElement = Find-SeElement -Driver $driver -XPath $xpath
  return $targetElement
}

function get_element_id($driver, $id) {
  $targetElement = Find-SeElement -Driver $driver -Id $id
  return $targetElement
}

function get_output_text() {
  
}

function do_script($driver, $script) {
  # ex 1: "return document.body.offsetWidth"
  # ex 1: "return document.getElementsByTagName("textarea")[0].value"
  # ex 1: "return document.getElementsByTagName("textarea")[1].value"
  return $driver.ExecuteScript($script)
}

function translate_table_cells_element($driver, $input_text) {
  # TODO
  #   other translate web site
  return translate_table_cells_element_intra $driver $input_text
}

function translate_table_cells_element_intra($driver, $input_text) {
  # 翻訳記号を削除
  $all_text = $input_text.Replace($translate_id, "")

  Write-Host "all text: " + $all_text

  $input_translate_index = 0
  $output_translate_index = 1
  $do_translate_id = "secret_do_id"
  $clear_translate_id = "secret_clear_id"

  # 入力先に代入
  # TODO
  #   まずは、JavaScriptコマンド実行で値に直接代入方式
  $script_context = 'document.getElementsByTagName("textarea")[' + $input_translate_index + '].value = `' + $all_text + '`;'
  Write-Host "script: " + $script_context
  do_script $driver $script_context

  # 翻訳実行
  $translate_element = get_element_id $driver $do_translate_id
  $translate_element.click()

  # 翻訳待ち
  for ($j = 0; $j -lt $CountWaitMax; $j++) {
    Write-Host "wait translate count: " $j
    Start-Sleep -Milliseconds $waitSleepMiliSeondTranslate

    # 翻訳結果を取得
    #$output_text_area = get_element_id $driver "textarea2"
    #$translated_text = $output_text_area.Text # Textは空文字のまま
    $script_context = 'return document.getElementsByTagName("textarea")[' + $output_translate_index + '].value;'
    $translated_text = do_script $driver $script_context
    if ($translated_text -ne "") {
      Write-Host "detect translated"
      break
    }
  }

  # クリア
  $clear_element = get_element_id $driver $clear_translate_id
  $clear_element.click()

  return $translated_text
}

function set_output_text($driver, $target_element, $text) {
  Write-Host "set output text: " + $text

  # 既存内容を削除
  # TODO
  #   ブラウザ上で見えていないとエラーとなる。フォーカス等して移動する必要がある？
  #     長文はまだ扱えない。暫定的に拡大縮小で小さくしておく？
  #   既存の文字列削除はよいやり方があれば変更
  # 対象のセルを表示
  $target_element.Click() # セルの選択
  $target_element.Click() # 
  $target_element.Click() # Click3回連続で文字列の選択
  $target_element.SendKeys(" ") # BACKSPACE/DELETE等ができないので、ひとまずスペースで削除する

  # テキストを代入
  # TODO
  #   ひとまずSendKeysで入力とする
  $target_element.SendKeys($text)
}

function translate_table() {
  $get_rows_xpath = "//tr/td[1]/p[1]"
  $table_item_size = get_element_xpath $driver_share $get_rows_xpath
  for ($i = 2; $i -lt $table_item_size.Length + 2; $i++) { # 1行目は取れないので2から開始
    Write-Host "rows index: " + $i
    $notes_en_specify_xpath = "//tr[$i]/td[4]/p"
    $notes_jp_specify_xpath = "//tr[$i]/td[5]/p"
    $notes_vn_specify_xpath = "//tr[$i]/td[6]/p"
    $notes_en = get_element_xpath $driver_share $notes_en_specify_xpath
    $notes_jp = get_element_xpath $driver_share $notes_jp_specify_xpath
    $notes_vn = get_element_xpath $driver_share $notes_vn_specify_xpath

    $target_notes = $notes_jp
    $all_text = ""
    Write-Host "calls index: " + $target_notes.Length
    for ($j = 0; $j -lt $target_notes.Length; $j++) {
      $cell = $target_notes[$j]
      Write-Host "text: " + $cell.Text
      $all_text += $cell.Text + $str_crlf
    }
    $target_notes = $all_text
    if ($target_notes.Contains($translate_id) -eq $True) {
      $target_translate_driver = $driver_jp_en
      $target_output_element = $notes_en
      if ($target_translate_driver -ne $None) {
        $translated_text = translate_table_cells_element $target_translate_driver ($target_notes)
        set_output_text $driver_share $target_output_element[0] $translated_text
      }
      $target_translate_driver = $driver_jp_vn
      $target_output_element = $notes_vn
      if ($target_translate_driver -ne $None) {
        $translated_text = translate_table_cells_element $target_translate_driver ($target_notes)
        set_output_text $driver_share $target_output_element[0] $translated_text
      }
    }

    # TODO
    #   英語をベトナム語と日本語に翻訳させる処理

    # TODO
    #   ベトナム語を英語と日本語に翻訳させる処理
  }
}

################################################################################
function run-loop() {
  while ($True) {
    "loop start: "

    translate_table

    "sleep(ms): " + $sleepMiliSecond
    Start-Sleep -Milliseconds $sleepMiliSecond
  }
}
################################################################################


########
# init #
########

Import-Module Selenium

$driver_share = open_driver $target_url_share

# TODO
# 手動操作を自動化
#   ログイン
#   鉛筆(編集)アイコンクリック

Read-Host "表示されたConfluenceにログインして、Enterキーを入力してください。"
Read-Host "対象のミーティング議事録のページに移動して、Enterキーを入力してください。"
Read-Host "鉛筆(編集)アイコンクリックして編集モードにして、Enterキーを入力してください。"


$driver_jp_en = open_driver $target_url_translate

# TODO
# 手動操作を自動化
#   ログイン
#   翻訳設定(例：日本語->英語)
Read-Host "表示されたWeb翻訳にログインして、翻訳設定(日本語->英語)をして、Enterキーを入力してください。"

$driver_jp_vn = open_driver $target_url_translate

Read-Host "表示されたWeb翻訳にログインして、翻訳設定(日本語->ベトナム語)をして、Enterキーを入力してください。"


# test
#translate_table


########
# main #
########

run-loop

close_driver $driver_jp_en


</code></pre>
</div>



#4. 動作手順

事前に、以下のセットアップを実施します。

* PowerShell Seleniumインストール
   * 管理者権限でPowerShellを起動し、インストールコマンド「Install-Module Selenium」を実行
* Google Chromeのインストール
* Google Chrome Web Driverの取得(Chromeとバージョンを合わすことを推奨)
* Confluenceのミーティング議事録のページを用意
   * Confluenceのミーティング議事録のページをテンプレートから作成
   * Discussion topics内のNotesの列の右に2つ列(日本語用のNotes:jp、ベトナム語用のNotes:vn)を作成
* 上記3章のPowerShellプログラムのパラメータ設定
   * chromeExePath：Chromeの実行ファイルパスを指定
   * chromeWebDriverDirPath：Chrome Web Driverのフォルダパスを指定(注：実行ファイルパスではありません)
   * target_url_share：Confluenceミーティング議事録のURL
   * target_url_translate：Web翻訳のURL
   * 以下は必要があれば変更
      * sleepMiliSecond：翻訳の実行間隔をミリ秒で指定
      * CountWaitMax：翻訳待ちの回数
      * waitSleepMiliSeondTranslate：翻訳待ちのインターバル時間(ミリ秒)
      * translate_id：翻訳実施識別子(この文字列があると翻訳が実行される)


ミーティング時は以下のように動作させる想定です。

* 起動
   * 上記3章のPowerShellプログラムを実行(操作説明のメッセージが表示されたら、以下の操作をしてEnterを入力していく)
   * Confluenceのログイン画面が表示されたら手動でログイン(今後自動化させたい処理です)
   * Web翻訳ページが表示されたら翻訳設定(例：日本語->英語)(今後自動化させたい処理です)
* 翻訳
   * 上記の起動で表示されたのとは別に、ChromeブラウザでConfluenceのミーティング議事録を開く
   * 新たに開いたConfluenceのミーティング議事録のNotes:jpの列のセルに、日本語で議事トピックを記載していく
      * translate_idの識別子(デフォルト「〓〓」)を記載しておくと、一定間隔(デフォルト1秒ループ)で定期的に翻訳される
      * 翻訳しなくてよくなったら、translate_idの識別子(デフォルト「〓〓」)を削除します



#5. さいごに

実際のミーティングで、まだ動作検証できていないので、色々と問題点等が出てくるかと思いますが、
まずはお試しとして活用して、必要に応じて改善していければと思っています。


あと、今回のようにConfluenceを活用するのではなく、Teamsのホワイトボードや、TeamsのPowerPoint、Webホワイトボードサービス等、いくつかの候補を検討しました。
どれも画像イメージが共有でき、複数ユーザで同時編集が可能そうなので。
どれが適しているかは、まだわかっていないで、時間があり可能であれば、色々試してみたいところです。


では、最後までお読みいただきありがとうございました。
何かの参考になれば幸いです。