DEV Community

Bartłomiej Stefański
Bartłomiej Stefański

Posted on • Originally published at bstefanski.com on

Simple, pragmatic and performant i18n solution for JavaScript applications

There're great libraries for i18n that support absolutely everything like interpolation with React components, server-side rendering, and code splitting (or maybe I should call it - JSON splitting). This is one example of such library - react-i18next.

The biggest drawback and issue, something that keeps me from using it is its bundle size. Assuming you're using gzip for compression like everyone else, it will take 20kB+ from your bundle. For some people/teams it's an acceptable amount, but for me, I don't believe it to be a good trade-off.

i18next package size summaryi18next package size summary

react-i18next package size summaryreact-i18next package size summary

And that's why I decided to write my implementation that has great DX (supports dotted paths with autocompletion) and is easy to scale/maintain. I used the js-cookie library to get and parse the cookie with legible & declarative API.

The example is done specifically for Next.js, but you can seamlessly port it to any other library/framework.


// i18n.ts 

import Cookies from "js-cookie";

import get from "lodash/get";

import { en } from "./en";

type Locales = "en";

const defaultTranslations: Record<Locales, Partial<typeof en>> = {

 en,

};

export const t = (key: Join<PathsToStringProps<typeof en>, ".">, translations = defaultTranslations) => {

const locale = Cookies.get("NEXT\_LOCALE") as Locales;

return get(translations[locale] || translations["en"], key);

};

type PathsToStringProps<T> = T extends string

? []

: {

[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>];

}[Extract<keyof T, string>];

type Join<T extends string[], D extends string> = T extends []

? never

: T extends [infer F]

? F

: T extends [infer F, ...infer R]

? F extends string

? `${F}${D}${Join<Extract<R, string[]>, D>}`

: never

: string;

Enter fullscreen mode Exit fullscreen mode

This is how the translations file looks like


// en.ts 

export const en = {

 ctaSection: {

 title: "Some value for demo purposes",

// ...The rest of the items, removed for brevity 

}

}

Enter fullscreen mode Exit fullscreen mode

And this is how you use it:

An example of how you could use the "t" functionAn example of how you could use the "t" function

You don't have to worry about the performance unless the file exceeds a few hundred lines. After that, you can use dynamic imports and split the translations file into smaller chunks.

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay