DLL for Unity IL2CPP games to machine-translate UI text on the fly
Find a file
2026-07-10 21:38:16 +02:00
.cargo init 2026-07-09 23:18:48 +02:00
src translation: trim spacing around restored tags 2026-07-09 23:18:48 +02:00
.gitattributes init 2026-07-09 23:18:48 +02:00
.gitignore init 2026-07-09 23:18:48 +02:00
build.rs init 2026-07-09 23:18:48 +02:00
Cargo.lock release: bump version to 0.1.1 2026-07-10 21:38:16 +02:00
Cargo.toml release: bump version to 0.1.1 2026-07-10 21:38:16 +02:00
README.md translation: handle mangled tokens more robustly 2026-07-09 23:18:48 +02:00
rust-toolchain.toml init 2026-07-09 23:18:48 +02:00
rustfmt.toml init 2026-07-09 23:18:48 +02:00

Unity Auto Translator

A dll that hooks into Unity IL2CPP (yes, only IL2CPP is supported for now) games at runtime and machine-translates UI text on the fly using Google Translate's free client=gtx endpoint (no API key needed).

You can use any loader/injector you want, I recommend something like UnityDllLoader

How it works

  • Hooks UnityEngine.UI.Text and TMPro.TMP_Text (their setters, OnEnable, and SetVerticesDirty) via inline hooking.
  • The first time a piece of text is seen, the original is shown immediately while translation happens on a background thread. Once the result comes back, it's pushed onto the same UI element, but only if that element is still showing the same text (Unity likes to recycle components across screens, so this check matters). Applying it back onto the UI has to happen on the main thread, so a hook on Canvas.SendWillRenderCanvases (called once per frame regardless of which text is involved) makes sure that handoff actually happens instead of waiting on some unrelated text to update first.
  • Translations are cached to disk (unity_auto_translator/translations.jsonl) so repeated views are instant and don't hit the network again.
  • Numbers and rich-text tags (<color=...>, etc.) are swapped out for placeholder tokens before being sent to Google and restored afterwards, so they don't get mistranslated, reordered, dropped or whatever.
  • Long text gets split into sentence/clause-sized pieces (never through one of our own tokens) and each piece is sent as its own request. Google's endpoint silently splits sufficiently long input into translation chunks internally, and that split can land in the middle of a placeholder token, corrupting it; keeping every request under that length avoids it entirely. If a number token still can't survive after retrying, the number gets sent as real text instead of a token, as a one-off, less cacheable fallback so the text still ends up translated.
  • Requests already queued up together get batched into a single call to cut down on round-trips, with a configurable rate limit between requests.

Building

  • Requires a nightly Rust toolchain.
  • Works with both Windows toolchains:
    • x86_64-pc-windows-msvc
    • x86_64-pc-windows-gnu
  • Build with cargo build --release, or pass an explicit target if needed:
    • cargo build --release --target x86_64-pc-windows-msvc
    • cargo build --release --target x86_64-pc-windows-gnu
  • Output:
    • target/<target-triple>/release/unity_auto_translator.dll

Usage

  1. Build (or just use prebuilt) unity_auto_translator.dll.
  2. Inject it into the target game process with your loader/injector of choice.
  3. On first run it creates unity_auto_translator/config.ini (relative to the process's working directory) with some default values.
  4. Set SourceLanguage to the game's actual language and restart. This both improves translation quality and lets the filter skip text that obviously isn't in that language (pure latin labels, numbers, etc.) without wasting a request on it.

Configuration (config.ini)

[General]

  • SourceLanguage: language the game's text is written in (e.g. zh-CN, ja, auto).
  • TargetLanguage: language to translate into (e.g. en).

[Filter]

  • ExcludedPrefixes: comma-separated text prefixes that should never be translated.
  • ExcludedSubstrings: semicolon-separated substrings; text containing any of them is never translated.
  • ExcludedPatterns: semicolon-separated regular expressions; text matching any of them is never translated. Invalid patterns are logged and skipped rather than failing startup.

[RateLimit]

  • MinDelayMs: minimum delay between translation requests. 0 disables it.

[Batching]

  • MaxSize: max number of texts combined into a single translation request.
  • MaxChars: max combined length (as it would appear once percent-encoded into the request URL) before starting a new batch.

[Network]

  • MaxRetries: retry attempts before giving up on a translation.
  • TimeoutMs: per-request network timeout.

Cache file

Translations live as newline-delimited JSON at unity_auto_translator/translations.jsonl, one {"original": ..., "translated": ...} per line. There's no separate override mechanism, just edit or append a line and restart (the last entry for a given original wins on load).

Known limitations

  • Google Translate only, no fallback provider.
  • config.ini isn't hot-reloaded, so changes need a game restart.
  • Right-to-left target languages (Arabic, Hebrew, ...) aren't handled specially, and Unity's own text layout doesn't support them well regardless.