ZodulaZodula
Advance

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

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 hit

Pattern 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 variables

Fallback 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")
// 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 button

Common 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:

  1. Translation exists: Verify the translation record exists in the database
  2. Language matches: Ensure the language code matches the current language
  3. Key matches: Check that the translation key exactly matches the t() call

Variables Not Working

For template variables to work:

  1. Use double braces: {{variable}} not {variable}
  2. Provide variables object: t("Hello {{name}}", { name: "John" })
  3. Match variable names: The variable name in the template must match the object key

Performance Issues

For better performance:

  1. Use caching: The system automatically caches translations
  2. Avoid dynamic keys: Use static translation keys when possible
  3. Batch translations: Load all needed translations at once

Migration and Updates

Adding New Languages

  1. Create language records in zodula__Language
  2. Add translations for the new language in zodula__Translation
  3. Update the language selection component if needed

Updating Existing Translations

  1. Modify translation records in the database
  2. Clear browser cache to see changes
  3. 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.