# 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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

- [Custom Validators](custom-validators.md) - SDK basic usage
- [Security Guide](security.md) - ReDoS protection, SQL injection prevention
- [Built-in Validators](built-in.md) - 289 built-in validators reference
