Alexa Presentation Language（APL）スキル 開発チュートリアル

# 1. はじめに

---

## 参考文献
今回のAlexa Presentation Language（APL）スキル 開発チュートリアルは、以下のサイトを参考に、2018年11月時点の開発環境にアレンジし、まとめたものとなります。

- [はじめてのAlexa Presentation Language (APL)](https://developer.amazon.com/ja/blogs/alexa/post/a3712152-cd02-4fda-9d33-3b64ef3a13af/jp-alexa-presentation-language-part1)
 
---

## このスライドを見てできること
- Alexa Presentation Language（APL）スキルの作り方が理解できるようになる。

---

## 今回作成するAlexaスキル

- スキル名：ハローワールド

※ディスプレイ対応のAlexaスキルが作成できます。

```text:トークシナリオ
ユーザー：「アレクサ、ハローワールドを開いて」
アレクサ：「XXXXXXXX。」（APLによるディスプレイ表示）

```

---

## 前提条件
- ASK-CLI（askコマンド）を利用します。askコマンドをご存知で無い方は、[Alexa Skills Kitコマンドラインインターフェース（ASK CLI）クイックスタートガイド](https://developer.amazon.com/ja/docs/smapi/quick-start-alexa-skills-kit-command-line-interface.html#step-2-install-and-initialize-ask-cli)をご参照ください。
- Displayインターフェース（Bodyテンプレート・Listテンプレート）を理解している方が理解が深まると思います。ご存知で無い方は、[Amazon Echo Alexaスキル 入門（Echo Spot編）](https://qiita.com/zono_0/items/77843f2906a937dded4e)をご参照ください。

---

# 2. 事前準備・開発環境

---
## 事前準備

- Amazon Developerアカウントの用意（[Alexaスキル開発トレーニングシリーズ 第1回 初めてのスキル開発](https://developer.amazon.com/ja/blogs/alexa/post/6e716e5c-55b0-445b-b936-9cfac4712e7b/training-1)）
- AWSアカウントの用意（[AWS アカウント作成の流れ](https://aws.amazon.com/jp/register-flow/)）
- IAMロールの用意（ [初めてLambda用ロールを作成する場合の手順](https://github.com/alexa/skill-sample-nodejs-fact/blob/ja-JP/lambda-role.md) ）

---

## 必要なソフトウェア

- ブラウザ：Google Chrome（推奨）
- エディタ：Visual Studio Code（推奨）

---

## 必要な開発環境

- マイク＆スピーカが利用できるパソコン
- インターネット（Wi-Fi）

---

# 3. スキルとモデルの作成

---


## ask new

- ask new コマンドを利用し、hello-world スキルを新規作成する。

```shell:Ubuntuコマンドライン
#作業ディレクトリ（任意）に移動（ホームディレクトリ ~/を想定）
$ cd

#スキルの作成
$ ask new
? Please type in your new skill name: hello-world

#作業ディレクトリに移動（hello-worldを想定）
$ cd hello-world

```

- [GitHub](https://github.com/alexa/skill-sample-nodejs-hello-world)からも同様の[ZIPファイル](https://github.com/alexa/skill-sample-nodejs-hello-world/archive/master.zip)をダウンロードすることは可能ですが、ZIPファイルには、必要なモジュール ask-sdk-core が含まれておりません。ZIP解凍後、コマンドラインから./lambda/custom/フォルダ内（index.jsとpackage.jsonファイルがあるフォルダ内）に移動し、npm install コマンドを実行することで、必要なモジュールを取り込むことが可能です。

```shell:Ubuntuコマンドライン
$ cd ./lambda/custom

$ npm install
```

---

## skill.jsonファイルの日本語スキル化

- ./skill.json ファイルをエディタで開き、"en-US" の文字列を "ja-JP" に書き替えて、日本語スキル化する。

```text:skill.json
修正前："en-US": {
修正後："ja-JP": {
```

---

## en-US.jsonファイルのリネーム

- ./models/en-US.jsonファイル名を「ja-JP.json」にリネームし、日本語スキル化する。

---

## ja-JP.jsonファイルの日本語スキル化

- ./models/ja-JP.jsonファイルを開き、"invocationName"（スキル呼び出し名）の設定値を以下のとおり日本語化する。

```text:ja-JP.json
修正前："invocationName": "greeter",
修正後："invocationName": "ハローワールド",
```
---

## ja-JP.jsonファイルの日本語スキル化

- HelloWorldIntentインテントのサンプル発話の日本語化
- ./models/ja-JP.jsonファイルを開き、HelloWorldIntentのサンプル発話を以下のとおり日本語化する。

```text:ja-JP.json
        {
          "name": "HelloWorldIntent",
          "slots": [

          ],
          "samples": [
            "ハロー",
            "セイハロー",
            "セイハローワールド"
          ]
        }

```

---

# 4. Lambdaの作成

---

## node_modulesの最新化

1. ./lambda/customフォルダに移動する。
2. 以下のnpmコマンドを実行し、node_modulesを更新する。

```shell:Ubuntuコマンドライン
#作業ディレクトリ（任意）に移動（~/hello-world/lambda/customを想定）
$ cd ./lambda/custom

#node_modulesの最新化（index.jsファイルがあるフォルダで実行すること）
$ npm i -S ask-sdk-core@latest
$ npm i -S ask-sdk-model@latest

```

---

## index.jsの修正

- ./lambda/custom/index.jsをエディタで開き、ソースプログラムを修正する。
- APL対応のポイントは、supportsAplを利用しデバイスがAPIに対応しているか判定することです。その他は、通常のスキルとほぼ同様となります。

```javascript:index.js
/* template by ぽいもんラボ zono_0 */
/* eslint-disable  func-names */
/* eslint-disable  no-console */
"use strict";


/**
 * モジュール参照
 * @author zono_0
 */ 
const Alexa = require('ask-sdk-core');


/**
 * ディスプレイサポート（APL対応）判定値
 * @author zono_0
 */ 
const supportsApl = (handlerInput) => {
  const hasDisplay =
    handlerInput.requestEnvelope.context &&
    handlerInput.requestEnvelope.context.System &&
    handlerInput.requestEnvelope.context.System.device &&
    handlerInput.requestEnvelope.context.System.device.supportedInterfaces &&
    handlerInput.requestEnvelope.context.System.device.supportedInterfaces['Alexa.Presentation.APL'];

  return hasDisplay;
};


/**
 * ローンチリクエストハンドラ
 * @author zono_0
 */
const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    const speechText = 'ハローワールドスキルへようこそ。このスキルでは、英語のあいさつの練習をおこないます。ハローとおっしゃってください。';

    // ディスプレイ有り（APL対応）の場合
    if (supportsApl(handlerInput)) {
      // APL対応（documentに設定したテンプレートレイアウトを利用し、datasourcesの内容をディスプレイに表示します。）
      handlerInput.responseBuilder
        .addDirective({
          type : 'Alexa.Presentation.APL.RenderDocument',
          version: '1.0',
          document: require('./homepage.json'),
          datasources: require('./data.json')
        });
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withShouldEndSession(false)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};


/**
 * ハローワールドインテントハンドラ
 * @author zono_0
 */
const HelloWorldIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HelloWorldIntent';
  },
  handle(handlerInput) {
    const speechText = 'お上手ですね、こんにちは。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withShouldEndSession(true)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};


/**
 * ヘルプインテントハンドラ
 * @author zono_0
 */
const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = 'このスキルでは、英語のあいさつの練習をおこないます。ハローとおっしゃってください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withShouldEndSession(false)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};


/**
 * キャンセル＆ストップインテントハンドラ
 * @author zono_0
 */
const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {
    const speechText = '終了を受け付けました。ご利用ありがとうございます。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withShouldEndSession(true)
      .withSimpleCard('ハローワールド', speechText)
      .getResponse();
  },
};


/**
 * セッションエンドリクエストハンドラ
 * @author zono_0
 */
const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};


/**
 * エラーハンドラ
 * @author zono_0
 */
const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('ちょっと分かりませんでした。ハローとおっしゃってください。')
      .reprompt('このスキルでは、英語のあいさつの練習をおこないます。ハローとおっしゃってください。')
      .withShouldEndSession(false)
      .getResponse();
  },
};


/**
 * メインンロジック
 * @author zono_0
 */
const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    HelloWorldIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();

```

---

# 5. スキルのデプロイ

---

## ask deploy

- ask deploy コマンドを利用し、hello-world スキルをAmazon Developer PortalおよびAWS Lambdaにデプロイします。
- デプロイ先のAWS Lambdaリージョンは、カレントディレクトリ配下の ~/.aws/configファイルにて指定できます。

```shell:Ubuntuコマンドライン
#作業ディレクトリ（任意）に移動（~/hello-worldを想定）
$ cd hello-world

#スキルのデプロイ（skill.jsonフィルと同じディレクトリで実行すること。）
$ ask deploy

```

```shell:Ubuntuコマンドライン
$ cat ~/.aws/config
[default]
region=ap-northeast-1
output=json

```

---

## デプロイ結果の確認（AWS Lambda）

1. 当該AWSにログインし、デプロイ先リージョンのLambda関数一覧を確認する。
2. 新規関数「ask-custom-hello-world-default」が作成されていることを確認する。

![lambda.png](https://qiita-image-store.s3.amazonaws.com/0/240522/11ae4887-8e18-61ba-3dc7-c1755cf0b4de.png)

---

## デプロイ結果の確認（Amazon Developer Portal）

1. 当該Amazon Developer Portalにログインし、スキル一覧を確認する。
2. 新規スキル「hello-world」が作成されていることを確認する。

![adp.png](https://qiita-image-store.s3.amazonaws.com/0/240522/659efba2-7521-7747-e0c5-875ca990f5d5.png)

---

# 6. Alexa Presentation Language（APL）の作成

---

## Amazon Developer Portal

- Amazon Developer Portalのスキル一覧から当該スキルの「編集」を、クリックする。

![apl1.png](https://qiita-image-store.s3.amazonaws.com/0/240522/534826b4-909f-5c57-47dc-f28b133b2a63.png)

---

## Amazon Developer Portal

- 「画面表示」をクリックし、APLオーサリングツール（デザインツール）を開く。

![apl2.png](https://qiita-image-store.s3.amazonaws.com/0/240522/eb6a0ef0-6440-b6d9-59cc-bff2a8c0cfe6.png)

---

## Amazon Developer Portal

- 「画面表示サンプル」（任意）を、クリックする。

![apl3.png](https://qiita-image-store.s3.amazonaws.com/0/240522/957f23c9-eb27-1e1e-07f8-590dd8d0be67.png)

---

## Amazon Developer Portal

1. 「コードを書き出し」を、クリックする。
2. 「apl_template_export.json」ファイルをダウンロードし、保存する。

- ※ファイルダウンロードが完了したら、この画面の内容を保存せずに離れても結構です。

![apl4.png](https://qiita-image-store.s3.amazonaws.com/0/240522/15d220d3-be87-a0c4-2578-e238234d58dd.png)

---

## APL JSONファイル

- 「apl_template_export.json」ファイルをダウンロード後、index.jsファイルがあるフォルダにファイルを移動する。（フォルダパス：/hello-world/lambda/custom）

![apl5.png](https://qiita-image-store.s3.amazonaws.com/0/240522/8c81bb3a-7d26-1033-7a51-df440ad556a1.png)

---

## 2つのJSONファイル

1. 「apl_template_export.json」をコピーし、2つのファイルにする。
2. 1つのファイル名を「homepage.json」に、もう1つのふぁいる名を「data.json」にリネームする。

![apl6.png](https://qiita-image-store.s3.amazonaws.com/0/240522/54f7219c-c9be-2ddd-62aa-10701f4ddff9.png)

---

## homepage.json

- 「homepage.json」をエディタで開き、[はじめてのAlexa Presentation Language (APL)](https://developer.amazon.com/ja/blogs/alexa/post/a3712152-cd02-4fda-9d33-3b64ef3a13af/jp-alexa-presentation-language-part1)の記載を参考に、修正する。

```text:homepage.json
{
    "type": "APL",
    "version": "1.0",
    "theme": "dark",
    "import": [
        {
            "name": "alexa-layouts",
            "version": "1.0.0"
        }
    ],
    "resources": [
        {
            "description": "Stock color for the light theme",
            "colors": {
                "colorTextPrimary": "#151920"
            }
        },
        {
            "description": "Stock color for the dark theme",
            "when": "${viewport.theme == 'dark'}",
            "colors": {
                "colorTextPrimary": "#f0f1ef"
            }
        },
        {
            "description": "Standard font sizes",
            "dimensions": {
                "textSizeBody": 48,
                "textSizePrimary": 27,
                "textSizeSecondary": 23,
                "textSizeSecondaryHint": 25
            }
        },
        {
            "description": "Common spacing values",
            "dimensions": {
                "spacingThin": 6,
                "spacingSmall": 12,
                "spacingMedium": 24,
                "spacingLarge": 48,
                "spacingExtraLarge": 72
            }
        },
        {
            "description": "Common margins and padding",
            "dimensions": {
                "marginTop": 40,
                "marginLeft": 60,
                "marginRight": 60,
                "marginBottom": 40
            }
        }
    ],
    "styles": {
        "textStyleBase": {
            "description": "Base font description; set color and core font family",
            "values": [
                {
                    "color": "@colorTextPrimary",
                    "fontFamily": "Amazon Ember"
                }
            ]
        },
        "textStyleBase0": {
            "description": "Thin version of basic font",
            "extend": "textStyleBase",
            "values": {
                "fontWeight": "100"
            }
        },
        "textStyleBase1": {
            "description": "Light version of basic font",
            "extend": "textStyleBase",
            "values": {
                "fontWeight": "300"
            }
        },
        "mixinBody": {
            "values": {
                "fontSize": "@textSizeBody"
            }
        },
        "mixinPrimary": {
            "values": {
                "fontSize": "@textSizePrimary"
            }
        },
        "mixinSecondary": {
            "values": {
                "fontSize": "@textSizeSecondary"
            }
        },
        "textStylePrimary": {
            "extend": [
                "textStyleBase1",
                "mixinPrimary"
            ]
        },
        "textStyleSecondary": {
            "extend": [
                "textStyleBase0",
                "mixinSecondary"
            ]
        },
        "textStyleBody": {
            "extend": [
                "textStyleBase1",
                "mixinBody"
            ]
        },
        "textStyleSecondaryHint": {
            "values": {
                "fontFamily": "Bookerly",
                "fontStyle": "italic",
                "fontSize": "@textSizeSecondaryHint",
                "color": "@colorTextPrimary"
            }
        }
    },
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "when": "${viewport.shape == 'round'}",
                "type": "Container",
                "direction": "column",
                "items": [
                    {
                        "type": "Image",
                        "source": "${payload.bodyTemplate7Data.backgroundImage.sources[0].url}",
                        "scale": "best-fill",
                        "position": "absolute",
                        "width": "100vw",
                        "height": "100vh",
                        "scrim": true
                    },
                    {
                        "type": "AlexaHeader",
                        "headerTitle": "${payload.bodyTemplate7Data.title}",
                        "headerAttributionImage": "${payload.bodyTemplate7Data.logoUrl}"
                    },
                    {
                        "type": "Container",
                        "grow": 1,
                        "alignItems": "center",
                        "justifyContent": "center",
                        "items": [
                            {
                                "type": "Image",
                                "source": "${payload.bodyTemplate7Data.image.sources[0].url}",
                                "scale": "best-fill",
                                "width": "100vh",
                                "height": "70vw",
                                "align": "center"
                            }
                        ]
                    }
                ]
            },
            {
                "type": "Container",
                "items": [
                    {
                        "type": "Image",
                        "source": "${payload.bodyTemplate7Data.backgroundImage.sources[0].url}",
                        "scale": "best-fill",
                        "position": "absolute",
                        "width": "100vw",
                        "height": "100vh"
                    },
                    {
                        "type": "AlexaHeader",
                        "headerTitle": "${payload.bodyTemplate7Data.title}",
                        "headerAttributionImage": "${payload.bodyTemplate7Data.logoUrl}"
                    },
                    {
                        "type": "Container",
                        "direction": "row",
                        "paddingLeft": "5vw",
                        "paddingRight": "5vw",
                        "paddingBottom": "5vh",
                        "alignItems": "center",
                        "justifyContent": "center",
                        "items": [
                            {
                                "type": "Image",
                                "height": "75vh",
                                "width": "90vw",
                                "source": "${payload.bodyTemplate7Data.image.sources[0].url}",
                                "scale": "best-fill",
                                "align": "center"
                            }
                        ]
                    }
                ]
            }
        ]
    }
}

```

---

## data.json

- 「data.json」をエディタで開き、[はじめてのAlexa Presentation Language (APL)](https://developer.amazon.com/ja/blogs/alexa/post/a3712152-cd02-4fda-9d33-3b64ef3a13af/jp-alexa-presentation-language-part1)の記載を参考に、修正する。

```text:data.json
{
    "bodyTemplate7Data": {
        "type": "object",
        "objectId": "bt7Sample",
        "title": "Today's Daily Photo of Cheese",
        "backgroundImage": {
            "contentDescription": null,
            "smallSourceUrl": null,
            "largeSourceUrl": null,
            "sources": [
                {
                    "url": "https://d2o906d8ln7ui1.cloudfront.net/images/BT7_Background.png",
                    "size": "small",
                    "widthPixels": 0,
                    "heightPixels": 0
                },
                {
                    "url": "https://d2o906d8ln7ui1.cloudfront.net/images/BT7_Background.png",
                    "size": "large",
                    "widthPixels": 0,
                    "heightPixels": 0
                }
            ]
        },
        "image": {
            "contentDescription": null,
            "smallSourceUrl": null,
            "largeSourceUrl": null,
            "sources": [
                {
                    "url": "https://d2o906d8ln7ui1.cloudfront.net/images/MollyforBT7.png",
                    "size": "small",
                    "widthPixels": 0,
                    "heightPixels": 0
                },
                {
                    "url": "https://d2o906d8ln7ui1.cloudfront.net/images/MollyforBT7.png",
                    "size": "large",
                    "widthPixels": 0,
                    "heightPixels": 0
                }
            ]
        },
        "logoUrl": "https://d2o906d8ln7ui1.cloudfront.net/images/cheeseskillicon.png",
        "hintText": "Try, \"Alexa, search for blue cheese\""
    }
}

```

---

## Lambdaの再デプロイ

- ask deploy -t コマンドを利用し、hello-world スキルのLambdaプログラムのみをAWS Lambdaに再デプロイします。

```shell:Ubuntuコマンドライン
#作業ディレクトリ（任意）に移動（~/hello-worldを想定）
$ cd hello-world

#スキルのデプロイ（skill.jsonフィルと同じディレクトリで実行すること。）
$ ask deploy -t lambda

```

---

## Amazon Developer Portal

- 「インターフェース」を、クリックする。

![apl8.png](https://qiita-image-store.s3.amazonaws.com/0/240522/222d3ddf-cd07-8d54-7f12-71f0e08b22de.png)

---

## Amazon Developer Portal

1. Alexa Presentation Languageを、チェックする。
2. 「インターフェースを保存」を、クリックする。

![apl9.png](https://qiita-image-store.s3.amazonaws.com/0/240522/46404535-dc60-ba92-d21a-bf33bd1ebb2b.png)

追記：2019/02/04
- ディスプレイタップ等に正しく対応するために、「Displayインターフェース」もあわせてチェックしておくことをおすすめします。「Displayインターフェース」をチェックすることで、ブラウザシミュレータでの「タップ（クリック）」テストが可能になるかと思います。

---

# 7. スキルのテスト

---

## ブラウザシミュレータ

1. 「テスト」タブを、クリックする。
2. 「テスト有効」を、ONにする。
3. 「スキルI/O」のチェックを、はずす。

![apl11.png](https://qiita-image-store.s3.amazonaws.com/0/240522/63eda5c5-1971-382b-663f-592f474fc3e0.png)

---

## ブラウザシミュレータ

1. 「ハローワールドを開いて」と発話し、スキルを起動する。

- ※ APLのサンプル画像が表示されれば、OKです。

![apl12.png](https://qiita-image-store.s3.amazonaws.com/0/240522/c5d4ba16-abc2-b0c9-fbed-b2211f01317f.png)

---

# 8. 補足

---

## APL定義を修正したい場合

- 表示する画像データを変更したい場合は、「data.json」内容の修正にチャレンジしましょう。
- 表示する画像のレイアウトを変更したい場合は、「homepage.json」内容の修正にチャレンジしましょう。
- 直接JSONファイルを編集することも可能ですし、[APLオーサリングツール（デザインツール）](https://developer.amazon.com/ja/docs/alexa-presentation-language/apl-authoring-tool.html)を利用して編集することもできます。

---

## APLオーサリングツール（デザインツール）の使い方

- 2019年2月21日に開催された[Alexaスキル開発ハンズオン](https://i-enter.connpass.com/event/118258/)の公開資料「[APLハンズオン](https://apl-handson.netlify.com/#0)」のご参照をおすすめいたします。

---

## APLの向こう側

- 動的に「data.json」内容を変えたい！！という場合は、ファイル定義での利用をやめて、Lambdaプログラムのdatasources指定箇所を、直接JSON記述することで、APLの向こう側にたどりつけるかもしれません。

```java:index.js（抜粋：動作検証はしてないよ。）
    // いろいろ試してみてね！！
    let TITLE = 'タイトルを変数で動的に変えてみてね。';

    // ディスプレイ有り（APL対応）の場合
    if (supportsApl(handlerInput)) {
      // APL対応（documentに設定したテンプレートレイアウトを利用し、datasourcesの内容をディスプレイに表示します。）
      handlerInput.responseBuilder
        .addDirective({
          type : 'Alexa.Presentation.APL.RenderDocument',
          version: '1.0',
          document: require('./homepage.json'),
          datasources: {
              bodyTemplate7Data: {
                  "title": `${TITLE}`,
                  "logoUrl": "https://d2o906d8ln7ui1.cloudfront.net/images/cheeseskillicon.png",
                  "url": "https://d2o906d8ln7ui1.cloudfront.net/images/BT7_Background.png"
              }
          }
        });
    }

```

---

## APLのベータ世界線

- 動的に「data.json」内容を変えたい！！が、Lambdaプログラムで動的操作したいわけじゃない！data.jsonファイルを切り替えて扱いたいだけなんだ！というような利用ケースもあるかもしれません。この場合は、datasources指定箇所でrequireする「ファイル名」を、変数として扱うことで、もう一つのベータ世界線までもたどりつことができるかもしれません。

```java:index.js（抜粋：動作検証はしてないよ。）
    // いろいろ試してみてね！！
    let DATA_JSON = './data2.json';

    // ディスプレイ有り（APL対応）の場合
    if (supportsApl(handlerInput)) {
      // APL対応（documentに設定したテンプレートレイアウトを利用し、datasourcesの内容をディスプレイに表示します。）
      handlerInput.responseBuilder
        .addDirective({
          type : 'Alexa.Presentation.APL.RenderDocument',
          version: '1.0',
          document: require('./homepage.json'),
          datasources: require(`${DATA_JSON}`)
        });
    }

```

---

## APLの仕組みを深く理解する

- 2019年3月1日に開催された[Alexaスキル開発ハンズオン 〜APL対応スキルのつくりかた〜](https://aajug.connpass.com/event/120754/presentation/)の後日解説がまとめられている「[APLハンズオン コード解説](https://dev.classmethod.jp/voice-assistant/aajug-vol2-apl-handson-details/)」のご参照をおすすめいたします。

---

# 9. おわりに

---

## おわりに
みなさん、Alexa Presentation Language（APL）スキル 開発チュートリアルはいかがでしたか？みなさんの環境でも、うまく実行できていれば幸いです。うまくいけば、1時間もあれば完成するかと思います。
スマートスピーカのディスプレイ対応によって、スマートスピーカ界隈がより盛り上がっていくことを願っております。

2018/11/24 TAKAHIRO NISHIZONO

---

# end