Internationalization (Translations)
Translations are used to internationalize an application and display content in different
languages. In Lila, translations are stored in the app/locales
directory
and
can be dynamically loaded into the application.
️ Localized Routing & Language Detection
Instead of manually creating duplicate routes (like /, /es, and /en) and forcing the language inside each renderer, Lila provides the highly elegant @locales decorator from core.routing. This registers all localized path prefixes for the handler automatically and dynamically sets the active language for the request.
from core.routing import Router, locales
from core.templates import render
router = Router()
# English: Example rendering HTML file with Jinja2, using the @locales decorator to automatically register localized prefixes.
# Español: Ejemplo de renderizar un archivo HTML con Jinja2, usando el decorador @locales para registrar prefijos localizados de forma automática.
@router.get("/")
@locales(["es", "en"])
async def home(request: Request):
context = {
"url": f"http://{HOST}:{PORT}"
}
response = render(
request=request, template="index", context=context
)
return response
Under the hood, decorating your route with @locales(["es", "en"]) will automatically register /, /es, and /en to the same handler. When the user requests a localized URL, Lila dynamically sets the appropriate active language inside the request state, which is then seamlessly picked up by Translate.lang() and the template render() function.
Forcing Language Manually (Fallback)
If you still need to force a specific language manually without using localized route prefixes, the render method also accepts an optional lang_default parameter:
@router.get("/custom-es")
async def custom_home(request: Request):
response = render(
request=request,
template="index",
lang_default="es" # Manually forces Spanish translations
)
return response
Translation Files
To load a locale file, use the Translate
class from
lila.core.translate.
You can access translations using:
Translate.get_translations- returns a dictionary with all translationsTranslate.t- returns a specific translation (returns original text if not found)
Example translation file (app/locales/translations.json):
{
"Send": {
"es": "Enviar",
"en": "Send"
},
"Cancel": {
"es": "Cancelar",
"en": "Cancel"
},
"Accept": {
"es": "Aceptar",
"en": "Accept"
},
"Email": {
"es": "Email",
"en": "Email"
},
"Name": {
"es": "Nombre",
"en": "Name"
},
"Back": {
"es": "Volver",
"en": "Back"
},
"Hi": {
"es": "Hola",
"en": "Hi"
}
}
Using Translations
Get a specific translation:
from lila.core.translate import Translate
msg_error_login = Translate.t(
key="Incorrect email or password",
request=request,
file_name="guest"
)
Helpers and Performance
Lila includes helpers to manage the application state and ensure high performance through caching.
Setting the Language
You can change the user's language dynamically. This stores the preference in an encrypted session cookie.
Option A: Dynamic POST Route (e.g. for fetch/AJAX)
@router.post("/set-language")
async def change_lang(request: Request):
data = await request.json()
new_lang = data.get("lang", "en")
response = JSONResponse({"success": True})
await Translate.set_lang(request, response, new_lang)
return response
Option B: Automatic Query Parameter Switching (Zero Code/Routes Required)
To completely avoid defining custom routes or controllers, Lila provides a built-in global switcher. Simply append the query parameter ?lang=code (or ?changeLang=code, ?change_lang=code) to any URL in your entire application. Lila will intercept it, update the session language preference automatically, and seamlessly perform an instant, clean redirect to the same URL without the query parameter.
<!-- Dynamically switches the active language to Spanish or English and reloads the current page cleanly -->
<a href="?lang=es">🇪🇸 Español</a>
<a href="?lang=en">🇺🇸 English</a>
Translating Pydantic Errors
If you use Pydantic models for validation, you can translate the error messages into Spanish automatically.
try:
user = UserModel(**data)
except ValidationError as e:
# Translates the first error found
err_msg = Translate.translate_pydantic_error(e.errors()[0], target_lang="es")
return JSONResponse({"error": err_msg}, status_code=400)
Performance Note: The Translate class uses internal caching to avoid
re-reading JSON files and re-processing dictionaries on every request. Language settings are also cached
within the request state to minimize session decryption overhead.
Client-Side Instant Translation
To provide a completely instantaneous user experience when switching languages, Lila's SPA navigation engine (spa.js) features an optimistic translation layer. When a user requests a language switch via a query parameter (e.g., ?lang=es), the engine intercepts it and instantly translates the page on the client side before the network request completes.
How it works:
- Initial Bootstrapping: The backend serializes the current language's active translations dictionary and boots the client with
window.LILA_TRANSLATIONS(defined insidebase.html). - Languages History: As the user navigates, the client-side SPA cache maintains a dictionary of translations (
langsHistory) for each language loaded from the server responses. - DOM Translation: When switching language, Lila traverses the visible text nodes in the DOM, finds the translation key in the current language's dictionary, and replaces it with the target language's translation instantly (within 0-2ms).
- Server Sync: In the background, the SPA engine performs a fetch to synchronize with the server, updating the local cache and ensuring the page has the latest backend state.
Note: Since translations occur instantly on the client, the user experiences zero lag or layout shifts during language switches, even on slow or latent connections.
Usage in Templates
Translations are automatically available in the translate context variable:
{{ translate["Send"] }}
{{ translate["Hi"] }} {{ user.name }}