-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: samples for hybrid glossaries tutorial (#327)
* feat: Node.js samples for hybrid glossaries tutorial * fix: lint * fix: header * fix: adding package.json
- Loading branch information
Showing
4 changed files
with
308 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
/** | ||
* Copyright 2019 Google LLC | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
async function main( | ||
projectId, // Your GCP Project Id | ||
inFile = 'resources/example.png', | ||
outFile = 'resources/example.mp3', | ||
glossaryLangs = ['fr', 'en'], | ||
glossaryName = 'bistro-glossary', | ||
glossaryUri = 'gs://cloud-samples-data/translation/bistro_glossary.csv' | ||
) { | ||
// [START translate_hybrid_imports] | ||
// Imports the Google Cloud client library | ||
const textToSpeech = require('@google-cloud/text-to-speech'); | ||
const translate = require('@google-cloud/translate').v3beta1; | ||
const vision = require('@google-cloud/vision'); | ||
|
||
// Import other required libraries | ||
const fs = require('fs'); | ||
//const escape = require('escape-html'); | ||
const util = require('util'); | ||
// [END translate_hybrid_imports] | ||
|
||
// [START translate_hybrid_vision] | ||
/** | ||
* Detects text in an image file | ||
* | ||
* ARGS | ||
* inputFile: path to image file | ||
* RETURNS | ||
* string of text detected in the input image | ||
**/ | ||
async function picToText(inputFile) { | ||
// Creates a client | ||
const client = new vision.ImageAnnotatorClient(); | ||
|
||
// Performs text detection on the local file | ||
const [result] = await client.textDetection(inputFile); | ||
return result.fullTextAnnotation.text; | ||
} | ||
// [END translate_hybrid_vision] | ||
|
||
// [START translate_hybrid_create_glossary] | ||
/** Creates a GCP glossary resource | ||
* Assumes you've already manually uploaded a glossary to Cloud Storage | ||
* | ||
* ARGS | ||
* languages: list of languages in the glossary | ||
* projectId: GCP project id | ||
* glossaryName: name you want to give this glossary resource | ||
* glossaryUri: the uri of the glossary you uploaded to Cloud Storage | ||
* RETURNS | ||
* nothing | ||
**/ | ||
async function createGlossary( | ||
languages, | ||
projectId, | ||
glossaryName, | ||
glossaryUri | ||
) { | ||
// Instantiates a client | ||
const translationClient = await new translate.TranslationServiceClient(); | ||
|
||
// Construct glossary | ||
const glossary = { | ||
languageCodesSet: { | ||
languageCodes: languages, | ||
}, | ||
inputConfig: { | ||
gcsSource: { | ||
inputUri: glossaryUri, | ||
}, | ||
}, | ||
name: translationClient.glossaryPath( | ||
projectId, | ||
'us-central1', | ||
glossaryName | ||
), | ||
}; | ||
|
||
// Construct request | ||
const request = { | ||
parent: translationClient.locationPath(projectId, 'us-central1'), | ||
glossary: glossary, | ||
}; | ||
|
||
// Create glossary using a long-running operation. | ||
try { | ||
const [operation] = await translationClient.createGlossary(request); | ||
// Wait for operation to complete. | ||
await operation.promise(); | ||
console.log(`Created glossary ` + glossaryName + '.'); | ||
} catch (AlreadyExists) { | ||
console.log( | ||
'The glossary ' + | ||
glossaryName + | ||
' already exists. No new glossary was created.' | ||
); | ||
} | ||
} | ||
// [END translate_hybrid_create_glossary] | ||
|
||
// [START translate_hybrid_translate] | ||
/** | ||
* Translates text to a given language using a glossary | ||
* | ||
* ARGS | ||
* text: String of text to translate | ||
* sourceLanguageCode: language of input text | ||
* targetLanguageCode: language of output text | ||
* projectId: GCP project id | ||
* glossaryName: name you gave your project's glossary | ||
* resource when you created it | ||
* RETURNS | ||
* String of translated text | ||
**/ | ||
async function translateText( | ||
text, | ||
sourceLanguageCode, | ||
targetLanguageCode, | ||
projectId, | ||
glossaryName | ||
) { | ||
// Instantiates a client | ||
const translationClient = new translate.TranslationServiceClient(); | ||
const glossary = translationClient.glossaryPath( | ||
projectId, | ||
'us-central1', | ||
glossaryName | ||
); | ||
const glossaryConfig = { | ||
glossary: glossary, | ||
}; | ||
// Construct request | ||
const request = { | ||
parent: translationClient.locationPath(projectId, 'us-central1'), | ||
contents: [text], | ||
mimeType: 'text/plain', // mime types: text/plain, text/html | ||
sourceLanguageCode: sourceLanguageCode, | ||
targetLanguageCode: targetLanguageCode, | ||
glossaryConfig: glossaryConfig, | ||
}; | ||
|
||
// Run request | ||
const [response] = await translationClient.translateText(request); | ||
// Extract the string of translated text | ||
return response.glossaryTranslations[0].translatedText; | ||
} | ||
// [END translate_hybrid_translate] | ||
|
||
// [START translate_hybrid_text_to_speech] | ||
/** | ||
* Generates synthetic audio from plaintext tagged with SSML. | ||
* | ||
* Given the name of a text file and an output file name, this function | ||
* tags the text in the text file with SSML. This function then | ||
* calls the Text-to-Speech API. The API returns a synthetic audio | ||
* version of the text, formatted according to the SSML commands. This | ||
* function saves the synthetic audio to the designated output file. | ||
* | ||
* ARGS | ||
* text: String of plaintext | ||
* outFile: String name of file under which to save audio output | ||
* RETURNS | ||
* nothing | ||
* | ||
*/ | ||
async function syntheticAudio(text, outFile) { | ||
// Replace special characters with HTML Ampersand Character Codes | ||
// These codes prevent the API from confusing text with SSML tags | ||
// For example, '<' --> '<' and '&' --> '&' | ||
let escapedLines = text.replace(/&/g, '&'); | ||
escapedLines = escapedLines.replace(/"/g, '"'); | ||
escapedLines = escapedLines.replace(/</g, '<'); | ||
escapedLines = escapedLines.replace(/>/g, '>'); | ||
|
||
// Convert plaintext to SSML | ||
// Tag SSML so that there is a 2 second pause between each address | ||
const expandedNewline = escapedLines.replace(/\n/g, '\n<break time="2s"/>'); | ||
const ssmlText = '<speak>' + expandedNewline + '</speak>'; | ||
|
||
// Creates a client | ||
const client = new textToSpeech.TextToSpeechClient(); | ||
|
||
// Constructs the request | ||
const request = { | ||
// Select the text to synthesize | ||
input: {ssml: ssmlText}, | ||
// Select the language and SSML Voice Gender (optional) | ||
voice: {languageCode: 'en-US', ssmlGender: 'MALE'}, | ||
// Select the type of audio encoding | ||
audioConfig: {audioEncoding: 'MP3'}, | ||
}; | ||
|
||
// Performs the Text-to-Speech request | ||
const [response] = await client.synthesizeSpeech(request); | ||
// Write the binary audio content to a local file | ||
const writeFile = util.promisify(fs.writeFile); | ||
await writeFile(outFile, response.audioContent, 'binary'); | ||
console.log('Audio content written to file ' + outFile); | ||
} | ||
// [END translate_hybrid_text_to_speech] | ||
|
||
// [START translate_hybrid_integration] | ||
await createGlossary(glossaryLangs, projectId, glossaryName, glossaryUri); | ||
const text = await picToText(inFile); | ||
const translatedText = await translateText( | ||
text, | ||
'fr', | ||
'en', | ||
projectId, | ||
glossaryName | ||
); | ||
syntheticAudio(translatedText, outFile); | ||
// [END translate_hybrid_integration] | ||
} | ||
|
||
main(...process.argv.slice(2)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/** | ||
* Copyright 2019 Google LLC | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const fs = require('fs'); | ||
const {assert} = require('chai'); | ||
const {TranslationServiceClient} = require('@google-cloud/translate').v3beta1; | ||
const cp = require('child_process'); | ||
|
||
const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); | ||
|
||
const REGION_TAG = 'translate_hybrid_glossaries'; | ||
|
||
describe(REGION_TAG, () => { | ||
const translationClient = new TranslationServiceClient(); | ||
const location = 'us-central1'; | ||
const glossaryId = 'bistro-glossary'; | ||
const outputFile = 'resources/example.mp3'; | ||
|
||
before(async function() { | ||
try { | ||
fs.unlinkSync(outputFile); | ||
} catch (e) { | ||
// don't throw an exception | ||
} | ||
}); | ||
|
||
it('should create a glossary', async () => { | ||
const projectId = await translationClient.getProjectId(); | ||
const output = execSync(`node hybridGlossaries.js ${projectId}`); | ||
assert(output.includes(glossaryId)); | ||
assert( | ||
output.includes('Audio content written to file resources/example.mp3') | ||
); | ||
assert.strictEqual(fs.existsSync(outputFile), true); | ||
}); | ||
|
||
after(async function() { | ||
fs.unlinkSync(outputFile); | ||
assert.strictEqual(fs.existsSync(outputFile), false); | ||
// get projectId | ||
const projectId = await translationClient.getProjectId(); | ||
const name = translationClient.glossaryPath( | ||
projectId, | ||
location, | ||
glossaryId | ||
); | ||
const request = { | ||
parent: translationClient.locationPath(projectId, location), | ||
name: name, | ||
}; | ||
|
||
// Delete glossary using a long-running operation. | ||
// You can wait for now, or get results later. | ||
const [operation] = await translationClient.deleteGlossary(request); | ||
|
||
// Wait for operation to complete. | ||
await operation.promise(); | ||
}); | ||
}); |