Skip to content

Commit b6b77d2

Browse files
crowdusAce Nassri
authored and
Ace Nassri
committed
feat: samples for hybrid glossaries tutorial (#327)
* feat: Node.js samples for hybrid glossaries tutorial * fix: lint * fix: header * fix: adding package.json
1 parent 001f2f5 commit b6b77d2

File tree

4 files changed

+308
-1
lines changed

4 files changed

+308
-1
lines changed

translate/hybridGlossaries.js

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
async function main(
19+
projectId, // Your GCP Project Id
20+
inFile = 'resources/example.png',
21+
outFile = 'resources/example.mp3',
22+
glossaryLangs = ['fr', 'en'],
23+
glossaryName = 'bistro-glossary',
24+
glossaryUri = 'gs://cloud-samples-data/translation/bistro_glossary.csv'
25+
) {
26+
// [START translate_hybrid_imports]
27+
// Imports the Google Cloud client library
28+
const textToSpeech = require('@google-cloud/text-to-speech');
29+
const translate = require('@google-cloud/translate').v3beta1;
30+
const vision = require('@google-cloud/vision');
31+
32+
// Import other required libraries
33+
const fs = require('fs');
34+
//const escape = require('escape-html');
35+
const util = require('util');
36+
// [END translate_hybrid_imports]
37+
38+
// [START translate_hybrid_vision]
39+
/**
40+
* Detects text in an image file
41+
*
42+
* ARGS
43+
* inputFile: path to image file
44+
* RETURNS
45+
* string of text detected in the input image
46+
**/
47+
async function picToText(inputFile) {
48+
// Creates a client
49+
const client = new vision.ImageAnnotatorClient();
50+
51+
// Performs text detection on the local file
52+
const [result] = await client.textDetection(inputFile);
53+
return result.fullTextAnnotation.text;
54+
}
55+
// [END translate_hybrid_vision]
56+
57+
// [START translate_hybrid_create_glossary]
58+
/** Creates a GCP glossary resource
59+
* Assumes you've already manually uploaded a glossary to Cloud Storage
60+
*
61+
* ARGS
62+
* languages: list of languages in the glossary
63+
* projectId: GCP project id
64+
* glossaryName: name you want to give this glossary resource
65+
* glossaryUri: the uri of the glossary you uploaded to Cloud Storage
66+
* RETURNS
67+
* nothing
68+
**/
69+
async function createGlossary(
70+
languages,
71+
projectId,
72+
glossaryName,
73+
glossaryUri
74+
) {
75+
// Instantiates a client
76+
const translationClient = await new translate.TranslationServiceClient();
77+
78+
// Construct glossary
79+
const glossary = {
80+
languageCodesSet: {
81+
languageCodes: languages,
82+
},
83+
inputConfig: {
84+
gcsSource: {
85+
inputUri: glossaryUri,
86+
},
87+
},
88+
name: translationClient.glossaryPath(
89+
projectId,
90+
'us-central1',
91+
glossaryName
92+
),
93+
};
94+
95+
// Construct request
96+
const request = {
97+
parent: translationClient.locationPath(projectId, 'us-central1'),
98+
glossary: glossary,
99+
};
100+
101+
// Create glossary using a long-running operation.
102+
try {
103+
const [operation] = await translationClient.createGlossary(request);
104+
// Wait for operation to complete.
105+
await operation.promise();
106+
console.log(`Created glossary ` + glossaryName + '.');
107+
} catch (AlreadyExists) {
108+
console.log(
109+
'The glossary ' +
110+
glossaryName +
111+
' already exists. No new glossary was created.'
112+
);
113+
}
114+
}
115+
// [END translate_hybrid_create_glossary]
116+
117+
// [START translate_hybrid_translate]
118+
/**
119+
* Translates text to a given language using a glossary
120+
*
121+
* ARGS
122+
* text: String of text to translate
123+
* sourceLanguageCode: language of input text
124+
* targetLanguageCode: language of output text
125+
* projectId: GCP project id
126+
* glossaryName: name you gave your project's glossary
127+
* resource when you created it
128+
* RETURNS
129+
* String of translated text
130+
**/
131+
async function translateText(
132+
text,
133+
sourceLanguageCode,
134+
targetLanguageCode,
135+
projectId,
136+
glossaryName
137+
) {
138+
// Instantiates a client
139+
const translationClient = new translate.TranslationServiceClient();
140+
const glossary = translationClient.glossaryPath(
141+
projectId,
142+
'us-central1',
143+
glossaryName
144+
);
145+
const glossaryConfig = {
146+
glossary: glossary,
147+
};
148+
// Construct request
149+
const request = {
150+
parent: translationClient.locationPath(projectId, 'us-central1'),
151+
contents: [text],
152+
mimeType: 'text/plain', // mime types: text/plain, text/html
153+
sourceLanguageCode: sourceLanguageCode,
154+
targetLanguageCode: targetLanguageCode,
155+
glossaryConfig: glossaryConfig,
156+
};
157+
158+
// Run request
159+
const [response] = await translationClient.translateText(request);
160+
// Extract the string of translated text
161+
return response.glossaryTranslations[0].translatedText;
162+
}
163+
// [END translate_hybrid_translate]
164+
165+
// [START translate_hybrid_text_to_speech]
166+
/**
167+
* Generates synthetic audio from plaintext tagged with SSML.
168+
*
169+
* Given the name of a text file and an output file name, this function
170+
* tags the text in the text file with SSML. This function then
171+
* calls the Text-to-Speech API. The API returns a synthetic audio
172+
* version of the text, formatted according to the SSML commands. This
173+
* function saves the synthetic audio to the designated output file.
174+
*
175+
* ARGS
176+
* text: String of plaintext
177+
* outFile: String name of file under which to save audio output
178+
* RETURNS
179+
* nothing
180+
*
181+
*/
182+
async function syntheticAudio(text, outFile) {
183+
// Replace special characters with HTML Ampersand Character Codes
184+
// These codes prevent the API from confusing text with SSML tags
185+
// For example, '<' --> '&lt;' and '&' --> '&amp;'
186+
let escapedLines = text.replace(/&/g, '&amp;');
187+
escapedLines = escapedLines.replace(/"/g, '&quot;');
188+
escapedLines = escapedLines.replace(/</g, '&lt;');
189+
escapedLines = escapedLines.replace(/>/g, '&gt;');
190+
191+
// Convert plaintext to SSML
192+
// Tag SSML so that there is a 2 second pause between each address
193+
const expandedNewline = escapedLines.replace(/\n/g, '\n<break time="2s"/>');
194+
const ssmlText = '<speak>' + expandedNewline + '</speak>';
195+
196+
// Creates a client
197+
const client = new textToSpeech.TextToSpeechClient();
198+
199+
// Constructs the request
200+
const request = {
201+
// Select the text to synthesize
202+
input: {ssml: ssmlText},
203+
// Select the language and SSML Voice Gender (optional)
204+
voice: {languageCode: 'en-US', ssmlGender: 'MALE'},
205+
// Select the type of audio encoding
206+
audioConfig: {audioEncoding: 'MP3'},
207+
};
208+
209+
// Performs the Text-to-Speech request
210+
const [response] = await client.synthesizeSpeech(request);
211+
// Write the binary audio content to a local file
212+
const writeFile = util.promisify(fs.writeFile);
213+
await writeFile(outFile, response.audioContent, 'binary');
214+
console.log('Audio content written to file ' + outFile);
215+
}
216+
// [END translate_hybrid_text_to_speech]
217+
218+
// [START translate_hybrid_integration]
219+
await createGlossary(glossaryLangs, projectId, glossaryName, glossaryUri);
220+
const text = await picToText(inFile);
221+
const translatedText = await translateText(
222+
text,
223+
'fr',
224+
'en',
225+
projectId,
226+
glossaryName
227+
);
228+
syntheticAudio(translatedText, outFile);
229+
// [END translate_hybrid_integration]
230+
}
231+
232+
main(...process.argv.slice(2));

translate/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
},
1414
"dependencies": {
1515
"@google-cloud/automl": "^1.0.0",
16+
"@google-cloud/text-to-speech": "^1.1.4",
1617
"@google-cloud/translate": "^4.1.3",
18+
"@google-cloud/vision": "^1.2.0",
1719
"yargs": "^14.0.0"
1820
},
1921
"devDependencies": {
22+
"@google-cloud/storage": "^3.2.1",
2023
"chai": "^4.2.0",
21-
"@google-cloud/storage": "^3.0.0",
2224
"mocha": "^6.0.0",
2325
"uuid": "^3.3.2"
2426
}

translate/resources/example.png

10.9 KB
Loading
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
const fs = require('fs');
19+
const {assert} = require('chai');
20+
const {TranslationServiceClient} = require('@google-cloud/translate').v3beta1;
21+
const cp = require('child_process');
22+
23+
const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'});
24+
25+
const REGION_TAG = 'translate_hybrid_glossaries';
26+
27+
describe(REGION_TAG, () => {
28+
const translationClient = new TranslationServiceClient();
29+
const location = 'us-central1';
30+
const glossaryId = 'bistro-glossary';
31+
const outputFile = 'resources/example.mp3';
32+
33+
before(async function() {
34+
try {
35+
fs.unlinkSync(outputFile);
36+
} catch (e) {
37+
// don't throw an exception
38+
}
39+
});
40+
41+
it('should create a glossary', async () => {
42+
const projectId = await translationClient.getProjectId();
43+
const output = execSync(`node hybridGlossaries.js ${projectId}`);
44+
assert(output.includes(glossaryId));
45+
assert(
46+
output.includes('Audio content written to file resources/example.mp3')
47+
);
48+
assert.strictEqual(fs.existsSync(outputFile), true);
49+
});
50+
51+
after(async function() {
52+
fs.unlinkSync(outputFile);
53+
assert.strictEqual(fs.existsSync(outputFile), false);
54+
// get projectId
55+
const projectId = await translationClient.getProjectId();
56+
const name = translationClient.glossaryPath(
57+
projectId,
58+
location,
59+
glossaryId
60+
);
61+
const request = {
62+
parent: translationClient.locationPath(projectId, location),
63+
name: name,
64+
};
65+
66+
// Delete glossary using a long-running operation.
67+
// You can wait for now, or get results later.
68+
const [operation] = await translationClient.deleteGlossary(request);
69+
70+
// Wait for operation to complete.
71+
await operation.promise();
72+
});
73+
});

0 commit comments

Comments
 (0)