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 カスタマイズライフの一助となれたら幸いです。