Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Added localization #76

Merged
merged 4 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions config/language.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"FULFILLMENT_STATUS_CODES":{
"order-picked-up":{
"message": "Your order has been picked up"
},
"order-on-the-way": {
"message": "Your order is on the way"
},
"order-delivered":{
"message": "Your order has been delivered"
},
"ticket-issued":{
"message": "Your ticket has been issued"
},
"ticket-validated":{
"message": "Your ticket has been validated"
},
"charging-started": {
"message": "Your charging has started"
},
"charging-stopped":{
"message": "Your charging has stopped"
},
"charger-not-working":{
"message": "Hi, it looks likes your charger is not working. DO you want to find other chargin stations?"
},
"charging-completed":{
"message": "Hi, your chargin is complete!"
},
"checked-in": {
"message": "You have succesfully checked-in to your stay!"
},
"checked-out":{
"message": "You have succesfully checked-out from your stay!"
}
},
"ALL_MESSAGES": {
"session_cleared": "Your session has been cleared! You're all set to start a new one.",
"session_and_profile_cleared": "Both your session and profile have been cleared! You're ready to start fresh.",
"route_selected": "Your route is all set! Here's your navigation link: ${url}. What's next on your agenda?",
"request_in_progress": "Hang tight! We're currently processing your request...",
"request_processed": "All done! Your request has been processed and we're getting your response ready...",
"request_failed": "Oops, we hit a snag processing your request. Want to give it another shot?",
"request_to_beckn_failed": "Looks like we couldn't get that request through. How about we try something different?",
"incident_on_road": "Heads up: ${message}. Could you share your current location so I can find some alternative routes for you?",
"formatting_failed": "We had trouble understanding that instruction.",
"api_call_failed": "Oops, we ran into an issue calling the API.",
"failed_to_process_instruction": "We had trouble processing that instruction.",
"missing_source": "Mind sharing the starting point for your journey?",
"missing_destination": "Could you let us know your destination?",
"route_list_description": "Here are some route options for you. Which one do you prefer?"
}
}
4 changes: 2 additions & 2 deletions config/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"item.descriptor should not be used in search intent for this domain",
"search must have two stops for this domain.",
"Supported stop.type : check-in, check-out",
"fulfillment stops should not have location for this domain.",
"fulfillment.stops[i].time should be an object and contain timestamp"
"fulfillment.stops[i].time should be an object and contain timestamp",
"fulfillment.stops[i].time should only be used if explicity shared by the user"
]
},
"tourism": {
Expand Down
15 changes: 8 additions & 7 deletions controllers/Bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import DBService from '../services/DBService.js'
import logger from '../utils/logger.js'
import { v4 as uuidv4 } from 'uuid'
import MapsService from '../services/MapService.js'
import get_text_by_key from '../utils/language.js'
const mapService = new MapsService()

const actionsService = new ActionsService()
Expand Down Expand Up @@ -167,11 +168,11 @@ async function process_text(req, res) {
...EMPTY_SESSION,
profile: session.profile
};
response.formatted = 'Session cleared! You can start a new session now.';
response.formatted = get_text_by_key('session_cleared');
}
else if(ai.action?.action === 'clear_all'){
session = EMPTY_SESSION;
response.formatted = 'Session & profile cleared! You can start a new session now.';
response.formatted = get_text_by_key('session_and_profile_cleared');
}
else if(ai.action?.action === 'get_routes'){
const routes = await mapService.generate_routes(message, session.text, session.avoid_point|| []);
Expand All @@ -192,7 +193,7 @@ async function process_text(req, res) {
const index= Math.max(1-details_response.index,0);
session.selected_route = session.routes[index];
const url = `https://www.google.com/maps/dir/${session.selected_route.source_gps.lat},${session.selected_route.source_gps.lng}/${session.selected_route.destination_gps.lat},${session.selected_route.destination_gps.lng}/`;
route_response.message = `Your route has been actived. Here is the link to navigate : ${url}. What do you want to do next?`;
route_response.message = get_text_by_key('route_selected', {url: url});
const map_image_url = await mapService.get_static_image_path([session.selected_route]);
if(map_image_url){
const map_image_url_server = await actionsService.download_file(map_image_url);
Expand Down Expand Up @@ -284,7 +285,7 @@ async function process_action(action, text, session, sender=null, format='applic
ai.action = action;
ai.bookings = session.bookings;

format!='application/json' && await actionsService.send_message(sender, `_Please wait while we process your request through open networks..._`)
format!='application/json' && await actionsService.send_message(sender, get_text_by_key('request_in_progress'))

// Get schema
const schema = await ai.get_schema_by_action(action.action);
Expand Down Expand Up @@ -343,10 +344,10 @@ async function process_action(action, text, session, sender=null, format='applic
if(request.status){
// call api
const api_response = await actionsService.call_api(request.data.url, request.data.method, request.data.body, request.data.headers)
format!='application/json' && await actionsService.send_message(sender, `_Your request is processed, generating a response..._`)
format!='application/json' && await actionsService.send_message(sender, get_text_by_key('request_processed'))
if(!api_response.status){
logger.error(`Failed to call the API: ${api_response.error}`)
response.formatted = 'Request could not be processed. Do you want to try again?'
response.formatted = get_text_by_key('request_failed')
}
else{

Expand Down Expand Up @@ -376,7 +377,7 @@ async function process_action(action, text, session, sender=null, format='applic

}
else{
response.formatted = "Could not process this request. Can you please try something else?"
response.formatted = get_text_by_key('request_to_beckn_failed')
}
}

Expand Down
3 changes: 2 additions & 1 deletion controllers/ControlCenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../utils/constants.js'
import DBService from '../services/DBService.js'
import MapsService from '../services/MapService.js'
import get_text_by_key from '../utils/language.js'

const action = new Actions()

Expand Down Expand Up @@ -118,7 +119,7 @@ export const notify = async (req, res) => {
// send whatsapp and add to context
if(status){
try{
const reply_message = `${message}. Please share your current location and I'll try to find some alternate routes?`
const reply_message = get_text_by_key('incident_on_road', {message: message})
await action.send_message(session.key, reply_message);

// update session
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"scripts": {
"test": "NODE_ENV=test mocha tests --recursive --timeout 900000 -r dotenv/config --exit",
"test:unit": "NODE_ENV=test mocha tests/unit --recursive --timeout 0 -r dotenv/config --exit",
"test:unit": "NODE_ENV=test mocha tests/unit tests/utils --recursive --timeout 0 -r dotenv/config --exit",
"test:apis": "NODE_ENV=test mocha tests/apis --recursive --timeout 0 -r dotenv/config --exit",
"start": "nodemon --env-file=.env server.js",
"dev": "NODE_ENV=dev && npm start",
Expand Down
7 changes: 4 additions & 3 deletions services/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {createWriteStream} from 'fs'
import path from 'path'
import { v4 as uuidv4 } from 'uuid'
import { fileURLToPath } from 'url';
import get_text_by_key from '../utils/language.js'
const __filename = fileURLToPath(import.meta.url);
const accountSid = process.env.TWILIO_ACCOUNT_SID
const authToken = process.env.TWILIO_AUTH_TOKEN
Expand Down Expand Up @@ -64,7 +65,7 @@ class Actions {
async process_instruction(message, context=[]) {
let response = {
status: false,
formatted: 'Failed to process the instruction',
formatted: get_text_by_key('formatting_failed'),
}
try {

Expand All @@ -82,7 +83,7 @@ class Actions {
logger.info(`Making api call...`)
const call_api_response = await this.call_api(beckn_request.data.url, beckn_request.data.method, beckn_request.data.body, beckn_request.data.headers)
if(!call_api_response.status){
response.formatted = `Failed to call the API: ${call_api_response.error}`
response.formatted = get_text_by_key('api_call_failed')
response.data = call_api_response.data
}
else{
Expand All @@ -104,7 +105,7 @@ class Actions {
}
} catch (error) {
logger.error(`Error processing instruction: ${error.message}`)
response.formatted = `Failed to process the instruction: ${error.message}`
response.formatted = get_text_by_key('failed_to_process_instruction')
}

return response;
Expand Down
11 changes: 6 additions & 5 deletions services/MapService.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import logger from '../utils/logger.js'
import AI from './AI.js'
const ai = new AI();
import polyline from '@mapbox/polyline';
import get_text_by_key from "../utils/language.js";


class MapsService {
Expand Down Expand Up @@ -84,10 +85,10 @@ class MapsService {
logger.info(JSON.stringify(details, null, 2));
if(!details.source || !details.destination) {
if (!details.source ) {
response.errors.push("Can you please specify the source location?");
response.errors.push(get_text_by_key('missing_source'));
}
if (!details.destination) {
response.errors.push("Can you please specify the destination location?");
response.errors.push(get_text_by_key('missing_destination'));
}
}
else{
Expand All @@ -97,10 +98,10 @@ class MapsService {

if(!source_gps || !destination_gps) {
if(!source_gps) {
response.errors.push("Can you please specify the source location?");
response.errors.push(get_text_by_key('missing_source'));
}
if(!destination_gps) {
response.errors.push("Can you please specify the destination location?");
response.errors.push(get_text_by_key('missing_destination'));
}
}
else{
Expand All @@ -120,7 +121,7 @@ class MapsService {
logger.info(`Route image path : ${path}`)

response.data.routes_formatted = {
"description": `these are the various routes that you can take. Which one would you like to select:`,
"description": get_text_by_key('route_list_description'),
"routes": response.data.routes.map((route, index) => `Route ${index+1}: ${route.summary}`)
}
response.status = true;
Expand Down
18 changes: 18 additions & 0 deletions tests/utils/language.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, it} from 'mocha'
import * as chai from 'chai'
import get_text_by_key from '../../utils/language.js'
const expect = chai.expect

describe('test cases for language utils', ()=>{
it('Should return valid messge from language key', ()=>{
const message = get_text_by_key('session_cleared')
expect(message).to.be.a('string');
})

it('Should return valid messge from language key using variable', ()=>{
let key = 'KEY_TO_MATCH'
const message = get_text_by_key('route_selected', {url:key})
expect(message).to.be.a('string');
expect(message).to.contain(key);
})
})
16 changes: 16 additions & 0 deletions utils/language.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { readFileSync } from 'fs';
const language = JSON.parse(readFileSync('./config/language.json'))


function get_text_by_key(key, variables = {}, category='ALL_MESSAGES'){
let text = language[category][key] || null;
if (text) {
Object.keys(variables).forEach(variable => {
/* eslint no-useless-escape: "off" */
text = text.replace(new RegExp(`\\$\{${variable}\}`, 'g'), variables[variable]);
});
}
return text;
}

export default get_text_by_key;
Loading