kintone Webhook × Zapier でレコード内文章を簡単に翻訳&表示してみよう!

著者名:武井 琢治

目次

はじめに

こんにちは。kintoneをより良くする武井です。

これからの時代はどなた様もダイバーシティ経営が大事になって来ますね。
多言語対応しているkintoneは時代のトレンドに沿っているといえます。

ですが、kintoneにはまだ自動翻訳機能まではありません。
そこで今回は、レコード内にある文章に対して「半自動翻訳」のような機能をつける方法を紹介したいと思います。

完成形サンプル

今回のカスタマイズでできること

  • ステータスを変更することで、議事録の内容を別のフィールドへ日英および日中翻訳して表示します。
  • 議事録の内容を変更した場合、翻訳内容を空にし、ステータスを未処理状態に戻します。

システム概要図

今回は上図のようにkintoneのWebhook機能を利用します。
Zapierにレコードデータを送信後データを翻訳し、Zapierの機能によってkintoneのレコードを翻訳後のデータに更新します。

準備

kintoneアプリおよびZapierの準備が必要です。

kintoneアプリ

  1. 以下のフィールドをもつ議事録アプリを作成します。

    フィールドコード フィールドタイプ
    日付 日付
    議題 文字列(1行)
    参加者 ユーザー選択
    議事録 文字列(複数行)
    英語 文字列(複数行)
    中国語 文字列(複数行)
  2. プロセス管理を有効にします。
    ステータスは「未処理・翻訳開始・翻訳完了」の3つを作成します。
    プロセスは以下のとおり設定します。

  3. 「アプリの設定 > JavaScript / CSSでカスタマイズ > PC用のJavaScriptファイル」にサンプルコードを設定します。
    サンプルコードは、後述の サンプルコードをお手元のエディタにコピーしてください。 ファイル名はtranslation.js 、文字コードをUTF-8で保存します。
    ファイル名は任意ですが、ファイルの拡張子はjsにしてください。

Zapier

今回はkintoneとのデータ連携を手軽に実現できるZapierを使用します。
なお、今回のカスタマイズでは、3ステップ以上のzap(処理)を作成するため、Zapierのプレミアムプラン契約が必要となります。

  1. Zapier (External link) にアクセスします。

  2. 画面上部の「MAKE A ZAP」を選択します。

  3. 下図のように入力フォームに「webhooks」と入力し、「Webhooks by Zapier」を選択します。

  4. 「Catch Hook」を選択し、「Continue」を選択します。

  5. 「Pick Off a Child Key」の入力フォームに次の内容を貼り付け、「Continue」を選択します。
    レコードの「議事録」フィールドのデータを取得するためのJSONスキーマとなります。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    {
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "object",
      "properties": {
        "record": {
          "type": "object",
          "properties": {
            "議事録": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  6. 下図のように表示されるURLをコピーします。

  7. kintoneの議事録アプリに戻り「アプリの設定 > Webhook」からWebhookを追加します。

    下図のように設定し、「保存」を選択し、アプリを更新します。
    コピー&ペーストする際https://が重複しないように注意してください。

  8. Zapierに戻り「OK, I did this」を選択します。

  9. kintoneで適当なレコードを作成し、レコードのステータスを「翻訳開始」にします。
    ここは、8. の手順からすばやく行ってください。

  10. 成功すると下図のような画面が表示されます。ここで「Continue」を選択します。

  11. 画面左の「+」ボタンを押し、「Filter」を選択します。

  12. 「Only Continue If...」が選択されているので「Save + Continue」を選択します。

  13. 下図のように設定し「Continue」を選択します。

  14. 「Test Filter」を選択後、「Continue」を選択します。

  15. 初期画面に戻るので、入力フォームに「translate」と入力し、「Translate by Zapier」を選択します。

  16. 「Translate Text」を選択し、「Save + Continue」を選択します。

  17. 各項目を下図のように入力し、「Continue」を選択後、「Create & Continue」を選択します。

  18. 「Add a step」を選択します。

  19. 初期画面に戻るので、再び入力フォームに「translate」と入力し、「Translate by Zapier」を選択します。

  20. 「Translate Text」を選択し、「Save + Continue」を選択します。

  21. 各項目を下図のように入力し、「Continue」を選択後、「Create & Continue」を選択します。

  22. 「Add a step」を選択します。

  23. 初期画面に戻るので、入力フォームに「kintone」と入力し、「kintone」選択します。

  24. 「Update Record By Record ID」を選択し「Save + Continue」を選択します。

  25. kintoneアカウントを選択して「Save + Continue」を選択します。

    登録しているアカウントがない場合は、新規に登録してください。

  26. 各項目を下図のように入力し、「Continue」を選択後、「Create & Continue」を選択します。

  27. 「Add a step」を選択します。

  28. 初期画面に戻るので、再び入力フォームに「kintone」と入力し、「kintone」選択します。

  29. 「Update Status」を選択し「Save + Continue」を選択します。

  30. kintoneアカウントを選択して「Save + Continue」を選択します。

  31. 各項目を下図のように設定し「Continue」を選択し、「Skip test & Continue」を選択します。
    その後、「Continue」を選択します。

  32. 下図のように適当なzap名称を入力し、zapをオンにします。

これでZapierの設定は完了です。

サンプルコード

以下のコードをtranslation.jsとします。

 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
/**
 * kinone x zapier sample program
 * Copyright (c) 2017 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */
(function() {
  'use strict';
  let flg = false;

  function fetchRecords(appId, query, opt_offset, opt_limit, opt_records) {
    const offset = opt_offset || 0;
    const limit = opt_limit || 500;
    let allRecords = opt_records || [];
    const params = {
      app: appId,
      query: query + ' limit ' + limit + ' offset ' + offset
    };
    return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
      allRecords = allRecords.concat(resp.records);
      if (resp.records.length === limit) {
        return fetchRecords(appId, query, offset + limit, limit, allRecords);
      }
      return allRecords;
    });
  }

  kintone.events.on(['app.record.index.edit.show', 'app.record.edit.show', 'app.record.create.show'],
    (event) => {
      const record = event.record;
      record.中国語.disabled = true;
      record.英語.disabled = true;

      return event;
    });

  kintone.events.on(['app.record.edit.submit', 'app.record.index.edit.submit'], (event) => {
    const record = event.record;

    return new kintone.Promise((resolve, reject) => {
      const appId = kintone.app.getId();
      const recId = record.$id.value;
      fetchRecords(appId, '$id = ' + recId).then((resp) => {
        if (resp[0].議事録.value !== record.議事録.value) {
          flg = true;
          record.中国語.value = '';
          record.英語.value = '';
        }
        resolve(event);
      });
    });
  });

  // レコード更新競合エラーが出てしまうため処理を分ける
  kintone.events.on(['app.record.edit.submit.success', 'app.record.index.edit.submit.success'], (event) => {

    return new kintone.Promise((resolve, reject) => {
      if (flg && event.record.ステータス.value === '翻訳完了') {
        const body = {
          app: kintone.app.getId(),
          id: event.record.$id.value,
          action: '未処理にする'
        };
        kintone.api(kintone.api.url('/k/v1/record/status', true), 'PUT', body, () => {
          resolve(event);
        });
      } else {
        resolve(event);
      }
    });
  });

})();

プログラム解説

上記のサンプルコードを部分的に解説していきます。

29
30
31
32
33
34
35
36
kintone.events.on(['app.record.index.edit.show', 'app.record.edit.show', 'app.record.create.show'],
  (event) => {
    const record = event.record;
    record.中国語.disabled = true;
    record.英語.disabled = true;

    return event;
  });

中国語と英語のフィールドは翻訳結果の表示用のためdisabled(入力不可)にしています。

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
kintone.events.on(['app.record.edit.submit', 'app.record.index.edit.submit'], (event) => {
  const record = event.record;

  return new kintone.Promise((resolve, reject) => {
    const appId = kintone.app.getId();
    const recId = record.$id.value;
    fetchRecords(appId, '$id = ' + recId).then((resp) => {
      if (resp[0].議事録.value !== record.議事録.value) {
        flg = true;
        record.中国語.value = '';
        record.英語.value = '';
      }
      resolve(event);
    });
  });
});

編集保存した際に議事録の内容が変更された場合は、翻訳結果をブランクに戻しています。
全件取得関数(fetchRecords関数)につきましては offset の制限値を考慮した kintone のレコード一括取得についてを参照してください。

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
kintone.events.on(['app.record.edit.submit.success', 'app.record.index.edit.submit.success'], (event) => {

  return new kintone.Promise((resolve, reject) => {
    if (flg && event.record.ステータス.value === '翻訳完了') {
      const body = {
        app: kintone.app.getId(),
        id: event.record.$id.value,
        action: '未処理にする'
      };
      kintone.api(kintone.api.url('/k/v1/record/status', true), 'PUT', body, () => {
        resolve(event);
      });
    } else {
      resolve(event);
    }
  });
});

議事録の内容をブランクにする際、ステータスについても「未処理」状態に戻します。
なお、上記のイベントと同時に行うとレコード更新エラーが出てしまうため、イベントを分けて書いています。

拡張

さらに本プログラムを拡張すれば、以下のようなことも可能です。

  • 英語・中国語以外の言語に翻訳する。
  • レコードを保存した瞬間に翻訳された文章を表示する。 Microsoft Translator Text API (External link) などを使用してコードを書く必要があります。

終わりに

いかがでしたでしょうか。
Webhook機能の便利さの一端を見ることができたように思います。
翻訳の精度がもう少し上がるとさらに使い勝手も高まりそうですが、ここはAI技術の進歩等により時間の問題かと思われます。
皆様のすばらしいkintoneカスタマイズライフの一助となれたら幸いです。