diff --git a/README.md b/README.md index d8d6388..c65219d 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,32 @@ -# 📊 Project: Complex API - -### Goal: Use data returned from one api to make a request to another api and display the data returned - -### How to submit your code for review: - -- Fork and clone this repo -- Create a new branch called answer -- Checkout answer branch -- Push to your fork -- Issue a pull request -- Your pull request description should contain the following: - - (1 to 5 no 3) I completed the challenge - - (1 to 5 no 3) I feel good about my code - - Anything specific on which you want feedback! - -Example: -``` -I completed the challenge: 5 -I feel good about my code: 4 -I'm not sure if my constructors are setup cleanly... -``` +# English Translator App + +> Get the English translation and pronunciation of any word. + +> Screenshot of completed English Translator App + +## Table of Contents + +1. [Tech Stack](#tech-stack) +1. [Development](#development) + 1. [Translation and Dictionary API](#translation-and-dictionary-api) + 1. [Notes](#notes) + +## Tech Stack + +- **HTML** +- **CSS** +- **JavaScript** + +## Development + +### Translation and Dictionary API + +- Obtain a free Dictionary API key from Merriam-Webster at: https://dictionaryapi.com/ +- Update the `MERRIAM_WEBSTER_KEY` value with your key on line 1 of main.js. +- Open the app in your browser, select a language, enter a word, and view the English translation, definition, and pronunciation! + +### Notes + +The API key-free Google Translate API was found in this GitHub issue: https://github.com/ssut/py-googletrans/issues/268. + +Visit the official Dictionary API documentation at https://dictionaryapi.com/products/api-collegiate-dictionary for more information on API use, copyright, and rate limitations. diff --git a/images/screenshot.png b/images/screenshot.png new file mode 100644 index 0000000..ff4d2f9 Binary files /dev/null and b/images/screenshot.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..bfe660c --- /dev/null +++ b/index.html @@ -0,0 +1,35 @@ + + + + + + + + + English Translator + + + +

English Translator

+

For English Language Learners

+ +
+

+ Select a language from the list below, and enter the word you'd like to + translate to English. +

+
+ + + + +
+
+
+ + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..11ae40a --- /dev/null +++ b/main.js @@ -0,0 +1,429 @@ +const MERRIAM_WEBSTER_KEY = ""; // ADD KEY + +document.querySelector("button").onclick = getTrans; + +// Available languages: https://developers.google.com/workspace/admin/directory/v1/languages +const languages = [ + ["", ""], + ["Amharic", "am"], + ["Arabic", "ar"], + ["Basque", "eu"], + ["Bengali", "bn"], + ["Portuguese (Brazil)", "pt-BR"], + ["Bulgarian", "bg"], + ["Catalan", "ca"], + ["Cherokee", "chr"], + ["Croatian", "hr"], + ["Czech", "cs"], + ["Danish", "da"], + ["Dutch", "nl"], + ["Estonian", "et"], + ["Filipino", "fil"], + ["Finnish", "fi"], + ["French", "fr"], + ["German", "de"], + ["Greek", "el"], + ["Gujarati", "gu"], + ["Hebrew", "iw"], + ["Hindi", "hi"], + ["Hungarian", "hu"], + ["Icelandic", "is"], + ["Indonesian", "id"], + ["Italian", "it"], + ["Japanese", "ja"], + ["Kannada", "kn"], + ["Korean", "ko"], + ["Latvian", "lv"], + ["Lithuanian", "lt"], + ["Malay", "ms"], + ["Malayalam", "ml"], + ["Marathi", "mr"], + ["Norwegian", "no"], + ["Polish", "pl"], + ["Portuguese (Portugal)", "pt-PT"], + ["Romanian", "ro"], + ["Russian", "ru"], + ["Serbian", "sr"], + ["Chinese (PRC)", "zh-CN"], + ["Slovak", "sk"], + ["Slovenian", "sl"], + ["Spanish", "es"], + ["Swahili", "sw"], + ["Swedish", "sv"], + ["Tamil", "ta"], + ["Telugu", "te"], + ["Thai", "th"], + ["Chinese (Taiwan)", "zh-TW"], + ["Turkish", "tr"], + ["Urdu", "ur"], + ["Ukrainian", "uk"], + ["Vietnamese", "vi"], + ["Welsh", "cy"], +]; + +languages.forEach((lang) => { + const option = document.createElement("option"); + option.innerText = lang[0]; + option.value = lang[1]; + document.querySelector("select").appendChild(option); +}); + +function getTrans() { + // Remove previous translation from DOM, if any + document.getElementById("container").innerText = ""; + + const originalText = document.querySelector("input").value; + const sourceLanguage = document.querySelector("select").value; + + const translationURL = `https://clients5.google.com/translate_a/t?client=dict-chrome-ex&sl=${ + sourceLanguage ? sourceLanguage : "auto" + }&tl=en&q=${originalText}`; + + fetch(translationURL) + .then((res) => res.json()) + .then((data) => { + // Comment out fetch call and use data below for testing to prevent getting rate limited + // const data = [["Hello", "ar"]]; + + // If a language is specified, the response is structured as: ['translation'] + // If no language is specified, google detects the language and the response is structured as: + // [['translation', 'language abbr']] + const translationText = Array.isArray(data[0]) + ? data[0][0].toLowerCase() + : data[0].toLowerCase(); + + const dictionaryURL = `https://www.dictionaryapi.com/api/v3/references/collegiate/json/${translationText}?key=${MERRIAM_WEBSTER_KEY}`; + + fetch(dictionaryURL) + .then((res) => res.json()) + .then((obj) => { + // Comment out fetch call and use obj below for testing to prevent getting rate limited + // const obj = [ + // { + // meta: { + // id: "bye:1", + // uuid: "b990326a-aee9-47ff-b2e4-69b893972ccb", + // sort: "020521500", + // src: "collegiate", + // section: "alpha", + // stems: ["by", "bye", "byes"], + // offensive: false, + // }, + // hom: 1, + // hwi: { + // hw: "bye", + // prs: [ + // { + // mw: "ˈbī", + // sound: { + // audio: "by000001", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // fl: "noun", + // ins: [ + // { + // il: "plural", + // if: "byes", + // prs: [ + // { + // mw: "ˈbīz", + // sound: { + // audio: "by000003", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // ], + // def: [ + // { + // sseq: [ + // [ + // [ + // "sense", + // { + // dt: [ + // [ + // "text", + // "{bc}the position of a participant in a tournament who advances to the next round without playing ", + // ], + // [ + // "vis", + // [ + // { + // t: "drew a first-round {wi}bye{/wi}", + // }, + // ], + // ], + // ], + // }, + // ], + // ], + // ], + // }, + // ], + // et: [["text", "alteration of {et_link|by:2|by:2}"]], + // date: "1883", + // shortdef: [ + // "the position of a participant in a tournament who advances to the next round without playing", + // ], + // }, + // { + // meta: { + // id: "bye:1", + // uuid: "b990326a-aee9-47ff-b2e4-69b893972ccb", + // sort: "020521500", + // src: "collegiate", + // section: "alpha", + // stems: ["by", "bye", "byes"], + // offensive: false, + // }, + // hom: 1, + // hwi: { + // hw: "bye", + // prs: [ + // { + // mw: "ˈbī", + // sound: { + // audio: "by000001", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // fl: "noun", + // ins: [ + // { + // il: "plural", + // if: "byes", + // prs: [ + // { + // mw: "ˈbīz", + // sound: { + // audio: "by000003", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // ], + // def: [ + // { + // sseq: [ + // [ + // [ + // "sense", + // { + // dt: [ + // [ + // "text", + // "{bc}the position of a participant in a tournament who advances to the next round without playing ", + // ], + // [ + // "vis", + // [ + // { + // t: "drew a first-round {wi}bye{/wi}", + // }, + // ], + // ], + // ], + // }, + // ], + // ], + // ], + // }, + // ], + // et: [["text", "alteration of {et_link|by:2|by:2}"]], + // date: "1883", + // shortdef: [ + // "the position of a participant in a tournament who advances to the next round without playing", + // ], + // }, + // { + // meta: { + // id: "bye:1", + // uuid: "b990326a-aee9-47ff-b2e4-69b893972ccb", + // sort: "020521500", + // src: "collegiate", + // section: "alpha", + // stems: ["by", "bye", "byes"], + // offensive: false, + // }, + // hom: 1, + // hwi: { + // hw: "bye", + // prs: [ + // { + // mw: "ˈbī", + // sound: { + // audio: "by000001", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // fl: "noun", + // ins: [ + // { + // il: "plural", + // if: "byes", + // prs: [ + // { + // mw: "ˈbīz", + // sound: { + // audio: "by000003", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // ], + // def: [ + // { + // sseq: [ + // [ + // [ + // "sense", + // { + // dt: [ + // [ + // "text", + // "{bc}the position of a participant in a tournament who advances to the next round without playing ", + // ], + // [ + // "vis", + // [ + // { + // t: "drew a first-round {wi}bye{/wi}", + // }, + // ], + // ], + // ], + // }, + // ], + // ], + // ], + // }, + // ], + // et: [["text", "alteration of {et_link|by:2|by:2}"]], + // date: "1883", + // shortdef: [ + // "the position of a participant in a tournament who advances to the next round without playing", + // ], + // }, + // { + // meta: { + // id: "bye:1", + // uuid: "b990326a-aee9-47ff-b2e4-69b893972ccb", + // sort: "020521500", + // src: "collegiate", + // section: "alpha", + // stems: ["by", "bye", "byes"], + // offensive: false, + // }, + // hom: 1, + // hwi: { + // hw: "bye", + // prs: [ + // { + // mw: "ˈbī", + // sound: { + // audio: "by000001", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // fl: "noun", + // ins: [ + // { + // il: "plural", + // if: "byes", + // prs: [ + // { + // mw: "ˈbīz", + // sound: { + // audio: "by000003", + // ref: "c", + // stat: "1", + // }, + // }, + // ], + // }, + // ], + // def: [ + // { + // sseq: [ + // [ + // [ + // "sense", + // { + // dt: [ + // [ + // "text", + // "{bc}the position of a participant in a tournament who advances to the next round without playing ", + // ], + // [ + // "vis", + // [ + // { + // t: "drew a first-round {wi}bye{/wi}", + // }, + // ], + // ], + // ], + // }, + // ], + // ], + // ], + // }, + // ], + // et: [["text", "alteration of {et_link|by:2|by:2}"]], + // date: "1883", + // shortdef: [ + // "the position of a participant in a tournament who advances to the next round without playing", + // ], + // }, + // ]; + + const h3 = document.createElement("h3"); + h3.innerText = `${originalText} → ${translationText}`; + document.getElementById("container").appendChild(h3); + + // Only include first 3 definitions as the returned list's relevance fades further down + obj.slice(0, 3).forEach((word) => { + const { shortdef, hwi, fl } = word; + const firstLetter = translationText[0]; + const section = document.createElement("section"); + + if (hwi.prs) { + const audioFile = hwi.prs[0].sound.audio; + const audio = document.createElement("audio"); + audio.controls = true; + audio.src = `https://media.merriam-webster.com/audio/prons/en/us/mp3/${firstLetter}/${audioFile}.mp3`; + section.appendChild(audio); + } + const div = document.createElement("div"); + div.innerHTML = ` +

${hwi.prs ? hwi.prs[0].mw : ""} - ${fl}

+

${shortdef}

+ `; + section.appendChild(div); + document.getElementById("container").appendChild(section); + }); + }) + .catch((err) => console.error(err)); + }) + .catch((err) => console.error(err)); +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..8982f8e --- /dev/null +++ b/style.css @@ -0,0 +1,142 @@ +:root { + --black: rgba(51, 51, 51, 1); + --white: rgba(246, 246, 246, 1); + --accent: rgba(250, 128, 114, 1); + --3d-effect: -webkit-linear-gradient( + top, + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0.15) + ); +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; + font-weight: unset; + font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif; + letter-spacing: 0.4px; + color: var(--black); +} + +body { + max-width: 80vw; + margin: auto; + color: var(--black); + background-color: var(--white); + padding-bottom: 4em; +} + +h1 { + background-color: var(--accent); + color: var(--white); + width: 100vw; + margin: 0 -10vw 0.25em; + padding: 1em 7.5vw; + background-image: var(--3d-effect); +} + +h4 { + font-style: italic; +} + +main { + margin: 2em; +} + +main > p { + text-align: center; + margin-bottom: 1.25em; +} + +#form { + display: flex; + gap: 1em; + justify-content: center; + align-items: center; +} + +input, +select, +button { + padding: 0.5rem; + border: 0.5px solid var(--black); + border-radius: 4px; + font-size: unset; +} + +input { + height: 37px; +} + +button { + border: none; + cursor: pointer; + background-color: var(--accent); + color: var(--white); + transform: scale(1); + font-weight: 600; + background-image: var(--3d-effect); +} + +button:hover { + opacity: 0.8; +} + +button:focus { + transform: scale(0.98); +} + +input:focus, +select:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 6px var(--accent); +} + +#container { + display: flex; + flex-direction: column; + margin: 2em 0; +} + +#container h3 { + margin-top: 1em; + text-align: center; + font-size: 1.25em; +} + +#container > section { + display: flex; + flex-direction: column; + background-color: lightgrey; + padding: 1em 2em; + margin: 1em 0; + gap: 1.5em; + box-shadow: 0 0 6px var(--accent); + + div p { + margin: 0.5em 0; + } +} + +@media screen and (max-width: 1024px) { + body { + max-width: 90vw; + } + + h1 { + margin: 0 -5vw 0.25em; + padding: 1em 5vw; + } +} + +@media screen and (max-width: 768px) { + main { + margin: 2em 0; + } + + #form { + flex-direction: column; + } +}