Editorial illustration: Persian in Unity takes more than translation

Persian in Unity takes more than translation

The problem I needed to solve

For Persian, translating strings was only part of the job. Letter joining, visual order and embedded numbers could make a correct message unreadable.

Implementation in this snapshot

PersianGlyphShaper.Fix normalizes presentation forms and shapes connected letters. PreserveNumericRuns inserts direction marks to preserve numbers; PersianTextProcessingMode chooses processing behavior.

The design decision and its cost

ShapeLettersAndReverse is a fallback that restores Latin and numeric runs to their internal order. I separated translated content from the shaped text used for rendering.

Current boundaries

This utility is not a complete Unicode bidirectional engine. Mixed text, punctuation, fonts and TextMeshPro RTL settings need checking together; a method name does not guarantee every linguistic case.

From a JSON key to fonts and joined letters

The bilingual-development post describes JSON files with unique text keys, and LocalizationService loads Resources/Localization/en and fa. Their items contain key and value fields. GetText checks the requested language’s JSON table before LocalizedTextTable or fallback. Translated display text should not replace a stable key. TryGetTextBySourceText intentionally maps English source text; matching an already displayed Persian value can mix languages after reopening a page.

Text selection solves only part of the problem. LocalizationTextStyleSet can assign a TMP_FontAsset, alignment, isRightToLeftText and character or word spacing overrides per language. ApplyTo applies these settings and supports a fallback style. Assigning a Persian font does not establish that its atlas contains every required presentation form and symbol. Readability depends on the font asset, processing mode and RTL settings together; a correct JSON string can still appear incorrectly joined or spaced.

PersianGlyphShaper chooses isolated, initial, medial or final forms from neighboring letters and treats join boundaries such as half-spaces and punctuation explicitly. PreserveNumericRuns adds invisible direction marks for values such as 200 and 3/10. ShapeLettersAndReverse is a lightweight layout fallback rather than a full bidi engine. LocalizationBootstrapper can exclude binders and NonLocalizedText from automatic processing, avoiding a second pass over already shaped Persian after switching languages or reopening a page. Mixed-text coverage still needs evaluation with actual examples.

What this stage taught me

For me, readable Persian is part of gameplay. Players should understand scores and selection prompts immediately without deciphering reversed numbers or disconnected letters.

Explore Dalo

Latest devlogs

Editorial illustration: Pasur launch and telemetry in the shared structure

Pasur launch and telemetry in the shared structure

Adding Pasur needs both a correct mode handoff and valid public telemetry.

Read article
Editorial illustration: Pasur bot decisions from capture options

Pasur bot decisions from capture options

My teammate’s Pasur bot chooses among legal options without inspecting hidden opposing cards.

Read article
Editorial illustration: Capturing table cards in Pasur

Capturing table cards in Pasur

In my teammate’s implementation, Pasur combines selecting a hand card with selecting table cards. Its rules must produce valid sets and prohibit ignoring an available capture.

Read article
Dalo