Skip to content

DB Internationalization

Internationalize SQLModel string attributes with the I18nMixin mixin. The mixin lets you declare localized variants of a string field (for example name_en, name_fr) and resolves the appropriate locale at runtime.

Register localized fields

Define localized attributes and register them in __localized_fields__. The fallback language must always be available. Use the Lang enum to define the available languages at the model level.

Example model

from typing import Optional

from sqlmodel import SQLModel
from sqlmodel import Field

from ecodev_core import I18nMixin
from ecodev_core import Lang


class LocalizedModel(I18nMixin, SQLModel, table=True):  # type: ignore[misc]
    """Localized entity storing translated names."""

    __tablename__ = 'localized_model'
    __localized_fields__ = {'name': [Lang.EN, Lang.FR]}
    __fallback_lang__ = Lang.EN

    id: Optional[int] = Field(default=None, primary_key=True)
    name_en: str = Field(description='English display name')
    name_fr: Optional[str] = Field(default=None, description='French display name')

Here the field name is declared as localized with English and French variants. The fallback language (Lang.EN) is required (non nullable).

Runtime locale resolution

from sqlmodel import Session, select

from ecodev_core import engine
from ecodev_core import set_lang
from ecodev_core import Lang
from ecodev_core import localized_col

with Session(engine) as session:
    english_only = LocalizedModel(name_en='Hello')
    translated = LocalizedModel(name_en='Hello', name_fr='Bonjour')

    session.add(english_only)
    session.add(translated)
    session.commit()

    set_lang(Lang.FR)
    print(translated.name)  # Bonjour

    set_lang(Lang.EN)
    print(translated.name)  # Hello

    set_lang(Lang.EN)
    print(english_only.name)  # Hello

    set_lang(Lang.FR)
    print(english_only.name)  # Hello

set_lang uses a contextvars context variable so the active language flows through any functions executed within the same request/task.

import contextvars

DB_LANG = 'db_lang'
CONTEXT_DB_LANG = contextvars.ContextVar(DB_LANG, default=Lang.EN)


def set_lang(lang: Lang) -> None:
    CONTEXT_DB_LANG.set(lang)


def get_lang() -> Lang:
    return Lang(CONTEXT_DB_LANG.get())

Querying localized data

localized_col helps you build queries that use the active language (or an explicit override):

set_lang(Lang.FR)
with Session(engine) as session:
    localized_query = select(
        localized_col('name', LocalizedModel),
        LocalizedModel.id,
    )
    print(session.exec(localized_query).all())

    localized_query = select(
        localized_col('name', LocalizedModel, Lang.EN),
        LocalizedModel.id,
    )
    print(session.exec(localized_query).all())

    filtered = select(LocalizedModel).where(
        localized_col('name', LocalizedModel) == 'Bonjour'
    )
    print(session.exec(filtered).all())

The first query yields localized values in French as defined using set_lang while the second yields localized values in english. The last query filters on the localized value. See the db_i18n functional tests in ecodev-core for more usage examples.