Alternative solution for small React apps that need i18n
In most projects I've worked on, the default pick for internationalization has always been react-i18next. It works, and it covers a lot of ground. But when I kicked off a tiny personal project called Veedgee, I started wondering: do I really need all of that?
It was a simple app. Not much copy, only two languages, and still I'd have to pull in a pretty big library, wire up a bunch of config, and deal with that annoying test setup. So, given how dead simple the site was going to be, I decided to try something different this time: code my own translation layer. It ended up tiny, and it worked great!
After Veedgee, I brought the same strategy to my personal site and it worked again. So the next step was obvious: turn it into a library. That's how Polang was born.
What's annoying about react-i18next (for small apps)
This isn't about the library being bad. It's solid. The issue is using a cannon to swat a fly. Here's what bugs me the most:
1. Rough first impression
You look at it and it's not obvious how to wire everything up right away. There are murky bits, and the whole setup experience ends up feeling kinda rough.
2. Tests are a pain
You have to repeat the library setup in tests, so getting them running usually means async work, mocks, and more boilerplate than you'd want, far away from a plug-and-play experience.
3. Persisting the locale
If you want to remember the user's language, or use a search param to load the site with a preset locale, you either wire it yourself or add a plugin. None of that ships built-in by default.
4. Translations live far from the component
If you want translations sitting next to the component, like you probably already do with styles and tests, you're in for extra work because the library isn't really designed around that structure.
What Polang is going for
The idea with Polang is to make those pain points go away with as little friction as possible. All you need is:
Import the i18n Provider and set the supported locales
import { I18nProvider } from '@compilorama/polang';
const locales = [
{ code: 'en-US', name: 'English US' },
{ code: 'pt-BR', name: 'Português BR' },
];
export default function App() {
return (
<I18nProvider locales={locales}>
<YourApp />
</I18nProvider>
);
}
Connect LocaleSelect to your layout
import { useTranslation, LocaleSelect } from '@compilorama/polang';
import translations from './header.t.js';
export default function Header() {
const { t } = useTranslation(translations);
return (
<header>
<LocaleSelect aria-label={t('locale')} />
</header>
);
}
That's it. Write your strings, hook them up with useTranslation. No magic, no cryptic config.
Testing components that use translations
Same simple story. To test any component that relies on translations:
- Wrap it with the i18n Provider
- Pass at least one locale into the provider
Done. Your tests run like a gentle breeeze.
Component-oriented translations
Polang fits nicely if you like a component-first setup. You can keep the component, styles, tests, and translations all in the same place.
When to use it (and when not to)
Reach for Polang if:
- your app is small
- you need just a few languages
- you want ultra-fast setup
- you want to avoid unjustified complexity
Skip it if:
- you need advanced i18n features
- you're juggling complex multi-namespace setups
- you rely on heavy integrations
Look, not every project needs a giant solution. Sometimes less is more. Polang exists to nail the basics without getting in your way.
If that sounds useful, visit the library repo for more details. For a real-world example, check the Polang website repo.