Skip to content

Internationalization (i18n)

Truthound provides an enterprise-grade i18n system for internationalizing validator error messages.

Supported Languages

Core Languages (7)

Code Language Direction
en English LTR
ko Korean LTR
ja Japanese LTR
zh Chinese LTR
de German LTR
fr French LTR
es Spanish LTR

Extended Languages (8)

Code Language Direction Notes
pt Portuguese LTR
pt-BR Portuguese (Brazil) LTR Brazil
pt-PT Portuguese (Portugal) LTR Portugal
it Italian LTR
ru Russian LTR
ar Arabic RTL
he Hebrew RTL
fa Persian RTL

Basic Usage

Setting the Locale

from truthound.validators.i18n import (
    set_validator_locale,
    get_validator_locale,
)

# Set locale
set_validator_locale("ko")

# Get current locale
locale = get_validator_locale()
print(locale)  # "ko"

Retrieving Messages

from truthound.validators.i18n import (
    get_validator_message,
    ValidatorMessageCode,
)

# Set locale
set_validator_locale("ko")

# Retrieve message
msg = get_validator_message(
    ValidatorMessageCode.NULL_VALUES_FOUND,
    column="email",
    count=10,
)
print(msg)  # "'email' 컬럼에서 10개의 null 값이 발견되었습니다"

Formatting Issue Messages

from truthound.validators.i18n import format_issue_message
from truthound.validators.base import ValidationIssue

issue = ValidationIssue(
    column="email",
    issue_type="null_value",
    count=10,
    severity="high",
)

# Format issue message
msg = format_issue_message(issue, locale="ko")

ValidatorMessageCode

All validator message codes:

Completeness

Code Description
NULL_VALUES_FOUND Null values found
COMPLETENESS_BELOW_THRESHOLD Completeness below threshold
REQUIRED_COLUMN_MISSING Required column missing

Uniqueness

Code Description
DUPLICATE_VALUES_FOUND Duplicate values found
UNIQUENESS_CONSTRAINT_VIOLATED Uniqueness constraint violated

Range & Distribution

Code Description
VALUES_OUT_OF_RANGE Values out of range
MIN_VALUE_EXCEEDED Below minimum value
MAX_VALUE_EXCEEDED Exceeds maximum value
OUTLIERS_DETECTED Outliers detected

Pattern & Format

Code Description
PATTERN_MISMATCH Pattern mismatch
INVALID_EMAIL_FORMAT Invalid email format
INVALID_DATE_FORMAT Invalid date format
INVALID_PHONE_FORMAT Invalid phone format

Type

Code Description
TYPE_MISMATCH Type mismatch
UNEXPECTED_TYPE Unexpected type

Relationship

Code Description
FOREIGN_KEY_VIOLATION Foreign key violation
REFERENTIAL_INTEGRITY_ERROR Referential integrity error

CLDR Plural Rules

Support for CLDR (Common Locale Data Repository) standard plural rules:

Plural Categories

Category Description Example
zero Zero Arabic 0
one Singular 1 item
two Dual Arabic 2
few Few Russian 2-4
many Many Russian 5-20
other Other General plural

Using Plurals

from truthound.validators.i18n import (
    pluralize,
    get_plural_category,
    PluralCategory,
)

# Get plural category
category = get_plural_category(5, "ru")
print(category)  # PluralCategory.FEW

# Plural message
msg = pluralize(
    count=5,
    forms={
        "one": "{count} файл",
        "few": "{count} файла",
        "many": "{count} файлов",
        "other": "{count} файлов",
    },
    locale="ru",
)
print(msg)  # "5 файлов"

Plural Rules by Language

Language Categories Rules
English one, other 1 = one, else other
Korean other All other
Japanese other All other
Chinese other All other
Russian one, few, many, other Complex rules
Arabic zero, one, two, few, many, other 6 forms
French one, other 0, 1 = one, else other
German one, other 1 = one, else other

Number and Date Formatting

Number Formatting

from truthound.validators.i18n import (
    format_number,
    format_currency,
    NumberStyle,
)

# Number formatting
format_number(1234567.89, "de")   # "1.234.567,89"
format_number(1234567.89, "en")   # "1,234,567.89"
format_number(1234567.89, "ko")   # "1,234,567.89"
format_number(1234567.89, "ar")   # "١٬٢٣٤٬٥٦٧٫٨٩"

# Currency formatting
format_currency(1000, "USD", "en")  # "$1,000.00"
format_currency(1000, "EUR", "de")  # "1.000,00 €"
format_currency(1000, "KRW", "ko")  # "₩1,000"

Date Formatting

from truthound.validators.i18n import (
    format_date,
    format_time,
    format_relative_time,
    DateStyle,
    TimeStyle,
)
from datetime import datetime, timedelta

now = datetime.now()

# Date formatting
format_date(now, "ko", DateStyle.SHORT)   # "24. 12. 28."
format_date(now, "ko", DateStyle.MEDIUM)  # "2024. 12. 28."
format_date(now, "ko", DateStyle.LONG)    # "2024년 12월 28일"
format_date(now, "ko", DateStyle.FULL)    # "2024년 12월 28일 토요일"

format_date(now, "de", DateStyle.LONG)    # "28. Dezember 2024"
format_date(now, "ja", DateStyle.LONG)    # "2024年12月28日"

# Time formatting
format_time(now, "en", TimeStyle.SHORT)   # "3:30 PM"
format_time(now, "de", TimeStyle.SHORT)   # "15:30"
format_time(now, "ko", TimeStyle.MEDIUM)  # "오후 3:30:00"

# Relative time
format_relative_time(timedelta(days=-1), "ko")   # "어제"
format_relative_time(timedelta(hours=-3), "en")  # "3 hours ago"
format_relative_time(timedelta(days=2), "de")    # "in 2 Tagen"

RTL Language Support

Right-to-left (RTL) language support:

RTL Languages

from truthound.validators.i18n import (
    RTL_LANGUAGES,
    is_rtl_language,
    get_locale_direction,
    TextDirection,
)

print(RTL_LANGUAGES)
# {"ar", "he", "fa", "ur", "yi", "ps", "sd", "dv", "ug", "ku"}

is_rtl_language("ar")  # True
is_rtl_language("ko")  # False

get_locale_direction("ar")  # TextDirection.RTL
get_locale_direction("en")  # TextDirection.LTR

BiDi Handling

from truthound.validators.i18n import (
    BiDiHandler,
    BiDiConfig,
    BiDiControl,
    wrap_bidi,
    detect_direction,
)

# Detect text direction
direction = detect_direction("مرحبا")  # TextDirection.RTL
direction = detect_direction("Hello")  # TextDirection.LTR

# BiDi handler
config = BiDiConfig(
    insert_marks=True,      # Insert direction markers
    force_direction=None,   # Force direction (None = auto)
    isolate_numbers=True,   # Isolate numbers
    strip_marks=False,      # Strip existing markers
)

handler = BiDiHandler(config)
wrapped = handler.process("Hello مرحبا 123")

# Convenience function
wrapped = wrap_bidi("Hello مرحبا", BiDiControl.LRI)

Regional Dialect Support

Registering Dialects

from truthound.validators.i18n import (
    create_dialect,
    register_dialect,
    get_dialect_registry,
    get_fallback_chain,
)

# Create dialect
en_us = create_dialect(
    code="en-US",
    parent="en",
    name="English (United States)",
    overrides={
        ValidatorMessageCode.DATE_FORMAT_ERROR: "Invalid date format. Expected MM/DD/YYYY.",
    },
)

# Register dialect
register_dialect(en_us)

# Get fallback chain
chain = get_fallback_chain("en-US")
# ["en-US", "en"]

chain = get_fallback_chain("pt-BR")
# ["pt-BR", "pt"]

Setting Regional Locale

from truthound.validators.i18n import set_validator_locale

# Set regional dialect
set_validator_locale("en-US")
set_validator_locale("zh-TW")  # Traditional Chinese
set_validator_locale("pt-BR")  # Brazilian Portuguese

TMS Integration

Translation Management System (TMS) integration:

Supported TMS Providers

Provider Class API
Crowdin CrowdinProvider REST API v2
Lokalise LokaliseProvider REST API v2
Phrase PhraseProvider REST API v2

TMS Configuration

from truthound.validators.i18n import (
    TMSConfig,
    TMSManager,
    create_provider,
    CrowdinProvider,
)

# Crowdin configuration
config = TMSConfig(
    provider="crowdin",
    api_key="your-api-key",
    project_id="your-project-id",
    base_url="https://api.crowdin.com/api/v2",
    sync_interval_seconds=3600,
    auto_sync=True,
)

# Create provider
provider = create_provider(config)

# Or create directly
provider = CrowdinProvider(
    api_key="your-api-key",
    project_id="your-project-id",
)

TMS Manager

from truthound.validators.i18n import get_tms_manager

manager = get_tms_manager(config)

# Sync translations
await manager.sync()

# Check translation status
status = await manager.get_translation_status("ko")
print(status.progress_percentage)
print(status.translated_count)
print(status.approved_count)

# Handle webhook
event = manager.parse_webhook(request_body)
if event.event_type == "translation.completed":
    await manager.sync()

Dynamic Catalog Management

CatalogManager

from truthound.validators.i18n import (
    CatalogManager,
    get_catalog_manager,
    FileSystemStorage,
    MemoryStorage,
)

# Memory storage (default)
manager = get_catalog_manager()

# File system storage
storage = FileSystemStorage(path="/translations")
manager = CatalogManager(storage=storage)

# Load catalog
catalog = manager.load_catalog("ko")

# Save catalog
manager.save_catalog("ko", catalog)

# Invalidate cache
manager.invalidate_cache("ko")
manager.clear_cache()

Context-Based Messages

from truthound.validators.i18n import (
    ContextResolver,
    MessageContext,
    resolve_message,
)

# Context resolver
resolver = ContextResolver()

# Context-specific message retrieval
msg = resolve_message(
    code=ValidatorMessageCode.NULL_VALUES_FOUND,
    context=MessageContext.TECHNICAL,  # formal, informal, technical
    locale="ko",
    column="email",
    count=10,
)

Custom Catalogs

Creating Catalogs

from truthound.validators.i18n import (
    ValidatorMessageCatalog,
    CatalogBuilder,
    create_custom_catalog,
)

# Create with builder
builder = CatalogBuilder("custom")
builder.add_message(
    ValidatorMessageCode.NULL_VALUES_FOUND,
    "Column '{column}' contains {count} null values",
)
builder.add_message(
    ValidatorMessageCode.DUPLICATE_VALUES_FOUND,
    "Found {count} duplicate values in '{column}'",
)
catalog = builder.build()

# Convenience function
catalog = create_custom_catalog(
    locale="custom",
    messages={
        ValidatorMessageCode.NULL_VALUES_FOUND: "Custom null message: {count}",
    },
    fallback="en",
)

Registering Catalogs

from truthound.validators.i18n import get_catalog_manager

manager = get_catalog_manager()
manager.register_catalog("custom", catalog)

# Use
set_validator_locale("custom")
msg = get_validator_message(ValidatorMessageCode.NULL_VALUES_FOUND, count=5)

Retrieving Existing Catalogs

from truthound.validators.i18n import (
    get_all_catalogs,
    get_all_extended_catalogs,
    get_supported_locales,
    get_extended_supported_locales,
    get_default_messages,
    get_korean_messages,
    get_japanese_messages,
    get_chinese_messages,
    get_german_messages,
    get_french_messages,
    get_spanish_messages,
    get_portuguese_messages,
    get_italian_messages,
    get_russian_messages,
    get_arabic_messages,
    get_hebrew_messages,
    get_persian_messages,
)

# Supported locales list
locales = get_supported_locales()
# ["en", "ko", "ja", "zh", "de", "fr", "es"]

extended = get_extended_supported_locales()
# ["pt", "pt-BR", "pt-PT", "it", "ru", "ar", "he", "fa"]

# All catalogs
all_catalogs = get_all_catalogs()
all_extended = get_all_extended_catalogs()

# Individual catalogs
en_messages = get_default_messages()
ko_messages = get_korean_messages()
ar_messages = get_arabic_messages()

Next Steps