facebook

Blog

Stay updated

How to use Azure and Google in Cloud services to translate and the Sentiment Analysis from an Angular Client
Lost in Translations: translation and Sentiment Analysis in real time
Tuesday, April 30, 2019

Foreword

Let’s go on with our journey in the cognitive services world (read the previous article), This time, we start from an Angular project. We will use Google Translate API to translate the text inserted in a text-area in real-time, taking advantage of Angular Reactive Forms and we will undergo the same text to a Sentiment Analysis at once, thanks to the Text Analytics API in Azure.

To use Google translation services, it is needed to create a project in GCP console, in which to enable the Cloud Translation API. In the project’s configuration panel, it is possible to extract the key to insert in http calls. The documentation of doable REST calls is available to the following link. Starting from it, we create two Typescript interfaces, in order to manage the request and the response.

export interface GoogleTranslateRequest {
  q: string;
  source: string;
  target: string;
  format: string;
}
export interface Translation {
  translatedText: string;
}
 
export interface Data {
  translations: Translation[];
}
 
export interface GoogleTranslateResponse {
  data: Data;
  language: string;
}

We create then a service to run the REST call through the Angular HttpClientModule. It is important to note how the environment has been imported, to recover the service’s URL and the key from the environment file. The class has a translate method which returns an Observable<GoogleTranslateResponse>

@Injectable({
  providedIn: 'root'
})
export class GoogleService {
 
  url = environment.googleTranslateUrl + environment.googleApiKey;
 
  constructor(private http: HttpClient) {}
 
  translate(obj: GoogleTranslateRequest): Observable<GoogleTranslateResponse> {
    return this.http.post<GoogleTranslateResponse>(this.url, obj);
  }
}

The Azure Text Analytics sentiment analysis service

From the Azure portal is possible to add a new resource of Artificial intelligence and Machine Learning type, Text Analyze. A URL and a key for the REST call will be linked to this resource.

In this case too, we create an interface for the request and another one for the response

export interface AzureSentimentRequest {
  documents: AzureSentimentDocument[];
}
 
export interface AzureSentimentDocument {
  language: string;
  id: number;
  text: string;
}
export interface Document {
  id: string;
  score: number;
}
 
export interface AzureSentimentResponse {
  documents: Document[];
  errors: any[];
}

We create then a service called Azureservice, that makes available a method called analyze. Languages and text to be analyzed should be passed to this method and it will return a Observable<AzureSentimentResponse>.

@Injectable({
  providedIn: 'root'
})
export class AzureService {
 
  httpOptions = {
    headers: new HttpHeaders({ 'Ocp-Apim-Subscription-Key': environment.azureTextAnalyticsKey,
    'Content-Type': 'application/json',
    Accept: 'application/json'
  })};
 
  constructor(private http: HttpClient) {}
 
  analize(lang: string, textInput: string): Observable<AzureSentimentResponse> {
 
    const body: AzureSentimentRequest = {
      documents : [
      {id: 1,
      language: lang,
      text: textInput
      }]
    };
    return this.http.post<AzureSentimentResponse>(environment.azureTextAnalyticsUrl, body, this.httpOptions);
  }
}

The component

In the app.module.ts we imported the ReactiveFormesModule, in order to make available, through an observable, the text to be analyzed originated from a textarea.

<div class="form-group">
  <label for="testoOriginale">Testo da Tradurre</label>
  <textarea class="form-control" id="testoOriginale" rows="3" [formControl]='testoOriginale'></textarea>
</div>

In the typescript code of the component, we can declare

testoIstantaneo: Observable<string> = null;

and then link to testoistantaneo the valueChanges of the formControl testoOriginale

this.testoIstantaneo = this.testoOriginale.valueChanges;

Before we create the subscribe in our Observable, we insert a “one second” waiting time, starting from the last text input, so that the call to services would not be run any time you wrote a new letter in the textarea. This is possible thanks to the operator debounce() insert in pipe to the Observable.

this.testoIstantaneo.pipe( debounceTime(1000) ).subscribe(testo => {
 
}

At this point, we are ready to run the calls to REST services in the subscribe. We request 4 translations: (from Italian to English, to Japanese, to Arabian, to Finnish), by the configuration of below objects:

private impostaTraduzione() {
    this.impostazioniTraduzione = [
      { q: '', source: 'it', target: 'en', format: 'text' },
      { q: '', source: 'it', target: 'ja', format: 'text' },
      { q: '', source: 'it', target: 'ar', format: 'text' },
      { q: '', source: 'it', target: 'fi', format: 'text' }
    ];
  }

These are the calls

this.testoIstantaneo.pipe( debounceTime(1000) ).subscribe(testo => {
     this.azureSentiment.analize('it', testo).subscribe((sentiments: AzureSentimentResponse) => {
       this.sentiments = sentiments;
     });
     this.traduzioni = [];
     this.impostazioniTraduzione.forEach(impostazione => {
       impostazione.q = testo;
       this.googleTanslatorService.translate(impostazione).subscribe((response: GoogleTranslateResponse) => {
         response.language = impostazione.target;
         this.traduzioni.push(response);
       });
     });
   });

The subscription makes available to the html template a translation array and an array including the sentiment analysis results.

<div class="form-group">
  <label for="testoOriginale">Testo da Tradurre</label>
  <textarea class="form-control" id="testoOriginale" rows="3" [formControl]='testoOriginale'></textarea>
</div>
 
<div *ngIf="sentiments && sentiments.documents">
  <div *ngFor="let sentiment of sentiments.documents">
      <div class="progress" style="margin-bottom: 10px">
          <div class="progress-bar progress-bar-striped progress-bar-animated"  [ngClass]="{
            'bg-success':sentiment.score >= 0.50,
            'bg-danger': sentiment.score < 0.50}"  role="progressbar" [attr.aria-valuenow]="sentiment.score*100" aria-valuemin="0" aria-valuemax="100"
                    [style.width.%]="sentiment.score*100"></div>
      </div>
  </div>
</div>
 
<div *ngFor="let traduzione of traduzioni">
  <app-translation [traduzione]='traduzione'></app-translation>
</div>

The component app-translation is a bootstrap card, which displays the translation result.

<div class="card text-white bg-primary mb-3">
  <div class="card-header">{{traduzione.language}}</div>
  <div class="card-body">
    <p *ngIf="traduzione && traduzione.data && traduzione.data.translations && traduzione.data.translations.length >0"
      class="card-text">{{traduzione.data.translations[0].translatedText}}</p>
  </div>
</div>

The progress bar will be green if the text analysis result is bigger or equal to 0,50 (that’s to say if it expresses a positive mood), but it becomes red for lower values.

You can find all the code here:

See you next!!