Zum Hauptinhalt springen

Localization & Build Pipeline

Borrowd is built to serve diverse local communities. From day one, I designed a 6-language translation pipeline covering English (en), German (de), Turkish (tr), Serbian (sr), Slovenian (sl), and Romanian (ro).

The challenge with robust i18n (internationalization) is maintaining translation files and ensuring the browser loads them efficiently. Instead of hand-maintaining fragile JSON files or making runtime API calls for string translations, I created an automated build-time translation generation pipeline.

The Translation Source​

I define core translation strings once in backend .properties files (e.g., messages.properties for English, messages_de.properties for German). This provides a single source of truth for both the backend error messages and the frontend UI labels.

# messages_en.properties
borrow.status.accepted=Borrow Request Accepted
button.confirm=Confirm

# messages_de.properties
borrow.status.accepted=Leihanfrage Akzeptiert
button.confirm=Bestätigen

Build-Time Generation & Hashing​

During the Gradle build, a custom generation task parses these property files into static, optimized JSON bundles (translations_<locale>.json) under src/jsMain/resources/.

To ensure fast load times and prevent stale browser cache issues when deploying updates, I built a custom Webpack & Gradle build configuration (webpack.config.d/).

Cache-Busting​

Webpack detects the generated translation JSONs using asset resource rules, outputting files with a 6-character content hash (e.g., translations_de.4a2b1c.json). Browsers can cache these assets infinitely because any translation change generates a brand-new hashed URL. The HtmlWebpackPlugin dynamically injects the correct hashed bundles into the index.html template.

Local Dev Proxying

During local development, my custom proxy.js config automatically proxies /api calls from the Kotlin/JS dev server (port 8081) to the Ktor backend (port 8080). This eliminates CORS setup headaches during development.

The Runtime Engine​

Once the browser loads the hashed assets, the Kotlin/JS frontend fetches the appropriate localized translation JSON on initial app load.

I built a custom L18n runtime engine in Kotlin/JS that provides:

  1. Type-safe string lookups for UI components.
  2. Dynamic parameter replacement (e.g., Hello {name}).
  3. Automatic fallback to English if a localized string key is missing, ensuring the UI never breaks or shows raw keys to the user.

All UI labels, button states, state machine notices, and email templates run through this pipeline, ensuring a consistent multi-lingual experience across the entire Borrowd platform.