<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Luong Xuan Trung Dung</title>
    <description>The latest articles on DEV Community by Luong Xuan Trung Dung (@adrianluong).</description>
    <link>https://dev.to/adrianluong</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3438205%2F59b8688e-221d-4cdd-bc18-8b425d267e54.jpeg</url>
      <title>DEV Community: Luong Xuan Trung Dung</title>
      <link>https://dev.to/adrianluong</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adrianluong"/>
    <language>en</language>
    <item>
      <title>Setup Odoo 19 and Postgres 15 with Docker</title>
      <dc:creator>Luong Xuan Trung Dung</dc:creator>
      <pubDate>Thu, 30 Oct 2025 13:34:12 +0000</pubDate>
      <link>https://dev.to/adrianluong/setup-odoo-19-and-postgres-15-with-docker-45lg</link>
      <guid>https://dev.to/adrianluong/setup-odoo-19-and-postgres-15-with-docker-45lg</guid>
      <description>&lt;p&gt;I'm writing this from the satisfaction of having a local &lt;strong&gt;Odoo&lt;/strong&gt; instance with its isolated running environment in my Windows machine. Having looked up near and far, through searches, with multiple LLM models and 2 days of frustration, I finally did it.&lt;/p&gt;

&lt;p&gt;I could have just installed Odoo's official Windows installer but there is a question of management. Besides, the installed Odoo from this method is packaged with Postgres 12, which is relatively old in my opinion.&lt;/p&gt;

&lt;p&gt;I tried to set up Odoo from scratch by its Git repository since this is recommended to developers, but installing its required Python libraries always ended up in a variety of errors. In addition, my pre-installed Postgres 18 was conflicted with this Odoo due to CTYPE and COLLATION.&lt;/p&gt;

&lt;p&gt;Thus, after removing everything from the previous attempts, I went for Docker and here is what I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Docker Desktop version 4.18 or later.&lt;/li&gt;
&lt;li&gt;Docker CLI, which is packaged with Docker Desktop. Make sure to add it to your machine's PATH environment variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Docker 101
&lt;/h2&gt;

&lt;p&gt;It's useful to know some Docker terms before we continue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image&lt;/strong&gt;: a read-only template that contains the application code, runtime, libraries and any other dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container&lt;/strong&gt;: a lightweight, isolated, and executable package running instance of an &lt;strong&gt;Image&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume&lt;/strong&gt;: a persistent data stores for containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind mount&lt;/strong&gt;: a way to link a specific file or directory on the host machine's filesystem directly into a Docker container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compose file&lt;/strong&gt;: a simple YAML text file to manage the building of Docker &lt;strong&gt;Containers&lt;/strong&gt; stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of a docker-compose.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:5000"&lt;/span&gt;
    &lt;span class="na"&gt;develop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sync&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/code&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis:alpine"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Docker Compose YAML
&lt;/h2&gt;

&lt;p&gt;Like the example above, I declared the building of 2 &lt;strong&gt;containers&lt;/strong&gt;, one for &lt;strong&gt;Odoo&lt;/strong&gt; (version 19) and one for &lt;strong&gt;Postgres&lt;/strong&gt; (version 15). Afterward, the containers are meant to run in the default ports (8069 for Odoo and 5432 for Postgres).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odoo:19&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8069:8069"&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thing is when you delete a container, any data written to its writable layer is also deleted. Docker's &lt;strong&gt;volumes&lt;/strong&gt; are meant for this, for storing data outside of the container's file system, on the host machine as a backup.&lt;/p&gt;

&lt;p&gt;Now, with that in mind, I declared the volumes for the containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odoo:19&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8069:8069"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;odoo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/odoo&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;odoo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might notice, in order to use Odoo with Postgres, we also need to declare environment variables. These include a database, a Postgres superadmin's username and password for Odoo's content management.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;db&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=postgres&lt;/span&gt; &lt;span class="c1"&gt;# Example DB name, don't use in production&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=odoo&lt;/span&gt; &lt;span class="c1"&gt;# Example username, don't use in production&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=oodo&lt;/span&gt; &lt;span class="c1"&gt;# Example password, don't use in production&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This superadmin account and database is automatically created in the built Postgres container. For Odoo to actually use it, remember to confirm them once more in the browser.&lt;/p&gt;

&lt;p&gt;How it should look in your browser when the containers are running properly. Image from &lt;a href="https://www.cybrosys.com/blog/how-to-setup-odoo-17-development-environment-using-pycharm-in-ubuntu-20-04" rel="noopener noreferrer"&gt;Cybrosys&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmot220k5v4hgzrebko8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmot220k5v4hgzrebko8.png" alt="Final setup in browser, Odoo 17 in Ubuntu 20" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Odoo Development
&lt;/h2&gt;

&lt;p&gt;Odoo development involves creating custom modules. Thus, the YAML above needs to be updated with &lt;strong&gt;bind mounts&lt;/strong&gt; so that your modules can be copied (or mounted) into the containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;odoo:19&lt;/span&gt; &lt;span class="c1"&gt;# Choose your desired Odoo version&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8069:8069"&lt;/span&gt; &lt;span class="c1"&gt;# Maps host port 8069 to container port 8069&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;odoo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/odoo&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;./config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/odoo&lt;/span&gt; &lt;span class="c1"&gt;# For Odoo configuration file&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;./addons&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/mnt/extra-addons&lt;/span&gt; &lt;span class="c1"&gt;# For custom modules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means, in the directory of the Docker Compose YAML file, there should be 2 folders, &lt;em&gt;config&lt;/em&gt; (for custom Odoo config) and &lt;em&gt;addons&lt;/em&gt; (for your custom modules).&lt;/p&gt;

&lt;p&gt;Now, as you want to deploy your modules to the local Odoo application, the easiest method is to remove the Odoo container (just the container, not its volume data), then restart it to trigger the bind mount. This is irritating but I found no other way. &lt;/p&gt;

&lt;p&gt;Here are the Docker commands that I used, which &lt;em&gt;web&lt;/em&gt; is the declared Odoo container in the YAML file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker compose down web&lt;/code&gt;: remove the container&lt;br&gt;
&lt;code&gt;docker compose up -d --no-deps web&lt;/code&gt;: restart the container&lt;/p&gt;

&lt;p&gt;or this combine command: &lt;code&gt;docker compose up -d --no-deps --force-recreate web&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;After the setup was done, I happily went on through the Odoo tutorial to learn about its &lt;a href="https://www.odoo.com/documentation/19.0/developer/tutorials/server_framework_101.html" rel="noopener noreferrer"&gt;server framework&lt;/a&gt; just to face another source of frustration since I could not set up the container's Python environment in my code editor and I could not quite understand that document. Anyways, maybe at some point, I'll be writing about this.&lt;/p&gt;

&lt;p&gt;Nonetheless, I managed to finish that as well with the result is a functioning custom module that also serves as a &lt;a href="https://github.com/adrian-luong/odoo-real-estate-module" rel="noopener noreferrer"&gt;demo&lt;/a&gt; for this article.&lt;/p&gt;

&lt;p&gt;I hope you enjoy this tutorial. Let me know if you have any questions by: &lt;a href="https://github.com/adrian-luong" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/adrian-luong-3a3a99202/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://mailto:luongxuantrungdung211@gmail.com/" rel="noopener noreferrer"&gt;Email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for your time.&lt;/p&gt;

</description>
      <category>odoo</category>
      <category>docker</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Simple internationalization using React Context</title>
      <dc:creator>Luong Xuan Trung Dung</dc:creator>
      <pubDate>Sun, 24 Aug 2025 04:01:35 +0000</pubDate>
      <link>https://dev.to/adrianluong/simple-internationalization-using-react-context-1ioc</link>
      <guid>https://dev.to/adrianluong/simple-internationalization-using-react-context-1ioc</guid>
      <description>&lt;p&gt;The age of globalization has opened the door for a diverse range of internet content, especially for those that is not English. This creates a trend where websites become adaptible to multiple languages, serving appropriate contents according to user's preferences. These websites are known to have been &lt;strong&gt;internationalized&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In my research of making such sites in &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;/a&gt;, a popular framework for web development, I have followed many tutorials of many existing libraries to implement the features, yet failed to make the site serving the correct content. This frustrated me, since I think internationalization should accomplish something as simple as a change of text. Thus, I have to change my perspective, by looking at the foundation of those libraries, which is React's Context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Below are what I think you should have known before continuing with this document:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of React, more specifically: &lt;a href="https://react.dev/learn/your-first-component" rel="noopener noreferrer"&gt;Function component&lt;/a&gt;, &lt;a href="https://react.dev/learn/passing-props-to-a-component" rel="noopener noreferrer"&gt;component's props&lt;/a&gt;, &lt;a href="https://react.dev/learn/state-a-components-memory" rel="noopener noreferrer"&gt;state&lt;/a&gt; and &lt;a href="https://react.dev/learn/synchronizing-with-effects" rel="noopener noreferrer"&gt;effect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Javascript's ES6 syntax and JSON file format.&lt;/li&gt;
&lt;li&gt;An IDE, a software that supports your coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are confident with the knowledge above, feel free to take a look at this &lt;a href="https://github.com/adrian-luong/react-context-internationalization" rel="noopener noreferrer"&gt;demo&lt;/a&gt; for reference. Or, you can create a sample React app repository to follow along, using these commands in a folder opened in your chosen IDE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For NPM users: &lt;code&gt;npx create-react-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For Yarn users: &lt;code&gt;yarn create react-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The content JSON files
&lt;/h2&gt;

&lt;p&gt;These files are what I used to store translatable texts, any piece of content that can be switched between languages. Here is the English content of the demo, which I named &lt;strong&gt;en.json&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"English"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vietnamese"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"edit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"saveToReload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"and save to reload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"learnReact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Learn React"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed, the texts above come from the generated React repository. I simply translate those to my native language Vietnamese and create a new file, named &lt;strong&gt;vi.json&lt;/strong&gt;. Remember, it's important to standardize these content files. They should always have the same keys but different values.&lt;/p&gt;

&lt;p&gt;Using those content files in a context is a bit tricky, since you would need to import the files asynchronously, read and return them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dictionaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../contents/en.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../contents/vi.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getDictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dictionaries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;]?.()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;dictionaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;en&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The constant &lt;strong&gt;dictionaries&lt;/strong&gt; stands for the collection of read JSON contents.&lt;br&gt;
The function &lt;strong&gt;getDictionary&lt;/strong&gt; is how we would get the correct content for usage in the context.&lt;/p&gt;
&lt;h2&gt;
  
  
  The context and its provider
&lt;/h2&gt;

&lt;p&gt;React's &lt;a href="https://react.dev/learn/passing-data-deeply-with-context" rel="noopener noreferrer"&gt;context&lt;/a&gt; is a convinient method to manage states that are meant to be used in multiple components. In the demo, these values  would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;locale&lt;/strong&gt;: the current language of the website, should be either &lt;strong&gt;'en'&lt;/strong&gt; or &lt;strong&gt;'vi'&lt;/strong&gt; for the sake of simplicity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;setLocale&lt;/strong&gt;: the method to set the website to another language, its only parameter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dictionary&lt;/strong&gt;: the content corresponding to the current language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how the context is defined with those values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LocaleContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to used that context in any component, a provider must be defined. It's a wrapper component that takes the &lt;a href="https://react.dev/learn/passing-props-to-a-component#passing-jsx-as-children" rel="noopener noreferrer"&gt;&lt;strong&gt;children&lt;/strong&gt;&lt;/a&gt; props, which means it would be the parent of other components within the same tree. Because of this, we turn those values into states that can affect the &lt;strong&gt;children&lt;/strong&gt; or wrapped components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LocaleProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDictionary&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LocaleContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dictionary&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LocaleContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the hook &lt;strong&gt;useEffect&lt;/strong&gt; is for listening to the changes of the state that I named &lt;strong&gt;lang&lt;/strong&gt;. So, whenever &lt;strong&gt;lang&lt;/strong&gt; is set to a different value, a nameless async function would be called to get the content corresponding to that &lt;strong&gt;lang&lt;/strong&gt;. This is how the website's content is served.&lt;/p&gt;

&lt;p&gt;After all definitions are done, the context can now be used in React's main component by wrapping other components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LocaleProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppContent&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LocaleProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to use context and change its states
&lt;/h2&gt;

&lt;p&gt;The common usage of the mentioned context is displaying content. Here is how it's done in the component named &lt;strong&gt;AppContent&lt;/strong&gt;, which is wrapped by the context provider. In a sense, it would be similar to destructuring an object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AppContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dictionary&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LocaleContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/code&amp;gt; {dictionary.saveToReload}.&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
                    &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://reactjs.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noopener noreferrer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                    &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;marginRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;learnReact&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LocaleSwitcher&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have noticed another component that is named &lt;strong&gt;LocaleSwitcher&lt;/strong&gt;. This one is a simple switch to control the current language of the website, using the defined state setter named &lt;strong&gt;setLocale&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LocaleSwitcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LocaleContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;switchLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;defaultLocale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;switchLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/option&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/option&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/select&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, it's a &lt;strong&gt;select&lt;/strong&gt; tag with a handle for when its &lt;strong&gt;option&lt;/strong&gt; is selected. The option's values should match the acceptable values of the &lt;strong&gt;locale&lt;/strong&gt; state, as the handler function &lt;strong&gt;switchLanguage&lt;/strong&gt; would set that state to any of those. So by selecting any of those options, you should be able to change the content in the demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything in Typescript
&lt;/h2&gt;

&lt;p&gt;Personally, I prefer Typescript to Javascript because of type safety. A mishandling value would result in the application ended up in layers of exception. Besides, who doesn't want to have a clear vision on the inputs and outputs of their system? Well, that is if you can handle the nuisance of having to define types in almost every piece of code.&lt;/p&gt;

&lt;p&gt;Let's begin with the types (and interfaces, which are object types) used with the context states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Dictionary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Awaited&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;getDictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ILanguageContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;strong&gt;Locale&lt;/strong&gt; type represents all supporting languages in the demo. The &lt;strong&gt;Dictionary&lt;/strong&gt; one stands for the type of the read JSON content files, which is an object type with clear defined keys. Finally, the context's interface to combine those 2 types. Here is how the types are used in context, under the Typescript syntax that is called &lt;a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer"&gt;generic&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LanguageContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ILanguageContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LanguageProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dictionary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDictionary&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dictionary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setDictionary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LanguageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;setLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dictionary&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LanguageContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other than above changes, nothing else really need to change in the demo's main code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;I admit that internationalization is more than just changing texts. There are things like time zone(s), currency, number format that need to be changed, as well, and they are based on location, not selected language. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/adrian-luong/react-context-internationalization" rel="noopener noreferrer"&gt;demo&lt;/a&gt;, at the moment, fails to detect that as I hard-coded the default language to be English ('en') without taking into account of the user's location.&lt;/p&gt;

&lt;p&gt;Another disadvantage of this text-change context is its dependence to static files, the JSON ones. Thus, a roburst website with dynamic content would require a CMS (content management system) to handle its content.&lt;/p&gt;

&lt;p&gt;The context in the demo should be usable in the React ecology, meaning all React-based frameworks. I used an instance of it in my &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt; project since all I need is a simple text change.&lt;/p&gt;

&lt;p&gt;I hope you enjoy this tutorial. Let me know if you have any questions by: &lt;a href="https://github.com/adrian-luong" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/adrian-luong-3a3a99202/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://mailto:luongxuantrungdung211@gmail.com/" rel="noopener noreferrer"&gt;Email&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for your time.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Trackable Flask API using EventSource</title>
      <dc:creator>Luong Xuan Trung Dung</dc:creator>
      <pubDate>Sat, 16 Aug 2025 10:45:20 +0000</pubDate>
      <link>https://dev.to/adrianluong/trackable-flask-api-using-eventsource-365f</link>
      <guid>https://dev.to/adrianluong/trackable-flask-api-using-eventsource-365f</guid>
      <description>&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;APIs are connections between computers to exchange information or data. For a website, we expect them to return a visual change, something as simple as a now different text or a floating message in the corner of the screen.&lt;/p&gt;

&lt;p&gt;However, what if the API we are waiting for is taking too long to respond to us? Are we supposed to stare at the screen now? Is there anything else we can do on the website at the moment? Such frustration and panic would ruin the experience of any patient user. And to us developers, the general solution is an API that would return its progress periodically.&lt;/p&gt;

&lt;p&gt;In this tutorial, I’m going to introduce an answer to the above issue, by using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource" rel="noopener noreferrer"&gt;EventSource&lt;/a&gt; class along with a &lt;a href="https://flask.palletsprojects.com/en/stable/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; API server. The complete demo is available &lt;a href="https://github.com/adrian-luong/trackable-flask-api" rel="noopener noreferrer"&gt;here&lt;/a&gt; for reference&lt;/p&gt;

&lt;p&gt;Below are the prerequisites for the demo as mentioned in its README, please make sure they are installed properly before you continue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An IDE for a comfortable coding experience, either &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; or &lt;a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer"&gt;PyCharm&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pyenv/pyenv" rel="noopener noreferrer"&gt;Pyenv&lt;/a&gt;, a simple Python version manager. For Windows users, you should use the recommended &lt;a href="https://github.com/pyenv-win/pyenv-win" rel="noopener noreferrer"&gt;pyenv-win&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;, the programming language used in Flask. This tutorial is meant for version 3 or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Flask server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Virtual environment
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://flask.palletsprojects.com/en/stable/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; is a lightweight Python web application framework that can be used to easily and quickly set up an API server. To set it up, we would need to make a Python virtual environment. This is crucial to prevent potential conflicts with other Python projects in your machine.&lt;/p&gt;

&lt;p&gt;If you have properly installed &lt;a href="https://github.com/pyenv/pyenv" rel="noopener noreferrer"&gt;Pyenv&lt;/a&gt;, you should be able to run the following command in a desired location:&lt;/p&gt;

&lt;p&gt;In Mac OS and Linux: &lt;code&gt;&amp;lt;pyenv&amp;gt; /versions/3.11.11/bin/python -m venv .venv&lt;/code&gt;&lt;br&gt;
In Windows: &lt;code&gt;&amp;lt;pyenv&amp;gt; /pyenv-win/versions/3.11.11/bin/python -m venv .venv&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Whereas &lt;em&gt;&amp;lt;pyenv&amp;gt;&lt;/em&gt; stands for where you have installed the tool in your machine.&lt;/p&gt;

&lt;p&gt;Once the command is done, there should be a hidden folder &lt;em&gt;.venv&lt;/em&gt; in your chosen location. Any &lt;em&gt;Python 3&lt;/em&gt; version should be fine for this tutorial, so you can replace &lt;em&gt;3.11.11&lt;/em&gt; in the above command with any desirable version.&lt;/p&gt;

&lt;p&gt;The next step is to activate the installed environment, by using this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Mac OS and Linux: &lt;code&gt;source &amp;lt;location&amp;gt;/.venv/bin/activate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In Windows: &lt;code&gt;source &amp;lt;location&amp;gt;/.venv/Scripts/activate&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whereas &lt;em&gt;&amp;lt;location&amp;gt;&lt;/em&gt; is where you install the environment. The successful activated environment would be indicated by the prefix &lt;em&gt;(.venv)&lt;/em&gt; in your IDE's terminal.&lt;/p&gt;

&lt;p&gt;In case you activate the wrong environment, you can leave it by a simpler command: &lt;code&gt;deactivate&lt;/code&gt;. Otherwise, you should continue with installing Flask: &lt;code&gt;pip install flask&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The session APIs
&lt;/h3&gt;

&lt;p&gt;We only need 3 APIs for the demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GET &lt;strong&gt;/generate&lt;/strong&gt;: generate strings from 2 arguments: amount and length.  It is meant to be trackable.&lt;/li&gt;
&lt;li&gt;POST &lt;strong&gt;/session&lt;/strong&gt;: set up session values.&lt;/li&gt;
&lt;li&gt;GET &lt;strong&gt;/&lt;/strong&gt;: Render a simple web page from the templates folder, as well as setting default session values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the API to return the index page and getting sessions:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf3up75w6qn9kzjiw88q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf3up75w6qn9kzjiw88q.png" alt="The API to return the index page and getting sessions" width="453" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The index page from the server is rendered in your browser similar to any HTML file, except that it has been put through &lt;a href="https://flask.palletsprojects.com/en/stable/templating/" rel="noopener noreferrer"&gt;Jinja2&lt;/a&gt;, Flask’s default template engine, to assemble it with session values before returning to the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7j5pd5v7chg5x7yq5px.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7j5pd5v7chg5x7yq5px.png" alt="How the session values are assembled to the index page by Jinja2" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flask.palletsprojects.com/en/stable/api/#flask.session" rel="noopener noreferrer"&gt;Sessions&lt;/a&gt; are values that persist in system memory as long as the server can run, which can be used between APIs. In this tutorial, they determine which set of characters should be used in the string generation API. &lt;/p&gt;

&lt;p&gt;Here is the API to set session values, received from the client and return an HTTP response:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75epa5ya0mi5ie4lc4vn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F75epa5ya0mi5ie4lc4vn.png" alt="The API to set session values, received from the client and return an HTTP response" width="573" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The values are chosen to be numerical for better communication between the client side (Javascript) and the server side (Python), since numbers are the same between 2 languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Javascript/JQuery client
&lt;/h2&gt;

&lt;p&gt;The rendered index page consists of 3 noticeable parts: the checkboxes that determine session values, the form to generate strings and the box for displaying the generated result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjx145s1j8qdh6ailxnk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjx145s1j8qdh6ailxnk3.png" alt="Checkboxes for session values" width="191" height="109"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbicrv0jebuiwwh3rt0zb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbicrv0jebuiwwh3rt0zb.png" alt="Their JQuery listener" width="389" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The checkboxes above are used to set and unset Flask server’s session values. They are controlled by the JQuery’s &lt;a href="https://www.w3schools.com/jsref/event_onchange.asp" rel="noopener noreferrer"&gt;onChange&lt;/a&gt; event listener, which would use &lt;a href="https://api.jquery.com/jQuery.ajax/" rel="noopener noreferrer"&gt;AJAX&lt;/a&gt; to call API:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jl4gxgh4w3kl6h7gyp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jl4gxgh4w3kl6h7gyp0.png" alt="The function to call the API to send new values for the sessions. Returns a toast message" width="533" height="421"&gt;&lt;/a&gt;&lt;br&gt;
(The function to call the API to send new values for the sessions. Returns a toast message)&lt;/p&gt;

&lt;h3&gt;
  
  
  The form and the progress bar
&lt;/h3&gt;

&lt;p&gt;In addition to session values, the API to generate strings would require 2 arguments: the amount of string to generate and the string length. The values for them come from 2 number inputs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatfk7yyt8glcve3sdb98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatfk7yyt8glcve3sdb98.png" alt="The form when submitting would call the API to generate strings with updates on progress" width="800" height="65"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon submitting the form, strings would be generated and the progress bar in the result box would be updated while the API is running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3twv92k498quhbhslon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3twv92k498quhbhslon.png" alt="The progress and result of the running string generation API" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The trackable API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Server-side
&lt;/h3&gt;

&lt;p&gt;The trackable string generation API would emit a data message after every running instance. These messages are used for handling on the client side by updating the progress bar and display generated string. Here is an example of said message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg6k0hcxo207elqddq6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg6k0hcxo207elqddq6u.png" alt="What the API returns or yields per every run" width="367" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that message, &lt;em&gt;generated_string&lt;/em&gt; is the generated by looping through a set of characters (from the provided session values) and randomly picking one by one until it has the desirable length.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2gpr8bgpay88zgdkx65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2gpr8bgpay88zgdkx65.png" alt="(How the string is generated. The sets of characters are Python’s built-in values" width="524" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the loop is finished, the API should also emit a custom event for the browser to display a toast message.&lt;/p&gt;

&lt;p&gt;In order to use session values, the &lt;em&gt;for&lt;/em&gt; loop must become written as a function, then wrapped in a &lt;a href="https://flask.palletsprojects.com/en/stable/api/#flask.stream_with_context" rel="noopener noreferrer"&gt;&lt;strong&gt;stream_with_context&lt;/strong&gt;&lt;/a&gt;. Finally the API should return an HTTP response with a special mimetype &lt;strong&gt;text/event-stream&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-side
&lt;/h3&gt;

&lt;p&gt;As mentioned, submitting the form would call the trackable API as an instance of the &lt;strong&gt;EventSource&lt;/strong&gt; class. This kind of trackable API, however, is limited to the GET method and cannot accept any payload body, which is why URL arguments have to be used, like in this image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp9v6p4z58d3cbxm38b3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp9v6p4z58d3cbxm38b3.png" alt="(How the trackable API should be called, as an instance of the EventSource class" width="569" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every yield from the API corresponds to a JSON-like data being sent to the browser and can be extracted for updating the progress bar and result box:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j77fhm2l5jmltuu2im7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j77fhm2l5jmltuu2im7.png" alt="Handle for every completed process, which also updates the length of the progress bar" width="563" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the API is done, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource" rel="noopener noreferrer"&gt;EventSource&lt;/a&gt; instance should be closed with a toast message, whether successful or not:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qdui6daxdpzcm459cmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qdui6daxdpzcm459cmf.png" alt="Handle for when the API is done" width="644" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Future plan
&lt;/h2&gt;

&lt;p&gt;There are other ways to develop a trackable API, such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket" rel="noopener noreferrer"&gt;WebSocket&lt;/a&gt;. This method is more superior due to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowing other HTTP methods, aside from GET.&lt;/li&gt;
&lt;li&gt;Allowing sending custom payload bodies.&lt;/li&gt;
&lt;li&gt;Allowing intercommunication between client and server. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perhaps, that can be a tutorial for another day.&lt;/p&gt;

&lt;p&gt;I hope you enjoy this tutorial. Let me know if you have any questions by: &lt;a href="https://github.com/adrian-luong" rel="noopener noreferrer"&gt;Github&lt;/a&gt; or &lt;a href="//mailto:luongxuantrungdung211@gmail.com"&gt;Email&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thank you for your time. &lt;/p&gt;

</description>
      <category>jquery</category>
      <category>flask</category>
      <category>javascript</category>
      <category>python</category>
    </item>
  </channel>
</rss>
