{"id":3972,"date":"2017-07-19T15:43:00","date_gmt":"2017-07-19T22:43:00","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/?p=3972"},"modified":"2020-03-18T10:52:48","modified_gmt":"2020-03-18T17:52:48","slug":"facilitating-growth-capital-funding-africa-bots","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/facilitating-growth-capital-funding-africa-bots\/","title":{"rendered":"Facilitating Growth Capital Funding in Africa with Bots"},"content":{"rendered":"<h2>Overview<\/h2>\n<p>We recently worked with <a href=\"https:\/\/www.ovamba.com\/home\" target=\"_blank\" rel=\"noopener noreferrer\">Ovamba<\/a> to build a bot that could simplify their\u00a0funding application intake process. This code story explores the modular, reusable bot architecture we built, the localization we implemented, and unique challenges we faced while creating the bot (for example, working with the new <a href=\"http:\/\/adaptivecards.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Adaptive Cards<\/a>, and combining\u00a0Webpack and\u00a0Bot Framework). Below you&#8217;ll learn about our <a href=\"https:\/\/github.com\/user1m\/botbuilder-template\" target=\"_blank\" rel=\"noopener noreferrer\">bot-builder template<\/a>, which can speed up your own bot development, and how we\u00a0solved some common challenges regarding integrating other technologies with the Bot Framework.<\/p>\n<h2 id=\"toc_0\">Background<\/h2>\n<p>Ovamba is a USA-based startup focused on helping Africa\u2019s small and medium enterprises (SMEs) with <a href=\"https:\/\/s3-eu-central-1.amazonaws.com\/blog.cdn.ovamba.com\/wp-content\/uploads\/2016\/09\/OvambaVideo1_FINAL_1920_1080-2forclients.mp4\">short-term trade and growth capital funding<\/a>. Given that the majority of their customers are currently using feature phones (not smartphones), Ovamba wanted a solution to facilitate their funding application process with new customers that would run great on a feature phone.<\/p>\n<p>Using the <a href=\"https:\/\/dev.botframework.com\/\">Microsoft Bot Framework<\/a> (MBF), we were able to build a\u00a0bot that could tap into a multitude of channels, including Ovamba&#8217;s existing Android application, to meet their\u00a0customers on their preferred conversation platforms.<\/p>\n<p><figure id=\"attachment_4611\" aria-labelledby=\"figcaption_attachment_4611\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" class=\"wp-image-4611 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/ovamba-android.png\" alt=\"A view of the bot integrated into the Ovamba android application\" width=\"293\" height=\"521\" \/><figcaption id=\"figcaption_attachment_4611\" class=\"wp-caption-text\">The bot integrated into the Ovamba android application<\/figcaption><\/figure><\/p>\n<h2 id=\"toc_1\">Bot Framework<\/h2>\n<p><figure class=\"wp-caption alignnone\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/MBF-overview.png\" alt=\"A view of Bot Framework capabilities \" width=\"1024\" height=\"558\" \/><figcaption class=\"wp-caption-text\">Bot Framework capabilities<\/figcaption><\/figure><\/p>\n<p>Bots enable you\u00a0to communicate with users through conversation; these conversations can take place through text, speech, cards, buttons, and other visuals. The advantage of building a bot is that it enables you\u00a0to reach users through their preferred method of communication with a single codebase.<\/p>\n<p>Microsoft Bot Framework is a platform that enables you to build bots using the C# or Node.js SDKs. Essentially, when you build a bot, you are building a RESTful web service. The Bot Framework service enables you to easily connect to channels such as Facebook, Skype, Slack, etc. If you&#8217;d like to learn more about the Bot Framework, <a href=\"https:\/\/docs.microsoft.com\/en-us\/bot-framework\/overview-introduction-bot-framework\">the documentation provides great information<\/a>.<\/p>\n<p>Because the Bot Framework can be localized to\u00a0any language and connected\u00a0to channels like SMS, Facebook\u00a0Messenger, and mobile apps, Ovamba\u00a0decided that a bot would be the best avenue\u00a0to help\u00a0them to communicate with, and gather information from their customers.\u00a0The\u00a0<a href=\"https:\/\/dev.botframework.com\">Microsoft Bot Framework<\/a> was a good fit because it provides all these features, in particular:<!--more--><\/p>\n<h3 id=\"toc_2\"><strong>Localization<\/strong><\/h3>\n<p>The <a href=\"https:\/\/docs.microsoft.com\/en-us\/bot-framework\/nodejs\/bot-builder-nodejs-localization\">localization feature<\/a> enables users to communicate with the bot in multiple languages. One of Ovamba&#8217;s top feature requirements was the ability to communicate with each\u00a0customer in his or her local dialect.<\/p>\n<h3 id=\"toc_3\"><strong>Android &amp; SMS Availability<\/strong><\/h3>\n<p>Although we didn&#8217;t focus on SMS at this hack, SMS and Android support was a top priority for Ovamba, since most of their\u00a0customers use either Android\u00a0or simple feature phones that don&#8217;t run apps. One useful feature of the Bot Framework is\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/bot-framework\/portal-configure-channels\">support for SMS<\/a> as a channel; no matter how complex and visual your bot is, the communication is stripped down so that it works well on SMS, too.<\/p>\n<h3 id=\"toc_4\">Speech<\/h3>\n<p>In addition to text-based communication, the Bot Framework enables a user to speak to a bot and have the bot to respond back in speech. In some cases, you may\u00a0have to provide your own <a href=\"https:\/\/docs.microsoft.com\/en-us\/bot-framework\/nodejs\/bot-builder-nodejs-text-to-speech\">speech to text<\/a>\u00a0support on the client based on the channel you&#8217;re using\u00a0(for example, Android). One way to accomplish this task is to record what the user says, translate it (on the device or in the cloud), then send the text to the bot. Alternatively, the Bot Framework has a third party NodeJS npm package,\u00a0<a href=\"https:\/\/www.npmjs.com\/package\/botbuilder-calling\">botbuilder-calling<\/a>, that enables similar functionality.<\/p>\n<h2 id=\"toc_5\">Bot Architecture<\/h2>\n<p>To get a simple, modular, and testable architecture, we merged two bot templates:\u00a0<a href=\"https:\/\/github.com\/swellaby\/generator-swell\/blob\/master\/docs\/CHATBOT.md\">SWELL Generator<\/a> and <a href=\"https:\/\/github.com\/transmute-industries\/transmute-bot\">transmute-bot<\/a>. This combination became the building block for the hackfest. The <a href=\"https:\/\/github.com\/user1m\/botbuilder-template\">resulting architecture<\/a>\u00a0includes scripts for automated building, packaging of our release code, testing (using mochajs, chaijs, and sinonjs), and visual code coverage.<\/p>\n<p><figure class=\"wp-caption alignnone\" ><img decoding=\"async\" title=\"Tests\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/ovamva-test-cov.png\" alt=\"A view of passing tests and code coverage results\" width=\"1276\" height=\"1260\" \/><figcaption class=\"wp-caption-text\">View of passing tests and code coverage results<\/figcaption><\/figure><\/p>\n<p>The modularity came in the form of what we called &#8220;Skills&#8221;. A skill encapsulates all the code for your <strong><code>Dialogs<\/code><\/strong>, <strong><code>Messages<\/code><\/strong>, <strong><code>Intents<\/code><\/strong>,\u00a0and other capabilities. Skills enable you to break down the functionality of your bot into modular pieces.<\/p>\n<p><figure class=\"wp-caption aligncenter\" ><img decoding=\"async\" title=\"Architecture\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/ovamba-bot-architecture.png\" alt=\"A view of the bot architecture and skills\" width=\"380\" height=\"303\" \/><figcaption class=\"wp-caption-text\">View of bot architecture and &#8220;Skills&#8221;<\/figcaption><\/figure><\/p>\n<p>Once you&#8217;ve created a skill, simply register it with the bot to give the bot those capabilities and dialogs.<\/p>\n<p><strong>bot.ts:<\/strong><\/p>\n<pre class=\"lang:js decode:true javascript\">private registerDialogs() {\r\n    \/\/ this.bot.dialog('\/', this.dialogs);\r\n    RootSkill.register(this.bot, this.dialogs);\r\n    StartSkill.register(this.bot, this.dialogs);\r\n    MainMenuSkill.register(this.bot, this.dialogs); \/\/ Add a line like this for every dialog\r\n    BasicInfoFormSkill.register(this.bot, this.dialogs);\r\n    LanguageSelectionSkill.register(this.bot, this.dialogs);\r\n}\r\n\r\nprivate bindDialogsToIntents() {\r\n    this.dialogs.matches('start', StartSkill.Dialogs.Start);\r\n    this.dialogs.matches('mainMenu', MainMenuSkill.Dialogs.MainMenu); \/\/ Add a line like this for every intent\r\n    this.dialogs.matches('basicInfoForm', BasicInfoFormSkill.Dialogs.BasicInfoForm);\r\n    this.dialogs.matches('languageSelection', LanguageSelectionSkill.Dialogs.LanguageSelection);\r\n}\r\n<\/pre>\n<p>You can find<a href=\"https:\/\/github.com\/user1m\/botbuilder-template\" target=\"_blank\" rel=\"noopener noreferrer\">\u00a0our botbuilder template<\/a> on GitHub.<\/p>\n<h2 id=\"toc_6\">Localization<\/h2>\n<p>Ultimately, Ovamba&#8217;s goal is to localize their applications and services into their customers&#8217; regional African dialects. Given the current lack of translations from English to these lesser-known dialects, we focused on an initial set of 3 languages: English, French, and Arabic. The full-featured Bot Framework has <strong><a href=\"https:\/\/docs.microsoft.com\/en-us\/bot-framework\/nodejs\/bot-builder-nodejs-localization\" target=\"_blank\" rel=\"noopener noreferrer\">support for localization built in<\/a><\/strong>:<\/p>\n<blockquote><p>By default, the localization system will search for the bot&#8217;s prompts in the <strong>.\/locale\/{lang-tag}\/index.json<\/strong> file where <strong>{lang-tag}<\/strong> is a valid <a href=\"https:\/\/en.wikipedia.org\/wiki\/IETF_language_tag\" target=\"_blank\" rel=\"noopener noreferrer\">IETF language tag<\/a> representing the preferred locale for which to find prompts.<\/p><\/blockquote>\n<p>To accomplish this task, we created a <span class=\"lang:default decode:true crayon-inline\">locale<\/span>\u00a0\u00a0folder in the bot and defined sub-folders of\u00a0<a href=\"https:\/\/www.w3schools.com\/tags\/ref_language_codes.asp\">ISO language codes<\/a> (e.g.\u00a0<span class=\"lang:default decode:true crayon-inline\">locale\/fr<\/span>) and included our\u00a0<span class=\"lang:default decode:true crayon-inline\">index.json<\/span>\u00a0\u00a0file there (<span class=\"lang:default decode:true crayon-inline\">locale\/fr\/index.json<\/span>).<\/p>\n<p><figure class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/Screen-Shot-2017-06-27-at-4.06.35-PM.png\" alt=\"A view of the bot localization structure\" width=\"300\" height=\"291\" \/><figcaption class=\"wp-caption-text\">View of bot localization structure<\/figcaption><\/figure><\/p>\n<pre class=\"lang:js decode:true\">{ \r\n   .\r\n   .\r\n   .\r\n\r\n   \"B-CNPS\": \"Quel est votre num\u00e9ro CNPS?\",\r\n   \"B-DrivingLicense\": \"Quel est votre num\u00e9ro de permis de conduire?\",\r\n   \"B-NationalId\": \"Quel est votre num\u00e9ro d'identification nationale?\",\r\n   \"B-LanguagesSpoken\": \"Quelles langues connaissez-vous?\",\r\n   \"B-Occupation\": \"Quel est votre occupation?\",\r\n   \"B-Address\": \"Quelle est la premi\u00e8re ligne de votre adresse?\",\r\n   \"B-City\": \"Dans quelle ville vis-vous?\",\r\n   .\r\n   .\r\n   .\r\n}<\/pre>\n<p>Now you can use the <span class=\"lang:default decode:true crayon-inline \">session.localizer.gettext(session.preferredLocale(), &#8220;B-CNPS&#8221;)<\/span>\u00a0\u00a0 and the Bot Framework will automatically retrieve the correct localized string for you. Note that if <span class=\"lang:default decode:true crayon-inline\">session.preferredLocale()<\/span>\u00a0isn&#8217;t set, the Bot Framework will default to the English locale.<\/p>\n<h3 id=\"toc_7\">Localization Issues<\/h3>\n<p>Since we were using dependencies (<a href=\"https:\/\/github.com\/tombarton\/botbuilder-forms\" target=\"_blank\" rel=\"noopener noreferrer\">botbuilder-forms<\/a>\u00a0and\u00a0<a href=\"http:\/\/adaptivecards.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">adaptive cards<\/a>) that did not directly support localization, we had to add that support explicitly. For adaptive cards, we wrote a helper method that converts specific text found in the JSON schema of adaptive cards to the localized versions at run-time:<\/p>\n<pre class=\"lang:js decode:true javascript\">export function localizeAdaptiveCards(cardQuestions: any, session: Session) {\r\n    for (const q of cardQuestions.body) {\r\n        q.text = (q.hasOwnProperty(\"text\")) ? session.localizer.gettext(session.preferredLocale(), q.text) : null;\r\n        q.speak = (q.hasOwnProperty(\"speak\")) ? session.localizer.gettext(session.preferredLocale(), q.speak) : null;\r\n        q.placeholder = (q.hasOwnProperty(\"placeholder\")) ? session.localizer.gettext(session.preferredLocale(), q.placeholder) : null;\r\n    }\r\n    for (const a of cardQuestions.actions) {\r\n        a.title = session.localizer.gettext(session.preferredLocale(), a.title);\r\n    }\r\n}\r\n<\/pre>\n<p><figure class=\"wp-caption aligncenter\" ><img decoding=\"async\" title=\"Adaptive Cards Localized\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/ovamba-adaptive-cards-localized.png\" alt=\"A view of an adaptive card localized\" width=\"402\" height=\"469\" \/><figcaption class=\"wp-caption-text\">Adaptive cards localized<\/figcaption><\/figure><\/p>\n<p>We added support for localization to BotBuilder-Forms by appropriately calling <span class=\"lang:default decode:true crayon-inline\">session.localizer.gettext(session.preferredLocale(), &#8220;sometext&#8221;)<\/span>\u00a0\u00a0where needed. These updates were submitted as a <a href=\"https:\/\/github.com\/tombarton\/botbuilder-forms\/pull\/1\" target=\"_blank\" rel=\"noopener noreferrer\">pull request<\/a> back to the open source project.<\/p>\n<h2 id=\"toc_8\">Challenges<\/h2>\n<h3>Localization<\/h3>\n<p>We ran into several interesting issues while adding localization support. The first was directly related to how the Bot Framework resolves file names for the localization choice.\u00a0This problem is described in <a href=\"https:\/\/github.com\/Microsoft\/BotBuilder-Azure\/issues\/2\" target=\"_blank\" rel=\"noopener noreferrer\">Issue #2 &#8211; Botbuilder Framework<\/a>,\u00a0which is still open at the time of writing. In <strong><code>bot.ts<\/code><\/strong>\u00a0, the following code loads the localization JSON files from the proper directory.<\/p>\n<pre class=\"lang:js decode:true javascript\">const botLocalePath = __dirname + '\/locale';\r\n<\/pre>\n<pre class=\"lang:js decode:true javascript\">private setUp() {\r\n  const url = config.luis.url.replace('##APP##', config.luis.app).replace('##KEY##', config.luis.key);\r\n  this.recognizer = new builder.LuisRecognizer(url);\r\n  this.dialogs = new builder.IntentDialog({ recognizers: [this.recognizer] });\r\n\r\n  this.bot.set(localizerSettings, {\r\n    botLocalePath: botLocalePath, \/\/config.languages.path,\r\n    defaultLocale: 'en'\/\/languageChoices[languageChoice]\r\n  });\r\n}\r\n<\/pre>\n<p>The constant <span class=\"lang:default decode:true crayon-inline \">botLocalePath<\/span>\u00a0in the first block must be set to the NodeJS internal variable of <strong><span class=\"lang:default decode:true crayon-inline\">__dirname<\/span><\/strong>, concatenated to the path of the locale files. The <strong><span class=\"lang:default decode:true crayon-inline\">__dirname<\/span><\/strong>\u00a0value is the directory of the startup script at runtime, so the concatenation generates an absolute path. In the <span class=\"lang:default decode:true crayon-inline \">setUp<\/span>\u00a0function, the <span class=\"lang:default decode:true crayon-inline \">localizerSettings<\/span>\u00a0are initialized with the path based on the <span class=\"lang:default decode:true crayon-inline \">botLocalePath<\/span>\u00a0.<\/p>\n<h3>WebPack<\/h3>\n<p>The second issue is related to the first. \u00a0When you use WebPack\u00a0for bundling and optimization, the\u00a0<strong><span class=\"lang:default decode:true crayon-inline\">__dirname<\/span><\/strong>\u00a0variable is optimized away unless you make an appropriate configuration setting. If the\u00a0<strong><span class=\"lang:default decode:true crayon-inline\">__dirname<\/span><\/strong>\u00a0variable is optimized away, then the code above will fail. This issue applies generally to any NodeJS application using <strong><span class=\"lang:default decode:true crayon-inline\">__dirname<\/span><\/strong>\u00a0\u00a0or <strong><span class=\"lang:default decode:true crayon-inline\">__filename<\/span><\/strong>\u00a0\u00a0bundling with WebPack. To prevent this optimization, go to the <span class=\"lang:default decode:true crayon-inline\">webpack.config.js<\/span>\u00a0\u00a0file, and ensure that the <span class=\"lang:default decode:true crayon-inline \">node<\/span>\u00a0\u00a0property contains an object with the <span class=\"lang:default decode:true crayon-inline\">{ __dirname: false, __filename: false }<\/span>\u00a0, which will prevent both variables from being optimized out and should appear as is in the optimized bundle.<\/p>\n<pre class=\"lang:js decode:true javascript\">module.exports = {\r\n    target: \"node\",\r\n    entry: \".\/release\/server.js\",\r\n    output: {\r\n        path: __dirname + \"\/release\/\",\r\n        filename: \"bundle.js\",\r\n        library: '',\r\n        libraryTarget: 'commonjs-module'\r\n    },\r\n    node: {\r\n        __dirname: false,\r\n        __filename: false\r\n    }\r\n};\r\n<\/pre>\n<h3>Adaptive Cards<\/h3>\n<p>A third issue faced was with the new adaptive cards features in the Bot Framework, using the JSON template. The issue was that data from input fields were not being picked up. Adaptive cards enable you to create a form with a submit action, which looks something like this:<\/p>\n<pre title=\"Adaptive Card Form\" class=\"lang:js decode:true javascript\">{\r\n  \"$schema\": \"http:\/\/adaptivecards.io\/schemas\/adaptive-card.json\",\r\n  \"type\": \"AdaptiveCard\",\r\n  \"version\": \"1.0\",\r\n  \"body\": [\r\n    {\r\n        \"type\": \"TextBlock\",\r\n        \"text\": \"Your name\",\r\n        \"wrap\": true\r\n    },\r\n    {\r\n        \"type\": \"Input.Text\",\r\n        \"id\": \"myName\",\r\n        \"placeholder\": \"Last, First\"\r\n    },\r\n    {\r\n        \"type\": \"TextBlock\",\r\n        \"text\": \"Your email\",\r\n        \"wrap\": true\r\n    },\r\n    {\r\n        \"type\": \"Input.Text\",\r\n        \"id\": \"myEmail\",\r\n        \"placeholder\": \"youremail@example.com\",\r\n        \"style\": \"email\"\r\n    },\r\n  ],\r\n  \"actions\": [\r\n    {\r\n      \"type\": \"Action.Submit\",\r\n      \"title\": \"Submit\"\r\n    }\r\n  ]\r\n}\r\n<\/pre>\n<p>Upon pressing the submit button, it will gather up the input fields and merge them with the optional data field in the submit action. It then generates an event to the client asking for data to be submitted; it&#8217;s then completely up to the client to determine how that data is processed. In the Bot Framework, the data is sent as an activity through the message endpoint. We wrote some middleware to pick this up and handle the submission of the data to Ovamba&#8217;s backend.<\/p>\n<pre title=\"Middleware\" class=\"lang:js decode:true\">if (this.bot) {\r\n    this.bot.use(\r\n        this.routingMiddleware()\r\n    );\r\n}\r\n\r\npublic routingMiddleware() {\r\n    return {\r\n        botbuilder: (session: any, next: Function) =&gt; {\r\n            \/\/ value contains the data from the adaptive card form\r\n            if (session.message.value) {\r\n                const formData = session.message.value;\r\n\r\n                request.post(\r\n                'OVAMBA_ENDPOINT_URL',\r\n                {\r\n                    json: formData\r\n                },\r\n\r\n                .\r\n                .\r\n                .\r\n<\/pre>\n<p>But the input field data was\u00a0not getting picked up. Turns out there is an issue using \u00a0<span class=\"lang:default decode:true crayon-inline\">ColumnSet<\/span>\u00a0 (like in the code snippet below), that prevents the data from those input fields from being picked up.\u00a0<a href=\"https:\/\/github.com\/Microsoft\/BotBuilder\/issues\/3047\" target=\"_blank\" rel=\"noopener noreferrer\">This issue has been raised on the project&#8217;s GitHub repo<\/a>. It was an interesting bug because our code for the adaptive card worked in the browser (for example, <a href=\"http:\/\/adaptivecards.io\/visualizer\/?card=\/samples\/cards\/Input%20Form.json\" target=\"_blank\" rel=\"noopener noreferrer\">this form<\/a>) but in our bot, the data from input fields wasn&#8217;t getting passed through.<\/p>\n<pre title=\"Adaptive Card ColumnSet\" class=\"lang:js decode:true\">\"body\": [\r\n    {\r\n      \"type\": \"ColumnSet\",\r\n      \"columns\": [\r\n        {\r\n          \"type\": \"Column\",\r\n          \"size\": 1,\r\n          \"items\": [\r\n            {\r\n              \"type\": \"Input.Text\",\r\n              \"id\": \"myName\",\r\n              \"placeholder\": \"Last, First\"\r\n            },\r\n<\/pre>\n<p>Again, the issue is related to using <span class=\"lang:default decode:true crayon-inline\">ColumnSet<\/span>, so we worked around the issue by\u00a0removing the columns.<\/p>\n<pre class=\"lang:js decode:true\"> \"body\": [\r\n    {\r\n      \"type\": \"TextBlock\",\r\n      \"text\": \"B-DOB\",\r\n      \"speak\": \"B-DOB\",\r\n      \"wrap\": true\r\n    },\r\n    {\r\n      \"type\": \"Input.Date\",\r\n      \"id\": \"basic_dateofbirth\"\r\n    },\r\n<\/pre>\n<h2 id=\"toc_9\">Conclusion<\/h2>\n<p>This project with Ovamba has\u00a0given us a solid <a href=\"https:\/\/github.com\/user1m\/botbuilder-template\" target=\"_blank\" rel=\"noopener noreferrer\">template<\/a> for future Bot Framework projects that includes\u00a0automated scripts for building, packaging,\u00a0and testing code. We&#8217;ve also built\u00a0solutions to localize <a href=\"http:\/\/adaptivecards.io\" target=\"_blank\" rel=\"noopener noreferrer\">adaptive cards<\/a> and <a href=\"https:\/\/github.com\/tombarton\/botbuilder-forms\" target=\"_blank\" rel=\"noopener noreferrer\">botbuilder-forms<\/a>. This knowledge can be reused in similar projects that require localization and adaptive cards or turn-based prompts.<\/p>\n<h3 id=\"toc_10\">Future Work<\/h3>\n<p>We plan to continue our work with Ovamba, especially building an African dialect language recognition system to enable application localization and cater to the specific dialects of their customers. This task presents many\u00a0interesting challenges. For example, during the hackfest, we learned\u00a0that some African languages we\u00a0wanted to support existed in spoken form only (no written form), which left us considering how might we localize text for these languages. We look forward to tackling these challenges and more in our future work with Ovamba.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We built a bot with Microsoft Bot Framework (MBF) to tap into a multitude of channels, including a client&#8217;s existing Android app, and implemented localization features, to connect with potential customers on their preferred online platforms.<\/p>\n","protected":false},"author":21389,"featured_media":11983,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13],"tags":[26,109,110,235,384],"class_list":["post-3972","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bots","tag-adaptive-cards","tag-bot-framework-sdk","tag-bots","tag-localization","tag-webpack"],"acf":[],"blog_post_summary":"<p>We built a bot with Microsoft Bot Framework (MBF) to tap into a multitude of channels, including a client&#8217;s existing Android app, and implemented localization features, to connect with potential customers on their preferred online platforms.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3972","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21389"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=3972"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/3972\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/11983"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=3972"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=3972"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=3972"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}