Android Language App · Case Study
LinguiSpark app icon

LINGUISPARK

An Android app that takes an English speaker from their very first Polish word to confident intermediate. Thirty-two units across four CEFR levels, pronunciation for every word, lessons that work with no connection and sync when they get one, and a second interface language built over the whole thing. Designed, built, localised and released single-handed.

Get It On Google Play
Role
Solo developer
Studio
NeonArc Studio
Platform
Android
Stack
React Native · TypeScript
Backend
Firebase
Status
Live on Google Play
The Problem

EVERY APP ASSUMES YOU ALREADY STARTED

I moved to Poland speaking no Polish. The apps I tried all made the same assumption: that I could already read the alphabet, that I knew what a case was, that ł and ż and ę were sounds I had heard before. They start at lesson one of a course that quietly began somewhere else.

LinguiSpark starts at zero. The first thing it teaches is how the letters sound, and every single word on every card can be tapped to hear it spoken. From there it runs a structured A1 → A2 → B1 → B2 path, thirty-two units, each one a short lesson followed immediately by practice on what was just taught.

It is a real product with real users on the Play Store, not a portfolio exercise. That constraint is what makes it interesting to build: it has to install, launch, work offline, survive a phone being reset, and keep someone coming back on day thirty.

The App
Under The Hood

FOUR DECISIONS THAT SHAPED IT

01

The curriculum is code, not a CMS

All thirty-two units are TypeScript data files conforming to a shared Unit type, one file per unit. There is no backend serving content and no admin panel.

That means the entire course is type-checked at build time, reviewable in a diff, and testable. A malformed exercise is a compile error rather than something a learner discovers in Warsaw on a train.

02

Offline first, cloud second

Progress is written locally and reconciled into Firestore field by field, rather than the last device to sync overwriting the record wholesale.

Lessons are bundled into the app, so the whole course works on a plane or an underground platform. Sign-in is optional and adds cross-device continuity on top, instead of being the price of entry.

03

Pronunciation with no audio files

Every Polish word is spoken by the device's own text-to-speech engine, slowed slightly for learners. There is not a single recorded audio asset in the bundle.

The trade is a small loss of control over voice quality. What it buys is that every word ever added to the course arrives with audio automatically, at zero download size. That is the difference between a handful of spoken words and all of them.

04

A second language, retrofitted without forking

The app shipped English-only. Adding Ukrainian without duplicating thirty-two units meant building a gloss layer: translations keyed to each item's own stable id, resolved at render time, sitting over untouched Polish content.

The interface followed via a dependency-free translation catalogue, now several hundred keys held at parity between the two languages by an automated test. The Polish under examination is never substituted, because in a grammar exercise the Polish options are the question.

Shipping It

THE PART THAT ISN'T WRITING CODE

Getting an app finished and getting an app published turned out to be two different projects. Google requires a new personal developer account to run a closed test with a minimum number of testers for a continuous period before production access is even unlocked, which puts a hard floor under the timeline no amount of engineering removes.

Build

Cloud Pipeline

Expo Application Services builds the production Android bundle, so releases do not depend on the state of one local machine.

Release

22 Versioned Builds

Every build numbered and tracked, from the first closed test through to the current production version.

Test

632 Automated Tests

Type-checking and a full Jest suite gate every change, including tests that guard the translation catalogues against drift.

Verify

Real Devices

Some faults only exist in a release build on real hardware. Those get diagnosed over adb on a physical phone, not in a simulator.

The most instructive bug of the project was invisible to every local check. Type-checking passed, the tests passed, the bundle exported cleanly, and the installed app died at the splash screen every time. A transitive dependency had quietly resolved to a version built against a newer SDK, which JavaScript tolerated and the native linker did not. It was only findable by reading the crash log off a real device.

Green tests are not the same thing as a working app. That lesson cost a release cycle and was worth it.

What's Next

The architecture separates a course from the app that renders it, so a new language is content plus a gloss layer rather than a fork. Polish for English speakers is the version on the store today; Polish for Ukrainian speakers is built on that same seam and in testing. More language pairs, and an iOS release, follow the same path.

Back to Portfolio