-
Notifications
You must be signed in to change notification settings - Fork 15
Adding Translations
Invio supports internationalization (i18n) with separate translation files for the frontend UI and backend invoice rendering. This guide explains how to add a new language to Invio.
Invio uses a two-part translation system:
- Frontend translations — UI labels, buttons, forms, and messages in the admin interface
- Backend translations — Invoice template text (headings, labels) that appear in rendered PDFs and HTML
Both systems use JSON files with key-value pairs. The frontend supports interpolation for dynamic values.
Currently supported locales:
-
en— English (default) -
nl— Dutch (Nederlands)
Create a new JSON file in frontend/i18n/locales/ named after your locale code (e.g., de.json for German, fr.json for French, es.json for Spanish).
Location: frontend/i18n/locales/<locale>.json
Template: Copy frontend/i18n/locales/en.json as your starting point.
Structure:
{
"Dashboard": "Dashboard",
"Invoices": "Invoices",
"Settings": "Settings",
"Save": "Save",
"Cancel": "Cancel"
}Interpolation: Some keys use {{variable}} syntax for dynamic values:
{
"Invoice number already exists": "Invoice number already exists",
"Export failed with status {{status}}": "Export failed with status {{status}}",
"Discount {{percentage}}%": "Discount {{percentage}}%"
}Keep the {{variable}} placeholders intact and translate around them.
Complete the translation:
- Translate all ~220 keys from English to your target language
- Preserve special characters and formatting
- Keep technical terms (like "Ctrl+S") as-is or adapt to local conventions
Create a matching JSON file in backend/src/i18n/locales/ with the same locale code.
Location: backend/src/i18n/locales/<locale>.json
Template: Copy backend/src/i18n/locales/en.json as your starting point.
Structure:
{
"invoiceTitle": "Invoice",
"invoiceNumberLabel": "Invoice Number",
"dueDateLabel": "Due Date",
"billToHeading": "BILL TO",
"itemHeaderDescription": "Description",
"subtotalLabel": "Subtotal",
"totalLabel": "Total",
"paymentInformationHeading": "Payment Information",
"thankYouNote": "Thank you for your business!"
}Complete the translation:
- Translate all required keys (see
REQUIRED_KEYSinbackend/src/i18n/translations.ts) - These labels appear in invoice PDFs, so use professional invoice terminology
- Keep formatting consistent (e.g., "BILL TO" vs "Bill To" — match your locale's convention)
Edit frontend/i18n/mod.ts to import and register your new locale:
import enMessages from "./locales/en.json" with { type: "json" };
import nlMessages from "./locales/nl.json" with { type: "json" };
import deMessages from "./locales/de.json" with { type: "json" }; // Add this
const catalogs: Record<string, UiMessages> = {
en: enMessages as UiMessages,
nl: nlMessages as UiMessages,
de: deMessages as UiMessages, // Add this
};Edit backend/src/i18n/translations.ts to import and register your locale:
import enRaw from "./locales/en.json" with { type: "json" };
import nlRaw from "./locales/nl.json" with { type: "json" };
import deRaw from "./locales/de.json" with { type: "json" }; // Add this
// In the catalogs object:
const catalogs: Record<string, InvoiceLabels> = {
en: validateAndNormalize(enRaw as Record<string, unknown>, "en"),
nl: validateAndNormalize(nlRaw as Record<string, unknown>, "nl"),
de: validateAndNormalize(deRaw as Record<string, unknown>, "de"), // Add this
};-
Set locale in Settings:
- Log into Invio admin
- Navigate to Settings → Appearance
- Select your new locale from the dropdown
- Save changes
-
Test frontend:
- Navigate through all pages (Dashboard, Invoices, Customers, Settings)
- Create/edit an invoice
- Verify all labels are translated correctly
- Check for missing keys (they'll display as the key itself)
-
Test backend:
- Create or edit an invoice
- Click "Download PDF" or "View HTML"
- Verify invoice labels use your translation
- Check for proper formatting and professional appearance
-
Test number/date formats:
- In Settings → Appearance, set number format and date format
- Create an invoice and verify currency and dates render correctly
Invio resolves the locale in this order:
- User-selected locale in Settings (stored in database)
- Locale from invoice data (if set)
- Browser/request locale (frontend only)
- Default (
en)
In addition to translations, you can configure:
-
Number format:
comma(1.234,56) orperiod(1,234.56) -
Date format: Custom pattern using tokens like
YYYY-MM-DD,DD/MM/YYYY, etc.
These are set in Settings → Appearance and stored separately from the locale.
- Form validation messages — Users see these on errors
- Button labels — "Save", "Cancel", "Delete", "Publish", etc.
- Status labels — "Draft", "Sent", "Paid", "Overdue", "Cancelled"
- Placeholders — Input field hints like "e.g. INV-2025-001"
- Help text — Longer descriptions in Settings
- Invoice headings — Professional invoice terminology
- Table headers — Clear, concise column labels
- Summary section — Financial terms like "Subtotal", "Tax", "Total"
- Payment information — Banking and payment terms
- Thank you note — Professional closing message
- Currency symbols — Respect local conventions ($1,000.00 vs €1.000,00)
- Date formats — MM/DD/YYYY (US) vs DD/MM/YYYY (Europe) vs YYYY-MM-DD (ISO)
- Formal vs informal — Some languages distinguish (Sie/du, vous/tu)
- Address formats — Order of street, city, postal code varies by country
- All keys from
en.jsonare present - No untranslated English text remains
- Interpolation variables (
{{var}}) preserved - Professional tone for invoice labels
- Tested in UI and PDFs
- Number/date formats configured for locale
- No grammar or spelling errors
- Consistent terminology throughout
Once you've completed and tested your translation:
- Fork the Invio repository
- Add your translation files
- Update
frontend/i18n/mod.tsandbackend/src/i18n/translations.ts - Test thoroughly
- Submit a Pull Request with:
- Clear title: "Add [Language] translation"
- Description of what was translated
- Note any locale-specific considerations
Your contribution helps make Invio accessible to more users worldwide!
When Invio adds new features:
- New keys may be added to
en.jsonfiles - You'll need to add translations for these keys
- Missing keys will fall back to English
- Watch the repository for translation updates
-
Missing keys? Compare your translation to
en.json - Formatting issues? Check the JSON syntax (commas, quotes)
- Display problems? Verify locale code matches everywhere
-
Questions? Open an issue on GitHub with the
i18nlabel
Need an example? Review the Dutch (nl) translations in:
frontend/i18n/locales/nl.jsonbackend/src/i18n/locales/nl.json