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.




