Translations (i18n)
Fullfinity supports multi-language translations using an approach where the English source text serves as the translation key.
Overview
Section titled “Overview”- Backend-driven: All translations are managed on the backend and served to the frontend
- English-text-as-key format: Translation files use
{"English Text": "Translated Text"}format - AI-powered: Translations are generated using OpenAI or Anthropic APIs
- Per-module: Each module has its own
i18n/folder with translation files
Translation File Format
Section titled “Translation File Format”Translation files are stored in each module’s i18n/ directory:
modules/crm/├── models/├── views/└── i18n/ ├── es.json # Spanish ├── fr.json # French └── de.json # GermanEach file uses English text as the key:
{ "Customer": "Cliente", "Expected Revenue": "Ingresos esperados", "Mark as Won": "Marcar como ganado", "Leads in this stage are considered won": "Los leads en esta etapa se consideran ganados"}Translation CLI
Section titled “Translation CLI”The fullfinity-translate CLI extracts translatable strings and generates translations.
Basic Usage
Section titled “Basic Usage”# Extract strings from a module (shows counts)./fullfinity-translate -c config.yaml --extract fullfinity/modules/crm
# Translate a single module to Spanish./fullfinity-translate -c config.yaml fullfinity/modules/crm es
# Translate all modules to multiple languages./fullfinity-translate -c config.yaml fullfinity/modules es,fr,de
# Translate to all supported languages./fullfinity-translate -c config.yaml --all fullfinity/modules/invoicing
# List supported languages./fullfinity-translate --list-languagesFrontend strings
Section titled “Frontend strings”React t() strings are extracted from app/src and stored with the core module
(modules/core/i18n/<lang>.json), because all translations are served from the backend.
They are translated automatically whenever core is within the path you pass, e.g.:
./fullfinity-translate -c config.yaml fullfinity/modules es # all modules + frontend./fullfinity-translate -c config.yaml fullfinity/modules/core es # core + frontendLoading into the database
Section titled “Loading into the database”Generated i18n/*.json files are not read at runtime — they’re loaded into the
Translation table. After generating, reload them:
- UI: Languages → pick the language → Generate translations (generates + loads in one step), or Reload translations to just load existing files.
- These map to
Language.action_generate_translations/action_load_translations. Loading only happens for installed modules.
Supported Languages
Section titled “Supported Languages”| Code | Language |
|---|---|
| es | Spanish |
| fr | French |
| de | German |
| pt-BR | Portuguese (Brazil) |
| zh-CN | Chinese (Simplified) |
| ja | Japanese |
| ar | Arabic |
| it | Italian |
| nl | Dutch |
| ru | Russian |
| ko | Korean |
| hi | Hindi |
| tr | Turkish |
| pl | Polish |
| vi | Vietnamese |
Configuration
Section titled “Configuration”Add an AI API key to your config.yaml:
# Use either OpenAI or AnthropicOPENAI_API_KEY: "sk-proj-..."# orANTHROPIC_API_KEY: "sk-ant-..."What Gets Translated
Section titled “What Gets Translated”From Python Models
Section titled “From Python Models”The CLI extracts:
- Field descriptions:
description="Customer Name" - Field hints:
hint="Enter the customer's full name" - Error messages:
raise ValidationError("Invalid email format")
class Contact(Model): name = Char( description="Contact Name", # Extracted hint="Full name of the contact", # Extracted max_length=255 )From JSON Views
Section titled “From JSON Views”The CLI extracts these keys from view definitions:
title- Section/tab titleslabel- Field labelsdescription- Descriptionsplaceholder- Input placeholdershint- Help textmessage- Messagesname- Only fromUiMenuandWindowAction(display names)
{ "type": "field", "name": "contact", "properties": { "label": "Customer", "placeholder": "Select a customer", "hint": "The customer for this order" }}From Frontend (React)
Section titled “From Frontend (React)”Strings wrapped in the t() function are extracted. The English text is the key —
pass it as the first argument:
t("Save Changes") // key + fallback in onet("Saved {count} records", { count }) // with interpolation paramsDo not invent a separate identifier key, e.g. t("save_changes", "Save Changes").
The whole pipeline (extraction, i18n/*.json, the Translation.key/source columns,
and the served lookup dict) is keyed by the English source text, so a synthetic key
like "save_changes" is never present at runtime and the string silently falls back to
English. Keying by the English text is also collision-free — the source string is
already unique, whereas a lowercased/underscored identifier can collapse distinct
strings together ("Sign up" and "Sign-up").
How Translation Works at Runtime
Section titled “How Translation Works at Runtime”Backend
Section titled “Backend”The translate_arch() function translates view architectures based on user language:
from fullfinity.engine.translation import translate_arch, get_translations_dict
# Get translations for Spanishtranslations = await get_translations_dict(env, "es")
# Translate a viewtranslated_arch = translate_arch(view.arch, translations)Frontend
Section titled “Frontend”Use the useTranslation hook:
import { useTranslation } from '../contexts/TranslationContext';
function MyComponent() { const { t } = useTranslation();
return ( <Button>{t("Save Changes")}</Button> );}Adding New Translatable Strings
Section titled “Adding New Translatable Strings”- In Python models, use
description=andhint=:
name = Char(description="Product Name", hint="Enter product name")- In JSON views, use translatable keys:
{ "type": "fieldset", "title": "Customer Information", "content": [...]}- In React components, wrap with
t():
<Button>{t("Submit Order")}</Button>- Run the translation CLI to generate translations:
./fullfinity-translate -c config.yaml fullfinity/modules/mymodule esIncremental Translation
Section titled “Incremental Translation”The CLI only translates new strings. Existing translations are preserved:
# First run: translates all 50 strings./fullfinity-translate -c config.yaml fullfinity/modules/crm es# Output: Translating 50 new strings...
# Add new fields, run again: only new strings translated./fullfinity-translate -c config.yaml fullfinity/modules/crm es# Output: Translating 3 new strings...Best Practices
Section titled “Best Practices”-
Use descriptive English text - The English text becomes the key, so make it clear and unique
-
Avoid programmatic strings - Don’t translate identifiers, field names, or code
-
Review AI translations - AI translations are good but may need manual review for domain-specific terms
-
Translate early and often - Run translations as part of your development workflow
-
Keep translations in version control - The
i18n/*.jsonfiles should be committed