# Single Element Formatters

> **Relevant source files**
> * [ArWikiCats/translations_formats/DataModel/__init__.py](../ArWikiCats/translations_formats/DataModel/__init__.py)
> * [ArWikiCats/translations_formats/DataModel/model_data.py](../ArWikiCats/translations_formats/DataModel/model_data.py)
> * [ArWikiCats/translations_formats/DataModel/model_data_base.py](../ArWikiCats/translations_formats/DataModel/model_data_base.py)
> * [ArWikiCats/translations_formats/DataModel/model_data_time.py](../ArWikiCats/translations_formats/DataModel/model_data_time.py)
> * [ArWikiCats/translations_formats/DataModel/model_data_v2.py](../ArWikiCats/translations_formats/DataModel/model_data_v2.py)
> * [ArWikiCats/translations_formats/DataModelDouble/model_multi_data_double.py](../ArWikiCats/translations_formats/DataModelDouble/model_multi_data_double.py)
> * [ArWikiCats/translations_formats/__init__.py](../ArWikiCats/translations_formats/__init__.py)
> * [ArWikiCats/translations_formats/data_new_model.py](../ArWikiCats/translations_formats/data_new_model.py)
> * [ArWikiCats/translations_formats/data_with_time.py](../ArWikiCats/translations_formats/data_with_time.py)
> * [ArWikiCats/translations_formats/multi_data.py](../ArWikiCats/translations_formats/multi_data.py)

Single Element Formatters are classes that handle category translations containing one dynamic element to be replaced. They match a single key in the input category (e.g., "football", "yemen", "14th-century") and use template patterns to produce Arabic output. Single Element Formatters form the building blocks for more complex multi-element formatters (see [Multi-Element Formatters](25.Multi-Element-Formatters.md))

This page covers the three concrete implementations of Single Element Formatters: `FormatData`, `FormatDataV2`, and `FormatDataFrom`. For information about the abstract base class they inherit from, see [FormatDataBase Architecture](23.FormatDataBase-Architecture.md). For information about factory functions that create these formatters, see [Factory Functions and Usage](27.Factory-Functions-and-Usage.md)

## Architecture Overview

All Single Element Formatters inherit from `FormatDataBase` and implement two abstract methods: `apply_pattern_replacement` and `replace_value_placeholder`. The inheritance hierarchy defines a common interface for pattern matching and template application while allowing each subclass to handle value replacement differently.

```mermaid
classDiagram
    class FormatDataBase {
        «abstract»
        +formatted_data: Dict[str, str]
        +data_list: Dict[str, Any]
        +key_placeholder: str
        +pattern: re.Pattern
        +match_key(category: str) : str
        +normalize_category(category: str, key: str) : str
        +get_template(key: str, category: str) : str
        +get_key_label(key: str) : Any
        +search(category: str) : str
        +apply_pattern_replacement(template: str, value: Any) : str
        +replace_value_placeholder(label: str, value: Any) : str
    }
    class FormatData {
        +value_placeholder: str
        +apply_pattern_replacement(template: str, value: str) : str
        +replace_value_placeholder(label: str, value: str) : str
    }
    class FormatDataV2 {
        +apply_pattern_replacement(template: str, value: Dict) : str
        +replace_value_placeholder(label: str, value: Dict) : str
    }
    class FormatDataFrom {
        +search_callback: Callable
        +match_key_callback: Callable
        +fixing_callback: Callable
        +match_key(category: str) : str
        +apply_pattern_replacement(template: str, value: str) : str
        +replace_value_placeholder(label: str, value: str) : str
    }
    FormatDataBase <|-- FormatData
    FormatDataBase <|-- FormatDataV2
    FormatDataBase <|-- FormatDataFrom
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_base.py L38-L74](../ArWikiCats/translations_formats/DataModel/model_data_base.py#L38-L74), [ArWikiCats/translations_formats/DataModel/model_data.py L37-L64](../ArWikiCats/translations_formats/DataModel/model_data.py#L37-L64), [ArWikiCats/translations_formats/DataModel/model_data_v2.py L32-L57](../ArWikiCats/translations_formats/DataModel/model_data_v2.py#L32-L57)

### Common Resolution Flow

All Single Element Formatters follow the same resolution pipeline defined in `FormatDataBase._search`:

```mermaid
flowchart TD

Input["Input: category string"]
CheckDirect["Direct match in<br/>formatted_data_ci?"]
MatchKey["match_key()<br/>Find key in category"]
CheckKey["Key found?"]
GetLabel["get_key_label(key)<br/>Lookup Arabic value"]
CheckLabel["Label found?"]
GetTemplate["get_template(key, category)<br/>Find template pattern"]
CheckTemplate["Template found?"]
ApplyPattern["apply_pattern_replacement()<br/>Replace placeholder with value"]
Output["Output: Arabic translation"]
Fail["Output: empty string"]

Input --> CheckDirect
CheckDirect --> Output
CheckDirect --> MatchKey
MatchKey --> CheckKey
CheckKey --> Fail
CheckKey --> GetLabel
GetLabel --> CheckLabel
CheckLabel --> Fail
CheckLabel --> GetTemplate
GetTemplate --> CheckTemplate
CheckTemplate --> Fail
CheckTemplate --> ApplyPattern
ApplyPattern --> Output
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_base.py L264-L293](../ArWikiCats/translations_formats/DataModel/model_data_base.py#L264-L293)

## FormatData: Simple String Replacement

`FormatData` is the primary single-element formatter for categories where the data value is a simple string. It replaces a single placeholder in the template with the corresponding Arabic label.

### Data Structure

| Component | Type | Example |
| --- | --- | --- |
| `formatted_data` | `Dict[str, str]` | `{"{sport} players": "لاعبو {sport_label}"}` |
| `data_list` | `Dict[str, str]` | `{"football": "كرة القدم"}` |
| `key_placeholder` | `str` | `"{sport}"` |
| `value_placeholder` | `str` | `"{sport_label}"` |

### Translation Process

```mermaid
flowchart TD

Cat["Category:<br/>'football players'"]
Pattern["pattern.search()<br/>Match 'football'"]
Key["Matched key:<br/>'football'"]
DataList["data_list_ci.get('football')<br/>→ 'كرة القدم'"]
Normalize["normalize_category()<br/>'football players'<br/>→ '{sport} players'"]
Template["formatted_data_ci.get()<br/>→ 'لاعبو {sport_label}'"]
Replace["template.replace()<br/>'{sport_label}' → 'كرة القدم'"]
Result["Result:<br/>'لاعبو كرة القدم'"]

Cat --> Pattern
Key --> DataList
Key --> Normalize
DataList --> Replace
Template --> Replace

subgraph Replacement ["Replacement"]
    Replace
    Result
    Replace --> Result
end

subgraph subGraph3 ["Template Selection"]
    Normalize
    Template
    Normalize --> Template
end

subgraph subGraph2 ["Data Lookup"]
    DataList
end

subgraph subGraph1 ["Key Matching"]
    Pattern
    Key
    Pattern --> Key
end

subgraph Input ["Input"]
    Cat
end
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data.py L37-L132](../ArWikiCats/translations_formats/DataModel/model_data.py#L37-L132)

### Code Example

The following demonstrates `FormatData` usage from the actual implementation:

```css
# From ArWikiCats/translations_formats/DataModel/model_data.py:134-184
formatted_data = {
    "{sport} players": "لاعبو {sport_label}",
    "{sport} coaches": "مدربو {sport_label}",
    "{sport} managers": "مدربو {sport_label}",
}

data_list = {
    "american football": "كرة قدم أمريكية",
    "canadian football": "كرة قدم كندية",
}

bot = FormatData(
    formatted_data=formatted_data,
    data_list=data_list,
    key_placeholder="{sport}",
    value_placeholder="{sport_label}"
)

result = bot.search("american football players")
# result == "لاعبو كرة قدم أمريكية"
```

### Implementation Details

The `apply_pattern_replacement` method performs simple string replacement:

```python
# From ArWikiCats/translations_formats/DataModel/model_data.py:100-116
def apply_pattern_replacement(self, template_label: str, sport_label: str) -> str:
    final_label = template_label.replace(self.value_placeholder, sport_label)

    if self.value_placeholder not in final_label:
        return final_label.strip()

    return ""
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data.py L66-L132](../ArWikiCats/translations_formats/DataModel/model_data.py#L66-L132)

## FormatDataV2: Dictionary-Based Replacement

`FormatDataV2` extends the base formatter to support dictionary values in `data_list`, enabling multiple placeholder replacements from a single key. This is used when a single entity (e.g., a country) requires different forms in the output template (e.g., demonym vs. country name)

### Data Structure

| Component | Type | Example |
| --- | --- | --- |
| `formatted_data` | `Dict[str, str]` | `{"{country} writers": "{demonym} كتاب من {country_ar}"}` |
| `data_list` | `Dict[str, Union[str, Dict]]` | `{"yemen": {"demonym": "يمنيون", "country_ar": "اليمن"}}` |
| `key_placeholder` | `str` | `"{country}"` |

Note that `FormatDataV2` does **not** have a `value_placeholder` parameter because the dictionary keys serve as the placeholder names.

### Translation Process with Multiple Placeholders

```mermaid
flowchart TD

Cat["Category:<br/>'yemen writers'"]
Pattern["pattern.search()<br/>Match 'yemen'"]
Key["Matched key:<br/>'yemen'"]
DataList["data_list_ci.get('yemen')<br/>→ {'demonym': 'يمنيون',<br/>'country_ar': 'اليمن'}"]
Normalize["normalize_category()<br/>'yemen writers'<br/>→ '{country} writers'"]
Template["formatted_data_ci.get()<br/>→ '{demonym} كتاب من {country_ar}'"]
Replace1["{demonym} → 'يمنيون'"]
Replace2["{country_ar} → 'اليمن'"]
Result["Result:<br/>'يمنيون كتاب من اليمن'"]

Cat --> Pattern
Key --> DataList
Key --> Normalize
DataList --> Replace1
DataList --> Replace2
Template --> Replace1

subgraph Multi-Replacement ["Multi-Replacement"]
    Replace1
    Replace2
    Result
    Replace1 --> Replace2
    Replace2 --> Result
end

subgraph subGraph3 ["Template Selection"]
    Normalize
    Template
    Normalize --> Template
end

subgraph subGraph2 ["Data Lookup"]
    DataList
end

subgraph subGraph1 ["Key Matching"]
    Pattern
    Key
    Pattern --> Key
end

subgraph Input ["Input"]
    Cat
end
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_v2.py L32-L122](../ArWikiCats/translations_formats/DataModel/model_data_v2.py#L32-L122)

### Implementation Details

The `apply_pattern_replacement` method iterates over dictionary entries:

```python
# From ArWikiCats/translations_formats/DataModel/model_data_v2.py:81-100
def apply_pattern_replacement(self, template_label: str, sport_label: Union[str, Dict[str, str]]) -> str:
    if not isinstance(sport_label, dict):
        return template_label

    final_label = template_label

    if isinstance(sport_label, dict):
        for key, val in sport_label.items():
            if isinstance(val, str) and val:
                final_label = final_label.replace(f"{{{key}}}", val)

    return final_label.strip()
```

### Use Case: Nationality Resolution

`FormatDataV2` is heavily used in nationality resolution where each nationality provides multiple grammatical forms:

```python
# Real-world example from nationality resolver
data_list = {
    "yemeni": {
        "nat_label": "يمنيون",      # plural masculine
        "nat_label_f": "يمنيات",    # plural feminine
        "nat_label_g": "يمنية",     # singular feminine
    }
}

formatted_data = {
    "{nat_en} men": "{nat_label}",
    "{nat_en} women": "{nat_label_f}",
    "{nat_en} actresses": "ممثلات {nat_label_f}",
}
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_v2.py L59-L122](../ArWikiCats/translations_formats/DataModel/model_data_v2.py#L59-L122)

## FormatDataFrom: Callback-Based Replacement

`FormatDataFrom` is a specialized formatter that uses callback functions instead of static data dictionaries. It is primarily used for temporal pattern resolution where the key extraction and value generation require custom logic.

### Data Structure

| Component | Type | Purpose |
| --- | --- | --- |
| `search_callback` | `Callable` | Convert matched key to Arabic (e.g., "14th-century" → "القرن 14") |
| `match_key_callback` | `Callable` | Extract temporal key from category |
| `fixing_callback` | `Callable` | Normalize temporal expressions before matching |

### YearFormatData Factory

The primary use of `FormatDataFrom` is through the `YearFormatData` factory function:

```python
# From ArWikiCats/translations_formats/DataModel/model_data_time.py:34-66
def YearFormatData(
    key_placeholder: str,
    value_placeholder: str,
) -> FormatDataFrom:
    return FormatDataFrom(
        formatted_data={},
        key_placeholder=key_placeholder,
        value_placeholder=value_placeholder,
        search_callback=convert_time_to_arabic,
        match_key_callback=match_time_en_first,
        fixing_callback=standardize_time_phrases,
    )
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_time.py L1-L67](../ArWikiCats/translations_formats/DataModel/model_data_time.py#L1-L67)

### Temporal Pattern Translation

```mermaid
flowchart TD

Cat["Category:<br/>'14th-century writers'"]
Fix["standardize_time_phrases()<br/>'14th-century' → '14th-century'"]
Match["match_time_en_first()<br/>Extract '14th-century'"]
Convert["convert_time_to_arabic()<br/>'14th-century' → 'القرن 14'"]
Template["Template: '{year1} writers'<br/>→ 'كتاب {year1}'"]
Replace["Replace '{year1}' → 'القرن 14'"]
Result["Result:<br/>'كتاب القرن 14'"]

Cat --> Fix
Match --> Convert
Convert --> Template

subgraph subGraph3 ["Template Application"]
    Template
    Replace
    Result
    Template --> Replace
    Replace --> Result
end

subgraph subGraph2 ["Callback: search_callback"]
    Convert
end

subgraph subGraph1 ["Callback: match_key_callback"]
    Fix
    Match
    Fix --> Match
end

subgraph Input ["Input"]
    Cat
end
```

### Supported Temporal Patterns

The callback functions in `FormatDataFrom` handle multiple temporal patterns:

| English Pattern | Matched Key | Arabic Output |
| --- | --- | --- |
| `"1990s"` | `"1990s"` | `"عقد 1990"` |
| `"14th-century"` | `"14th-century"` | `"القرن 14"` |
| `"2010"` | `"2010"` | `"2010"` |
| `"1990–1999"` | `"1990–1999"` | `"1990–1999"` |

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data_time.py L1-L67](../ArWikiCats/translations_formats/DataModel/model_data_time.py#L1-L67)

## Comparison and Usage Guidance

### Selection Matrix

```mermaid
flowchart TD

Start["Need single-element formatter"]
Q1["Value is<br/>time-based pattern?"]
Q2["Value requires<br/>multiple placeholders?"]
Q3["Simple<br/>string replacement?"]
UseFrom["Use FormatDataFrom<br/>via YearFormatData()"]
UseV2["Use FormatDataV2"]
UseBasic["Use FormatData"]

Start --> Q1
Q1 --> UseFrom
Q1 --> Q2
Q2 --> UseV2
Q2 --> Q3
Q3 --> UseBasic
```

### Implementation Comparison

| Feature | FormatData | FormatDataV2 | FormatDataFrom |
| --- | --- | --- | --- |
| **Value Type** | `str` | `str \| Dict[str, str]` | Generated by callback |
| **Placeholder Count** | 1 | Multiple | 1 |
| **Data Source** | Static dictionary | Static dictionary | Callback function |
| **Primary Use Case** | Sports, simple jobs | Nationalities, countries | Years, decades, centuries |
| **Example Template** | `"{sport} players"` | `"{country} {demonym} writers"` | `"{year1} events"` |
| **File Path** | [model_data.py](../model_data.py) | [model_data_v2.py](../model_data_v2.py) | [model_data_form.py](../model_data_form.py) <br/>  (via [model_data_time.py](../model_data_time.py) <br/> ) |

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data.py L37-L132](../ArWikiCats/translations_formats/DataModel/model_data.py#L37-L132), [ArWikiCats/translations_formats/DataModel/model_data_v2.py L32-L122](../ArWikiCats/translations_formats/DataModel/model_data_v2.py#L32-L122), [ArWikiCats/translations_formats/DataModel/model_data_time.py L34-L66](../ArWikiCats/translations_formats/DataModel/model_data_time.py#L34-L66)

### Code Entity Mapping

The following table maps natural language concepts to concrete code entities:

| Concept | Class | Factory Function | Module |
| --- | --- | --- | --- |
| Simple formatter | `FormatData` | N/A (direct instantiation) | `model_data.py` |
| Dictionary formatter | `FormatDataV2` | N/A (direct instantiation) | `model_data_v2.py` |
| Year formatter | `FormatDataFrom` | `YearFormatData()` | `model_data_time.py` |
| Dual-element formatter | `MultiDataFormatterBase` | `format_multi_data()` | `multi_data.py` |
| Dual-element dict formatter | `MultiDataFormatterBaseV2` | `format_multi_data_v2()` | `multi_data.py` |
| Year+country formatter | `MultiDataFormatterBaseYear` | `format_year_country_data()` | `data_with_time.py` |
| Film formatter | `MultiDataFormatterDataDouble` | `format_films_country_data()` | `data_new_model.py` |

**Sources:** [ArWikiCats/translations_formats/__init__.py L1-L97](../ArWikiCats/translations_formats/__init__.py#L1-L97), [ArWikiCats/translations_formats/multi_data.py L1-L290](../ArWikiCats/translations_formats/multi_data.py#L1-L290), [ArWikiCats/translations_formats/data_with_time.py L1-L179](../ArWikiCats/translations_formats/data_with_time.py#L1-L179), [ArWikiCats/translations_formats/data_new_model.py L1-L111](../ArWikiCats/translations_formats/data_new_model.py#L1-L111)

## Integration with Resolver Chain

Single Element Formatters are used throughout the resolver chain (see [Resolver System](14.Resolver-System.md)). Each resolver typically instantiates one or more formatters:

```mermaid
flowchart TD

SportsData["SPORT_KEY_RECORDS<br/>(431 sports)"]
SportsFormat["FormatData instance<br/>key_placeholder='{sport}'<br/>value_placeholder='{sport_label}'"]
NatData["All_Nat<br/>(843 nationalities)"]
NatFormat["FormatDataV2 instance<br/>key_placeholder='{nat_en}'<br/>Supports: nat_label, nat_label_f, etc."]
TimeFormat["YearFormatData instance<br/>key_placeholder='{year1}'<br/>Callbacks: convert_time_to_arabic"]
Input1["'football players'"]
Input2["'yemeni writers'"]
Input3["'14th-century events'"]

Input1 --> SportsFormat
Input2 --> NatFormat
Input3 --> TimeFormat

subgraph subGraph3 ["Category Input"]
    Input1
    Input2
    Input3
end

subgraph subGraph2 ["Time Resolver"]
    TimeFormat
end

subgraph subGraph1 ["Nationality Resolver"]
    NatData
    NatFormat
    NatData --> NatFormat
end

subgraph subGraph0 ["Sports Resolver"]
    SportsData
    SportsFormat
    SportsData --> SportsFormat
end
```

**Sources:** [ArWikiCats/translations_formats/DataModel/model_data.py L1-L184](../ArWikiCats/translations_formats/DataModel/model_data.py#L1-L184), [ArWikiCats/translations_formats/DataModel/model_data_v2.py L1-L122](../ArWikiCats/translations_formats/DataModel/model_data_v2.py#L1-L122), [ArWikiCats/translations_formats/DataModel/model_data_time.py L1-L67](../ArWikiCats/translations_formats/DataModel/model_data_time.py#L1-L67)
