Guide β Making an App Multilingual
1. Install the package
1 npm install @xpulse/i18n --registry=https://npm.xpulse.one
Minimal configuration (DE as default, DE + EN supported):
1 { 2 "i18n" : { 3 "supported" : [ "de" , "en" ] , 4 "default" : "de" 5 } 6 }
Full options:
1 { 2 "i18n" : { 3 "supported" : [ "de" , "en" ] , 4 "default" : "de" , 5 "use-seo" : true , 6 "exclude" : [ "/_theme/*" , "/_logger/*" , "/_debug/*" , "/api/*" , "/health" ] , 7 "paths" : { 8 "translations" : "src/translations/" 9 } 10 } 11 }
3. Service loader
@xpulse/i18n is loaded automatically via the service loader once the package is installed.
The package's own xpulse.json already declares the service entry:
1 { 2 "service" : { 3 "xpulse-i18n" : { "load" : true , "depends" : [ "xpulse-router" ] } 4 } 5 }
No manual init() call needed.
4. Create translation files
Structure under src/translations/:
1 src /translations/2 de/ 3 platform.index .json 4 platform.footer .json 5 en/ 6 platform.index .json 7 platform.footer .json
Format: flat key-value, variables as {key}:
1 { 2 "tagline" : "Private Tools. No Server. No Compromises." , 3 "cta" : "Get started" , 4 "status.days" : "{n} day{s} remaining" 5 }
5. Add the lang switcher to your template
On the <html> tag:
1 <html lang ="{% htmlLang(_route) %}" >
Lang switcher anywhere in the layout:
1 {% langSwitcher(_route.path) %}
Output:
1 <div class ="xpulse-lang-switcher" > 2 <a href ="/tool/chat/" 3 onclick ="localStorage.setItem('xpulse_lang','de')" 4 class ="xpulse-lang-switcher__option" >DE</a > 5 <a href ="/en/tool/chat/" 6 onclick ="localStorage.setItem('xpulse_lang','en')" 7 class ="xpulse-lang-switcher__option xpulse-lang-switcher__option--active" 8 aria-current ="true" >EN</a > 9 </div >
6. Style the switcher
The classes xpulse-lang-switcher, xpulse-lang-switcher__option and
xpulse-lang-switcher__option--active can be styled freely, e.g.:
1 .xpulse-lang-switcher {2 display : flex; 3 gap : 0.5rem ; 4 } 5 .xpulse-lang-switcher__option {6 opacity : 0.5 ; 7 text-decoration : none; 8 } 9 .xpulse-lang-switcher__option--active {10 opacity : 1 ; 11 font-weight : bold; 12 }
7. Read translated strings in a handler
1 import i18n from '@xpulse/i18n' ;2 3 4 const lang = i18n.lang (req);5 const tagline = i18n.t ('tagline' , lang);6 const status = i18n.t ('status.days' , lang, { n : 3 , s : 's' });
What happens automatically
All registered routes get a /:lang/ variant (e.g. /en/tool/chat/)
req.lang is set on every request
An inline script in <head> reads localStorage and redirects visitors to the correct language version (no FOUC)
With use-seo: true, a <link rel="canonical"> and <link rel="alternate" hreflang> tags are injected into <head> automatically β set app.url in xpulse.json to get absolute URLs (required by Google for hreflang)