Translation System
Learn how to implement internationalization (i18n) in your Zodula applications
Overview
Zodula provides a comprehensive translation system that allows you to build multilingual applications. The system supports dynamic language switching, template variables, and automatic translation key detection.
Core Concepts
Translation Hook
The useTranslation hook is the primary way to access translation functionality:
import { useTranslation } from "@/hooks/use-translation"
function MyComponent() {
const { t, currentLanguage, setLanguage, availableLanguages } = useTranslation()
return (
<div>
<h1>{t("Welcome")}</h1>
<p>{t("Current language: {{lang}}", { lang: currentLanguage })}</p>
</div>
)
}Basic Translation Usage
The t function is used to translate text:
const { t } = useTranslation()
// Simple translation
const welcomeText = t("Welcome")
// Translation with variables
const greeting = t("Hello, {{name}}", { name: "John" })Translation with Variables
Zodula supports template variables in translations using {{variable}} syntax:
// Translation key: "Welcome back, {{name}}"
// Translation value: "Bienvenido de nuevo, {{name}}"
const message = t("Welcome back, {{name}}", { name: "John" })
// Result: "Bienvenido de nuevo, John"Auto-Detection of Variables
The system can automatically detect variables from input strings:
// If you have a translation key: "Welcome, {{name}}"
// And you call: t("Welcome, John")
// The system will automatically extract "John" as the name variable
const message = t("Welcome, John") // Automatically detects "John" as {{name}}Language Management
Getting Available Languages
const { availableLanguages, currentLanguage, setLanguage } = useTranslation()
// availableLanguages structure:
// [
// { code: "en", name: "English", flag: "πΊπΈ" },
// { code: "es", name: "Spanish", flag: "πͺπΈ" },
// { code: "fr", name: "French", flag: "π«π·" }
// ]Language Selection Component
import { LanguageSelection } from "@/components/custom/language-selection"
function Header() {
return (
<div className="flex items-center gap-4">
<h1>My App</h1>
<LanguageSelection />
</div>
)
}Programmatic Language Switching
const { setLanguage, currentLanguage } = useTranslation()
// Switch to Spanish
setLanguage("es")
// Switch to French
setLanguage("fr")
console.log(currentLanguage) // "fr"Real-World Examples
Breadcrumb Component
Here's how the breadcrumb component uses translations:
import { useTranslation } from "@/hooks/use-translation"
export const Breadcrumb = ({ items, showHome = true }) => {
const { t } = useTranslation()
const generateBreadcrumb = () => {
const breadcrumbItems = []
if (showHome) {
breadcrumbItems.push({
label: t("Home"),
href: "/admin",
icon: <Home className="w-4 h-4" />
})
}
// Add doctype breadcrumb
breadcrumbItems.push({
label: t(doctype || ""),
href: `/admin/doctypes/${doctype}`
})
// Add action breadcrumb
if (action === "list") {
breadcrumbItems.push({
label: t("List"),
href: `/admin/doctypes/${doctype}/list`
})
} else if (action === "form") {
breadcrumbItems.push({
label: t("New"),
href: `/admin/doctypes/${doctype}/form`
})
}
return breadcrumbItems
}
return (
<nav className="flex items-center space-x-1">
{generateBreadcrumb().map((item, index) => (
<div key={index}>
{item.label}
</div>
))}
</nav>
)
}List Toolbar Component
import { useTranslation } from "@/hooks/use-translation"
export function ListToolbar({ onColumnSettings }) {
const { t } = useTranslation()
return (
<div className="flex items-center justify-between">
<Input placeholder={t("Search by ID")} />
<DropdownMenu>
<DropdownMenuTrigger>
<Button variant="outline">
<MoreHorizontal className="w-4 h-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={onColumnSettings}>
<Settings className="h-4 w-4" />
{t("Column Settings")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}Advanced Features
Caching and Performance
The translation system includes intelligent caching:
// Translations are cached per language
// First call: fetches from database
const text1 = t("Welcome") // Database lookup
// Subsequent calls: uses cache
const text2 = t("Welcome") // Cache hitPattern Matching
The system supports pattern-based translations:
// Translation key: "User {{count}} of {{total}}"
// Input: "User 5 of 10"
// System automatically detects: { count: "5", total: "10" }
const message = t("User 5 of 10") // Automatically extracts variablesFallback Behavior
// If translation doesn't exist, returns the original key
const text = t("Non-existent key") // Returns "Non-existent key"Setting Up Translations
1. Create Translation Records
Translations are stored in the zodula__Translation DocType:
// Translation record structure
{
key: "Welcome", // The translation key
translation: "Bienvenido", // The translated text
language: "es" // The language code
}2. Create Language Records
Languages are stored in the zodula__Language DocType:
// Language record structure
{
name: "Spanish", // Full language name
abbr: "es", // Language code
flag_emoji: "πͺπΈ" // Flag emoji
}3. Set Default Language
Configure the default language in Website Settings:
// Website Setting record
{
default_language: "en" // Default language code
}Best Practices
1. Use Descriptive Keys
// Good
t("user.profile.edit.button.save")
// Avoid
t("btn1")2. Group Related Translations
// Group by feature
t("auth.login.title")
t("auth.login.button.submit")
t("auth.login.error.invalid_credentials")
// Group by component
t("breadcrumb.home")
t("breadcrumb.list")
t("breadcrumb.edit")3. Handle Pluralization
// Use variables for counts
t("{{count}} items", { count: 5 })
t("{{count}} item", { count: 1 })4. Provide Context
// Be specific about context
t("button.save") // Generic save button
t("form.user.save") // User form save button
t("modal.confirm.save") // Modal confirmation save buttonCommon Patterns
Dynamic Content Translation
function UserCard({ user }) {
const { t } = useTranslation()
return (
<div>
<h3>{t("User: {{name}}", { name: user.name })}</h3>
<p>{t("Joined on {{date}}", { date: user.joinDate })}</p>
</div>
)
}Conditional Translations
function StatusBadge({ status }) {
const { t } = useTranslation()
const getStatusText = () => {
switch (status) {
case 'active':
return t("Active")
case 'inactive':
return t("Inactive")
case 'pending':
return t("Pending")
default:
return t("Unknown")
}
}
return <span>{getStatusText()}</span>
}Form Field Translations
function UserForm() {
const { t } = useTranslation()
return (
<form>
<label>{t("Name")}</label>
<input placeholder={t("Enter your name")} />
<label>{t("Email")}</label>
<input placeholder={t("Enter your email")} />
<button type="submit">{t("Save User")}</button>
</form>
)
}Troubleshooting
Translation Not Found
If a translation doesn't appear, check:
- Translation exists: Verify the translation record exists in the database
- Language matches: Ensure the language code matches the current language
- Key matches: Check that the translation key exactly matches the
t()call
Variables Not Working
For template variables to work:
- Use double braces:
{{variable}}not{variable} - Provide variables object:
t("Hello {{name}}", { name: "John" }) - Match variable names: The variable name in the template must match the object key
Performance Issues
For better performance:
- Use caching: The system automatically caches translations
- Avoid dynamic keys: Use static translation keys when possible
- Batch translations: Load all needed translations at once
Migration and Updates
Adding New Languages
- Create language records in
zodula__Language - Add translations for the new language in
zodula__Translation - Update the language selection component if needed
Updating Existing Translations
- Modify translation records in the database
- Clear browser cache to see changes
- Test with different languages to ensure consistency
This translation system provides a robust foundation for building multilingual Zodula applications with support for dynamic content, template variables, and seamless language switching.
Zodula