<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Loris Sigrist</title>
    <description>The latest articles on DEV Community by Loris Sigrist (@lorissigrist).</description>
    <link>https://dev.to/lorissigrist</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1258258%2Fbf59fa98-dbf1-43f2-a8f3-8c4bc50bc6c5.png</url>
      <title>DEV Community: Loris Sigrist</title>
      <link>https://dev.to/lorissigrist</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lorissigrist"/>
    <language>en</language>
    <item>
      <title>Building an i18n library for the modern Web</title>
      <dc:creator>Loris Sigrist</dc:creator>
      <pubDate>Thu, 01 Feb 2024 14:05:08 +0000</pubDate>
      <link>https://dev.to/lorissigrist/building-an-i18n-library-for-the-modern-web-521m</link>
      <guid>https://dev.to/lorissigrist/building-an-i18n-library-for-the-modern-web-521m</guid>
      <description>&lt;p&gt;Over the last few years we've seen the emergence of "partial hydration" patterns across many frameworks. The idea is that most rendering happens on the server, with only the interactive parts of a page actually shipping code to the client. The client and server &lt;em&gt;cooperate&lt;/em&gt; to show a user a site. This idea has come in many iterations, be it React Server Components, Astro Islands, or even HTMX. &lt;/p&gt;

&lt;p&gt;This has some interesting implications for i18n libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Since the server-rendered and client-rendered parts always share the same language, language state is &lt;em&gt;global&lt;/em&gt;. The only way to switch languages is to rerender everything, including the server-rendered parts, which  can only be done by fully reloading the page. Thus, any form of message reactivity or language lazy-loading is useless.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Most Translations are rendered on the server &amp;amp; don't depend on client side state&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;On the server, any i18n library really serves as a templating helper, so they should excel at doing that!&lt;/li&gt;
&lt;li&gt;Since only a minority of messages will include client side state, the bundle shipped by an i18n library should only include those messages and the code they require.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Status Quo
&lt;/h2&gt;

&lt;p&gt;Most i18n libraries are still conceptualised as monoliths that do all the work in the same place. Language Detection, Message Fallbacks, Lazy Loading &amp;amp; so many more features. However, doing all the work in one place usually means doing it &lt;strong&gt;twice&lt;/strong&gt;! Once on the server and again on the client. This has resulted in some truly impressive bundle sizes. &lt;code&gt;i18next&lt;/code&gt;, one of the most popular i18n libraries needs over 40kB to render a single message. This is after bundling. &lt;/p&gt;

&lt;p&gt;Clearly there is a lot of room for improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  A modern i18n library
&lt;/h2&gt;

&lt;p&gt;What would an i18n library look like that embraces the cooperation between Server and Client, that's built for partial hydration? &lt;/p&gt;

&lt;p&gt;That's exactly what &lt;a href="https://www.inlang.com"&gt;we&lt;/a&gt; tried to accomplish with &lt;a href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs"&gt;ParaglideJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paraglide is a compiler that &lt;em&gt;compiles&lt;/em&gt; your messages into JS modules. Each message is it's own export.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Paraglide's output&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @param {{ name: string }} params
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`Hello &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;my_other_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`My Other Message`&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This takes advantage of modern tooling. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TypeScript. Messages are fully type-safe, including any parameters they take. This makes Paraglide a joy to use for templating.&lt;/li&gt;
&lt;li&gt;Modern Build tools remove JS code that isn't used automatically. Because messages are individual JS exports, they can individually be removed if they aren't used. This automatically only ships messages that are needed on the client. This results in some &lt;em&gt;tiny&lt;/em&gt; bundle-sizes, starting as low as 100 bytes. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can further take advantage of the cooperation between server and client to skip language detection on the client entirely. Because the server already decided which language to render, the client bundle can just read which language was used from the HTML.&lt;/p&gt;

&lt;p&gt;Because &lt;a href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs"&gt;ParaglideJS&lt;/a&gt;is a compiler, fallback messages can be resolved at build time, so no runtime code is needed for that.&lt;/p&gt;

&lt;p&gt;So far, this approach is working very well in any partial-hydration setting. However, even in frameworks without partial hydration &lt;a href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs"&gt;ParaglideJS&lt;/a&gt; can still be useful. It still only ships messages that are used on a given &lt;em&gt;page&lt;/em&gt; without you needing to manually split messages into namespaces as you usually would. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Going forward, scaling &lt;em&gt;down&lt;/em&gt; and integration with modern tooling is going to be increasingly important for i18n libraries. &lt;a href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs"&gt;ParaglideJS&lt;/a&gt; is one attempt at this which can be used &lt;em&gt;today&lt;/em&gt;. Clearly there is a lot of room for innovation in this space &amp;amp; we're interested in how it will develop over the next few months and years. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>i18n</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
