At SAP Japan, diverse teams face communication challenges due to language barriers, hindering collaboration and innovation. We revived a forgotten language exchange app, empowering it with AI to revolutionize internal communication. Our app proactively addresses language barriers in talent acquisition, customer engagement, and networking events.
This blog will dive deeper into how this BTP & AI-powered app simplifies language learning, why multilingual support is crucial in a global organization, and how it fosters global connection and collaboration. Witness the journey of AI transforming communication at SAP Japan, one conversation at a time.
The diagram provided below illustrates a summary of the business process of the application:
Business Process Diagram
The diagram provided below illustrates a summary of the application configuration and the flow of requests between components:
Architecture Diagram
The application consists of creating multiple services which are being called from SAP Build. The code below shows the CAP application schema definition
using {managed} from '@sap/cds/common';
namespace sap.capire.languageexchange;
entity User {
key email : String(255) not null;
firstName : String(100) not null;
lastName: String (100) not null;
description: LargeString;
summary: LargeString;
canTeach: LargeString;
wantToLearn: LargeString;
overseasPartner: String(10);
country: String (100);
imageData: LargeBinary @Core.MediaType: imageType;
imageType: String @Core.IsMediaType;
languagesToLearn: many String(255);
languagesToTeach: many String(255);
latitude: Decimal(9,6); // GPS Latitude
longitude: Decimal(9,6); // GPS Longitude
messages: Association to many Message on messages.receiverEmail = $self;
favorites: Association to many Favorite on favorites.favoriteUserEmail = email;
}
entity Message {
key ID: UUID;
senderEmail: Association to User on senderEmail.email = sender_email ;
sender_email: type of User:email;
receiverEmail : Association to User on receiverEmail.email = receiver_email ;
receiver_email: type of User:email;
content : LargeString;
status : String(20);
}
entity Favorite {
key userEmail: String(255) not null;
key favoriteUserEmail :String(255) not null ;
}
entity Languages {
key ID: Integer;
ISOCODE: String(2);
SPRSL: String(2);
SPTXT: String(200);
SPTXT2: String(200);
}
entity Countries {
key ID: Integer;
ISOCODE: String(2);
LAND1: String(2);
LANDX: String(200);
NATIO:String(200);
LANDX50: String(200);
NATIO50: String(200);
}
The code below shows the code to expose the services using the services.cds file
using {sap.capire.languageexchange as languageexchange} from '../db/schema';
@path:'service/languageexchange'
service LanguageExchange{
entity User as projection on languageexchange.User;
entity Message as projection on languageexchange.Message;
entity Favorite as projection on languageexchange.Favorite;
entity Languages as projection on languageexchange.Languages;
entity Countries as projection on languageexchange.Countries;
}
Loaded initial data via csv file that is exported from our on-premise S/4 HANA master data into both the Countries and Languages table so that we can dynamically set the language of the application and country text based on the output given by OpenAI using the Resume Uploader Flask API.
Initial data for countries table
Initial data for languages table
For integration with SAP Build Apps we also had to account for the CORS policies
"use strict";
const cds = require("@sap/cds");
const cors = require("cors");
cds.on("bootstrap", app => app.use(cors()));
module.exports = cds.server;
With all the backend services in place, I then proceeded to deploy the CAP application to a HANAHDI Container hosted on Kyma with the help of Helm, Pack, and Docker. Details on how to perform this process can be found in this tutorial.
Then I proceed to integrate the CAP Application into Build Apps simply by establishing an ODataIntegration. Details on how to perform this process can be found in this tutorial.
Please refer to the logic canvas below to see how multilingual support is established in SAP Build Apps
Overview of the Logic Canvas
The custom javascript function getLocale will get the current locale from the user's browser
getLocale Function
The custom javascript function Return Language based on Browser Locale and Return Countries based on Browser Locale will filter from the languages and countries table and return the list of languages and countries based on the user's browser locale
Return Languages based on Browser Locale Function
In SAP Build Apps we set the supported languages, fallback languages, and any other in-app text manually
The list of users can be retrieved using the Get record flow function and a list of objects will be returned in JSON format.
Overview of Logic Canvas
Based on the output of the Get record flow function, filter by the user's preferences such as whether he/she is willing to meet an overseas partner, followed by the type of languages that the users can speak and lastly sort them by alphabetical order.
const currentLoggedInUser = inputs.input1;
const odataURL = "https://languageexchange-srv-language-exchange.c-290ae5b.kyma.shoot.live.k8s-hana.ondemand.com/service/languageexchange/User";
var output_ = {};
// Make a GET request to fetch user records
await fetch(odataURL)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
// Filter the user records based on selected criteria
const current_user_languages_to_learn = currentLoggedInUser.languagesToLearn;
const isOverseasPartner = currentLoggedInUser.overseasPartner === "Yes";
// Filter users based on language preferences, overseasPartner field, and country
const allLanguagesMatchedUsers = data.value.filter(user => {
const userIsInSameCountry = user.country === currentLoggedInUser.country;
const userIsInDifferentCountry = user.country !== currentLoggedInUser.country;
const userHasOverseasPartner = user.overseasPartner === "Yes";
if ((userHasOverseasPartner && (isOverseasPartner && !userIsInSameCountry)) || (!userHasOverseasPartner && (!isOverseasPartner && userIsInDifferentCountry))) {
return current_user_languages_to_learn.every(language => user.languagesToTeach.includes(language));
} else {
return false;
}
});
const someLanguagesMatchedUsers = data.value.filter(user => {
const userIsInSameCountry = user.country === currentLoggedInUser.country;
const userIsInDifferentCountry = user.country !== currentLoggedInUser.country;
const userHasOverseasPartner = user.overseasPartner === "Yes";
if ((userHasOverseasPartner && (isOverseasPartner && !userIsInSameCountry)) || (!userHasOverseasPartner && (!isOverseasPartner && userIsInDifferentCountry))) {
return current_user_languages_to_learn.some(language => user.languagesToTeach.includes(language)) && !allLanguagesMatchedUsers.includes(user);
} else {
return false;
}
});
// Sort users alphabetically by first name
allLanguagesMatchedUsers.sort((a, b) => a.firstName.localeCompare(b.firstName));
someLanguagesMatchedUsers.sort((a, b) => a.firstName.localeCompare(b.firstName));
// Concatenate the sorted arrays to get the final result
const matching_users = allLanguagesMatchedUsers.concat(someLanguagesMatchedUsers);
// Log or use the sorted matching_users array as needed
// Log the filtered user records
console.log(matching_users);
output_ = matching_users;
})
.catch(error => {
console.error("Error fetching or processing data:", error);
});
return { result: output_ };
We use the GPS location flow function to retrieve the current latitude and longitude from the user's device.
Overview of Logic Canvas
Based on the latitude and longitude that have been retrieved, I created a custom javascript function that used the Haversine formula to filter the list of users within 1km - 1000km.
const excludedUser = inputs.currentEmail;
const odataURL = "https://languageexchange-srv-language-exchange.c-290ae5b.kyma.shoot.live.k8s-hana.ondemand.com/service/languageexchange/User";
const lat = inputs.lat;
const long = inputs.long;
const distanceThreshold = inputs.distance; // in meters
console.log(distanceThreshold);
output_ = {};
// Make a GET request to fetch user records
await fetch(odataURL)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
// Filter the user records based on selected criteria
const nearbyUsers = data.value.filter(user => {
const shouldExcludeUser = user.email === excludedUser;
if (shouldExcludeUser) {
return false;
}
// Calculate distance between two points using Haversine formula
const userLat = user.latitude;
const userLong = user.longitude;
const radlat1 = Math.PI * lat / 180;
const radlat2 = Math.PI * userLat / 180;
const theta = long - userLong;
const radtheta = Math.PI * theta / 180;
let distance = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
distance = Math.acos(distance);
distance = distance * 180 / Math.PI;
distance = distance * 60 * 1.1515 * 1.609344 * 1000; // in meters
return distance <= distanceThreshold;
});
console.log(nearbyUsers);
nearbyUsers.sort((a, b) => {
// Compare first names
let firstNameComparison = a.firstName.localeCompare(b.firstName);
// If first names are the same, compare last names
if (firstNameComparison === 0) {
return a.lastName.localeCompare(b.lastName);
}
return firstNameComparison;
});
// Log the filtered user records
output_ = nearbyUsers;
})
.catch(error => {
console.error("Error fetching or processing data:", error);
});
return { result: output_ };
You may view the demo of the application here
Leveraging the SAP BTP platform's robust capabilities, I seamlessly integrated and facilitated communication between various applications. Guided by comprehensive tutorials, I delved into CAP development, gaining practical experience within a week. This enabled me to confidently add, edit, and create services and functions within the CAP application, ultimately deploying it to Cloud Foundry with ease.
Beyond the core platform, the integration with openAI provided valuable insights into their functionalities. This enriched my understanding of the underlying mechanisms and solidified my technical foundation.
I trust you found this article informative. Feel free to share your thoughts or ask any questions in the SAP Build Question section. I am always happy to engage in further discussions.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
17 | |
15 | |
13 | |
11 | |
9 | |
8 | |
8 | |
8 | |
7 |