This document provides a comprehensive technical overview of the translation architecture in the POS System, covering frontend UI labels, backend messages, and dynamic database content.
The frontend uses the industry-standard @ngx-translate library to handle static labels (buttons, headers, navigation, etc.).
- Library:
@ngx-translate/coreand@ngx-translate/http-loader. - Storage: Translation files are located in
front/public/i18n/*.json(e.g.,en.json,es.json,ca.json,de.json,zh-CN.json,hi.json). - Service:
LanguageService(front/src/app/services/language.service.ts) acts as a wrapper around@ngx-translate/core.
In front/src/app/app.config.ts, the TranslateModule is initialized with a TranslateHttpLoader that fetches JSON files from the /i18n/ path.
- Persistence: The selected language code is stored in
localStorageunder the keypos_language. - Detection: Upon initialization, the system checks:
localStoragefirst.- Browser's
navigator.languagesecond. - Fallback to
en(English) third.
- Normalization: The
normalizeLanguageCodemethod handles variants (e.g.,es-MXores_ESare both mapped toes, whilezh-Hansis mapped tozh-CN).
Static text is rendered using the translate pipe or directive:
<h1>{{ 'DASHBOARD.TITLE' | translate }}</h1>The backend (FastAPI) handles localized error and success messages for API responses.
Static messages are defined in back/app/messages.py as a nested dictionary:
MESSAGES = {
"en": {"incorrect_username_or_password": "Incorrect email or password", ...},
"es": {"incorrect_username_or_password": "Email o contraseña incorrectos", ...},
...
}The language is determined in back/app/main.py via the _get_requested_language dependency:
- Query Parameter:
?lang=estakes highest priority. - Header:
Accept-Languageheader (e.g.,es;q=1.0, en;q=0.5) is parsed. - Fallback: Defaults to
en.
The backend uses back/app/language_service.py to normalize language codes, ensuring consistency with the frontend (e.g., converting zh_CN to zh-CN).
For dynamic content like product names, descriptions, or category names, the system uses a database-backed I18nText model.
Defined in back/app/models.py:
entity_type: String identifier (e.g.,"product","tenant","product_catalog").entity_id: ID of the record being translated.field: The specific field name (e.g.,"name","description").lang: Language code (e.g.,"es","zh-CN").text: The translated content.tenant_id:- If
NULL: Global/base translation (shared by all). - If set: Tenant-specific override.
- If
back/app/translation_service.py provides the logic for fetching translations with a fallback mechanism:
- Try Tenant-specific translation.
- Try Global translation.
- Return the canonical value from the main table if no translation exists.
The backend provides dedicated endpoints in back/app/main.py for managing these translations:
GET /i18n/{entity_type}/{entity_id}: Retrieves all available translations for an entity.PUT /i18n/{entity_type}/{entity_id}: Updates or creates translations for specific fields and languages.
The following languages are currently supported across the stack:
- en: English (Default)
- es: Español
- ca: Català
- de: Deutsch
- zh-CN: 中文 (简体)
- hi: हिन्दी
- User Change: User selects "Español" in
LanguagePickerComponent. - Frontend Update:
LanguageServicecallstranslate.use('es'), updating UI labels and settinglocalStorage. - API Requests: Frontend includes
Accept-Language: esin headers via theauthInterceptor(indirectly viaLanguageService.getAcceptLanguageHeader). - Backend Response:
- Backend detects
esand returns localized error messages frommessages.py. - Backend fetches localized product names from the
I18nTexttable usingTranslationService.
- Backend detects
To add support for a new language (e.g., French - fr):
- Create
front/public/i18n/fr.jsonand translate the labels. - In
front/src/app/services/language.service.ts, add the new language to theSUPPORTED_LANGUAGESarray:{ code: 'fr', label: 'Français', locale: 'fr-FR' }
- In
back/app/language_service.py, add'fr'to theSUPPORTED_LANGUAGESlist. - In
back/app/messages.py, add a new key"fr"to theMESSAGESdictionary and translate the strings.
- Use the
PUT /i18n/{entity_type}/{entity_id}endpoint to add French translations for products, categories, etc. - (Optional) Update
back/seed_translations.pyto include default translations for the new language.