Internationalization (I18n) API ReferenceThis document provides detailed information about the internationalization system in the Summon library. The I18n systemprovides comprehensive support for multiple languages, right-to-left (RTL) layouts, and localized content.

Internationalization (I18n) API Reference

This document provides detailed information about the internationalization system in the Summon library. The I18n system provides comprehensive support for multiple languages, right-to-left (RTL) layouts, and localized content.

Table of Contents


Overview

The Summon I18n system provides:

  • Multi-language support with dynamic language switching
  • RTL/LTR layout support for proper text direction handling
  • Translation management with placeholder support
  • Directional modifiers for layout-aware styling
  • Composable integration with CompositionLocal pattern

Key Features

  • Type-safe language definitions
  • Automatic layout direction detection
  • Directional padding and margin utilities
  • Translation key management
  • RTL-aware component mirroring

Core Types

Language Definition

data class Language(
    val code: String,        // ISO language code (e.g., "en", "ar", "he")
    val name: String,        // Display name (e.g., "English", "العربية")
    val direction: LayoutDirection
)

enum class LayoutDirection {
    LTR,    // Left-to-Right (English, French, etc.)
    RTL     // Right-to-Left (Arabic, Hebrew, etc.)
}

Language Examples

val English = Language("en", "English", LayoutDirection.LTR)
val Arabic = Language("ar", "العربية", LayoutDirection.RTL)
val Hebrew = Language("he", "עברית", LayoutDirection.RTL)
val French = Language("fr", "Français", LayoutDirection.LTR)

I18n Configuration

Global configuration for supported languages and default settings.

I18nConfig Object

object I18nConfig {
    val supportedLanguages: List<Language>
    var defaultLanguage: Language?

    fun configure(block: I18nConfigBuilder.() -> Unit)
    fun changeLanguage(languageCode: String): Boolean
}

class I18nConfigBuilder {
    val languages: MutableList<Language>
    var defaultLanguage: Language?

    fun language(
        code: String,
        name: String,
        direction: LayoutDirection = LayoutDirection.LTR
    )
    fun defaultLanguage(language: Language)
}

Configuration Example

I18nConfig.configure {
    language("en", "English", LayoutDirection.LTR)
    language("ar", "العربية", LayoutDirection.RTL)
    language("he", "עברית", LayoutDirection.RTL)
    language("fr", "Français", LayoutDirection.LTR)

    defaultLanguage = Language("en", "English", LayoutDirection.LTR)
}

I18n Context

The I18nContext provides translation functionality and language state to components.

I18nContext Class

data class I18nContext(
    val language: String = "en",
    val translations: Map<String, String> = emptyMap(),
    val translator: (String, String) -> String = { key, lang -> key }
) {
    fun getString(key: String): String
    fun getString(key: String, vararg args: Any): String
}

CompositionLocal Support

val LocalI18n: CompositionLocal<I18nContext>

@Composable
fun I18nProvider(
    language: String,
    translations: Map<String, String> = emptyMap(),
    translator: (String, String) -> String = { key, _ -> key },
    content: @Composable () -> Unit
)

@Composable
fun rememberI18n(): I18nContext

Usage Examples

// Provide I18n context
@Composable
fun App() {
    val translations = mapOf(
        "welcome" to "Welcome",
        "hello_user" to "Hello, {0}!",
        "button_submit" to "Submit"
    )

    I18nProvider(
        language = "en",
        translations = translations
    ) {
        MainContent()
    }
}

// Use translations in components
@Composable
fun WelcomeMessage(userName: String) {
    val i18n = rememberI18n()

    Text(i18n.getString("welcome"))
    Text(i18n.getString("hello_user", userName))
}

Language Management

Language Provider

@Composable
fun LanguageProvider(
    language: Language,
    content: @Composable () -> Unit
)

val LocalLanguage: CompositionLocal<Language>
val LocalLayoutDirection: CompositionLocal<() -> LayoutDirection>

@Composable
fun rememberCurrentLanguage(): Language

@Composable
fun rememberLayoutDirection(): LayoutDirection

Dynamic Language Switching

// Global language switching
fun changeLanguage(languageCode: String): Boolean

// Component-level language switching
@Composable
fun LanguageSwitcher(
    currentLanguage: String,
    onLanguageChange: (String) -> Unit
) {
    val supportedLanguages = I18nConfig.supportedLanguages

    Select(
        value = currentLanguage,
        onValueChange = onLanguageChange,
        options = supportedLanguages.map {
            SelectOption(it.code, it.name)
        }
    )
}

RTL Support

RTL utilities for proper right-to-left layout support.

RtlUtils Object

object RtlUtils {
    @Composable
    fun isRtl(): Boolean

    @Composable
    fun <T> directionalValue(ltrValue: T, rtlValue: T): T
}

RTL Detection and Values

@Composable
fun DirectionalComponent() {
    val isRtl = RtlUtils.isRtl()

    val textAlign = RtlUtils.directionalValue(
        ltrValue = "left",
        rtlValue = "right"
    )

    val marginDirection = RtlUtils.directionalValue(
        ltrValue = "margin-left",
        rtlValue = "margin-right"
    )

    Text(
        text = "Content",
        modifier = Modifier()
            .style("text-align", textAlign)
            .style(marginDirection, "16px")
    )
}

Translation System

String Resources

object StringResources {
    fun loadTranslations(language: String): Map<String, String>
    fun getTranslation(key: String, language: String): String?
}

class TranslationProvider {
    fun getTranslations(language: String): Map<String, String>
    fun addTranslations(language: String, translations: Map<String, String>)
}

Translation Management

// Define translations
val englishTranslations = mapOf(
    "app_name" to "My App",
    "welcome_message" to "Welcome to our application!",
    "user_greeting" to "Hello, {0}!",
    "item_count" to "You have {0} items",
    "button_save" to "Save",
    "button_cancel" to "Cancel",
    "error_network" to "Network connection error"
)

val arabicTranslations = mapOf(
    "app_name" to "تطبيقي",
    "welcome_message" to "مرحباً بك في تطبيقنا!",
    "user_greeting" to "مرحباً، {0}!",
    "item_count" to "لديك {0} عناصر",
    "button_save" to "حفظ",
    "button_cancel" to "إلغاء",
    "error_network" to "خطأ في الاتصال بالشبكة"
)

// Usage with placeholder replacement
@Composable
fun UserProfile(user: User) {
    val i18n = rememberI18n()

    Text(i18n.getString("user_greeting", user.name))
    Text(i18n.getString("item_count", user.itemCount))
}

Directional Modifiers

Layout-aware modifiers that adapt to text direction.

Directional Padding and Margin

@Composable
fun Modifier.directionalPadding(
    start: String,
    top: String,
    end: String,
    bottom: String
): Modifier

@Composable
fun Modifier.directionalMargin(
    start: String,
    top: String,
    end: String,
    bottom: String
): Modifier

RTL Mirroring

@Composable
fun Modifier.mirrorInRtl(): Modifier

@Composable
fun Modifier.directionalRow(): Modifier

Directional Modifier Examples

@Composable
fun Card(content: @Composable () -> Unit) {
    Box(
        modifier = Modifier()
            .backgroundColor("#ffffff")
            .borderRadius("8px")
            .directionalPadding(
                start = "16px",
                top = "12px",
                end = "16px",
                bottom = "12px"
            )
            .directionalMargin(
                start = "8px",
                top = "0px",
                end = "8px",
                bottom = "16px"
            )
    ) {
        content()
    }
}

@Composable
fun IconButton(icon: String, onClick: () -> Unit) {
    Button(
        onClick = onClick,
        modifier = Modifier()
            .mirrorInRtl() // Mirror the icon in RTL
    ) {
        Icon(icon)
    }
}

@Composable
fun NavigationRow() {
    Row(
        modifier = Modifier()
            .directionalRow() // Reverses to row-reverse in RTL
    ) {
        Text("Previous")
        Spacer(Modifier().weight(1f))
        Text("Next")
    }
}

Usage Examples

Complete I18n Setup

@Composable
fun MultilingualApp() {
    var currentLanguage by remember { mutableStateOf("en") }

    // Configure supported languages
    LaunchedEffect(Unit) {
        I18nConfig.configure {
            language("en", "English", LayoutDirection.LTR)
            language("ar", "العربية", LayoutDirection.RTL)
            language("he", "עברית", LayoutDirection.RTL)
            defaultLanguage = Language("en", "English", LayoutDirection.LTR)
        }
    }

    // Load translations for current language
    val translations = remember(currentLanguage) {
        when (currentLanguage) {
            "en" -> englishTranslations
            "ar" -> arabicTranslations
            "he" -> hebrewTranslations
            else -> englishTranslations
        }
    }

    I18nProvider(
        language = currentLanguage,
        translations = translations
    ) {
        LanguageProvider(
            language = Language(currentLanguage, "",
                if (currentLanguage in listOf("ar", "he")) LayoutDirection.RTL
                else LayoutDirection.LTR
            )
        ) {
            AppContent(
                onLanguageChange = { currentLanguage = it }
            )
        }
    }
}

RTL-Aware Components

@Composable
fun UserCard(user: User) {
    val i18n = rememberI18n()
    val isRtl = RtlUtils.isRtl()

    Card(
        modifier = Modifier()
            .fillMaxWidth()
            .directionalPadding("16px", "12px", "16px", "12px")
    ) {
        Row(
            modifier = Modifier()
                .directionalRow()
                .alignItems(AlignItems.Center)
        ) {
            // Profile image
            Image(
                src = user.avatarUrl,
                alt = i18n.getString("user_avatar"),
                modifier = Modifier()
                    .width("48px")
                    .height("48px")
                    .borderRadius("50%")
                    .directionalMargin(
                        start = "0px",
                        top = "0px",
                        end = "12px",
                        bottom = "0px"
                    )
            )

            // User info
            Column {
                Text(
                    text = user.name,
                    modifier = Modifier()
                        .themeTextStyle("subtitle")
                        .style("text-align",
                            RtlUtils.directionalValue("left", "right")
                        )
                )

                Text(
                    text = i18n.getString("user_status", user.status),
                    modifier = Modifier()
                        .themeTextStyle("caption")
                        .style("text-align",
                            RtlUtils.directionalValue("left", "right")
                        )
                )
            }

            Spacer(Modifier().weight(1f))

            // Action button
            Button(
                onClick = { /* action */ },
                label = i18n.getString("button_contact"),
                modifier = Modifier()
                    .mirrorInRtl() // Mirror button in RTL if it has directional icon
            )
        }
    }
}

Form with I18n

@Composable
fun ContactForm() {
    val i18n = rememberI18n()
    var name by remember { mutableStateOf("") }
    var email by remember { mutableStateOf("") }
    var message by remember { mutableStateOf("") }

    Form(
        modifier = Modifier()
            .maxWidth("500px")
            .directionalPadding("20px", "20px", "20px", "20px")
    ) {
        FormField(
            label = i18n.getString("form_name_label"),
            required = true
        ) {
            TextField(
                value = name,
                onValueChange = { name = it },
                placeholder = i18n.getString("form_name_placeholder"),
                modifier = Modifier()
                    .fillMaxWidth()
                    .style("text-align",
                        RtlUtils.directionalValue("left", "right")
                    )
            )
        }

        FormField(
            label = i18n.getString("form_email_label"),
            required = true
        ) {
            TextField(
                value = email,
                onValueChange = { email = it },
                type = "email",
                placeholder = i18n.getString("form_email_placeholder"),
                modifier = Modifier()
                    .fillMaxWidth()
                    .style("text-align", "left") // Email always LTR
            )
        }

        FormField(
            label = i18n.getString("form_message_label")
        ) {
            TextField(
                value = message,
                onValueChange = { message = it },
                placeholder = i18n.getString("form_message_placeholder"),
                modifier = Modifier()
                    .fillMaxWidth()
                    .height("120px")
                    .style("text-align",
                        RtlUtils.directionalValue("left", "right")
                    )
            )
        }

        Row(
            modifier = Modifier()
                .directionalRow()
                .justifyContent(JustifyContent.SpaceBetween)
                .marginTop("20px")
        ) {
            Button(
                onClick = { /* cancel */ },
                label = i18n.getString("button_cancel"),
                variant = ButtonVariant.SECONDARY
            )

            Button(
                onClick = { /* submit */ },
                label = i18n.getString("button_submit"),
                variant = ButtonVariant.PRIMARY
            )
        }
    }
}

Best Practices

  1. Use semantic translation keys: Prefer "welcome_message" over "text1"
  2. Plan for text expansion: Some languages require 30-40% more space
  3. Test with RTL languages: Ensure layouts work properly in both directions
  4. Use directional modifiers: Prefer directionalPadding() over fixed padding
  5. Consider cultural differences: Colors, icons, and layouts may have different meanings
  6. Handle pluralization: Use separate keys for singular/plural forms
  7. Provide fallbacks: Always have default translations for missing keys

Migration Notes

When adding I18n to existing components:

  • Replace hardcoded strings with translation keys
  • Update layouts to use directional modifiers
  • Test with both LTR and RTL languages
  • Consider text direction for form inputs
  • Update icon usage to be direction-aware

See Also

© 2025Yousef
Built withSummonSummon