Skip to content

Latest commit

 

History

History
270 lines (210 loc) · 8.81 KB

File metadata and controls

270 lines (210 loc) · 8.81 KB

LIPAS Internationalization (i18n) Guide

This document describes how translations work in LIPAS to help LLM code assistants quickly understand and work with the translation system.

Overview

LIPAS uses the Tongue library for internationalization, supporting three languages:

  • Finnish (:fi) - Primary language/fallback
  • Swedish (:se)
  • English (:en)

Translation Sources in LIPAS

LIPAS translations come from two main sources:

  1. Translation Files - Static translations for UI elements, labels, and messages
  2. Data-Driven Translations - Dynamic translations generated from data files

Translation Files

Directory Structure

webapp/src/cljc/lipas/i18n/
├── core.cljc          # Main i18n logic and translation function
├── utils.cljc         # Macro for loading translations at compile time
├── fi.cljc           # Finnish translations loader
├── se.cljc           # Swedish translations loader
├── en.cljc           # English translations loader
├── fi/               # Finnish translation files
│   ├── actions.edn
│   ├── general.edn
│   ├── route.edn     # Route-specific translations
│   └── ...
├── se/               # Swedish translation files
│   └── ...
└── en/               # English translation files
    └── ...

🔑 KEY CONCEPT: Translation Key Namespace → File Mapping

The translation system maps translation key namespaces directly to file names based on the top-level-keys list in utils.cljc.

How It Works:

  1. Translation key: :route/editor-title
  2. Namespace extracted: :route (part before the /)
  3. File lookup: route.edn (namespace name + .edn)
  4. Loading condition: :route must be in top-level-keys list

Examples:

;; Translation key → File location
:actions/save         → fi/actions.edn, se/actions.edn, en/actions.edn
:route/editor-title   → fi/route.edn, se/route.edn, en/route.edn
:map/zoom-in         → fi/map.edn, se/map.edn, en/map.edn
:lipas.user/name     → fi/lipas_user.edn, se/lipas_user.edn, en/lipas_user.edn

File Organization

  • Each language has its own directory: fi/, se/, en/
  • Translation files are EDN files organized by feature/domain
  • File naming convention: namespace_name.edn (dots in namespace names are replaced with underscores)
  • File names must match entries in top-level-keys list

File Format

;; Example: webapp/src/cljc/lipas/i18n/fi/actions.edn
{:add "Lisää"
 :back "Takaisin"
 :cancel "Peruuta"
 :save "Tallenna"
 :delete "Poista"}

Important: Keys in the file should NOT include the namespace prefix. The system automatically adds it when loading.

Top-Level Translation Categories

The system organizes translations into these top-level keys (defined in utils.cljc):

(def top-level-keys
  [:accessibility
   :actions        ; → actions.edn files
   :admin
   :general        ; → general.edn files
   :route          ; → route.edn files
   :map            ; → map.edn files
   ;; ... many more
   ])

Data-Driven Translations

Some translations in LIPAS are automatically generated from data files rather than stored in translation files. These provide dynamic translations for domain-specific content.

Sources of Data-Driven Translations:

  • Sports site types from lipas.data.types
  • Cities from lipas.data.cities
  • Materials from lipas.data.materials
  • Owners/administrators from respective data files

How Data-Driven Translations Work:

Data files contain multilingual information that gets transformed into translations at runtime:

;; Example from lipas.data.types
{:type-code 1234
 :name {:fi "Uimahalli"
        :se "Simhall"
        :en "Swimming Hall"}
 :description {:fi "Sisäuimala..."
               :se "Inomhusbassäng..."
               :en "Indoor swimming..."}}

Localization Functions

The core.cljc file provides special localization functions for working with data-driven content:

  • localize - Localizes sports site data based on locale (mutating)
  • localize2 - Non-mutating version that adds -localized fields
;; Usage example
(localize sports-site-data :fi)  ; Returns localized version
(localize2 sports-site-data :fi) ; Returns original + localized fields

Using Translations in Code

In ClojureScript Components

  1. Subscribe to translator function:
(ns lipas.ui.example.views
  (:require [lipas.ui.utils :refer [<==]]))

(defn my-component []
  (let [tr (<== [:lipas.ui.subs/translator])]
    [:div
     [:h1 (tr :general/welcome)]      ; Loads from general.edn
     [:button {:on-click #(...)}
      (tr :actions/save)]]))          ; Loads from actions.edn
  1. Translation Key Format:
  • Use namespaced keywords: :namespace/key
  • Namespace must be in top-level-keys list
  • Examples:
    • :actions/saveactions.edn files
    • :route/editor-titleroute.edn files
    • :map.tools/drawmap_tools.edn files (dots → underscores)

With Parameters

;; In translation file:
{:welcome "Welcome {1}!"}

;; In code:
(tr :general/welcome user-name)

Using Data-Driven Translations

For data-driven content, use the localization functions:

;; Access localized sports site type names
(get-in (localize sports-site :fi) [:type :name])

;; Or use the non-mutating version
(get-in (localize2 sports-site :fi) [:type :name-localized])

Adding New Translations

Adding New Translation Files

Step-by-step process:

  1. Add to top-level-keys:

    ;; In utils.cljc
    (def top-level-keys
      [;; existing keys
       :your-new-namespace])
  2. Create translation files:

    ;; fi/your_new_namespace.edn
    {:key1 "Finnish translation"
     :key2 "Another Finnish translation"}
    
    ;; se/your_new_namespace.edn
    {:key1 "Swedish translation"
     :key2 "Another Swedish translation"}
    
    ;; en/your_new_namespace.edn
    {:key1 "English translation"
     :key2 "Another English translation"}
  3. Use in code:

    (tr :your-new-namespace/key1)
    (tr :your-new-namespace/key2)
  4. Recompilation: Shadow-cljs will automatically recompile when it detects the file changes

Adding Data-Driven Translations

For domain-specific content that needs to be managed as data:

  1. Add to appropriate data namespace (e.g., lipas.data.types)
  2. Use multilingual data structure:
    {:name {:fi "Finnish name"
            :se "Swedish name"
            :en "English name"}
     :description {:fi "Finnish description"
                   :se "Swedish description"
                   :en "English description"}}
  3. Use localization functions to access translations in components

Troubleshooting

Translation Keys Showing as Literal Text

Problem: You see {MISSING KEY :ROUTE/EDITOR-TITLE} in the UI

Causes:

  1. Missing namespace in top-level-keys: The namespace :route is not in the top-level-keys list
  2. Missing translation files: No route.edn files exist in language directories
  3. Wrong file location: Translation is in wrong file based on namespace

Solutions:

  1. Add the namespace to top-level-keys in utils.cljc
  2. Create the missing translation files
  3. Move translations to the correct file based on their namespace

Important Notes

  1. Compile-time loading: Translations are loaded at compile time by the deftranslations macro
  2. Recompilation needed: Changes to translation files or top-level-keys require recompilation (shadow-cljs handles this automatically when files change)
  3. Fallback language: Finnish (:fi) is the fallback when translations are missing
  4. File naming: Use underscores for dots in namespace names (:map.toolsmap_tools.edn)
  5. Key organization: Group related translations by logical namespace, not by UI location
  6. Dynamic loading: Data-driven translations are loaded at runtime from data files

Quick Reference for LLMs

To add new file-based translations:

  1. Determine the correct namespace based on functionality
  2. Check if namespace exists in top-level-keys (add if missing)
  3. Add/modify translations in appropriate .edn files for all languages
  4. Use (tr :namespace/key) in ClojureScript code

To debug missing translations:

  1. Check if namespace is in top-level-keys
  2. Verify translation files exist for all languages
  3. Confirm translations are in correct files based on namespace
  4. Allow recompilation if files or top-level-keys changed

For data-driven content:

  1. Add multilingual data to appropriate data namespace
  2. Use localize or localize2 functions to access localized versions
  3. Consider whether content belongs in translation files or data files based on its nature