Optimizing Build Size for React i18n Translations, Part 1
🌍 Optimizing React i18n in a real project
While developing the Autowini service, I had adopted react-i18next for multilingual (i18n) support.
At first I built it by importing the translation JSON files, but as the project grew, I ran into larger chunk files and slower initial loading.

So I changed how languages are handled altogether and applied i18n bundle optimization. Here is how I did it.
📌 The old way: bundling languages with import
At first, I imported the translation JSON files and put them directly into resources, like this.
import ko from './locales/ko/translation.json';
import en from './locales/en/translation.json';
i18n.init({
resources: {
ko: { translation: ko },
en: { translation: en },
},
lng: 'ko',
fallbackLng: 'en',
});The advantages of this approach:
- Simple structure, quick to set up
- All translations are bundled with the app, so loading is fast
But the drawbacks were clear too:
- As the number of languages grows and the JSON files get bigger, the JS bundle size grows sharply
- In the end, this leads to longer build times and slower initial loading
🔧 The improvement: static files + HttpBackend loading
So I changed the structure as follows:
- Move the translation JSON files out to /public/locales/{{lng}}/translation.json
- Use i18next-http-backend so only the current language is loaded over the network at runtime
- Remove unneeded translation files from the JS chunks → smaller build size
import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
i18n
.use(HttpBackend)
.use(initReactI18next)
.init({
backend: {
loadPath: '/locales/{{lng}}/translation.json',
},
lng: userLanguage,
fallbackLng: 'en',
interpolation: { escapeValue: false },
});🧠 What exactly is HttpBackend?
i18next-http-backend is an i18n backend plugin that lets you load translation resources asynchronously through HTTP requests.
In other words, instead of importing the translation JSON files and including them when the app first loads, the browser fetches only the files needed for that language over the network.
Example: a request to /locales/ar/translation.json → the translations come back as a JSON response
📉 Why does this help optimize the build?
The bundler I use is Vite, which uses Rollup internally.
Rollup combines every imported file into a single bundle (JS chunk), so if you import JSON, all of it ends up inside the JS.
But resources in the /public folder are only copied as static files and are not included in the bundle, which gave the following results.
- Smaller JS chunks
- Shorter build times
- Faster initial loading
Build size results & thoughts


With the old structure, where every translation file was imported into the bundle, I often saw warning messages whenever I ran vite build. I'm glad this optimization took at least a little of that burden off.
As the project grows and the translation data increases, I'm sure this approach will prove even more effective.
- #Autowini
- #i18n
- #React
- #i18n build size optimization