Learn the Maasai Language the Fun Way

October 20, 2022
Written by
Michael Jaroya
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Learn the Maasai Language the Fun Way

Learning a new language can be hard!

You get passionate about learning a new language for work reasons or because you plan to go on a vacation and want to immerse yourself into the culture and interact with the locals. You sign up for a course, hire an online tutor, or start watching youtube videos. You are psyched and keep this up for a minute, but it quickly starts feeling boring and like a chore.

Sound familiar? Yes, I know; we have all been there. Don't worry. The Maasai language app is up to the rescue!

The app enables you to learn the Maasai language in an easy and fun way. No hassles! Just open WhatsApp and start learning.

Why the Maasai language? The Maasai culture has a wealth of life lessons, moral principles, and practices that everyone should yearn to learn. Plus, if you ever want to see the world's eighth wonder, the wildebeest migration, you have to learn at least some basic Maasai language. But, in return, you will brush shoulders with Maasai people during your vacation.

How does the language app work?

  1. You text the word "Learn" to a Twilio WhatsApp number.
  2. You immediately get a picture response, which contains a multiple-choice question.
  3. You respond with your answer. The language app then informs you if you are wrong or right. If you are wrong, you try again. If you are correct, then congratulations! You are shown an example Maasai sentence with the word and an English translation of the example; an example is vital for the learner to understand the word usage context.
  4. Text the word "next" to see a picture with the next question, and the process repeats itself.

Prerequisites

To follow this tutorial, you need the following:

Create a New Laravel Project

To start off, create a new Laravel project using the Composer create-project command, and change into the newly created Laravel project directory, by running the commands below.

$ composer create-project laravel/laravel learn-maasai
$ cd learn-maasai

Install Twilio SDK

Next, install the Twilio PHP Helper Library to enable easier interaction with the Twilio API from the application by running the command below:

composer require twilio/sdk

Initialize the database

To create the learn_maasai database, login into your MySQL instance and run the following command:

create database learn_maasai;

Set the required environment variables

To set the required environment variables, add the following entries to the end of .env in the top-level directory of the project:

TWILIO_AUTH_TOKEN="<<Your Auth Token>>"
TWILIO_SID="<<Your Account SID>>"
TWILIO_WHATSAPP_NUMBER="<<Your Twilio Whatsapp Number>>" 

Then, retrieve your Twilio Account SID and Auth Token from the Account Info section of the Twilio Console, and your WhatsApp sandbox number from the WhatsApp Sandbox and insert them in place of the respective placeholders in .env.  

To send a WhatsApp message from your application, you'll need to activate the Twilio Sandbox for WhatsApp.

Then, update the following three, existing, settings in .env with the respective details from your MySQL instance.

DB_DATABASE=learn_maasai
DB_USERNAME="<<Your MySQL username>>"
DB_PASSWORD="<<Your MySQL password>>"

Then, set the Twilio-specific environment variables by adding the following array configuration to the end of the array returned from config/services.php.

'twilio' => [ 
    'sid' => env('TWILIO_SID'),
    'auth_token' => env('TWILIO_AUTH_TOKEN'), 
    'whatsapp_number' => env('TWILIO_WHATSAPP_NUMBER') 
],

Build the Maasai Language App

Now, it's time to add the business logic. As outlined earlier, the application will be able to:

  • Send a picture with a question
  • Check the learner's choice against the correct answer and respond accordingly
  • Store the user details and learner responses

Let's build this functionality step-by-step.

Add a Model, Controller, Migration, and Seeder

The app needs to listen to API requests and respond with a follow-up question or an answer. To achieve this, you need to create a questions table using a migration, seed the questions data into the table, and create a model and a controller to handle the logic.

To complete these four steps, run the following Artisan command:

php artisan make:model Question -m -c -s

The command creates four files:

  • The Model: app/Models/Question.php
  • The Controller: app/Http/Controllers/QuestionController.php
  • The Seeder: database/seeders/QuestionSeeder.php
  • The database migration: database/migrations/{current timestamp}__create_questions_table.php

Now, open the migration file and update the up() method to match the following code:

<?php

Schema::create('questions', function (Blueprint $table) {
    $table->id();
    $table->string('english_word');
    $table->string('maasai_word');
    $table->string('question_image')->nullable()->comment("The question image path");
    $table->string('answer_image')->nullable()->comment("The answer image path");
    $table->string('choice_pull')->nullable()->comment("The pull of choices for this number. To help differentiate which question the learner is answering.");
    $table->string('right_choice')->nullable()->comment("The correct choice");
    $table->timestamps();
});

Then, open database/seeders/QuestionSeeder.php and update the run() method to match the following code

<?php

$numbers =[
    ["One","Nabo","A,B,C","B","question.png","answer.png"],
    ["Two","Are","D,E,F","F","question.png","answer.png"],
    ["Three","Uni","G,H,I","G","question.png","answer.png"],
    ["Four","Ong'uan","J,K,L","J","question.png","answer.png"],
    ["Five","Imiet","M,N,O","N","question.png","answer.png"],
    ["Six","Ile","P,Q,R","Q","question.png","answer.png"],
    ["Seven","Naapishana","S,T,U","S","question.png","answer.png"],
    ["Eight","Isiet","V,W,X","X","question.png","answer.png"],
    ["Nine","Naaudo","Y,Z,AA","Z","question.png","answer.png"],
    ["Ten","Tomon","AB,AC,AD","AB","question.png","answer.png"]
];
      
foreach($numbers as $number){
    $question = new Question();
    $question->english_word =$number[0];
    $question->maasai_word =$number[1];
    $question->question_image =$number[4];
    $question->answer_image =$number[5];
    $question->choice_pull =$number[2];
    $question->right_choice =$number[3];
    $question->save();
}

Then, add the following use statement to the top of the class.

use App\Models\Question;

After that, open database/seeders/DatabaseSeeder.php and update the run() method to match the code below.

$this->call([
    QuestionSeeder::class,
]);

The call() method executes database/seeders/QuestionSeeder.php when seeding the database.

Fourthly, open app/Models/Question.php and add the following functions to the end of the class.

<?php

/**
  * Send whatsapp message
  * @param $to
  * @param $image_path
  * @return string
  */
public static function send($to,$image_path)
{
    $from = config('services.twilio.whatsapp_number');
    $twilio = new Client(config('services.twilio.sid'), config('services.twilio.auth_token'));
    return $twilio->messages->create('whatsapp:' . $to, [
    "from" => 'whatsapp:' . $from,
    "mediaUrl" => [$image_path],
    ]);
}

/**
  * Fetch question based on learner's response
  * @param $to
  * @param $user_id
  * @return string
  */
public static function fetchQuestion($response, $user_id)
{
    $next_question = Question::select('question_image', 'id');

    if ($response == 'next') {
        $current_session = Session::where('user_id', $user_id)
            ->whereRaw('CURDATE(NOW()) = CURDATE("created_at")')
            ->pluck('question_id')->toArray();

        if ($current_session) {
            $next_question->whereNotIn('question_id', $current_session);
        }
    }

    $question = $next_question->inRandomOrder()->first();

    Session::logSession($user_id, $question->id);

    return $question->question_image;
}

Then, add the following use statement to the top of the class.

use Twilio\Rest\Client;

The send() function accepts two parameters: the WhatsApp receiver number, and the image path. It then creates a new instance of the Twilio\Rest\Client class passing it the Twilio configurations. The create() method is called passing from and mediaUrl parameters. Finally, the WhatsApp message is sent.

The fetchQuestion() method accepts two parameters: the response from the user and the user id. It then picks a random question and returns the image path. If the response from the user is "next", it checks all the questions they have been asked and excludes those from the query.

Next, add the logic to receive and send WhatsApp messages, in the code example below, to app/Http/Controllers/QuestionController.php:

public function reply(Request $request)
{
    $response = trim($request->Body);
    $whatsapp_number = str_replace("whatsapp:","",$request->From);
    $user = User::where('whatsapp_number', $whatsapp_number)->first();

    if (!$user) {
        $user = new User();
        $user->whatsapp_number = $whatsapp_number;
        $user->save();
    }

    if (strlen($response) == 1) {
        $question = Question::where('choice_pull', 'like', '%' . $response . '%')
            ->first();
        
        if ($question) {      
            $path = 'wrong.png';

            if (strtolower($response)  == strtolower($question->right_choice)) {
                Session::logSession($user->id, $question->id,true);
                $path = $question->answer_image;
            }
        }
    } else {
        $path = Question::fetchQuestion($response, $user->id);
    }

    return Question::send($whatsapp_number, url('images/' . $path));
}

Then, add the following use statements to the top of the class.

use App\Models\Question;
use App\Models\Session;
use App\Models\User;

The reply() function logs the user's WhatsApp number, checks if the incoming message is from a new user or a user responding to the previously asked questions, and then sends replies accordingly.

Add a session Model and migration file

The app also needs to track the learner's progress, hence the need for a session table.

To create the supporting Model file, run the following Artisan command:

php artisan make:model Session -m

The command creates two files:

  • The model: app/Models/Session.php
  • The database migration: database/migrations/{current timestamp}__create_sessions_table.php

Open the sessions migration file and update the up() method to match the following code

<?php
Schema::create('sessions', function (Blueprint $table) {
    $table->id();
    $table->integer('user_id');
    $table->integer('question_id');
    $table->integer('status')->default(0);
    $table->timestamps();
});

Then, update app/Models/Session.php, add update the logSession() method as shown below:

<?php

public static function logSession($user_id, $question_id,$status_update = false)
{
    if ($status_update) {
        $session = Session::where('question_id',$question_id)
            ->where('user_id',$user_id)
            ->orderBy('id')
            ->first();

        if ($session) {
            $session->status = 1;
            $session->save();
            return;
        }
    }

    $session = new Session();
    $session->user_id = $user_id;
    $session->question_id = $question_id;
    $session->save();
}

The logSession() method accepts three parameters; user id, the question id, and the status update check, which is false by default. The function logs the learner's responses. If the learner picks the right choice, it updates the status to one.

Update the user migration

Laravel ships with a users' migration file and model. However, for this app, you won't handle authentication; therefore, most of the columns are not needed.

So, open the user's migration file (database/migrations/2014_10_12_000000_create_users_table.php) and update the up() method as shown below:

<?php
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('whatsapp_number');
    $table->timestamps();
});

Now that you have all the migration files ready, it is time to create the respective tables in the database, by executing the migrate Artisan command below.

php artisan migrate

You also need to seed the questions table. To do this, execute the db:seed Artisan command below

php artisan db:seed

To confirm if the database has been populated with data, log in to your MySQL instance and run the following commands.

use learn_maasai;

select * from questions;

+----+--------------+-------------+----------------+--------------+-------------+--------------+---------------------+---------------------+
| id | english_word | maasai_word | question_image | answer_image | choice_pull | right_choice | created_at          | updated_at          |
+----+--------------+-------------+----------------+--------------+-------------+--------------+---------------------+---------------------+
|  1 | One          | Nabo        | question.png   | answer.png   | A,B,C       | B            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  2 | Two          | Are         | question.png   | answer.png   | D,E,F       | F            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  3 | Three        | Uni         | question.png   | answer.png   | G,H,I       | G            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  4 | Four         | Ong'uan     | question.png   | answer.png   | J,K,L       | J            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  5 | Five         | Imiet       | question.png   | answer.png   | M,N,O       | N            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  6 | Six          | Ile         | question.png   | answer.png   | P,Q,R       | Q            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  7 | Seven        | Naapishana  | question.png   | answer.png   | S,T,U       | S            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  8 | Eight        | Isiet       | question.png   | answer.png   | V,W,X       | X            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
|  9 | Nine         | Naaudo      | question.png   | answer.png   | Y,Z,AA      | Z            | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
| 10 | Ten          | Tomon       | question.png   | answer.png   | AB,AC,AD    | AB           | 2022-09-29 09:04:17 | 2022-09-29 09:04:17 |
+----+--------------+-------------+----------------+--------------+-------------+--------------+---------------------+---------------------+
10 rows in set (0.00 sec)

Define the API Route

Finally, add the API route. You will add this endpoint in the Twilio console as a Webhook.

At the end of routes/api.php, add the following route definition:

Route::post('/reply', [QuestionController::class, 'reply']);

Then, add the following use statement to the top of the file.

use App\Http\Controllers\QuestionController;

Install the images

To finish up the application, you now need to download the images for the answer, question, and wrong responses. Create a new directory, named images, inside the public directory, then download the three images from the GitHub repository accompanying this tutorial to public/images.

Create a temporary public URL using Ngrok

To test the app locally, first start the app by running the following command.

php artisan serve

Then, you need to use Ngrok. It assigns a local port a publicly accessible URL over the Internet. Run the command below, in a new terminal session, to expose the Laravel app running on port 8000 to the Internet:

ngrok http 8000

Test the App

Now, head over to the Twilio console, WhatsApp Sandbox settings, and set "WHEN A MESSAGE COMES IN" to be the ngrok Forwarding URL. and click Save.

The ngrok Forwarding URL is: <<NGROK URL>>/api/reply

Configure the Twilio Sandbox for WhatsApp

Then, text the word "Learn" to the Twilio WhatsApp Number. You should get a WhatsApp picture message with a question:

Step one of the application demoed in WhatsApp

Pick A as the answer and see what happens.

An example of answering incorrectly in the app

Sadly, the correct answer is B. Now pick B and see what happens.

An example of being asked a question in the app

Nice! It worked.

That's how to learn the Maasai language the fun way

There you go. You've now built an app to learn the Maasai language. There are a few things you could do to make the app more robust, however:

  1. You have just touched on numbers. Cover more topics. You could add topics like greetings, months of the year, directions, etc.
  2. Add voice. Instead of images, you could use video. Help learners know exactly how to pronounce the Maasai words.
  3. Add tests. I'd recommend you look into Pest PHP to learn more about adding tests to a Laravel application.
  4. Add the competition element to the app. Family members could compete on who got the most questions right.

I would love to see what you build! The code is available on GitHub. Feel free to make PR requests, report issues and play with it.

Want to learn a new language? Then, what are you waiting for? There is never a perfect time to start. The time to act is now.

Michael Jaroya is the Co-founder of Lughayangu. He is an entrepreneur, a wannabee copywriter and a history enthusiast. You can find him on Twitter and GitHub.

"Maasai" (in the tutorial's main image) by Anita Ritenour is licensed under CC BY 2.0.