<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[There is No Spoon]]></title><description><![CDATA[Freelance Fullstack Engineer. I take small to medium projects and complete them solo. Speciality: React, React Native, NodeJS, GraphQL, IaC (Terraform), Databas]]></description><link>https://code.naishe.in</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 11:34:46 GMT</lastBuildDate><atom:link href="https://code.naishe.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Better Search Engine with Skill Issue]]></title><description><![CDATA[In the last few days, a couple of things got clear. One, the coding as we know it is gone forever. The hype around AI coding agents, and many evolving toolings like MCP, skills or context engineering,]]></description><link>https://code.naishe.in/better-search-engine-with-skill-issue</link><guid isPermaLink="true">https://code.naishe.in/better-search-engine-with-skill-issue</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[coding]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Sat, 04 Apr 2026 07:38:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61f0c53687fd2728e7cd48bf/70cff95a-c278-4d70-a101-07e8e233883a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the last few days, a couple of things got clear. One, the coding as we know it is gone forever. The hype around AI coding agents, and many evolving toolings like MCP, skills or context engineering, idea honing etc are changing how we write code -- or rather, how we direct AI to write code. Two, as of now, AI has skill gap. It's an amazing search engine that looks deep into your codebase, its own knowledge base, and also searches the web and filters out the exact or near exact info for you, and plugs the desired changes in your codebase at the right places. <em>Almost always</em>. But when it fails to do that, and you don't have the right skill to pull AI out of the local minima, you're in the soup.</p>
<h1>Day 6: Compose won't compose</h1>
<p>Writing an Android app in Kotlin: while learning and playing around with AI was fun so far, LLM was stuck at trying to implement the <code>FlowRow</code> feature of the <code>Compose</code> library. I had no idea how Gradle dependencies are managed; and Android has two of them with a lot of magic lines that weave things. When the AI agents failed, they looked into logs, tried to fix, failed again, and I was exhausted watching this little Tom and Jerry show.</p>
<p>It was giving confident feedback:</p>
<blockquote>
<p>Looks like the experimental feature is not supported by animated view, let’s get rid of animated view. Now it compiles and the box will now expand properly.</p>
</blockquote>
<p>The box didn’t expand. It went on and on and on and on. Sigh!</p>
<h2>Skill Issue</h2>
<p>When AI works, it works; but when it fails, it fails staggeringly. It shows a clear skill issue. And, if the operator, in this case me, has the skill issue as well… the problem becomes real. Now, you need to look in the thousands of lines of code that LLM has vomited which was working a minute ago. Now you need to look into the possible damages it has done when it went berserk trying to fix the code by changing unrelated code. While I am hopeful that these agents will continue to be smarter, right now you need to know two things:</p>
<ol>
<li><p><strong>Keep yourself acquainted with the code that it writes.</strong> And, it's an extreme mental load. Unlike human coders, the amount of code AI spits in a day is massive. You need to give yourself a fair amount of time and familiarize with what it’s doing. It will pay off when your coding companion goes postal.</p>
</li>
<li><p><strong>You can’t “vibe” code and be insincere.</strong> You need to be skilled to be able to roll your sleeve and get some elbow grease on. I have been suggested to use multiple agents, which I haven’t tried. It may solve the issue, I don’t know. It feels very helpless asking the AIs, watching them fail, and then asking another and praying. But it maybe how future of coding look like.</p>
</li>
</ol>
<h3>The NeoVim Config Drama</h3>
<p>While talking about skill issue, I remembered another incident from a few days ago when I decided to switch to NeoVim from VS Code. VS Code was giving up on my moribund computer, so NeoVim made sense.</p>
<p>I am super comfortable with Vim editor, so getting initial setup done wasn't big deal. It's only when I decide to use LazyVim and plugins when I thought let's have AI assist me.</p>
<p>Bad idea. Because it confidentially lies. Just as I was setting up the config and plugins to upgrade the barebones NeoVim, a couple of plugins won’t work as expected. I see the error messages every time I launch NeoVim. The LLMs kept telling me about various fixes: more and more complicated but completely wrong solutions and eventually (after a good 30 minutes of head banging) it said this:</p>
<blockquote>
<p>If it still fails, honestly—just use the simpler config without Treesitter for now. Treesitter is nice to have, but not essential. You can code perfectly fine without it. Telescope is more important for your workflow anyway.</p>
</blockquote>
<p>While it’s not incorrect; but there exists a better solution than giving up. Looking into docs and forums, I found out a version mismatch between two plugins was causing this. So, here's what happened.</p>
<ol>
<li><p>LLM, as a copy-and-paste tech, pasted codes from someone’s repo who used explicit version numbers. It didn’t do it for all the plugins. Some were <code>@latest</code> and others were on some specific versions.</p>
</li>
<li><p>When fixed the versioning, the LLM didn't fix configs accordingly until I looked up the documentation, and told LLM what to do.</p>
</li>
</ol>
<p><em>Here is the thing, and it's a repeat theme</em>: if you have skill issue, AI will take you for a ride. As of early 2026, you can reach 90% success without ever looking at the code and just prompting the GPTs; and for a vast majority of businesses that's good enough.</p>
<h1>Day 6: Custom Cloud, Deployment and Automation</h1>
<p>A client once asked me to migrate his services from expensive AWS to the cheapest cloud service provider. Back then, I was sworn by the big three because of reliability, documentation, and the plethora of available tooling. With my newly found superpower with LLM, this time, I called him up and signed a two day contract; that much confidence. (Of course, I knew the worst case I’d be writing a couple of Dockerfiles, shell scripts, and cron jobs)</p>
<p>Since, I had written the whole code base of the internal web application, I knew every detail: node version, Vite build script, file permissions, and post deployment test scripts; so I wrote a meticulous 2000 words long prompt with exactly what it should do. It whirred and buzzed for a solid 10 minutes and spat two docker-compose files (two hosts), .env files, modified 4 Dockerfiles, added health checks to the backend, and as specified added Traefik for routing and Let’s Encrypt SSL. Perfect. Copied the Docker-compose files to prod VMs. Two minutes later. All services were up on the new domain.</p>
<p>The rest of the afternoon was spent reading the generated code; found that the code generated is using the old versions of some dependencies, docker-compose was emitting half a dozen warnings about deprecation. Fixed them. Migrated the data over. Created and secured the VM images. Happily called the client to say that work is done and I still need 2 days of payment.</p>
<h2>Cheap cloud, no documentation, no support: LLM Shines</h2>
<p>CloudPe is probably the cheapest cloud service provider that I could find. It provides you VM, Volume, Network, and Object Store; but that’s it. Barely, any documentation, and support people are clueless, screaming on social media tagging them returns no response. I didn’t know I would suffer this because they had a documentation selection on their website which, on close inspection, looks like someone did a poor job at it.</p>
<p>The client asked me to shutdown the servers at 11 PM and launch it again in the morning at 7 AM. Spoilt by big clouds, I thought I’d just import their SDK in a Python script and write a cron job to shelf and unshelf the VMs. I'd trigger this script from one of the client’s office machines. It wasn’t a critical requirement, so if it didn’t work on some days, it would be acceptable.</p>
<p>I was surprised that the API documentation was thin and had no SDK. A glimpse of hope came when I saw OpenStack somewhere in their sparse documents and started “hacking”. lol. Auth won’t work, tried a couple of iterations and then gave the LLM one task:</p>
<blockquote>
<p>The VM provider is very likely using OpenStack. I don’t know which version. I want to manage the VM using OpenStack Python SDK, but it’s failing when I tried authenticating using: username and password and username and secret key. You have access to the shell command to run the Python script, edit it, and keep iterating till the problem is solved.</p>
</blockquote>
<p>A few seconds of cling-clangs and bangs later, it came with a functioning YAML format that does authentication. If it wasn't the AI agent, I would have given up. This task was not in the scope. But hey, now the client saves 8 hours of billing, thanks to AI.</p>
<h1>Reality Check</h1>
<p>At this point, it's repetitive to say that web development has fundamentally changed. I am confident this will sharply cut the number of developers needed to do a job. However, skill is still somewhat important, occasionally when AI is stuck solving something the way it's intended and needed a nudge in the right direction.</p>
<hr />
<blockquote>
<p>The cover image is generated by ChatGPT 5.2 using prompt: "Generate an image of a dejected tin man with rest of the friends of Wizard of Oz. The friends are in the background, and distracted, and on the soil land lies a broken laptop. The environment is farmland, and these folks are on a thin dirt road, the sky had clouds. Make this image like line art in the children books from the '50s. No coloring, and it should look like drawn on a distressed paper that has yellowed due to aging."</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[I Know Kung Fu]]></title><description><![CDATA[Projects from the scratch using modern and common stack is easy thing to do. It was always easy, even before the AI assisted coding was mainstream, writing a webapp, deploying it to any cloud, making ]]></description><link>https://code.naishe.in/i-know-kung-fu</link><guid isPermaLink="true">https://code.naishe.in/i-know-kung-fu</guid><category><![CDATA[AI]]></category><category><![CDATA[coding]]></category><category><![CDATA[refactoring]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Mon, 16 Mar 2026 13:16:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61f0c53687fd2728e7cd48bf/a15616c7-2988-4b6f-b867-8e387b360197.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Projects from the scratch using modern and common stack is easy thing to do. It was always easy, even before the AI assisted coding was mainstream, writing a webapp, deploying it to any cloud, making it scale, collecting the stats, alerts, making payment gateways work was so mind numbing easy that I could do it half asleep. It's the amount of typing that bothered me, which AI seems to have solved. Next, I decided to up the ante for AI. Let's see if it can actually do a messy cleanup job; and then whether I can use it to learn something new quickly. I had mixed results.</p>
<h1>Day 4: Reanimating a 2016 WebApp in 2026</h1>
<p>Back in 2016, MERN stack was very different from today. React wasn’t yet function oriented, no hooks, Redux was marred with complicated “stitching” to get it working, and Babel was a complicated mess. Node wasn’t nice as well. Typescript wasn’t fully baked and EcmaScript didn’t have a lot of syntax niceties, promises were still requiring Bluebird (or Q). MongoDB ORMs, like Mongoose, weren't supporting (schema) types properly either. But the was a fully functional solid functioning software, serving a decade in production.</p>
<p>The software is still running in production thanks to containerization to provide infrastructure for 2016 stack. The client who I did project for, approached me multiple times in the last couple of years to add new features, and I said no; dreading entangling my foot in a mess that I no longer feel familiar or comfortable working with.</p>
<p>What was nagging me so far in my experiments with AI assisted coding, was the fact that all these projects I did so for were, what commonly known as, green field projects. No real figuring out, no existing coding pattern to learn, no deprecated library to replace <em>without</em> rewriting the entire codebase. I had almost entirely forgotten how things were done a decade ago. And, if I had to try, I would rather request a rewrite of the whole codebase. Well, this time, I was over enthusiastic about AI, and unsurprisingly it did poorly.</p>
<h2>Attempt 1: Robot, modernize this code!</h2>
<p>I woke up from my daydream of AI freeing us developers from the nagging nitty-gritty of coding and focusing on architecture when I tried this. I allowed the LLM to read and understand the code and then provided the latest stack, asked it to update the codebase to use React 19, keep Booostrap CSS (yeah!) styles untouched, update Redux to its modern form, bake Typescript in backend and front-end code. The prompt was more fine-grained, more explicit, I provided guardrails, and tests that it must pass. And click “Go!”.</p>
<p>It whirred and grunted, and whistled, and stumbled, and recovered, and it went on like this a good 21 minutes. Exhausted more than 90% of daily token quota and presented me with a non building, non functional hot garbage. In desperate attempts to fix it, I finished all the daily tokens.</p>
<p>"Well, maybe this brand’s LLM isn’t so great at learning, editing, and fixing the code.", I thought. Subsequently, I bought two more LLM subscriptions -- top three LLMs for coding all resulting in the similar fate, a non-functioning Frankenstein’s monster. Time to call it a day.</p>
<h2>Attempt 2: Rewrite the app learning from the codebase</h2>
<p>The next day, I was still reluctant to put in more effort in refactoring, so I asked the LLMs to learn the code and create a replica app (in UI and functionality) using the modern stack. Before lunch, I had a web app that was nothing like the original. It did have similar texts and some (MongoDB) collections were similar but LLM got the app completely wrong.</p>
<h2>Attempt 3: Guide LLM like you’re buddy coding with a junior developer</h2>
<p>I realized the LLMs were not there, yet. They were very close, and very soon they are likely to be able to do the messy job. So, if I wanted to refactor this, I would have to do a little more legwork than I expected.</p>
<p>Since I developed the whole system, I knew if I had to refactor this code, where would I start, what and which files to touch first and what changes need to be done in what order. I didn’t want to switch to another state management library because this was a proof of concept whether LLMs can help in real life messy scenarios like this. So, I started giving AI piecemeal tasks in the order I would do it.</p>
<p>I started with updating dependencies, Node versions, removing old Babel config, and adding prettier and modern tooling. I realized back in 2016, I didn’t care to segregate data logic from UI logic. I carefully went file by file refactoring the whole UI. It was broken all the time; and there were more than desirable times when I had to fix the code by hand because LLM wouldn’t just do the job. Once I was done, the UI was working and the back-end was still old.</p>
<p>It was getting evening, and I was frustrated. A quick look at the back-end code, I realized I could keep the existing code as-is and update the <a href="http://node.js">Node.js</a> library. So, instead of changing every function, I decided to use a completely different ORM and added new code in Typescript and kept the old code.</p>
<p>Bottom line: it was frustrating, but AI was a massive help. I could finish the job in a day that would have taken me something like 30 days. If it was manual effort by me, I would rather have it rewritten faster.</p>
<h1>Day 5: Deep Diving Android Development</h1>
<p>The last time I touched Android development was way back in 2014. It was frustrating. The IDE was clunky, the emulator was slow, the horrible XML layout sent chills to my spine, and I didn't even dive deep, I was just building an application that makes API calls and renders screens. Since then, React Native has been my favorite way to develop applications for mobile.</p>
<p>For the last few years, I had an annoying problem with my Android 12 phone: I had to play music to change the audio destination from bluetooth to phone or to another bluetooth headset. All I wanted was to open the audio output selector. How hard would it be? “Pretty easy!” (All hard projects start like this.)</p>
<h2>Learning Kotlin</h2>
<p>I’ve used Kotlin for server apps, but I can’t describe myself as an expert Kotlin developer. I can read and write code, but I still roll my eyes on syntactic sugar the language adds. It’s nice. A million times better than Java.</p>
<p>Since I’ve never used all the bells and whistles that come with Kotlin, vibe coding with an assistance that explains what and why something is done, and gives me links for further reading, seemed like a good idea. I can comfortably say that the amount of learning I had in 8 hours that day, is more than what I could have learnt in a week.</p>
<h2>Early Failure</h2>
<p>As it turned out, there was no way to switch audio in Android 12 and below, it’s a platform level restriction. The only way is to play music, and have the user choose the audio device from the pull down menu of the card that shows media playing.</p>
<p>This is amazing, the amount of hoops I’ve jumped through to find a way to get it working for Android 12, and finally coming to this conclusion is mind boggling. In an unassisted, pre-AI, post Google world, these experiments and attempts would have easily eaten up a couple of days.</p>
<ol>
<li><p>AI initially pretended it's possible to programmatically select an audio device on Android 12 without having to have an audio streaming.</p>
</li>
<li><p>When that failed during the tests, and I proposed playing a silent audio if no audio was playing for a moment, just to enable the audio switcher to work. AI spewed out some non-working code.</p>
</li>
<li><p>After some Google searches, and reading old forums, I realized it can't force show the audio output device selector for Android 12. (As we will see, I later learnt that It's an restriction at the SDK level.) The AI agent concurred.</p>
</li>
<li><p>I was hard pressing against AI because I had an old phone with Android 12 and wanted it to work. So, I wanted it to look up the internet and give me when and what exactly changed that allowed this to work in Android 13. AI took its sweet time and gave me the exact location in the Android's source code where and when it was allowed for code to show the audio change menu, and it was not for Android 12, the codebase belonged to Android 13+.</p>
</li>
</ol>
<p>Although, I found out this is a fundamentally impossible to do it in Android 12, I’ve failed with a satisfying “why” answer in 3 hours. Figuring things out in almost unfamiliar coding / SDK territory could easily have taken up a day, at least, and likely, I wouldn't have had a good answer to defend. So, I had a bittersweet feeling at this moment.</p>
<h2>Getting a functioning app and knowing Android APIs</h2>
<p>It always bothered me that all my projects so far were web services: content delivery, feed management, or business application. It bothered me why people don’t use PWAs instead of building the apps. The app gives them no extra benefit except being able to download from the app / Play stores. (But, that's another topic for another day).</p>
<p>This time I wanted to play with Android's APIs, doing native things: things like writing an app that has a Quick Tile (the Android’s pull down menu where you have Wi-Fi tile, and Airport Mode tile etc), intercepting media playback requests, making Android API calls to show the switch audio device drawer.</p>
<p>With a newly found excitement of ability to do it while learning made me understand and build and app that has:</p>
<ol>
<li><p>A quick tile to change audio output device (for Android 13 and above)</p>
</li>
<li><p>A fancy UI written using Jetpack Compose, which feels oddly similar to React (or other modern web dev tooling)</p>
</li>
<li><p>Build a rule engine and an observer to prompt to switch to a preferred audio output device if the user set it so. (Of course, the ideal case would have been to automatically switching to the desired output device, but it seems like Android doesn't like third party apps doing that)</p>
</li>
</ol>
<p>I will update this post once the app is approved on Android 13. I am still testing it on a borrowed phone.</p>
<h1>The End of the Honeymoon Period</h1>
<p>What seemed like an unstoppable force screaming through social media, crushing software jobs by causing relentless firing, and the one which The Big Players claim it (the AI) to be our gods, it's not there yet.</p>
<p>Don't get me wrong, it's powerful: powerful than Google search, which was powerful than community forums, which was powerful than reading Complete References. It's just the jump in efficiency like it always has been, but this time the jump is really high.</p>
<p>Coming down from early exhilarating experience of AI crest into the trough of messy interactions with LLMs, the AI agents seem less god like and more like excellent pattern matchers. Sometimes it feels smart, and most of the times it does a good job even with poorly specified specs. It's an important tool in my developer's pocket: the most powerful one for sure!</p>
<blockquote>
<p>The cover image was generated by ChatGPT 5.2 with prompt "an image of a coder in wild west style with a smoking gun and a broken Thinkpad... the opponent has knives. Kinda like the coder brought a gun to sword fight."</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[10x to 100x Engineer]]></title><description><![CDATA[I have been using an AI assistant for a couple years now, but until Jan 2026, I was using the AI agents more like a better auto-complete and a faster web-search. It was great. In the mid Jan 2026, I w]]></description><link>https://code.naishe.in/10x-to-100x-engineer</link><guid isPermaLink="true">https://code.naishe.in/10x-to-100x-engineer</guid><category><![CDATA[AI]]></category><category><![CDATA[coding]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Sun, 15 Mar 2026 13:50:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61f0c53687fd2728e7cd48bf/dd4b8c82-3f42-4da9-a01d-03ebd0538916.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been using an AI assistant for a couple years now, but until Jan 2026, I was using the AI agents more like a better auto-complete and a faster web-search. It was great. In the mid Jan 2026, I wanted to build a simple pain tracking app that’s completely offline, no login required, ads free, simple, and shows trends for myself to track my condition so that I understand flare ups, time of day patterns of the pain, the good days-bad days, and how’s average pain varies over time in different body parts.</p>
<p>This is a fairly simple app. A week of coding to build a functioning app, nothing polished. 10 days to polish things up. 15 to build a release candidate. About a two week of work for me. Then I thought, well, let’s check how good AI coding agents have become. So, I swiped my card for a month-long subscription of an AI service and fired-up my IDE. What I am going to tell here is how things snowballed from casually building an app in a set-up (React, React Native, Typescript, Sqlite, and plotting libraries) for which I can churn code in my dream to diving deep into interacting with Android’s MediaRouter2 and looking into Android’s codebase to understand why I am targeting a certain minimum SDK version while progressively building deliverables at a mind boggling speed without much fear of not knowing what AI is doing.</p>
<p>Since it’s a long article, I am going to split it into multiple smaller posts.</p>
<h1>Day 1: Pain Recorder App</h1>
<p>Tuesday morning, sitting next to my mom in a hospital after her successful surgery of a life threatening condition, I started to feel my DISH (Diffuse Idiopathic Skeletal Hyperostosis) bothering me. As I popped Gabapentin, I realized I am delaying my visit to the doctor to figure out what’s going on. I always fumble when doctors ask what hurts, how much, when it hurts the most, and so on. It’s usually the recollection of the last couple of days; and if it happened to be good days, it goes like, “there wasn’t much pain in the last couple of days, but it was hurting before… I can’t clearly recall” I feel like doctors get insincere once they hear this. So, sitting there, I decided to write an app. Glued to an uncomfortable chair with my 9 year old MacBook Pro with its <a href="https://www.keyboardsettlement.com/">infamous broken keyboard</a> -- I wished I had a junior engineer to write the code while I oversee, because the stack (React Native) was so familiar to me, there wasn't a new learning with this project.</p>
<p>I decided to give an AI agent a chance. I prepared a very clear phase wise development plan, created a palette that’s colorblind friendly, wrote down unit tests that it must generate and pass, and got my Balsamiq mockups to provide me the UI sketches as PNGs. Initialized a new Expo project, and started feeding the AI. Lo and behold, 4 hours of careful instructions and validating every phase I had a production ready app. It blew my mind.</p>
<p>(I will paste the URL here once the Play Store approves my app, it seems you can’t publish a health related app as an individual.)</p>
<h1>Day 2: A website of my choice</h1>
<p>In the evening, I decided, I would like to publish it on the Play Store. Which demanded a location to read privacy policy and terms and conditions. I got <a href="http://workslocally.com">workslocally.com</a> registered and decided to use Github pages to publish a simple Markdown driven site. It looked ugly. Signed off for the day.</p>
<p>The newly found confidence in AI’s capability, told me to make a better website, still simple but not unoriginal. I have been wanting to use <a href="https://github.com/jekyll/jekyll">Jekyll</a> for quite some time now, and this is the time. Ruby on my geriatric computer full of Ruby tombstones wouldn’t work and my love for Ruby isn’t that great. I found <a href="https://www.11ty.dev">Eleventy</a> -- similar in concept and a quick 30 minutes of reading got me what it does and it’s pretty slick. Again, I can build a website in 4 hours with good CSS and mobile support, but why not make AI worth my dollars.</p>
<p>Within 30 minutes, I had a functioning framework for a website with placeholder text, and got my Github actions written to generate the page and deploy to Github pages every time a commit is made in the main branch.</p>
<p>I filled up the content, merged into main and you can now see <a href="https://workslocally.com">https://workslocally.com</a></p>
<h1>Day 2 and 3: An Invoice Reader Image Processing</h1>
<p>I’ve been bugged by an auditor to automate the invoice reading that his team does. His team takes hundreds to thousands of invoice pictures, and PDFs of printed, or handwritten invoices and extracts various pieces of information from it to create a unified excel sheet. I, while impressed by the capabilities of modern LLMs, it was the fact that it's indeterministic, made me uneasy. Reading invoices that were handwritten, possibly in Hindi made me even less confident. But, I was on a rampage. Bring it on.</p>
<p>I decided to use FastAPI, Gemini, and React+Vite. FastAPI and Vite weren’t really the things I used in professional development. A few toy projects here and there. I drew a back of a napkin UI diagram, got it approved by the client; and instead of providing exact mockups to the coding agent, I gave it vague ideas and let it do design for me. AI spat the code, and I (git) committed when a UI or UI component turned out to be of my liking and modify or undo when I didn’t like the result, and tweaked the prompt.</p>
<p>A six hours of effort and an MVP is ready. After a half an hour of discussion with a demo later, I spent a couple of hours building a production ready webapp using ReactJS, FastAPI (Python), and Postgres. The client, a medium sized auditing firm, wasn't really keen on SRE and uptime and had an on premise Ubuntu server. I got it deployed using Docker Compose and gave a primer to their admin person. (As of now) They have been trying this software for six weeks now, and liking it. There might be more feature requests.</p>
<h1>So far, so good</h1>
<p>These are low hanging fruits for LLMs in early 2026. I knew a greenfield run of the mill project isn't going to be a toughie for LLM. Although, it did hallucinate a little here and there, but still better than a junior engineer. I wanted to drag LLM to some real messes, and we will see in the next posts, it's not there yet but the gap is closing real fast.</p>
<blockquote>
<p>The cover image was generated by ChatGPT 5.2 using the following prompt: "a tron like bike with a programmer like guy on it zooming fast on a road made of colorful coding view of dark UI of an editor and it's a curve. The shot is a low shot taken from the road level... the scene is like '80s sci-fi movies, and so are the colors."</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Automated Versioning of React Native Apps for Play Store]]></title><description><![CDATA[This post was originally published on Medium by me on Oct 15, 2021

It started with me forgetting to edit android/app/build.gradle file to increment the version number before uploading to Google Play Store on a Friday evening when I had popcorn getti...]]></description><link>https://code.naishe.in/automated-versioning-of-react-native-apps-for-play-store</link><guid isPermaLink="true">https://code.naishe.in/automated-versioning-of-react-native-apps-for-play-store</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[build]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Fri, 15 Oct 2021 13:36:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175815729/l2kwnL0uu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was <a target="_blank" href="https://medium.com/cybermonkey/automated-versioning-of-react-native-apps-for-play-store-c346c18c9f91?source=friends_link&amp;sk=9a7c3fd4f48c0de86e2ef20fbe23a13d">originally published on Medium</a> by me on Oct 15, 2021</p>
</blockquote>
<p>It started with me forgetting to edit <code>android/app/build.gradle</code> file to increment the version number before uploading to Google Play Store on a Friday evening when I had popcorn getting ready in the oven and friends already started streaming <a target="_blank" href="https://en.wikipedia.org/wiki/Squid_Game">Squid Game</a> in the hall. Play Store console didn’t let me publish the app with the old conflicting version (of course!), and I had to redo the slow build. I needed a way to automate this!</p>
<p>A couple of PSAs first:</p>
<ol>
<li>This article is only about Android releases. It does not go into iOS or React Native Web/Desktop releases.</li>
<li>If you have FastLane automation, you may want to use <a target="_blank" href="https://github.com/beplus/fastlane-plugin-versioning_android">their versioning plugin</a></li>
<li>If you are using Expo, look into <a target="_blank" href="https://docs.expo.dev/distribution/release-channels/">their Release Channels</a></li>
<li>If you are using Microsoft AppCenter Codepush, <a target="_blank" href="https://docs.microsoft.com/en-us/appcenter/distribution/codepush/cli">look into versioning mechanism in CodePush</a></li>
</ol>
<p>With that out of the way, let’s get going.</p>
<p>The demand started when the client wanted to see the version of the binary on the React Native screen. It looks something like this (it used to be a manual update to <code>package.json</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175800214/AVAUjmGB0.png" alt="The binary version number should be shown on the React Native screen" /></p>
<p>☝️ <em>The binary version number should be shown on the React Native screen</em></p>
<p>In React Native, I found no easy way to fetch the version number of the binary without having to install a massive library like <a target="_blank" href="https://github.com/react-native-device-info/react-native-device-info">react-native-device-info</a>, which sounded like an overkill to me. We know that we can fetch the version from <code>package.json</code>, what if there was a way to keep version numbers in <code>package.json</code> and <code>build.gradle</code> in sync? Auto-may-shun!!!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175802038/mMiAtykXH.png" alt="Automation!!!" /></p>
<p>☝️ Automation!!! Courtesy <a target="_blank" href="https://workchronicles.com/automation/">https://workchronicles.com/automation/</a></p>
<p><strong>STEP 1:</strong> Add these two utility functions to <code>android/app/build.gradle</code></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="3f013bd9fac779406531f90a695dd5ab"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/3f013bd9fac779406531f90a695dd5ab" class="embed-card">https://gist.github.com/naishe/3f013bd9fac779406531f90a695dd5ab</a></div><p>☝️ The Utility Functions That Fetch The Version Name and Code From package.json during the build</p>
<p>The idea is stolen from <a target="_blank" href="https://stackoverflow.com/a/54146923">this StackOverflow post</a></p>
<p><strong>STEP 2:</strong> Edit the lines in the block below to utilize our newly minted utility functions at the top of the file.</p>
<pre><code class="lang-gradle">defaultConfig {  
  applicationId YOUR_APPLICATION_ID  
  minSdkVersion rootProject.ext.minSdkVersion  
  targetSdkVersion rootProject.ext.targetSdkVersion  

  // Edit these two lines  
  versionCode 110  
  versionName 0.1.10  
}
</code></pre>
<p>And the block above should look like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="4f8a0030902837ffa263b64c06f1657c"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/4f8a0030902837ffa263b64c06f1657c" class="embed-card">https://gist.github.com/naishe/4f8a0030902837ffa263b64c06f1657c</a></div><p>☝️ Changes in <code>android/app/build.gradle</code> to use the utility methods</p>
<p>At this step, if you go ahead, and run <code>./gradlew bundleRelease</code>, you will see it picks up version value from <code>package.json</code>. If you are OK with manually updating <code>version</code> in <code>package.json</code> and running gradle build, you are done here. But, AUTOMATION?!!</p>
<p><strong>STEP 3:</strong> <code>yarn</code> and <code>npm</code> both provide a mechanism to update the version of the project (and also automatically tag it for you). See more details here: <a target="_blank" href="https://classic.yarnpkg.com/en/docs/cli/version">https://classic.yarnpkg.com/en/docs/cli/version</a></p>
<p>Along with updating the version, it also calls a version lifecycle method <code>postversion</code> (also a <code>preversion</code> before the versioning starts). So, if we call our Gradle build as part of the post version step, the following things will happen:</p>
<ol>
<li>Yarn will set the new release version (this process is interactive!) in <code>package.json</code>, and create a tag.</li>
<li>Yarn will kickoff gradle build which will use the new version in <code>package.json</code></li>
</ol>
<p>A win-win!</p>
<p>Let’s add a <code>postversion</code> script that performs the Gradle build in <code>package.json</code></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="bac4144de133ae58d23d5c13ae4f76f7"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/bac4144de133ae58d23d5c13ae4f76f7" class="embed-card">https://gist.github.com/naishe/bac4144de133ae58d23d5c13ae4f76f7</a></div><p>☝️ Add a new npm script</p>
<p>And, we are done! Open up your cli and fire the command <code>yarn version</code> and observe the smoothness. Ah-tow-mation, finally!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643209898635/FV6a4iP-k.png" alt="Watch the version code and version name derived from version in package.json" />
☝️ Watch the version code and version name derived from version in package.json</p>
<p>By the way, this is how you fetch the version from <code>package.json</code> on the React Native side:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="cbe9c3e835af01f8b01d22491e7e12c5"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/cbe9c3e835af01f8b01d22491e7e12c5" class="embed-card">https://gist.github.com/naishe/cbe9c3e835af01f8b01d22491e7e12c5</a></div><p>☝️ Fetching version from package.json in a React Native Component</p>
<p>That’s all, folks!</p>
]]></content:encoded></item><item><title><![CDATA[Deep Linking Push Notifications with React Navigation]]></title><description><![CDATA[This post was originally published on Medium by me on Dec 6, 2020

When a user taps on a push notification, they expect to land on the screen the notification is about. When I started to implement this feature, I found no easy way to do it. Although,...]]></description><link>https://code.naishe.in/deep-linking-push-notifications-with-react-navigation</link><guid isPermaLink="true">https://code.naishe.in/deep-linking-push-notifications-with-react-navigation</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[push notifications]]></category><category><![CDATA[Firebase]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Sun, 06 Dec 2020 11:47:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175824287/fHTdPzeoR.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was <a target="_blank" href="https://medium.com/cybermonkey/deep-linking-push-notifications-with-react-navigation-5fce260ccca2?source=friends_link&amp;sk=2877b6701b07dd244280f66e91037f16">originally published on Medium</a> by me on Dec 6, 2020</p>
</blockquote>
<p>When a user taps on a push notification, they expect to land on the screen the notification is about. When I started to implement this feature, I found no easy way to do it. Although, I had <a target="_blank" href="https://en.wikipedia.org/wiki/Mobile_deep_linking">deep linking</a> figured out, still there was no definitive approach to pass a deep link in push notification and let the link do the magic of taking the user to the intended screen.</p>
<p>We know where to go, but not how to</p>
<p>React Native Firebase project suggests a simple case of setting <code>initialRouteName</code> in the <code>Navigator</code> tag after extracting the route name from the notification’s data object. Refer: <a target="_blank" href="https://rnfirebase.io/messaging/notifications#handling-interaction">https://rnfirebase.io/messaging/notifications#handling-interaction</a></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="1eba531e9cbea51947f60b0df3ce61c1"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/1eba531e9cbea51947f60b0df3ce61c1" class="embed-card">https://gist.github.com/naishe/1eba531e9cbea51947f60b0df3ce61c1</a></div><p>☝️ Firebase Cloud Messaging suggest this mechanism to handle Push Notification for redirection</p>
<p>This has some obvious drawbacks:</p>
<ol>
<li>It seems to be convoluted to handle a situation where screens are scattered in nested Navigator tags across various files.</li>
<li>It is not obvious how to deal with parameters that may be required to pass to the screen to render it</li>
<li>We have a perfectly working deep linking mechanism to open the app and auto-navigate with parameters, why can’t we reuse it instead of reinventing the wheel?</li>
<li>If you try use a solution that lets you redirect to any screen from anywhere without props (<a target="_blank" href="https://reactnavigation.org/docs/navigating-without-navigation-prop/">https://reactnavigation.org/docs/navigating-without-navigation-prop/</a>), you will end up requiring to wait for the navigator to render to have this working. (this was my first solution, but I always ended up triggering the redirection/navigation as soon as the app starts before the <code>Navigator</code> is rendered, causing crash)</li>
</ol>
<p>Lucky for us, not long ago, a PR (<a target="_blank" href="https://github.com/react-navigation/react-navigation/pull/8987">https://github.com/react-navigation/react-navigation/pull/8987</a>) sorted this out for us. This article is about how to implement it.</p>
<p>I assume you have push notification functional. If not, head here to get that working first: <a target="_blank" href="https://rnfirebase.io/messaging/usage">https://rnfirebase.io/messaging/usage</a>. I also assume that you have React Navigation configured, and you are on a version 5.8.10 or above. I am using <code>"@react-navigation/native": "^5.8.10"</code> in this article.</p>
<hr />
<p>Before the linking is configured our app looks something like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="e448ec0436deb8cabaf06acd436c3a98"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/e448ec0436deb8cabaf06acd436c3a98" class="embed-card">https://gist.github.com/naishe/e448ec0436deb8cabaf06acd436c3a98</a></div><p>☝️ Simplest App with React Navigation</p>
<h3 id="heading-how-to-configure-deep-linking">How to configure deep linking</h3>
<p>Deep linking using React Navigation is well explained here: <a target="_blank" href="https://reactnavigation.org/docs/deep-linking">https://reactnavigation.org/docs/deep-linking</a> However, it took me some time to figure out how to support deep links that have <code>myapp://</code> as well as <code>https://app.myapp.com/</code> on Android phones. Trick is to add two different intent filters one for each like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f30cf0159a4d42117e83e10d54b474da"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/f30cf0159a4d42117e83e10d54b474da" class="embed-card">https://gist.github.com/naishe/f30cf0159a4d42117e83e10d54b474da</a></div><p>☝️ Configure URL Scheme in Android in a way that handles custom as well as HTTPS prefixed URLs</p>
<p>Now we gotta tell the <code>NavigationContainer</code> that we are reacting to these schemes, so my <code>App.tsx</code> becomes:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="da4830b72eeb81f8a5e29b6e45692d86"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/da4830b72eeb81f8a5e29b6e45692d86" class="embed-card">https://gist.github.com/naishe/da4830b72eeb81f8a5e29b6e45692d86</a></div><p>☝️ Deep Linking Done!</p>
<p>At this point, you rebuild your app (commonly, using <code>npx react-native run-android</code>), and you should be able to do the following from your CLI:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f5ae5bedc413e94c764f8b1aedba828b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/f5ae5bedc413e94c764f8b1aedba828b" class="embed-card">https://gist.github.com/naishe/f5ae5bedc413e94c764f8b1aedba828b</a></div><p>☝️ Test if your deep links work</p>
<p>Now that the deep linking is sorted, we can now freely share the deep link with https scheme in emails and websites and when people click this deep link on their desktops or phones that does not have your app installed, it will take them to your website where they can see the web version or you ask them to install the app nicely. If they have the app, they would find it delightful.</p>
<h3 id="heading-reusing-deep-links-in-push-notification">Reusing Deep Links in Push Notification</h3>
<p>Push Notification payload contains a data attribute. You can send any key-value pair as data. If we just pass the deep link as a part of data object and somehow we tell our navigation mechanism to open the link as the first screen instead of the home screen of the app, our work is done.</p>
<p>React Navigation provides a neat little function in the <code>linking</code> attribute of the <code>NavigationContainer</code> tag. <code>NavigationContainer</code> commonly contains all the application (and definitely all the routes). This function is named <code>getInitialUrl</code> and it is called every time the application launches from the quit state. It is where you will land when the app is opened from a push notification. So, we need to put the logic here to redirect to the appropriate screen. Since we cleverly pass the deep link as a part of push notification, we can let React Navigation deal with it.</p>
<p>Here is how our <code>linking</code> attribute looks like once we add <code>getInitialUrl</code> function to it (refer: <a target="_blank" href="https://reactnavigation.org/docs/navigation-container#linkinggetinitialurl%29:">https://reactnavigation.org/docs/navigation-container#linkinggetinitialurl):</a></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="46dfb1e23612398e4bd525f03c001dd9"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/46dfb1e23612398e4bd525f03c001dd9" class="embed-card">https://gist.github.com/naishe/46dfb1e23612398e4bd525f03c001dd9</a></div><p>☝️ Handle the case when Push Notification arrives and the app is closed</p>
<p>But what about the case when we receive the push notification when our app is in the background? You need to add a listener in that case. It looks like this (refer: <a target="_blank" href="https://reactnavigation.org/docs/navigation-container/#linkingsubscribe">https://reactnavigation.org/docs/navigation-container/#linkingsubscribe</a>):</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="fbc0ca0a3fa81da1872c96902d4c3140"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/fbc0ca0a3fa81da1872c96902d4c3140" class="embed-card">https://gist.github.com/naishe/fbc0ca0a3fa81da1872c96902d4c3140</a></div><p>☝️ Handle the case when Push Notification arrives and the app is running in background</p>
<p>We are done now. When a push notification is tapped either <code>getInitialUrl</code> is called or <code>onNotificationOpenedApp</code> invokes our listener passed in <code>subscribe</code> method. In both cases, React Navigation handles the incoming URL and redirects to the right screen.</p>
<p>To test this, go to Firebase console, to the project that’s linked to this app (URL looks something like this: https://console.firebase.google.com/u/0/project/<strong>YOUR-PROJECT-NAME-HERE</strong>/notification/compose), fill out fields. In the Custom Data field, add a <code>link</code> key, with value to one of the valid deep links.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175820213/eSqcpaSXh.png" alt="At the bottom of the screen there is a way to push key-value payload" /></p>
<p>☝️ At the bottom of the screen there is a way to push key-value payload</p>
<p>Assuming you have your device’s FCM token handy, send a test notification.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175822720/St75o1b7N.png" alt="Keep your FCM token handy to test your code" /></p>
<p>☝️ Keep your FCM token handy to test your code</p>
<p>Recline your chair, raise your arms, take a sip of hot tea while you wait for the notification to appear (should not take more than a moment), tap the notification and watch yourself chauffeured to the screen mentioned in the link. You are done for the day.</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Over-the-Air Updates in React Native with CodePush: Part 2]]></title><description><![CDATA[This post was originally published on Medium by me on Aug 5, 2020

This is the second and the final part of the two part series. Read the first part here.
As happy and dandy as we feel after setting up a pipeline that makes majority of app release by...]]></description><link>https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-2</link><guid isPermaLink="true">https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-2</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Wed, 05 Aug 2020 07:45:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175884572/lyfmLoyN5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was <a target="_blank" href="https://medium.com/cybermonkey/mastering-over-the-air-updates-in-react-native-with-codepush-part-2-37df58de6e7e">originally published on Medium</a> by me on Aug 5, 2020</p>
</blockquote>
<p>This is the second and the final part of the two part series. <a target="_blank" href="https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-1">Read the first part here</a>.</p>
<p>As happy and dandy as we feel after setting up a pipeline that makes majority of app release bypass the app stores, but the happiness does not last long when you want to do a pre-release testing (Alpha Track in app store) or a beta testing with beta participants, or A/B Testing over 20% of all the users. Only if CodePush could do these for us. Well, you are in luck, CodePush can do all this and more. Let’s explore?</p>
<h3 id="heading-an-app-with-multiple-deployment-keys">An App with Multiple Deployment Keys</h3>
<p>Let’s try and get our app to do the following:</p>
<ol>
<li>Do not pull the updates automatically</li>
<li>Pull the updates when the programmer or the user wants</li>
<li>When the updates are pulled programmatically, do not apply the updates immediately (Because that may stun the user. Imagine you are filling up a long from, and just before the submit button was clicked, the app reboots. Not nice.)</li>
<li>When user switches the deployment key (as in, they switch from Stable version to Beta version or vice versa), load the changes immediately</li>
<li>Provide an “Open Sesame” to the users to switch between beta and stable versions.</li>
</ol>
<h4 id="heading-manual-updates"><strong>MANUAL UPDATES</strong></h4>
<p>Remember the <code>AppWrappedInCodePush.tsx</code> file from Part 1? It looked laughingly small. We are going to make it the center of our deployment logic. Here is the full version of it:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="044ac7f2a80a4afa35ddb88b33b42af5"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/044ac7f2a80a4afa35ddb88b33b42af5" class="embed-card">https://gist.github.com/naishe/044ac7f2a80a4afa35ddb88b33b42af5</a></div><p>☝️ AppWrappedInCodePush.tsx</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="319a07bd971e03001b7f8c03d0de4f0f"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/319a07bd971e03001b7f8c03d0de4f0f" class="embed-card">https://gist.github.com/naishe/319a07bd971e03001b7f8c03d0de4f0f</a></div><p>☝️ constants.ts</p>
<p>Let’s read it through:</p>
<p><a target="_blank" href="https://gist.github.com/naishe/044ac7f2a80a4afa35ddb88b33b42af5#file-appwrappedincodepush-tsx-L35">#35</a>: We are now passing a parameter that tells the CodePush client to <em>not</em> pull the code automatically at the start up.</p>
<p>As soon as the main application is mounted, the lines <a target="_blank" href="https://gist.github.com/naishe/044ac7f2a80a4afa35ddb88b33b42af5#file-appwrappedincodepush-tsx-L14-L28">#14–28</a> does a couple of things:</p>
<ol>
<li>Checks if <code>AsyncStore</code> has a value for the key <code>IS_BETA_USER</code></li>
<li>If that key is set to <code>true</code>, sync the codebase with beta stream (because we are using the Staging Key)</li>
<li>Otherwise, sync the codebase with the stable stream as we pass the Production Key in this case</li>
<li>Noteworthy, we are setting the <code>installMode</code> to <code>codePush.InstallMode.ON_NEXT_RESTART</code>. Because, from the point of view of the user, this update is happening automatically, and in the background. If you install the code immediately, causing a reboot of the app, the user may not like it. So, we download and wait for the next time the user opens the app to show them the shiny new app.</li>
</ol>
<p>As of now, we have not set <code>IS_BETA_USER</code> to anything, so the behavior stays unchanged compared to what we had at the end of Part 1 of this article. But, we can see if somehow we set the <code>IS_BETA_USER</code> value to <code>true</code> in our AsyncStorage, we will start seeing the beta version of the app.</p>
<h4 id="heading-switching-between-stable-and-beta-versions"><strong>SWITCHING BETWEEN STABLE AND BETA VERSIONS</strong></h4>
<p>I like <a target="_blank" href="https://en.wikipedia.org/wiki/Easter_egg_%28media%29#:~:text=In%20computer%20software%2C%20Easter%20eggs,otherwise%20serious%20piece%20of%20software.">Easter Eggs</a> in software. What we want to do here is to create a component that looks like plain text information of the software’s version. When a user taps it 8 times, we show them the option to switch between stable and beta versions with a fair warning.</p>
<p>Here is that component’s code look like:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="6e6eb26db46d4da9b4249b3bde96d072"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/6e6eb26db46d4da9b4249b3bde96d072" class="embed-card">https://gist.github.com/naishe/6e6eb26db46d4da9b4249b3bde96d072</a></div><p>☝️ VersionInfo.tsx</p>
<p>Line number <a target="_blank" href="https://gist.github.com/naishe/6e6eb26db46d4da9b4249b3bde96d072#file-versioninfo-tsx-L58-L78">#58 — #78</a> is the core of it. The rest is just the display logic.</p>
<p>The <code>toggleBetaUser</code> does the following:</p>
<ol>
<li>Sets the <code>IS_BETA_USER</code> value as true or false and stores it in AsyncStore for persistence</li>
<li>Calls CodePush’s sync method with appropriate key with <code>installMode</code> as <code>IMMEDIATE</code>. This will cause the code to be downloaded from an appropriate release stream and apply it immediately after the download. This will cause the app to reboot and show the downloaded version.</li>
</ol>
<p>Now that we have a mechanism to toggle and save the user preference to AsyncStore, our code in the first part of this article would work. If use has set beta mode on, it will sync with the Staging stream, otherwise CodePush will sync with the Production stream.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175881120/U5vChgVSM.jpeg" alt="From left to right, top to bottom: 1. Stable version V5, 2. After the user tapped the version text 8 times, 3. The user opted-in for beta and assets are downloading, 4. User is now on beta version" /></p>
<p>☝️ From left to right, top to bottom: <strong>1.</strong> Stable version V5, <strong>2.</strong> After the user tapped the version text 8 times, <strong>3.</strong> The user opted-in for beta and assets are downloading, <strong>4.</strong> User is now on beta version</p>
<hr />
<h4 id="heading-fun-things-to-do-while-syncing"><strong>FUN THINGS TO DO WHILE SYNCING</strong></h4>
<p><em>MetaData</em>: <code>codePush.metadata()</code> returns null or a <code>LocalPackage</code> object. Among other things, it has a couple of cool attributes. <code>LocalPackage.appVersion</code> gives you the version of the binary installed on the device. You can see <code>0.0.4</code> in the screenshot above. <code>LocalPackage.label</code> gives the CodePush’s resource version installed. It is indicated by <code>v5</code> in production deployment, and <code>v3</code> in staging deployment. You can also ask <code>deploymentKey</code> from <code>LocalPackage</code>.</p>
<p><em>Check for Updates:</em> <code>codePush.checkForUpdate()</code> — This method returns a <code>Promise&lt;null|RemotePackage&gt;</code>. If null, there is nothing to do; all up to date. If it returns a package object, then you can either show to the user that a new update is available or pull the latest.</p>
<p><em>Synchronize at Will:</em> <code>codePush.sync()</code> — This method kicks off synchronization. This packs a couple goodies. Here is the method signature:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* 1.*/</span> codePush.sync(  
<span class="hljs-comment">/* 2.*/</span> options: <span class="hljs-built_in">Object</span>,  
<span class="hljs-comment">/* 3.*/</span> syncStatusChangeCallback: <span class="hljs-function">(<span class="hljs-params">syncStatus: <span class="hljs-built_in">Number</span></span>) =&gt;</span> <span class="hljs-keyword">void</span>,  
<span class="hljs-comment">/* 4.*/</span> downloadProgressCallback: <span class="hljs-function">(<span class="hljs-params">progress: DownloadProgress</span>) =&gt;</span> <span class="hljs-keyword">void</span>,  
<span class="hljs-comment">/* 5.*/</span> handleBinaryVersionMismatchCallback: <span class="hljs-function">(<span class="hljs-params">update: RemotePackage</span>) =&gt;</span> <span class="hljs-keyword">void</span>
<span class="hljs-comment">/* 6.*/</span> ): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">Number</span>&gt;;
</code></pre>
<p><em>options</em> is a JSON object. The most notable attributes are <code>deploymentKey</code> and <code>installMode</code>. The former is the deployment you want the app to sync with, and the latter is how you want the install to happen: immediately, after restart, when the app goes to background or any other way. There are other attributes that can be found in the documentation.</p>
<p><em>syncStatusChangeCallback</em> is a function with its signature as <code>(syncStatus: Number) =&gt; void</code>, where syncStatus can be any of the values as shown in the <code>enum</code> below. This is what I am using in my code to display the spinner until the <code>syncStatus</code> reaches the <code>UPDATE_INSTALLED</code> state</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="60cbfcf3a24e1b685f9d73b2043a0add"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/60cbfcf3a24e1b685f9d73b2043a0add" class="embed-card">https://gist.github.com/naishe/60cbfcf3a24e1b685f9d73b2043a0add</a></div><p><em>downloadProgressCallback</em> is a function with its signature as <code>(progress: DownloadProgress) =&gt; void</code>. You can get really fancy with this. You can use <code>DownloadProgress.totalBytes</code> and <code>DownloadProgress.receivedBytes</code> to show progress bar animation or something.</p>
<p><em>handleBinaryVersionMismatchCallback</em> can be used to handle situations where you are trying to sync with a deployment whose binary version has diverged from the one on the local machine. This is your opportunity to remind the user to download the latest version from the app store.</p>
<hr />
<h4 id="heading-enforce-certain-users-to-have-certain-deployment"><strong>ENFORCE CERTAIN USERS TO HAVE CERTAIN DEPLOYMENT</strong></h4>
<p>I hope you do not discriminate against your users on the basis of gender, race, region, belief or anything that they do not have their control over. That’s just not the nicest thing to do. But, it may make sense in certain cases to differentiate the users. What if you want to enable the beta version for all the users whose <code>UserProfile</code> object has <code>isBetaUser = true</code> in the database. Or, maybe, you wanted to roll out the beta version first in certain countries and based on their feedback, you want to decide whether to promote beta version globally.</p>
<p>In all such cases, wherever you fetching user profile just set <code>AsyncStore.setItem(‘IS_BETA_USER’, `${userProfile.isBetaUser}`)</code></p>
<p>Ta-da!</p>
<p><strong>Why stop here?</strong></p>
<p>The binary is already embedded with the production key. So, everyone will get the production version to start with. If nothing is altered, they will continue to have production updates. Why can’t we, instead of having a binary value — <code>IS_BETA_USER</code> and hardcoded Deployment Key, dynamically set Deployment Key in AsyncStore and use that key to sync?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175882999/ksQkVY1my.png" alt="Different UIs based on your thumb size?" /></p>
<p>☝️ Different UIs based on your thumb size?</p>
<p><em>That means, we can set different Deployment Keys for different users and make releases specific to them</em>. For example, if your sync logic looks like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// undefined userProfile.deploymentKey would use   </span>
<span class="hljs-comment">// the key stored in the binary  </span>
codePush.sync({  
 <span class="hljs-attr">deploymentKey</span>: userProfile.deploymentKey,  
 <span class="hljs-attr">installMode</span>: codePush.InstallMode.ON_NEXT_RESTART,  
});
</code></pre>
<p>A hypothetical ticket to make a completely new release only to a specific group of users and its workflow would look like this:</p>
<p><strong><em>TICKET #666</em>:</strong> Make a deployment specific to Indian and Australian users.<br /><strong><em>DevOps</em>:</strong> Well, since deployment keys are just a name and an identifier, let’s create one for this ticket</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new deployment key  </span>
<span class="hljs-comment"># appcenter codepush deployment add &lt;KEY_NAME&gt; --app &lt;USER&gt;/&lt;APP_NAME&gt;</span>
appcenter codepush deployment add colonial_cousins --app nishant/blog-app

Deployment colonial_cousins has been created <span class="hljs-keyword">for</span> nishant/blog-app with key nkaKawu1CtuHuzzahGIJWq_LOTVcAHEcyLEWt
</code></pre>
<p><strong><em>DBA</em>:</strong> Let’s set users in India and Australia with <code>deployment_key</code> as <code>nkaKawu1CtuHuzzahGIJWq_LOTVcAHEcyLEWt</code>, others may be null or whatever value they are set to.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> user_profiles   
<span class="hljs-keyword">SET</span> deployment_key = ‘nkaKawu1CtuHuzzahGIJWq_LOTVcAHEcyLEWt’   
<span class="hljs-keyword">WHERE</span> country <span class="hljs-keyword">IN</span> (‘AU’, ‘<span class="hljs-keyword">IN</span>’)
</code></pre>
<p><strong><em>Release Engineer</em>:</strong> Well, let’s push this release to Indians and Aussies, and close the ticket.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Release to specific deployment  </span>
<span class="hljs-comment"># appcenter codepush release-react -a &lt;USER&gt;/&lt;APP_NAME&gt; -d &lt;KEY_NAME&gt;</span>

appcenter codepush release-react -a nishant/blog-app -d colonial_cousins
</code></pre>
<p>There we go. We did not touch client code, we did not ask the user to do something special, and still, we have managed to distribute a targeted software update. Isn’t it smooth?</p>
<hr />
<h4 id="heading-ab-testing"><strong>A/B TESTING</strong></h4>
<p>Dark Mode vs Light Mode?<br />Tabs vs drawer?<br />Automated grouping or ungrouped messages?<br />How much time spent on the app on an average?</p>
<p>Sometimes, you want to be cautious, and release an upgrade to a percentage of users first and then, based on analytical data, you want to decide whether to go ahead with the upgrade or rollback. CodePush makes it as simple as specifying a parameter in the release command. You can specify what percentage of the users get this release. Here is an example</p>
<pre><code class="lang-bash"><span class="hljs-comment"># appcenter codepush release-react -a &lt;USER&gt;/&lt;APP_NAME&gt; -d &lt;KEY_NAME&gt; -r &lt;PERCENTAGE_ROLLOUT&gt;</span>

<span class="hljs-comment"># The following will roll out the blog app to 20% of the users who are having Production deployment</span>

appcenter codepush release-react -a nishant/blog-app -d Production -r 20%
</code></pre>
<hr />
<h4 id="heading-promoting-staging-release-to-production"><strong>PROMOTING STAGING RELEASE TO PRODUCTION</strong></h4>
<p>A common scenario is where once you have tested the app in staging, you want everyone else to have that version. It is a good practice, because it gives you confidence that the tested bundle is going to the production users, not a single bit different.</p>
<p>You can do it by running the promote command</p>
<pre><code class="lang-bash"><span class="hljs-comment">#appcenter codepush promote -s &lt;SOURCE_DEPLOYMENT_NAME&gt; -d &lt;DESTINATION_DEPLOYMENT_NAME&gt; -a &lt;USER&gt;/&lt;APP_NAME&gt;</span>

appcenter codepush promote -s Staging -d Production -a nishant/blog-app
</code></pre>
<p>Want to be super cautious and promote only to partial users? Just use the <code>-r</code> argument along with the other options above.</p>
<p>As always, Deployments are just names and identifiers, you can promote any deployment to any deployment.</p>
<hr />
<h4 id="heading-binary-targeted-release"><strong>BINARY TARGETED RELEASE</strong></h4>
<p>Imagine you have two binaries in app stores, one versioned 42.69.8 and another 43.0.0. The older version has some bugs which are fixed in the newer version, but the code in the newer version is incompatible to the code in the older version (maybe because you changed REST response, or added a new node module that’s not there in the older version).</p>
<p>You want to make a targeted release to the 42.69.8 <em>binary</em> release version.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Release to specific deployment</span>

<span class="hljs-comment"># appcenter codepush release-react -a &lt;USER&gt;/&lt;APP_NAME&gt; -d &lt;KEY_NAME&gt; -t &lt;VERSION&gt;</span>

appcenter codepush release-react -a nishant/blog-app -d Production -t 42.69.8
</code></pre>
<hr />
<h4 id="heading-fancier-things"><strong>FANCIER THINGS</strong></h4>
<p>It should be clear to you by now that a deployment is just a name and an identifier associated with it. So, can we use it in our JavaScript as a name and an identifier attached to it? Of course!</p>
<p>You can use the identifier as the environment name. You can have things like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// I am assuming you have method somewhere to get currently  </span>
<span class="hljs-comment">// used deployment key and a constants object that holds  </span>
<span class="hljs-comment">// the deployment keys</span>

<span class="hljs-keyword">const</span> isStaging =  
  getDeploymentKey() === constants.STAGING_DEPLOYMENT_KEY;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> API_ROOT_URL = isStaging  
  ? <span class="hljs-string">'https://test.tehbesttodo.dev/api'</span>
  : <span class="hljs-string">'https://production.tehbesttodo.app/api'</span>;
</code></pre>
<hr />
<p>I think I will close at this. There are many other helpful commands and tricks to use with CodePush, you just need to peek out of the box a little and read the documentation: <a target="_blank" href="https://docs.microsoft.com/en-us/appcenter/distribution/codepush/">https://docs.microsoft.com/en-us/appcenter/distribution/codepush/</a>. Definitely, the latter.</p>
<p>If you liked the article please do not hesitate to slap the clap button for 50 times. Yes, you can. It is not tiring at all! And share it may be?</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Over-the-Air Updates in React Native with CodePush: Part 1]]></title><description><![CDATA[This post was originally published on Medium by me on Aug 4, 2020

The initial days of a new mobile app development is a melting pot of frequently added features and improvement, broken-for-certain-devices releases, bugs slipped from the tired eyes, ...]]></description><link>https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-1</link><guid isPermaLink="true">https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-1</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Tue, 04 Aug 2020 02:30:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175876487/0zApoKF1s.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was <a target="_blank" href="https://medium.com/cybermonkey/mastering-over-the-air-updates-in-react-native-with-codepush-part-1-faf241a7f84b?source=friends_link&amp;sk=27b35de7307202e6be3759a5f056e580">originally published on Medium</a> by me on Aug 4, 2020</p>
</blockquote>
<p>The initial days of a new mobile app development is a melting pot of frequently added features and improvement, broken-for-certain-devices releases, bugs slipped from the tired eyes, and an out-of-patience-midnight-oil-burning-DevOps with a dark sense of humor. The only thing you <em>do not</em> want at this point is a third party authority with their impractical review policies guarding the doors of the mobile phones where your broken code is pushing the users to the brink of uninstalling the app and writing a scathing review on the store about how poorly developed the app is, ignoring all the hard work done by a team of three night owls surviving on Ramen noodle providing the app for free by paying the bills using AWS free credits (<a target="_blank" href="https://aws.amazon.com/activate/">https://aws.amazon.com/activate/</a>) and digging into their parents savings. No, we do not want this.</p>
<p><strong>WE WANT SEAMLESS UPDATES, AND WE WANT IT NOW!</strong></p>
<h3 id="heading-enter-codepush">Enter CodePush</h3>
<p>CodePush is a nifty little utility for React Native and Cordova developers to update JavaScript code and bundle assets on client devices on the fly, in minutes after the release. Remember, <em>Not the full app, just the JavaScript and bundle assets.</em> Let me explain this quickly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175867351/Lkx66SFhL.png" alt="CodePush: push your code" /></p>
<p>☝️ CodePush: push your code</p>
<p>React Native application is made of JavaScript code, image assets, and platform specific binaries bundled by the bundler. CodePush takes responsibility of keeping the JavaScript code and the asset part of the bundle in sync with the latest code that you have uploaded to the CodePush server. When the user installs your app, they get the version of JavaScript and assets that came with the binary that you uploaded to the AppStore or PlayStore. As soon as the user launches the app, the CodePush client that’s installed as a part of your app, checks the latest version of JavaScript bundle on CodePush server and downloads it. Then on, every time you update the bundle on CodePush server, the app syncs itself with it. Doesn’t it sound too similar to how web pages work?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175869009/iZ_4V8E-f.png" alt="CodePush code synchronization in short" />
☝️ CodePush code synchronization in short</p>
<p>Let’s clear up our expectations here. This is what we want:</p>
<ol>
<li>Set up an AppCenter account and configure our app with the CodePush library</li>
<li>Release the binary (.aab, .apk, .ipa) to the stores of your choice (or just distribute them over FTP, I don’t care)</li>
<li>Change some JavaScript, and release it via CodePush</li>
<li>Restart the app (this time new bundle should be downloaded and installed), and restart again (this time the new bundle should be applied) you should see the new changes</li>
<li>Allow the users to choose whether they want to become beta user</li>
<li>Use CodePush deployments to serve bleeding edge updates to beta users while others still have the stable version</li>
<li>Use CodePush to promote the beta version to production</li>
</ol>
<h3 id="heading-setting-up-codepush">Setting Up CodePush</h3>
<p>First, we need to create a Microsoft AppCenter account and set up a new project. It is easier than setting up your social media account. <em>It is free</em>, so, if you haven’t yet, go ahead and create an account here: <a target="_blank" href="https://appcenter.ms/">https://appcenter.ms/</a> . I will be waiting for you. Once in, go to <code>Add New &gt; Add New App</code>. Give a recognizable App Name, select OS either Android or iOS, select React Native as Platform and click Add New App button.</p>
<p>Select the App, from the left menu under Distribute, click CodePush. From the center, click "Create Standard Deployments"</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175870927/yztss1SHz.png" alt="CodePush is hidden under Distribute menu" /></p>
<p>☝️ CodePush is hidden under Distribute menu</p>
<p>Now, you should see a “Everything is Ready” message with info on how to integrate CodePush with your app and how to release it. On the top right corner, you should be seeing a spanner🔧wrench icon. Click it. You should be seeing two identifiers: one titled “Staging” and another named “Production”. Two channels essentially. Two streams if you will. The Names do not matter. You can use any of these for production release. But, no one likes to watch the world burning. So, let’s assume, we will use the Production key for production deployments and Staging for staging release (or beta testers).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175873090/D3taHck1V.png" alt="What’s in a name: deployment names and corresponding keys" /></p>
<p>☝️ What’s in a name: deployment names and corresponding keys</p>
<p>For the remaining of the article, whenever we are talking about Deployment Keys, Keys, Production Deployment Key, Staging Deployment Key, or Beta Deployment key, we are talking about one or both of these values. (Over the course of the article, I created two different projects in CodePush, so, the values in the text below may not match the values in the image above)</p>
<hr />
<h3 id="heading-integrate-codepush-with-the-app">Integrate CodePush with the App</h3>
<p>With our CodePush server configuration done, we are ready to do what we love the most. Code. (Really?) CodePush comes under Microsoft’s AppCenter service which provides much wider services than just managing release synchronizations.</p>
<p><strong>Install AppCenter CLI</strong></p>
<p>Just follow the command and replace appropriate values wherever it makes sense.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Get the CLI  </span>
npm install -g appcenter-cli

<span class="hljs-comment"># Login  </span>
appcenter login

<span class="hljs-comment"># View your apps, we will use these names later  </span>
appcenter apps list

<span class="hljs-comment"># View the environments and corresponding deployment keys  </span>
<span class="hljs-comment"># appcenter codepush deployment list -k --app &lt;USERNAME&gt;/&lt;APPNAME&gt;  </span>
appcenter codepush deployment list -k --app nishant/blog-app

┌────────────┬───────────────────────────────────────┐  
│ Name       │ Key                                   │  
├────────────┼───────────────────────────────────────┤  
│ Production │ Jbach1Qk_achQarrrhNOA9zwVlJnruLsa9jsr │  
├────────────┼───────────────────────────────────────┤  
│ Staging    │ jhai-muzz27Nt7Y11KLp-cBsNA22EGjGHEYfr │  
└────────────┴───────────────────────────────────────┘
</code></pre>
<h3 id="heading-code-changes">Code Changes</h3>
<p>Install the appropriate version of react-native-code-push as mentioned here: <a target="_blank" href="https://github.com/microsoft/react-native-code-push">https://github.com/microsoft/react-native-code-push</a></p>
<p>As of now here is the versions of react-native-code-push and corresponding react-native versions:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="69ffa2038a9f11a9aa508ee74635b43d"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/69ffa2038a9f11a9aa508ee74635b43d" class="embed-card">https://gist.github.com/naishe/69ffa2038a9f11a9aa508ee74635b43d</a></div><p><strong>WRAP THE APP</strong></p>
<p>Wrap the main app component with CodePush’s higher order component. You can do it the way it is explained here in their official documentation: <a target="_blank" href="https://github.com/microsoft/react-native-code-push#plugin-usage">https://github.com/microsoft/react-native-code-push#plugin-usage</a></p>
<p>I personally do not like too many things in my <code>App.tsx</code> or <code>App.jsx</code> file. In a realistic case, you must already be wrapping the main App component with various <code>Context</code> providers like Theme, Redux, GraphQL client, Authorization provider et-cetera. Adding more noise to this is just… well, not so pretty. But prettiness apart, CodePush has a separate concern altogether. CodePush is a continuous delivery solution provider, and the rest of the code is either to rendering the UI or to perform the business logic.</p>
<p>So, I created a separate class component that wraps the App component with CodePush component. And, edited <code>index.js</code> to use this new class component. Here is what they look like</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="c2b552b99c649cb6918544c0f9cd6e53"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/c2b552b99c649cb6918544c0f9cd6e53" class="embed-card">https://gist.github.com/naishe/c2b552b99c649cb6918544c0f9cd6e53</a></div><p>☝️ AppWrappedInCodePush.tsx</p>
<blockquote>
<p><strong>💡 No CodePush in local env?!</strong></p>
<p>If you don’t wanna activate CodePush while you are in the debugging mode. You can conditionally apply CodePush wrapper like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> __DEV__ ? App : codePush(AppWrappedInCodePush);
</code></pre>
</blockquote>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="c5f42603f1283032ada08e3ee41f0fe0"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/c5f42603f1283032ada08e3ee41f0fe0" class="embed-card">https://gist.github.com/naishe/c5f42603f1283032ada08e3ee41f0fe0</a></div><p>☝️ Changes to be made in index.js</p>
<p>Now, we need to do OS specific changes. Please note that OS specific instructions below are for React Native 0.60.0 and onwards. For older versions, <a target="_blank" href="https://docs.microsoft.com/en-us/appcenter/distribution/codepush/">refer the official documentation</a>.</p>
<p><strong>Android</strong></p>
<p>For the Android side of the code, here are diffs and list of files to be changed.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a27201e2708bd42cc550d372c273152b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/a27201e2708bd42cc550d372c273152b" class="embed-card">https://gist.github.com/naishe/a27201e2708bd42cc550d372c273152b</a></div><p>☝️ android/app/build.gradle</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="1075527924b06b7a57c99b6726f8898b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/1075527924b06b7a57c99b6726f8898b" class="embed-card">https://gist.github.com/naishe/1075527924b06b7a57c99b6726f8898b</a></div><p>☝️ MainApplication.java</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="3bed620624a955a7d112db62dfd46895"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/3bed620624a955a7d112db62dfd46895" class="embed-card">https://gist.github.com/naishe/3bed620624a955a7d112db62dfd46895</a></div><p>☝️ strings.xml contains the Production CodePush Deployment Key, use your own key!</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="96cb7a6f46d3c48df2edc856f5aa1ee8"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/96cb7a6f46d3c48df2edc856f5aa1ee8" class="embed-card">https://gist.github.com/naishe/96cb7a6f46d3c48df2edc856f5aa1ee8</a></div><p>☝️ settings.gradle</p>
<blockquote>
<p><strong>🐞 POSSIBLE BUG!</strong><br />Got a message in late 2021, someone saying CodePush sync wasn’t working on Android. Back then, I tested on a new project, it worked. Recently (early 2022), a freelance project I was working on I saw the issue. A little bit of searching around led me to this GitHub post: <a target="_blank" href="https://github.com/microsoft/react-native-code-push/issues/1961">CodePushUnknownException</a>. In case you are facing this, please make the following change to your <code>android/app/build.gradle</code> file, add the following lines under <code>android.defaultConfig</code> block:</p>
<pre><code class="lang-gradle">// The code below is one single line!  
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\\"%d\\"", System.currentTimeMillis())
</code></pre>
</blockquote>
<p><strong>iOS</strong></p>
<p>iOS side requires fewer changes, here they are:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="8f95ac9312d9f5cdadc42af45e7872f6"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/8f95ac9312d9f5cdadc42af45e7872f6" class="embed-card">https://gist.github.com/naishe/8f95ac9312d9f5cdadc42af45e7872f6</a></div><p>☝️ AppDelegate.m</p>
<p>You need to add the <code>CodePushDeploymentKey</code> in your <code>Info.plist</code> and set its value to the Production key from the CodePush console.</p>
<p>You can use XCode to add this, as show below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175874744/4TStyDI2_.png" alt="Add CodePush Production Deployment Key as a new attribute to Plist, use your own" /></p>
<p>☝️ Add CodePush Production Deployment Key as a new attribute to Plist, use your own</p>
<p>Or you can edit the <code>Info.plist</code> directly, it looks like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a58eb7554e4adfe9531d6d7e6ffd940c"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/a58eb7554e4adfe9531d6d7e6ffd940c" class="embed-card">https://gist.github.com/naishe/a58eb7554e4adfe9531d6d7e6ffd940c</a></div><p>☝️ Editing Info.plist in a text editor? Here are the changes. Make sure the Production Deployment Key is right.</p>
<hr />
<p>Phew!!! A lot of changes, feeling anxious? I think that’s the right response. Now, you need to build the production release binary and distribute it to the users (via AppStore, PlayStore, and/or by just sharing the APK or IPA file).</p>
<p>Cool. The users have the app up and running. Now, make a change (or many changes) to JavaScript files. Perform a CodePush release, here how you do it:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Make your changes and then  </span>
<span class="hljs-comment"># appcenter codepush release-react -a &lt;USERNAME&gt;/&lt;APPNAME&gt;  -d Production  </span>
appcenter codepush release-react -a nishant/blog-app -d Production

Detecting android app version:  
Using the target binary version value <span class="hljs-string">"0.0.4"</span> from <span class="hljs-string">"android/app/build.gradle"</span>.

Running <span class="hljs-string">"react-native bundle"</span> <span class="hljs-built_in">command</span>:  
[-- snip --]  
index.js --platform android  
Welcome to React Native!  
Learn once, write anywhere  
[-- snip --]

Releasing update contents to CodePush:  
Successfully released an update containing the <span class="hljs-string">"/var/folders/89/s5j46yyd7xn1s9ctl\_5td2d80000gn/T/code-push202072-97439-5f5fka.gokh2/CodePush"</span> directory to the <span class="hljs-string">"Production"</span> deployment of the <span class="hljs-string">"blog-app"</span> app.
</code></pre>
<p>Give it a few moments, and then open the app (maybe keep it up for a minute or two so that the sync completes), close the app, and open the app again. You should be seeing the new changes.</p>
<p>Congratulate yourself. 🎉</p>
<p>At this point, we have the pipeline set. Since the majority of the code movement in React Native applications happen in JavaScript, whenever we want to release the updated code to our users, we’d just push the code through CodePush and have it reflected immediately on the users devices without waiting for AppStore or PlayStore to approve it.</p>
<p>A lot of us would be happy with just this. However, wouldn’t it be better if we could have two release streams — one for a smaller audience, the beta testers who would get the preview version and another the stable release for everyone else? When we are happy with the beta version, we’d promote it to stable release for everyone else to see it.</p>
<p>Shouldn’t we have more control over the updates? Is there a way to manually kick off the update? Should we ask the user whether they want the update? Can we show update progress? Can we rollback a bad release? Since you mentioned preview and stable versions, can we use this for A/B testing? Can we have more than two deployment keys? Can we remotely control the user’s version (deployment key) by setting key specific to them on their user profile when they log in? Can we use these keys to switch the environment as in, one key point to test environment APIs and other to production APIs? Also, when is CodePush not enough and we require a binary release to the stores?</p>
<p>We will discuss all these fun things in the next part.</p>
<p>Fresh from the oven, <a target="_blank" href="https://code.naishe.in/mastering-over-the-air-updates-in-react-native-with-codepush-part-2">here is the second part</a></p>
]]></content:encoded></item><item><title><![CDATA[Mastering Multiple Language Support in React Native: Part 3]]></title><description><![CDATA[This post was originally published on Medium by me on July 22, 2020

In this final chapter of the three part tutorial, we will discuss some of the advanced features offered by i18next, these topics will include rendering dynamic strings using feature...]]></description><link>https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-3</link><guid isPermaLink="true">https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-3</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[i18n]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Wed, 22 Jul 2020 12:50:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175906858/0X1D1eWFD.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-3-d8091339c767?source=friends_link&amp;sk=20a5424484a055640a2b4139f6547774">originally published on Medium</a> by me on July 22, 2020</p>
</blockquote>
<p>In this final chapter of the three part tutorial, we will discuss some of the advanced features offered by <code>i18next</code>, these topics will include rendering dynamic strings using features such as interpolation, formatting, setting timezone locale, pluralization, nesting, context, and JSON objects and arrays.</p>
<p>If you want to learn about configuring localization and internationalization with i18next, jump to <a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">part 1 of this series</a>.</p>
<p>If you want to learn about organizing translations for a medium to large application and providing language change control to the users, hop on to <a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-2">part 2 of the series</a>.</p>
<p>If you want to view code for this part, please clone the repo at: <a target="_blank" href="https://github.com/naishe/rn_multilanguage">https://github.com/naishe/rn_multilanguage</a> and checkout <code>part_3</code> branch.</p>
<p>With all the formality done, let’s earn some elbow grease, shall we?</p>
<p><strong>INTERPOLATION</strong></p>
<p>The most frequently used utility is interpolation. Interpolation allows you to provide a templated string like <code>{ howdy: “Hey {{ username }}, how are you doing today?”}</code> and render it with the dynamic value you pass from the rendering code.</p>
<pre><code class="lang-js">t(<span class="hljs-string">"howdy"</span>, { <span class="hljs-attr">username</span>: <span class="hljs-string">"Nishant"</span>});
<span class="hljs-comment">// returns: Hey Nishant, how are you doing?</span>
</code></pre>
<p>But, life isn’t easy. Most of the time you have things like <code>user</code> object and <code>userBill</code> object and you need to print something like <code>Hey &lt;username&gt;, your account has been suspended until your last bill Rs &lt;bill amount&gt; is cleared.</code> Here is the trick:</p>
<pre><code class="lang-js"><span class="hljs-comment">// String template  </span>
<span class="hljs-attr">accountSuspended</span>: <span class="hljs-string">"Hey {{ user.username }}, your account has been suspended until your last bill Rs {{ userBill.total }} is cleared."</span>

<span class="hljs-comment">// Rendering  </span>
<span class="hljs-keyword">const</span> user = {  
 <span class="hljs-attr">username</span>: <span class="hljs-string">"Nishant"</span>,  
 <span class="hljs-attr">intelligenceLevel</span>: <span class="hljs-number">7</span>  
};

<span class="hljs-keyword">const</span> userBill = {  
 <span class="hljs-attr">total</span>: <span class="hljs-number">420.69</span>,  
 <span class="hljs-attr">paymentDueDate</span>: <span class="hljs-string">"May the fourth"</span>  
};

t(<span class="hljs-string">"accountSuspended"</span>, { user, userBill })  
<span class="hljs-comment">// returns: Hey Nishant, your account has been suspended until your last bill Rs 420.69 is cleared.</span>
</code></pre>
<hr />
<p><strong>FORMATTING</strong></p>
<p>You can create your custom formatting function and pass its name along with the string to be formatted to <code>i18next.t</code> and it will do it for you. You need to register your formation functions in <code>init</code> configuration under the <code>interpolation</code> attribute as <code>format</code> key.</p>
<p>For example, if you want to write a formatting function that takes a number and prints the Indian flag that many times if language code is <code>hi</code>, Hindi, else prints world emoji those many times. If it is not a number it returns whatever is passed. The code looks like as below:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="4fa54ae675bf7b410a22d7ef7fd26ca7"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/4fa54ae675bf7b410a22d7ef7fd26ca7" class="embed-card">https://gist.github.com/naishe/4fa54ae675bf7b410a22d7ef7fd26ca7</a></div><p>I haven’t found a great use case where I would use formatting. One of the most useful things we can do is we can instruct Moment.js (<a target="_blank" href="https://momentjs.com/">https://momentjs.com/</a>) to use locale whenever the language preference changes.</p>
<p>You just need to add a listener after initializing <code>i18next</code>, like this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// i18next configuration file</span>

<span class="hljs-comment">//Make sure you import the locales  </span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'moment/min/locales'</span>;  
<span class="hljs-keyword">import</span> moment <span class="hljs-keyword">from</span> <span class="hljs-string">'moment'</span>;

<span class="hljs-comment">// — removed for brevity — </span>

i18n.on(<span class="hljs-string">'languageChanged'</span>, <span class="hljs-function">(<span class="hljs-params">lng: <span class="hljs-built_in">string</span></span>) =&gt;</span> moment.locale(locale));
</code></pre>
<p>A caveats here. Moment does not load all the locales so you need to import the locales.</p>
<p>Let’s have a look how formatting code looks like:</p>
<pre><code class="lang-jsx">&lt;Text&gt;{t(<span class="hljs-string">'flagsCount'</span>, {<span class="hljs-attr">count</span>: <span class="hljs-number">3</span>})}&lt;/Text&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>  
  Moment.js Locale 42 minutes ago:  
  {moment().subtract(42, 'minutes').fromNow()}  
<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>
</code></pre>
<p>And it renders:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175888591/5pvMD4R4j.png" alt="Format in action, also automatically switches locale for Moment.JS" /></p>
<p>☝️ Format in action, also automatically switches locale for Moment.JS</p>
<hr />
<p><strong>PLURALIZATION</strong></p>
<p>Different languages handle pluralization differently. It is not uncommon to come across uses where you need to display difference strings depending on the quantity of subject matter. Here is an example in English:</p>
<p><em>Singular</em>: 1 knife found during the autopsy<br /><em>Plural</em>: 3 knives found during the autopsy</p>
<p>To display different content based on the number passed to <code>i18next.t</code> function, you need to define two keys, one regular key to be used for singular cases and another key suffixed with <code>_plural</code> for plural usage.</p>
<pre><code class="lang-js">{  
  <span class="hljs-attr">autopsy</span>: “{{count}} knife was found during the autopsy”,  
  autopsy\_plural: “{{count}} knives were found during the autopsy”,  
}

<span class="hljs-comment">// returns:  </span>
<span class="hljs-comment">// t(‘autopsy’, {count: 1}) = 1 knife was found during the autopsy  </span>
<span class="hljs-comment">// t(‘autopsy’, {count: 7}) = 7 knives are found during the autopsy</span>
</code></pre>
<p>However, this way of writing sentences does not have human touch to it, it is still too beep-boop-1-krrr-knife-was-found-isque. We would probably want something like this:</p>
<p>No knife was found on the dead body.<br />A knife was found during the autopsy.<br />7 knives were found during the autopsy. Looks like a work of a deranged maniac.</p>
<p>To that end, let’s try specifying what number triggers which translation. So, for example, a set of keys like this should serve our purpose:</p>
<pre><code class="lang-js">{  
 <span class="hljs-attr">autopsy_0</span>: <span class="hljs-string">"No knife was found on the dead body."</span>,  
 <span class="hljs-attr">autopsy</span>: <span class="hljs-string">"A knife was found during the autopsy."</span>,  
 <span class="hljs-attr">autopsy_plural</span>: <span class="hljs-string">"{{ count }} knives were found during the autopsy. Looks like a work of a deranged maniac."</span>,  
}

<span class="hljs-comment">// returns:  </span>
<span class="hljs-comment">// t(‘autopsy’, {count: 0}) = 0 knives were found during the autopsy. Looks like a work of a deranged maniac.</span>

<span class="hljs-comment">// t(‘autopsy’, {count: 1}) = A knife was found during the autopsy.</span>

<span class="hljs-comment">// t(‘autopsy’, {count: 7}) = 7 knives were found during the autopsy. Looks like a work of a deranged maniac.</span>
</code></pre>
<p>Hmmm, it did not work that way expected. I was disappointed to learn this.</p>
<p><strong>Limitations of Plural:</strong> The variable has to be named <code>count</code>. I would have liked a case where you can tell which field in a JSON object controls the pluralization. But, this is just a minor inconvenience.</p>
<p>You need to know which locale supports what kind of pluralization, so you cannot arbitrarily create keys <code>key_0</code>, <code>key_1</code>, <code>key_2</code>, and so on and expect it to work. You can use this lookup utility to see what suffixes work for your locale <a target="_blank" href="https://jsfiddle.net/sm9wgLze">https://jsfiddle.net/sm9wgLze</a></p>
<p>Also, have a look at this issue: <a target="_blank" href="https://github.com/i18next/i18next/issues/1220">https://github.com/i18next/i18next/issues/1220</a></p>
<p>Let me explain what I just said, for example:</p>
<p><code>hi</code> supports <code>key</code> (for count = 1) and <code>key_plural</code> (for count other than 1, even zero and negative counts)</p>
<p><code>en</code> supports <code>key</code> (for count =1) and <code>key_plural</code> (for all other values of count)</p>
<p><code>ar</code> — Arabic — supports <code>key_0</code> (for count=0), <code>key_1</code> (for count=1), <code>key_2</code> (for count=2), <code>key_3</code> (for count=3 to 10), <code>key_4</code> (for count=11 to 99), <code>key_5</code> (for count=100 and above)</p>
<p>So, pluralization does not look much fun anymore. If you still want to use count based translation, the good people from i18next have provided a post processor <a target="_blank" href="https://github.com/i18next/i18next-intervalplural-postprocessor">https://github.com/i18next/i18next-intervalplural-postprocessor</a> You can learn about it on their GitHub repo.</p>
<hr />
<p><strong>CONTEXT</strong></p>
<p>Context can be thought of as “which variation of a key can be used in a given context”. Case in point, ThatMate (<a target="_blank" href="https://thatmate.com">https://thatmate.com</a>) is a sexual and mental health awareness platform for teenagers. We have been planning to use binary and gender neutral pronouns in English locales. Here is quick glance on what it looks like</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175890832/HcWeFbXgJ.png" alt="Learn more about gender spectrum at https://lbgtrc.msu.edu/educational-resources/pronouns/" /></p>
<p>☝️ Learn more about <a target="_blank" href="https://lbgtrc.msu.edu/educational-resources/pronouns/">gender spectrum</a></p>
<pre><code class="lang-js">{  
  <span class="hljs-attr">loveThyself_she</span>: <span class="hljs-string">"She is in love with herself"</span>,

  <span class="hljs-attr">loveThyself_he</span>: <span class="hljs-string">"He is in love with himself"</span>,

  <span class="hljs-attr">loveThyself_they</span>: <span class="hljs-string">"They is in love with themself"</span>,   
  <span class="hljs-comment">//yes, "is" is the right helping verb for they here</span>

  <span class="hljs-attr">loveThyself_ze</span>: <span class="hljs-string">"Ze is in love with hirself"</span>,

  <span class="hljs-attr">loveThyself_ey</span>: <span class="hljs-string">"Ey is in love with eirself"</span>,  
}
</code></pre>
<p>We want the appropriate translation to appear on screen based on what the user has chosen as their preferred pronoun. So, if a user’s profile says their preferred pronoun is “ey”, we want then to see the last variations of “loveThyself”. My context here is “ey”</p>
<p>Context is a really powerful tool. For example, Hindi, ‘hi’ does not have equivalent five pronouns. It has just three pronouns, for male, for female, and a gender neutral. We need to fallback on gender neutral pronouns in Hindi for ‘ze’ and ‘ey’ pronouns. But that does not mean we will have to create five keys the same as the case above and have ‘ze’, ‘ey’ and ‘they’ have the same content. We can just define, three and non matching will fallback to suffix-less translation. So, the Hindi keys look like this:</p>
<pre><code class="lang-js">{  
  <span class="hljs-attr">loveThyself_she</span>: <span class="hljs-string">'वह अपने से प्रेम करती है।,  
  loveThyself_he: '</span>वह अपने से प्रेम करता है।<span class="hljs-string">',  
  loveThyself: '</span>वो अपने से प्रेम करते हैं।<span class="hljs-string">',  
}</span>
</code></pre>
<p>And to access these contexts you need to call `i18next.t` with key and context as options. So, for a code that look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> genderPronouns = \[<span class="hljs-string">'she'</span>, <span class="hljs-string">'he'</span>, <span class="hljs-string">'they'</span>, <span class="hljs-string">'ze'</span>, <span class="hljs-string">'ey'</span>\];

<span class="hljs-comment">// — code removed for brevity</span>

{   
  genderPronouns.map(<span class="hljs-function">(<span class="hljs-params">context</span>) =&gt;</span>   
    (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{context}</span>&gt;</span>{t('loveThyself', {context})}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>)  
  )  
}
</code></pre>
<p>The outcome in <code>en</code> language code is as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175893153/QFa29iQpf.png" alt="All five genders have their appropriate pronouns" /></p>
<p>☝️ All five genders have their appropriate pronouns</p>
<p>And, the same code render the following in <code>hi</code> language:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175897734/9uGbdQ7x9.png" alt="Hindi has just three forms of the pronouns" /></p>
<p>☝️Hindi has just three forms of the pronouns</p>
<p>If you look closely, even though you do not get the script, the last three fallback to the translation that does not have any context (no suffix).</p>
<p>— — — — — — —</p>
<p>There are only a few good things in the world. You can count them if you think hard and long lying on your back well fed and healthy in a serene environment. Context is one of those. Remember how disappointing it was that pluralization could not help us with cases where we specified translations for count = 0, 1, and for all other values of count. Context with help of interpolation can do that. The following code gives the desired result:</p>
<pre><code class="lang-jsx">&lt;Text&gt;{t(‘autopsy’, {<span class="hljs-attr">count</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">context</span>: ‘<span class="hljs-number">0</span>’})}&lt;/Text&gt;  
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>{t(‘autopsy’, {count: 1, context: ‘1’})}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>  
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>{t(‘autopsy’, {count: 7, context: ‘7’})}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>
</code></pre>
<p>The result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175899611/WX5HcYkKW.png" alt="Pluralization combined with context can do what Pluralization should have done" /></p>
<p>☝️ Pluralization combined with context can do what Pluralization should have done</p>
<hr />
<p><strong>OBJECTS AND ARRAYS</strong></p>
<p>Language resources are just a big JSON object with string keys and values that could string, JSON objects or arrays. Sometimes you may want <code>i18next</code> to return you the object or array instead of string. One such case appeared when we wanted to display a random funny loading message from a list of loading messages. If we get a list of strings, we could just do <code>list[ Math.floor( Math.random() * list.length ) ]</code>. Luckily for us, we can retrieve a key as a JSON object or array, just by instructing <code>i18next</code> so.</p>
<pre><code class="lang-js">{  
  <span class="hljs-attr">funnyLoadingMessages</span>: [  
    <span class="hljs-string">"Maybe the server gnomes are sleeping"</span>,  
    <span class="hljs-string">"What is taking so long?"</span>,  
    <span class="hljs-string">"Damn! I can believe I paid for this app."</span>,  
    <span class="hljs-string">"One mississippi, two mississippi, …"</span>,  
  ]  
}
</code></pre>
<p>And the following code returns the array</p>
<pre><code class="lang-js">t(<span class="hljs-string">"funnyLoadingMessages"</span>, { <span class="hljs-attr">returnObjects</span>: <span class="hljs-literal">true</span> });
</code></pre>
<p>You can apply interpolation and pluralization on the return as well.</p>
<hr />
<p><strong>REFERENCING OTHER KEYS</strong><br />You can reference one key into another key’s translations. For example, something like this:</p>
<pre><code class="lang-js">{  
  <span class="hljs-attr">salutation</span>: <span class="hljs-string">"howdy!"</span>,  
  <span class="hljs-attr">goodnight</span>: <span class="hljs-string">"$t( salutation ) What are you doing so late up? I am signing off. Good night, m8!"</span>  
}

t(<span class="hljs-string">"goodnight"</span>);  
<span class="hljs-comment">// returns: howdy! What are you doing so late up? I am signing off. Good night, m8!</span>
</code></pre>
<p>And as always, interpolation, context, etc works as expected.</p>
<hr />
<p>This concludes the third and the final chapter of this tutorial. But, by no means it is a substitute of the documentation offered by i18next (<a target="_blank" href="https://www.i18next.com/">https://www.i18next.com/</a>) and react-i18next (<a target="_blank" href="https://react.i18next.com/">https://react.i18next.com/</a>), so if you want to dive deeper please look into those resources.</p>
<p>Here is how the final app looks like</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175902053/k1VXIfjXS.jpeg" alt="Left is Hindi locale, and right is English" /></p>
<p>☝️ Left is Hindi locale, and right is English</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175905303/A86CXvtpV.jpeg" alt="Of course, you’d give the repo a try, but just in case, you wanted to see" /></p>
<p>☝️ Of course, you’d give the repo a try, but just in case, you wanted to see</p>
<p>Please press the clap button 50 times (yes, you can) if you liked the article. I wrote this article while looking for a complete How-To for multiple language support on React Native in the middle of a break neck ongoing sprint, so there is a good chance that I have made buckets of spelling and grammatical mistakes. Please be kind and let me know if you come across mistakes of any kind. I hope my tardy sense of humor did not bore you to death.</p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">Read Part 1</a></p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">Read Part 2</a></p>
]]></content:encoded></item><item><title><![CDATA[Mastering Multiple Language Support in React Native: Part 2]]></title><description><![CDATA[This post was originally published on Medium by me on July 22, 2020

This article runs in three parts. 
Read Part 1
Read Part 3
If you are looking for the code for part 2, checkout part_2 branch of the GitHub repo at https://github.com/naishe/rn_mult...]]></description><link>https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-2</link><guid isPermaLink="true">https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-2</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[i18n]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Wed, 22 Jul 2020 05:03:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175859415/h8xncApAw.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was originally <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-2-d33262acc21d?source=friends_link&amp;sk=e670ed196678a2ffbd38899301d69c32">published on Medium</a> by me on July 22, 2020</p>
</blockquote>
<p>This article runs in three parts. </p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">Read Part 1</a></p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-3">Read Part 3</a></p>
<p>If you are looking for the code for part 2, checkout <code>part_2</code> branch of the GitHub repo at <a target="_blank" href="https://github.com/naishe/rn_multilanguage">https://github.com/naishe/rn_multilanguage</a></p>
<p>In the <a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">last part</a>, we have got the basics of multiple language support done. We could set up a project, guess the best possible language, store and retrieve language preference and display content in the language. It sounds like we are done, but for any serious medium to large project, there are a bunch of things you want to do to avoid pulling your hair over the course of app development. In this article, we will do some advanced things. Here is the list of things we will do:</p>
<ol>
<li>Splitting the translations into manageable files and move the JSON away from the config file</li>
<li>Allow users to change the language and apply it across the app</li>
<li>[Let’s leave it for part 3] Rendering dynamic strings using features such as interpolation, formatting, setting timezone locale, pluralization, nesting, context, and JSON objects and arrays</li>
</ol>
<p><strong>Organizing translations: Namespaces and Deep Objects</strong></p>
<p>What to want when it comes to organizing translations? We want each language to have its own directory, and we want translations to split in multiple files. Maybe, I want to have a “common” file that has translation for commonly used texts like OK, Cancel, Back, and so on. I also want one file for each screen, so I want translations for Home screen in home file, and Settings screen in settings file. So, this is what I want as far as folder structure is concerned</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175842698/gFPZNsZkf.png" alt="Folder Structure of Translation Files" /></p>
<p>☝️ Folder Structure of Translation Files</p>
<p>Where my <code>common.ts</code>, <code>homePage.ts</code>, and <code>index.ts</code> looks as follows:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="3e7c4343612f47a2fedec3082e66fdb4"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/3e7c4343612f47a2fedec3082e66fdb4" class="embed-card">https://gist.github.com/naishe/3e7c4343612f47a2fedec3082e66fdb4</a></div><p>☝️ <code>common.ts</code>, <code>homePage.ts</code> and <code>index.ts</code> for English (en)</p>
<p>So, essentially, when we <code>import en from './en'</code>, we get a JSON that looks like:</p>
<pre><code class="lang-js">{  
 <span class="hljs-attr">common</span>: {  
  <span class="hljs-attr">ok</span>: <span class="hljs-string">"OK"</span>,  
  <span class="hljs-attr">cancel</span>: <span class="hljs-string">"Cancel"</span>
 },

 <span class="hljs-attr">homePage</span>: {  
  <span class="hljs-attr">welcome</span>: <span class="hljs-string">"Welcome to i18Next!"</span>  
 }  
}
</code></pre>
<p>In <code>i18n</code> lingo, <code>common</code> and <code>homePage</code> are called namespaces. And, we can refer to the contents of a namespace by referring to the key with the namespace as prefix with colon as separator. So, to access <code>ok</code> key from <code>common</code> namespace, we need to do the following, <code>t('common:ok')</code> and it will render the value of <code>ok</code> from the selected language’s common file.</p>
<p>What if we want another layer of grouping? What if we want to group translation of a dialogue box component within the home page. Like this:</p>
<pre><code class="lang-js">{  
 <span class="hljs-attr">homePage</span>: {  
  <span class="hljs-attr">welcome</span>: <span class="hljs-string">"Welcome to i18Next!"</span>,  
  <span class="hljs-attr">title</span>: <span class="hljs-string">"An unnecessarily lengthy tutorial"</span>,

  <span class="hljs-attr">dialogue</span>: {  
   <span class="hljs-attr">title</span>: <span class="hljs-string">"An unnecessary dialogue box"</span>,  
   <span class="hljs-attr">close</span>: <span class="hljs-string">"Go back to home page"</span>
  }  
 }  
}
</code></pre>
<p>We can use dot notation to traverse through the nested JSON object. Here, <code>t("homePage:title")</code> will return "An unnecessarily lengthy tutorial" and <code>t("homePage:dialogue.title")</code> will return "An unnecessary dialogue box".</p>
<p>Now that the file organization and nested translation is sorted out, we are mostly OK with it. But, wouldn’t it be good if we do not have to type namespace for the most commonly used namespace? It would be so nice, if we could, just do <code>t(“cancel”)</code> and we get the value from the <code>common</code> namespace. Well, we are in luck. We can specify default namespace in configuration and for all the cases where a namespace is not mentioned it will look into default namespace.</p>
<p>At this point, out i18next configuration looks like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="7f23c7f387dfa0abd04b8d28dc7c0f71"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/7f23c7f387dfa0abd04b8d28dc7c0f71" class="embed-card">https://gist.github.com/naishe/7f23c7f387dfa0abd04b8d28dc7c0f71</a></div><p>And, a code looks like this,</p>
<pre><code class="lang-jsx">&lt;Text&gt;homePage NS: {t(<span class="hljs-string">'homePage:welcome'</span>)}&lt;/Text&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Deafult NS: {t('ok')}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span></span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175844411/W-p-5uoE3L.png" alt="Yeah, I see the typo!" />
☝️ Yeah, I see the typo! 😅</p>
<hr />
<p><strong>Allow Users to Switch Language</strong></p>
<p>It will be a powerful display of your confidence if you choose a user’s preferred language for them and not let them choose the language they want your app to be. But, we do not live in that kind of world where installing an app is a long term commitment. So, we need to provide a way to let user change the language and it is as easy as calling <code>i18n.changeLanguage(“YOUR_LANGUAGE_CODE”)</code></p>
<p>When you call <code>changeLanguage</code> it changes language for the whole application at the same time, the <code>cacheUserLanguage</code> is also called with the language code of the newly selected language. If you remember from the last part, this is where our logic to store the language code also exists. So, as soon as we change language, the new change gets applied and the preference is stored. Neat!</p>
<p>A small React Native utility component that does the whole heavy lifting for you would look like this:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="8f61a4084c76d2c9f90a6b27f046bd01"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/8f61a4084c76d2c9f90a6b27f046bd01" class="embed-card">https://gist.github.com/naishe/8f61a4084c76d2c9f90a6b27f046bd01</a></div><p>And the outcome looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175854964/xuSNwKxNy.png" alt="The default View (language is picked from user preference)" /></p>
<p>☝️ The default View (language is picked from user preference)</p>
<p>As you click English, the whole app changes to English and it is stored to <code>AsyncStore</code> for future.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175857545/8Q-P-HdiH.png" alt="Everything is in English" /></p>
<p>☝️ Everything is in English</p>
<hr />
<p>Well, at this point, the majority of us are done, except for the annoying ones with their “what ifs”. A bunch of advanced features will be discussed in part 3. Hope to see you there.</p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1">Read Part 1</a></p>
<p><a target="_blank" href="https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-3">Read Part 3</a></p>
]]></content:encoded></item><item><title><![CDATA[Mastering Multiple Language Support in React Native: Part 1]]></title><description><![CDATA[This post was originally published on Medium by me on July 21, 2020

This article runs in three parts. If you are looking for code, please use part_1 branch of this repo: https://github.com/naishe/rn_multilanguage
The second part can be accessed at h...]]></description><link>https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1</link><guid isPermaLink="true">https://code.naishe.in/mastering-multiple-language-support-in-react-native-part-1</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[i18n]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nishant Neeraj]]></dc:creator><pubDate>Mon, 20 Jul 2020 18:55:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175914450/0nQd659Z3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>This post was originally <a target="_blank" href="https://medium.com/cybermonkey/multiple-language-support-in-react-native-part-1-fa6966b62332?source=friends_link&amp;sk=ae13f7ed429fc83208f033321129ea55">published on Medium</a> by me on July 21, 2020</p>
</blockquote>
<p>This article runs in three parts. If you are looking for code, please use <code>part_1</code> branch of this repo: <a target="_blank" href="https://github.com/naishe/rn_multilanguage">https://github.com/naishe/rn_multilanguage</a></p>
<p>The <strong>second part</strong> can be accessed at <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-2-d33262acc21d">https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-2-d33262acc21d</a></p>
<p><strong>Part 3</strong> is here: <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-3-d8091339c767">https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-3-d8091339c767</a></p>
<p>If you are serving a multilingual audience through your app, it becomes quickly obvious that supporting the languages of the demography will give you an edge over the singular language app. Working on <a target="_blank" href="https://thatmate.com">ThatMate</a>’s mobile app that serves sexual and mental health education to children and young adults in India, we came to realize that we will have to support multiple Indian languages along with English sooner than later. Luckily, for us, the React Native ecosystem makes it really simple. This article will explain how we did it.</p>
<p>Please be aware that fully localization is much more than just multiple language support, it includes currency conversion, date and time format, RTL (right to left) language and UI direction support, calendar type, timezone, whether they use metric system, and temperature unit among other things. This article is about supporting multiple languages, however, it can easily be extended to provide full localization support.</p>
<p><strong>NOTE:</strong> This tutorial assumes that your project is created using React Native CLI (as in <code>npx react-native init AwesomeProject</code> ). If you are using Expo, there is an excellent guide on Expo website <a target="_blank" href="https://docs.expo.io/versions/v38.0.0/sdk/localization/%29.">https://docs.expo.io/versions/v38.0.0/sdk/localization</a>). The Expo guide uses the <code>i18n-js</code> library for internationalization. If you want to use <code>i18next</code>, you will have to use <code>expo-localization</code> instead of <code>react-native-localization</code> as the dependency.</p>
<p>If you are trying this on a fresh project, create a new React Native project:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Initialize a new React Native project</span>
npx react-native init rn_multilanguage --template react-native-template-typescript
<span class="hljs-comment"># Go to the directory generated</span>
<span class="hljs-built_in">cd</span> rn_multilanguage
<span class="hljs-comment">#Install dependencies</span>
npm install --save i18next react-i18next react-native-localize @react-native-community/async-storage
</code></pre>
<p>Where the installed libraries are:</p>
<p><strong><code>react-native-localize</code>:</strong> The library that provides helper functions to figure out the device’s localized preferences like languages, timezone etc. Read more here: <a target="_blank" href="https://github.com/react-native-community/react-native-localize">https://github.com/react-native-community/react-native-localize</a></p>
<p><strong><code>i18next</code>:</strong> Our internationalization library. Read more about this here: <a target="_blank" href="https://www.i18next.com/">https://www.i18next.com/</a></p>
<p><strong><code>react-i18next</code>:</strong> This library provides nice binding with React and React Native projects using hooks, higher order components and also providers Trans — translator component. Read more about it here: <a target="_blank" href="https://react.i18next.com/">https://react.i18next.com/</a></p>
<p><strong><code>@react-native-community/async-storage</code>:</strong> Local storage library, we will use this library to persist the language preference over app restarts</p>
<hr />
<p>Now that we have our workspace set up, let’s start coding. In general, this is what we want:</p>
<ol>
<li>Initialize internationalization at the beginning of the app</li>
<li>Check user’s stored language preference, and show the content in that language</li>
<li>If this is the first time user launching our app or there is <em>no</em> language preference saved, find the best suited language for the user by providing all supported languages and looking into device’s language preference</li>
<li>If no matching language is found in step fallback to default language</li>
</ol>
<p><strong>Initialize i18next for React</strong></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a6d9ea9d23214875ac176b63387ab833"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/a6d9ea9d23214875ac176b63387ab833" class="embed-card">https://gist.github.com/naishe/a6d9ea9d23214875ac176b63387ab833</a></div><p>Let’s break this code down:</p>
<p>Line #46 to #68 is where the initialization is happening. We are passing a custom language detector to be used. Also, passing <code>initReactI18next</code> from <code>react-i18next</code> to enable React and i18next integration. Then we pass the initialization settings.</p>
<p>Initialization has three parts:</p>
<p><strong>Resources:</strong> A JSON object that contains the supported language codes and for each language code, we have a key and the equivalent translation in that language. In the code snippet, we have two language codes: <code>en</code> for English and <code>hi</code> for Hindi. We have added the same keys to both the languages and the associated values are the representation of that key in the respective languages. So, if you do <code>t("Welcome to React")</code> the users with Hindi preference would see “रियेक्ट तथा रियेक्ट-आई १८ एन में आपका स्वागत है” and the users with English as their preference would see “Welcome to React and react-i18next”.</p>
<p>You might be thinking that this way of setting translation will quickly become unwieldy. And, perhaps what if you want to organize the translations in a more nested way, or maybe how gender or pluralization could be handled? Worry not. Let’s get through the basics, then we will see how we can make our life much easier by keeping translations in separate files and how to use nesting.</p>
<p><strong>React:</strong> React specific settings go here. I have disabled suspense here. Otherwise, you will get a warning in the lines of</p>
<pre><code class="lang-text">Exception has occurred: Error  
Error: I18nextWithTranslation suspended while rendering, but no fallback UI was specified.  

Add a &lt;Suspense fallback=...&gt; component higher in the tree to provide a loading indicator or placeholder to display.
</code></pre>
<p>You will either have to wrap your components in <code>&lt;Suspense&gt;</code> tag like it is suggested here: <a target="_blank" href="https://react.i18next.com/latest/using-with-hooks#translate-your-content">https://react.i18next.com/latest/using-with-hooks#translate-your-content</a> or you can disable Suspense like I did.</p>
<p><strong>Interpolation:</strong> i18next provides you to use template strings and interpolate them at the render time. For example the following translation can be evaluated dynamically</p>
<pre><code class="lang-json">{  
 <span class="hljs-attr">"sayMyName"</span>: <span class="hljs-string">"My name is {{ name }}"</span>  
}
</code></pre>
<p>When you do the following:</p>
<pre><code class="lang-javascript">i18next.t(<span class="hljs-string">"sayMyName"</span>, <span class="hljs-string">"&lt;Nishant&gt;"</span>)  
<span class="hljs-comment">// returns: My name is &amp;lt;Nishant&amp;gt;</span>
</code></pre>
<p>This is to avoid XSS attacks. Read more here: <a target="_blank" href="https://www.i18next.com/translation-function/interpolation#unescape">https://www.i18next.com/translation-function/interpolation#unescape</a></p>
<p>However, in React Native we do not have this issue. We want strings to be rendered as is. So, we want the last interpolation to return us “My name is ” and for that, we want to turn off the <code>escapeValue</code>.</p>
<hr />
<p>Now that we know how to initialize i18next. Let’s check out how to detect the user language. And, that is the fun part.</p>
<p>I18next follows a middleware plugin sort of architecture. If you have used Express.js, you might be familiar with the concept. In essence, the library exposes a specification, inputs to the plugin and expected outputs from the plugin. In our case, our plugin code spans from line #10 to line #44.</p>
<p>We specify that it is a <code>languageDetector</code> plugin (#11), the detection happens asynchronously (#14). At #15, it provides you with various objects that might be required for your plugin to initialize. I left it empty because I did not need it, but I mentioned it here in case you wanted to do something. Then comes the meat of the code, the <code>detect</code> function. Detect function is provided with a callback function that takes the detected language. Note that the callback function is passed when we mention <code>async</code> is true. If your detection is synchronous, then you can set <code>async:false</code> and return the language code from the detect function. Let’s dig into this detect function, shall we?</p>
<p>At line #23, we look into the AsyncStorage for previously stored language code. I know that I have stored it with the key <code>APP_LANG</code> (I will tell you later where I store the user preference, for now assume that language code is stored in APP_LANG and APP_LANG is a name I provided. You can use your own key if you did not like it). If fetching the language code errors out or returns a falsy value, we will try guessing the user’s preference.</p>
<p><strong>Guessing User’s Language Preference:</strong> It only makes sense to suggest a language if we somehow get a list of user’s acceptable languages prioritized by users preference for those languages and compare that list with the languages that we provide for the app and then find which language fits the best. Luckily for us, React-Native-Localize has a tool that does exactly that.</p>
<p>At line #33,</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> bestLng = RNLocalize.findBestAvailableLanguage(\[<span class="hljs-string">'en'</span>, <span class="hljs-string">'hi'</span>\]);
</code></pre>
<p>We ask react-native-localize to find the best suitable language among the provided language codes by comparing it with the user’s system language preference. In the case shown in the screenshot below, the function will return “hi”, Hindi, as the best language.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175910262/uCcMw7htB.png" alt="User’s Language Preference: Tamil first, Hindi second, and English third
" /></p>
<p>☝️ User’s Language Preference: Tamil first, Hindi second, and English third</p>
<p>In case there is no common language between the list of languages you support and the list of languages in the user’s language preference, <code>RNLocalize.findBestAvailableLanguage</code> returns <code>undefined</code> and that is our clue to return the fallback language. In our case, we are returning <code>en</code>, English, as our fallback language at line #35.</p>
<p>Lastly, if it is a sunny day, the stored language will be fetched from local store and we will return this as I did at line #38.</p>
<p><strong>Storing User Language Preference:</strong> My favorite part of the plugin is the <code>cacheUserLanguage</code> method, because it centralizes the logic to store user selection. This method is called every time after <code>detect</code> is called and, as we will see later in this post series, when the user opts to change the language.</p>
<p><strong>Step 2: Integration with the App</strong></p>
<p>At this point, we have configured i18next to work with React to guess the best language and to store the selected language permanently. What’s the wait? Let’s integrate with our React Native code. It’s as simple as importing this file in the application main entry file, which is, for most of us mortals, either <code>App.tsx</code> or <code>App.jsx</code>.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="5ef6f3feccb5ebd33ef411f98ef152a5"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/naishe/5ef6f3feccb5ebd33ef411f98ef152a5" class="embed-card">https://gist.github.com/naishe/5ef6f3feccb5ebd33ef411f98ef152a5</a></div><p>At this point our app looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643175912834/Lft6c4ddq.png" alt="Automatically selected Hindi and served the content" /></p>
<p>☝️ Automatically selected Hindi and served the content</p>
<p><strong>Are we done yet?</strong> Well, almost. We haven’t given a way to change the language. Neither have we looked at how to split translations in different files. How to interpolate, and how to return an object or array of strings. We do not know how to handle gender specific sentences or plural and singular sentences. Let’s take a break. And continue this in the next article.</p>
<p><strong>Part 2:</strong> <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-2-d33262acc21d">https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-2-d33262acc21d</a><br /><strong>Part 3:</strong> <a target="_blank" href="https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-3-d8091339c767">https://medium.com/cybermonkey/mastering-multiple-language-support-in-react-native-part-3-d8091339c767</a></p>
<p>#React #ReactNative #Internationalization #i18n</p>
]]></content:encoded></item></channel></rss>