├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app.yaml ├── dialogflow-agent.zip ├── language_list.py ├── main.py ├── requirements.txt └── translate_response.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | ## Contributor License Agreements 3 | We'd love to accept your sample apps and patches! Before we can take them, we 4 | have to jump a couple of legal hurdles. 5 | Please fill out either the individual or corporate Contributor License Agreement 6 | (CLA). 7 | * If you are an individual writing original source code and you're sure you 8 | own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 9 | * If you work for a company that wants to allow you to contribute your work, 10 | then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 11 | Follow either of the two links above to access the appropriate CLA and 12 | instructions for how to sign and return it. Once we receive it, we'll be able to 13 | accept your pull requests. 14 | ## Contributing A Patch 15 | 1. Submit an issue describing your proposed change to the repo in question. 16 | 1. The repo owner will respond to your issue promptly. 17 | 1. If your proposed change is accepted, and you haven't already done so, sign a 18 | Contributor License Agreement (see details above). 19 | 1. Fork the desired repo, develop and test your code changes. 20 | 1. Ensure that your code adheres to the existing style in the sample to which 21 | you are contributing. Refer to the 22 | [Google Cloud Platform Samples Style Guide](https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 23 | recommended coding standards for this organization. 24 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 25 | 1. Submit a pull request. 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dialogflow Fulfillment Translation Sample for Python (Flask) 2 | ============================================================== 3 | ## Setup Instructions 4 | 5 | ### Dialogflow Setup 6 | 1. Create an account on Dialogflow 7 | 1. Create a new Dialogflow agent 8 | 1. Restore the `dialogflow-agent.zip` ZIP file in the root of this repo 9 | 1. Go to your agent's settings and then the *Export and Import* tab 10 | 1. Click the *Restore from ZIP* button 11 | 1. Select the `dialogflow-agent.zip` ZIP file in the root of this repo 12 | 1. Type *RESTORE* and and click the *Restore* button 13 | 14 | ### Fulfillment Setup 15 | 1. Click on the Google Cloud project ID in your agent's setting to open the Google Cloud console 16 | 1. Enable the [Google Cloud Translation API](http://console.cloud.google.com/apis/library/translate.googleapis.com/) 17 | 1. [Create an API key](https://cloud.google.com/docs/authentication/api-keys#creating_an_api_key) and copy the value into the `API_KEY` varible in `main.py` and save the file 18 | 1. Run `pip install -r requirements.txt` 19 | 1. Deploy fulfillment to App Engine 20 | 1. [Download and authenticate the Google Cloud SDK](https://cloud.google.com/sdk/docs/quickstart-macos) 21 | 1. Run `gcloud app deploy`, make a note of the service URL, which will be used in the next step 22 | 1. Set the fulfillment URL in Dialogflow to your App Engine service URL 23 | 1. Go to your [agent's fulfillment page](https://console.dialogflow.com/api-client/#/agent//fulfillment) 24 | 1. Click the switch to enable webhook for your agent 25 | 1. Enter you App Engine service URL and append `/webhook` (e.g. `https://translate-10929.appspot.com/webhook`) to the URL field 26 | 1. Click *Save* at the bottom of the page 27 | 28 | ## How to report bugs 29 | * If you find any issues, please open a bug here on GitHub 30 | 31 | ## How to make contributions? 32 | Please read and follow the steps in the CONTRIBUTING.md 33 | 34 | ## License 35 | See LICENSE.md 36 | 37 | ## Terms 38 | Your use of this sample is subject to, and by using or downloading the sample files you agree to comply with, the [Google APIs Terms of Service](https://developers.google.com/terms/) and the [Dialogflow's Terms of Use and Privacy Policy](https://dialogflow.com/terms/). -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | runtime: python 2 | env: flex 3 | entrypoint: gunicorn -b :$PORT main:app 4 | 5 | runtime_config: 6 | python_version: 3 7 | 8 | # This sample incurs costs to run on the App Engine flexible environment. 9 | # The settings below are to reduce costs during testing and are not appropriate 10 | # for production use. For more information, see: 11 | # https://cloud.google.com/appengine/docs/flexible/python/configuring-your-app-with-app-yaml 12 | manual_scaling: 13 | instances: 1 14 | resources: 15 | cpu: 1 16 | memory_gb: 0.5 17 | disk_size_gb: 10 -------------------------------------------------------------------------------- /dialogflow-agent.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/fulfillment-translate-python/9a79fc7b836aaa487e200f08f0ba0bef6a085dab/dialogflow-agent.zip -------------------------------------------------------------------------------- /language_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2017 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """This is a sample for a translation fulfillment webhook for an Dialogflow agent 17 | 18 | This is meant to be used with the sample translate agent for Dialogflow, it uses 19 | the Google Cloud Translation API and requires an API key from an API project 20 | with the Google Cloud Translation API enabled. 21 | """ 22 | 23 | _LANGUAGE_LIST = { 24 | 'Afrikaans': 'af', 25 | 'Albanian': 'sq', 26 | 'Amharic': 'am', 27 | 'Arabic': 'ar', 28 | 'Armenian': 'hy', 29 | 'Azeerbaijani': 'az', 30 | 'Basque': 'eu', 31 | 'Belarusian': 'be', 32 | 'Bengali': 'bn', 33 | 'Bosnian': 'bs', 34 | 'Bulgarian': 'bg', 35 | 'Catalan': 'ca', 36 | 'Cebuano': 'ceb', 37 | 'Chichewa': 'ny', 38 | 'Simplified Chinese': 'zh-CN', 39 | 'Traditional Chinese': 'zh-TW', 40 | 'Corsican': 'co', 41 | 'Croatian': 'hr', 42 | 'Czech': 'cs', 43 | 'Danish': 'da', 44 | 'Dutch': 'nl', 45 | 'English': 'en', 46 | 'Esperanto': 'eo', 47 | 'Estonian': 'et', 48 | 'Filipino': 'tl', 49 | 'Finnish': 'fi', 50 | 'French': 'fr', 51 | 'Frisian': 'fy', 52 | 'Galician': 'gl', 53 | 'Georgian': 'ka', 54 | 'German': 'de', 55 | 'Greek': 'el', 56 | 'Gujarati': 'gu', 57 | 'Haitian Creole': 'ht', 58 | 'Hausa': 'ha', 59 | 'Hawaiian': 'haw', 60 | 'Hebrew': 'iw', 61 | 'Hindi': 'hi', 62 | 'Hmong': 'hmn', 63 | 'Hungarian': 'hu', 64 | 'Icelandic': 'is', 65 | 'Igbo': 'ig', 66 | 'Indonesian': 'id', 67 | 'Irish': 'ga', 68 | 'Italian': 'it', 69 | 'Japanese': 'ja', 70 | 'Javanese': 'jw', 71 | 'Kannada': 'kn', 72 | 'Kazakh': 'kk', 73 | 'Khmer': 'km', 74 | 'Korean': 'ko', 75 | 'Kurdish': 'ku', 76 | 'Kyrgyz': 'ky', 77 | 'Lao': 'lo', 78 | 'Latin': 'la', 79 | 'Latvian': 'lv', 80 | 'Lithuanian': 'lt', 81 | 'Luxembourgish': 'lb', 82 | 'Macedonian': 'mk', 83 | 'Malagasy': 'mg', 84 | 'Malay': 'ms', 85 | 'Malayalam': 'ml', 86 | 'Maltese': 'mt', 87 | 'Maori': 'mi', 88 | 'Marathi': 'mr', 89 | 'Mongolian': 'mn', 90 | 'Burmese': 'my', 91 | 'Nepali': 'ne', 92 | 'Norwegian': 'no', 93 | 'Pashto': 'ps', 94 | 'Persian': 'fa', 95 | 'Polish': 'pl', 96 | 'Portuguese': 'pt', 97 | 'Punjabi': 'pa', 98 | 'Romanian': 'ro', 99 | 'Russian': 'ru', 100 | 'Samoan': 'sm', 101 | 'Scots Gaelic': 'gd', 102 | 'Serbian': 'sr', 103 | 'Sesotho': 'st', 104 | 'Shona': 'sn', 105 | 'Sindhi': 'sd', 106 | 'Sinhala': 'si', 107 | 'Slovak': 'sk', 108 | 'Slovenian': 'sl', 109 | 'Somali': 'so', 110 | 'Spanish': 'es', 111 | 'Sundanese': 'su', 112 | 'Swahili': 'sw', 113 | 'Swedish': 'sv', 114 | 'Tajik': 'tg', 115 | 'Tamil': 'ta', 116 | 'Telugu': 'te', 117 | 'Thai': 'th', 118 | 'Turkish': 'tr', 119 | 'Ukrainian': 'uk', 120 | 'Urdu': 'ur', 121 | 'Uzbek': 'uz', 122 | 'Vietnamese': 'vi', 123 | 'Welsh': 'cy', 124 | 'Xhosa': 'xh', 125 | 'Yiddish': 'yi', 126 | 'Yoruba': 'yo', 127 | 'Zulu': 'zu' 128 | } 129 | 130 | _LANGUAGE_CODE_LIST = { 131 | 'af': 'Afrikaans', 132 | 'sq': 'Albanian', 133 | 'am': 'Amharic', 134 | 'ar': 'Arabic', 135 | 'hy': 'Armenian', 136 | 'az': 'Azeerbaijani', 137 | 'eu': 'Basque', 138 | 'be': 'Belarusian', 139 | 'bn': 'Bengali', 140 | 'bs': 'Bosnian', 141 | 'bg': 'Bulgarian', 142 | 'ca': 'Catalan', 143 | 'ceb': 'Cebuano', 144 | 'ny': 'Chichewa', 145 | 'zh-CN': 'Simplified Chinese', 146 | 'zh-TW': 'Traditional Chinese', 147 | 'co': 'Corsican', 148 | 'hr': 'Croatian', 149 | 'cs': 'Czech', 150 | 'da': 'Danish', 151 | 'nl': 'Dutch', 152 | 'en': 'English', 153 | 'eo': 'Esperanto', 154 | 'et': 'Estonian', 155 | 'tl': 'Filipino', 156 | 'fi': 'Finnish', 157 | 'fr': 'French', 158 | 'fy': 'Frisian', 159 | 'gl': 'Galician', 160 | 'ka': 'Georgian', 161 | 'de': 'German', 162 | 'el': 'Greek', 163 | 'gu': 'Gujarati', 164 | 'ht': 'Haitian Creole', 165 | 'ha': 'Hausa', 166 | 'haw': 'Hawaiian', 167 | 'iw': 'Hebrew', 168 | 'hi': 'Hindi', 169 | 'hmn': 'Hmong', 170 | 'hu': 'Hungarian', 171 | 'is': 'Icelandic', 172 | 'ig': 'Igbo', 173 | 'id': 'Indonesian', 174 | 'ga': 'Irish', 175 | 'it': 'Italian', 176 | 'ja': 'Japanese', 177 | 'jw': 'Javanese', 178 | 'kn': 'Kannada', 179 | 'kk': 'Kazakh', 180 | 'km': 'Khmer', 181 | 'ko': 'Korean', 182 | 'ku': 'Kurdish', 183 | 'ky': 'Kyrgyz', 184 | 'lo': 'Lao', 185 | 'la': 'Latin', 186 | 'lv': 'Latvian', 187 | 'lt': 'Lithuanian', 188 | 'lb': 'Luxembourgish', 189 | 'mk': 'Macedonian', 190 | 'mg': 'Malagasy', 191 | 'ms': 'Malay', 192 | 'ml': 'Malayalam', 193 | 'mt': 'Maltese', 194 | 'mi': 'Maori', 195 | 'mr': 'Marathi', 196 | 'mn': 'Mongolian', 197 | 'my': 'Burmese', 198 | 'ne': 'Nepali', 199 | 'no': 'Norwegian', 200 | 'ps': 'Pashto', 201 | 'fa': 'Persian', 202 | 'pl': 'Polish', 203 | 'pt': 'Portuguese', 204 | 'pa': 'Punjabi', 205 | 'ro': 'Romanian', 206 | 'ru': 'Russian', 207 | 'sm': 'Samoan', 208 | 'gd': 'Scots Gaelic', 209 | 'sr': 'Serbian', 210 | 'st': 'Sesotho', 211 | 'sn': 'Shona', 212 | 'sd': 'Sindhi', 213 | 'si': 'Sinhala', 214 | 'sk': 'Slovak', 215 | 'sl': 'Slovenian', 216 | 'so': 'Somali', 217 | 'es': 'Spanish', 218 | 'su': 'Sundanese', 219 | 'sw': 'Swahili', 220 | 'sv': 'Swedish', 221 | 'tg': 'Tajik', 222 | 'ta': 'Tamil', 223 | 'te': 'Telugu', 224 | 'th': 'Thai', 225 | 'tr': 'Turkish', 226 | 'uk': 'Ukrainian', 227 | 'ur': 'Urdu', 228 | 'uz': 'Uzbek', 229 | 'vi': 'Vietnamese', 230 | 'cy': 'Welsh', 231 | 'xh': 'Xhosa', 232 | 'yi': 'Yiddish', 233 | 'yo': 'Yoruba', 234 | 'Zulu': 'zu' 235 | } 236 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2017 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """This is a sample for a translation fulfillment webhook for an Dialogflow agent 17 | 18 | This is meant to be used with the sample translate agent for Dialogflow, it uses 19 | the Google Cloud Translation API and requires an API key from an API project 20 | with the Google Cloud Translation API enabled. 21 | """ 22 | 23 | import json 24 | import random 25 | from http.client import HTTPException 26 | from urllib.error import HTTPError, URLError 27 | 28 | from flask import Flask, jsonify, make_response, request 29 | from googleapiclient.discovery import build 30 | 31 | from language_list import _LANGUAGE_CODE_LIST as language_code_dict 32 | from language_list import _LANGUAGE_LIST as language_dict 33 | from translate_response import (_TRANSLATE_ERROR, _TRANSLATE_INTO_W, 34 | _TRANSLATE_NETWORK_ERROR, _TRANSLATE_RESULT, 35 | _TRANSLATE_UNKNOWN_LANGUAGE, _TRANSLATE_W, 36 | _TRANSLATE_W_FROM, _TRANSLATE_W_FROM_TO, 37 | _TRANSLATE_W_TO) 38 | 39 | # API key to access the Google Cloud Translation API 40 | # 1. Go to console.google.com create or use an existing project 41 | # 2. Enable the Cloud Translation API in the console for your project 42 | # 3. Create an API key in the credentials tab and paste it below 43 | API_KEY = '' 44 | TRANSLATION_SERVICE = build('translate', 'v2', developerKey=API_KEY) 45 | 46 | app = Flask(__name__) 47 | log = app.logger 48 | 49 | 50 | @app.route('/webhook', methods=['POST']) 51 | def webhook(): 52 | """This method handles the http requests for the Dialogflow webhook 53 | 54 | This is meant to be used in conjunction with the translate Dialogflow agent 55 | """ 56 | 57 | # Get request parameters 58 | req = request.get_json(force=True) 59 | action = req.get('queryResult').get('action') 60 | 61 | # Check if the request is for the translate action 62 | if action == 'translate.text': 63 | # Get the parameters for the translation 64 | text = req['queryResult']['parameters'].get('text') 65 | source_lang = req['queryResult']['parameters'].get('lang-from') 66 | target_lang = req['queryResult']['parameters'].get('lang-to') 67 | 68 | # Fulfill the translation and get a response 69 | output = translate(text, source_lang, target_lang) 70 | 71 | # Compose the response to Dialogflow 72 | res = {'fulfillmentText': output, 73 | 'outputContexts': req['queryResult']['outputContexts']} 74 | else: 75 | # If the request is not to the translate.text action throw an error 76 | log.error('Unexpected action requested: %s', json.dumps(req)) 77 | res = {'speech': 'error', 'displayText': 'error'} 78 | 79 | return make_response(jsonify(res)) 80 | 81 | 82 | def translate(text, source_lang, target_lang): 83 | """Returns a string containing translated text, or a request for more info 84 | 85 | Takes text input, source and target language for the text (all strings) 86 | uses the responses found in translate_response.py as templates 87 | """ 88 | 89 | # Validate the languages provided by the user 90 | source_lang_code = validate_language(source_lang) 91 | target_lang_code = validate_language(target_lang) 92 | 93 | # If both languages are invalid or no languages are provided tell the user 94 | if not source_lang_code and not target_lang_code: 95 | response = random.choice(_TRANSLATE_UNKNOWN_LANGUAGE) 96 | 97 | # If there is no text but two valid languages ask the user for input 98 | if not text and source_lang_code and target_lang_code: 99 | response = random.choice(_TRANSLATE_W_FROM_TO).format( 100 | lang_from=language_code_dict[source_lang_code], 101 | lang_to=language_code_dict[target_lang_code]) 102 | 103 | # If there is no text but a valid target language ask the user for input 104 | if not text and target_lang_code: 105 | response = random.choice(_TRANSLATE_W_TO).format( 106 | lang=language_code_dict[target_lang_code]) 107 | 108 | # If there is no text but a valid source language assume the target 109 | # language is English if the source language is not English 110 | if (not text and 111 | source_lang_code and 112 | source_lang_code != 'en' and 113 | not target_lang_code): 114 | target_lang_code = 'en' 115 | 116 | # If there is no text, no target language and the source language is English 117 | # ask the user for text 118 | if (not text and 119 | source_lang_code and 120 | source_lang_code == 'en' and 121 | not target_lang_code): 122 | response = random.choice(_TRANSLATE_W_FROM).format( 123 | lang=language_code_dict[source_lang_code]) 124 | 125 | # If there is no text and no languages 126 | if not text and not source_lang_code and not target_lang_code: 127 | response = random.choice(_TRANSLATE_W) 128 | 129 | # If there is text but no languages 130 | if text and not source_lang_code and not target_lang_code: 131 | response = random.choice(_TRANSLATE_INTO_W) 132 | 133 | # If there is text and a valid target language but no source language 134 | if text and not source_lang_code and target_lang_code: 135 | response = translate_text(text, source_lang_code, target_lang_code) 136 | 137 | # If there is text and 2 valid languages return the translation 138 | if text and source_lang_code and target_lang_code: 139 | response = translate_text(text, source_lang_code, target_lang_code) 140 | 141 | # If no response is generated from the any of the 8 possible combinations 142 | # (3 booleans = 2^3 = 8 options) return an error to the user 143 | if not response: 144 | response = random.choice(_TRANSLATE_ERROR) 145 | 146 | return response 147 | 148 | 149 | def translate_text(query, source_lang_code, target_lang_code): 150 | """returns translated text or text indicating a translation/network error 151 | 152 | Takes a text to be translated, source language and target language code 153 | 2 letter ISO code found in language_list.py 154 | """ 155 | 156 | try: 157 | translations = TRANSLATION_SERVICE.translations().list( 158 | source=source_lang_code, 159 | target=target_lang_code, 160 | q=query 161 | ).execute() 162 | translation = translations['translations'][0] 163 | if 'detectedSourceLanguage' in translation.keys(): 164 | source_lang_code = translation['detectedSourceLanguage'] 165 | resp = random.choice(_TRANSLATE_RESULT).format( 166 | text=translation['translatedText'], 167 | fromLang=language_code_dict[source_lang_code], 168 | toLang=language_code_dict[target_lang_code]) 169 | except (HTTPError, URLError, HTTPException): 170 | resp = random.choice(_TRANSLATE_NETWORK_ERROR) 171 | except Exception: 172 | resp = random.choice(_TRANSLATE_ERROR) 173 | return resp 174 | 175 | 176 | def validate_language(language): 177 | """returns 2 letter language code if valid, None if language is invalid 178 | 179 | Uses dictionary in language_list.py to verify language is valid 180 | """ 181 | 182 | try: 183 | lang_code = language_dict[language] 184 | except KeyError: 185 | lang_code = None 186 | return lang_code 187 | 188 | if __name__ == '__main__': 189 | PORT = 8080 190 | 191 | app.run( 192 | debug=True, 193 | port=PORT, 194 | host='0.0.0.0' 195 | ) 196 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.12.2 2 | google-api-python-client==1.6.6 3 | gunicorn==19.7.1 -------------------------------------------------------------------------------- /translate_response.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2017 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the 'License'); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an 'AS IS' BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """This is a sample for a translation fulfillment webhook for an API.AI agent 17 | 18 | This is meant to be used with the sample translate agent for API.AI, it uses 19 | the Google Cloud Translation API and requires an API key from an API project 20 | with the Google Cloud Translation API enabled. 21 | """ 22 | 23 | _TRANSLATE_W = [ 24 | u'Sure. What do you want to translate and into which language?', 25 | u'Okay. Just tell me what you need to translate and into which language.', 26 | u'No problem. What are we translating, and into which language?', 27 | u'All right. What do you need translated, and into which language?' 28 | ] 29 | 30 | _TRANSLATE_INTO_W = [ 31 | u'Which language would you like to translate this into?', 32 | u'Okay. What language are you trying to translate into?', 33 | u'Which language did you want this translated into?', 34 | u'Just tell me what language you want this translated into.' 35 | ] 36 | 37 | _TRANSLATE_W_FROM = [ 38 | u'Sure thing. Just tell me what you want to translate from {lang}.', 39 | u'Absolutely. I can translate from {lang}.' 40 | u' What would you like to translate?', 41 | u'I am familiar with {lang}.' 42 | u' Let me know what you need to translate from it.', 43 | u'Easy enough. What do you want to translate from {lang}?' 44 | ] 45 | 46 | _TRANSLATE_W_TO = [ 47 | u'Sure thing. Just tell me what you want to translate into {lang}.', 48 | u'Absolutely. I can translate into {lang}.' 49 | u' What would you like to translate?', 50 | u'I am familiar with {lang}. Let me know what you need to translate.', 51 | u'Easy enough. What do you want to translate into {lang}?' 52 | ] 53 | 54 | _TRANSLATE_W_FROM_TO = [ 55 | u'Sure thing. Just tell me what you want to translate from' 56 | u' {lang_from} into {lang_to}.', 57 | u'Absolutely. I can translate from {lang_from} into {lang_to}.' 58 | u' What would you like to translate?', 59 | u'Of course! I can translate from {lang_from} into {lang_to}.' 60 | u' Just let me know what you need to translate.', 61 | u'Easy enough.' 62 | u' What do you want to translate from {lang_from} into {lang_to}?', 63 | ] 64 | 65 | _TRANSLATE_UNKNOWN_LANGUAGE = [ 66 | u'Sorry, I couldn\'t find a translation into this language.', 67 | u'Unfortunately this language is unfamiliar to me.' 68 | u' I wasn\'t able to get a translation.', 69 | u'I\'m not too familiar with this language.' 70 | u' I wasn\'t able to translate that.', 71 | u'Sorry. I haven\'t learned this language yet.' 72 | ] 73 | 74 | _TRANSLATE_RESULT = [ 75 | u'Here is how that translates from {fromLang} into {toLang}: {text}.', 76 | u'Here is the translation from {fromLang} into {toLang}: {text}.', 77 | u'Okay, I translated that from {fromLang} into {toLang} for you: {text}.', 78 | u'That translates from {fromLang} to {toLang} like so: {text}.', 79 | ] 80 | 81 | _TRANSLATE_NETWORK_ERROR = [ 82 | u'Sorry, the translation service is not responding.' 83 | u' Let\'s try again in a bit.', 84 | u'I can\'t connect to the translation service now.' 85 | u' Sorry about this. Let\'s retry in a minute.', 86 | u'Seems like there\'s a connection problem with the translation service.' 87 | u' Let\'s give a moment and try again.', 88 | u'Looks like the translation service isn\'t responding right now.' 89 | u' We can try again in a moment if you like.' 90 | ] 91 | 92 | _TRANSLATE_ERROR = [ 93 | u'I\'m not quite sure what happened,' 94 | u' but I was unable to get translation at this time.', 95 | u'Sorry, I ran into an unexpected problem while trying' 96 | u' to get a translation. Let\'s try that again.', 97 | u'I\'m sorry. I wasn\'t able to complete that translation for some reason.' 98 | u' Let\'s try again in a moment.', 99 | u'Looks like something went wrong in the middle of that translation.' 100 | u' Better try that again.', 101 | u'I\'m not sure what happened,' 102 | u' but I wasn\'t able to finish translating that.' 103 | u' We may need to try that again.' 104 | ] 105 | --------------------------------------------------------------------------------